一、文件上传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.ts
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { FileInfo } from './types';
/**
* 上传文件
*
* @param file
*/
export function uploadFileApi(file: File): AxiosPromise<FileInfo> {
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
<!--src/components/Upload/SingleUpload.vue-->
<template>
<!-- 上传组件 -->
<el-upload
class="single-uploader"
v-model="imgUrl"
:show-file-list="false"
list-type="picture-card"
:before-upload="handleBeforeUpload"
:http-request="uploadFile"
>
<img v-if="imgUrl" :src="imgUrl" class="single" />
<el-icon v-else class="single-uploader-icon"><Plus /></el-icon>
</el-upload>
</template>
<script setup lang="ts">
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<string | undefined>({
get() {
return props.modelValue;
},
set(val) {
// imgUrl改变时触发修改父组件绑定的v-model的值
emit('update:modelValue', val);
}
});
/**
* 自定义图片上传
*
* @param options
*/
async function uploadFile(options: UploadRequestOptions): Promise<any> {
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;
}
</script>
<style scoped>
.single-uploader .single {
width: 178px;
height: 178px;
display: block;
}
</style>
<style>
.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;
}
</style>
<!--src/components/Upload/MultiUpload.vue-->
<!--
多图上传组件
@author: youlaitech
@date 2022/11/20
-->
<template>
<el-upload
v-model:file-list="fileList"
list-type="picture-card"
:before-upload="handleBeforeUpload"
:http-request="handleUpload"
:on-remove="handleRemove"
:on-preview="handlePreview"
:limit="props.limit"
>
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
<script setup lang="ts">
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<string>,
default: [] as Array<string>
},
/**
* 文件上传数量限制
*/
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<any> {
// 上传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;
};
</script>
三、使用案例
在src/views/component下新建uploader.vue
<!--src/views/component/uploader.vue-->
<script setup lang="ts">
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);
</script>
<template>
<div class="app-container">
<el-form ref="dataFormRef" :model="formData">
<el-form-item label="单图上传">
<single-upload v-model="formData.picUrl"></single-upload>
</el-form-item>
<el-form-item label="多图上传">
<multi-upload v-model="formData.picUrls"></multi-upload>
</el-form-item>
</el-form>
</div>
</template>
版权归原作者 文子阳 所有, 如有侵权,请联系我们删除。