<template>
<div id="app">
<div class="model" v-show="model" @click="model = false">
<div class="model-show">
<img :src="modelSrc" alt />
</div>
</div>
<p>例子</p>
<div class="cut">
<vue-cropper
ref="cropper"
:img="option.img"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:full="option.full"
:fixed="fixed"
:fixed-number="fixedNumber"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:fixed-box="option.fixedBox"
:original="option.original"
:auto-crop="option.autoCrop"
:auto-crop-width="option.autoCropWidth"
:auto-crop-height="option.autoCropHeight"
:center-box="option.centerBox"
@real-time="realTime"
:high="option.high"
@img-load="imgLoad"
mode="cover"
:max-img-size="option.max"
@crop-moving="cropMoving"
></vue-cropper>
</div>
<div
class="show-preview"
:style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden', 'margin': '5px'}"
>
<div :style="previews.div">
<img :src="previews.url" :style="previews.img" />
</div>
</div>
<div class="test-button">
<button @click="changeImg" class="btn">changeImg</button>
<label class="btn" for="uploads">upload</label>
<input
type="file"
id="uploads"
style="position:absolute; clip:rect(0 0 0 0);"
accept="image/png, image/jpeg, image/gif, image/jpg"
@change="uploadImg($event, 1)"
/>
<button @click="startCrop" v-if="!crap" class="btn">start</button>
<button @click="stopCrop" v-else class="btn">stop</button>
<button @click="clearCrop" class="btn">clear</button>
<button @click="refreshCrop" class="btn">refresh</button>
<button @click="changeScale(1)" class="btn">+</button>
<button @click="changeScale(-1)" class="btn">-</button>
<button @click="rotateLeft" class="btn">rotateLeft</button>
<button @click="rotateRight" class="btn">rotateRight</button>
<button @click="finish('base64')" class="btn">preview(base64)</button>
<button @click="finish('blob')" class="btn">preview(blob)</button>
<button @click="down('base64')" class="btn">download(base64)</button>
<a @click="down('base64')" class="btn">download(base64)</a>
<a @click="down('blob')" class="btn">download(blob)</a>
<a @click="() => option.img = ''" class="btn">清除图片</a>
<div style="display:block; width: 100%;">
<label class="c-item">
<span>上传图片是否显示原始宽高 (针对大图 可以铺满)</span>
<input type="checkbox" v-model="option.original" />
<span>original: {{ option.original}}</span>
</label>
<label class="c-item">
<span>能否拖动图片</span>
<input type="checkbox" v-model="option.canMove" />
</label>
<label class="c-item">
<span>能否拖动截图框</span>
<input type="checkbox" v-model="option.canMoveBox" />
<span>canMoveBox: {{ option.canMoveBox}}</span>
</label>
<label class="c-item">
<span>截图框固定大小</span>
<input type="checkbox" v-model="option.fixedBox" />
<span>fixedBox: {{ option.fixedBox}}</span>
</label>
<label class="c-item">
<span>是否输出原图比例的截图</span>
<input type="checkbox" v-model="option.full" />
<span>full: {{ option.full}}</span>
</label>
<label class="c-item">
<span>是否自动生成截图框</span>
<input type="checkbox" v-model="option.autoCrop" />
<span>autoCrop: {{ option.autoCrop}}</span>
</label>
<label class="c-item">
<span>是否根据dpr生成适合屏幕的高清图片</span>
<input type="checkbox" v-model="option.high" />
<span>high: {{ option.high}}</span>
</label>
<label class="c-item">
<span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
<input type="checkbox" v-model="option.centerBox" />
<span>centerBox: {{ option.centerBox}}</span>
</label>
<label class="c-item">
<p>输出图片格式</p>
<label>
jpg
<input type="radio" name="type" value="jpeg" v-model="option.outputType" />
</label>
<label>
png
<input type="radio" name="type" value="png" v-model="option.outputType" />
</label>
<label>
webp
<input type="radio" name="type" value="webp" v-model="option.outputType" />
</label>
</label>
</div>
</div>
</div>
<!-- <script src="https://vuejs.org/js/vue.min.js"></script> -->
<!-- <script src="https://cdn.bootcss.com/vue/2.6.11/vue.js"></script> -->
<!-- <script src="https://cdn.jsdelivr.net/npm/vue-cropper@0.5.5/dist/index.min.js"></script> -->
</template> <script>
import { VueCropper } from 'vue-cropper'
export default {
name: "CropperImage",
components: {
VueCropper
},
props: ['Name'],
data () {
return {
model: false,
modelSrc: '',
crap: false,
previews: {},
lists: [
{
img: 'https://avatars2.githubusercontent.com/u/15681693?s=460&v=4'
}
],
option: {
img: 'https://avatars2.githubusercontent.com/u/15681693?s=460&v=4',
size: 1,
full: false,
outputType: 'png',
canMove: true,
fixedBox: false,
original: false,
canMoveBox: true,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 750,
autoCropHeight: 340,
centerBox: true,
high: true,
max: 99999
},
show: true,
fixed: false,
fixedNumber: [16, 9]
}
},
methods: {
changeImg () {
this.option.img = this.lists[~~(Math.random() * this.lists.length)].img
},
startCrop () {
// start
this.crap = true
this.$refs.cropper.startCrop()
},
stopCrop () {
// stop
this.crap = false
this.$refs.cropper.stopCrop()
},
clearCrop () {
// clear
this.$refs.cropper.clearCrop()
},
refreshCrop () {
// clear
this.$refs.cropper.refresh()
},
changeScale (num) {
num = num || 1
this.$refs.cropper.changeScale(num)
},
rotateLeft () {
this.$refs.cropper.rotateLeft()
},
rotateRight () {
this.$refs.cropper.rotateRight()
},
finish (type) {
console.log(type)
// 输出
// var test = window.open('about:blank')
// test.document.body.innerHTML = '图片生成中..'
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
console.log(data)
var img = window.URL.createObjectURL(data)
this.model = true
this.modelSrc = img
})
} else {
this.$refs.cropper.getCropData((data) => {
this.model = true
this.modelSrc = data
})
}
},
// 实时预览函数
realTime (data) {
console.log('实时预览函数')
this.previews = data
console.log(data)
},
finish2 (type) {
console.log(type)
this.$refs.cropper2.getCropData((data) => {
this.model = true
this.modelSrc = data
})
},
finish3 (type) {
console.log(type) this.$refs.cropper3.getCropData((data) => {
this.model = true
this.modelSrc = data
})
},
down (type) {
// event.preventDefault()
var aLink = document.createElement('a')
aLink.download = 'demo'
// 输出
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
this.downImg = window.URL.createObjectURL(data)
aLink.href = window.URL.createObjectURL(data)
aLink.click()
})
} else {
this.$refs.cropper.getCropData((data) => {
this.downImg = data
aLink.href = data
aLink.click()
})
}
}, uploadImg (e, num) {
//上传图片
// this.option.img
var file = e.target.files[0]
if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
alert('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种')
return false
}
var reader = new FileReader()
reader.onload = (e) => {
let data
if (typeof e.target.result === 'object') {
// 把Array Buffer转化为blob 如果是base64不需要
data = window.URL.createObjectURL(new Blob([e.target.result]))
} else {
data = e.target.result
}
if (num === 1) {
this.option.img = data
} else if (num === 2) {
this.example2.img = data
}
}
// 转化为base64
// reader.readAsDataURL(file)
// 转化为blob
reader.readAsArrayBuffer(file)
},
imgLoad (msg) {
console.log(msg)
},
cropMoving (data) {
console.log(data, '截图框当前坐标')
}
},
}
</script> <style scoped >
* {
margin: 0;
padding: 0;
} .cut {
width: 500px;
height: 500px;
margin: 30px auto;
} .c-item {
max-width: 800px;
margin: 10px auto;
margin-top: 20px;
} .content {
margin: auto;
max-width: 1200px;
margin-bottom: 100px;
} .test-button {
display: flex;
flex-wrap: wrap;
align-content: center;
justify-content: center;
} .btn {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #c0ccda;
color: #1f2d3d;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 20px 10px 0px 0px;
padding: 9px 15px;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #50bfff;
border-color: #50bfff;
transition: all 0.2s ease;
text-decoration: none;
user-select: none;
} .des {
line-height: 30px;
} code.language-html {
padding: 10px 20px;
margin: 10px 0px;
display: block;
background-color: #333;
color: #fff;
overflow-x: auto;
font-family: Consolas, Monaco, Droid, Sans, Mono, Source, Code, Pro, Menlo,
Lucida, Sans, Type, Writer, Ubuntu, Mono;
border-radius: 5px;
white-space: pre;
} .show-info {
margin-bottom: 50px;
} .show-info h2 {
line-height: 50px;
} /*.title, .title:hover, .title-focus, .title:visited {
color: black;
}*/ .title {
display: block;
text-decoration: none;
text-align: center;
line-height: 1.5;
margin: 20px 0px;
background-image: -webkit-linear-gradient(
left,
#3498db,
#f47920 10%,
#d71345 20%,
#f7acbc 30%,
#ffd400 40%,
#3498db 50%,
#f47920 60%,
#d71345 70%,
#f7acbc 80%,
#ffd400 90%,
#3498db
);
color: transparent;
-webkit-background-clip: text;
background-size: 200% 100%;
animation: slide 5s infinite linear;
font-size: 40px;
} .test {
height: 500px;
} .model {
position: fixed;
z-index: 10;
width: 100vw;
height: 100vh;
overflow: auto;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.8);
} .model-show {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
} .model img {
display: block;
margin: auto;
max-width: 80%;
user-select: none;
background-position: 0px 0px, 10px 10px;
background-size: 20px 20px;
background-image: linear-gradient(
45deg,
#eee 25%,
transparent 25%,
transparent 75%,
#eee 75%,
#eee 100%
),
linear-gradient(45deg, #eee 25%, white 25%, white 75%, #eee 75%, #eee 100%);
} .c-item {
display: block;
user-select: none;
} @keyframes slide {
0% {
background-position: 0 0;
}
100% {
background-position: -100% 0;
}
}
</style>

再次封装 【精简】

<template>
<div class="cropper-content">
<div class="cropper-box">
<div class="cropper">
<vue-cropper
ref="cropper"
:img="option.img"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:full="option.full"
:fixed="fixed"
:fixed-number="fixedNumber"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:fixed-box="option.fixedBox"
:original="option.original"
:auto-crop="option.autoCrop"
:auto-crop-width="option.autoCropWidth"
:auto-crop-height="option.autoCropHeight"
:center-box="option.centerBox"
@real-time="realTime"
:high="option.high"
@img-load="imgLoad"
mode="cover"
:max-img-size="option.max"
@crop-moving="cropMoving"
></vue-cropper>
</div>
<!--底部操作工具按钮-->
<div class="footer-btn">
<div class="scope-btn">
<!-- <label class="btn" for="uploads">选择封面</label> -->
<!-- <input
type="file"
id="uploads"
style="position:absolute; clip:rect(0 0 0 0);"
accept="image/png, image/jpeg, image/gif, image/jpg"
@change="selectImg($event)"
/>-->
<el-button
size="mini"
type="danger"
plain
icon="el-icon-zoom-in"
@click="changeScale(1)"
>放大</el-button>
<el-button
size="mini"
type="danger"
plain
icon="el-icon-zoom-out"
@click="changeScale(-1)"
>缩小</el-button>
<el-button size="mini" type="danger" plain @click="rotateLeft">↺ 左旋转</el-button>
<el-button size="mini" type="danger" plain @click="rotateRight">↻ 右旋转</el-button>
</div>
<div class="upload-btn">
<el-button size="mini" type="success" @click="uploadImg('blob')">
确定
<i class="el-icon-upload"></i>
</el-button>
<el-button size="mini" @click="down('base64')" type="primary">图片下载</el-button>
</div>
</div>
</div> <!--预览效果图-->
<div class="show-preview">
<div :style="previews.div" class="preview">
<img :src="previews.url" :style="previews.img" />
</div>
<label class="c-item">
<p>图片下载格式:</p>
<label>
<input type="radio" name="type" value="jpeg" v-model="option.outputType" />
jpg
</label>
<label>
<input type="radio" name="type" value="png" v-model="option.outputType" />
png
</label>
<label>
<input type="radio" name="type" value="webp" v-model="option.outputType" />
webp
</label>
</label>
</div>
</div>
</template> <script>
import { VueCropper } from 'vue-cropper'
import api from "@/service/api"
export default {
name: "CropperImage",
components: {
VueCropper
},
props: {
Name: {
type: String,
},
cropperImage: {
type: String,
}
},
data () {
return {
name: this.Name,
previews: {},
option: {
img: this.cropperImage, //裁剪图片的地址
outputSize: 1, //裁剪生成图片的质量(可选0.1 - 1)
outputType: 'jpeg', //裁剪生成图片的格式(jpeg || png || webp)
info: true, //图片大小信息
canScale: true, //图片是否允许滚轮缩放
autoCrop: true, //是否默认生成截图框
autoCropWidth: 230, //默认生成截图框宽度
autoCropHeight: 150, //默认生成截图框高度
fixed: true, //是否开启截图框宽高固定比例
fixedNumber: [1.53, 1], //截图框的宽高比例
full: false, //false按原比例裁切图片,不失真
fixedBox: true, //固定截图框大小,不允许改变
canMove: false, //上传图片是否可以移动
canMoveBox: true, //截图框能否拖动
original: false, //上传图片按照原始比例渲染
centerBox: false, //截图框是否被限制在图片里面
height: true, //是否按照设备的dpr 输出等比例图片
infoTrue: false, //true为展示真实输出图片宽高,false展示看到的截图框宽高
maxImgSize: 3000, //限制图片最大宽度和高度
enlarge: 1, //图片根据截图框输出比例倍数
mode: '230px 150px', //图片默认渲染方式
},
fixedNumber: [16, 9],
fixed: false,
}
},
methods: {
// 图片下载
down (type) {
// event.preventDefault()
var aLink = document.createElement('a')
console.log(aLink) // a 标签
aLink.download = '子设备图片' // 名字
// 输出
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
this.downImg = window.URL.createObjectURL(data)
aLink.href = window.URL.createObjectURL(data)
aLink.click()
})
} else {
this.$refs.cropper.getCropData((data) => {
this.downImg = data
aLink.href = data
aLink.click()
})
}
},
// 截图框移动
cropMoving (data) {
console.log('截图框移动', data)
},
//图片移动回调函数
imgMoving (data) {
console.log('图片移动', data)
},
//初始化函数
imgLoad (msg) {
console.log("工具初始化函数=====" + msg)
},
//图片缩放
changeScale (num) {
num = num || 1
this.$refs.cropper.changeScale(num)
},
//向左旋转
rotateLeft () {
this.$refs.cropper.rotateLeft()
},
//向右旋转
rotateRight () {
this.$refs.cropper.rotateRight()
},
//实时预览函数
realTime (data) {
console.log('实时预览')
this.previews = data
},
//选择图片
// selectImg (e) {
// let file = e.target.files[0]
// if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
// this.$message({
// message: '图片类型要求:jpeg、jpg、png',
// type: "error"
// })
// return false
// }
// //转化为blob
// let reader = new FileReader()
// reader.onload = (e) => {
// let data
// if (typeof e.target.result === 'object') {
// data = window.URL.createObjectURL(new Blob([e.target.result]))
// } else {
// data = e.target.result
// }
// this.option.img = data
// }
// //转化为base64
// reader.readAsDataURL(file)
// },
//上传图片
uploadImg (type) {
if (type === 'blob') {
//获取截图的blob数据
this.$refs.cropper.getCropBlob(async (data) => {
let formData = new FormData()
formData.append('file', data, "DX.jpg")
//调用axios上传
const res = await api.baseData.uploadFiles(formData)
// let { data: res } = await _this.$http.post('/api/file/imgUpload', formData)
if (res.code === 200) {
this.$message({
type: "success",
message: res.msg
})
// 把最终的裁剪的图片传过去
const obj = {
// name: file.name,
url: res.url,
}
this.$emit('uploadImgSuccess', obj)
// 通知父组件关闭弹窗
this.$emit('closeDialog')
} else {
this.$message({
message: '文件服务异常,请联系管理员!',
type: "error"
})
}
})
}
},
},
created () {
console.log('cropperImage')
this.option.img = this.cropperImage
},
}
</script> <style scoped lang="scss">
.btn {
height: 20px;
line-height: 0.6;
cursor: pointer;
background: #fff;
border: 1px solid #c0ccda;
color: #1f2d3d;
text-align: center;
box-sizing: border-box;
outline: none;
// margin: 20px 10px 0px 0px;
padding: 9px 15px;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #50bfff;
border-color: #50bfff;
transition: all 0.2s ease;
text-decoration: none;
user-select: none;
}
.cropper-content {
display: flex;
display: -webkit-flex;
justify-content: flex-end;
.cropper-box {
flex: 1;
width: 100%;
.cropper {
width: auto;
height: 300px;
}
} .show-preview {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
flex-direction: column;
justify-content: start;
align-items: center;
.preview {
overflow: hidden;
border: 1px solid #67c23a;
background: #cccccc;
}
}
}
.footer-btn {
margin-top: 30px;
display: flex;
display: -webkit-flex;
justify-content: flex-end;
.scope-btn {
display: flex;
display: -webkit-flex;
justify-content: space-between;
padding-right: 10px;
}
.upload-btn {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
}
.btn {
outline: none;
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
-webkit-transition: 0.1s;
transition: 0.1s;
font-weight: 500;
padding: 8px 15px;
font-size: 12px;
border-radius: 3px;
color: #fff;
background-color: #409eff;
border-color: #409eff;
margin-right: 10px;
}
}
</style>

