vue-elementui-admin 文件上传组件模块

本文介绍了如何使用Vue开发一个组件,实现图片、视频等文件的选择、上传、替换和删除功能,以及文件类型的筛选和上传限制。组件还包含一个弹窗用于选择和上传文件,并与父组件通过props传递数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果图:

 

组件代码:

<template>
  <div>
    <div class="components-upload-box">

      <draggable :list="imgList" v-bind="$attrs" :set-data="setData" class="item-box">

        <div class="item" v-for="(item, index) in imgList">
          <img :src="item" v-if="inspectionType(item) == 'img'">
          <svg-icon v-else class="icon" :icon-class="inspectionType(item)" />
          <div class="btn">
            <div class="edit" @click="componentsUploadEdit(index)">替换</div>|<div class="del"
              @click="componentsUploadDel(index)">删除</div>
          </div>
        </div>

      </draggable>

      <div class="item add" @click="addFileBtn">
        <i class="el-icon-circle-plus-outline" />
        添加文件
      </div>
    </div>

    <!-- 《《《《选择/上传文件弹窗 -->
    <el-dialog class="dialogUploadVisible" title="选择文件" :visible.sync="dialogUploadVisible" width="950px">
      <div class="l-r">
        <div class="left">
          <div class="title">分组名称</div>
          <div :class="fileType=='all'?'item on':'item'" @click="edit_fileType('all')">
            <svg-icon class="icon" icon-class="all-file" />全部
          </div>
          <div :class="fileType=='image'?'item on':'item'" @click="edit_fileType('image')">
            <svg-icon class="icon" icon-class="image" />图片
          </div>
          <div :class="fileType=='video'?'item on':'item'" @click="edit_fileType('video')">
            <svg-icon class="icon" icon-class="video" />视频
          </div>
          <div :class="fileType=='audio'?'item on':'item'" @click="edit_fileType('audio')">
            <svg-icon class="icon" icon-class="audio" />音频
          </div>
          <div :class="fileType=='file'?'item on':'item'" @click="edit_fileType('file')">
            <svg-icon class="icon" icon-class="file" />文件
          </div>
          <div :class="fileType=='package'?'item on':'item'" @click="edit_fileType('package')">
            <svg-icon class="icon" icon-class="package" />压缩包
          </div>
        </div>

        <div class="right">
          <div class="top">
            <div>全部</div>
            <el-button type="primary">上传<i class="el-icon-upload el-icon--right"></i></el-button>
          </div>

          <div class="file-list-box">
            <div class="file-list">

              <template v-for="(item, index) in testImg">
                <a href="javascript:void(0)" title="图片名称"
                  :class="inspectCheckedFile(item.id)?'file-item on':'file-item'" @click="clickFile(item)">
                  <img v-if="item.file == 'img'" class="img" :src="item.url">
                  <svg-icon v-else-if="item.file == 'video'" class="icon" icon-class="shipin" />
                  <svg-icon v-else-if="item.file == 'audio'" class="icon" icon-class="yinpin" />
                  <svg-icon v-else-if="item.file == 'file'" class="icon" icon-class="wenjian" />
                  <svg-icon v-else-if="item.file == 'package'" class="icon" icon-class="yasuobao" />
                  <svg-icon v-else class="icon" icon-class="weizhi" />

                  <span>{{item.name}}</span>
                  <i class="el-icon-success" />
                </a>
              </template>

            </div>

            <el-pagination class="file-page" background :current-page="2" :page-size="10"
              layout="total, prev, pager, next, jumper" :total="400">
            </el-pagination>
          </div>
        </div>
      </div>

      <div class="checkedFile-box">
        <div class="title">已选文件:</div>

        <div class="itemList">
          <a href="javascript:void(0)" :title="item.name" class="item" v-for="(item, index) in checkedFile">
            <img v-if="item.file == 'img'" class="img" :src="item.url">
            <svg-icon v-else-if="item.file == 'video'" class="icon" icon-class="shipin" />
            <svg-icon v-else-if="item.file == 'audio'" class="icon" icon-class="yinpin" />
            <svg-icon v-else-if="item.file == 'file'" class="icon" icon-class="wenjian" />
            <svg-icon v-else-if="item.file == 'package'" class="icon" icon-class="yasuobao" />
            <svg-icon v-else class="icon" icon-class="weizhi" />

            <span>{{item.name}}</span>

            <i class="el-icon-error" @click="clickFile(item)" />
          </a>
        </div>
      </div>

      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogUploadVisible = false">取 消</el-button>
        <el-button type="primary" @click="uploadFileOk">确 定</el-button>
      </div>
    </el-dialog>
    <!-- 选择/上传文件弹窗 》》》》 -->

  </div>
