前端最终规范


带上传进度的上传功能

<template>
    <div class="app-container">
        <div>
            <div class="font-family-h font-bold c-fc-333 margin-b title"><i class="square"></i>{{curName}}内容</div>
        </div>
        <div v-show="isDefault" class="relative c-width100 height120 border-radio4 c-ph20 c-pv10 border c-bg-fa">

            <div class="vhcenter pointer" style="height:100%;line-height:118px;">
            <!-- <div class="vhcenter pointer"> -->
                <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">
                </synchronize-video>
                <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%;"  >
                  <!-- <el-button slot="trigger" size="mini" type="primary">选取文件</el-button> -->
                  <div slot="trigger" >
                      <img src="@/assets/icon/upload.png" class="width40 height40"><span>上传{{curName}}</span>
                  </div>
                </el-upload>
            </div>
        </div>
        <div class="Mplupload-box">
            <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">
                <section class="clearfix">
                    <span class="fl c-text-ellipsis1 c-line-30 margin-r10" style="max-width:60%;">{{item.name}}</span>
                    <span class="fl c-line-30 c-fc-sgray">{{item.msize|byteTransform}}</span>
                    <span class="fr c-line-30 c-fc-fred" v-if="item.uploadStatus">上传失败</span>
                    <span class="fr c-line-30 c-fc-blue" v-else>{{item.percent|Datastatus}}{{item.analysis|anastatus}}</span>
                </section>
                <section>
                    <p class="upload-progress">
                        <span class="upload-bar c-bg-blue" :style="'right:'+(100 - item.percent)+'%'"></span>
                        <!-- <span class="upload-bar c-bg-blue" :style="'width:'+item.percent+'%'"></span> -->
                    </p>
                </section>
                <section class="c-line-30 clearfix">
                  <span class="fl">已上传:{{item.curSize|byteTransform}}/{{item.msize|byteTransform}}</span>
                     <!-- {{item.speed}} -->
                  <span class="fr c-fs-12 c-fc-666" v-if="item.uploadErrorTip">失败原因:{{item.uploadErrorTip}}</span>
                </section>
            </div>

        </div>
        <div class="c-fs-14 c-line-30 c-fc-sgray">
            {{ruleContent}}
        </div>
        <div class="center" v-if="isComplete||allComplete">
            <el-button class="btn-item margin-t30 margin-b30" style="" type="primary" v-loading.fullscreen="btnLoading"
               @click="back">保存
            </el-button>
        </div>

        <b></b>
    </div>
</template>

<script>

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 && this.countFinsh != 0) {
        return true
      }
      return false
    }
  },
  filters: {
    Datastatus(val) {
      if (val < 0) {
        return ''
      }
      if (val == 0) {
        return '正在解析中'
      }
      if (val < 100) {
        return (val + '%')
      }
      if (val >= 100) {
        return '已完成'
      }
    },
    anastatus(val) {
      if (val < 0) {
        return ''
      }
      if (val == 0) {
        return ''
      }
      if (val < 100) {
        return ('进度' + val + '%')
      }
    },
    byteTransform
    // (limit) {
    //   let size = ''
    //   if (limit < 1 * 1024) { // 小于0.1KB,则转化成B
    //     size = limit.toFixed(2) + 'B'
    //   } else if (limit < 1 * 1024 * 1024) { // 小于1MB,则转化成KB
    //     size = (limit / 1024).toFixed(2) + 'KB'
    //   } else if (limit < 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 > 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) => {
        // 转码
        convertTxVodFile(params.resId, { type: 'resLibrary' }).then(res => {

        })
      })
    },
    submitVideoError(err, { uploadCount }) {
      // this.data.forEach(el => {
      //   if (el.percent < 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) => {
          // if (res.data.statusCode == 200) {

          // }
        }).catch(() => {
          this.$message({
            message: '音频入库失败!',
            type: 'error'
          })
        })
      }
      let countUnUploaded = 0 // 未上传个数
      console.log(filelist)
      filelist.filter((item, index) => {
        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(() => {
          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,文件名请勿包含特殊字符(#^;<>);
                    上传过程中,请勿关闭页面,否则上传失败;
                    `
      }
    },
    getImgSign() {
      imageSign({ fId: this.fId }).then((res) => {
        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((_) => {

      })
    },
    getQiniuToken() {
      getUploadToken().then(res => {
        this.audioData.token = res.data.data
        this.uploadData.token = res.data.data
      }).catch(err => {
        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 = /[\/]|[\?]|[\:]|[\*]|[\"]|[<]|[>]|[,]|[']|[\+]|[\#]|[%]/.test(file.name)
        if (isFu) {
          this.$message({
            message: `文件名不得包含 / : * ? " < > , ' + # %等特殊字符`,
            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 < 3
        if (!isLt3M) {
          this.$message.error('上传图片大小不能超过 3MB!')
          return false
        }
        const isLength = file.name.length <= 100
        if (!isLength) {
          this.$message.error('图片名称不能超过 100 个字!')
          return false
        }
        return isImg && isLt3M && 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 < 300
        if (!isLt3M) {
          this.$message.error('上传音频大小不能超过 300M!')
          return false
        }
        return isImg && 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 < 2048
        if (!isLt3M) {
          this.$message.error('上传视频大小不能超过 2GB!')
          return false
        }
        const isFu = /[\/]|[\?]|[\:]|[\*]|[\"]|[<]|[>]|[,]|[']/.test(file.name)
        if (isFu) {
          this.$message({
            message: `视频文件名不得包含 / : * ? " < > , ' 等字符`,
            type: 'warning'
          })
          return false
        }
        return isImg && isLt3M && !isFu
      }
    },
    back() {
      this.$store.dispatch('delVisitedViews', this.$route).then((views) => {
        this.$router.push({ name: 'materialBank' })
      })
    }
  }
}
</script>

<style scoped lang="scss">
    .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;
        }
    }
</style>
<style>
    .vhcenter .el-upload {
        width: 100% !important;
    }
    .drag-upload .el-upload-dragger {
      width: 100%;
      height: 100%;
      background: #fafafa;
      border: 0;
    }
</style>

页面列表

ITEM_HTML