vue2基于 vue-cropper插件对图片裁剪的更多相关文章

  1. 使用cropper插件进行图片裁剪 并上传

    cropper插件的使用和 github地址: github 官方实例 我参考的中文文档: https://www.cnblogs.com/baka-sky/p/8001577.html 因为我是.n ...

  2. node.js平台下,cropper.js实现图片裁剪预览并转换为base64发送至服务端。

    一 .准备工作 1.首先需要先下载cropper,常规使用npm,进入项目路径后执行以下命令: npm install cropper 2. cropper基于jquery,在此不要忘记引入jq,同时 ...

  3. cropper.js实现图片裁剪预览并转换为base64发送至服务端。

    一 .准备工作 1.首先需要先下载cropper,常规使用npm,进入项目路径后执行以下命令: npm install cropper 2. cropper基于jquery,在此不要忘记引入jq,同时 ...

  4. 利用jquery的imgAreaSelect插件实现图片裁剪示例

    http://www.cnblogs.com/mizzle/archive/2011/10/13/2209891.html 将用户上传的图片进行裁剪再保存是现在web2.0应用中常常处理的工作,现在借 ...

  5. vue常用插件之图片预览

    v-viewer(1.4.2) 非常实用的图片预览插件,支持旋转.缩放.翻转等操作 一.npm安装 npm i v-viewer -S 二.全局引入(main.js中) import 'viewerj ...

  6. 基于vue上传base64图片,通过canvas压缩base64

    其实和vue关系不大,和我们之前做上传压缩性质是一样的 当然下面的代码是没有处理ios横屏拍照的bug的 有兴趣的可以多搜一下  网上都有相应的解答 .. var that = this if (e. ...

  7. H5项目 使用Cropper.js 实现图片 裁剪 操作 (APP端)

    参考地址: 1.https://www.jianshu.com/p/b252a7cbcf0b 2.https://blog.csdn.net/weixin_38023551/article/detai ...

  8. 基于Vue的v-charts导出图片并下载

    依赖 npm install file-saver 页面 <ve-chart ref="chart"></ve-chart> <el-button t ...

  9. 基于cropper.js的图片上传和裁剪

    项目中要求图片上传并裁剪的功能,之前也有接触过很多图片裁剪插件,效果体验不是很好,今天推荐一款好用的插件-cropper,超级好用,裁剪功能丰富,满足了各种需求. 功能: 1:点击选择图片,弹出文件夹 ...

  10. struts2+jsp+jquery+Jcrop实现图片裁剪并上传

    <1> 使用html标签上传需要裁剪的大图. <2> 在页面呈现大图,使用Jcrop(Jquery)对大图进行裁剪,并且可以进行预览. <3> 选择好截取部分之后发 ...

随机推荐

  1. ArcGIS for Android入门(Java):ViewPage + Fragment 底部导航栏添加地图

    修改为ViewPage + Fragment 可以参考里面runoob的教程:我也是参考这个修改的:这前面的网上都有很多相应的教程,我觉得这一部分的难点主要是在Fragment里面加载地图: 在fra ...

  2. 【Vue】Re10 Webpack 第二部分(Loader)

    一.Loader解决的问题: Loader解决的问题是让webpack能够打包其他类型的文件 例如CSS.IMG等非JavaScript脚本文件 在后面我们使用Vue组件文件时也需要VueLoader ...

  3. JVM的本地方法栈-通俗理解

    1.本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的, 2.其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的N ...

  4. 代码随想录Day7

    454.四数相加Ⅱ 给你四个整数数组 nums1.nums2.nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <= i, j, k ...

  5. Synology NAS GitLab 配置

    安装 安装的时候会提示服务器名.root用户名等,这步服务器名千万不要写错,不然会登不上去,提示 502. root 密码 网上有很多说 root 密码怎么获取的,但是都不适用. 实际上是第一个访问 ...

  6. JavaScript设计模式样例二十 —— 中介者模式

    中介者模式(Mediator Pattern) 定义:用来降低多个对象和类之间的通信复杂性.目的:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独 ...

  7. Vue状态管理库Pinia详解

    Pinia 是 Vue 的状态管理库,它提供了一种更简单.更不规范的 API 来管理应用的状态.Pinia 的设计哲学是简单性和易用性,它避免了 Vuex 中的许多复杂概念,如 mutations 和 ...

  8. Android 国际化:新增越南语语系(Java)

    前提: 1. 在res文件夹中,新增values-vi文件夹(越南语文件夹) 2. 在步骤1的文件夹中,新增strings.xml 背景: 1. targetSdkVersion 29 2. Jdk ...

  9. 利用分布式锁在ASP.NET Core中实现防抖

    前言 在 Web 应用开发过程中,防抖(Debounce) 是确保同一操作在短时间内不会被重复触发的一种有效手段.常见的场景包括防止用户在短时间内重复提交表单,或者避免多次点击按钮导致后台服务执行多次 ...

  10. 剖析 Redis List 消息队列的三种消费线程模型

    Redis 列表(List)是一种简单的字符串列表,它的底层实现是一个双向链表. 生产环境,很多公司都将 Redis 列表应用于轻量级消息队列 .这篇文章,我们聊聊如何使用 List 命令实现消息队列 ...