基于element二次封装的文件上传组件 el- upload

  1. 增加自定义文件列表UI。
  2. 支持自定义手动上传,并且支持中断过上传文件。
  3. 自定义进度条动画效果。

直接上ui界面效果吧。 

文件列表展示 

 

删除背景按钮 

 

 进度条加载效果,且在上传过程中支持手动中断

 下面是代码

<template>
  <Upload
    drag
    multiple
    type="drag"
    ref="uploadRef"
    class="upload_file"
    :file-list="fileList"
    :limit="uploadFileInfo.limit"
    :action="uploadFileInfo.actionUrl"
    :format="uploadFileInfo.format"
    :headers="uploadFileInfo.headers"
    :max-size="uploadFileInfo.maxSize"
    :on-format-error="formatError"
    :on-success="handleSuccess"
    :on-progress="handleprogress"
    :before-remove="beforeRemove"
    :before-upload="beforeUpload"
    :on-exceed="handleExceed"
    :with-credentials="true"
    list-type="picture"
  >
    <div
      class="file-item"
      slot="file"
      slot-scope="{ file }"
      @click="operationBtn(file)"
    >
      <img class="file_icon" :src="showFlieIcon(file)" alt="" srcset="" />
      <div class="file-info">
        <div class="file-name">{{ file.name }}</div>
        <div class="file-size" style="font-size: 13px; font-weight: 500">
          {{ getFileIfno(file) }}
        </div>
      </div>
      <!-- 进度条样式 -->
      <div class="file-percentage" v-if="file.percentage !== 100">
        <div
          class="progress-width"
          :style="{
            width: file.percentage < 20 ? '20%' : file.percentage + '%',
          }"
        >
          <span style="color: #fff">{{ Math.round(file.percentage) }} %</span>
        </div>
      </div>
      <div class="opertion-btn">
        <Icon class="delet-btn" type="md-trash" size="22" color="#fff" />
      </div>
    </div>
    <div style="padding: 20px 0">
      <div>
        <Icon type="ios-folder" size="22" color="#3399ff" />
        <span>点击或者将文件拖拽到这里</span>
        <a href="" onclick="event.preventDefault(); return false;">上传文件</a>
      </div>
      <ul class="tips_info">
        <!-- <li>单个文件大小限制<=10MB</li> -->
        <li>一个知识库最多允许100个文件 ,一次最多上传20个文件</li>
        <li>
          支持文本、表格、图片、音频、视频等格式
          <span style="color: #2d8cf0" @click.stop="fileTypeExplain"
            >文件上传说明</span
          >
        </li>
      </ul>
    </div>
    <FileTypeModal v-model="fileTypeModalState" />
  </Upload>
</template>