</template>

<script>
  import draggable from 'vuedraggable'
  export default {
    name: 'UploadFile',
    components: {
      draggable
    },
    props: {
      only: { //允许选择的文件类型['img', 'video', 'audio', 'file', 'package']
        type: Array,
        default: () => {
          return ['img', 'video', 'audio', 'file', 'package']
        }
      },
      num: {  //默认允许上传9个
        type: Number,
        default: 9
      },
      imgLists: { //已有的文件
        type: Array,
        default: () => {
          return []
        }
      }
    },
    data() {
      return {
        editIndex: '-1', //替换第几个
        dialogUploadVisible: false,
        fileType: 'all',
        testImg: [{
          id: 1,
          name: '图片名称',
          file: 'img',
          url: 'http://www.news.cn/photo/2021-11/23/1128089521_16375988814621n.jpg'
        }, {
          id: 2,
          name: '图片名称',
          file: 'img',
          url: 'http://www.news.cn/photo/2021-11/23/1128089521_16375988814971n.jpg'
        }, {
          id: 3,
          name: '图片名称',
          file: 'img',
          url: 'http://www.news.cn/photo/2021-11/23/1128089521_16375988815281n.jpg'
        }, {
          id: 4,
          name: '视频名称',
          file: 'video',
          url: 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'
        }, {
          id: 5,
          name: '音频名称',
          file: 'audio',
          url: 'http://em.sycdn.kuwo.cn/3e60a6c24cd470707a4095449165b22e/619c9cee/resource/n3/30/66/2205407643.mp3'
        }, {
          id: 6,
          name: '文件名称',
          file: 'file',
          url: 'http://baidu.com/file/test.txt'
        }, {
          id: 7,
          name: '压缩包名称',
          file: 'package',
          url: 'http://baidu.com/file/test.zip'
        }],
        checkedFile: [], //选中的文件
      }
    },
    computed: {
      inspectCheckedFile: function() { // 判断是否已选
        return function(id) {
          return this.checkedFile.some((item) => {
            return item.id === id;
          });
        }
      },
      inspectionType() { //判断Url文件类型
        return function(fileUrl) {
          let arr = fileUrl.split('.');
          // 文件类型
          let typeList = [{
            name: 'img',
            list: ['bmp', 'jpg', 'png', 'tga', 'gif', 'svg', 'psd', 'pcd', 'ai', 'raw', 'wmf', 'webp', 'swf', 'tiff']
          }, {
            name: 'shipin',
            list: ['avi', 'wmv', 'mpg', 'mpeg', 'vob', '3gb', 'mp4', 'mkv', 'rmvb', 'mov', 'flv', 'mvb']
          }, {
            name: 'wenjian',
            list: ['txt', 'doc', 'docx', 'xlsx', 'xls', 'log', 'pdf', 'ppt', 'tmdx', 'tmd', 'pmd', 'pmdx']
          }, {
            name: 'yinpin',
            list: ['cda', 'wav', 'mp3', 'wma', 'ra', 'midi', 'ogg', 'ape', 'flac', 'aac', 'rma', 'asf', 'mid', 'rmi', 'xmi', 'vqf', 'tvq', 'mod', 'ope', 'aiff', 'au']
          }, {
            name: 'yasuobao',
            list: ['rar', 'zip', 'rar4']
          }]

          if (arr.length > 1) {
            let type = arr[arr.length - 1];

            let tina = typeList.filter((p) => {
              return p.list.indexOf(type) > -1
            });
            if (tina.length) {
              return tina[0].name
            } else {
              return 'weizhi'
            }
          } else {
            return 'weizhi'
          }
        }
      }
    },
    created() {
      this.imgList = this.imgLists;
    },
    methods: {
      uploadFileOk(){ //弹窗确认
        const that = this;
        if(!that.checkedFile.length){
          that.$message({
            message: '请选择文件',
            type: 'warning'
          });
          return false;
        }

        if(that.editIndex > -1){
          that.imgList.splice(that.editIndex, 1, that.checkedFile[0].url)
        }else{
          that.imgList = [...that.imgList, ...that.checkedFile.map(item => {return item.url})]
        }
        that.dialogUploadVisible = false;

        that.$emit('getFileData', that.imgList);    //数据传递给父级
      },
      addFileBtn(){ //弹窗
        this.dialogUploadVisible = true;
        this.checkedFile = [];
        this.editIndex = -1;
      },
      componentsUploadDel(index) { //删除
        this.imgList.splice(index, 1)
      },
      componentsUploadEdit(index) { //替换
        this.editIndex = index;
        this.dialogUploadVisible = true;
        this.checkedFile = [];
      },
      setData(dataTransfer) { //官方文档给的,避免Firefox bug
        // to avoid Firefox bug
        // Detail see : https://github.com/RubaXa/Sortable/issues/1012
        dataTransfer.setData('Text', '')
      },
      edit_fileType(type) { //切换分组
        this.fileType = type;
      },
      clickFile(file) { //点击选中/取消选中文件
        const that = this;
        let checkedFile = that.checkedFile

        if(checkedFile.length >= that.num){
          that.$message({
            message: '最多选择'+that.num+'个',
            type: 'warning'
          });
          return false;
        }

        let tina = checkedFile.filter((p) => {
          return p.id == file.id;
        });
        let index = checkedFile.indexOf(tina[0]);
        if (index > -1) {
          that.checkedFile.splice(index, 1);
        } else {
          //判断是否允许上传该类型文件
          if (that.only.indexOf(file.file) === -1) {
            that.$message.error('不允许选择该类型文件');
            return false;
          }

          // 替换用的,删除原先勾选
          if(that.editIndex > -1){
            that.checkedFile = [];
          }

          that.checkedFile.push(file)
        }
      }
    }
  }
