vue+element 实现和后端交互的上传文件功能

2021-07-12  本文已影响0人  好名字都让你们用了
:组件中api 项目中用到的 ,各位根据各自进行更换

1:主要上传组件代码

<template>
  <div class='upload'>
    <el-upload ref='upload' :class='{has: isShowHasClass, unshow: hideUploadList}'
               :action='api' :headers='headers' :file-list='fileList' :limit='limit' :accept='accept' :multiple='multiple' :list-type='listType' :disabled='disabled'
               :on-preview='handlePictureCardPreview' :on-remove='handleRemove' :before-upload='checkImage' :on-change="uploadChange" :on-exceed="checkExceed"
               :on-success='uploadSuccess' :on-error='uploadFail'>
      <i class='el-icon-plus' v-if='listType == "picture-card"'></i>
      <el-button type='primary' v-else>{{ text }}</el-button>
    </el-upload>
  </div>
</template>

<script>
import {mapActions} from 'vuex'
import {loadPreviewPlugin, previewImage} from 'utils'
import {isArrayFn} from 'utils/variable'
export default {
  props: {
    disabled: {
      type:Boolean,
      default:false,
    },
    isUpdateUrl: {
      type: Boolean,
      default: false,
    },
    value: {
      type: String | Array,
      default: () => [],
    },
    url: {
      type: String | Array,
      default: () => [],
    },
    type: {
      type: String,
      default: 'image',
    },
    unClick: {
      type: Boolean,
      default: false,
    },
    isLyc: {
      type: Boolean,
      default: false,
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    isHidedList: {
      type: Boolean,
      default: true,
    },
    richText: {
      type: Boolean,
      default: false,
    },
    isClear: {
      type: Boolean,
      default: false,
    },
    accept: {
      type: String,
      default: 'image/*',
    },
    size: {
      type: [Number,String],
      default: 1024, // 默认1024kb,即:1M;
    },
    text: {
      type: String,
      default: '选择图片',
    },
    limit: {
      type: Number,
      default: 1,
    },
    listType: {
      type: String,
      default: 'picture-card'
    },
    imageSize: {
      type: String,
      default: '',
    },
    imgType:{
      type:String,
      default:'book:cover'
    }
  },
  data () {
    return {
      urlClone: this.value,
      fileList: [],
      headers: {'X-HB-Client-Type': 'shop-pc',},
      loading: false,
      excelData: {
        header: null,
        results: null
      }
    }
  },
  watch: {
    value (nVal, oVal) {
      // 监听父组件清空url的时候清空文件
      if (nVal) return
      this.clearFiles()
    },
    isUpdateUrl (nVal, oVal) {
      // 弹框或tab切换类型不触发beforeMount,用该方式更新urlClone
      if (!nVal) return
      this.initFieldList()
    },
  },
  computed: {
    hideUploadList () {
      return this.multiple && this.isHidedList
    },
    api () {
      if(this.type == 'image') { // 图片
        return '/restcollege/collegeadmin/v1/upload/images'
      } else if (this.type == 'text' || this.type == 'xls') { // excel
        return '/restcollege/collegeadmin/v1/upload/lyric'
      } else if (this.type == 'audio') { // 音频
        return '/restcollege/collegeadmin/v1/upload/audio'
      } else if (this.type == 'bnl') { // 点读包:上传类型为'bnl', 'zip', 'rar', 'tar'
        return '/restcollege/collegeadmin/v1/upload/bookbnl'
      }else if (this.type=='bookImage'){   //小彼恩图片
        this.headers={'X-HB-Client-Type': 'xiaobien-pc',}
        return `/xiaobienapi/common/v1/upload/images/?imgType=${this.imgType}`
      }
    },
    isShowHasClass () {
      // 多张不去掉上传按钮
      if (this.multiple) return false
      // 单张判断是否已生成url
      if (!this.value) return false
      return this.value.length || this.urlClone.length
    },
  },
  methods: {
    ...mapActions(['uploadFile']),
    getImageList (fileList) {
      return fileList.map(item => {
        if (item.status != 'success') return ''
        if (item.response) { // 新上传的图
          return item.response.path
        } else { // 以前上传的图
          return item.url
        }
      })
    },
    clearFiles () {
      this.$refs.upload.clearFiles()
    },
    handleRemove (file, fileList) {
      let imagePathList = []
      fileList.map((item, index) => {
        if (item.uid == file.uid) {
          fileList.splice(index, 1)
        }
      })
      imagePathList = this.getImageList(fileList)
      this.urlClone = []
      this.$emit('update:value', imagePathList)
      // 嵌套多层的情况手动更新数据
      this.$emit('removeChange', imagePathList)
    },
    handlePictureCardPreview (file) {
      // 图片预览
      if(this.unClick || this.type != 'image' && this.type != 'bookImage') return
      previewImage(file.path || file.url)
      return false
    },
    uploadChange (e) {
      this.$emit('uploadChange', e)
    },
    uploadFail (err, file, fileList) {
      this.$message.error(`${JSON.parse(err.message).message},请重新上传`)
    },
    uploadSuccess (response, file, fileList) {
      // 音频类
      if (this.type == 'audio') {
        if (response.size > this.size ) {
          this.$message.error(`音频不能超过${this.size}M`)
          return
        }
        this.$emit('update:value', {...response, name: file.name.split('.mp3')[0]})
        return
      }
      // 富文本类
      if(this.richText) {
        this.$emit('asyncData', fileList)
        return
      }
      // excel类
      if(this.type == 'xls') {
        this.$emit('update:value', response.path)
        this.upload(file.raw)
        return
      }
      if (this.type == 'image' ||this.type=='bookImage') {
        // 图片类
        let imageList = this.getImageList(fileList)
        let imagePath
        if (fileList.length == 1) {
          // 单张直接返回图片地址
          imagePath = imageList[0]
        } else {
          // 多张返回图片地址列表
          imagePath = imageList
        }
        this.$emit('update:value', imagePath)
      }
      // 歌词文件
      if (this.type == 'text') {
        this.$emit('update:value', response.path)
      }
      // // 点读包文件
      // if (this.type == 'bnl') {
      //   this.$emit('update:value', response.path)
      //   this.$emit('update:size', response.size)
      // }
    },
    checkExceed (files, fileList) {
      this.$message.error(`最多只能上传${this.limit}张`)
    },
    checkImage (file) {
      let _this = this
      //普通的判断可以用return false
      // 获取文件尺寸,判断尺寸在不在规定范围之内
      if(this.isLyc) {
        if(file.name.split('.')[1].toLowerCase() != 'lrc') {
          this.$message.error('请上传格式正确的歌词文件')
          return false
        }
      }
      if (!_this.imageSize) {
        return true
      }
      return new Promise(function(resolve, reject) {
        let reader = new FileReader()
        let size = JSON.parse(_this.imageSize)
        reader.readAsDataURL(file)
        reader.onload = function(theFile) {
          let image = new Image()
          image.src = theFile.target.result
          image.onload = function() {
            if (file.size > _this.size * 1024 ) {
              _this.$message.error(`${file.name}大小不对,请重新上传!`)
              reject('图片大小不对')
              return
            }
            if(size.width && size.height) {
              const noSizeLimit = !this.height || !this.width
              const rightSize = size.width == this.width && size.height == this.height
              if (noSizeLimit || rightSize) {
                file.width = size.width
                file.height = size.height
                resolve(file)
                return
              } else {
                _this.$message.error(`${file.name}尺寸不对,请重新上传!`)
                reject('图片尺寸不对')
                return
              }
            }

            if(!size.width || !size.height) {
              if(size.width) {
                if(size.width == this.width) {
                  file.width = size.width
                  resolve(file)
                } else {
                  _this.$message.error(`${file.name}尺寸不对,请重新上传!`)
                  reject('图片尺寸不对')
                }
              } else {
                if(size.height == this.height) {
                  file.height = size.height
                  resolve(file)
                } else {
                  _this.$message.error(`${file.name}尺寸不对,请重新上传!`)
                  reject('图片尺寸不对')
                }
              }
            }
          }
        }
      })
    },
    generateData({ header, results }) {
      this.excelData.header = header
      this.excelData.results = results
      this.$emit('uploaded', results)
      // this.onSuccess && this.onSuccess(this.excelData)
    },
    handleDrop(e) {
      e.stopPropagation()
      e.preventDefault()
      if (this.loading) return
      const files = e.dataTransfer.files
      if (files.length !== 1) {
        this.$message.error('Only support uploading one file!')
        return
      }
      const rawFile = files[0] // only use files[0]

      if (!this.isExcel(rawFile)) {
        this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
        return false
      }
      this.upload(rawFile)
      e.stopPropagation()
      e.preventDefault()
    },
    handleDragover(e) {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy'
    },
    handleUpload() {
      this.$refs['excel-upload-input'].click()
    },
    handleClick(e) {
      const files = e.target.files
      const rawFile = files[0] // only use files[0]
      if (!rawFile) return
      this.upload(rawFile)
    },
    upload(rawFile) {
      if (!this.beforeUpload) {
        this.readerData(rawFile)
        return
      }
      const before = this.beforeUpload(rawFile)
      if (before) {
        this.readerData(rawFile)
      }
    },
    readerData(rawFile) {
      this.loading = true
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = e => {
          const data = e.target.result
          const workbook = XLSX.read(data, { type: 'array' })
          const firstSheetName = workbook.SheetNames[0]
          const worksheet = workbook.Sheets[firstSheetName]
          const header = this.getHeaderRow(worksheet)
          const results = XLSX.utils.sheet_to_json(worksheet)
          this.generateData({ header, results })
          this.loading = false
          resolve()
        }
        reader.readAsArrayBuffer(rawFile)
      })
    },
    getHeaderRow(sheet) {
      const headers = []
      const range = XLSX.utils.decode_range(sheet['!ref'])
      let C
      const R = range.s.r
      /* start in the first row */
      for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
        const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
        /* find the cell in the first row */
        let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
        headers.push(hdr)
      }
      return headers
    },
    isExcel(file) {
      return /\.(xlsx|xls|csv)$/.test(file.name)
    },
    initFieldList () {
      // 传入的 value 的值为空或者为数组时获取原格式
      // 传入的 value 为单张且有值时将string转为array格式
      this.urlClone = (isArrayFn(this.value) || !this.value) ? this.value : [this.value]
      // 已有图片的情况下初始化照片墙列表
      if (this.urlClone && this.urlClone.length) {
        this.fileList = this.urlClone.map(item => {
          return {
            name: item.name || item.audio_name || item,
            url: item,
          }
        })
      }
    },
  },
  beforeMount () {
    loadPreviewPlugin()
    this.initFieldList()
  },
}
</script>

<style lang='less' scoped>
.has {
  margin-top: -3px;
  :global(.el-upload--picture-card) {
    display: none;
  }
  :global(.el-upload--) {
    display: none;
  }
}
.unshow {
  :global(.el-upload-list--) {
    display: none;
  }
  :global(.el-upload-list--picture) {
    display: none;
  }
}
</style>
上一篇 下一篇

猜你喜欢

热点阅读