19 changed files with 914 additions and 1540 deletions
			
			
		| After Width: | Height: | Size: 5.8 KiB | 
| @ -0,0 +1,318 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="upload-box"> | ||||||
|  |     <el-upload | ||||||
|  |       v-model:file-list="fileList" | ||||||
|  |       :accept="fileType.join(',')" | ||||||
|  |       :action="uploadUrl" | ||||||
|  |       :before-upload="beforeUpload" | ||||||
|  |       :class="['upload', drag ? 'no-border' : '']" | ||||||
|  |       :disabled="showPlus" | ||||||
|  |       :drag="drag" | ||||||
|  |       :http-request="httpRequest" | ||||||
|  |       :limit="limit" | ||||||
|  |       :auto-upload="false" | ||||||
|  |       :multiple="limit > 1" | ||||||
|  |       :on-error="uploadError" | ||||||
|  |       :on-exceed="handleExceed" | ||||||
|  |       :on-change="handleChange" | ||||||
|  |       list-type="picture-card" | ||||||
|  |       ref="uploadRef" | ||||||
|  |     > | ||||||
|  |       <div class="upload-empty" v-if="!showPlus"> | ||||||
|  |         <slot name="empty"> | ||||||
|  |           <Icon icon="ep:plus" :size="30" color="#ccc" /> | ||||||
|  |         </slot> | ||||||
|  |       </div> | ||||||
|  |       <template #file="{ file }"> | ||||||
|  |         <img :src="file.url" class="upload-image" /> | ||||||
|  |         <div class="upload-handle" @click.stop> | ||||||
|  |           <div class="handle-icon" @click="imagePreview(file.url!)"> | ||||||
|  |             <Icon icon="ep:zoom-in" :size="30" /> | ||||||
|  |             <span>查看</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="handle-icon" @click="handleRemove(file)"> | ||||||
|  |             <Icon icon="ep:delete" :size="30" /> | ||||||
|  |             <span>删除</span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </template> | ||||||
|  |     </el-upload> | ||||||
|  |     <div class="el-upload__tip"> | ||||||
|  |       <slot name="tip"></slot> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { ElNotification } from 'element-plus' | ||||||
|  | import { createImageViewer } from '@/components/ImageViewer' | ||||||
|  | import { batchUploadFile } from '@/api/infra/file' | ||||||
|  | import { propTypes } from '@/utils/propTypes' | ||||||
|  | import { useUpload } from '@/components/UploadFile/src/useUpload' | ||||||
|  | 
 | ||||||
|  | defineOptions({ name: 'UploadImgs' }) | ||||||
|  | 
 | ||||||
|  | const message = useMessage() // 消息弹窗 | ||||||
|  | // 查看图片 | ||||||
|  | const imagePreview = (imgUrl: string) => { | ||||||
|  |   createImageViewer({ | ||||||
|  |     zIndex: 9999999, | ||||||
|  |     urlList: [imgUrl] | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const uploadRef = ref() | ||||||
|  | 
 | ||||||
|  | type FileTypes = | ||||||
|  |   | 'image/apng' | ||||||
|  |   | 'image/bmp' | ||||||
|  |   | 'image/gif' | ||||||
|  |   | 'image/jpeg' | ||||||
|  |   | 'image/pjpeg' | ||||||
|  |   | 'image/png' | ||||||
|  |   | 'image/svg+xml' | ||||||
|  |   | 'image/tiff' | ||||||
|  |   | 'image/webp' | ||||||
|  |   | 'image/x-icon' | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired, | ||||||
|  |   drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true) | ||||||
|  |   limit: propTypes.number.def(1), // 最大图片上传数 ==> 非必传(默认为 5张) | ||||||
|  |   fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M) | ||||||
|  |   fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"]) | ||||||
|  |   height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px) | ||||||
|  |   width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px) | ||||||
|  |   borderRadius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px) | ||||||
|  | }) | ||||||
|  | const showPlus = ref(false) | ||||||
|  | const { uploadUrl, httpRequest } = useUpload() | ||||||
|  | 
 | ||||||
