优化上传体验:使用Vue和Element UI实现多文件分开上传与进度条显示的完美指南

        最近接触一个项目中用到文件上传并要求可以添加编辑上传的文件信息,将文件转成二进制流的方式传参给后端,并要求带有实时进度条

        话不多说,直接上效果图

  1. template代码

<template>
  <div>
    <p class="title">可以选择多文件进行批量上传</p>
    <div>
      <el-upload
        class="upload-demo"
        ref="upload"
        action="#"
        :http-request="uploadReport"
        :headers="{ 'x-auth-token': token }" //必要的条件,请求时的token,根据看自己请求时的toen字段名来决定
        accept=".pdf, .doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.html"//文件类型
        :auto-upload="false"
        :on-change="changeFile"
        :file-list="fileList"
        :show-file-list="false"
        multiple
      >
        <el-button slot="trigger" type="primary">选取文件</el-button>
        <div style="padding: 0 10px">
          <el-table
            :data="fileList"
            border
            :header-cell-style="{ background: '#ddd' }"
          >
            <el-table-column
              prop="name"
              label="文件名"
              align="center"
              show-overflow-tooltip
            >
            </el-table-column>
            <el-table-column prop="size" label="大小" align="center">
            </el-table-column>
            <el-table-column label="进度" align="center">
              <template slot-scope="scope">
                <el-progress
                  :text-inside="true"
                  :stroke-width="24"
                  :percentage="progressArr[scope.$index]"
                  status="success"
                ></el-progress>
              </template>
            </el-table-column>
            <el-table-column prop="status" label="状态" align="center">
              <template slot-scope="scope">
                <span v-if="statusArr[scope.$index] !== '上传成功'" style="color:red">{
   
   {statusArr[scope.$index]}}</span>
                <span
                  v-else-if="
                    scope.row.status === 'ready' &&
                    progressArr[scope.$index] === 0
                  "
                  >等待上传
                </span>
                <span
                  v-else-if="
                    progressArr[scope.$index] > 0 &&
                    progressArr[scope.$index] !== 100
                  "
                  >正在上传
                </span>
                <span v-else-if="progressArr[scope.$index] === 100"
                  >上传成功
                </span>                
              </template>
            </el-table-column>
            <el-table-column label="APT组织" align="center">
              <template slot-scope="scope">
                <el-select
                  v-model="apt[scope.$index].apt"
                  placeholder="请选择或搜索APT"
                  clearable
                  filterable
                  @change="changeApt($event, scope.$index)"
                >
                  <el-option
                    v-for="item in aptsArr"
                    :key="item.id"
                    :label="item.name"
                    :value="item.name"
                  >
                  </el-option>
                </el-select>
              </template>
            </el-table-column>
            <el-table-column prop="date" label="描述" align="center">
              <template slot-scope="scope">
                <el-input
                  v-model="apt[scope.$index].des"
                  placeholder="请输入描述"
                  clearable
                  @input="changeInput($event, scope.$index)"
                ></el-input>
              </template>
            </el-table-column>
            <el-table-column label="封面" align="center">
              <template slot-scope="scope">
                <el-upload
                  class="avatar-uploader"
                  action="你的上传地址"
                  :headers="{ 'x-auth-token': token }"
                  :show-file-list="false"
                  :on-success="handleLogoSuccess"
                  :before-upload="beforeLogoUpload"
                >
                  <el-button plain @click="curRowIndex = scope.$index">
                    <span v-if="!apt[scope.$index].logo">
                        <i class="el-icon-upload"></i>
                        上传封面
                    </span>
                    <span v-else>重新上传</span>
                  </el-button>
                </el-upload>
              </template>
            </el-table-column>
            <el-table-column
              label="操作"
              align="center"
              width="120px"
              show-overflow-tooltip
            >
              <template slot-scope="scope">
                <el-button
                  size="mini"
                  type="danger"
                  class="el-icon-delete"
                  @click="handleDelete(scope.$index, scope.row)"
                  >删除</el-button
                >
              </template>
            </el-table-column>
          </el-table>
        </div>
        <el-button
          style="margin-left: 10px"
          type="success"
          @click="submitUpload"
          >开始上传
        </el-button>
      </el-upload>
    </div>
  </div>
</template>

 2. script代码

