|
@@ -0,0 +1,234 @@
|
|
|
+<template>
|
|
|
+ <div class="upload-file">
|
|
|
+ <el-upload
|
|
|
+ ref="uploadRef"
|
|
|
+ drag
|
|
|
+ :action="uploadFileUrl"
|
|
|
+ :file-list="_fileList"
|
|
|
+ class="upload-file-uploader"
|
|
|
+ :show-file-list="false"
|
|
|
+ :multiple="true"
|
|
|
+ :disabled="self_disabled"
|
|
|
+ :limit="limit"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :on-exceed="handleExceed"
|
|
|
+ :on-success="uploadSuccess"
|
|
|
+ :on-error="uploadError"
|
|
|
+ :accept="fileType.join(',')"
|
|
|
+ :headers="headers">
|
|
|
+ <el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
|
|
+ <div class="el-upload__text">拖动文件到此区域进行上传或 <em>点击上传</em></div>
|
|
|
+ </el-upload>
|
|
|
+ <!-- 上传提示 -->
|
|
|
+ <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>
|
|
|
+ <!-- 文件列表 -->
|
|
|
+ <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
|
|
+ <li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in _fileList">
|
|
|
+ <el-link :href="`${file.url}`" :underline="false" target="_blank">
|
|
|
+ <span class="document">
|
|
|
+ {{ file.fileName }}
|
|
|
+ </span>
|
|
|
+ </el-link>
|
|
|
+ <div class="ele-upload-list__item-content-action">
|
|
|
+ <el-link :underline="false" @click="handleRemove(index)" type="danger">删除</el-link>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ </transition-group>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="Upload">
|
|
|
+import { type UploadProps, type UploadFile, type UploadInstance, formContextKey, formItemContextKey } from 'element-plus'
|
|
|
+import { showLoading, hideLoading } from '@/components/Loading/loading'
|
|
|
+import { FilesBO, FilesVO } from '@/api/interface/system/files'
|
|
|
+import FilesApi from '@/api/module/system/files'
|
|
|
+import { listToString } from '@/utils'
|
|
|
+import { globalHeaders } from '@/axios/config'
|
|
|
+interface UploadFileProps {
|
|
|
+ modelValue?: string
|
|
|
+ disabled?: boolean // 是否禁用上传组件 ==> 非必传(默认为 false)
|
|
|
+ drag?: boolean // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
|
|
+ limit?: number // 最大图片上传数 ==> 非必传(默认为 5张)
|
|
|
+ fileSize?: number // 图片大小限制 ==> 非必传(默认为 5M)
|
|
|
+ isShowTip?: boolean // 是否显示提示信息 ==> 非必传(默认为 true)
|
|
|
+ text?: string // 按钮文字
|
|
|
+ icon?: string
|
|
|
+ uploadApi?: string
|
|
|
+ fileType?: Array<string>
|
|
|
+}
|
|
|
+// 默认值
|
|
|
+const props = withDefaults(defineProps<UploadFileProps>(), {
|
|
|
+ modelValue: () => '',
|
|
|
+ drag: true,
|
|
|
+ disabled: false,
|
|
|
+ uploadApi: '/system/files/upload',
|
|
|
+ limit: 1,
|
|
|
+ fileSize: 5,
|
|
|
+ fileType: () => ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'txt', 'pdf'],
|
|
|
+ text: '文件上传',
|
|
|
+ isShowTip: true
|
|
|
+})
|
|
|
+
|
|
|
+const baseUrl = import.meta.env.VITE_API_PREFIX_PATH
|
|
|
+const uploadFileUrl = ref(`${baseUrl}${props.uploadApi}`) // 上传文件服务器地址
|
|
|
+const headers = ref(globalHeaders())
|
|
|
+const uploadRef = ref<UploadInstance>()
|
|
|
+const number = ref(0)
|
|
|
+const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize))
|
|
|
+const uploadList = ref<any[]>([])
|
|
|
+const emit = defineEmits<{
|
|
|
+ 'update:modelValue': [value: any]
|
|
|
+}>()
|
|
|
+
|
|
|
+// 获取 el-form 组件上下文
|
|
|
+const formContext = inject(formContextKey, void 0)
|
|
|
+// 获取 el-form-item 组件上下文
|
|
|
+const formItemContext = inject(formItemContextKey, void 0)
|
|
|
+// 判断是否禁用上传和删除
|
|
|
+const self_disabled = computed(() => {
|
|
|
+ return props.disabled || formContext?.disabled
|
|
|
+})
|
|
|
+
|
|
|
+const _fileList = ref<any[]>([])
|
|
|
+
|
|
|
+// 监听 props.modelValue 列表默认值改变
|
|
|
+watch(
|
|
|
+ () => props.modelValue,
|
|
|
+ async (val: string) => {
|
|
|
+ if (val) {
|
|
|
+ let temp = 1
|
|
|
+ // 首先将值转为数组
|
|
|
+ let list: any[] = []
|
|
|
+ if (Array.isArray(val)) {
|
|
|
+ list = val as FilesBO[]
|
|
|
+ } else {
|
|
|
+ const res = await FilesApi.list({ fileId: val })
|
|
|
+ list = res.data.map((file: FilesVO) => {
|
|
|
+ return {
|
|
|
+ fileName: file.originalName,
|
|
|
+ fileId: file.fileId
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ // 然后将数组转为对象数组
|
|
|
+ _fileList.value = list.map(item => {
|
|
|
+ item = { name: item.fileName, url: item.fileId }
|
|
|
+ item.uid = new Date().getTime() + temp++
|
|
|
+ return item
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ _fileList.value = []
|
|
|
+ return []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { deep: true, immediate: true }
|
|
|
+)
|
|
|
+/**
|
|
|
+ * @description 文件上传之前判断
|
|
|
+ * @param rawFile 选择的文件
|
|
|
+ * */
|
|
|
+const beforeUpload: UploadProps['beforeUpload'] = rawFile => {
|
|
|
+ // 校验文件格式
|
|
|
+ const fileName = rawFile.name.split('.')
|
|
|
+ const fileExt = fileName[fileName.length - 1]
|
|
|
+ const isTypeOk = props.fileType.indexOf(fileExt) >= 0
|
|
|
+ // 校检文件大小
|
|
|
+ const isLt = rawFile.size / 1024 / 1024 < props.fileSize
|
|
|
+ if (!isTypeOk) {
|
|
|
+ ElMessage.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (!isLt) {
|
|
|
+ ElMessage.error(`文件大小不能超过 ${props.fileSize}M!`)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ number.value++
|
|
|
+ showLoading('正在上传文件,请稍候...')
|
|
|
+ return isTypeOk && isLt
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 文件上传成功
|
|
|
+ * @param response 上传响应结果
|
|
|
+ * @param uploadFile 上传的文件
|
|
|
+ * */
|
|
|
+const uploadSuccess = (response: any | undefined, uploadFile: UploadFile) => {
|
|
|
+ if (response.code !== 200) {
|
|
|
+ number.value--
|
|
|
+ ElMessage.error(response.msg)
|
|
|
+ uploadRef.value?.handleRemove(uploadFile)
|
|
|
+ uploadedSuccessfully()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ uploadList.value.push({ fileName: response.data.fileName, fileId: response.data.fileId })
|
|
|
+ uploadedSuccessfully()
|
|
|
+}
|
|
|
+
|
|
|
+// 上传结束处理
|
|
|
+const uploadedSuccessfully = () => {
|
|
|
+ if (number.value > 0 && uploadList.value.length === number.value) {
|
|
|
+ _fileList.value = _fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
|
|
|
+ uploadList.value = []
|
|
|
+ number.value = 0
|
|
|
+ emit('update:modelValue', listToString(_fileList.value))
|
|
|
+ hideLoading()
|
|
|
+ }
|
|
|
+ // 监听表单验证
|
|
|
+ formItemContext?.prop && formContext?.validateField([formItemContext.prop as string])
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 删除图片
|
|
|
+ * @param file 删除的文件
|
|
|
+ * */
|
|
|
+const handleRemove = (index: number) => {
|
|
|
+ const fileId = _fileList.value[index].fileId
|
|
|
+ FilesApi.deleteFile([fileId])
|
|
|
+ _fileList.value.splice(index, 1)
|
|
|
+ emit('update:modelValue', listToString(_fileList.value))
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 文件上传错误
|
|
|
+ * */
|
|
|
+const uploadError = () => {
|
|
|
+ ElMessage.error('文件上传失败,请您重新上传!')
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 文件数超出
|
|
|
+ * */
|
|
|
+const handleExceed = () => {
|
|
|
+ ElMessage.warning(`当前最多只能上传 ${props.limit} 个文件 ,请移除后上传!`)
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.upload-file-uploader {
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+.upload-file-list .el-upload-list__item {
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ line-height: 2;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+}
|
|
|
+.upload-file-list .ele-upload-list__item-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ color: inherit;
|
|
|
+}
|
|
|
+.ele-upload-list__item-content-action .el-link {
|
|
|
+ margin-right: 10px;
|
|
|
+}
|
|
|
+</style>
|