|
@@ -0,0 +1,170 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ ref="uploadRef"
|
|
|
|
|
+ class="upload-demo"
|
|
|
|
|
+ :action="uploadUrl"
|
|
|
|
|
+ :headers="headers"
|
|
|
|
|
+ :data="extraData"
|
|
|
|
|
+ :multiple="multiple"
|
|
|
|
|
+ :limit="limit"
|
|
|
|
|
+ :accept="accept"
|
|
|
|
|
+ :show-file-list="showFileList"
|
|
|
|
|
+ :before-upload="beforeUpload"
|
|
|
|
|
+ :on-success="handleSuccess"
|
|
|
|
|
+ :on-error="handleError"
|
|
|
|
|
+ :on-exceed="handleExceed"
|
|
|
|
|
+ :on-change="handleChange"
|
|
|
|
|
+ :auto-upload="autoUpload"
|
|
|
|
|
+ :disabled="disabled"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-button :type="buttonType" :icon="buttonIcon" :size="buttonSize" :loading="loading">
|
|
|
|
|
+ {{ buttonText }}
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <template #tip v-if="showTip">
|
|
|
|
|
+ <div class="el-upload__tip">
|
|
|
|
|
+ {{ tipText || `请上传${accept ? accept.replace(/\*/g, '') : ''}格式文件,且不超过${fileSize}MB` }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { ref } from 'vue'
|
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
|
+import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
|
|
|
|
|
+
|
|
|
|
|
+interface Props {
|
|
|
|
|
+ uploadUrl: string // 上传地址(必填)
|
|
|
|
|
+ headers?: Record<string, string> // 请求头
|
|
|
|
|
+ extraData?: Record<string, any> // 额外参数
|
|
|
|
|
+ multiple?: boolean // 是否支持多选文件
|
|
|
|
|
+ limit?: number // 最大允许上传个数
|
|
|
|
|
+ accept?: string // 接受上传的文件类型
|
|
|
|
|
+ fileSize?: number // 文件大小限制(MB)
|
|
|
|
|
+ showFileList?: boolean // 是否显示已上传文件列表
|
|
|
|
|
+ autoUpload?: boolean // 是否自动上传
|
|
|
|
|
+ disabled?: boolean // 是否禁用
|
|
|
|
|
+ buttonText?: string // 按钮文字
|
|
|
|
|
+ buttonType?: string // 按钮类型
|
|
|
|
|
+ buttonIcon?: string // 按钮图标
|
|
|
|
|
+ buttonSize?: string // 按钮尺寸
|
|
|
|
|
+ showTip?: boolean // 是否显示提示文字
|
|
|
|
|
+ tipText?: string // 自定义提示文字
|
|
|
|
|
+ isHandleSuccess?: boolean // 是否处理成功回调
|
|
|
|
|
+ isHandleError?: boolean // 是否处理错误回调
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
+ uploadUrl: '/file/upload',
|
|
|
|
|
+ multiple: false,
|
|
|
|
|
+ limit: 1,
|
|
|
|
|
+ fileSize: 10,
|
|
|
|
|
+ showFileList: false,
|
|
|
|
|
+ autoUpload: true,
|
|
|
|
|
+ disabled: false,
|
|
|
|
|
+ buttonText: '上传文件',
|
|
|
|
|
+ buttonType: 'primary',
|
|
|
|
|
+ buttonSize: 'default',
|
|
|
|
|
+ showTip: true,
|
|
|
|
|
+ isHandleSuccess: true,
|
|
|
|
|
+ isHandleError: true
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const emit = defineEmits(['success', 'error', 'change', 'exceed'])
|
|
|
|
|
+
|
|
|
|
|
+const uploadRef = ref<UploadInstance>()
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+// 上传前校验
|
|
|
|
|
+const beforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
|
|
|
|
|
+ if (props.accept) {
|
|
|
|
|
+ const fileType = rawFile.type
|
|
|
|
|
+ const acceptTypes = props.accept.split(',')
|
|
|
|
|
+ const isValidType = acceptTypes.some(type => {
|
|
|
|
|
+ if (type.startsWith('.')) {
|
|
|
|
|
+ return rawFile.name.toLowerCase().endsWith(type.toLowerCase())
|
|
|
|
|
+ }
|
|
|
|
|
+ return fileType.includes(type.replace(/\*/g, ''))
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ if (!isValidType) {
|
|
|
|
|
+ ElMessage.error(`只能上传${props.accept}格式的文件!`)
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (props.fileSize) {
|
|
|
|
|
+ const isLtSize = rawFile.size / 1024 / 1024 < props.fileSize
|
|
|
|
|
+ if (!isLtSize) {
|
|
|
|
|
+ ElMessage.error(`文件大小不能超过${props.fileSize}MB!`)
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 文件上传成功
|
|
|
|
|
+const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile, uploadFiles) => {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ if (props.isHandleSuccess) {
|
|
|
|
|
+ ElMessage.success('上传成功')
|
|
|
|
|
+ }
|
|
|
|
|
+ emit('success', { response, uploadFile, uploadFiles })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 文件上传失败
|
|
|
|
|
+const handleError: UploadProps['onError'] = (error, uploadFile, uploadFiles) => {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ if (props.isHandleError) {
|
|
|
|
|
+ ElMessage.error('上传失败')
|
|
|
|
|
+ }
|
|
|
|
|
+ emit('error', { error, uploadFile, uploadFiles })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 文件状态改变
|
|
|
|
|
+const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+ emit('change', { uploadFile, uploadFiles })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 文件超出个数限制
|
|
|
|
|
+const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
|
|
|
|
|
+ ElMessage.warning(`最多只能上传${props.limit}个文件`)
|
|
|
|
|
+ emit('exceed', { files, uploadFiles })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 手动上传
|
|
|
|
|
+const submitUpload = () => {
|
|
|
|
|
+ uploadRef.value?.submit()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 清空已上传文件列表
|
|
|
|
|
+const clearFiles = () => {
|
|
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 手动触发文件选择
|
|
|
|
|
+const handleClick = () => {
|
|
|
|
|
+ uploadRef.value?.$el.querySelector('.el-upload__input')?.click()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 暴露方法给父组件
|
|
|
|
|
+defineExpose({
|
|
|
|
|
+ submitUpload,
|
|
|
|
|
+ clearFiles,
|
|
|
|
|
+ handleClick
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.upload-demo {
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-upload__tip {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|