提交 | 用户 | 时间
|
820397
|
1 |
<template> |
H |
2 |
<div class="upload-box"> |
|
3 |
<el-upload |
|
4 |
:id="uuid" |
|
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 |
:multiple="false" |
|
13 |
:on-error="uploadError" |
|
14 |
:on-success="uploadSuccess" |
|
15 |
:show-file-list="false" |
|
16 |
> |
|
17 |
<template v-if="modelValue"> |
|
18 |
<img :src="modelValue" class="upload-image" /> |
|
19 |
<div class="upload-handle" @click.stop> |
|
20 |
<div v-if="!disabled" class="handle-icon" @click="editImg"> |
|
21 |
<Icon icon="ep:edit" /> |
|
22 |
<span v-if="showBtnText">{{ t('action.edit') }}</span> |
|
23 |
</div> |
|
24 |
<div class="handle-icon" @click="imagePreview(modelValue)"> |
|
25 |
<Icon icon="ep:zoom-in" /> |
|
26 |
<span v-if="showBtnText">{{ t('action.detail') }}</span> |
|
27 |
</div> |
|
28 |
<div v-if="showDelete && !disabled" class="handle-icon" @click="deleteImg"> |
|
29 |
<Icon icon="ep:delete" /> |
|
30 |
<span v-if="showBtnText">{{ t('action.del') }}</span> |
|
31 |
</div> |
|
32 |
</div> |
|
33 |
</template> |
|
34 |
<template v-else> |
|
35 |
<div class="upload-empty"> |
|
36 |
<slot name="empty"> |
|
37 |
<Icon icon="ep:plus" /> |
|
38 |
<!-- <span>请上传图片</span> --> |
|
39 |
</slot> |
|
40 |
</div> |
|
41 |
</template> |
|
42 |
</el-upload> |
|
43 |
<div class="el-upload__tip"> |
|
44 |
<slot name="tip"></slot> |
|
45 |
</div> |
|
46 |
</div> |
|
47 |
</template> |
|
48 |
|
|
49 |
<script lang="ts" setup> |
|
50 |
import type { UploadProps } from 'element-plus' |
|
51 |
|
|
52 |
import { generateUUID } from '@/utils' |
|
53 |
import { propTypes } from '@/utils/propTypes' |
|
54 |
import { createImageViewer } from '@/components/ImageViewer' |
|
55 |
import { useUpload } from '@/components/UploadFile/src/useUpload' |
|
56 |
|
|
57 |
defineOptions({ name: 'UploadImg' }) |
|
58 |
|
|
59 |
type FileTypes = |
|
60 |
| 'image/apng' |
|
61 |
| 'image/bmp' |
|
62 |
| 'image/gif' |
|
63 |
| 'image/jpeg' |
|
64 |
| 'image/pjpeg' |
|
65 |
| 'image/png' |
|
66 |
| 'image/svg+xml' |
|
67 |
| 'image/tiff' |
|
68 |
| 'image/webp' |
|
69 |
| 'image/x-icon' |
|
70 |
|
|
71 |
// 接受父组件参数 |
|
72 |
const props = defineProps({ |
|
73 |
modelValue: propTypes.string.def(''), |
|
74 |
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true) |
|
75 |
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false) |
|
76 |
fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M) |
|
77 |
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"]) |
|
78 |
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px) |
|
79 |
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px) |
|
80 |
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px) |
|
81 |
showDelete: propTypes.bool.def(true), // 是否显示删除按钮 |
|
82 |
showBtnText: propTypes.bool.def(true) // 是否显示按钮文字 |
|
83 |
}) |
|
84 |
const { t } = useI18n() // 国际化 |
|
85 |
const message = useMessage() // 消息弹窗 |
|
86 |
// 生成组件唯一id |
|
87 |
const uuid = ref('id-' + generateUUID()) |
|
88 |
// 查看图片 |
|
89 |
const imagePreview = (imgUrl: string) => { |
|
90 |
createImageViewer({ |
|
91 |
zIndex: 9999999, |
|
92 |
urlList: [imgUrl] |
|
93 |
}) |
|
94 |
} |
|
95 |
|
|
96 |
const emit = defineEmits(['update:modelValue']) |
|
97 |
|
|
98 |
const deleteImg = () => { |
|
99 |
emit('update:modelValue', '') |
|
100 |
} |
|
101 |
|
|
102 |
const { uploadUrl, httpRequest } = useUpload() |
|
103 |
|
|
104 |
const editImg = () => { |
|
105 |
const dom = document.querySelector(`#${uuid.value} .el-upload__input`) |
|
106 |
dom && dom.dispatchEvent(new MouseEvent('click')) |
|
107 |
} |
|
108 |
|
|
109 |
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => { |
|
110 |
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize |
|
111 |
const imgType = props.fileType |
|
112 |
if (!imgType.includes(rawFile.type as FileTypes)) |
|
113 |
message.notifyWarning('上传图片不符合所需的格式!') |
|
114 |
if (!imgSize) message.notifyWarning(`上传图片大小不能超过 ${props.fileSize}M!`) |
|
115 |
return imgType.includes(rawFile.type as FileTypes) && imgSize |
|
116 |
} |
|
117 |
|
|
118 |
// 图片上传成功提示 |
|
119 |
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => { |
|
120 |
message.success('上传成功') |
|
121 |
emit('update:modelValue', res.data) |
|
122 |
} |
|
123 |
|
|
124 |
// 图片上传错误提示 |
|
125 |
const uploadError = () => { |
|
126 |
message.notifyError('图片上传失败,请您重新上传!') |
|
127 |
} |
|
128 |
</script> |
|
129 |
<style lang="scss" scoped> |
|
130 |
.is-error { |
|
131 |
.upload { |
|
132 |
:deep(.el-upload), |
|
133 |
:deep(.el-upload-dragger) { |
|
134 |
border: 1px dashed var(--el-color-danger) !important; |
|
135 |
|
|
136 |
&:hover { |
|
137 |
border-color: var(--el-color-primary) !important; |
|
138 |
} |
|
139 |
} |
|
140 |
} |
|
141 |
} |
|
142 |
|
|
143 |
:deep(.disabled) { |
|
144 |
.el-upload, |
|
145 |
.el-upload-dragger { |
|
146 |
cursor: not-allowed !important; |
|
147 |
background: var(--el-disabled-bg-color); |
|
148 |
border: 1px dashed var(--el-border-color-darker) !important; |
|
149 |
|
|
150 |
&:hover { |
|
151 |
border: 1px dashed var(--el-border-color-darker) !important; |
|
152 |
} |
|
153 |
} |
|
154 |
} |
|
155 |
|
|
156 |
.upload-box { |
|
157 |
.no-border { |
|
158 |
:deep(.el-upload) { |
|
159 |
border: none !important; |
|
160 |
} |
|
161 |
} |
|
162 |
|
|
163 |
:deep(.upload) { |
|
164 |
.el-upload { |
|
165 |
position: relative; |
|
166 |
display: flex; |
|
167 |
align-items: center; |
|
168 |
justify-content: center; |
|
169 |
width: v-bind(width); |
|
170 |
height: v-bind(height); |
|
171 |
overflow: hidden; |
|
172 |
border: 1px dashed var(--el-border-color-darker); |
|
173 |
border-radius: v-bind(borderradius); |
|
174 |
transition: var(--el-transition-duration-fast); |
|
175 |
|
|
176 |
&:hover { |
|
177 |
border-color: var(--el-color-primary); |
|
178 |
|
|
179 |
.upload-handle { |
|
180 |
opacity: 1; |
|
181 |
} |
|
182 |
} |
|
183 |
|
|
184 |
.el-upload-dragger { |
|
185 |
display: flex; |
|
186 |
align-items: center; |
|
187 |
justify-content: center; |
|
188 |
width: 100%; |
|
189 |
height: 100%; |
|
190 |
padding: 0; |
|
191 |
overflow: hidden; |
|
192 |
background-color: transparent; |
|
193 |
border: 1px dashed var(--el-border-color-darker); |
|
194 |
border-radius: v-bind(borderradius); |
|
195 |
|
|
196 |
&:hover { |
|
197 |
border: 1px dashed var(--el-color-primary); |
|
198 |
} |
|
199 |
} |
|
200 |
|
|
201 |
.el-upload-dragger.is-dragover { |
|
202 |
background-color: var(--el-color-primary-light-9); |
|
203 |
border: 2px dashed var(--el-color-primary) !important; |
|
204 |
} |
|
205 |
|
|
206 |
.upload-image { |
|
207 |
width: 100%; |
|
208 |
height: 100%; |
|
209 |
object-fit: contain; |
|
210 |
} |
|
211 |
|
|
212 |
.upload-empty { |
|
213 |
position: relative; |
|
214 |
display: flex; |
|
215 |
flex-direction: column; |
|
216 |
align-items: center; |
|
217 |
justify-content: center; |
|
218 |
font-size: 12px; |
|
219 |
line-height: 30px; |
|
220 |
color: var(--el-color-info); |
|
221 |
|
|
222 |
.el-icon { |
|
223 |
font-size: 28px; |
|
224 |
color: var(--el-text-color-secondary); |
|
225 |
} |
|
226 |
} |
|
227 |
|
|
228 |
.upload-handle { |
|
229 |
position: absolute; |
|
230 |
top: 0; |
|
231 |
right: 0; |
|
232 |
display: flex; |
|
233 |
width: 100%; |
|
234 |
height: 100%; |
|
235 |
cursor: pointer; |
|
236 |
background: rgb(0 0 0 / 60%); |
|
237 |
opacity: 0; |
|
238 |
box-sizing: border-box; |
|
239 |
transition: var(--el-transition-duration-fast); |
|
240 |
align-items: center; |
|
241 |
justify-content: center; |
|
242 |
|
|
243 |
.handle-icon { |
|
244 |
display: flex; |
|
245 |
flex-direction: column; |
|
246 |
align-items: center; |
|
247 |
justify-content: center; |
|
248 |
padding: 0 6%; |
|
249 |
color: aliceblue; |
|
250 |
|
|
251 |
.el-icon { |
|
252 |
margin-bottom: 40%; |
|
253 |
font-size: 130%; |
|
254 |
line-height: 130%; |
|
255 |
} |
|
256 |
|
|
257 |
span { |
|
258 |
font-size: 85%; |
|
259 |
line-height: 85%; |
|
260 |
} |
|
261 |
} |
|
262 |
} |
|
263 |
} |
|
264 |
} |
|
265 |
|
|
266 |
.el-upload__tip { |
|
267 |
line-height: 18px; |
|
268 |
text-align: center; |
|
269 |
} |
|
270 |
} |
|
271 |
</style> |