Vue2文件上传相关
导入弹窗
<template><el-dialog:title="title":visible.sync="fileUploadVisible"append-to-bodyclose-on-click-modalclose-on-press-escapewidth="420px"><div v-if="showDatePicker">选择时间:<el-date-pickerv-model="dataDate"type="date"placeholder="选择日期"value-format="yyyy-MM-dd"format="yyyy-MM-dd"></el-date-picker></div><div class="my-upload"><el-uploadclass="upload-file-uploader"ref="fileUpload"multipledrag:action="uploadFileUrl":before-upload="handleBeforeUpload":file-list="fileList":limit="limit":on-error="handleUploadError":on-exceed="handleExceed":on-success="handleUploadSuccess":show-file-list="false":headers="headers":data="infoType"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em style="color: rgb(22, 93, 255)">点击上传</em></div></el-upload><!-- 文件列表 --><transition-groupclass="upload-file-list el-upload-list el-upload-list--text"name="el-fade-in-linear"tag="ul"><li:key="file.url"class="el-upload-list__item ele-upload-list__item-content"v-for="(file, index) in fileList"><el-link :href="file.url" :underline="false" target="_blank"><span class="el-icon-document"> {{ getFileName(file.name) }} </span></el-link><div class="ele-upload-list__item-content-action"><el-link:underline="false"@click="handleDelete(index)"type="danger">删除</el-link></div></li></transition-group></div><div style="text-align: center; padding: 15px">点击右侧按钮,下载导入模板<span class="import-text" v-if="showDownLoad" @click="onDownLoad">下载</span></div><div class="tip-text" v-if="tipText"><span>特别提醒:</span>{{ tipText }}</div></el-dialog>
</template>
<script>
import dayjs from "dayjs";
import { saveAs } from "file-saver";
import { getToken } from "@/utils/auth";export default {props: {title: {type: String,default: "导入",},tipText: {type: String,default: "",},// 值value: [String, Object, Array],// 数量限制limit: {type: Number,default: 99,},// 文件类型, 例如['png', 'jpg', 'jpeg']fileType: {type: Array,default: () => ["xls", "xlsx"],// default: () => ["png", "jpg", "jpeg"],},// 下载模版apidownloadApi: {type: Function,default: () => {console.log("[ ] >", "downloadApi");},},// 下载模板标题downloadTitle: {type: String,default: "导入模板",},// 导入apiimportApi: {type: Function,default: () => {console.log("[ ] >", "importApi");},},// 是否真是下载模板showDownLoad: {type: Boolean,default: true,},showDatePicker: {type: Boolean,default: false,},// 模板下载地址uploadUrl: { type: String, default: "" },// 导入需要的InfoTypeinfoType: {type: Object,default: () => {},},},data() {return {number: 0,uploadList: [],uploadFileUrl: process.env.VUE_APP_BASE_API + "/file/upload", // 上传文件服务器地址headers: {Authorization: "Bearer " + getToken(),},fileList: [],fileUploadVisible: false,file: null,dataDate: "",};},mounted() {},watch: {value: {handler(val) {if (val) {let temp = 1;// 首先将值转为数组const list = Array.isArray(val) ? val : this.value.split(",");// 然后将数组转为对象数组this.fileList = list.map((item) => {if (typeof item === "string") {item = { name: item, url: item };}item.uid = item.uid || new Date().getTime() + temp++;return item;});} else {this.fileList = [];return [];}},deep: true,immediate: true,},},methods: {onShow() {this.fileUploadVisible = true;console.log(process.env.VUE_APP_BASE_API + "/file/upload");this.dataDate = "";},// 下载模版onDownLoad() {this.downloadApi().then((res) => {const blob = new Blob([res]);saveAs(blob, `${this.downloadTitle}.xlsx`);});},// 上传前校检格式和大小handleBeforeUpload(file) {if (this.showDatePicker && this.dataDate == "") {this.$message.warning("请先选择日期");} else {// 校检文件类型if (this.fileType) {const fileName = file.name.split(".");const fileExt = fileName[fileName.length - 1];const isTypeOk = this.fileType.indexOf(fileExt) >= 0;if (!isTypeOk) {this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);return false;}}this.file = file;this.$modal.loading("正在上传文件,请稍候...");this.number++;return true;}},// 文件个数超出handleExceed() {this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);},// 上传失败handleUploadError(err) {this.$modal.msgError("上传文件失败,请重试");this.$modal.closeLoading();},// 上传成功回调handleUploadSuccess(res, file) {if (this.showDatePicker && this.dataDate == "") {} else {if (res.code == 200) {const formData = new FormData();formData.append("file", this.file);if (this.showDatePicker) {formData.append("dataDate", this.dataDate);}this.importApi(formData).then((res) => {if (res.code == 0 || res.code == 200) {this.$message.success("导入成功");this.uploadList = [];this.fileList = [];this.fileUploadVisible = false;this.$modal.closeLoading();this.$emit("getList");}}).catch(() => {this.$modal.closeLoading();});return;}this.$modal.msgError("上传文件失败,请重试");this.$modal.closeLoading();}},// 删除文件handleDelete(index) {this.fileList.splice(index, 1);this.$emit("input", this.listToString(this.fileList));},// 上传结束处理uploadedSuccessfully() {if (this.number > 0 && this.uploadList.length === this.number) {this.fileList = this.fileList.concat(this.uploadList);this.uploadList = [];this.number = 0;this.$emit("input", this.listToString(this.fileList));this.$modal.closeLoading();}},// 获取文件名称getFileName(name) {// 如果是url那么取最后的名字 如果不是直接返回if (name?.lastIndexOf("/") > -1) {return name.slice(name.lastIndexOf("/") + 1);} else {return name;}},// 对象转成指定字符串分隔listToString(list, separator) {let strs = "";separator = separator || ",";for (let i in list) {strs += list[i].url + separator;}return strs != "" ? strs.substr(0, strs.length - 1) : "";},},
};
</script>
<style lang="scss" scoped>
::v-deep .el-dialog__header > .el-dialog__title {font-weight: 600 !important;
}.import-text {color: rgb(22, 93, 255);text-align: center;font-size: 14px;cursor: pointer;
}.my-upload {margin-top: 20px;display: flex;justify-content: center;.el-icon-upload {color: rgb(22, 93, 255);font-size: 100px;}
}.tip-text {padding: 0 10px;margin-top: 20px;color: rgb(241, 51, 51);
}.upload-file-uploader {margin-bottom: 5px;
}.upload-file-list .el-upload-list__item {border: 1px solid #e4e7ed;line-height: 2;margin-bottom: 10px;position: relative;
}.upload-file-list .ele-upload-list__item-content {display: flex;justify-content: space-between;align-items: center;color: inherit;
}.ele-upload-list__item-content-action .el-link {margin-right: 10px;
}:v-deep .el-upload,
.el-upload--text {// margin: 0 auto !important;width: 100% !important;
}
</style>
弹窗使用
<!-- 导入 -->
<FileUploadref="fileUpload"title="导入数据":importApi="importRealApi":onReset="getRealList"@getList="getList":infoType="infoType":downloadApi="downRealTempleteApi":showDatePicker="true":tipText="tipText"downloadTitle="房产交易信息导入模板"
/>
1.为什么要进行 const formData = new FormData() 处理
HTTP 请求中,文件不能直接作为普通文本字段发送,需要以二进制流的形式上传。FormData
专门用于构造包含文件的表单数据,浏览器会自动将其编码成 multipart/form-data
格式,符合文件上传的标准
2.multipart/form-data 格式是什么
multipart/form-data
是一种专门为上传文件设计的 HTTP 请求编码格式,它通过分隔符将多个字段(包括文件)分开,保证文件数据能被正确传输和解析,是文件上传的标准方式。