提交 | 用户 | 时间
|
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> |