|  | const fileList = ref<any>([]) | ||||||
|  | const uploadNumber = ref<number>(0) | ||||||
|  | const uploadList = ref<any>([]) | ||||||
|  | /** | ||||||
|  |  * @description 文件上传之前判断 | ||||||
|  |  * @param rawFile 上传的文件 | ||||||
|  |  * */ | ||||||
|  | const beforeUpload = (rawFile: any) => { | ||||||
|  |   const imgSize = rawFile.size / 1024 / 1024 < props.fileSize | ||||||
|  |   const imgType = props.fileType | ||||||
|  |   if (!imgType.includes(rawFile.type as FileTypes)) | ||||||
|  |     ElNotification({ | ||||||
|  |       title: '温馨提示', | ||||||
|  |       message: '上传图片不符合所需的格式!', | ||||||
|  |       type: 'warning' | ||||||
|  |     }) | ||||||
|  |   if (!imgSize) | ||||||
|  |     ElNotification({ | ||||||
|  |       title: '温馨提示', | ||||||
|  |       message: `上传图片大小不能超过 ${props.fileSize}M!`, | ||||||
|  |       type: 'warning' | ||||||
|  |     }) | ||||||
|  |   uploadNumber.value++ | ||||||
|  |   return imgType.includes(rawFile.type as FileTypes) && imgSize | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 图片上传成功 | ||||||
|  | interface UploadEmits { | ||||||
|  |   (e: 'update:modelValue', value: string[]): void | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits<UploadEmits>() | ||||||
|  | const handleChange = (file, fileList) => { | ||||||
|  |   showPlus.value = fileList.length >= props.limit | ||||||
|  |   emitUpdateModelValue(fileList) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 监听模型绑定值变动 | ||||||
|  | watch( | ||||||
|  |   () => props.modelValue, | ||||||
|  |   (val: any) => { | ||||||
|  |     if (!val) { | ||||||
|  |       fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置 | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     fileList.value = [] // 保障数据为空 | ||||||
|  |     fileList.value.push(...val) | ||||||
|  |   }, | ||||||
|  |   { immediate: true, deep: true } | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const emitUpdateModelValue = (fileList: any) => { | ||||||
|  |   emit('update:modelValue', fileList) | ||||||
|  | } | ||||||
|  | // 删除图片 | ||||||
|  | const handleRemove = (uploadFile: any) => { | ||||||
|  |   fileList.value = fileList.value.filter((item: any) => { | ||||||
|  |     if (uploadFile.id) { | ||||||
|  |       return item.id !== uploadFile.id | ||||||
|  |     } else { | ||||||
|  |       return item.uid !== uploadFile.uid | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   emitUpdateModelValue(fileList.value) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 图片上传错误提示 | ||||||
|  | const uploadError = () => { | ||||||
|  |   ElNotification({ | ||||||
|  |     title: '温馨提示', | ||||||
|  |     message: '图片上传失败,请您重新上传!', | ||||||
|  |     type: 'error' | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 文件数超出提示 | ||||||
|  | const handleExceed = () => { | ||||||
|  |   ElNotification({ | ||||||
|  |     title: '温馨提示', | ||||||
|  |     message: `当前最多只能上传 ${props.limit} 张图片,请移除后上传!`, | ||||||
|  |     type: 'warning' | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const handlerUpload = async () => { | ||||||
|  |   const formData = new FormData() | ||||||
|  |   fileList.value.forEach((file: any) => { | ||||||
|  |     formData.append('files', file.raw) | ||||||
|  |   }) | ||||||
|  |   let res = await batchUploadFile(formData) | ||||||
|  |   fileList.value = res.data | ||||||
|  |   emitUpdateModelValue(fileList.value) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | defineExpose({ | ||||||
|  |   handlerUpload | ||||||
|  | }) | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .is-error { | ||||||
|  |   .upload { | ||||||
|  |     :deep(.el-upload--picture-card), | ||||||
|  |     :deep(.el-upload-dragger) { | ||||||
|  |       border: 1px dashed var(--el-color-danger) !important; | ||||||
|  | 
 | ||||||
|  |       &:hover { | ||||||
|  |         border-color: var(--el-color-primary) !important; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | :deep(.is-disabled) { | ||||||
|  |   .el-upload--picture-card, | ||||||
|  |   .el-upload-dragger { | ||||||
|  |     display: none !important; | ||||||
|  |     cursor: not-allowed; | ||||||
|  |     background: var(--el-disabled-bg-color); | ||||||
|  |     border: 1px dashed var(--el-border-color-darker); | ||||||
|  | 
 | ||||||
|  |     &:hover { | ||||||
|  |       border-color: var(--el-border-color-darker) !important; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | .upload-box { | ||||||
|  |   .no-border { | ||||||
|  |     :deep(.el-upload-list--picture-card) { | ||||||
|  |       gap: 20px; | ||||||
|  | 
 | ||||||
|  |       .el-upload-list__item { | ||||||
|  |         margin: 0 !important; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     :deep(.el-upload--picture-card) { | ||||||
|  |       border: none !important; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   :deep(.upload) { | ||||||
|  |     .el-upload-dragger { | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       padding: 0; | ||||||
|  |       gap: 20px; | ||||||
|  |       overflow: hidden; | ||||||
|  |       border: 1px dashed var(--el-border-color-darker); | ||||||
|  |       border-radius: v-bind(borderRadius); | ||||||
|  | 
 | ||||||
|  |       &:hover { | ||||||
|  |         border: 1px dashed var(--el-color-primary); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .el-upload-dragger.is-dragover { | ||||||
|  |       background-color: var(--el-color-primary-light-9); | ||||||
|  |       border: 2px dashed var(--el-color-primary) !important; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .el-upload-list__item, | ||||||
|  |     .el-upload--picture-card { | ||||||
|  |       width: v-bind(width); | ||||||
|  |       height: v-bind(height); | ||||||
|  |       background-color: transparent; | ||||||
|  |       border-radius: v-bind(borderRadius); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .upload-image { | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       object-fit: contain; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .upload-handle { | ||||||
|  |       position: absolute; | ||||||
|  |       inset: 0; | ||||||
|  |       display: flex; | ||||||
|  |       cursor: pointer; | ||||||
|  |       background: rgb(0 0 0 / 60%); | ||||||
|  |       opacity: 0; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       transition: var(--el-transition-duration-fast); | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       margin: 0; | ||||||
|  |       gap: 15%; | ||||||
|  |       .handle-icon { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         align-items: center; | ||||||
|  |         justify-content: center; | ||||||
|  |         color: aliceblue; | ||||||
|  |         &:hover { | ||||||
|  |           color: var(--el-color-primary); | ||||||
|  |           transition: var(--el-transition-duration-fast); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .el-upload-list__item { | ||||||
|  |       &:hover { | ||||||
|  |         .upload-handle { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     .upload-empty { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: column; | ||||||
|  |       align-items: center; | ||||||
|  |       font-size: 12px; | ||||||
|  |       line-height: 30px; | ||||||
|  |       color: var(--el-color-info); | ||||||
|  |       .el-icon { | ||||||
|  |         font-size: 28px; | ||||||
|  |         color: var(--el-text-color-secondary); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .el-upload__tip { | ||||||
|  |     line-height: 15px; | ||||||
|  |     text-align: center; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @ -1,501 +0,0 @@ | |||||||
| <template> |  | ||||||
|   <main class="upload-container"> |  | ||||||
|     <section class="file-list"> |  | ||||||
|       <section |  | ||||||
|         class="file" |  | ||||||
|         v-for="(file, index) in fileList" |  | ||||||
|         :key="file.uid" |  | ||||||
|         v-if="fileList.length > 0" |  | ||||||
|       > |  | ||||||
|         <el-image :src="getfileUrl(file)" class="img" /> |  | ||||||
|         <Icon |  | ||||||
|           icon="ep:close" |  | ||||||
|           class="icon close" |  | ||||||
|           :size="13" |  | ||||||
|           @click="remove(file, index)" |  | ||||||
|           color="#fff" |  | ||||||
|         /> |  | ||||||
|       </section> |  | ||||||
|       <section class="emty" v-else> |  | ||||||
|         <Icon icon="svg-icon:customs-empty" :size="40" /> |  | ||||||
|         <span>暂无图片</span> |  | ||||||
|       </section> |  | ||||||
|     </section> |  | ||||||
|     <el-upload |  | ||||||
|       :file-list="fileList" |  | ||||||
|       :action="uploadUrl" |  | ||||||
|       :data="uploadExtendParams" |  | ||||||
|       :http-request="httpRequest" |  | ||||||
|       :auto-upload="false" |  | ||||||
|       :on-change="onChange" |  | ||||||
|       :on-success="onSuccess" |  | ||||||
|       :on-error="onError" |  | ||||||
|       multiple |  | ||||||
|       :limit="limit > 0 ? limit : ''" |  | ||||||
|       :show-file-list="false" |  | ||||||
|       :accept="accept" |  | ||||||
|       ref="uploadRef" |  | ||||||
|       :on-exceed="exceed" |  | ||||||
|       class="flex justify-center items-center" |  | ||||||
|     > |  | ||||||
|       <section class="button"> |  | ||||||
|         <Icon icon="svg-icon:customs-upload" :size="20" color="#00a3ff" /> |  | ||||||
|         选取图片 |  | ||||||
|       </section> |  | ||||||
|     </el-upload> |  | ||||||
| 
 |  | ||||||
|     <section class="upload-loading" v-show="uploadLoading"> |  | ||||||
|       <div class="loading"> <span></span><span></span><span></span><span></span> </div> |  | ||||||
|     </section> |  | ||||||
|   </main> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import { useUpload } from '@/components/UploadFile/src/useUpload' |  | ||||||
| import { ElMessage, ElMessageBox } from 'element-plus' |  | ||||||
| defineOptions({ |  | ||||||
|   name: 'InnerUploader' |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| const uploadLoading = ref(false) |  | ||||||
| 
 |  | ||||||
| const emit = defineEmits(['handlerRemove', 'handlerSuccess', 'hanlderWriteState']) |  | ||||||
| 
 |  | ||||||
| // 上传额外参数 |  | ||||||
| const uploadExtendParams = (file: any) => { |  | ||||||
|   return { writeAble: extendParams.value[file.uid] } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const props = defineProps({ |  | ||||||
|   /** |  | ||||||
|    * 文件列表 |  | ||||||
|    * @default Array |  | ||||||
|    */ |  | ||||||
|   uploadList: { |  | ||||||
|     type: Array, |  | ||||||
|     default: () => [] |  | ||||||
|   }, |  | ||||||
|   limit: { |  | ||||||
|     type: Number, |  | ||||||
|     default: '' |  | ||||||
|   }, |  | ||||||
|   /** |  | ||||||
|    * 是否可已进行文件权限编辑 |  | ||||||
|    * @default true |  | ||||||
|    */ |  | ||||||
|   iswrite: { |  | ||||||
|     type: Boolean, |  | ||||||
|     default: true |  | ||||||
|   }, |  | ||||||
|   accept: { |  | ||||||
|     type: String, |  | ||||||
|     default: '*' |  | ||||||
|   } |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| watch( |  | ||||||
|   () => props.uploadList, |  | ||||||
|   (newVal) => { |  | ||||||
|     fileList.value = newVal |  | ||||||
|   }, |  | ||||||
|   { deep: true } |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const fileList: any = ref([]) |  | ||||||
| 
 |  | ||||||
| // 额外参数 |  | ||||||
| const extendParams = ref({}) |  | ||||||
| 
 |  | ||||||
| // el-upload组件 |  | ||||||
| const uploadRef = ref() |  | ||||||
| 
 |  | ||||||
| // 自定义上传地址与方式 |  | ||||||
| const { uploadUrl, httpRequest } = useUpload() |  | ||||||
| 
 |  | ||||||
| // 文件状态改变 |  | ||||||
| const onChange = (file: any) => { |  | ||||||
|   if (props.accept !== '*') { |  | ||||||
|     if (props.accept.split(',').findIndex((i) => file.name.includes(i)) == -1) { |  | ||||||
|       unref(uploadRef).handleRemove(file) |  | ||||||
|       ElMessage({ message: `${file.name}文件格式不正确`, type: 'error' }) |  | ||||||
|       return |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   if (file.status === 'ready') { |  | ||||||
|     fileList.value.push(file) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const getfileUrl = (file: any) => { |  | ||||||
|   return file.attachmentPath || URL.createObjectURL(file.raw) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 上传错误的处理 |  | ||||||
| const onError = (e: any, file) => { |  | ||||||
|   ElMessage({ message: `${file.name}上传失败,${e}`, type: 'error' }) |  | ||||||
|   // unref(uploadRef).handleRemove(file) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 移除文件 |  | ||||||
| const remove = (file: any, index: any) => { |  | ||||||
|   if (file.status === 'success') { |  | ||||||
|     ElMessageBox.alert(`确认移除${file.name}吗?`, '删除', { |  | ||||||
|       confirmButtonText: '确认', |  | ||||||
|       showCancelButton: true, |  | ||||||
|       cancelButtonText: '取消', |  | ||||||
|       distinguishCancelAndClose: true, |  | ||||||
|       showClose: false |  | ||||||
|     }) |  | ||||||
|       .then(() => { |  | ||||||
|         fileList.value.splice(index, 1) |  | ||||||
|         emit('handlerRemove', file) |  | ||||||
|       }) |  | ||||||
|       .catch(() => {}) |  | ||||||
|     return |  | ||||||
|   } |  | ||||||
|   fileList.value.splice(index, 1) |  | ||||||
|   unref(uploadRef).handleRemove(file) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 触发组件el的上传事件 |  | ||||||
| const upload = () => { |  | ||||||
|   if (fileList.value.filter((file) => file.status == 'ready').length > 0) { |  | ||||||
|     uploadLoading.value = true |  | ||||||
|     unref(uploadRef).submit() |  | ||||||
|     return |  | ||||||
|   } |  | ||||||
|   ElMessage('暂无可上传的文件') |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 成功回调暂存 |  | ||||||
| const successMap: any = ref([]) |  | ||||||
| 
 |  | ||||||
| // 上传成功的回调 |  | ||||||
| const onSuccess = (response: any) => { |  | ||||||
|   successMap.value.push(response) |  | ||||||
|   if (fileList.value.filter((file) => file.status == 'ready').length == 0) { |  | ||||||
|     uploadLoading.value = false |  | ||||||
|     emit('handlerSuccess', successMap.value) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function exceed() { |  | ||||||
|   ElMessage({ |  | ||||||
|     message: `只允许上传${props.limit}张图片` |  | ||||||
|   }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| onMounted(() => { |  | ||||||
|   fileList.value = props.uploadList |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| onBeforeUnmount(() => { |  | ||||||
|   fileList.value = [] |  | ||||||
|   unref(uploadRef).clearFiles() |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| onActivated(() => { |  | ||||||
|   // console.log('onActivated') |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| // 在组件撒上暴露方法 |  | ||||||
| defineExpose({ |  | ||||||
|   upload |  | ||||||
| }) |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style lang="scss" scoped> |  | ||||||
| :deep(.el-upload-list__item:hover) { |  | ||||||
|   background-color: transparent !important; |  | ||||||
| } |  | ||||||
| :deep(.el-upload) { |  | ||||||
|   flex-flow: column; |  | ||||||
|   gap: 10px; |  | ||||||
|   align-items: flex-start; |  | ||||||
| } |  | ||||||
| .file-list { |  | ||||||
|   width: 220px; |  | ||||||
|   margin-bottom: 10px; |  | ||||||
|   display: block; |  | ||||||
|   border-radius: 6px; |  | ||||||
|   padding: 8px 6px; |  | ||||||
| } |  | ||||||
| .file { |  | ||||||
|   // width: fit-content; |  | ||||||
|   margin-bottom: 5px; |  | ||||||
|   // padding: 4px 10px; |  | ||||||
|   display: flex; |  | ||||||
|   flex-flow: row nowrap; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 5px; |  | ||||||
|   cursor: pointer; |  | ||||||
|   border-radius: 6px; |  | ||||||
|   position: relative; |  | ||||||
|   justify-content: center; |  | ||||||
|   transition: 0.2s; |  | ||||||
|   .img { |  | ||||||
|     border-radius: 6px; |  | ||||||
|   } |  | ||||||
|   .info { |  | ||||||
|     flex: 1; |  | ||||||
|     overflow: hidden; |  | ||||||
|     display: flex; |  | ||||||
|     flex-flow: column nowrap; |  | ||||||
|     .name { |  | ||||||
|       font-weight: bold; |  | ||||||
|       font-size: small; |  | ||||||
|       letter-spacing: 1px; |  | ||||||
|       white-space: nowrap; |  | ||||||
|       overflow: hidden; |  | ||||||
|       text-overflow: ellipsis; |  | ||||||
|     } |  | ||||||
|     .size { |  | ||||||
|       font-size: 12px; |  | ||||||
|     } |  | ||||||
|     .time { |  | ||||||
|       font-size: 12px; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .state { |  | ||||||
|     font-size: small; |  | ||||||
|   } |  | ||||||
|   .icon { |  | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|     right: 0; |  | ||||||
|     transform: translateX(20%) translateY(-20%); |  | ||||||
|     border-radius: 50%; |  | ||||||
|     transition: 0.2s; |  | ||||||
|     padding: 2px; |  | ||||||
|     background-image: radial-gradient(circle at 60% 60%, #fcfcfb 2%, red 54%); |  | ||||||
|   } |  | ||||||
|   .close:hover { |  | ||||||
|     // background-color: #f83f2a; |  | ||||||
|     // box-shadow: 0 0 2px 0 #640404; |  | ||||||
|     // filter: drop-shadow(0 0 1px #000000ab); |  | ||||||
|     opacity: 0.9; |  | ||||||
|   } |  | ||||||
|   &:hover { |  | ||||||
|     background-color: #fff; |  | ||||||
|     box-shadow: 0 0 4px 0px #ccc; |  | ||||||
|     border-radius: 4px; |  | ||||||
|   } |  | ||||||
|   .program { |  | ||||||
|     position: absolute; |  | ||||||
|     bottom: 0%; |  | ||||||
|     // border-radius: 999px; |  | ||||||
|     background-color: #89ffc2a0; |  | ||||||
|     left: 0; |  | ||||||
|     width: var(--p); |  | ||||||
|     // width: 100%; |  | ||||||
|     transition: 0.2s; |  | ||||||
|     height: 100%; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| .emty { |  | ||||||
|   width: 100%; |  | ||||||
|   display: flex; |  | ||||||
|   flex-flow: column nowrap; |  | ||||||
|   justify-content: center; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 5px; |  | ||||||
|   color: #9ea3b4; |  | ||||||
| } |  | ||||||
| .button { |  | ||||||
|   display: flex; |  | ||||||
|   padding: 5px 16px; |  | ||||||
|   color: #00a3ff; |  | ||||||
|   justify-content: center; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 4px; |  | ||||||
|   border-radius: 6px; |  | ||||||
|   background: #f1faff; |  | ||||||
| } |  | ||||||
| .upload-container { |  | ||||||
|   width: fit-content; |  | ||||||
|   position: relative; |  | ||||||
|   .upload-loading { |  | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|     left: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 100%; |  | ||||||
|     background-color: transparent; |  | ||||||
|     z-index: 100; |  | ||||||
|     display: flex; |  | ||||||
|     flex-flow: row nowrap; |  | ||||||
|     align-items: center; |  | ||||||
|     justify-content: center; |  | ||||||
|     background-color: #ffffff84; |  | ||||||
|     .loading { |  | ||||||
|       --w: 13ch; |  | ||||||
|       // font-weight: bold; |  | ||||||
|       // font-family: monospace; |  | ||||||
|       font-size: medium; |  | ||||||
|       letter-spacing: var(--w); |  | ||||||
|       width: var(--w); |  | ||||||
|       overflow: hidden; |  | ||||||
|       white-space: nowrap; |  | ||||||
|       color: #0000; |  | ||||||
|       text-shadow: |  | ||||||
|         calc(0 * var(--w)) 0 #000, |  | ||||||
|         calc(-1 * var(--w)) 0 #000, |  | ||||||
|         calc(-2 * var(--w)) 0 #000, |  | ||||||
|         calc(-3 * var(--w)) 0 #000, |  | ||||||
|         calc(-4 * var(--w)) 0 #000, |  | ||||||
|         calc(-5 * var(--w)) 0 #000, |  | ||||||
|         calc(-6 * var(--w)) 0 #000, |  | ||||||
|         calc(-7 * var(--w)) 0 #000, |  | ||||||
|         calc(-8 * var(--w)) 0 #000, |  | ||||||
|         calc(-9 * var(--w)) 0 #000; |  | ||||||
|       animation: c10 2s infinite linear; |  | ||||||
|       &:before { |  | ||||||
|         content: '文件上传中...'; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       @keyframes c10 { |  | ||||||
|         9.09% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) -10px #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         18.18% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) -10px #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         27.27% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) -10px #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         36.36% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) -10px #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         45.45% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) -10px #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         54.54% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) -10px #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         63.63% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) -10px #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         72.72% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) -10px #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         81.81% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) -10px #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         90.90% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) -10px #000; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
| @ -1,205 +0,0 @@ | |||||||
| <template> |  | ||||||
|   <main class="upload-container"> |  | ||||||
|     <el-upload |  | ||||||
|       :file-list="fileList" |  | ||||||
|       :action="uploadUrl" |  | ||||||
|       :http-request="httpRequest" |  | ||||||
|       :auto-upload="false" |  | ||||||
|       :on-success="onSuccess" |  | ||||||
|       :on-error="onError" |  | ||||||
|       multiple |  | ||||||
|       :limit="limit" |  | ||||||
|       :on-preview="handlerPerview" |  | ||||||
|       :on-remove="remove" |  | ||||||
|       :before-remove="beforeRemove" |  | ||||||
|       list-type="picture-card" |  | ||||||
|       accept=".jpg,.png,.jpeg,.gif" |  | ||||||
|       :on-exceed="onExceed" |  | ||||||
|       ref="uploadRef" |  | ||||||
|     > |  | ||||||
|       <Icon icon="ep:plus" /> |  | ||||||
|     </el-upload> |  | ||||||
|     <el-dialog v-model="perview.show" style="width: fit-content"> |  | ||||||
|       <img w-full :src="perview.url" alt="Preview Image" style="max-height: 60vh" /> |  | ||||||
|     </el-dialog> |  | ||||||
|   </main> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import { useUpload } from '@/components/UploadFile/src/useUpload' |  | ||||||
| import { ElMessage, ElMessageBox } from 'element-plus' |  | ||||||
| defineOptions({ |  | ||||||
|   name: 'InnerUploadImg' |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| const emit = defineEmits(['handlerRemove', 'handlerSuccess', 'hanlderWriteState']) |  | ||||||
| 
 |  | ||||||
| const props = defineProps({ |  | ||||||
|   uploadList: { |  | ||||||
|     type: Array, |  | ||||||
|     default: () => [] |  | ||||||
|   }, |  | ||||||
|   limit: { |  | ||||||
|     type: Number, |  | ||||||
|     default: 3 |  | ||||||
|   } |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| watch( |  | ||||||
|   () => props.uploadList, |  | ||||||
|   (newVal) => { |  | ||||||
|     fileList.value = newVal |  | ||||||
|   }, |  | ||||||
|   { deep: true } |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const fileList: any = ref([]) |  | ||||||
| 
 |  | ||||||
| // el-upload组件 |  | ||||||
| const uploadRef = ref() |  | ||||||
| 
 |  | ||||||
| // 自定义上传地址与方式 |  | ||||||
| const { uploadUrl, httpRequest } = useUpload() |  | ||||||
| 
 |  | ||||||
| // 上传错误的处理 |  | ||||||
| const onError = (e: any, file) => { |  | ||||||
|   ElMessage({ message: `${file.name}上传失败,${e}`, type: 'error' }) |  | ||||||
|   // unref(uploadRef).handleRemove(file) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 移除文件 |  | ||||||
| const remove = (file: any) => { |  | ||||||
|   emit('handlerRemove', file) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // el upload 删除之前的处理 |  | ||||||
| const beforeRemove = (file) => { |  | ||||||
|   return ElMessageBox.alert(`确认移除${file.name}吗?`, '删除', { |  | ||||||
|     confirmButtonText: '确认', |  | ||||||
|     showCancelButton: true, |  | ||||||
|     cancelButtonText: '取消', |  | ||||||
|     distinguishCancelAndClose: true, |  | ||||||
|     showClose: false |  | ||||||
|   }) |  | ||||||
|     .then(() => { |  | ||||||
|       return true |  | ||||||
|     }) |  | ||||||
|     .catch(() => { |  | ||||||
|       return false |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 触发组件el的上传事件 |  | ||||||
| const upload = () => { |  | ||||||
|   unref(uploadRef).submit() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 上传成功的回调 |  | ||||||
| const onSuccess = (response: any) => { |  | ||||||
|   emit('handlerSuccess', response) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 预览控制 |  | ||||||
| const perview = reactive({ |  | ||||||
|   show: false, |  | ||||||
|   url: '' |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| // 预览事件 |  | ||||||
| const handlerPerview = (file) => { |  | ||||||
|   perview.url = file.url |  | ||||||
|   perview.show = true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 超出上传数量的回调 |  | ||||||
| const onExceed = () => { |  | ||||||
|   ElMessage.warning(`只能上传${props.limit}张图片`) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| onMounted(() => { |  | ||||||
|   fileList.value = props.uploadList |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| // 在组件撒上暴露方法 |  | ||||||
| defineExpose({ |  | ||||||
|   upload |  | ||||||
| }) |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style lang="scss" scoped> |  | ||||||
| :deep(.el-upload-list__item:hover) { |  | ||||||
|   background-color: transparent !important; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .file { |  | ||||||
|   width: fit-content; |  | ||||||
|   margin-bottom: 5px; |  | ||||||
|   padding: 0px 10px; |  | ||||||
|   display: flex; |  | ||||||
|   flex-flow: row nowrap; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 10px; |  | ||||||
|   cursor: pointer; |  | ||||||
|   border-radius: 4px; |  | ||||||
|   position: relative; |  | ||||||
|   .name { |  | ||||||
|     color: blue; |  | ||||||
|     font-size: small; |  | ||||||
|     letter-spacing: 1px; |  | ||||||
|     width: 200px; |  | ||||||
|     white-space: nowrap; |  | ||||||
|     overflow: hidden; |  | ||||||
|     text-overflow: ellipsis; |  | ||||||
|   } |  | ||||||
|   .state { |  | ||||||
|     font-size: small; |  | ||||||
|   } |  | ||||||
|   .icon { |  | ||||||
|     padding: 2px; |  | ||||||
|     border-radius: 50%; |  | ||||||
|   } |  | ||||||
|   .edit:hover { |  | ||||||
|     background-color: #46c9f7; |  | ||||||
|     box-shadow: 0 0 2px 0 #144d61; |  | ||||||
|   } |  | ||||||
|   .close:hover { |  | ||||||
|     background-color: #f83f2a; |  | ||||||
|     box-shadow: 0 0 2px 0 #640404; |  | ||||||
|   } |  | ||||||
|   &:hover { |  | ||||||
|     background-color: #fff; |  | ||||||
|     box-shadow: 0 0 4px 0px #ccc; |  | ||||||
|     border-radius: 4px; |  | ||||||
|   } |  | ||||||
|   .program { |  | ||||||
|     position: absolute; |  | ||||||
|     bottom: 0%; |  | ||||||
|     // border-radius: 999px; |  | ||||||
|     background-color: #89ffc2a0; |  | ||||||
|     left: 0; |  | ||||||
|     width: var(--p); |  | ||||||
|     // width: 100%; |  | ||||||
|     transition: 0.2s; |  | ||||||
|     height: 100%; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .upload-container { |  | ||||||
|   width: fit-content; |  | ||||||
|   position: relative; |  | ||||||
|   .upload-loading { |  | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|     left: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 100%; |  | ||||||
|     background-color: transparent; |  | ||||||
|     z-index: 100; |  | ||||||
|     display: flex; |  | ||||||
|     flex-flow: row nowrap; |  | ||||||
|     align-items: center; |  | ||||||
|     justify-content: center; |  | ||||||
|     background-color: #ffffff84; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
| @ -1,568 +0,0 @@ | |||||||
| <template> |  | ||||||
|   <main class="upload-container"> |  | ||||||
|     <section class="file-list"> |  | ||||||
|       <section |  | ||||||
|         class="file" |  | ||||||
|         v-for="(file, index) in fileList" |  | ||||||
|         :key="file.uid" |  | ||||||
|         :style="setFileState(file)" |  | ||||||
|         v-if="fileList.length > 0" |  | ||||||
|       > |  | ||||||
|         <Icon :icon="getFileIcon(file.name)" :size="40" /> |  | ||||||
|         <el-tooltip class="box-item" effect="dark" :content="file.name" placement="top"> |  | ||||||
|           <section class="info"> |  | ||||||
|             <span class="name">{{ file.name }}</span> |  | ||||||
|             <span class="time" v-if="file.id"> |  | ||||||
|               {{ formatDate(file.createTime, 'YYYY年M月D日') }} |  | ||||||
|             </span> |  | ||||||
|             <span class="size" v-else>{{ niceBytes(file.size) }}</span> |  | ||||||
|           </section> |  | ||||||
|         </el-tooltip> |  | ||||||
|         <!-- <span |  | ||||||
|           class="state" |  | ||||||
|           v-show="file.status !== 'fail' && iswrite" |  | ||||||
|         > |  | ||||||
|           {{ extendParams[file.uid] ? '可编辑' : '只读' }} |  | ||||||
|           <Icon :icon="extendParams[file.uid] ? 'ep:edit-pen' : 'ep:view'" /> |  | ||||||
|         </span> --> |  | ||||||
|         <Icon |  | ||||||
|           :icon="extendParams[file.uid] ? 'ep:edit-pen' : 'ep:view'" |  | ||||||
|           class="icon edit" |  | ||||||
|           :size="15" |  | ||||||
|           @click="changeFileState(file)" |  | ||||||
|           v-show="file.status !== 'fail' && iswrite" |  | ||||||
|         /> |  | ||||||
|         <Icon icon="ep:delete" class="icon close" :size="15" @click="remove(file, index)" /> |  | ||||||
|         <section class="program" :style="{ '--p': `${processData[file.uid]}%` }"> </section> |  | ||||||
|       </section> |  | ||||||
|       <section class="emty" v-else> |  | ||||||
|         <Icon icon="svg-icon:customs-empty" :size="40" /> |  | ||||||
|         <span>暂无文件</span> |  | ||||||
|       </section> |  | ||||||
|     </section> |  | ||||||
|     <el-upload |  | ||||||
|       :file-list="fileList" |  | ||||||
|       :action="uploadUrl" |  | ||||||
|       :data="uploadExtendParams" |  | ||||||
|       :http-request="httpRequest" |  | ||||||
|       :auto-upload="false" |  | ||||||
|       :on-change="onChange" |  | ||||||
|       :on-success="onSuccess" |  | ||||||
|       :on-error="onError" |  | ||||||
|       :on-progress="onProgress" |  | ||||||
|       multiple |  | ||||||
|       :limit="limit > 0 ? limit : ''" |  | ||||||
|       :show-file-list="false" |  | ||||||
|       :accept="accept" |  | ||||||
|       ref="uploadRef" |  | ||||||
|       :on-exceed="exceed" |  | ||||||
|     > |  | ||||||
|       <section class="upload-button-group"> |  | ||||||
|         <section class="upload-button mr10px"> |  | ||||||
|           <Icon icon="svg-icon:customs-upload" :size="18" class="mr-5px" /> |  | ||||||
|           选取文件 |  | ||||||
|         </section> |  | ||||||
|         <section class="button" v-if="showUploadButton" @click.stop="upload"> |  | ||||||
|           <Icon icon="ep:upload-filled" :size="18" class="mr-5px" /> |  | ||||||
|           上传 |  | ||||||
|         </section> |  | ||||||
|       </section> |  | ||||||
|     </el-upload> |  | ||||||
| 
 |  | ||||||
|     <section class="upload-loading" v-show="uploadLoading"> |  | ||||||
|       <div class="loading"> <span></span><span></span><span></span><span></span> </div> |  | ||||||
|     </section> |  | ||||||
|   </main> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import { useUpload } from '@/components/UploadFile/src/useUpload' |  | ||||||
| import { getFileIcon, niceBytes } from '@/utils/filestate' |  | ||||||
| import { formatDate } from '@/utils/formatTime' |  | ||||||
| import { ElMessage, ElMessageBox } from 'element-plus' |  | ||||||
| defineOptions({ |  | ||||||
|   name: 'InnerUploader' |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| const uploadLoading = ref(false) |  | ||||||
| 
 |  | ||||||
| const emit = defineEmits(['handlerRemove', 'handlerSuccess', 'hanlderWriteState']) |  | ||||||
| 
 |  | ||||||
| // 上传额外参数 |  | ||||||
| const uploadExtendParams = (file: any) => { |  | ||||||
|   return { writeAble: extendParams.value[file.uid] } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const props = defineProps({ |  | ||||||
|   /** |  | ||||||
|    * 文件列表 |  | ||||||
|    * @default Array |  | ||||||
|    */ |  | ||||||
|   uploadList: { |  | ||||||
|     type: Array, |  | ||||||
|     default: () => [] |  | ||||||
|   }, |  | ||||||
|   limit: { |  | ||||||
|     type: Number, |  | ||||||
|     default: '' |  | ||||||
|   }, |  | ||||||
|   /** |  | ||||||
|    * 是否可已进行文件权限编辑 |  | ||||||
|    * @default true |  | ||||||
|    */ |  | ||||||
|   iswrite: { |  | ||||||
|     type: Boolean, |  | ||||||
|     default: true |  | ||||||
|   }, |  | ||||||
|   accept: { |  | ||||||
|     type: String, |  | ||||||
|     default: '*' |  | ||||||
|   }, |  | ||||||
|   showUploadButton: { |  | ||||||
|     type: Boolean, |  | ||||||
|     default: false |  | ||||||
|   } |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| watch( |  | ||||||
|   () => props.uploadList, |  | ||||||
|   (newVal) => { |  | ||||||
|     fileList.value = newVal |  | ||||||
|   }, |  | ||||||
|   { deep: true } |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const fileList: any = ref([]) |  | ||||||
| 
 |  | ||||||
| // 额外参数 |  | ||||||
| const extendParams = ref({}) |  | ||||||
| 
 |  | ||||||
| // el-upload组件 |  | ||||||
| const uploadRef = ref() |  | ||||||
| 
 |  | ||||||
| // 自定义上传地址与方式 |  | ||||||
| const { uploadUrl, httpRequest } = useUpload() |  | ||||||
| 
 |  | ||||||
| // 文件状态改变 |  | ||||||
| const onChange = (file: any) => { |  | ||||||
|   // console.log(Math.round(file.raw.size / 1024 / 1024) + 'MB') |  | ||||||
|   if (props.accept !== '*') { |  | ||||||
|     if (props.accept.split(',').findIndex((i) => file.name.includes(i)) == -1) { |  | ||||||
|       unref(uploadRef).handleRemove(file) |  | ||||||
|       ElMessage({ message: `${file.name}文件格式不正确`, type: 'error' }) |  | ||||||
|       return |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   if (file.status === 'ready') { |  | ||||||
|     extendParams.value[file.uid] = false |  | ||||||
|     fileList.value.push(file) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 更改文件属性 |  | ||||||
| const changeFileState = (file: any) => { |  | ||||||
|   extendParams.value[file.uid] = !extendParams.value[file.uid] |  | ||||||
|   if (file.status === 'success') { |  | ||||||
|     emit('hanlderWriteState', file) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 上传错误的处理 |  | ||||||
| const onError = (e: any, file) => { |  | ||||||
|   ElMessage({ message: `${file.name}上传失败,${e}`, type: 'error' }) |  | ||||||
|   // unref(uploadRef).handleRemove(file) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 移除文件 |  | ||||||
| const remove = (file: any, index: any) => { |  | ||||||
|   if (file.status === 'success') { |  | ||||||
|     ElMessageBox.alert(`确认移除${file.name}吗?`, '删除', { |  | ||||||
|       confirmButtonText: '确认', |  | ||||||
|       showCancelButton: true, |  | ||||||
|       cancelButtonText: '取消', |  | ||||||
|       distinguishCancelAndClose: true, |  | ||||||
|       showClose: false |  | ||||||
|     }) |  | ||||||
|       .then(() => { |  | ||||||
|         fileList.value.splice(index, 1) |  | ||||||
|         emit('handlerRemove', file) |  | ||||||
|       }) |  | ||||||
|       .catch(() => {}) |  | ||||||
|     return |  | ||||||
|   } |  | ||||||
|   fileList.value.splice(index, 1) |  | ||||||
|   unref(uploadRef).handleRemove(file) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 触发组件el的上传事件 |  | ||||||
| const upload = () => { |  | ||||||
|   if (fileList.value.filter((file) => file.status == 'ready').length > 0) { |  | ||||||
|     uploadLoading.value = true |  | ||||||
|     unref(uploadRef).submit() |  | ||||||
|     return |  | ||||||
|   } |  | ||||||
|   ElMessage('暂无可上传的文件') |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 成功回调暂存 |  | ||||||
| const successMap: any = ref([]) |  | ||||||
| 
 |  | ||||||
| // 上传成功的回调 |  | ||||||
| const onSuccess = (response: any) => { |  | ||||||
|   delete processData.value[response.uid] |  | ||||||
|   successMap.value.push(response) |  | ||||||
|   if (fileList.value.filter((file) => file.status == 'ready').length == 0) { |  | ||||||
|     uploadLoading.value = false |  | ||||||
|     emit('handlerSuccess', successMap.value) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 设置不同状态文件的背景 |  | ||||||
| const setFileState = (file: any) => { |  | ||||||
|   const colorlist = { |  | ||||||
|     success: '#89ffc3', |  | ||||||
|     fail: '#ffd2d2' |  | ||||||
|   } |  | ||||||
|   return { backgroundColor: colorlist[file.status] } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 进度条对象 |  | ||||||
| const processData = ref({}) |  | ||||||
| // 上传进度 |  | ||||||
| const onProgress = (UploadProgressEvent, uploadFile) => { |  | ||||||
|   // console.log('UploadProgressEvent', UploadProgressEvent) |  | ||||||
|   processData.value[uploadFile.uid] = UploadProgressEvent.percent |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function exceed() { |  | ||||||
|   ElMessage({ |  | ||||||
|     message: `只允许上传${props.limit}个文件` |  | ||||||
|   }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| onMounted(() => { |  | ||||||
|   fileList.value = props.uploadList |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| onBeforeUnmount(() => { |  | ||||||
|   fileList.value = [] |  | ||||||
|   unref(uploadRef).clearFiles() |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| onActivated(() => { |  | ||||||
|   console.log('onActivated') |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| // 在组件撒上暴露方法 |  | ||||||
| defineExpose({ |  | ||||||
|   upload |  | ||||||
| }) |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style lang="scss" scoped> |  | ||||||
| * { |  | ||||||
|   box-sizing: border-box; |  | ||||||
| } |  | ||||||
| :deep(.el-upload-list__item:hover) { |  | ||||||
|   background-color: transparent !important; |  | ||||||
| } |  | ||||||
| :deep(.el-upload) { |  | ||||||
|   flex-flow: column; |  | ||||||
|   gap: 10px; |  | ||||||
|   align-items: flex-start; |  | ||||||
| } |  | ||||||
| .file-list { |  | ||||||
|   width: 220px; |  | ||||||
|   max-height: 60vh; |  | ||||||
|   overflow-y: scroll; |  | ||||||
|   // margin: 10px 0; |  | ||||||
|   margin-bottom: 10px; |  | ||||||
|   display: block; |  | ||||||
|   border-radius: 6px; |  | ||||||
|   border: 1px solid #eff2f5; |  | ||||||
|   background: #fcfdfd; |  | ||||||
|   padding: 8px 6px; |  | ||||||
| } |  | ||||||
| .file { |  | ||||||
|   // width: fit-content; |  | ||||||
|   margin-bottom: 2px; |  | ||||||
|   padding: 4px 10px; |  | ||||||
|   display: flex; |  | ||||||
|   flex-flow: row nowrap; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 5px; |  | ||||||
|   cursor: pointer; |  | ||||||
|   border-radius: 6px; |  | ||||||
|   position: relative; |  | ||||||
|   justify-content: center; |  | ||||||
|   transition: 0.2s; |  | ||||||
|   .info { |  | ||||||
|     flex: 1; |  | ||||||
|     overflow: hidden; |  | ||||||
|     display: flex; |  | ||||||
|     flex-flow: column nowrap; |  | ||||||
|     .name { |  | ||||||
|       font-weight: bold; |  | ||||||
|       font-size: small; |  | ||||||
|       letter-spacing: 1px; |  | ||||||
|       white-space: nowrap; |  | ||||||
|       overflow: hidden; |  | ||||||
|       text-overflow: ellipsis; |  | ||||||
|     } |  | ||||||
|     .size { |  | ||||||
|       font-size: 12px; |  | ||||||
|     } |  | ||||||
|     .time { |  | ||||||
|       font-size: 12px; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .state { |  | ||||||
|     font-size: small; |  | ||||||
|   } |  | ||||||
|   .icon { |  | ||||||
|     padding: 2px; |  | ||||||
|     border-radius: 50%; |  | ||||||
|     transition: 0.2s; |  | ||||||
|   } |  | ||||||
|   // .edit:hover { |  | ||||||
|   //   background-color: #46c9f7; |  | ||||||
|   //   box-shadow: 0 0 2px 0 #144d61; |  | ||||||
|   // } |  | ||||||
|   .close:hover, |  | ||||||
|   .edit:hover { |  | ||||||
|     // background-color: #f83f2a; |  | ||||||
|     // box-shadow: 0 0 2px 0 #640404; |  | ||||||
|     filter: drop-shadow(0 0 1px #000000ab); |  | ||||||
|   } |  | ||||||
|   &:hover { |  | ||||||
|     background-color: #fff; |  | ||||||
|     box-shadow: 0 0 4px 0px #ccc; |  | ||||||
|     border-radius: 4px; |  | ||||||
|   } |  | ||||||
|   .program { |  | ||||||
|     position: absolute; |  | ||||||
|     bottom: 0%; |  | ||||||
|     // border-radius: 999px; |  | ||||||
|     background-color: #89ffc2a0; |  | ||||||
|     left: 0; |  | ||||||
|     width: var(--p); |  | ||||||
|     // width: 100%; |  | ||||||
|     transition: 0.2s; |  | ||||||
|     height: 100%; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| .emty { |  | ||||||
|   width: 100%; |  | ||||||
|   display: flex; |  | ||||||
|   flex-flow: column nowrap; |  | ||||||
|   justify-content: center; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 5px; |  | ||||||
|   color: #9ea3b4; |  | ||||||
| } |  | ||||||
| .upload-button-group { |  | ||||||
|   display: flex; |  | ||||||
|   flex-flow: row nowrap; |  | ||||||
| } |  | ||||||
| .upload-button { |  | ||||||
|   display: flex; |  | ||||||
|   padding: 5px 10px; |  | ||||||
|   color: #00a3ff; |  | ||||||
|   justify-content: center; |  | ||||||
|   align-items: center; |  | ||||||
|   gap: 4px; |  | ||||||
|   border-radius: 6px; |  | ||||||
|   background: #f1faff; |  | ||||||
|   border: 1px dashed #00a3ff; |  | ||||||
|   &:hover { |  | ||||||
|     border: 1px solid #00a3ff; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| .upload-container { |  | ||||||
|   width: fit-content; |  | ||||||
|   position: relative; |  | ||||||
|   .upload-loading { |  | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|     left: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 100%; |  | ||||||
|     background-color: transparent; |  | ||||||
|     z-index: 100; |  | ||||||
|     display: flex; |  | ||||||
|     flex-flow: row nowrap; |  | ||||||
|     align-items: center; |  | ||||||
|     justify-content: center; |  | ||||||
|     background-color: #ffffff84; |  | ||||||
|     .loading { |  | ||||||
|       --w: 13ch; |  | ||||||
|       // font-weight: bold; |  | ||||||
|       // font-family: monospace; |  | ||||||
|       font-size: medium; |  | ||||||
|       letter-spacing: var(--w); |  | ||||||
|       width: var(--w); |  | ||||||
|       overflow: hidden; |  | ||||||
|       white-space: nowrap; |  | ||||||
|       color: #0000; |  | ||||||
|       text-shadow: |  | ||||||
|         calc(0 * var(--w)) 0 #000, |  | ||||||
|         calc(-1 * var(--w)) 0 #000, |  | ||||||
|         calc(-2 * var(--w)) 0 #000, |  | ||||||
|         calc(-3 * var(--w)) 0 #000, |  | ||||||
|         calc(-4 * var(--w)) 0 #000, |  | ||||||
|         calc(-5 * var(--w)) 0 #000, |  | ||||||
|         calc(-6 * var(--w)) 0 #000, |  | ||||||
|         calc(-7 * var(--w)) 0 #000, |  | ||||||
|         calc(-8 * var(--w)) 0 #000, |  | ||||||
|         calc(-9 * var(--w)) 0 #000; |  | ||||||
|       animation: c10 2s infinite linear; |  | ||||||
|       &:before { |  | ||||||
|         content: '文件上传中...'; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       @keyframes c10 { |  | ||||||
|         9.09% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) -10px #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         18.18% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) -10px #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         27.27% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) -10px #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         36.36% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) -10px #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         45.45% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) -10px #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         54.54% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) -10px #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         63.63% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) -10px #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         72.72% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) -10px #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         81.81% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) -10px #000, |  | ||||||
|             calc(-9 * var(--w)) 0 #000; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         90.90% { |  | ||||||
|           text-shadow: |  | ||||||
|             calc(0 * var(--w)) 0 #000, |  | ||||||
|             calc(-1 * var(--w)) 0 #000, |  | ||||||
|             calc(-2 * var(--w)) 0 #000, |  | ||||||
|             calc(-3 * var(--w)) 0 #000, |  | ||||||
|             calc(-4 * var(--w)) 0 #000, |  | ||||||
|             calc(-5 * var(--w)) 0 #000, |  | ||||||
|             calc(-6 * var(--w)) 0 #000, |  | ||||||
|             calc(-7 * var(--w)) 0 #000, |  | ||||||
|             calc(-8 * var(--w)) 0 #000, |  | ||||||
|             calc(-9 * var(--w)) -10px #000; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
| @ -0,0 +1,182 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import position from '@/assets/imgs/position.png' | ||||||
|  | 
 | ||||||
|  | const show = ref(false) | ||||||
|  | const AMap = (window as any).AMap | ||||||
|  | let map: any = null | ||||||
|  | const center = ref() | ||||||
|  | const loading = ref(false) | ||||||
|  | let mapSearch: any = null | ||||||
|  | const address = ref() | ||||||
|  | const addressInfo = ref() | ||||||
|  | const message = useMessage() | ||||||
|  | 
 | ||||||
|  | const open = async (param) => { | ||||||
|  |   show.value = true | ||||||
|  |   await nextTick(() => { | ||||||
|  |     init(param) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | const emits = defineEmits(['success']) | ||||||
|  | 
 | ||||||
|  | const init = (param) => { | ||||||
|  |   loading.value = true | ||||||
|  |   navigator.geolocation.getCurrentPosition((position) => { | ||||||
|  |     center.value = [position.coords.longitude, position.coords.latitude] | ||||||
|  |     map = new AMap.Map('map-container', { | ||||||
|  |       center: center.value, // 设置地图中心点坐标 | ||||||
|  |       zoom: 14, // 设置地图缩放级别 | ||||||
|  |       viewMode: '2D' | ||||||
|  |     }) | ||||||
|  |     map.on('complete', () => { | ||||||
|  |       const logo = document.getElementsByClassName('amap-logo')[0] | ||||||
|  |       const copyRight = document.getElementsByClassName('amap-copyright')[0] | ||||||
|  |       logo.setAttribute('style', 'display:none !important') | ||||||
|  |       copyRight.setAttribute('style', 'display:none !important') | ||||||
|  |     }) | ||||||
|  |     AMap.plugin('AMap.Autocomplete', () => { | ||||||
|  |       mapSearch = new AMap.Autocomplete({ | ||||||
|  |         city: '锦州' | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |     if (param) { | ||||||
|  |       address.value = param.name | ||||||
|  |       addressInfo.value = param | ||||||
|  |       addMarker() | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   loading.value = false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const querySearchAsync = (queryString: string, cb: (arg: any) => void) => { | ||||||
|  |   mapSearch.search(queryString, (status, result) => { | ||||||
|  |     if (status === 'complete' && result.info === 'OK') { | ||||||
|  |       cb(result.tips) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   cb([]) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const handleSelect = (item: Record<string, any>) => { | ||||||
|  |   address.value = item.name | ||||||
|  |   addressInfo.value = item | ||||||
|  |   addMarker() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const addMarker = () => { | ||||||
|  |   clearMarker() | ||||||
|  |   const locate = [addressInfo.value.location.lng, addressInfo.value.location.lat] | ||||||
|  |   const icon = new AMap.Icon({ | ||||||
|  |     image: position, | ||||||
|  |     size: new AMap.Size(50, 50), | ||||||
|  |     imageSize: new AMap.Size(50, 50) | ||||||
|  |   }) | ||||||
|  |   const marker = new AMap.Marker({ | ||||||
|  |     icon, | ||||||
|  |     position: locate, | ||||||
|  |     offset: new AMap.Pixel(-25, -45) | ||||||
|  |   }) | ||||||
|  |   const infoWindow = new AMap.InfoWindow({ | ||||||
|  |     anchor: 'top-center', | ||||||
|  |     content: `<section class="flex flex-col gap-4px"><span class="font-bold">${addressInfo.value.name ?? ''}</span><span>${addressInfo.value.district || ''}${addressInfo.value.address}</span></section>` | ||||||
|  |   }) | ||||||
|  |   marker.on('click', () => { | ||||||
|  |     infoWindow.open(map, locate) | ||||||
|  |   }) | ||||||
|  |   infoWindow.open(map, locate) | ||||||
|  |   map.setCenter(locate) | ||||||
|  |   map.add(marker) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const clearMarker = () => { | ||||||
|  |   map.clearMap() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const confirm = () => { | ||||||
|  |   emits('success', addressInfo.value) | ||||||
|  |   reset() | ||||||
|  |   message.success('操作成功') | ||||||
|  |   show.value = false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const reset = () => { | ||||||
|  |   address.value = '' | ||||||
|  |   addressInfo.value = null | ||||||
|  |   clearMarker() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   init() | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | defineExpose({ | ||||||
|  |   open | ||||||
|  | }) | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Dialog v-model="show" title="选择地址" top="5vh" width="80vw" height="60vh"> | ||||||
|  |     <section id="map-container" v-loading="loading"> | ||||||
|  |       <section class="search-wrapper"> | ||||||
|  |         <el-autocomplete | ||||||
|  |           v-model="address" | ||||||
|  |           :fetch-suggestions="querySearchAsync" | ||||||
|  |           placeholder="请输入地址" | ||||||
|  |           @select="handleSelect" | ||||||
|  |           :debounce="500" | ||||||
|  |           popper-class="address-suggestion" | ||||||
|  |         > | ||||||
|  |           <template #default="{ item }"> | ||||||
|  |             <section class="flex gap-5px items-center suggestion"> | ||||||
|  |               <Icon icon="ep:location-filled" :size="20" /> | ||||||
|  |               <section class="flex flex-col gap-4px"> | ||||||
|  |                 <section class="font-bold">{{ item.name }}</section> | ||||||
|  |                 <section class="address">{{ `${item.district}${item.address}` }}</section> | ||||||
|  |               </section> | ||||||
|  |             </section> | ||||||
|  |           </template> | ||||||
|  |         </el-autocomplete> | ||||||
|  |         <el-button type="primary" @click="confirm"> | ||||||
|  |           <Icon icon="ep:select" class="mr-4px" /> | ||||||
|  |           确认 | ||||||
|  |         </el-button> | ||||||
|  |       </section> | ||||||
|  |     </section> | ||||||
|  |   </Dialog> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <style scoped lang="scss"> | ||||||
|  | .search-wrapper { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   z-index: 9999; | ||||||
|  |   padding: 10px; | ||||||
|  |   display: flex; | ||||||
|  |   flex-flow: row nowrap; | ||||||
|  |   gap: 8px; | ||||||
|  | } | ||||||
|  | .map-wrapper { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | #map-container { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 80vh; | ||||||
|  | } | ||||||
|  | .address-suggestion { | ||||||
|  |   .suggestion { | ||||||
|  |     .address { | ||||||
|  |       width: 200px; | ||||||
|  |       white-space: wrap; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | :deep(.amap-logo) { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | :deep(.amap-copyright) { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @ -1,43 +1,101 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| const show = ref(false) | const show = ref(true) | ||||||
| const TMap = (window as any).qq.maps | const TMap = (window as any).TMap | ||||||
| let map: any = null | let map: any = null | ||||||
|  | const center = ref() | ||||||
|  | const loading = ref(false) | ||||||
|  | let mapSearch: any = null | ||||||
| //当前位置经纬度 | //当前位置经纬度 | ||||||
| const coordinate = reactive({ | const coordinate = reactive({ | ||||||
|   latitude: undefined, |   latitude: undefined, | ||||||
|   longitude: undefined, |   longitude: undefined, | ||||||
|   address: '' |   address: '' | ||||||
| } as any) | } as any) | ||||||
|  | const address = ref('') | ||||||
| 
 | 
 | ||||||
| const open = () => { | const open = async () => { | ||||||
|   show.value = true |   show.value = true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const init = () => { | ||||||
|  |   loading.value = true | ||||||
|   navigator.geolocation.getCurrentPosition((position) => { |   navigator.geolocation.getCurrentPosition((position) => { | ||||||
|     coordinate.latitude = position.coords.latitude |     coordinate.latitude = position.coords.latitude | ||||||
|     coordinate.longitude = position.coords.longitude |     coordinate.longitude = position.coords.longitude | ||||||
|  |     center.value = new TMap.LatLng(coordinate.latitude, coordinate.longitude) | ||||||
|  |     map = new TMap.Map(document.getElementById('tencent-container'), { | ||||||
|  |       center: center.value, // 设置地图中心点坐标 | ||||||
|  |       zoom: 14, // 设置地图缩放级别 | ||||||
|  |       viewMode: '2D', | ||||||
|  |       disableDefaultUI: false | ||||||
|  |     }) | ||||||
|  |     const a = document.querySelector( | ||||||
|  |       'canvas+div:last-child div:last-child div:first-child [style="margin: 1px; display: flex; align-items: center; user-select: none;"]' | ||||||
|  |     ) | ||||||
|  |     mapSearch = new TMap.service.Search() | ||||||
|  |     a?.setAttribute('style', 'display:none') | ||||||
|   }) |   }) | ||||||
|   nextTick(() => { |   loading.value = false | ||||||
|     init() |  | ||||||
|   }) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const init = () => { | const querySearchAsync = (queryString: string, cb: (arg: any) => void) => { | ||||||
|   const center = new TMap.LatLng(coordinate.latitude, coordinate.longitude) |   mapSearch.searchRegion({ | ||||||
|   map = new TMap.Map(document.getElementById('tencent-container'), { |     keyword: queryString, | ||||||
|     center: center, // 设置地图中心点坐标 |     center: center.value, | ||||||
|     zoom: 11, // 设置地图缩放级别 |     autoExtend: true, | ||||||
|     viewMode: '2D' |     servicesk: 'Lut0q8OhqGkXr6t7dOvxuIhUUf0M3ZzD', | ||||||
|  |     success: (res: any) => { | ||||||
|  |       console.log(res) | ||||||
|  |     } | ||||||
|   }) |   }) | ||||||
|  |   cb([]) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const handleSelect = (item: Record<string, any>) => { | ||||||
|  |   console.log(item) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   init() | ||||||
|  | }) | ||||||
|  | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
|   open |   open | ||||||
| }) | }) | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <Dialog v-model="show" title="选择地址"> |   <Dialog v-model="show" title="选择地址" top="5vh" width="80vw" height="60vh"> | ||||||
|     <section id="tencent-container"></section> |     <section id="tencent-container" v-loading="loading"> | ||||||
|  |       <section class="search-wrapper"> | ||||||
|  |         <el-autocomplete | ||||||
|  |           v-model="address" | ||||||
|  |           :fetch-suggestions="querySearchAsync" | ||||||
|  |           placeholder="请输入地址" | ||||||
|  |           @select="handleSelect" | ||||||
|  |           :debounce="500" | ||||||
|  |         /> | ||||||
|  |       </section> | ||||||
|  |     </section> | ||||||
|   </Dialog> |   </Dialog> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped lang="scss"></style> | <style scoped lang="scss"> | ||||||
|  | .search-wrapper { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   z-index: 9999; | ||||||
|  |   padding: 10px; | ||||||
|  | } | ||||||
|  | .map-wrapper { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | #tencent-container { | ||||||
|  |   img { | ||||||
|  |     display: none; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | |||||||
| @ -1,202 +0,0 @@ | |||||||
| <template> |  | ||||||
|   <Dialog :title="dialogTitle" v-model="dialogVisible"> |  | ||||||
|     <el-form |  | ||||||
|       ref="formRef" |  | ||||||
|       :model="formData" |  | ||||||
|       :rules="formRules" |  | ||||||
|       label-width="100px" |  | ||||||
|       v-loading="formLoading" |  | ||||||
|     > |  | ||||||
|       <el-form-item label="选择企业" prop="enterpriseId"> |  | ||||||
|         <el-select |  | ||||||
|           v-model="formData.enterpriseId" |  | ||||||
|           filterable |  | ||||||
|           remote |  | ||||||
|           reserve-keyword |  | ||||||
|           placeholder="请输入企业名称搜索" |  | ||||||
|           :remote-method="remoteSearchEnterprise" |  | ||||||
|           :loading="enterpriseLoading" |  | ||||||
|         > |  | ||||||
|           <el-option |  | ||||||
|             v-for="item in enterpriseOptions" |  | ||||||
|             :key="item.id" |  | ||||||
|             :label="item.enterprisesName" |  | ||||||
|             :value="item.id" |  | ||||||
|           /> |  | ||||||
|         </el-select> |  | ||||||
|       </el-form-item> |  | ||||||
|       <el-form-item label="资质名称" prop="qualificationName"> |  | ||||||
|         <el-select v-model="formData.qualificationName" placeholder="请选择资质名称"> |  | ||||||
|           <el-option |  | ||||||
|             v-for="dict in getIntDictOptions(DICT_TYPE.ENTERPRISES_QUA)" |  | ||||||
|             :key="dict.value" |  | ||||||
|             :label="dict.label" |  | ||||||
|             :value="dict.value" |  | ||||||
|           /> |  | ||||||
|         </el-select> |  | ||||||
| 
 |  | ||||||
|       </el-form-item> |  | ||||||
|       <el-form-item label="资质到期日期" prop="expiryDate"> |  | ||||||
|         <el-date-picker |  | ||||||
|           v-model="formData.expiryDate" |  | ||||||
|           type="date" |  | ||||||
|           value-format="x" |  | ||||||
|           placeholder="选择资质到期日期" |  | ||||||
|         /> |  | ||||||
|       </el-form-item> |  | ||||||
|       <!-- <el-form-item label="资质描述" prop="qualificationDescription"> |  | ||||||
|         <Editor v-model="formData.qualificationDescription" height="150px" /> |  | ||||||
|       </el-form-item> --> |  | ||||||
|        |  | ||||||
|       <!-- <el-form-item label="办理日期" prop="handleDate"> |  | ||||||
|         <el-date-picker |  | ||||||
|           v-model="formData.handleDate" |  | ||||||
|           type="date" |  | ||||||
|           value-format="x" |  | ||||||
|           placeholder="选择办理日期" |  | ||||||
|         /> |  | ||||||
|       </el-form-item> --> |  | ||||||
|       <el-form-item label="资质编号" prop="enterpriseAuth"> |  | ||||||
|         <el-input v-model="formData.enterpriseAuth"  placeholder="请输入资质编号" /> |  | ||||||
|       </el-form-item> |  | ||||||
|     </el-form> |  | ||||||
|     <el-form-item label="资质图片" prop="files"> |  | ||||||
|       <UploadImgs v-model="fileIds" :limit="1" /> |  | ||||||
|     </el-form-item> |  | ||||||
|     <template #footer> |  | ||||||
|       <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> |  | ||||||
|       <el-button @click="dialogVisible = false">取 消</el-button> |  | ||||||
|     </template> |  | ||||||
|   </Dialog> |  | ||||||
| </template> |  | ||||||
| <script setup lang="ts"> |  | ||||||
| import { EnterpriseQualificationApi, EnterpriseQualificationVO } from '@/api/qualification' |  | ||||||
| import { EnterprisesApi } from '@/api/enterprises' |  | ||||||
| /** 企业资质 表单 */ |  | ||||||
| defineOptions({ name: 'EnterpriseQualificationForm' }) |  | ||||||
| import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' |  | ||||||
| const { t } = useI18n() // 国际化 |  | ||||||
| const message = useMessage() // 消息弹窗 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| const dialogVisible = ref(false) // 弹窗的是否展示 |  | ||||||
| const dialogTitle = ref('') // 弹窗的标题 |  | ||||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |  | ||||||
| const formType = ref('') // 表单的类型:create - 新增;update - 修改 |  | ||||||
| const formData = ref({ |  | ||||||
|   id: undefined, |  | ||||||
|   enterpriseId: undefined, |  | ||||||
|   qualificationName: undefined, |  | ||||||
|   expiryDate: undefined, |  | ||||||
|   qualificationDescription: undefined, |  | ||||||
|   updateBy: undefined, |  | ||||||
|   createBy: undefined, |  | ||||||
|   handleDate: undefined, |  | ||||||
|   enterpriseAuth: undefined, |  | ||||||
|   files: [] as any[], |  | ||||||
| }) |  | ||||||
| const formRules = reactive({ |  | ||||||
|   qualificationName: [{ required: true, message: '资质名称,例如:排污许可证、环保合格证不能为空', trigger: 'blur' }], |  | ||||||
| }) |  | ||||||
| const formRef = ref() // 表单 Ref |  | ||||||
| const fileIds=ref([]) |  | ||||||
| 
 |  | ||||||
| // 添加企业搜索相关的响应式变量 |  | ||||||
| const enterpriseLoading = ref(false) |  | ||||||
| const enterpriseOptions = ref([]) |  | ||||||
| 
 |  | ||||||
| // 远程搜索企业的方法 |  | ||||||
| const remoteSearchEnterprise = async (query: string) => { |  | ||||||
|   if (query === '') { |  | ||||||
|     enterpriseOptions.value = [] |  | ||||||
|     return |  | ||||||
|   } |  | ||||||
|   enterpriseLoading.value = true |  | ||||||
|   try { |  | ||||||
|     const res = await EnterprisesApi.getEnterprisesPage({ |  | ||||||
|       pageNo: 1, |  | ||||||
|       pageSize: 5, |  | ||||||
|       enterprisesName: query |  | ||||||
|     }) |  | ||||||
|     enterpriseOptions.value = res.list |  | ||||||
|   } finally { |  | ||||||
|     enterpriseLoading.value = false |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** 打开弹窗 */ |  | ||||||
| const open = async (type: string, id?: number,enterpriseId?:number) => { |  | ||||||
|   dialogVisible.value = true |  | ||||||
|   dialogTitle.value = t('action.' + type) |  | ||||||
|   formType.value = type |  | ||||||
|   resetForm() |  | ||||||
|   fileIds.value=[] |  | ||||||
|   // 修改时,设置数据 |  | ||||||
|   if (id) { |  | ||||||
|     formLoading.value = true |  | ||||||
|     try { |  | ||||||
|        let res1 = await EnterpriseQualificationApi.getEnterpriseQualification(id) |  | ||||||
|        formData.value = res1; |  | ||||||
|          // 确保资质名称是数字类型 |  | ||||||
|          formData.value.qualificationName = parseInt(formData.value.qualificationName) |  | ||||||
|          fileIds.value = res1.files |  | ||||||
| // 根据企业ID获取企业信息并设置下拉框选项 |  | ||||||
|       // 获取企业详情 |  | ||||||
|       |  | ||||||
|     } finally { |  | ||||||
|       formLoading.value = false |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   if (formData.value.enterpriseId || enterpriseId) { |  | ||||||
|     formData.value.enterpriseId = formData.value.enterpriseId || enterpriseId |  | ||||||
|         const res = await EnterprisesApi.getEnterprises(formData.value.enterpriseId) |  | ||||||
|         enterpriseOptions.value = [res] |  | ||||||
|       } |  | ||||||
| } |  | ||||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 |  | ||||||
| 
 |  | ||||||
| /** 提交表单 */ |  | ||||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 |  | ||||||
| const submitForm = async () => { |  | ||||||
|   // 校验表单 |  | ||||||
|   await formRef.value.validate() |  | ||||||
|   // 提交请求 |  | ||||||
|   formLoading.value = true |  | ||||||
|   try { |  | ||||||
|     formData.value.files = fileIds.value.map(f => ( f.id )) |  | ||||||
|     const data = formData.value as unknown as EnterpriseQualificationVO |  | ||||||
|      |  | ||||||
|     if (formType.value === 'create') { |  | ||||||
|       await EnterpriseQualificationApi.createEnterpriseQualification(data) |  | ||||||
|       message.success(t('common.createSuccess')) |  | ||||||
|     } else { |  | ||||||
|       await EnterpriseQualificationApi.updateEnterpriseQualification(data) |  | ||||||
|       message.success(t('common.updateSuccess')) |  | ||||||
|     } |  | ||||||
|     dialogVisible.value = false |  | ||||||
|     // 发送操作成功的事件 |  | ||||||
|     emit('success') |  | ||||||
|   } finally { |  | ||||||
|     formLoading.value = false |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** 重置表单 */ |  | ||||||
| const resetForm = () => { |  | ||||||
|   formData.value = { |  | ||||||
|     id: undefined, |  | ||||||
|     enterpriseId: undefined, |  | ||||||
|     qualificationName: undefined, |  | ||||||
|     expiryDate: undefined, |  | ||||||
|     qualificationDescription: undefined, |  | ||||||
|     updateBy: undefined, |  | ||||||
|     createBy: undefined, |  | ||||||
|     handleDate: undefined, |  | ||||||
|     enterpriseAuth: undefined, |  | ||||||
|   } |  | ||||||
|   formRef.value?.resetFields() |  | ||||||
| } |  | ||||||
| </script> |  | ||||||
| @ -0,0 +1,141 @@ | |||||||
|  | <template> | ||||||
|  |   <Dialog :title="dialogTitle" v-model="dialogVisible"> | ||||||
|  |     <el-form | ||||||
|  |       ref="formRef" | ||||||
|  |       :model="formData" | ||||||
|  |       :rules="formRules" | ||||||
|  |       v-loading="formLoading" | ||||||
|  |       label-width="auto" | ||||||
|  |     > | ||||||
|  |       <el-form-item label="选择企业" prop="enterpriseId" v-if="formData.id"> | ||||||
|  |         <span></span> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item label="资质名称" prop="qualificationName"> | ||||||
|  |         <el-select v-model="formData.qualificationName" placeholder="请选择资质名称"> | ||||||
|  |           <el-option | ||||||
|  |             v-for="dict in getIntDictOptions(DICT_TYPE.ENTERPRISES_QUA)" | ||||||
|  |             :key="dict.value" | ||||||
|  |             :label="dict.label" | ||||||
|  |             :value="dict.value" | ||||||
|  |           /> | ||||||
|  |         </el-select> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item label="资质编号" prop="enterpriseAuth"> | ||||||
|  |         <el-input v-model="formData.enterpriseAuth" placeholder="请输入资质编号" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item label="到期时间" prop="expiryDate"> | ||||||
|  |         <el-date-picker | ||||||
|  |           v-model="formData.expiryDate" | ||||||
|  |           type="date" | ||||||
|  |           value-format="x" | ||||||
|  |           placeholder="请选择到期时间" | ||||||
|  |           class="!w-100%" | ||||||
|  |         /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item label="资质照片" prop="photo"> | ||||||
|  |         <FileUploader v-model="formData.photo" ref="fileUploaderRef" /> | ||||||
|  |       </el-form-item> | ||||||
|  |     </el-form> | ||||||
|  |     <template #footer> | ||||||
|  |       <el-button @click="dialogVisible = false">取 消</el-button> | ||||||
|  |       <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> | ||||||
|  |     </template> | ||||||
|  |   </Dialog> | ||||||
|  | </template> | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { EnterpriseQualificationApi } from '@/api/qualification' | ||||||
|  | /** 企业资质 表单 */ | ||||||
|  | import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' | ||||||
|  | 
 | ||||||
|  | defineOptions({ name: 'Prove' }) | ||||||
|  | const message = useMessage() // 消息弹窗 | ||||||
|  | const fileUploaderRef = ref() | ||||||
|  | const dialogVisible = ref(false) // 弹窗的是否展示 | ||||||
|  | const dialogTitle = ref('新增资质') // 弹窗的标题 | ||||||
|  | const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||||
|  | const formData = ref({ | ||||||
|  |   id: undefined, | ||||||
|  |   enterpriseId: undefined, | ||||||
|  |   qualificationName: undefined, | ||||||
|  |   expiryDate: undefined, | ||||||
|  |   qualificationDescription: undefined, | ||||||
|  |   updateBy: undefined, | ||||||
|  |   createBy: undefined, | ||||||
|  |   handleDate: undefined, | ||||||
|  |   enterpriseAuth: undefined, | ||||||
|  |   photo: [] as any[] | ||||||
|  | }) | ||||||
|  | const formRules = reactive({ | ||||||
|  |   qualificationName: [{ required: true, message: '请选择资质名称', trigger: 'change' }], | ||||||
|  |   photo: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       validator: (rule, value, callback) => { | ||||||
|  |         if (value.length > 0) { | ||||||
|  |           callback() | ||||||
|  |         } else { | ||||||
|  |           callback(new Error('资质照片不能为空')) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   enterpriseAuth: [{ required: true, message: '请输入资质编号', trigger: 'blur' }], | ||||||
|  |   expiryDate: [{ required: true, message: '请选择到期时间', trigger: 'change' }] | ||||||
|  | }) | ||||||
|  | const formRef = ref() // 表单 Ref | ||||||
|  | 
 | ||||||
|  | /** 打开弹窗 */ | ||||||
|  | const open = async (params) => { | ||||||
|  |   dialogVisible.value = true | ||||||
|  |   resetForm() | ||||||
|  |   if (params) { | ||||||
|  |     dialogTitle.value = '编辑资质' | ||||||
|  |     formData.value.enterpriseId = params.enterpriseId || undefined | ||||||
|  |     formData.value.id = params.id || undefined | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||||
|  | 
 | ||||||
|  | /** 提交表单 */ | ||||||
|  | const emit = defineEmits(['success', 'addProve']) // 定义 success 事件,用于操作成功后的回调 | ||||||
|  | const submitForm = async () => { | ||||||
|  |   // 校验表单 | ||||||
|  |   const validate = await formRef.value.validate() | ||||||
|  |   if (!validate) return | ||||||
|  |   formLoading.value = true | ||||||
|  |   await unref(fileUploaderRef).handlerUpload() | ||||||
|  |   const data: any = formData.value | ||||||
|  |   if (data.id) { | ||||||
|  |     data.files = data.photo.map((i) => i.id) | ||||||
|  |     await EnterpriseQualificationApi.updateEnterpriseQualification(data) | ||||||
|  |     emit('success') | ||||||
|  |   } else if (data.enterpriseId) { | ||||||
|  |     data.files = data.photo.map((i) => i.id) | ||||||
|  |     await EnterpriseQualificationApi.createEnterpriseQualification(data) | ||||||
|  |     emit('success') | ||||||
|  |   } else { | ||||||
|  |     data.files = data.photo | ||||||
|  |     emit('addProve', data) | ||||||
|  |   } | ||||||
|  |   message.success('操作成功') | ||||||
|  |   dialogVisible.value = false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 重置表单 */ | ||||||
|  | const resetForm = () => { | ||||||
|  |   formData.value = { | ||||||
|  |     id: undefined, | ||||||
|  |     enterpriseId: undefined, | ||||||
|  |     qualificationName: undefined, | ||||||
|  |     expiryDate: undefined, | ||||||
|  |     qualificationDescription: undefined, | ||||||
|  |     updateBy: undefined, | ||||||
|  |     createBy: undefined, | ||||||
|  |     handleDate: undefined, | ||||||
|  |     enterpriseAuth: undefined, | ||||||
|  |     photo: [] as any[] | ||||||
|  |   } | ||||||
|  |   formRef.value?.resetFields() | ||||||
|  | } | ||||||
|  | </script> | ||||||
					Loading…
					
					
				
		Reference in new issue