拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Vue3上传(Upload)

可自定义设置以下属性接受上传的文件类型accept类型string默认 *与input typefile /的 accept 属性一致详见 input accept Attribute是否支持多选文件multiple开启后可选择多个文件类型boolean默认 false限制上传数量maxCount当为 1 时始终用最新上传的文件代替当前文件类型number默认 undefined上传描述文字tip类型string | slot默认 Upload预览图片缩放规则仅当上传文件为图片时生效fit类型fill | contain | cover | none | scale-down默认 contain是否支持拖拽上传draggable开启后可拖拽文件到选择框上传类型boolean默认 true是否禁用disabled类型boolean默认 false禁用时只能预览不能删除和上传Space 组件属性配置spaceProps用于配置多个文件时的排列方式类型object默认 {}Spin 组件属性配置spinProps用于配置上传中样式类型object默认 {}Image 组件属性配置imageProps用于配置图片预览类型object默认 {}Message 组件属性配置messageProps用于配置操作消息提示类型object默认 {}操作成功的消息提示actionMessage传 {} 即可不显示任何消息提示类型{upload?: string, remove?: string }默认 { upload: 上传成功, remove: 删除成功 }上传文件之前的钩子beforeUpload类型Function默认: () true参数为上传的文件返回 false 则停止上传返回 true 开始上传支持返回一个 Promise 对象如服务端校验等Promise 对象 reject 时停止上传resolve 时开始上传通常用来校验用户上传的文件格式和大小上传文件的方式uploadMode类型base64 | custom默认 base64自定义上传行为customRequest类型Function默认 () {}只有 uploadMode: custom 时才会使用 customRequest 自定义上传行为已上传的文件列表v-model:file-list类型Array{name?: string, url: any, [propName: string]: any}默认: []效果如下图在线预览①创建上传组件Upload.vue其中引入使用了以下组件与工具函数Vue3加载中SpinVue3全局提示MessageVue3图片ImageVue3间距Space获取依赖注入 useInjectscript setup langts import { ref, computed, watchEffect } from vue import Spin from components/spin import Message from components/message import Image from components/image import Space from components/space import { useInject } from components/utils export interface FileType { name?: string // 文件名 url: any // 文件地址 [propName: string]: any // 添加一个字符串索引签名用于包含带有任意数量的其他属性 } export interface MessageType { upload?: string // 上传成功的消息提示没有设置该属性时即不显示上传消息提示 remove?: string // 删除成功的消息提示没有设置该属性时即不显示删除消息提示 } export interface Props { accept?: string // 接受上传的文件类型与 input typefile / 的 accept 属性一致参考 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Attributes/accept multiple?: boolean // 是否支持多选文件开启后可选择多个文件 maxCount?: number // 限制上传数量。当为 1 时始终用最新上传的文件代替当前文件 tip?: string // 上传描述文字 string | slot fit?: contain | fill | cover | none | scale-down // 预览图片缩放规则仅当上传文件为图片时生效 draggable?: boolean // 是否支持拖拽上传开启后可拖拽文件到选择框上传 disabled?: boolean // 是否禁用只能预览不能删除和上传 spaceProps?: object // Space 组件属性配置用于配置多个文件时的排列方式 spinProps?: object // Spin 组件属性配置用于配置上传中样式 imageProps?: object // Image 组件属性配置用于配置图片预览 messageProps?: object // Message 组件属性配置用于配置操作消息提示 actionMessage?: MessageType // 操作完成的消息提示传 {} 即可不显示任何消息提示 beforeUpload?: Function // 上传文件之前的钩子参数为上传的文件返回 false 则停止上传返回 true 开始上传支持返回一个 Promise 对象如服务端校验等Promise 对象 reject 时停止上传resolve 时开始上传通常用来校验用户上传的文件格式和大小 uploadMode?: base64 | custom // 上传文件的方式默认是 base64可选 base64 | custom customRequest?: Function // 自定义上传行为只有 uploadMode: custom 时才会使用 customRequest 自定义上传行为 fileList?: FileType[] // (v-model) 已上传的文件列表 } const props withDefaults(definePropsProps(), { accept: *, // 默认支持所有类型 multiple: false, maxCount: undefined, tip: Upload, fit: contain, draggable: true, disabled: false, spaceProps: () ({}), spinProps: () ({}), imageProps: () ({}), messageProps: () ({}), actionMessage: () ({ upload: 上传成功, remove: 删除成功 }), beforeUpload: () true, uploadMode: base64, customRequest: () {}, fileList: () [] }) const uploadedFiles refFileType[]([]) // 上传文件列表 const showUpload refnumber(1) // 展示的上传框 const uploading refboolean[]([]) // 上传中 const uploadInputRef ref() // 上传文件控件引用 const imageRef ref() const messageRef ref() const { colorPalettes } useInject(Upload) // 主题色注入 const emits defineEmits([update:fileList, drop, change, preview, remove]) const maxFileCount computed(() { if (props.maxCount undefined) { return Infinity } return props.maxCount }) watchEffect(() { initUpload() }) function initUpload(): void { uploadedFiles.value [...props.fileList] if (uploadedFiles.value.length maxFileCount.value) { uploadedFiles.value.splice(maxFileCount.value) } if (props.disabled) { // 禁用 showUpload.value uploadedFiles.value.length } else { if (uploadedFiles.value.length maxFileCount.value) { showUpload.value props.fileList.length 1 } else { showUpload.value maxFileCount.value } } } // 检查 url 是否为图片 function isImage(url: string): boolean { const imageUrlReg /\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)$/i const base64Reg /^data:image/ return imageUrlReg.test(url) || base64Reg.test(url) } // 检查 url 是否为 pdf function isPDF(url: string): boolean { const pdfUrlReg /\.pdf$/i const base64Reg /^data:application\/pdf/ return pdfUrlReg.test(url) || base64Reg.test(url) } // 拖拽上传 function onDrop(e: DragEvent, index: number): void { const files e.dataTransfer?.files if (files?.length) { const len files.length for (let n 0; n len; n) { if (index n maxFileCount.value) { uploadFile(files[n], index n) } else { break } } // input 的 change 事件默认保存上一次 input 的 value 值同一 value 值(根据文件路径判断)在上传时不重新加载 uploadInputRef.value[index].value null } emits(drop, e) } function onClickFileInput(index: number): void { uploadInputRef.value[index].click() } // 点击上传 function onUpload(e: any, index: number): void { const files e.target.files if (files?.length) { const len files.length for (let n 0; n len; n) { if (index n maxFileCount.value) { uploadFile(files[n], index n) } else { break } } // input 的 change 事件默认保存上一次 input 的 value 值同一 value 值(根据文件路径判断)在上传时不重新加载 uploadInputRef.value[index].value null } } // 统一上传文件方法 function uploadFile (file: File, index: number): void { // console.log(开始上传 upload-event files:, file) function promiseFunction () { return new Promise((resolve, reject) { try { // 尝试执行传入的函数并获取返回值 const result props.beforeUpload(file) // 检查返回值是否是 Promise if (result instanceof Promise) { // 如果是 Promise则等待其 resolve 或 reject result.then(resolve, reject) } else { // 检查返回值是否为布尔值 if (typeof result boolean) { // 如果是布尔值根据值 resolve 或 reject result ? resolve(result) : reject(new Error(Function returned false)) } else { // 否则直接 resolve 返回值 resolve(result) } } } catch (error) { // 如果执行过程中抛出错误则 reject 错误 reject(error) } }) } promiseFunction().then(() { // 使用用户钩子进行上传前文件判断例如大小、类型限制 if (maxFileCount.value showUpload.value) { showUpload.value } if (props.uploadMode base64) { // 以 base64 方式读取文件 uploading.value[index] true base64Upload(file, index) } if (props.uploadMode custom) { // 自定义上传行为需配合 customRequest 属性 uploading.value[index] true customUpload(file, index) } }).catch((error: any) { console.warn(beforeUpload error:, error) }) } function base64Upload(file: File, index: number): void { const reader new FileReader() reader.readAsDataURL(file) // 以 base64 方式读取文件 reader.onloadstart function (e) { // 当读取操作开始时触发 // reader.abort() // 取消上传 // console.log(开始读取 onloadstart:, e) } reader.onabort function (e) { // 当读取操作被中断时触发 // console.log(读取中止 onabort:, e) } reader.onerror function (e) { // 当读取操作发生错误时触发 // console.log(读取错误 onerror:, e) } reader.onprogress function (e) { // 在读取 Blob 时触发读取上传进度50ms 左右调用一次 // console.log(读取中 onprogress:, e) // console.log(已读取:, Math.ceil(e.loaded / e.total * 100)) if (e.loaded e.total) { // 上传完成 uploading.value[index] false // 隐藏 loading 状态 } } reader.onload function (e) { // 当读取操作成功完成时调用 // console.log(读取成功 onload:, e) // 该文件的 base64 数据如果是图片则前端可直接用来展示图片 uploadedFiles.value[index] { name: file.name, url: e.target?.result } props.actionMessage.upload messageRef.value.success(props.actionMessage.upload) emits(update:fileList, uploadedFiles.value) emits(change, uploadedFiles.value) } reader.onloadend function (e) { // 当读取操作结束时触发要么成功要么失败触发 // console.log(读取结束 onloadend:, e) } } function customUpload(file: File, index: number): void { props .customRequest(file) .then((res: any) { uploadedFiles.value[index] res props.actionMessage.upload messageRef.value.success(props.actionMessage.upload) emits(update:fileList, uploadedFiles.value) emits(change, uploadedFiles.value) }) .catch((err: any) { if (maxFileCount.value 1) { showUpload.value uploadedFiles.value.length 1 } messageRef.value.error(err) }) .finally(() { uploading.value[index] false }) } function onPreview(index: number, url: string): void { if (isImage(url)) { const files uploadedFiles.value.slice(0, index).filter((file) !isImage(file.url)) imageRef.value[index - files.length].preview(0) } else { window.open(url) } emits(preview, uploadedFiles.value[index]) } function onRemove(index: number): void { if (uploadedFiles.value.length maxFileCount.value) { showUpload.value-- } const removeFile uploadedFiles.value.splice(index, 1) props.actionMessage.remove messageRef.value.success(props.actionMessage.remove) emits(remove, removeFile[0]) emits(update:fileList, uploadedFiles.value) emits(change, uploadedFiles.value) } function onInfo(content: string): void { messageRef.value.info(content) } function onSuccess(content: string): void { messageRef.value.success(content) } function onError(content: string): void { messageRef.value.error(content) } function onWarning(content: string): void { messageRef.value.warning(content) } function onLoading(content: string): void { messageRef.value.loading(content) } defineExpose({ info: onInfo, success: onSuccess, error: onError, warning: onWarning, loading: onLoading }) /script template div classupload-wrap :style--upload-primary-color: ${colorPalettes[5]}; Space gapsmall v-bindspaceProps div classupload-item-panel v-forn of showUpload :keyn div v-show!uploading[n - 1] !uploadedFiles[n - 1] classupload-item :class{ upload-disabled: disabled } dragenter.stop.prevent dragover.stop.prevent drop.stop.preventdraggable !disabled ? onDrop($event, n - 1) : () false click!disabled ? onClickFileInput(n - 1) : () false input refuploadInputRef typefile click.stop :acceptaccept :multiplemultiple changeonUpload($event, n - 1) styledisplay: none / div svg focusablefalse classplus-svg >script setup langts import Upload from ./Upload.vue import { ref, watchEffect } from vue import type { UploadFileType } from vue-amazing-ui const uploadRef ref() const files refUploadFileType[]([]) const fileList refUploadFileType[]([ { name: 1.jpg, url: https://cdn.jsdelivr.net/gh/themusecatcher/resources0.1.2/1.jpg }, { name: Markdown.pdf, url: https://cdn.jsdelivr.net/gh/themusecatcher/resources0.1.2/Markdown.pdf } ]) const imageList refUploadFileType[]([ { name: 1.jpg, url: https://cdn.jsdelivr.net/gh/themusecatcher/resources0.1.2/1.jpg } ]) const pdfList refUploadFileType[]([ { name: Markdown.pdf, url: https://cdn.jsdelivr.net/gh/themusecatcher/resources0.1.2/Markdown.pdf } ]) watchEffect(() { console.log(files, files.value) }) watchEffect(() { console.log(fileList, fileList.value) }) watchEffect(() { console.log(imageList, imageList.value) }) watchEffect(() { console.log(pdfList, pdfList.value) }) function onBeforeImageUpload(file: File) { if (file.size 500 * 1024) { // 文件大于 500KB 时取消上传 uploadRef.value.warning(文件必须小于 500KB) return false // 停止上传 } console.log(file, file) console.log(type, file.type) if (!file.type.includes(image)) { // 继续上传 uploadRef.value.error(只能上传图片) return false // 停止上传 } return true // 继续上传 } function onBeforePdfUpload(file: File) { const acceptTypes [application/pdf] if (file.size 500 * 1024) { // 文件大于 500KB 时取消上传 uploadRef.value.warning(文件必须小于 500KB) return false // 停止上传 } if (!acceptTypes.includes(file.type)) { // 继续上传 uploadRef.value.error(只能上传 pdf 格式的文件) return false // 停止上传 } return true // 继续上传 } function onCustomRequest(file: File) { return new Promise((resolve, reject) { setTimeout(() { // 模拟接口调用返回name和url if (file.type application/pdf) { var res { name: Markdown.pdf, url: https://cdn.jsdelivr.net/gh/themusecatcher/resources0.1.2/Markdown.pdf } } else { var res { name: 1.jpg, url: https://cdn.jsdelivr.net/gh/themusecatcher/resources0.1.2/1.jpg } } if (res) { resolve(res) } else { reject(upload request fail ...) } }, 1000) }) } function onDrop(e: DragEvent) { console.log(drop, e) } function onChange(files: UploadFileType[]) { console.log(change, files) } function onPreview(file: UploadFileType) { console.log(preview, file) } function onRemove(file: UploadFileType) { console.log(remove, file) } /script template div h1{{ $route.name }} {{ $route.meta.title }}/h1 h2 classmt30 mb10基本使用/h2 Upload v-model:fileListfiles droponDrop changeonChange previewonPreview removeonRemove / h2 classmt30 mb10禁用/h2 h3 classmb10只能预览不能删除和上传/h3 Upload disabled v-model:fileListfileList / h2 classmt30 mb10限制数量/h2 h3 classmb10通过 maxCount 限制上传数量当为 1 时始终用最新上传的代替当前/h3 Space vertical Upload :max-count1 tipmaxCount: 1 v-model:fileListfiles / Upload :max-count3 tipmaxCount: 3 v-model:fileListfileList / /Space h2 classmt30 mb10多文件上传/h2 h3 classmb10可一次选择多个文件进行上传/h3 Upload multiple v-model:fileListfiles / h2 classmt30 mb10自定义样式布局/h2 h3 classmb10缩略图等比覆盖上传描述文字使用上传/h3 Upload tip上传 fitcover v-model:fileListfileList / h3 classmt30 mb10排列间距使用middle自定义上传 uploading 动画样式/h3 Upload :space-props{ gap: middle } :spin-props{ indicator: spin-dot, color: #ff6900, tip: 上传中... } v-model:fileListfileList / h2 classmt30 mb10自定义操作完成的消息提示/h2 Upload :action-message{ upload: upload success, remove: remove success } v-model:fileListfileList / h2 classmt30 mb10上传文件校验/h2 h3 classmb10上传文件最大 500KB同时限定文件类型/h3 Space vertical Upload refuploadRef acceptimage/* tipOnly Image :before-uploadonBeforeImageUpload v-model:fileListimageList changeonChange removeonRemove / Upload refuploadRef acceptapplication/pdf tipOnly PDF :before-uploadonBeforePdfUpload v-model:fileListpdfList changeonChange removeonRemove / /Space h2 classmt30 mb10自定义上传行为/h2 Upload multiple upload-modecustom :custom-requestonCustomRequest v-model:fileListfileList changeonChange removeonRemove / /div /template
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门