前端控制上传文件,后统一提交

195 阅读3分钟

React 批量上传

这里使用了节流函数,通过上传方法:beforeUpload 触发方法来判断是否全部上传。

```import React, { useState, useRef } from 'react';
import { Upload, Button } from 'antd';
const { INVOICE_SERVER } = process.env;
import debounce from 'lodash/debounce';
import { getToken } from '../../../utils/utils';
import { UploadOutlined } from '@ant-design/icons';
import { request, history } from 'umi';

export default function uploadComponent(props) {
  const [btnStatus, setBtnStatus] = useState(false);
  const [uploadRes, setuploadRes] = useState({});
  const fileLists = useRef();
  const handleUpload = debounce(async (value) => {
    // 请求数据
    console.log('请求数据');
    const formData = new FormData();
    Promise.all(
      fileLists.current.map(async (file) => {
        try {
          formData.append('excelFile', file);
        } catch (error) {}
      }),
    ).then(() => {
      formData.append('templateNo', props.templateNo);
      
      request(`${INVOICE_SERVER}/excel/import`, {
        method: 'POST',
        data: formData,
        headers: {
          'x-access-token': getToken(),
        },
        requestType: 'form',
      })
        .then((response) => {
          setBtnStatus(false);
          setuploadRes(response)
          props.handleChangeUpload(response)
        })
        .catch((error) => {
          setBtnStatus(false);
        });
    }).catch( e => {
        setBtnStatus(false);
    });
  }, 1500);
  const uploadprops = {
    action: `${INVOICE_SERVER}/import`,
    name: 'excelFile',
    method: 'post',
    showUploadList: false, // 上传的时候不显示文件以及进度
    headers: {
      'x-access-token': getToken(),
    },
    beforeUpload(file, fileList) {
      console.log(file, fileList, 'beforeUpload');
      fileLists.current = [...fileList]; // 上传的时候不显示文件
      setBtnStatus(true);
      handleUpload();
      
      return false;
    },
    onChange({ file }) {
      console.log(file, 'file');
    },
    defaultFileList: [],
    multiple: true,
  };

  return (
    <div>
      <Upload {...uploadprops}>
        <Button type="primary" disabled={btnStatus} icon={<UploadOutlined />}>
          导入
        </Button>
      </Upload>
    </div>
  );
}

Vue

1. 使用Zip 批量上传

上传zip包,通过前端 jszip 三方包进行解压来统一上传

<el-upload
            :file-list="fileList"
            :before-upload="beforeUpload"
            :on-exceed="handleExceed"
            :limit="1"
            :show-file-list="true"
          >
            <template #trigger>
              <el-button type="primary">点击上传</el-button>
            </template>
            <template #tip>
              <div class="el-upload__tip">
                请上传单个ZIP压缩包
              </div>
            </template>
            <div class="uploaded-file" v-for="file in fileList" :key="file.uid">
              {{ file.name }}
            </div>
          </el-upload> 
          
    beforeUpload(file) {
      if (!this.form.type) {
        ElMessage.error({ message: "请选择类型" });
        return false;
      }
      if (file.type === "application/x-zip-compressed") {
        this.btnStatus = true;
        // 如果是zip文件格式,那么进行添加
        this.fileList.pop();
        this.fileList.push(file);
        const zip = new JSZip();
        let formData = new FormData();
        zip
          .loadAsync(file, {
            decodeFileName: function (bytes) {
              return iconv.decode(bytes, "gbk"); // 按中文编码
            },
          })
          .then((contents) => {
            // 对于每个文件,你可以调用 file(name) 来获取它的内容
            console.log(contents.files, "contents.files");
            let arrFn = [];
            for (let name in contents.files) {
              if (!contents.files[name].dir) {
                let promise = contents.files[name].async("blob").then((fileContent) => {
                  formData.append("files", fileContent, name);
                });
                arrFn.push(promise);
              }
            }
            Promise.all(arrFn).then(() => {
              formData.append("type", this.form.type);
              this.uploadFile(formData);
            });
          })
          .catch((error) => {
            this.btnStatus = false;
            // 处理解压错误
            console.error(error);
          });

        // 阻止 el-upload 组件自动上传文件
        return false;
      } else {
        return ElMessage({ message: "请上传zip格式文件", type: "error" });
      }
        },

多文件直接上传,每一个文件可以查看进度,并且可以进行排序,可编辑file标题

排序通过 sortable.js

<template>
  <div>
    <el-drawer
      class="upload-drawer"
      title="新增剧集"
      v-model="drawerVisible"
      direction="rtl"
      size="50%"
      :before-close="handleClose"
    >
      <el-upload
      :disabled="successFile.length !== fileList.length"
        v-model:file-list="fileList"
        class="upload-demo"
        :action="actionUrl"
        :headers="headers"
        multiple
        :before-upload="handleBeforeUpload"
        :on-preview="handlePreview"
        :on-change="handleAvatarChangeVideo"
        :on-exceed="handleExceed"
        :show-file-list="false"
        accept="video/mp4"
      >
        <el-button type="primary" :disabled="successFile.length !== fileList.length" class="btn-upload">点击上传</el-button>
        <template #tip>
          <div v-if="statusRef" class="upload-content">
            <div v-for="file in fileList" class="upload-content-item" :key="file.uid">
              <div>
                <span>标题:</span
                ><el-input style="width: 200px" v-model="file.newName"></el-input>

                <el-button
                  :disabled="successFile.length !== fileList.length"
                  class="btn"
                  type="danger"
                  @click="handleRemove(file)"
                  plain
                  >删除</el-button
                >
                <el-tooltip
                  class="box-item"
                  effect="dark"
                  content="移动"
                  placement="right"
                >
                  <el-icon
                    style="line-height: 32px; height: 32px"
                    size="20px"
                    color="#337ecc"
                    class="draggableClass btn"
                    ><Rank
                  /></el-icon>
                </el-tooltip>
              </div>
              <div class="progress">
                <span>剧集:</span>{{ file.name }}
                <el-progress :percentage="file.percentage"></el-progress>
              </div>
            </div>
          </div>
        </template>
      </el-upload>
      <div class="mt-footer">
        <el-button class="subBtn" :disabled="fileList.length == 0 || successFile.length !== fileList.length" @click="handleSubmit" type="primary" plain>上传</el-button>
        <el-button class="subBtn" @click="handleClose" type="danger" plain>取消</el-button>
      </div>
    </el-drawer>
  </div>
</template>

<script setup lang="ts" name="UploadSketch">
import {
	fetchBatchSave
} from '/@/api/sketch/index';
import { defineProps, toRef, ref, defineEmits } from "vue";
import { Session } from "/@/utils/storage";
import Sortable from "sortablejs";
import { ElMessage } from "element-plus";

const props = defineProps({
  visible: Boolean,
  shortPlayId: Number || String,
});

const drawerVisible = toRef(props, "visible"); // 抽屉是否显示
const fileList = ref([]); // 上传文件列表

const actionUrl = `${import.meta.env.VITE_API_URL}/${props.shortPlayId}`;
const headers = {
  "x-access-token": Session.get("token"),
};

const emit = defineEmits(["handleCloseAddPlay"]);
const handleClose = () => {
  emit("handleCloseAddPlay");
  console.log("close");
  
};

// 文件上传

/**
 * 文件上传前的钩子
 * @param file
 * @returns
 */
const handleBeforeUpload = (file: any) => {
  const isLt1G = file.size / 1024 / 1024 / 1024 < 1;
  let isExist = fileList.value.some((item) => item.name === file.name);
  if (!isLt1G || isExist) {
    ElMessage.error(!isLt1G ? "所选上传文件大小不能超过 1G" : '文件已存在');

    const index = fileList.value.findIndex((item) => item.uid === file.uid);
    if (index > -1) {
      fileList.value.splice(index, 1);
    }
    return false;
  }

};

const handlePreview = (file) => {
  console.log("@handlePreiew");
};

// 当超出限制时,执行的钩子函数
const handleExceed = () => {
  console.log("@handleExceed", file);
};

const successFile = ref([]); // 这里保存所有上传成功的文件
const handleAvatarChangeVideo = (file: any, fileList: any) => {
  if (file.status === "success") {
    file.newName = file.name.substring(0, file.name.lastIndexOf("."));
    if (file.response.entity) {
      successFile.value.push({
        name: file.name,
        url: file.response.entity,
      });
      // 判断上传成功的文件和 fileList 是否一样,如果一样, 表示全部上传完成
      if (successFile.value.length === fileList.length) {
        // 初始化 sortable、赋值、
        initSort();
      }
    }
  }
};

const handleRemove = (file: any) => {
  fileList.value = fileList.value.filter((item) => item.uid !== file.uid);
  successFile.value = fileList.value.filter((item) => item.name !== file.name);
};


const newFileList = ref([]);
const statusRef = ref(true);
const initSort = () => {
  const table = document.querySelector(`.upload-content`);
  Sortable.create(table, {
    group: "shared",
    animation: 100,
    handle: ".draggableClass",
    ghostClass: "ghostClass",
    onStart: () => {},
    // 结束拖动事件
    onEnd: (evt: any) => {
      statusRef.value = false;
      let oldIndex = evt.oldIndex;
      let newIndex = evt.newIndex;
      let temp = fileList.value[oldIndex];
      if (oldIndex < newIndex) {
        //向下移动调整顺序
        for (var i = oldIndex; i < newIndex; i++) {
          fileList.value[i] = fileList.value[i + 1];
        }
      } else if (oldIndex > newIndex) {
        //向上移动时调整顺序
        for (var i = oldIndex; i > newIndex; i--) {
          fileList.value[i] = fileList.value[i - 1];
        }
      }
      fileList.value[newIndex] = temp;
      statusRef.value = true;
      initSort();
    },
  });
};

const handleSubmit = () => {
  console.log("handleSubmit", fileList.value);
  let addVOS = fileList.value.map((item: any) => {
    return {
      name: item.newName,
      tencentOriginalUrl: item.response.entity,
    };
  });
  let data = {
    shortPlayId: props.shortPlayId,
    addVOS: addVOS
  }
  fetchBatchSave(data).then( res => {
    if (res.data.status === 200 && res.data.entity) {
        ElMessage.success('新增成功');
        handleClose()
    }
  })
  
};
</script>

<style lang="scss" scoped>
.btn-upload {
  margin: 6px 10px;
}

.upload-content {
  display: block;
  padding: 20px;
  margin-bottom: 60px;
  .upload-content-item {
    margin-top: 10px;
    padding: 10px 20px;
    width: 100%;
    height: 100px;
    border: 1px solid #ccc;
  }
  .btn {
    margin-left: 10px;
    float: right;
  }
  .progress {
    margin-top: 10px;
  }
}

.draggableClass {
  cursor: pointer;
}

.upload-drawer {
  position: relative;
  .mt-footer {
    position: absolute;
    margin-top: 60px;
    width: 100%;
    bottom: 0;
    height: 50px;
    line-height: 50px;
    margin-right: 20px;
    background-color: #eee;
    .subBtn {
      float: right;
      margin-top: 9px;
      margin-right: 20px;
    }
  }
}
</style>
<style>
.ghostClass {
  background-color: #409eff !important;
}
</style>