100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 封装:el-upload上传图片组件(解决图片闪动 多选问题)

封装:el-upload上传图片组件(解决图片闪动 多选问题)

时间:2019-04-23 11:04:03

相关推荐

封装:el-upload上传图片组件(解决图片闪动 多选问题)

流程描述:使用el-upload组件,自定义上传方法(调后台接口),传图片file给后台,后台返回对应阿里云的oss链接,前端临时保存,最后点击页面提交按钮,再传后台oss数组链接。

项目遇到问题描述&解决

将后台返回的oss直接赋值给el-upload对应的file-list属性绑定值对象的url字段时,会出现闪动问题。--默认组件绑定的url是blob链接,如果替换url取值,就会出现闪动问题。设置multiple多选后,因后台接口限制1秒内不能有重复请求,添加时间戳参数,也会出现上传多张,部分图片的时间戳有的一样的问题,需使用uid(图片文件唯一标志)字段来代替时间戳。

其他待解决问题:(1)粘贴上传图片,不点击输入框,点击粘贴元素所在的行空白处,然后按ctrl+v也会触发粘贴paste事件,即也会自动上传,但此时点击确定所在行,再按ctrl+v又不触发了,失焦导致?

效果图

vue2+TS版实现

<!--* @Description: 上传图片组件(可粘贴上传)* @Version:* @Author: xxx* @Date: -10-11 10:26:13* @LastEditors: Please set LastEditors* @LastEditTime: -10-10 13:33:20--><template><div class="upload-imgs-box"><div class="main-content"><el-uploadref="uploadRef"list-type="picture-card"action="#"accept=".png,.PNG,.jpg,.JPG,jpeg,.JPEG,.bmp,.BMP,.gif,.GIF":class="{'upload__disabled': isDisableUpload}":auto-upload="true"multiple:file-list="allFileList":show-file-list="true":limit="limit":before-upload="handleBeforeUpload":http-request="uploadOSS":on-success="handleSuccess":on-error="handleError":on-remove="handleRemove":on-exceed="handleExceed":on-preview="handlePreview"v-loading="loading"element-loading-text="正在上传中,请稍候..."element-loading-background="rgba(0, 0, 0, 0.5)"><i class="el-icon-plus"></i></el-upload><!-- 显示复制元素 & 手机端 & 上传个数不大于限制时:才显示粘贴图片上传组件 --><div v-if="showCopy && !isMobile && (allFileList.length < limit)" class="copy_wrap mt-10" @paste="handlePaste" :contenteditable="false"><input type="text" ref="copyInputRef" class="copy_main_content" autosize placeholder="点击粘贴图片到此处" maxlength="0"></div></div><el-dialog :visible.sync="showPreviewImage" append-to-body><img width="100%" :src="priviewImageUrl" alt=""></el-dialog></div></template><script lang="ts">import { Vue, Component, Prop, Ref } from 'vue-property-decorator'import { isPC } from '@/utils/index'import { getOSSUrlByFile } from '@/api/common'// element中回调函数中返回的fileexport interface ElFile {name: string // 文件名:如:截屏-12-14 下午8.31.25.pngpercentage: numberraw: Filesize: numberstatus: 'fail' | 'ready' | 'success' // 文件的状态(3种)uid: number // 文件唯一标志:IDurl: string // 如:"blob:http://localhost:9527/27499ca8-6ff1-4fa1-a3de-c64e4d8708e2"oss?: string // 自己附加的属性}// 使用 http-request属性后,默认回调中返回的信息interface HttpRequestInfo {action?: stringfile: File & { uid: number }[key: string]: any}@Component({name: 'UploadImgs'})export default class UploadImgs extends Vue {@Prop({ default: 9 }) private limit!: number // 最多上传个数, 默认是9张@Prop({ default: false }) private showCopy!: boolean // 是否需要展示粘贴图片插件,默认不展示@Prop({ default: 5 }) private maxFileSize!: number // 图片上传的最大文件大小为(单位是兆,M),默认是5M@Ref() copyInputRef!: HTMLElement // 复制图片的inputprivate priviewImageUrl = ''private showPreviewImage = falseprivate disabled = falseprivate allFileList: Partial<ElFile>[] = []private loading = falseget isMobile() {return !isPC()}get isDisableUpload() {return this.allFileList.length === this.limit}// 上传OSS:获取OSS链接private uploadOSS(infoObj: HttpRequestInfo, isCopyUploadFlag: string) {this.loading = trueconst { file } = infoObjconst { uid, name } = fileconst formData = new FormData()formData.append('file', file)formData.append('uid', uid + '') // number类型无法直接赋值给formData类型formData.append('name', name)getOSSUrlByFile(formData).then(res => {if (res.code === 200) {if (isCopyUploadFlag === 'isCopy') {this.copyInputRef.blur() // 移除复制图片框中的光标const { oss } = res.dataoss && this.allFileList.push({ url: oss }) // 因粘贴是自定义实现,即不会触发el-upload组件的任何回调方法,需自行处理。} else {infoObj.onSuccess(res) // 手动触发el-upload组件的on-success回调方法}} else {infoObj.onError(res)}}).finally(() => {this.loading = false})}private handleSuccess(res: { code: number, data: { oss: string } }, file: ElFile, fileList: ElFile[]) {const { oss } = res.dataconsole.log('文件上传成功', res, file, fileList)if (res.code === 200) {file.oss = oss // 注意:此处不能直接将后台返回的oss赋值给file.url字段,会出现闪动现象。this.allFileList = fileList} else {console.log('接口报错非200')}}private handleError() {// 注意:该回调函数必须写,不然:上传失败,页面上还显示图片。写了之后:页面会自动删除失败的图片。console.log('文件上传失败')}private handleRemove(file: ElFile, fileList: ElFile[]) {this.allFileList = fileListconsole.log('文件删除了', this.allFileList, file, fileList)}// 上传前:钩子函数private handleBeforeUpload(file: File) {const { size } = fileconst isLessLimitMaxFileSize = size / 1024 / 1024 < this.maxFileSize // 将文件大小转为M单位,并跟最大限制做比较if (!isLessLimitMaxFileSize) {this.$message.warning(`上传图片大小,不能超过 ${this.maxFileSize}M!`)return false} else {return true}}// 文件勾选超出限制private handleExceed(files: File[], fileList: ElFile[]) {const emptyNum = this.limit - (fileList.length)console.log('目前还能上传个数为', emptyNum)this.$message.warning(`上传文件个数,超出限制(最多${this.limit}张)`)}// 图片粘贴触发事件private handlePaste(event: any) {const items = (event.clipboardData || (window as any).clipboardData).items// 去除粘贴到div事件event.preventDefault()event.returnValue = falselet file = nullif (!items || items.length === 0) {this.$message.warning('当前不支持本地图片上传')return}// 搜索剪切板itemsfor (let i = 0; i < items.length; i++) {if (items[i].type.includes('image')) {file = items[i].getAsFile()break}}if (!file) {this.$message.warning('粘贴内容非图片')return}if (!this.handleBeforeUpload(file)) {return false}this.uploadOSS({ file: file }, 'isCopy')}// 预览:当前图片private handlePreview(file: ElFile) {this.priviewImageUrl = file.urlthis.showPreviewImage = true}}</script><style lang="scss" scoped>.upload-imgs-box {.copy_wrap {width: 148px;height: 148px;text-align: center;.copy_main_content {width: 148px;height: 148px;text-align: center;border: 1px dashed #c0ccda;border-radius: 6px;outline-color: #409EFF; // 设置input点击后出现的边框的颜色}}.upload__disabled {::v-deep .el-upload--picture-card {display: none; // 隐藏上传组件}}}</style>