</script>

<style lang="scss">
  .components-upload-box {
    width: 100%;
    display: flex;
    flex-wrap: wrap;

    .item-box {
      display: flex;
      flex-wrap: wrap;
    }

    .item {
      width: 100px;
      height: 100px;
      border-radius: 5px;
      border: 1px solid #dcdfe6;
      background-color: #fafafa;
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
      cursor: pointer;
      margin-right: 10px;
      margin-bottom: 10px;
      overflow: hidden;
      position: relative;

      .icon {
        font-size: 100px;
      }

      .btn {
        position: absolute;
        bottom: 0;
        left: 0;
        background-color: rgba(0, 0, 0, 0.5);
        width: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        height: 25px;
        text-align: center;
        color: #ffffff;
        border-radius: 5px;
        font-size: 12px;

        .edit {
          margin-right: 10px;
        }

        .del {
          margin-left: 10px;
        }
      }

      &>img {
        width: 100%;
        height: 100%;
        object-fit: cover;
      }

      .el-icon-circle-plus-outline {
        font-size: 30px;
        margin-bottom: 10px;
      }
    }

    .add {
      border: 1px dashed #dcdfe6;
      font-size: 12px;
    }
  }

  .dialogUploadVisible {
    margin: 0;
    padding: 0;

    .el-dialog__body {
      padding: 0 20px;
    }

    .l-r {
      display: flex;
      justify-content: space-between;

      .left {
        width: 200px;
        border: 1px solid #e3e3e3;
        border-radius: 5px;
        height: 410px;
        overflow: auto;

        .title {
          background-color: #d9dbdc;
          height: 45px;
          line-height: 45px;
          padding: 0 20px;
        }

        .on {
          background-color: #e7e9ea;
        }

        .item {
          cursor: pointer;
          height: 45px;
          line-height: 45px;
          padding: 0 20px;
          display: flex;
          align-items: center;

          .icon {
            font-size: 25px;
            margin-right: 10px;
          }

          &:hover {
            background-color: #e7e9ea;
          }
        }
      }

      .right {
        width: 700px;

        .top {
          display: flex;
          align-items: center;
          justify-content: space-between;
          color: #000000;
        }

        .file-list-box {
          border: 1px solid #e3e3e3;
          border-radius: 5px;
          padding: 20px;
          padding-right: 10px;
          margin-top: 10px;

          .file-list {
            display: flex;
            flex-wrap: wrap;
            height: 270px;

            .file-item {
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
              width: 123px;
              height: 123px;
              cursor: pointer;
              margin-right: 10px;
              margin-bottom: 10px;
              position: relative;

              .icon {
                font-size: 70px;
              }

              .img {
                width: 70px;
                height: 70px;
                object-fit: cover;
              }

              &>span {
                margin-top: 10px;
                font-size: 12px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                width: 100px;
                text-align: center;
              }

              .el-icon-success {
                position: absolute;
                top: 5px;
                left: 5px;
                font-size: 20px;
                color: #409eff;
                display: none;
              }
            }

            .on {
              border: 1px solid #409eff;
              border-radius: 5px;

              .el-icon-success {
                display: block;
              }
            }


          }
        }

      }

      .file-page {
        margin-top: 20px;
      }
    }

    .checkedFile-box {
      min-height: 141px;
      border: 1px solid #e3e3e3;
      border-radius: 5px;
      margin-top: 10px;
      padding: 10px;

      .title {
        color: #000000;
      }

      .itemList {
        display: flex;
        flex-wrap: wrap;
        align-items: center;

        .item {
          margin-top: 10px;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
          cursor: pointer;
          margin-right: 10px;
          position: relative;

          .icon {
            font-size: 70px;
          }

          .el-icon-error {
            position: absolute;
            top: -10px;
            right: -10px;
            font-size: 20px;
          }

          .img {
            width: 70px;
            height: 70px;
            object-fit: cover;
          }

          &>span {
            margin-top: 10px;
            font-size: 12px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            width: 80px;
            text-align: center;
          }

          .el-icon-success {
            position: absolute;
            top: 5px;
            left: 5px;
            font-size: 20px;
            color: #409eff;
            display: none;
          }
        }
      }
    }
  }

</style>

我在main.js引入并注册组件:

import UploadFile from "@/components/UploadFile/index.vue"; //文件上传
Vue.component('UploadFile', UploadFile); //注册组件

使用:

<template>
    <UploadFile :imgLists='imgList' @getFileData="getFileData"></UploadFile>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        imgList: [    //默认已经有的图片
          'http://www.news.cn/photo/2021-11/23/1128090597_16376363492121n.jpg',
          'http://www.news.cn/photo/2021-11/23/1128090597_16376363492911n.mp4',
          'http://www.news.cn/photo/2021-11/23/1128090597_16376363493681n.cs',
          'http://www.news.cn/photo/2021-11/23/1128090597_16376363493681n.zip'
        ]
      }
    },
    methods: {
      getFileData(e){    //子组件传到父组件的图片数据
        console.log(e)
      }
    }
  }

</script>

<style>
</style>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值