沙钢智慧能源系统前端代码
houzhongjian
2024-10-09 314507f8ddadd9c66e98d260c3b2a5dad1a04015
提交 | 用户 | 时间
314507 1 <template>
H 2   <div class="upload-box">
3     <el-upload
4       v-model:file-list="fileList"
5       :accept="fileType.join(',')"
6       :action="uploadUrl"
7       :before-upload="beforeUpload"
8       :class="['upload', drag ? 'no-border' : '']"
9       :disabled="disabled"
10       :drag="drag"
11       :http-request="httpRequest"
12       :limit="limit"
13       :multiple="true"
14       :on-error="uploadError"
15       :on-exceed="handleExceed"
16       :on-success="uploadSuccess"
17       list-type="picture-card"
18     >
19       <div class="upload-empty">
20         <slot name="empty">
21           <Icon icon="ep:plus" />
22           <!-- <span>请上传图片</span> -->
23         </slot>
24       </div>
25       <template #file="{ file }">
26         <img :src="file.url" class="upload-image" />
27         <div class="upload-handle" @click.stop>
28           <div class="handle-icon" @click="handlePictureCardPreview(file)">
29             <Icon icon="ep:zoom-in" />
30             <span>查看</span>
31           </div>
32           <div v-if="!disabled" class="handle-icon" @click="handleRemove(file)">
33             <Icon icon="ep:delete" />
34             <span>删除</span>
35           </div>
36         </div>
37       </template>
38     </el-upload>
39     <div class="el-upload__tip">
40       <slot name="tip"></slot>
41     </div>
42     <el-image-viewer
43       v-if="imgViewVisible"
44       :url-list="[viewImageUrl]"
45       @close="imgViewVisible = false"
46     />
47   </div>
48 </template>
49 <script lang="ts" setup>
50 import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
51 import { ElNotification } from 'element-plus'
52
53 import { propTypes } from '@/utils/propTypes'
54 import { useUpload } from '@/components/UploadFile/src/useUpload'
55
56 defineOptions({ name: 'UploadImgs' })
57
58 const message = useMessage() // 消息弹窗
59
60 type FileTypes =
61   | 'image/apng'
62   | 'image/bmp'
63   | 'image/gif'
64   | 'image/jpeg'
65   | 'image/pjpeg'
66   | 'image/png'
67   | 'image/svg+xml'
68   | 'image/tiff'
69   | 'image/webp'
70   | 'image/x-icon'
71
72 const props = defineProps({
73   modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
74   drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
75   disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
76   limit: propTypes.number.def(5), // 最大图片上传数 ==> 非必传(默认为 5张)
77   fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
78   fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
79   height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
80   width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
81   borderradius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px)
82 })
83
84 const { uploadUrl, httpRequest } = useUpload()
85
86 const fileList = ref<UploadUserFile[]>([])
87 const uploadNumber = ref<number>(0)
88 const uploadList = ref<UploadUserFile[]>([])
89 /**
90  * @description 文件上传之前判断
91  * @param rawFile 上传的文件
92  * */
93 const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
94   const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
95   const imgType = props.fileType
96   if (!imgType.includes(rawFile.type as FileTypes))
97     ElNotification({
98       title: '温馨提示',
99       message: '上传图片不符合所需的格式!',
100       type: 'warning'
101     })
102   if (!imgSize)
103     ElNotification({
104       title: '温馨提示',
105       message: `上传图片大小不能超过 ${props.fileSize}M!`,
106       type: 'warning'
107     })
108   uploadNumber.value++
109   return imgType.includes(rawFile.type as FileTypes) && imgSize
110 }
111
112 // 图片上传成功
113 interface UploadEmits {
114   (e: 'update:modelValue', value: string[]): void
115 }
116
117 const emit = defineEmits<UploadEmits>()
118 const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
119   message.success('上传成功')
120   // 删除自身
121   const index = fileList.value.findIndex((item) => item.response?.data === res.data)
122   fileList.value.splice(index, 1)
123   uploadList.value.push({ name: res.data, url: res.data })
124   if (uploadList.value.length == uploadNumber.value) {
125     fileList.value.push(...uploadList.value)
126     uploadList.value = []
127     uploadNumber.value = 0
128     emitUpdateModelValue()
129   }
130 }
131
132 // 监听模型绑定值变动
133 watch(
134   () => props.modelValue,
135   (val: string | string[]) => {
136     if (!val) {
137       fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
138       return
139     }
140
141     fileList.value = [] // 保障数据为空
142     fileList.value.push(
143       ...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
144     )
145   },
146   { immediate: true, deep: true }
147 )
148 // 发送图片链接列表更新
149 const emitUpdateModelValue = () => {
150   let result: string[] = fileList.value.map((file) => file.url!)
151   emit('update:modelValue', result)
152 }
153 // 删除图片
154 const handleRemove = (uploadFile: UploadFile) => {
155   fileList.value = fileList.value.filter(
156     (item) => item.url !== uploadFile.url || item.name !== uploadFile.name
157   )
158   emit(
159     'update:modelValue',
160     fileList.value.map((file) => file.url!)
161   )
162 }
163
164 // 图片上传错误提示
165 const uploadError = () => {
166   ElNotification({
167     title: '温馨提示',
168     message: '图片上传失败,请您重新上传!',
169     type: 'error'
170   })
171 }
172
173 // 文件数超出提示
174 const handleExceed = () => {
175   ElNotification({
176     title: '温馨提示',
177     message: `当前最多只能上传 ${props.limit} 张图片,请移除后上传!`,
178     type: 'warning'
179   })
180 }
181
182 // 图片预览
183 const viewImageUrl = ref('')
184 const imgViewVisible = ref(false)
185 const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
186   viewImageUrl.value = uploadFile.url!
187   imgViewVisible.value = true
188 }
189 </script>
190
191 <style lang="scss" scoped>
192 .is-error {
193   .upload {
194     :deep(.el-upload--picture-card),
195     :deep(.el-upload-dragger) {
196       border: 1px dashed var(--el-color-danger) !important;
197
198       &:hover {
199         border-color: var(--el-color-primary) !important;
200       }
201     }
202   }
203 }
204
205 :deep(.disabled) {
206   .el-upload--picture-card,
207   .el-upload-dragger {
208     cursor: not-allowed;
209     background: var(--el-disabled-bg-color) !important;
210     border: 1px dashed var(--el-border-color-darker);
211
212     &:hover {
213       border-color: var(--el-border-color-darker) !important;
214     }
215   }
216 }
217
218 .upload-box {
219   .no-border {
220     :deep(.el-upload--picture-card) {
221       border: none !important;
222     }
223   }
224
225   :deep(.upload) {
226     .el-upload-dragger {
227       display: flex;
228       align-items: center;
229       justify-content: center;
230       width: 100%;
231       height: 100%;
232       padding: 0;
233       overflow: hidden;
234       border: 1px dashed var(--el-border-color-darker);
235       border-radius: v-bind(borderradius);
236
237       &:hover {
238         border: 1px dashed var(--el-color-primary);
239       }
240     }
241
242     .el-upload-dragger.is-dragover {
243       background-color: var(--el-color-primary-light-9);
244       border: 2px dashed var(--el-color-primary) !important;
245     }
246
247     .el-upload-list__item,
248     .el-upload--picture-card {
249       width: v-bind(width);
250       height: v-bind(height);
251       background-color: transparent;
252       border-radius: v-bind(borderradius);
253     }
254
255     .upload-image {
256       width: 100%;
257       height: 100%;
258       object-fit: contain;
259     }
260
261     .upload-handle {
262       position: absolute;
263       top: 0;
264       right: 0;
265       display: flex;
266       width: 100%;
267       height: 100%;
268       cursor: pointer;
269       background: rgb(0 0 0 / 60%);
270       opacity: 0;
271       box-sizing: border-box;
272       transition: var(--el-transition-duration-fast);
273       align-items: center;
274       justify-content: center;
275
276       .handle-icon {
277         display: flex;
278         flex-direction: column;
279         align-items: center;
280         justify-content: center;
281         padding: 0 6%;
282         color: aliceblue;
283
284         .el-icon {
285           margin-bottom: 15%;
286           font-size: 140%;
287         }
288
289         span {
290           font-size: 100%;
291         }
292       }
293     }
294
295     .el-upload-list__item {
296       &:hover {
297         .upload-handle {
298           opacity: 1;
299         }
300       }
301     }
302
303     .upload-empty {
304       display: flex;
305       flex-direction: column;
306       align-items: center;
307       font-size: 12px;
308       line-height: 30px;
309       color: var(--el-color-info);
310
311       .el-icon {
312         font-size: 28px;
313         color: var(--el-text-color-secondary);
314       }
315     }
316   }
317
318   .el-upload__tip {
319     line-height: 15px;
320     text-align: center;
321   }
322 }
323 </style>