父组件中使用

<template><div><upload-imgs ref="upload_image_box" :showCopy="false"/><el-row class="upload__tip">最多上传9张,只支持jpg、png、jpeg、bmp或gif文件,且单个图片不能超过5M。</el-row><el-button type="primary" @click="submit">确定</el-button>...</div></template><script lang="ts">... // 省略codeinterface UploadImageComponent {allFileList: Partial<ElFile>[] // 最后上传的所有图片数组[{ url: 'http:xxx', ... }, { oss: 'http:xx', ... }]}@Ref('upload_image_box') readonly uploadImageCompoent!: UploadImageComponentprivate submit() {const { allFileList = [] } = this.uploadImageCompoent... // 省略codeconst imagesList: string[] = []allFileList.forEach((item: Partial<ElFile>) => {if (item.oss) {imagesList.push(item.oss) // 通过el-uplod上传的} else if (item.url) {imagesList.push(item.url) // 通过复制粘贴上传的}})// 最后提交给后台的参数:files为所有oss图片链接的数组字段。const params = { files: imagesList, .... }... // 调后台接口}</script>

其他涉及代码

// 判断是否是PC端显示export const isPC = () => {const userAgentInfo = navigator.userAgentconst Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']let isPCFlag = truefor (let v = 0; v < Agents.length; v++) {if (userAgentInfo.indexOf(Agents[v]) > 0) {isPCFlag = falsebreak}}return isPCFlag}// 上传File文件获取OSS链接(仅单个,不支持批量!)export const getOSSUrlByFile = (data = {}) => {return request({ // axios库相关封装的实现,注意需判断formdata类型的post请求不能序列化。url: 'xxxxxxxx',headers: {'Content-Type': 'multipart/form-data'},method: 'post',data})}// 上传类接口(传参为FormData类型的):不能序列化,否则会被序列化为空对象!... // axios请求拦截器if (request.method === 'post' && request.data) {if (!(request.data instanceof FormData)) {request.data = qs.stringify(request.data)}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。