Vue3 文件上传组件
基于 vue3 element-plus 文件上传组件封装,兄弟姐妹们,将就看一下吧:
支持:粘贴文件路径上传、文件识别开关,上代码,FileUpload.vue
<template>
<div>
<div v-if="canRecognition">
<el-checkbox v-model="isRecognition">开启识别(仅支持 .jpg .png .jpeg .bmp文件格式)</el-checkbox>
</div>
<el-input style="width:425px"
clearable
@paste.native="onPasteUpload($event)"
placeholder="可在此处粘贴(ctrl+v / command+v)文件内容,或选择上传文件">
</el-input>
<div>
<el-upload
style="width:100%;margin-top: 5px;"
:limit="limit"
:multiple="!(canRecognition && isRecognition)"
:before-upload="handleBeforeUpload"
:on-preview="handlePictureCardPreview"
:action="defaultAction"
:http-request="uploadImg"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:accept="canRecognition && isRecognition ? '.jpg,.jpeg,.png,.bmp' : '*'"
v-model:file-list="fileList">
<el-button type="primary" plain>选择上传文件</el-button>
<template #tip>
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
</template>
的文件
</div>
</template>
</el-upload>
</div>
</div>
</template>
<script setup name="FileUpload">
import { uploadFileSignleUrl, uploadFile } from '@/api/upload.js'
import { UUID } from 'uuidjs'
import remove from 'lodash/remove'
const { proxy } = getCurrentInstance();
const emits = defineEmits(['update:modelValue','recognition','removeCallback','successCallback']);
const props = defineProps({
canRecognition:{
type: Boolean,
default: false,
},
recognizeAction:{ // 识别文件 的url
type: String,
default: '',
},
defaultAction: {
type: String,
default: uploadFileSignleUrl,
},
modelValue: [String, Object, Array],
// 图片数量限制,默认不限制
limit: {
type: Number,
default: 0,
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["doc","docx", "xls", "xlsx","ppt", "txt", "pdf","png", "jpg", "jpeg","bmp"],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
},
});
// 是否显示上传提示
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)
);
// 上传的文件
const fileList = computed({
get: () => props.modelValue,
set: (val) => {
emits('update:modelValue', val)
}
})
// 使用图片识别
const isRecognition = ref(true)
// 粘贴回调
const onPasteUpload = (event) => {
let items = event.clipboardData && event.clipboardData.items;
let file = null
if (items && items.length) {
// 检索剪切板items
if (items[0].kind == 'file') {
file = items[0].getAsFile()
} else {
proxy.$modal.msgError('粘贴内容不是文件内容,请重新复制后粘贴')
}
}
if (!file) {
return;
}
const uid = UUID.generate()
file.uid = uid
fileList.value.push({
name: file.name,
size: file.size,
filename: 'file',
raw: file,
status: 'ready',
uid:uid,
})
uploadImg({ filename: 'file', file: file })
}
const getFormData = (filename,file) =>{
const formData = new FormData();
// const name = file.name.replace(/\.[a-zA-Z]+$/g,'') + '_'+ new Date().getTime() // 带上时间戳,避免文件地址重复被覆盖
// formData.append('name', name);
formData.append(filename, file);
return formData
}
// 文件上传
const uploadImg = (file)=>{
proxy.$modal.loading("上传中")
const data = getFormData(file.filename,file.file)
const url = (props.canRecognition && isRecognition.value) ? props.recognizeAction : props.defaultAction
uploadFile(url, data).then(res=>{
if(res.code == 200){
if(res.data){
// 文件url
let current = fileList.value.find(z=>z.uid == file.file.uid)
current.status = 'success'
const fileData = props.canRecognition && isRecognition.value ? res.data.oss : res.data
current.url = fileData.url
current.ossId = fileData.ossId
current.fileName = fileData.fileName
// 如果开启文件识别,返回识别内容
if(props.canRecognition && isRecognition.value){
emits("recognition", {
fileName: current.fileName,
fileUrl: current.url,
recognitionData : (res.data.recognition?.data || res.data.recognition)
})
}
}
emits('successCallback')
}else{
remove(fileList.value, z=>z.uid == file.file.uid)
proxy.$modal.msgError(res.msg || '发生错误,请稍后重试!')
}
proxy.$modal.closeLoading()
}).catch(error=>{
remove(fileList.value, z=>z.uid == file.file.uid)
proxy.$modal.closeLoading()
proxy.$modal.msgError(
error && typeof error == 'object'
? error.statusText || error.message || JSON.stringify(error)
: error || '发生错误,请稍后重试!'
)
})
}
// 上传前校验
const handleBeforeUpload = (file)=>{
if (props.fileType.length) {
const fileName = file.name.split('.');
const fileExt = fileName[fileName.length - 1];
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
if (!isTypeOk) {
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
return false;
}
}
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
return false;
}
}
return true
}
// 文件超出限制
const handleExceed = ()=>{
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}
// 预览文件
const handlePictureCardPreview = (uploadFile)=>{
window.open(uploadFile.url)
}
// 文件删除回调
const handleRemove = (file) => {
emits("removeCallback",file)
}
// 格式转化 url,url,url =>Array<{ name , url}>
const fileUrlStringToList =(fileStr, name)=>{
if(fileStr && fileStr.length >0){
return fileStr.split(',').map((url,index)=>{
return { name : name ? (name + (index + 1) + url.split('.').pop()) : url.split('/').pop(), url : url }
})
}else{
return []
}
}
// 格式转换 Array<{ name , url, ...attrs}> => url,url,url
const fileListToUrlString = (fileList)=>{
if(fileList && fileList.length >0){
return (fileList||[]).map(z=>z.url).join(',')
}else{
return ''
}
}
// 仅保留 name 和 url Array<{ name , url, ...attrs}> => Array<{ name , url }>
const stringifyForFileNameAndUrl = (fileList)=>{
if(fileList && fileList.length >0){
return JSON.stringify((fileList||[]).map(z=>{return { url : z.url, name : z.name}}))
}else{
return ''
}
}
// 解析 数组字符串
const parseFileList = (fileList)=>{
if(fileList && fileList.length >0){
return JSON.parse(fileList)
}else{
return []
}
}
defineExpose({
fileUrlStringToList,
fileListToUrlString,
stringifyForFileNameAndUrl,
parseFileList
})
</script>
<style scoped lang="scss">
</style>
作者:胡倩倩0903
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
posted on 2023-05-08 14:44 kitty20180903suzhou 阅读(356) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?