element-ui上传列表自定义实现,上传错误重传等公共组件
为什么需要自定义实现
当然是原生的element-ui不满足
展示的列表,elementui里面的请求错误的,不会展示在列表里面
element判断上传成功和失败是通过最外层的服务端返回的状态,而对于我们公司的后端,不论是成功还是失败都是返回的200,在返回值里面在封装了一层code来反应真正的成功或者失败,200表示成功,-1表示失败
实现的功能
会标明哪些上传成功和哪些是上传失败的
上传失败的可以重新上传
上传失败有错误提示,相同的错误提示只会提示一次
可以调节一排显示的个数
由于时间比较紧张,代码可能有一点乱,后期有时间在优化 其中用到的前端后端代码都有
后端代码
这里你可以随意返回,我是根据公司这边需求来做的 启动服务
node ./app.js 复制代码
// app.js const express = require('express') const app = express() const path = require('path') //引入multer const multer = require('multer') //注册一个对象,dest里放的是上传的文件存储的位置,可以在当前目录下,建立一个static目录,上传的文件都放在这里 const upload = multer({dest: './static/'}) //使用中间件,没有挂载路径,应用的每个请求都会执行该中间件。any表示接受一切,具体参考文档。 app.use(upload.any()) let errorNum = 0 //在req.files中获取文件数据 app.post('/api/upload',function(req, res){ const { originalname } = req.files[0] console.log(originalname, req.files) if (path.extname(originalname) === '.pdf') { res.send({ code: '-1', message: '上傳格式不能是txt和pdf' }) } else if(path.extname(originalname) === '.txt' && errorNum < 5) { errorNum++ res.status(500).send({ code: '500', message: '上传失败' }) } else { res.send({ code: '200', message: '成功' }) } }) app.listen(3000) 复制代码
前端组件的封装
// CommonUpload/index.vue <template> <div> <el-upload :show-file-list="false" :on-success="handleSuccess" v-bind="$attrs" v-on="$listeners" :on-progress="handleProgress" :on-error="handleError" ref="upload-outer" > <slot> <el-button size="small" type="primary">点击上传</el-button> </slot> </el-upload> <UploadList :file-list="fileList" @refreshUpload="refreshUpload" @handleDelete="handleDelete" ></UploadList> </div> </template> <script> import UploadList from '@/components/CommonUpload/UploadList'; export default { name: 'CommonUpload', components: { UploadList }, props: { attachList: { type: Array, default: () => [] } }, data() { return { fileList: [], errTipsMap: {}, refreshUploadFileInfo: { // 主要是为了上传失败,上传失败过后位置会发生变化 file: '', index: 0, progressing: false // 是否在重新上传 } } }, watch: { attachList: { handler(newVal) { this.fileList = newVal }, immediate: true, deep: true }, fileList: { handler(newVal) { this.$emit('update:attachList', newVal) }, immediate: true, deep: true } }, methods: { changeResponseStatus(fileList) { fileList.forEach(item => { if ((item.status !== 'success' || item.status !== 'fail') && item.response && item.response.code !== '200') { item.status = 'fail' } }) return fileList }, // 成功回调函数 handleSuccess(response, file, fileList) { this.fileList = this.changeResponseStatus(fileList) // 这里给出错误提示 const statusList = this.fileList.filter(item => ['success', 'fail'].includes(item.status) && item.response) const isAllEnd = statusList.length === fileList.length // 错误提示等全部上传完了,在给出提示,并且去除重复的提示 if (isAllEnd) { const errFileList = this.fileList.filter(item => item.status === 'fail') const errMessageList = [] errFileList.forEach(item => { if (!this.errTipsMap[item.uid]) { errMessageList.push(item.response.message) this.errTipsMap[item.uid] = true } }) // 错误信息去重 const noRepeatMsgList = [...new Set(errMessageList)] if (noRepeatMsgList.length) { this.$message.error(noRepeatMsgList.join(',')) } } }, isRefreshUpload() { if (this.refreshUploadFileInfo.progressing) { return true } }, handleError(err, file, fileList) { if (file.status !== 'fail') { return } if (this.isRefreshUpload()) { this.refreshUploadFileInfo.progressing = false this.fileList.splice(this.refreshUploadFileInfo.index, 0, file) } else { this.fileList.push(file) } this.errTipsMap[file.uid] = true this.$message.error(file.response?.message || '上传失败啦~~') }, handleProgress(event, file, fileList) { this.fileList = this.changeResponseStatus(fileList) }, refreshUpload(file) { this.errTipsMap[file.uid] = false this.refreshUploadFileInfo = { file: file, index: this.fileList.findIndex(item => item.uid === file.uid), progressing: true } this.$refs['upload-outer'].$refs['upload-inner'].upload(file.raw) }, handleDelete(file) { const delInd = this.fileList.findIndex(item => item.uid === file.uid) this.fileList.splice(delInd, 1) } } } </script> <style scoped> </style> 复制代码
// CommonUpload/UploadList <template> <div> <div v-for="file in fileList" :key="file.uid" > <div> <i class="el-icon-tickets file-prev-icon"></i> <div :title="file.name" > <span>{{ file.name }}</span> </div> <div> <div> <div v-if="file.status === 'uploading'">{{ parsePercentage(file.percentage) + '%' }}</div> <div v-else-if="file.status === 'success'"> <i></i> </div> <div v-else-if="file.status === 'fail'"> <i></i> </div> </div> <div> <i v-if="file.status === 'fail'" @click="refreshUpload(file)"></i> <i @click="handleDelete(file)"></i> </div> </div> </div> <el-progress v-if="file.status === 'uploading'" :type="'line'" :stroke-width="2" :percentage="parsePercentage(file.percentage)" :show-text="false" > </el-progress> </div> </div> </template> <script> export default { name: 'UploadList', props: { fileList: { type: Array, default: () => [] } }, methods: { parsePercentage(val) { return parseInt(val, 10); }, refreshUpload(file) { this.$emit('refreshUpload', file) }, handleDelete(file) { this.$emit('handleDelete', file) } } } </script> <style scoped> .upload-list__item { height: 32px; display: flex; align-items: center; position: relative; font-size: 12px; color: #2f2725; overflow: hidden; } .upload-list__item-name { display: flex; align-items: center; padding: 0 12px; height: 100%; width: 100%; } .file-after-icon, file-prev-icon, hover-delete { flex-shrink: 0; } .after-icon-box { width: 40px; text-align: end; } .file-name { flex: 1; margin-right: 10px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } .file-prev-icon { margin-right: 10px; } .progress-box { position: absolute; left: 0; bottom: 0; width: 100%; } .hover-delete { display: none; .el-icon-close { color: #c1cfe0; cursor: pointer; } .el-icon-refresh-right { margin-right: 5px; cursor: pointer; } } .upload-list__item-name:hover { background: #eff7ff; .file-after-icon { display: none; } .hover-delete { display: block; } } .file-after-icon { font-size: 12px; .el-icon-success { color: #7cd4ab; } .el-icon-error { color: #f66969; } } .upload-list__container { display: grid; grid-template-columns: 1fr 1fr; grid-column-gap: 20px; } </style> 复制代码
测试代码
<template> <div> <div>jobTest</div> <div style="width: 800px;"> <CommonUpload action="/api/upload" multiple ref="upload-outer" :attach-list.sync="attachList" > </CommonUpload> </div> </div> </template> <script> import CommonUpload from '@/components/CommonUpload' export default { name: 'JobTest', components: { CommonUpload }, data() { return { // 附件列表格式 attachList: [ // { // uid: 1000, // name: 'vueyuanma', // raw: 'file', // response: null, // status: 'success' // }, // { // uid: 1001, // name: '哈哈', // raw: 'file', // response: null, // status: 'success' // } ] } }, methods: {} } </script> <style scoped> </style>
作者:Abby不想说话46050
链接:https://juejin.cn/post/7054181880521818142