<script>
import axios from "axios";
import { findAptsAll } from "../../api/ttp.js";//这里是获取下拉框里面的选项
export default {
  name: "uploadReports",
  data() {
    return {
      token: sessionStorage.token,
      curRowIndex: 0,
      aptsArr: [],//下拉框选项
      fileList: [],//文件上传的列表
      apt: [], //这是是我接触到需要多传递的给后端的参数
      timer: null,//定时器的返回值
      progressArr: [],//控制进度条的进度
      statusArr: [],//控制文件上传的状态
    };
  },
  async created() {
    let { data } = await findAptsAll({ limit: 1000, page: 1 });
    this.aptsArr = data.data;
  },
  methods: {
    handleDelete(index, row) { //删除操作
      this.fileList.splice(index, 1);
      this.apt.splice(index, 1);
      this.progressArr.splice(index, 1);
      this.statusArr.splice(index,1)
    },
    changeFile(file, fileList) { 
      this.fileList = fileList.map((item, index) => {
        if (typeof item.size === "number") {
          this.apt[index] = { fileName: item.name, apt: "", logo: "", des: "" };
          this.progressArr[index] = 0;
          this.statusArr[index] = '上传成功'
          let str = item.size / 1024 + "";
          let arr = str.split(".");
          return {
            ...item,
            size: [arr[0], arr[1].slice(0, 1)].join(".") + "kb",
          };
        } else {
          return item;
        }
      });
    },
    submitUpload() {
      this.$refs.upload.submit();
    },
    handleLogoSuccess(res, file) {
      this.$message.success("封面上传成功!");
      this.apt = this.apt.map((item, index) => {
        if (index === this.curRowIndex) {
          return {
            ...item,
            logo: res.data,
          };
        } else {
          return item;
        }
      });
    },
    beforeLogoUpload(file) { //上传时的封面类型、大小控制
      const isJPG = file.type === "image/jpeg" || "image/png";
      const isLt10M = file.size / 1024 / 1024 < 10;

      if (!isJPG) {
        this.$message.error("上传封面只能是 JPG 或 PNG 格式!");
      }
      if (!isLt10M) {
        this.$message.error("上传封面大小不能超过 10MB!");
      }
      return isJPG && isLt10M;
    },
    uploadReport() { //由于submit()会触发多次自定义的http-request的方法,如果不加定时器控制的话,会重复触发上传
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }
      this.timer = setTimeout(() => {
        this.fileList.forEach((item, index) => {
          this.statusArr = this.statusArr.map(item=>'上传成功')//为了使得上传失败,修改后正常上传时的上传状态可以更改
          let formData = new FormData();
          formData.append("file", item.raw);
          formData.append("apt", JSON.stringify(this.apt));
          axios({
            url: "上传路径",
            method: "post",
            data: formData,
            headers: {
              "Content-Type":
                "multipart/form-data; boundary=----WebKitFormBoundarynl6gT1BKdPWIejNq",
              "x-auth-token": sessionStorage.token,
            },
            onUploadProgress: (progressEvent) => {//axios自带可以获取到文件上传是的进度,可以直接用
              let num =
                ((progressEvent.loaded / progressEvent.total) * 100) | 0;
              this.progressArr = this.progressArr.map((item, ind) => {
                if (ind === index) {
                  return num;
                } else {
                  return item;
                }
              });
            },
          }).then(({ data }) => {
            this.statusArr = this.statusArr.map((item, ind) => {
              if (ind === index && data.code!==0) {
                return data.msg;
              } else {
                return item;
              }
            });
          });
        });
      }, 100);
    },
    changeApt(val, index) {
      this.apt = this.apt.map((item, ind) => {
        if (ind === index) {
          return { ...item, apt: val };
        } else {
          return item;
        }
      });
    },
    changeInput(val, index) {
      this.apt = this.apt.map((item, ind) => {
        if (ind === index) {
          return { ...item, des: val };
        } else {
          return item;
        }
      });
    },
  },
};
</script>

注意:

        在编辑所有的信息时,由于用到的都是引用数据类型(数组),所以每次都要重新赋值(也就是为什么页面中的赋值都是以map的形式),来确保页面可以正常渲染

猜你喜欢

转载自blog.csdn.net/LaityMm/article/details/121947937