背景
不知道你上傳圖片的時候有沒有過這樣的情況,批量上傳多張圖片,可能因為圖片大小或者網絡問題,導致圖片返回的順序和上傳時的順序不一樣。因為我們公司是做電商的,即使我們的支持拖動排序,運營還是希望圖片能夠嚴格的按照他們上傳的順序展示。
解決問題
在上傳組件的on-success的方法中,有3個參數 response, file, fileList 其中fileList就是之前上傳成功圖片的集合,且upload組件提供了clearFiles方法,用來清空fileList,每次上傳成功,我們調用clearFiles方法就行了
上代碼
<template> <!-- 上傳單張圖片 --> <div v-if="!multiple" class="image-item"> <div class="image-wrap" v-if="imgUrl"> <img :src="imgUrl" :style="imgStyle" /> <div class="icon-wrap" @click.stop="removeFile"> <i class="el-icon-delete"></i> </div> </div> <el-upload v-else ref="imageUpload" action="//up.qbox.me" :before-upload="beforeUpload" :on-success="handleSuccess" class="image-uploader" :on-error="onError" :data="form" :show-file-list="false" :disabled="loading" accept="image/*"> <i :class="loading ? 'el-icon-loading' : 'el-icon-plus'" :style="imgStyle"></i> </el-upload> </div> <!-- 上傳多張圖片 --> <div class="image-list" v-else> <draggable v-model="showImgList" :options="{group:'image'}" @change="dragChange"> <div v-for="(image, index) in showImgList" :key="index" class="image-wrap"> <img :src="imgUrl" :style="imgStyle" /> <div class="icon-wrap" @click.stop="removeFile(index)"> <i class="el-icon-delete"></i> </div> </div> <el-upload ref="imageListUpload" action="//up.qbox.me" :before-upload="beforeUpload" :on-success="handleSuccess" class="image-uploader" :on-error="onError" :data="form" multiple :disabled="loading" :show-file-list="false" accept="image/*"> <i :class="loading ? 'el-icon-loading' : 'el-icon-plus'" :style="imgStyle"></i> </el-upload> </draggable> </div> </template> <script type="text/babel"> /** * 上傳圖片或文件 */ import md5 from 'blueimp-md5' import draggable from 'vuedraggable' export default { props: { // 接收和返回的數據 data: { type: [Array, String, Object], default: () => { return '' } }, // 上傳多個文件時,文件限制的個數 limit: { type: Number, default: () => { return 100 } }, // 一次上傳多個 multiple: { type: Boolean, default: false, }, //圖片展示的寬度 imgWidth: { type: Number, default: 150, }, imgHeight: { type: Number, default: 150, }, //期望上傳圖片的寬度 rule: [ Object, Function ] }, data() { return { imgUrl: '', imageCdn: '', //圖片的cdn form: { token: '', //七牛上傳的token }, showImgList: [], fileList: [], clipboard: false, isDrag: false, handleSuccess: null, loading: false, } }, components: { draggable }, watch: { data: { handler(value) { if (!this.multiple) { this.imgUrl = value } else if (this.multiple) { this.showImgList = value } }, immediate: true } }, computed: { imgStyle() { return { width: this.imgWidth + 'px', height: this.imgHeight + 'px', lineHeight: this.imgHeight + 'px', } } }, mounted() { //防抖 this.handleSuccess = _.debounce(this.uploadSuccess, 500) }, methods: { beforeUpload(file) { if (file.type.split('/')[0] === 'image') { let tempSize = file.size / 5242880 if (tempSize > 1) { this.$message.error('圖片尺寸不得大於5M!') return false } } this.loading = true let tempNames = file.name.split('.') let fileType = tempNames[tempNames.length - 1] let curr = (+new Date()).toString() let random = Math.random() * 10000 let md5Str = md5(`${curr}${random}${file.name}`) this.form.key = `ai-admin/${md5Str}.${fileType}` }, async uploadSuccess(response, file, fileList) { try { for (let fileInfo of fileList) { let imageInfo = await this.getImageInfo(fileInfo.response.key) if (this.rule) { this.rule(imageInfo, (error) => { if (error) { throw(error) } }) } if (imageInfo.width > 2048 || imageInfo.height > 2048) { throw(new Error('圖片長或者寬不能超過2048')) } else { if (this.type === 'image') { this.imgUrl = response.key this.$emit('update:data', response.key) } else { if (this.showImgList.length >= this.limit) { // 限制圖片張數 this.showImgList.length = this.limit throw(new Error(`最多上傳 ${this.limit} 張圖片`)) } this.showImgList.push(imageInfo) this.$emit('update:data', this.showImgList) } } } } catch (error) { this.$message.error(error.message) } finally { this.loading = false this.$refs.imageListUpload && this.$refs.imageListUpload.clearFiles() this.$refs.imageUpload && this.$refs.imageUpload.clearFiles() } }, removeFile(index) { this.$confirm('確定刪除該圖片嗎?', '提示', { confirmButtonText: '確定', cancelButtonText: '取消', type: 'warning' }).then(() => { if (this.type === 'image') { this.$emit('update:data', typeof this.data === 'object' ? {} : '') } else { this.showImgList.splice(index, 1) this.$emit('update:data', this.showImgList) } }) }, onError() { this.$message.error('上傳文件失敗') }, getImageInfo(url){ return new Promise((resolve, reject)=>{ let image = new Image() image.src = `${this.imageCdn}${url}` image.onload = () => { resolve({ image: url, width: image.width, height: image.height }) } image.onerror = () => { reject(new Error('Could not load image at ' + url)); }; }) }, dragChange() { this.$emit('update:data', this.showImgList) }, handleRemove(file, fileList) { let imgList = fileList.map(item => { return item.response.key }) this.$emit('update:data', imgList) }, handlerClipboard(event) { if (this.clipboard) { const rawFile = getImageFromClipboard(event) if (rawFile) { this.$refs.elUpload.handleStart(rawFile) this.$refs.elUpload.$refs['upload-inner'].upload(rawFile) } } }, } } </script> <style lang="less" scoped> .image-list, .image-item { display: flex; .image-wrap { position: relative; display: inline-block; box-sizing: content-box; margin: 0 8px 8px 0; vertical-align: top; &:hover { .icon-wrap { opacity: 1; } } .icon-wrap { position: absolute; left: 0; bottom: 0; width: 100%; height: 30px; cursor: default; text-align: center; color: #fff; opacity: 0; font-size: 20px; background-color: rgba(0, 0, 0, .7); transition: opacity .3s; .el-icon-zoom-in { cursor: pointer; margin-right: 8px; } .el-icon-delete { cursor: pointer; } } } } .image-item { display: inline-flex; } /deep/.image-uploader { display: inline-block; .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; [class^="el-icon"] { font-size: 28px; color: #8c939d; text-align: center; } &:hover { border-color: #409EFF; } } } </style>
注意
這是我封裝的上傳圖片的組件,支持 類v-model的傳參方式,上傳多張圖時支持拖動,寫圖片規則(例如寬高是多少),刪除圖片
特別注意:因為我們還封裝了圖片組件,在這里被我替換了,所以圖片展示有上面的代碼可能有點問題,稍微改下就行
用法(只允許上傳正方形的圖)
<template> <image-upload :data.sync="image" :rule="rule"></image-upload> </template> <script> export default { data(){ let validate = ({ width, height }, callback) => { if (width === height) { callback() } else { callback(new Error('請上傳正方形的圖')) } } return { rule: validate, image: '' } }, } </script>
效果