目录

一、文件上传api

二、封装组件

三、使用案例


一、文件上传api

在src/api下新建file文件夹,并在file文件夹下新建index.ts和types.ts

// src/api/file/types.ts/** * 文件API类型声明 */export interface FileInfo {name: string;url: string;}
// src/api/file/index.tsimport request from '@/utils/request';import { AxiosPromise } from 'axios';import { FileInfo } from './types';/** * 上传文件 * * @param file */export function uploadFileApi(file: File): AxiosPromise {const formData = new FormData();formData.append('file', file);return request({url: '/api/v1/files',method: 'post',data: formData,headers: {'Content-Type': 'multipart/form-data'}});}/** * 删除文件 * * @param filePath 文件完整路径 */export function deleteFileApi(filePath?: string) {return request({url: '/api/v1/files',method: 'delete',params: { filePath: filePath }});}

二、封装组件

单文件上传组件多文件上传组件

在src/components下新建Upload文件夹,并在Upload文件夹中新建SingleUpload.vue和MultiUpload.vue

import { computed } from 'vue';import { Plus } from '@element-plus/icons-vue';import {ElMessage,ElUpload,UploadRawFile,UploadRequestOptions} from 'element-plus';import { uploadFileApi } from '@/api/file';const emit = defineEmits(['update:modelValue']);const props = defineProps({modelValue: {type: String,default: ''}});const imgUrl = computed({get() {return props.modelValue;},set(val) {// imgUrl改变时触发修改父组件绑定的v-model的值emit('update:modelValue', val);}});/** * 自定义图片上传 * * @param options */async function uploadFile(options: UploadRequestOptions): Promise {const { data: fileInfo } = await uploadFileApi(options.file);imgUrl.value = fileInfo.url;}/** * 限制用户上传文件的格式和大小 */function handleBeforeUpload(file: UploadRawFile) {if (file.size > 2 * 1048 * 1048) {ElMessage.warning('上传图片不能大于2M');return false;}return true;}.single-uploader .single {width: 178px;height: 178px;display: block;}.single-uploader .el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.single-uploader .el-upload:hover {border-color: var(--el-color-primary);}.el-icon.single-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}
import { ref, watch } from 'vue';import { Plus } from '@element-plus/icons-vue';import {ElMessage,ElUpload,UploadRawFile,UploadRequestOptions,UploadUserFile,UploadFile,UploadProps} from 'element-plus';import { uploadFileApi, deleteFileApi } from '@/api/file';const emit = defineEmits(['update:modelValue']);const props = defineProps({/** * 文件路径集合 */modelValue: {type: Array,default: [] as Array},/** * 文件上传数量限制 */limit: {type: Number,default: 5}});const dialogImageUrl = ref('');const dialogVisible = ref(false);const fileList = ref([] as UploadUserFile[]);watch(() => props.modelValue,(newVal: string[]) => {const filePaths = fileList.value.map(file => file.url);// 监听modelValue文件集合值未变化时,跳过赋值if (filePaths.length > 0 &&filePaths.length === newVal.length &&filePaths.every(x => newVal.some(y => y === x)) &&newVal.every(y => filePaths.some(x => x === y))) {return;}fileList.value = newVal.map(filePath => {return { url: filePath } as UploadUserFile;});},{ immediate: true });/** * 自定义图片上传 * * @param params */async function handleUpload(options: UploadRequestOptions): Promise {// 上传API调用const { data: fileInfo } = await uploadFileApi(options.file);// 上传成功需手动替换文件路径为远程URL,否则图片地址为预览地址 blob:http://const fileIndex = fileList.value.findIndex(file => file.uid == (options.file as any).uid);fileList.value.splice(fileIndex, 1, {name: fileInfo.name,url: fileInfo.url} as UploadUserFile);emit('update:modelValue',fileList.value.map(file => file.url));}/** * 删除图片 */function handleRemove(removeFile: UploadFile) {const filePath = removeFile.url;if (filePath) {deleteFileApi(filePath).then(() => {// 删除成功回调emit('update:modelValue',fileList.value.map(file => file.url));});}}/** * 限制用户上传文件的格式和大小 */function handleBeforeUpload(file: UploadRawFile) {if (file.size > 2 * 1048 * 1048) {ElMessage.warning('上传图片不能大于2M');return false;}return true;}/** * 图片预览 */const handlePreview: UploadProps['onPreview'] = uploadFile => {dialogImageUrl.value = uploadFile.url!;dialogVisible.value = true;};

三、使用案例

在src/views/component下新建uploader.vue

import SingleUpload from '@/components/Upload/SingleUpload.vue';import MultiUpload from '@/components/Upload/MultiUpload.vue';import { ElForm } from 'element-plus';import { reactive, ref, toRefs } from 'vue';const dataFormRef = ref(ElForm);const state = reactive({formData: {picUrl:'https://oss.youlai.tech/default/2022/11/20/18e206dae97b40329661537d1e433639.jpg',picUrls: ['https://oss.youlai.tech/default/2022/11/20/8af5567816094545b53e76b38ae9c974.webp','https://oss.youlai.tech/default/2022/11/20/13dbfd7feaf848c2acec2b21675eb9d3.webp']}});const { formData } = toRefs(state);