前端最终规范


带上传进度的上传功能

<pre><code class="language-markdown">&lt;template&gt; &lt;div class="app-container"&gt; &lt;div&gt; &lt;div class="font-family-h font-bold c-fc-333 margin-b title"&gt;&lt;i class="square"&gt;&lt;/i&gt;{{curName}}内容&lt;/div&gt; &lt;/div&gt; &lt;div v-show="isDefault" class="relative c-width100 height120 border-radio4 c-ph20 c-pv10 border c-bg-fa"&gt; &lt;div class="vhcenter pointer" style="height:100%;line-height:118px;"&gt; &lt;!-- &lt;div class="vhcenter pointer"&gt; --&gt; &lt;synchronize-video ref="uploader" class="drag-upload" v-if="curName == '视频'" btnType="text" btnCss="c-fc-60 " btnText="上传视频" :maxSize="2" :isDrag="true" :multiple="true" :reupload="true" :limit="10" @beforeUpload="beforeUploadVideo" @onProgress="getProgress" @getData="submitVideoSuccess" @submitVideoError="submitVideoError"&gt; &lt;/synchronize-video&gt; &lt;el-upload drag v-else class="fl drag-upload" ref="avatarUpload" name="file" :action="url" limit="10" :on-exceed="onexceed" list-type="picture" :onChange="change" :data="uploadData" :before-upload="beforeUpload" multiple="true" :on-success="handleSuccess" :show-file-list="false" :auto-upload="true" :on-progress="progress" style="width: 100%;height:100%;" &gt; &lt;!-- &lt;el-button slot="trigger" size="mini" type="primary"&gt;选取文件&lt;/el-button&gt; --&gt; &lt;div slot="trigger" &gt; &lt;img src="@/assets/icon/upload.png" class="width40 height40"&gt;&lt;span&gt;上传{{curName}}&lt;/span&gt; &lt;/div&gt; &lt;/el-upload&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="Mplupload-box"&gt; &lt;div v-for="(item,index) in data" :key="index" class="relative c-width100 height120 border-radio4 c-ph20 c-pv10 border c-bg-fa margin-b10"&gt; &lt;section class="clearfix"&gt; &lt;span class="fl c-text-ellipsis1 c-line-30 margin-r10" style="max-width:60%;"&gt;{{item.name}}&lt;/span&gt; &lt;span class="fl c-line-30 c-fc-sgray"&gt;{{item.msize|byteTransform}}&lt;/span&gt; &lt;span class="fr c-line-30 c-fc-fred" v-if="item.uploadStatus"&gt;上传失败&lt;/span&gt; &lt;span class="fr c-line-30 c-fc-blue" v-else&gt;{{item.percent|Datastatus}}{{item.analysis|anastatus}}&lt;/span&gt; &lt;/section&gt; &lt;section&gt; &lt;p class="upload-progress"&gt; &lt;span class="upload-bar c-bg-blue" :style="'right:'+(100 - item.percent)+'%'"&gt;&lt;/span&gt; &lt;!-- &lt;span class="upload-bar c-bg-blue" :style="'width:'+item.percent+'%'"&gt;&lt;/span&gt; --&gt; &lt;/p&gt; &lt;/section&gt; &lt;section class="c-line-30 clearfix"&gt; &lt;span class="fl"&gt;已上传:{{item.curSize|byteTransform}}/{{item.msize|byteTransform}}&lt;/span&gt; &lt;!-- {{item.speed}} --&gt; &lt;span class="fr c-fs-12 c-fc-666" v-if="item.uploadErrorTip"&gt;失败原因:{{item.uploadErrorTip}}&lt;/span&gt; &lt;/section&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="c-fs-14 c-line-30 c-fc-sgray"&gt; {{ruleContent}} &lt;/div&gt; &lt;div class="center" v-if="isComplete||allComplete"&gt; &lt;el-button class="btn-item margin-t30 margin-b30" style="" type="primary" v-loading.fullscreen="btnLoading" @click="back"&gt;保存 &lt;/el-button&gt; &lt;/div&gt; &lt;b&gt;&lt;/b&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { imageSign, audioCallBack, saveVideo } from '@/api/materials' import { convertTxVodFile } from '@/api/courseDetail' import { getUploadToken } from '@/api/common' import synchronizeVideo from '@/views/templatePage/uploadVideo/synchronizeVideo.vue' import { randomString } from '@/utils/index' import { byteTransform } from '@/utils/index' export default { name: 'upload', components: { synchronizeVideo }, data() { return { nameList: { 'Image': '图片', 'Audio': '音频', 'Video': '视频' }, curName: '', // imgSignData: {}, url: '', isShowMaterial: false, // 选择素材弹窗 isDefault: true, // 进入页面默认显示的模块 isComplete: false, // 上传完成弹窗 data: [], ruleContent: '', // 规则内容 audioData: { token: null }, uploadData: { }, // 图片直传的参数 uploadParams: { name: '', policy: '', OSSAccessKeyId: '', success_action_status: '200', callback: '', signature: '' }, fId: -1, FideDir: '', // 图片服务器路径 uploadCount: 0, // 设置上传的索引 uploadIndex: {}, // 上传的索引 countFinsh: 0 // 上传完成的个数 } }, watch: { $route: function(to, from) { this.curName = '' // imgSignData: {}, this.url = '' this.data = [] this.isDefault = true // 进入页面默认显示的模块 this.isComplete = false this.ruleContent = '' // 规则内容 this.audioData = { token: null } this.uploadData = { } // 图片直传的参数 this.uploadParams = { name: '', policy: '', OSSAccessKeyId: '', success_action_status: '200', callback: '', signature: '' } this.FideDir = '' // 图片服务器路径 this.uploadCount = 0 // 设置上传的索引 this.uploadIndex = {} // 上传的索引 this.countFinsh = 0 // 上传完成的个数 this.init() } }, mounted() { this.init() console.log(111) console.log(this.$route, 9900) }, computed: { allComplete() { // 上传个数等于 完成个数 console.log(this.uploadCount, this.countFinsh) if (this.uploadCount == this.countFinsh &amp;&amp; this.countFinsh != 0) { return true } return false } }, filters: { Datastatus(val) { if (val &lt; 0) { return '' } if (val == 0) { return '正在解析中' } if (val &lt; 100) { return (val + '%') } if (val &gt;= 100) { return '已完成' } }, anastatus(val) { if (val &lt; 0) { return '' } if (val == 0) { return '' } if (val &lt; 100) { return ('进度' + val + '%') } }, byteTransform // (limit) { // let size = '' // if (limit &lt; 1 * 1024) { // 小于0.1KB,则转化成B // size = limit.toFixed(2) + 'B' // } else if (limit &lt; 1 * 1024 * 1024) { // 小于1MB,则转化成KB // size = (limit / 1024).toFixed(2) + 'KB' // } else if (limit &lt; 1 * 1024 * 1024 * 1024) { // 小于1GB,则转化成MB // size = (limit / (1024 * 1024)).toFixed(2) + 'MB' // } else { // 其他转化成GB // size = (limit / (1024 * 1024 * 1024)).toFixed(2) + 'GB' // } // var sizeStr = size + '' // 转成字符串 // var index = sizeStr.indexOf('.') // 获取小数点处的索引 // var dou = sizeStr.substr(index + 1, 2) // 获取小数点后两位的值 // if (dou == '00') { // 判断后两位是否为00,如果是则删除00 // return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2) // } // return size // } }, methods: { // /** -----------新版分割线-------- */视频上传 beforeUploadVideo({ file }) { this.isDefault = false this.data.push({ name: file.name, percent: 0, msize: file.size, analysis: 0, curSize: 0 }) this.uploadCount++ }, getProgress(data) { this.data[data.count].percent = Math.floor(data.percent * 100) this.data[data.count].uploadStatus = false this.data[data.count].uploadErrorTip = '' this.data[data.count].curSize = this.data[data.count].percent &gt; 0 ? this.data[data.count].msize * this.data[data.count].percent * 0.01 : 0 }, submitVideoSuccess(val) { var vm = this vm.countFinsh++ const data = val[0] const params = { resId: data.doneResult.fileId, resName: data.file.name, resUrl: data.doneResult.video.url, resType: 1, categoryId: vm.$route.params.fId } saveVideo(params).then((res) =&gt; { // 转码 convertTxVodFile(params.resId, { type: 'resLibrary' }).then(res =&gt; { }) }) }, submitVideoError(err, { uploadCount }) { // this.data.forEach(el =&gt; { // if (el.percent &lt; 100) { // el.uploadStatus = '上传失败' // el.uploadErrorTip = err // } // }) this.$set( this.data[uploadCount], 'uploadStatus', '上传失败' ) this.$set( this.data[uploadCount], 'uploadErrorTip', err ) this.$set(this.data, this.data, false) this.$message.error('上传失败的原因:' + err) }, /** -----------新版分割线-------- */ handleAvatarBeforeUpload() {}, onexceed() { this.$message({ type: 'info', message: '最多上传个数为10个' }) }, handleSuccess(res, file, filelist) { if (this.$route.params.type == 'Audio') { const audioUrl = res.hash audioCallBack({ audioName: file.name, audioUrl, fId: this.fId }).then((res) =&gt; { // if (res.data.statusCode == 200) { // } }).catch(() =&gt; { this.$message({ message: '音频入库失败!', type: 'error' }) }) } let countUnUploaded = 0 // 未上传个数 console.log(filelist) filelist.filter((item, index) =&gt; { if (item.status == 'uploading') { countUnUploaded += 1 } }) if (!countUnUploaded) { this.isComplete = true } }, // handleError(err, file, filelist) { // }, customRequest(data) { // axios.post(data.action,this.imgSignData) }, change(file, filelist) { console.log(filelist, 7878) }, beforeUpload(file) { // 上传规则限制 if (!this.rule(file)) { return false } this.isDefault = false if (this.$route.params.type == 'Image') { this.imgUpload(file) this.uploadIndex[file.uid] = this.uploadCount++ console.log(this.uploadIndex[file.uid]) } if (this.$route.params.type == 'Audio') { this.url = 'https://upload.qiniup.com/' this.getQiniuToken() if (!this.audioData.token) { this.$message({ type: 'info', message: '获取七牛信息失败请刷新页面重试' }) return } this.data.push({ msize: file.size, name: file.name, percent: 0, curSize: 0 }) this.uploadIndex[file.uid] = this.uploadCount++ console.log(this.uploadIndex[file.uid]) } if (this.$route.params.type == 'Video') { return false } }, progress(event, file) { if (this.$route.params.type == 'Video') { return false } else { const index = this.uploadIndex[file.uid] this.data[index].percent = (event.loaded / event.total * 100).toFixed(2) this.data[index].curSize = (file.size * this.data[index].percent * 0.01).toFixed(0) } }, imgUpload(file) { const index = file.name.lastIndexOf('.') const preFilename = file.name.slice(0, index).replace(/\s/g, '') // 图片名字 const filetype = file.name.slice(index) // 图片类型 this.uploadData.key = this.FideDir + preFilename + '_' + randomString(20) + filetype this.uploadData.name = file.name this.data.push({ msize: file.size, name: file.name, percent: 0, curSize: 0 }) }, init() { this.curName = this.nameList[this.$route.params.type] this.fId = this.$route.params.fId if (this.$route.params.type == 'Image') { this.getImgSign() this.ruleContent = ` 支持批量上传(单次最多10个),只支持JPG、PNG、GIF格式,文件大小建议不超过3M;文件名称不超过100个字; 上传过程中,请勿关闭页面,否则上传失败; ` } if (this.$route.params.type == 'Audio') { this.url = 'https://upload.qiniup.com/' this.$nextTick(() =&gt; { this.getQiniuToken() }) this.ruleContent = ` 支持批量上传(单次最多10个),只支持MP3格式,文件大小建议不超过300M; 上传过程中,请勿关闭页面,否则上传失败; ` } if (this.$route.params.type == 'Video') { this.url = 'https://upload.qiniup.com/' // this.getQiniuToken(); this.ruleContent = ` 支持批量上传(单次最多10个),只支持MP4格式,建议用chrome或者360极速模式浏览器上传; 单个文件大小建议不超过1G,文件名请勿包含特殊字符(#^;&lt;&gt;); 上传过程中,请勿关闭页面,否则上传失败; ` } }, getImgSign() { imageSign({ fId: this.fId }).then((res) =&gt; { if (res.status == 200) { const curData = res.data.data this.url = curData.host this.uploadData = { 'key': '', 'policy': curData.policy, 'OSSAccessKeyId': curData.accessid, 'success_action_status': '200', // 让服务端返回200,不然,默认会返回204 'callback': curData.callback, 'signature': curData.signature } this.FideDir = curData.dir } }).catch((_) =&gt; { }) }, getQiniuToken() { getUploadToken().then(res =&gt; { this.audioData.token = res.data.data this.uploadData.token = res.data.data }).catch(err =&gt; { console.log(err) }) }, emptySize(file, name) { if (file.size == 0) { this.$message.error(`上传的${name}内容不存在`) return true } return false }, rule(file) { // 上传规则 if (this.$route.params.type == 'Image') { // 封面选择文件判断 const isFu = /[\/]|[\?]|[\:]|[\*]|[\"]|[&lt;]|[&gt;]|[,]|[']|[\+]|[\#]|[%]/.test(file.name) if (isFu) { this.$message({ message: `文件名不得包含 / : * ? " &lt; &gt; , ' + # %等特殊字符`, type: 'warning' }) return false } const isImg = file.type == 'image/jpeg' || file.type == 'image/png' || file.type == 'image/gif' if (!isImg) { this.$message.warning('上传图片只能是 JPG , PNG 或者 GIF 格式') return false } if (this.emptySize(file, '图片')) { return false } const isLt3M = file.size / 1024 / 1024 &lt; 3 if (!isLt3M) { this.$message.error('上传图片大小不能超过 3MB!') return false } const isLength = file.name.length &lt;= 100 if (!isLength) { this.$message.error('图片名称不能超过 100 个字!') return false } return isImg &amp;&amp; isLt3M &amp;&amp; isLength } if (this.$route.params.type == 'Audio') { // 封面选择文件判断 const isImg = file.type == 'audio/mp3' || file.type == 'audio/mpeg' if (!isImg) { this.$message.warning('上传音频只能是MP3格式') return false } if (this.emptySize(file, '音频')) { return false } const isLt3M = file.size / 1024 / 1024 &lt; 300 if (!isLt3M) { this.$message.error('上传音频大小不能超过 300M!') return false } return isImg &amp;&amp; isLt3M } if (this.$route.params.type == 'Video') { // 封面选择文件判断 const isImg = file.type == 'video/mp4' if (!isImg) { this.$message.warning('上传视频只能是MP4格式') return false } if (this.emptySize(file, '视频')) { return false } const isLt3M = file.size / 1024 / 1024 &lt; 2048 if (!isLt3M) { this.$message.error('上传视频大小不能超过 2GB!') return false } const isFu = /[\/]|[\?]|[\:]|[\*]|[\"]|[&lt;]|[&gt;]|[,]|[']/.test(file.name) if (isFu) { this.$message({ message: `视频文件名不得包含 / : * ? " &lt; &gt; , ' 等字符`, type: 'warning' }) return false } return isImg &amp;&amp; isLt3M &amp;&amp; !isFu } }, back() { this.$store.dispatch('delVisitedViews', this.$route).then((views) =&gt; { this.$router.push({ name: 'materialBank' }) }) } } } &lt;/script&gt; &lt;style scoped lang="scss"&gt; .vhcenter { position: absolute; top:50%; left:50%; width:100%; transform:translate(-50%,-50%); text-align: center; img { vertical-align: middle; } } .Mplupload-box { max-height: 600px; overflow: auto; } .upload-progress { height: 8px; background-color: #ccc; margin: 14px 0; border-radius: 2px; position: relative; .upload-bar { position: absolute; // transition: all 0.67s; height: 8px; border-radius: 2px; left: 0; } } &lt;/style&gt; &lt;style&gt; .vhcenter .el-upload { width: 100% !important; } .drag-upload .el-upload-dragger { width: 100%; height: 100%; background: #fafafa; border: 0; } &lt;/style&gt; </code></pre>

页面列表

ITEM_HTML