You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							372 lines
						
					
					
						
							9.4 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							372 lines
						
					
					
						
							9.4 KiB
						
					
					
				
								<!--
							 | 
						|
								    * @FileDescription: index
							 | 
						|
								    * @Author: kahu
							 | 
						|
								    * @Date: 2022/12/14
							 | 
						|
								    * @LastEditors: kahu
							 | 
						|
								    * @LastEditTime: 2022/12/14
							 | 
						|
								-->
							 | 
						|
								<template>
							 | 
						|
								  <div class="content">
							 | 
						|
								    <el-upload
							 | 
						|
								      v-loading="isUploading"
							 | 
						|
								      :disabled="componentError"
							 | 
						|
								      class="upload-demo"
							 | 
						|
								      drag
							 | 
						|
								      :headers="headers"
							 | 
						|
								      :file-list="viewFileList"
							 | 
						|
								      :name="name"
							 | 
						|
								      :show-file-list="showFileList"
							 | 
						|
								      :list-type="showFileListType"
							 | 
						|
								      :multiple="multiple"
							 | 
						|
								      :action="uploadUrl"
							 | 
						|
								      :limit="limit"
							 | 
						|
								      :on-success="handleUploadSuccess"
							 | 
						|
								      :before-upload="handleBeforeUpload"
							 | 
						|
								      :on-error="handleUploadError"
							 | 
						|
								      :on-exceed="handleExceed"
							 | 
						|
								      :before-remove="handleBeforeRemove"
							 | 
						|
								      :on-remove="handleRemove"
							 | 
						|
								      :on-preview="handlePreviewOpen"
							 | 
						|
								    >
							 | 
						|
								      <i class="el-icon-upload"></i>
							 | 
						|
								      <div
							 | 
						|
								        class="el-upload__text"
							 | 
						|
								        v-if="!componentError"
							 | 
						|
								      >将文件拖到此处,或<em>点击上传</em></div>
							 | 
						|
								      <div
							 | 
						|
								        class="error-text"
							 | 
						|
								        v-else
							 | 
						|
								      > 组件配置错误,请查看控制台
							 | 
						|
								      </div>
							 | 
						|
								      <div
							 | 
						|
								        class="el-upload__tip"
							 | 
						|
								        slot="tip"
							 | 
						|
								      >
							 | 
						|
								        文件大小不超过{{ limitSize }}m,文件类型为{{ types.toString() }}
							 | 
						|
								      </div>
							 | 
						|
								    </el-upload>
							 | 
						|
								
							 | 
						|
								    <!-- 预览 -->
							 | 
						|
								    <el-dialog
							 | 
						|
								      title="预览"
							 | 
						|
								      :visible.sync="previewObj.show"
							 | 
						|
								      width="60%"
							 | 
						|
								      :before-close="handlePreviewClose"
							 | 
						|
								    >
							 | 
						|
								      <div class="preview-content">
							 | 
						|
								        <template v-if="previewObj.file && previewObj.file.type.includes('image')">
							 | 
						|
								          <el-image class="preview-item" :src="previewObj.file.url" />
							 | 
						|
								        </template>
							 | 
						|
								        <template v-if="previewObj.file && previewObj.file.type.includes('video')">
							 | 
						|
								          <video class="preview-item" controls :src="previewObj.file.url" />
							 | 
						|
								        </template>
							 | 
						|
								      </div>
							 | 
						|
								    </el-dialog>
							 | 
						|
								  </div>
							 | 
						|
								</template>
							 | 
						|
								
							 | 
						|
								<script>
							 | 
						|
								import mime from 'mime'
							 | 
						|
								let fullLoading = null
							 | 
						|
								const baseURL = process.env.VUE_APP_DOMAIN_PREFIX
							 | 
						|
								export default {
							 | 
						|
								  name: "Upload",
							 | 
						|
								  props: {
							 | 
						|
								    headers:{
							 | 
						|
								      type:Object,
							 | 
						|
								      default:()=>({})
							 | 
						|
								    },
							 | 
						|
								    /** 上传时候表单的KEY */
							 | 
						|
								    name: {
							 | 
						|
								      type: String,
							 | 
						|
								      default: () => "file"
							 | 
						|
								    },
							 | 
						|
								    /** 限制上传数量 */
							 | 
						|
								    limit: {
							 | 
						|
								      type: Number,
							 | 
						|
								      default: () => 5
							 | 
						|
								    },
							 | 
						|
								    /** 限制的那张大小 单位M */
							 | 
						|
								    limitSize: {
							 | 
						|
								      type: Number,
							 | 
						|
								      default: () => 5
							 | 
						|
								    },
							 | 
						|
								    /** 是否多选 */
							 | 
						|
								    multiple: {
							 | 
						|
								      type: Boolean,
							 | 
						|
								      default: () => true
							 | 
						|
								    },
							 | 
						|
								    /** 是否展示文件列表 */
							 | 
						|
								    showFileList: {
							 | 
						|
								      type: Boolean,
							 | 
						|
								      default: () => true
							 | 
						|
								    },
							 | 
						|
								    /** 文件展示方式 text/picture/picture-card */
							 | 
						|
								    showFileListType:{
							 | 
						|
								      type:String,
							 | 
						|
								      default:()=>'text'
							 | 
						|
								    },
							 | 
						|
								    /** 允许上传的文件尾缀 string[] */
							 | 
						|
								    types: {
							 | 
						|
								      type: Array,
							 | 
						|
								      default: () => (['jpg', 'png', 'gif'])
							 | 
						|
								    },
							 | 
						|
								    /** 默认的文件列表 string[] */
							 | 
						|
								    defaultFileList: {
							 | 
						|
								      type: Array,
							 | 
						|
								      default: () => ([])
							 | 
						|
								    },
							 | 
						|
								    /** 上传成功后端返回的字段名称 */
							 | 
						|
								    responseFileName: {
							 | 
						|
								      type: String,
							 | 
						|
								      default: () => 'url'
							 | 
						|
								    },
							 | 
						|
								    /** 是否需要全屏loading */
							 | 
						|
								    needFullScreenLoading:{
							 | 
						|
								      type:Boolean,
							 | 
						|
								      default:()=>true
							 | 
						|
								    }
							 | 
						|
								  },
							 | 
						|
								  data() {
							 | 
						|
								    return {
							 | 
						|
								      uploadUrl: `${ baseURL }/file/upload`,
							 | 
						|
								      // 真实文件列表
							 | 
						|
								      fileList: [],
							 | 
						|
								      // 默认展示的list,解决多上传只回调success一次的问题
							 | 
						|
								      viewFileList:[],
							 | 
						|
								      // 组件是否部署错误
							 | 
						|
								      componentError: false,
							 | 
						|
								      // 是否正在上传
							 | 
						|
								      isUploading:false,
							 | 
						|
								      // 预览对象
							 | 
						|
								      previewObj:{
							 | 
						|
								        show:false,
							 | 
						|
								        file:null
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								  },
							 | 
						|
								  watch: {
							 | 
						|
								    defaultFileList: {
							 | 
						|
								      handler() {
							 | 
						|
								        // 判断类型
							 | 
						|
								        const flag = Object.prototype.toString.call(this.defaultFileList) === '[object Array]'
							 | 
						|
								          && this.defaultFileList.length > 0 &&
							 | 
						|
								          Object.prototype.toString.call(this.defaultFileList[0]) !== '[object String]'
							 | 
						|
								        if (flag) {
							 | 
						|
								          this.componentError = true
							 | 
						|
								          throw new Error('defaultFileList格式错误,应为string[]格式')
							 | 
						|
								        }else{
							 | 
						|
								          this.componentError = false
							 | 
						|
								        }
							 | 
						|
								        this.viewFileList = this.defaultFileList.map(defaultFilePath => ({name: defaultFilePath, url: defaultFilePath}))
							 | 
						|
								        this.viewFileList.forEach(item=>{
							 | 
						|
								          this.fileList.push(item)
							 | 
						|
								        })
							 | 
						|
								      },
							 | 
						|
								      deep: true,
							 | 
						|
								      immediate: true
							 | 
						|
								    },
							 | 
						|
								    fileList:{
							 | 
						|
								      handler(){
							 | 
						|
								        this.handleNotifyFather()
							 | 
						|
								      },
							 | 
						|
								      deep:true,
							 | 
						|
								      immediate:false
							 | 
						|
								    }
							 | 
						|
								  },
							 | 
						|
								  methods: {
							 | 
						|
								    /**
							 | 
						|
								     * 检查type是否符合types的mime
							 | 
						|
								     * @param type 文件后缀
							 | 
						|
								     * @param types 可用文件后缀集合
							 | 
						|
								     */
							 | 
						|
								    handleCheckFileMime(type, types) {
							 | 
						|
								      const typeMimes = types.map(item => mime.getType(item))
							 | 
						|
								      return typeMimes.includes(type)
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    handleCheckFileSize(fileSize, limitSize) {
							 | 
						|
								      const limitByteSize = limitSize * 1024 * 1024
							 | 
						|
								      return limitByteSize > fileSize
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 上传之前的钩子
							 | 
						|
								     * @param file
							 | 
						|
								     * @return {undefined}
							 | 
						|
								     */
							 | 
						|
								    handleBeforeUpload(file) {
							 | 
						|
								      // 检查mime
							 | 
						|
								      const fileType = file.type || mime.getType(file.name.slice(file.name.lastIndexOf('.') + 1))
							 | 
						|
								      const checkFileMime = this.handleCheckFileMime(fileType, this.types)
							 | 
						|
								      const checkFileSize = this.handleCheckFileSize(file.size, this.limitSize);
							 | 
						|
								      !checkFileSize ? file.isJumpRemove = true : undefined
							 | 
						|
								      !checkFileSize ? this.$notify.warning(`文件大小不得超出${ this.limitSize }m`) : undefined
							 | 
						|
								      !checkFileMime ? file.isJumpRemove = true : undefined
							 | 
						|
								      !checkFileMime ? this.$notify.warning(`文件类型不在合法列表 ${ this.types }`) : undefined
							 | 
						|
								      if(checkFileSize && checkFileMime){
							 | 
						|
								        // 开启loading
							 | 
						|
								        this.isUploading = true
							 | 
						|
								        if(this.needFullScreenLoading){
							 | 
						|
								          fullLoading = this.$loading({
							 | 
						|
								            background:`rgba(255,255,255,0.5)`,
							 | 
						|
								            text:'上传中',
							 | 
						|
								            fullscreen:true
							 | 
						|
								          })
							 | 
						|
								        }
							 | 
						|
								      }
							 | 
						|
								      return checkFileSize && checkFileMime
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 上传成功钩子
							 | 
						|
								     * @param response
							 | 
						|
								     * @param file
							 | 
						|
								     * @param fileList
							 | 
						|
								     */
							 | 
						|
								    handleUploadSuccess(response, file, fileList) {
							 | 
						|
								      this.isUploading = false
							 | 
						|
								      if(this.needFullScreenLoading){
							 | 
						|
								        fullLoading?.close()
							 | 
						|
								      }
							 | 
						|
								      const successObj = {
							 | 
						|
								        url: response.data[this.responseFileName],
							 | 
						|
								        name: file.name
							 | 
						|
								      }
							 | 
						|
								      file.url =  response.data[this.responseFileName]
							 | 
						|
								      this.fileList.push(successObj)
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 上传失败的钩子
							 | 
						|
								     * @param err
							 | 
						|
								     * @param file
							 | 
						|
								     * @param fileList
							 | 
						|
								     */
							 | 
						|
								    handleUploadError(err, file, fileList) {
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 超出数量的钩子
							 | 
						|
								     * @param files
							 | 
						|
								     * @param fileList
							 | 
						|
								     */
							 | 
						|
								    handleExceed(files, fileList) {
							 | 
						|
								      this.$notify.warning(`文件总数大于可上传数量 ${ this.limit }`)
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 文件即将移除的钩子
							 | 
						|
								     * @param file
							 | 
						|
								     * @param fileList
							 | 
						|
								     */
							 | 
						|
								    async handleBeforeRemove(file, fileList) {
							 | 
						|
								      // 如果是超出文件大小调用,放行
							 | 
						|
								      if (file?.raw?.isJumpRemove) {
							 | 
						|
								        return true
							 | 
						|
								      }
							 | 
						|
								      return await this.$confirm('此操作将会删除已上传的文件, 是否继续?', '提示', {
							 | 
						|
								        confirmButtonText: this.$t('common.sure'),
							 | 
						|
								        cancelButtonText: this.$t('common.cancel'),
							 | 
						|
								        type: 'warning'
							 | 
						|
								      })
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 移除文件的钩子
							 | 
						|
								     */
							 | 
						|
								    handleRemove(file, fileList) {
							 | 
						|
								      if (file?.raw?.isJumpRemove) {
							 | 
						|
								        return
							 | 
						|
								      }
							 | 
						|
								      this.fileList.splice(this.fileList.findIndex(fileItem => file?.response?.data[this.responseFileName] === fileItem.url || file.url === fileItem.url), 1)
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 通知父组件
							 | 
						|
								     */
							 | 
						|
								    handleNotifyFather(){
							 | 
						|
								      this.$emit('change',this.fileList)
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * 预览
							 | 
						|
								     * 图片视频直接预览,其他下载
							 | 
						|
								     * @param file
							 | 
						|
								     */
							 | 
						|
								    handlePreviewOpen(file){
							 | 
						|
								      if(!file.type){
							 | 
						|
								        file.type = mime.getType(file?.url?.slice(file?.url?.lastIndexOf('.')+1)) || mime.getType(file?.name?.slice(file?.name.lastIndexOf('.')+1)) || undefined
							 | 
						|
								      }
							 | 
						|
								      if(file.type.includes('image') || file.type.includes('video')){
							 | 
						|
								        this.previewObj.file = file
							 | 
						|
								        this.previewObj.show = true
							 | 
						|
								      }else{
							 | 
						|
								        this.$confirm('需要下载才能预览此文件, 是否继续?', '提示', {
							 | 
						|
								          confirmButtonText: this.$t('common.sure'),
							 | 
						|
								          cancelButtonText: this.$t('common.cancel'),
							 | 
						|
								          type: 'warning'
							 | 
						|
								        }).then(() => {
							 | 
						|
								          let htmlAnchorElement = document.createElement('a');
							 | 
						|
								          htmlAnchorElement.download = file?.url.slice(file?.url.lastIndexOf('/')+1)
							 | 
						|
								          htmlAnchorElement.target='_bank'
							 | 
						|
								          htmlAnchorElement.href = file?.url
							 | 
						|
								          htmlAnchorElement.click()
							 | 
						|
								          htmlAnchorElement = null
							 | 
						|
								        }).catch(() => {
							 | 
						|
								        });
							 | 
						|
								      }
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    handlePreviewClose(){
							 | 
						|
								      this.previewObj.file = null
							 | 
						|
								      this.previewObj.show = false
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								</script>
							 | 
						|
								
							 | 
						|
								<style
							 | 
						|
								  lang="scss"
							 | 
						|
								  scoped
							 | 
						|
								>
							 | 
						|
								::v-deep .el-upload {
							 | 
						|
								  width: 100% !important;
							 | 
						|
								
							 | 
						|
								  .el-upload-dragger {
							 | 
						|
								    width: 100% !important;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								.error-text {
							 | 
						|
								  font-size: 18px;
							 | 
						|
								  font-weight: bolder;
							 | 
						|
								  color: red;
							 | 
						|
								  animation: error-animation 2.5s ease-in-out infinite;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								@keyframes error-animation {
							 | 
						|
								  0%, 100% {
							 | 
						|
								    font-size: 18px;
							 | 
						|
								    color: red;
							 | 
						|
								  }
							 | 
						|
								  25%, 75% {
							 | 
						|
								    font-size: 16px;
							 | 
						|
								    color: #b9b1b1;
							 | 
						|
								  }
							 | 
						|
								  50% {
							 | 
						|
								    font-size: 18px;
							 | 
						|
								    color: #500000;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								.preview-content{
							 | 
						|
								  display: flex;
							 | 
						|
								  align-items: center;
							 | 
						|
								  justify-content: center;
							 | 
						|
								  .preview-item{
							 | 
						|
								    min-width: 800px;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								</style>
							 |