Element-Plus - upload组件

7,203 阅读4分钟

微信图片_20220906095400.jpg

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上传

通过点击或者拖拽上传文件,结合我的项目实现的功能来说

该功能实现为点击上传文件,效果如下:

1662366442914.png

1662366757714.png

1662366878553.png

1662366903490.png

1662366985798.png

实现编码

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请求 URLstring
v-model:file-list默认上传文件UploadUserFile[][ ]
multiple是否支持多选文件booleanfalse
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是否自动上传文件booleantrue
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,如有更好的建议,欢迎指点,共同进步。

---- 下一篇预告 :TypeScript入门篇(2)