<script>
import { convertFileSize } from '../../../utils/tool';
import * as SVG from '../images/exprotImg';
import service from '@/axios/request';
// console.log(service, 'service');
import { EventBus } from '@/views/KnowledgeBase/utils/eventBus';
import FileTypeModal from './file-type-modal.vue';
export default {
  name: 'uploadFile',
  components: {
    FileTypeModal,
  },
  props: {
    allowRemove: {
      type: Function,
      default: () => {
        return () => {};
      },
    },
    // uploadFileInfo: {
    //   type: Object,
    //   default: () => {
    //     return {};
    //   },
    // },
  },
  data() {
    return {
      SVG,
      // 上传文件的信息
      fileTypeModalState: false,
      uploadFileInfo: {
        actionUrl: `${service.defaults.baseURL}/proxy/dataset/upload_file`, //上传地址信息
        headers: {
          'Server-App-Id': 'ios-lma-svc',
          'User-Account': 'ddh.du',
          'User-Roles': 'flow_admin',
        }, //请求头信息
        disabled: false, //是否禁用
        data: {}, //上传时附带的额外参数
        name: '',
        maxSize: 1024 * 20, //文件上传大小
        format: [
          'docx',
          'doc',
          'pdf',
          'txt',
          'md',
          'csv',
          'xlsx',
          'png',
          'jpg',
          'jpeg',
          'm4a',
          'mp3',
          'mp4',
          'mov',
        ], //文件接收的类型
        limit: 20, //最大允许上传个数
      },
      // 文件列表
      fileList: [],
      // 判断上传文件的个数
      isPevFileList: [],
      flieFlag: null,
      deletFiles: [], //删除的文件操作远程删除的时候需要用到
      handleExceedFlag: null, //文件超出提示标记
      isOverFlag: [], //是否超出规定文件数量5个
    };
  },
  methods: {
    // 设置文件显示图标
    showFlieIcon(file) {
      if (!file) return;
      console.log(file, 'file');
      const { name } = file.raw || {};
      const FileType = name?.split('.')[name?.split('.').length - 1] ?? '';
      // 如果是图片类型转换为base64图片展示
      const imgFlieList = ['png', 'jpg', 'jpeg'];
      if (imgFlieList.includes(FileType)) {
        return URL.createObjectURL(file.raw);
      } else {
        return SVG[FileType + 'Icon']
          ? SVG[FileType + 'Icon']
          : SVG.defaultIcon;
      }
    },

    // 文件上传说明
    fileTypeExplain() {
      this.fileTypeModalState = true;
    },
    // 显示文件大小单位
    getFileIfno(file) {
      if (!file.raw) return '';
      const { size, name } = file.raw;
      const FileType = name?.split('.')[name?.split('.').length - 1] ?? '';
      return `${FileType} · ${convertFileSize(size)}`;
    },

    // 删除文件
    async operationBtn(file) {
      const fileItem = this.$refs.uploadRef.uploadFiles.find(
        (e) => e.uid === file.uid,
      );
      if (fileItem) {
        await this.$refs.uploadRef.abort(file);
      }

      const index = this.$refs.uploadRef.uploadFiles.findIndex(
        (e) => e.uid === file.uid,
      );
      this.$refs.uploadRef.uploadFiles.splice(index, 1);
    },
    // 终止上传
    abortUpload() {
      this.$refs.uploadRef.abort(); // 调用 abort 方法中止上传
    },

    // 文件超出提示钩子
    handleExceed(files, fileList) {
      if (this.handleExceedFlag) {
        return;
      }
      this.handleExceedFlag = setTimeout(() => {
        this.$Message.error('最多每次上传20个文件 !');
        this.handleExceedFlag = null;
      }, 300);
    },

    // 上传时的钩子
    beforeUpload(file) {
      const { size, name } = file;
      // console.log(file, 'file');
      const { format } = this.uploadFileInfo;
      const type = name?.split('.')[name?.split('.').length - 1] ?? '';

      if (!format.includes(type)) {
        this.$Notice.warning({
          title: '文件错误提示',
          desc: `${name}文件类型不符合要求, 请重新上传 !`,
          duration: 6,
        });
        return false;
      }
      // if (size > 1024 * 1024 * 10) {
      //   this.$Message.error(`${name}-->超过文件规定大小 !`);
      //   return false;
      // }
      // if (this.isOverFlag.length > 5) {
      //   this.$Message.error(`一次只能上传5个文件 !`);
      //   return;
      // }
      return;
    },
    // 错误文件类型校验提示
    formatError(file, fileList) {
      this.$Message.error(`${file.name}-->文件类型错误 !`);
    },
    // 文件上传成功信息以及过滤掉后端报错的文件信息
    handleSuccess(res, file, fileList) {
      // if (res.ret === 0 && res.data) {
      //   const { file_id } = res.data;
      //   file.file_id = file_id;
      // } else {
      //   fileList.splice(fileList.indexOf(file), 1);
      //   this.$Message.error(`${res.msg} !`);
      // }
      // const fileIdList = fileList.map((mapItem) => mapItem.file_id);
      // this.isOverFlag = fileIdList;
      // this.$emit('on-change-file', fileIdList);
    },

    // 文件上传时的钩子
    handleprogress(event, file, fileList) {
      // console.log(this.$refs.upload, 'this.$refs.upload');
    },

    // 移除文件时移除掉要删除掉文件列表
    async beforeRemove(file, fileList) {
      const { file_id, name } = file;
      this.$emit('on-delet-file', { file_id, name });
      return new Promise((resolve, reject) => {
        EventBus.$on('remove-file-item', (state) => {
          // console.log(state, 'state状态');
          if (state) {
            resolve();
          } else {
            reject();
          }
        });
      }).then(() => {
        const fileIdList = fileList
          ?.filter((filterItem) => filterItem.file_id !== file.file_id)
          ?.map((mapItem) => mapItem.file_id);
        this.isOverFlag = fileIdList;
        this.$emit('on-change-file', fileIdList);
      });
    },
  },
  mounted() {
    // 判断本地环境添加请求头信息
    if (process.env.NODE_ENV === 'development') {
      this.uploadFileInfo.headers['User-Account'] = 'ddh.du';
      this.uploadFileInfo.headers['User-Roles'] = 'flow_admin';
      this.uploadFileInfo.headers['Server-App-Id'] = 'ios-lma-svc';
    }
    console.log(this.uploadFileInfo.headers,'this.uploadFileInfo.headers')
  },
  beforeDestroy() {
    // 组件销毁前终止上传
    this.abortUpload();
    this.$off('remove-file-item');
  },
};
</script>
<style lang="less" scoped>
.upload_file {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;

  .tips_info {
    line-height: 20px;
    font-size: 14px;
    color: #ccc;
  }

  .file-item {
    position: relative;
    padding: 10px;
    display: flex;
    gap: 8px;
    align-items: center;
    &:hover {
      .opertion-btn {
        display: flex;
      }
    }
    .file_icon {
      width: 40px;
      height: 40px;
      border-radius: 4px;
    }
    .file-info {
      font-size: 12px;
      .file-name {
        max-width: 120px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }

    // 进度条样式
    .file-percentage {
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
      width: 100%;
      border-radius: 8px;
      .progress-width {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100%;
        width: 0;
        background: rgba(0, 0, 0, 0.5);
      }
    }

    // 删除按钮
    .opertion-btn {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      display: none;
      justify-content: center;
      align-items: center;
      background: rgba(0, 0, 0, 0.3);
      border-radius: 8px;
      z-index: 999;
      .delet-btn {
        cursor: pointer;
        transition: all 0.3s;
        &:hover {
          opacity: 0.8;
        }
      }
    }

    // width: calc(50% - 5px);
  }
  ::v-deep .el-upload {
    width: 100%;
    // border: 1px dashed #ccc;
    border-radius: 4px;
    transition: all 0.3s;
    .el-upload-dragger {
      height: 120px;
      width: 100%;
    }
    &:hover {
      border-color: #57a3f3;
    }
  }
  //   文件列表信息
  ::v-deep .el-upload-list {
    width: 100%;
    // display: flex;
    // flex-direction: column;
    padding-right: 10px;
    max-height: 200px;
    overflow-y: auto;
    .ivu-upload-list-file {
      margin-bottom: 10px;
      border: 1px solid rgba(236, 238, 243, 1);
      border-radius: 9px;
    }
  }
  ::v-deep .el-upload-list--picture {
    margin-top: 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    background: rgb(248, 249, 252);
    border-width: 1px;
    border-radius: 8px;
    padding: 12px !important;
    .el-upload-list__item {
      height: auto;
      padding: 0;
      margin: 0;
      display: flex;
      width: calc(50% - 5px);
      border: none;
    }
  }
}
</style>
<style lang="less">
.ivu-notice-notice {
  border-radius: 12px;
  .ivu-notice-desc {
    line-height: 23px;
    white-space: 2px;
    word-wrap: break-word;
    word-break: break-all;
    overflow: hidden;
    white-space: wrap;
  }
}
</style>

对于Vue3和Element UI的el-upload二次封装,你可以参考以下步骤: 1. 首先,根据Element UI官方文档的指引,了解el-upload组件的使用方法和属性。你可以在官方文档中找到详细的代码示例和解释。 2. 创建一个Vue组件,例如UploadImg,用于封装el-upload组件。在组件中,你可以根据需要使用el-upload提供的上传、文件列表、删除等功能。你可以根据自己的需求,将el-upload的属性传递给子级组件或自定义函数进行处理。 3.组件中,你可以使用axios或其他HTTP库来进行文件的上传和删除操作。你可以根据自己的需要,封装适当的请求函数。比如,你可以创建一个delUpImage函数用于删除图片,或者创建一个uploadImg函数用于上传图片。 4.Vue的template中使用封装好的UploadImg组件,传递相应的属性和事件监听函数。这样,你就可以在页面中使用二次封装el-upload组件了。 下面是一个示例的代码结构: ```javascript // UploadImg.vue <template> <el-upload :limit="limit" action="" accept="image/*" :http-request="uploadFile" list-type="picture-card" :auto-upload="false" :file-list="fileList" :on-exceed="handleExceed" :before-remove="beforeRemove" ref="upload" > <img src="../assets/common_images/uploadImage.png" width="146" height="146"> </el-upload> <el-form-item> <el-button size="small" type="primary" style="margin-top: 20px;" @click="submitUpload">点击上传</el-button> </el-form-item> </template> <script> import { defineComponent } from 'vue'; import axios from 'axios'; export default defineComponent({ name: 'UploadImg', props: { limit: { type: Number, default: 6 }, fileList: { type: Array, default: () => [] } }, methods: { uploadFile(file) { // 上传文件的逻辑处理 }, handleExceed(files, fileList) { // 处理文件超出限制的逻辑 }, beforeRemove(file, fileList) { // 删除文件前的逻辑处理 }, submitUpload() { // 点击上传按钮的逻辑处理 } } }); </script> ``` 请注意,这只是一个示例代码,你需要根据自己的实际需求进行相应的修改和调整。另外,为了实现文件上传和删除操作,你可能还需要引入其他库或自定义函数来处理HTTP请求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值