el-upload组件
关于 Element-Plus 组件库,大家应该都比较熟悉,今天来总结一下关于Element-Plus中的上传组件 ----- upload组件
一、安装
包管理器
在项目中,我们通常会使用Vite和webpack搭建项目,所以我们使用包管理器进行安装Element-Plus组件库
# NPM
npm install element-plus --save
# Yarn
yarn add element-plus
# pnpm
pnpm install element-plus
这里就不说CDN引入了,该篇主要说的只是单个组件,有兴趣上官网 Element-Plus
二、引入组件库
拿Vite构建工具举例,我们可以将整个组件库引入,这会使我们使用的时候更方便,在 main.ts 文件下:
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
如果您使用 Volar,请在 tsconfig.json 中通过 compilerOptions.type 指定全局组件类型。
// tsconfig.json
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}
还有按需导入以及自动导入,有兴趣上官网 Element-Plus
三、Upload上传
通过点击或者拖拽上传文件,结合我的项目实现的功能来说
该功能实现为点击上传文件,效果如下:
实现编码
1、对话框组件 -- dialog
我们需要引入对话框组件,为了之后的业务逻辑,先实现点击按钮弹出对话框,以及取消关闭对话框,或者确定进行下一步业务逻辑。
根据我的项目逻辑,为父子组件传值,首先是父组件 A.vue
<template>
<div>
<el-button type="primary" icon="Download" @click="uploadFn">导入</el-button>
</div>
<UploadExcel :dialogUploadShow="dialogUploadVisible" @uploadCloseShow="uploadClose" />
</template>
<script lang="ts" setup>
// 当前文件夹下的创建的components文件夹里面引入uploadExcel.vue
import UploadExcel from "./components/uploadExcel.vue";
// 导入对话框显示/隐藏初始值
let dialogUploadVisible = ref(false);
// 点击弹出导入对话框
function uploadFn() {
dialogUploadVisible.value = true;
}
// 关闭导入对话框
function uploadClose() {
dialogUploadVisible.value = false;
}
</script>
子组件uploadExcel.vue代码编写
<template>
<el-dialog v-model="dialogUploadShow" title="操作" :before-close="cancel" custom-class="dialog" width="42%" >
<!-- upload上传组件占位 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
// 父传子 -- 父组件传值到子组件 :点击父组件按钮,弹出对话框
let props = withDefaults(
defineProps<{
dialogUploadShow: boolean;
}>(),
{
// 初始数据
dialogUploadShow: false,
}
);
// 子传父 -- 控制父组件关闭对话框
let emits = defineEmits<{
(event: "uploadCloseShow"): void;
}>();
// 点击取消按钮/关闭对话框/清空数据
// before-close在用户点击关闭按钮或者对话框的遮罩区域时被调用/关闭对话框
const cancel = () => {
emits("uploadCloseShow"); // 关闭对话框
// ---- 清空数据逻辑
};
// 点击确认按钮/判断/请求逻辑/关闭对话框/清空数据
const confirm = () => {
// ----请求相关逻辑
emits("uploadCloseShow"); // 关闭对话框
// ---- 清空数据逻辑
}
</script>
以上的代码即是对话框显示/隐藏,父子传值的实现
2、上传组件 -- upload
根据项目业务逻辑,我们现在需要引入 el-upload 组件,即为下面的代码:
<!-- 添加上传组件 -->
<el-upload v-model:file-list="fileList" class="upload-demo" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" multiple :on-preview="handlePreview" :on-remove="handleRemove" :before-remove="beforeRemove" :limit="3" :on-exceed="handleExceed">
<el-button plain icon="Download">点击上传</el-button>
</el-upload>
<!-- 添加上传组件 -->
upload基础用法中相关属性:
| 名称 | 描述 | 类型 | 默认值 | 必填 |
|---|---|---|---|---|
action | 请求 URL | string | — | 是 |
v-model:file-list | 默认上传文件 | UploadUserFile[] | [ ] | 否 |
multiple | 是否支持多选文件 | boolean | false | 否 |
on-preview | 点击文件列表中已上传的文件时的钩子 | (uploadFile: UploadFile) => void | — | 否 |
on-remove | 文件列表移除文件时的钩子 | (uploadFile: UploadFile, uploadFiles: UploadFiles) => void | — | 否 |
before-remove | 删除文件之前的钩子,参数为上传的文件和文件列表, 若返回 false或者返回 Promise且被 reject,则停止删除。 | (uploadFile: UploadFile, uploadFiles: UploadFiles) => Awaitable<boolean> | — | 否 |
on-exceed | 当超出限制时,执行的钩子函数 | (files: File[], uploadFiles: UploadFiles) => void | — | 否 |
limit | 允许上传文件的最大数量 | number | — | 否 |
根据项目需求,根据官网给的基础用法进行某些属性的删减,并添加我们需要的属性,代码如下:
<!-- uploadExcel.vue -->
<template>
<el-dialog v-model="dialogUploadShow" title="操作" :before-close="cancel" custom-class="dialog" width="42%">
<!-- 添加上传组件 -->
<el-upload class="upload-demo" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :limit="1" :on-change="handleChange" :auto-upload="false" accept=".xls,.xlsx">
<el-button plain icon="Download">点击上传</el-button>
</el-upload>
<!-- 添加上传组件 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm(uploadFileList)">确认</el-button>
</div>
</template>
</el-dialog>
</template>
新增属性描述如下:
| 名称 | 描述 | 类型 | 默认值 | 必填 |
|---|---|---|---|---|
on-change | 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 | (uploadFile: UploadFile, uploadFiles: UploadFiles) => void | — | 否 |
auto-upload | 是否自动上传文件 | boolean | true | 否 |
accept | 接受上传的文件类型(thumbnail-mode 模式下此参数无效) | string | — | 否 |
即:
on-change是我们看状态的钩子函数;
auto-upload修改默认值为false,根据我们的业务逻辑需要点击了对话框组件的确认按钮才发送请求,所以需要阻止upload组件默认发请求的行为,否则会进行一个文件二次发送请求;
accept 为我们设定的文件类型,则当我们点击上传时,会筛选出携带我们需要的后缀名类型文件;
实现上传文件逻辑
首先,我们先定义一些初始值变量,方便对数据进行操作渲染,我们在子组件uploadExcel.vue继续编码
<!-- uploadExcel.vue -->
<template>
<el-dialog v-model="dialogUploadShow" title="操作" :before-close="cancel" custom-class="dialog" width="42%">
<!-- 添加上传组件 -->
<el-upload class="upload-demo" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :limit="1" :on-change="handleChange" :auto-upload="false" accept=".xls,.xlsx">
<el-button plain icon="Download">点击上传</el-button>
</el-upload>
<!-- 添加上传组件 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm(uploadFileList)">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
// 父传子 -- 父组件传值到子组件 :点击父组件按钮,弹出对话框
let props
...
// 点击确认按钮/判断/请求逻辑/关闭对话框/清空数据
const confirm = () => {
// ----请求相关逻辑
emits("uploadCloseShow"); // 关闭对话框
// ---- 清空数据逻辑
}
// 导入参数的初始值
let fileRaw = ref();
// 用来将数据赋值 传到confirm确认按钮 -- 形参
let uploadFileList = ref();
// 选中文件后把参数赋值
const handleChange: UploadProps["onChange"] = async (uploadFile,uploadFiles) => {
// 设置初始值
fileRaw.value = [];
// 遍历数组的值
for (let i of uploadFiles) {
if (i.raw) {
// 将文件列表添加到数组
fileRaw.value.push(i.raw);
}
}
// 这个变量uploadFileList用来给确认按钮当形参
uploadFileList.value = uploadFile;
};
</script>
handleChange 绑定的属性是on-change,是我们看状态的钩子函数,这里我们接受的两个形参都是文件数据,当选中文件后,现将fileRaw赋值为空数组,然后我们遍历数组,这里的for of 是ES6新增循环方法,遍历数组里的每一项值,而不是for in遍历数组的索引,根据业务逻辑,我们把要上传的文件添加进数组里面进行下一步操作。
当我们把上传的文件添加到数组后,我们需要对数据进行处理,根据和后端交互,将文件传到后端需要将参数转化成二进制的格式,那么我们往下走逻辑。
<!-- uploadExcel.vue -->
<template>
<el-dialog v-model="dialogUploadShow" title="操作" :before-close="cancel" custom-class="dialog" width="42%">
<!-- 添加上传组件 -->
<el-upload class="upload-demo" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :limit="1" :on-change="handleChange" :auto-upload="false" accept=".xls,.xlsx" ref="uploadEle">
<el-button plain icon="Download">点击上传</el-button>
</el-upload>
<!-- 添加上传组件 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm(uploadFileList)">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ElMessage, ElMessageBox, UploadFile, UploadProps } from "element-plus";
import { upLoadProjectApi } from "../../../request/api";
// 父传子 -- 父组件传值到子组件 :点击父组件按钮,弹出对话框
let props
// ...
// 获取upload实例 -- 做清空处理
const uploadEle = ref();
// 用来将数据赋值 传到confirm确认按钮 -- 形参
let uploadFileList = ref();
// 选中文件后把参数赋值
const handleChange: UploadProps["onChange"] = async (uploadFile,uploadFiles) => {
// ...
// 这个变量uploadFileList用来给确认按钮当形参
uploadFileList.value = uploadFile;
};
// 点击确认按钮 接收参数(uploadFileList)
const confirm = async (uploadFile: UploadFile) => {
// 判断是否有上传文件
if (uploadFile) {
// 这里是点击确定发送过一次请求后,第二次进入进行判断
if (uploadFile.raw) {
// 将参数转换为二进制
const formData = new FormData(); // new FormData
formData.append("fileList", uploadFile.raw!); // 参数
// 发送POST上传文件请求 携带参数,请求头
let upLoadProjectRes = await upLoadProjectApi(formData, {
"Content-Type": "multipart/form-data;",
});
// code为0时则成功 -- 继续进行业务逻辑
if (upLoadProjectRes.data.code === 0) {
// 关闭对话框
emits("uploadCloseShow");
ElMessage({
message: upLoadProjectRes.data.msg,
type: "success",
});
// 清空文件列表等
fileRaw.value = [];
uploadFileList.value = [];
uploadEle.value.clearFiles();
} else {
// 关闭对话框
emits("uploadCloseShow");
ElMessage.error("表格格式不对!请检查!!");
// 清空文件列表等
fileRaw.value = [];
uploadFileList.value = [];
uploadEle.value.clearFiles();
}
} else {
// 第二次没有就提示请导入
ElMessage.error("请选择导入文件表格!");
}
} else {
// 没有就提示请导入
ElMessage.error("请选择导入文件表格!");
}
};
这里我用的是FormData()方法,只需要在点击提交时进行数据序列化就可以了,将formData处理好的文件当做参数传到后端,携带请求头。
这是请求方法,封装了axios请求拦截器和响应拦截器。接口类型就是返回/传值的对象树
浏览器载荷显示二进制,标头返回200,即请求成功了,响应则看后端有没有进行二次校验,如果可以的话,前端不需要主动去处理xls,xlsx的格式,所以只需要完成业务逻辑,当第一次点击进入对话框时,没有上传文件,为空则弹出提示框(请选择导入文件表格..),有值往下走逻辑,我这里套多了一次if判断,是当完成一次文件上传之后,第二次点击按钮打开弹框,防止重复请求,确认uploadFile.raw是不是有值,有值才往下走进行请求发送,最后就是返回回来的code码了,为0就是成功了,弹出提示组件,到此,导入功能就完成啦。
还有一种方法就是将文件转为Base64编码,方法给大家伙展示:
<script lang="ts" setup>
// Base64编码转换方法
function getBase64(file: any) {
return new Promise((resolve, reject) => {
const reader = new FileReader(); // 定义方法读取文件
reader.readAsDataURL(file); // 开始读文件 进行base64加密形成字符串
reader.onload = () => resolve(reader.result); // 成功返回对应的值
reader.onerror = (error) => reject(error); // 失败返回失败信息
});
}
</script>
业务逻辑大概雷同,将文件遍历添加到数组,文件当成参数传入getBase64进行Base64转换,然后就是发请求的逻辑了。
本篇由本人自己项目中相关业务进行的导入上传组件功能实现,仅供参考,该功能目前没有出现bug,如有更好的建议,欢迎指点,共同进步。