af-mobile-client-vue3 1.6.43 → 1.6.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/common/otherCharge/ChargePrintSelectorAndRemarks.vue +4 -1
- package/src/components/common/otherCharge/GridFileUploader.vue +4 -2
- package/src/components/core/ImageUploader/index.vue +94 -311
- package/src/components/core/Signature/AdaptiveSignatureField.vue +172 -172
- package/src/components/core/Uploader/index.vue +6 -2
- package/src/components/core/XDatePicker/index.vue +8 -2
- package/src/components/core/XMultiSelect/index.vue +6 -2
- package/src/components/core/XSelect/index.vue +40 -15
- package/src/components/data/AttachmentListItem/index.vue +174 -174
- package/src/components/data/XCellList/index.vue +16 -9
- package/src/components/data/XCellListFilter/index.vue +26 -3
- package/src/components/data/XForm/index.vue +10 -5
- package/src/components/data/XFormItem/index.vue +57 -73
- package/src/components/data/XReportGrid/XReportDemo.vue +33 -33
- package/src/components/data/XReportGrid/print.js +184 -184
- package/src/stores/modules/safecheckStore.ts +47 -47
- package/src/utils/PhotoClean.ts +21 -21
- package/src/utils/appBackManager.ts +23 -23
- package/src/utils/safeBack.ts +38 -38
- package/src/utils/timeUtil.ts +27 -27
- package/src/views/SafeInspection/SecurityCertificate/AddDevice/index.vue +9 -20
- package/src/views/SafeInspection/SecurityCertificate/OverallHiddenDangers/index.vue +21 -8
- package/src/views/SafeInspection/SecurityCertificate/contractSign/index.vue +0 -1
- package/src/views/SafeInspection/SecurityCertificate/index.vue +1 -2
- package/src/views/SafeInspection/SecurityCertificate/photoAiRecognition/components/PhotoCaptureCard.vue +21 -5
- package/src/views/SafeInspection/SecurityCertificate/photoSignature/SignatureComponent/SignatureComponent.vue +13 -3
- package/src/views/SafeInspection/SecurityCertificate/photoSignature/index.vue +9 -5
- package/src/views/SafeInspection/SecurityCertificate/userInfo/index.vue +11 -15
- package/src/views/SafeInspection/SecurityCertificate/userInfo/upaddress.vue +5 -4
- package/src/views/SafeInspection/SecurityFormItem/CameraView/index.vue +3 -9
- package/src/views/SafeInspection/SecurityFormItem/FormItem.vue +23 -15
- package/src/views/SafeInspection/SecurityFormItem/XMultiSelect/index.vue +8 -10
- package/src/views/SafeInspection/SecurityFormItem/index.vue +16 -10
- package/src/views/component/XFormView/index.vue +2 -2
- package/vite.config.ts +1 -1
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
Picker as VanPicker,
|
|
7
7
|
Popup as VanPopup,
|
|
8
8
|
} from 'vant'
|
|
9
|
-
import { ref, watch } from 'vue'
|
|
9
|
+
import { onBeforeUnmount, ref, watch } from 'vue'
|
|
10
10
|
|
|
11
11
|
const props = defineProps<{
|
|
12
12
|
modelValue: string
|
|
@@ -51,6 +51,9 @@ watch(() => props.modelValue, (newValue) => {
|
|
|
51
51
|
watch(() => props.remarks, (newValue) => {
|
|
52
52
|
remarks.value = newValue
|
|
53
53
|
})
|
|
54
|
+
onBeforeUnmount(() => {
|
|
55
|
+
showPicker.value = false
|
|
56
|
+
})
|
|
54
57
|
</script>
|
|
55
58
|
|
|
56
59
|
<template>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Icon as VanIcon,
|
|
11
11
|
ImagePreview as VanImagePreview,
|
|
12
12
|
} from 'vant'
|
|
13
|
-
import {
|
|
13
|
+
import {computed, onBeforeUnmount, onMounted, onUnmounted, reactive, ref} from 'vue'
|
|
14
14
|
|
|
15
15
|
export interface FileTypeConfig {
|
|
16
16
|
userType: string
|
|
@@ -371,7 +371,9 @@ function validateAll(): boolean {
|
|
|
371
371
|
const minLength = computed(() => {
|
|
372
372
|
return props.fileTypes.reduce((acc, config) => acc + config.picMinNum, 0)
|
|
373
373
|
})
|
|
374
|
-
|
|
374
|
+
onBeforeUnmount(() => {
|
|
375
|
+
showPreview.value = false
|
|
376
|
+
})
|
|
375
377
|
// 暴露验证方法给父组件
|
|
376
378
|
defineExpose({
|
|
377
379
|
validateAll,
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { NativePickedFile } from '@af-mobile-client-vue3/utils/nativeFilePicker'
|
|
3
2
|
// import cameraIcon from '@af-mobile-client-vue3/assets/img/component/camera.png'
|
|
4
3
|
import { deleteFile, upload } from '@af-mobile-client-vue3/services/api/common'
|
|
5
4
|
import { detectEnvironment } from '@af-mobile-client-vue3/utils/environment'
|
|
6
|
-
import { buildInputAccept, getFileTypeMeta, getUploadFormType, isImageFileName } from '@af-mobile-client-vue3/utils/fileType'
|
|
7
5
|
import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
|
|
8
|
-
import {
|
|
9
|
-
hasNativeFilePicker,
|
|
10
|
-
openNativeFilePicker,
|
|
11
|
-
triggerBrowserFileInput,
|
|
12
|
-
} from '@af-mobile-client-vue3/utils/nativeFilePicker'
|
|
13
|
-
import { toDataUrl } from '@af-mobile-client-vue3/utils/resourceUrl'
|
|
14
6
|
import { formatNow } from '@af-mobile-client-vue3/utils/timeUtil'
|
|
15
7
|
import {
|
|
16
8
|
ActionSheet,
|
|
@@ -19,7 +11,7 @@ import {
|
|
|
19
11
|
Icon as VanIcon,
|
|
20
12
|
Uploader as vanUploader,
|
|
21
13
|
} from 'vant'
|
|
22
|
-
import {
|
|
14
|
+
import { inject, onBeforeUnmount, ref } from 'vue'
|
|
23
15
|
|
|
24
16
|
interface WatermarkConfig {
|
|
25
17
|
fontSize?: number | string
|
|
@@ -36,7 +28,7 @@ const props = defineProps({
|
|
|
36
28
|
outerIndex: { default: undefined },
|
|
37
29
|
authority: { default: 'user' },
|
|
38
30
|
uploadMode: { default: 'server' },
|
|
39
|
-
attr: { type: Object as () => {
|
|
31
|
+
attr: { type: Object as () => { addOrEdit?: string, acceptCount?: number, minAcceptCount?: number, uploadImage?: boolean, watermark?: WatermarkConfig, useType?: string }, default: () => ({}) },
|
|
40
32
|
mode: { default: '新增' }, // 预览
|
|
41
33
|
serviceName: { type: String, default: '' },
|
|
42
34
|
// 整体只读:只允许预览,禁止拍照/上传/删除
|
|
@@ -47,19 +39,30 @@ const emit = defineEmits(['updateFileList', 'updateAllFileList'])
|
|
|
47
39
|
|
|
48
40
|
const imageList = ref<Array<any>>(props.imageList ?? [])
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
const
|
|
42
|
+
// 从父组件获取 commonId,仅读取一次而非持有引用
|
|
43
|
+
const parentCommonId = (() => {
|
|
44
|
+
try {
|
|
45
|
+
const parent = inject<any>('provideParent', undefined)
|
|
46
|
+
return parent?.commonId?.value ?? ''
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return ''
|
|
50
|
+
}
|
|
51
|
+
})()
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
// 浏览器 / 微信内 H5:统一用隐藏 file input(与单独 camera input 行为等价,均为系统选图)
|
|
54
|
+
// const fileInputRef = ref<HTMLInputElement | undefined>()
|
|
55
|
+
const parentData: any = inject('provideParent')
|
|
56
|
+
// 浏览器 / 微信内 H5:统一用隐藏 file input(与单独 camera input 行为等价,均为系统选图)
|
|
57
|
+
const fileInputRef = ref<HTMLInputElement | undefined>()
|
|
56
58
|
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
+
function openBrowserFilePicker() {
|
|
60
|
+
if (fileInputRef.value) {
|
|
61
|
+
fileInputRef.value.value = ''
|
|
62
|
+
fileInputRef.value.click()
|
|
63
|
+
}
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
const useNativeDocumentPicker = computed(() => hasNativeFilePicker())
|
|
62
|
-
|
|
63
66
|
function emitUpdatesAfterChange() {
|
|
64
67
|
// 新增:对外抛出完整与ID列表
|
|
65
68
|
(emit as any)('updateAllFileList', imageList.value.filter(item => item.status === 'done').map(item => item))
|
|
@@ -74,31 +77,13 @@ function emitUpdatesAfterChange() {
|
|
|
74
77
|
}
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
function isFileItemImage(item: any): boolean {
|
|
80
|
-
if (item?.isDocument)
|
|
81
|
-
return false
|
|
82
|
-
if (item?.isImage === false)
|
|
83
|
-
return false
|
|
84
|
-
if (item?.isImage === true && !item?.isDocument)
|
|
85
|
-
return true
|
|
86
|
-
return isImageFileName(item?.name || item?.photo_name || item?.f_downloadpath || '')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function getItemFileMeta(item: any) {
|
|
90
|
-
return getFileTypeMeta(item?.name || item?.photo_name || item?.f_downloadpath || '')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function createTempUploadingItem(name: string, previewUrl: string, isImage = true) {
|
|
80
|
+
function createTempUploadingItem(name: string, previewUrl: string) {
|
|
94
81
|
const temp = {
|
|
95
82
|
uid: Date.now() + Math.random().toString(36).substr(2, 5),
|
|
96
83
|
name,
|
|
97
84
|
status: 'uploading',
|
|
98
85
|
message: '上传中...',
|
|
99
|
-
url:
|
|
100
|
-
isImage: true,
|
|
101
|
-
isDocument: !isImage,
|
|
86
|
+
url: previewUrl,
|
|
102
87
|
}
|
|
103
88
|
if (!imageList.value)
|
|
104
89
|
imageList.value = [temp]
|
|
@@ -106,12 +91,12 @@ function createTempUploadingItem(name: string, previewUrl: string, isImage = tru
|
|
|
106
91
|
return temp
|
|
107
92
|
}
|
|
108
93
|
|
|
109
|
-
function uploadFileViaHttp(file: File, previewUrl: string, temp: any
|
|
94
|
+
function uploadFileViaHttp(file: File, previewUrl: string, temp: any) {
|
|
110
95
|
const formData = new FormData()
|
|
111
96
|
formData.append('avatar', file)
|
|
112
97
|
formData.append('resUploadMode', props.uploadMode)
|
|
113
98
|
formData.append('pathKey', 'Default')
|
|
114
|
-
formData.append('formType',
|
|
99
|
+
formData.append('formType', 'image')
|
|
115
100
|
formData.append('useType', 'Default')
|
|
116
101
|
formData.append('resUploadStock', '1')
|
|
117
102
|
formData.append('filename', file.name)
|
|
@@ -130,15 +115,9 @@ function uploadFileViaHttp(file: File, previewUrl: string, temp: any, formType:
|
|
|
130
115
|
if (index !== -1) {
|
|
131
116
|
imageList.value[index].uid = res.data.id
|
|
132
117
|
imageList.value[index].id = res.data.id
|
|
133
|
-
imageList.value[index].f_downloadpath = res.data.f_downloadpath
|
|
134
|
-
imageList.value[index].photo_name = res.data.f_filename
|
|
135
|
-
imageList.value[index].isDocument = formType === 'file'
|
|
136
|
-
imageList.value[index].isImage = true
|
|
137
|
-
if (formType === 'file')
|
|
138
|
-
imageList.value[index].url = DOC_TILE_PLACEHOLDER
|
|
139
118
|
delete imageList.value[index].message
|
|
140
119
|
imageList.value[index].status = 'done'
|
|
141
|
-
|
|
120
|
+
imageList.value[index].url = `${import.meta.env.VITE_APP_RESOURCE_PATH || ''}${res.data.f_downloadpath}`
|
|
142
121
|
}
|
|
143
122
|
}
|
|
144
123
|
else {
|
|
@@ -165,7 +144,7 @@ function buildWatermarkText() {
|
|
|
165
144
|
return lines.join('\n')
|
|
166
145
|
}
|
|
167
146
|
|
|
168
|
-
function
|
|
147
|
+
function handleBrowserFiles(files: FileList | null) {
|
|
169
148
|
if (!files || files.length === 0)
|
|
170
149
|
return
|
|
171
150
|
const max = props.attr?.acceptCount ?? Number.POSITIVE_INFINITY
|
|
@@ -173,120 +152,23 @@ function handleBrowserImageFiles(files: FileList | null) {
|
|
|
173
152
|
if (imageList.value.length >= max)
|
|
174
153
|
break
|
|
175
154
|
const file = files[i]
|
|
176
|
-
const formType = getUploadFormType(file)
|
|
177
|
-
if (formType === 'file') {
|
|
178
|
-
const temp = createTempUploadingItem(file.name, '', false)
|
|
179
|
-
uploadFileViaHttp(file, '', temp, 'file')
|
|
180
|
-
continue
|
|
181
|
-
}
|
|
182
155
|
const reader = new FileReader()
|
|
183
156
|
reader.onload = (e: any) => {
|
|
184
157
|
const previewUrl = e.target?.result as string
|
|
185
|
-
const temp = createTempUploadingItem(file.name, previewUrl
|
|
186
|
-
uploadFileViaHttp(file, previewUrl, temp
|
|
158
|
+
const temp = createTempUploadingItem(file.name, previewUrl)
|
|
159
|
+
uploadFileViaHttp(file, previewUrl, temp)
|
|
187
160
|
}
|
|
188
161
|
reader.readAsDataURL(file)
|
|
189
162
|
}
|
|
190
163
|
}
|
|
191
164
|
|
|
192
|
-
function handleBrowserDocumentFiles(files: FileList | null) {
|
|
193
|
-
if (!files || files.length === 0)
|
|
194
|
-
return
|
|
195
|
-
const max = props.attr?.acceptCount ?? Number.POSITIVE_INFINITY
|
|
196
|
-
for (let i = 0; i < files.length; i++) {
|
|
197
|
-
if (imageList.value.length >= max)
|
|
198
|
-
break
|
|
199
|
-
const file = files[i]
|
|
200
|
-
const formType = getUploadFormType(file)
|
|
201
|
-
if (formType === 'image') {
|
|
202
|
-
const reader = new FileReader()
|
|
203
|
-
reader.onload = (e: any) => {
|
|
204
|
-
const previewUrl = e.target?.result as string
|
|
205
|
-
const temp = createTempUploadingItem(file.name, previewUrl, true)
|
|
206
|
-
uploadFileViaHttp(file, previewUrl, temp, 'image')
|
|
207
|
-
}
|
|
208
|
-
reader.readAsDataURL(file)
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
const temp = createTempUploadingItem(file.name, '', false)
|
|
212
|
-
uploadFileViaHttp(file, '', temp, 'file')
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function handleNativeDocumentUpload(fileData: NativePickedFile) {
|
|
218
|
-
const filePath = fileData.filePath || fileData.path || ''
|
|
219
|
-
const fileName = fileData.name || filePath.split('/').pop() || 'file'
|
|
220
|
-
const tempFile = createTempUploadingItem(fileName, '', false)
|
|
221
|
-
|
|
222
|
-
const param = {
|
|
223
|
-
resUploadMode: props.uploadMode,
|
|
224
|
-
pathKey: 'Default',
|
|
225
|
-
formType: 'file' as const,
|
|
226
|
-
useType: props.attr?.useType || 'Default',
|
|
227
|
-
resUploadStock: '1',
|
|
228
|
-
filename: fileName,
|
|
229
|
-
filesize: fileData.size,
|
|
230
|
-
f_operator: 'server',
|
|
231
|
-
imgPath: filePath,
|
|
232
|
-
urlPath: `/api/${props.serviceName || import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
|
|
233
|
-
commonId: parentData?.commonId?.value ?? '',
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
mobileUtil.execute({
|
|
237
|
-
funcName: 'uploadResource',
|
|
238
|
-
param,
|
|
239
|
-
callbackFunc: (result: any) => {
|
|
240
|
-
const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
|
|
241
|
-
if (result.status === 'success') {
|
|
242
|
-
if (index !== -1) {
|
|
243
|
-
imageList.value[index].uid = result.data.id
|
|
244
|
-
imageList.value[index].id = result.data.id
|
|
245
|
-
imageList.value[index].f_downloadpath = result.data.f_downloadpath
|
|
246
|
-
imageList.value[index].photo_name = result.data.f_filename
|
|
247
|
-
imageList.value[index].isDocument = true
|
|
248
|
-
imageList.value[index].isImage = true
|
|
249
|
-
imageList.value[index].url = DOC_TILE_PLACEHOLDER
|
|
250
|
-
delete imageList.value[index].message
|
|
251
|
-
imageList.value[index].status = 'done'
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
else if (index !== -1) {
|
|
255
|
-
imageList.value[index].status = 'failed'
|
|
256
|
-
imageList.value[index].message = '上传失败'
|
|
257
|
-
}
|
|
258
|
-
emitUpdatesAfterChange()
|
|
259
|
-
},
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function onDocumentInputChange(e: Event) {
|
|
264
|
-
const input = e.target as HTMLInputElement
|
|
265
|
-
handleBrowserDocumentFiles(input.files)
|
|
266
|
-
showActionSheet.value = false
|
|
267
|
-
input.value = ''
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function onPickDocumentNative() {
|
|
271
|
-
showActionSheet.value = false
|
|
272
|
-
openNativeFilePicker({
|
|
273
|
-
accept: documentAccept.value,
|
|
274
|
-
multiple: true,
|
|
275
|
-
onSuccess: files => files.forEach(handleNativeDocumentUpload),
|
|
276
|
-
onError: (msg) => {
|
|
277
|
-
if (msg)
|
|
278
|
-
showFailToast(msg)
|
|
279
|
-
},
|
|
280
|
-
})
|
|
281
|
-
}
|
|
282
|
-
|
|
283
165
|
// 触发拍照
|
|
284
166
|
function triggerCamera() {
|
|
285
167
|
mobileUtil.execute({
|
|
286
168
|
funcName: 'takePicture',
|
|
287
169
|
param: (() => {
|
|
288
170
|
const param: any = {}
|
|
289
|
-
const watermark = (props.attr as any)?.watermark
|
|
171
|
+
const watermark = { ...(props.attr as any)?.watermark }
|
|
290
172
|
if (watermark) {
|
|
291
173
|
const txt = buildWatermarkText()
|
|
292
174
|
if (txt)
|
|
@@ -305,9 +187,6 @@ function triggerCamera() {
|
|
|
305
187
|
rePhotoCheck(result.data)
|
|
306
188
|
}
|
|
307
189
|
else if (result.status === 'error') {
|
|
308
|
-
if (result.message && result.message.startsWith('图片质量检测失败')) {
|
|
309
|
-
parentData?.onPhotoQualityFailed?.()
|
|
310
|
-
}
|
|
311
190
|
const env = detectEnvironment()
|
|
312
191
|
// App 环境下弹窗提示错误
|
|
313
192
|
if (env.isApp) {
|
|
@@ -317,8 +196,8 @@ function triggerCamera() {
|
|
|
317
196
|
})
|
|
318
197
|
return
|
|
319
198
|
}
|
|
320
|
-
//
|
|
321
|
-
|
|
199
|
+
// 浏览器模式:打开文件选择
|
|
200
|
+
openBrowserFilePicker()
|
|
322
201
|
}
|
|
323
202
|
},
|
|
324
203
|
})
|
|
@@ -351,7 +230,6 @@ function rePhotoCheck(photoData: any) {
|
|
|
351
230
|
if (result && result.status === 'success') {
|
|
352
231
|
const checkRePhotoResult = result.data
|
|
353
232
|
if (checkRePhotoResult.is_rephoto === true || checkRePhotoResult.is_rephoto === 'true') {
|
|
354
|
-
parentData?.onPhotoRephotodetected?.()
|
|
355
233
|
console.warn(`检测到当前图片属于翻拍照片\n请上传合法图片!`)
|
|
356
234
|
showFailToast({
|
|
357
235
|
message: `当前图片属于翻拍照片\n请上传合法图片!`,
|
|
@@ -396,14 +274,13 @@ function handlePhotoUpload(photoData: any) {
|
|
|
396
274
|
console.log('图片上传前的参数:', JSON.stringify(photoData))
|
|
397
275
|
// 添加临时预览
|
|
398
276
|
const mimeType = getImageMimeType(photoData.filePath)
|
|
399
|
-
const previewUrl = toDataUrl(photoData.content, mimeType)
|
|
400
277
|
const tempFile = {
|
|
401
278
|
uid: Date.now() + Math.random().toString(36).substr(2, 5),
|
|
402
279
|
name: photoData.filePath.split('/').pop(),
|
|
403
280
|
status: 'uploading',
|
|
404
281
|
message: '上传中...',
|
|
405
|
-
url:
|
|
406
|
-
content:
|
|
282
|
+
url: `data:${mimeType};base64,${photoData.content}`,
|
|
283
|
+
content: `data:image/png;base64,${photoData.content}`,
|
|
407
284
|
isImage: true,
|
|
408
285
|
type: mimeType,
|
|
409
286
|
}
|
|
@@ -418,7 +295,7 @@ function handlePhotoUpload(photoData: any) {
|
|
|
418
295
|
const param = {
|
|
419
296
|
resUploadMode: props.uploadMode,
|
|
420
297
|
pathKey: 'Default',
|
|
421
|
-
formType: 'image'
|
|
298
|
+
formType: 'image',
|
|
422
299
|
useType: props.attr?.useType || 'Default',
|
|
423
300
|
resUploadStock: '1',
|
|
424
301
|
filename: photoData.name,
|
|
@@ -426,7 +303,7 @@ function handlePhotoUpload(photoData: any) {
|
|
|
426
303
|
f_operator: 'server',
|
|
427
304
|
imgPath: photoData.filePath,
|
|
428
305
|
urlPath: `/api/${props.serviceName || import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
|
|
429
|
-
commonId:
|
|
306
|
+
commonId: parentCommonId,
|
|
430
307
|
}
|
|
431
308
|
if (props.isAsyncUpload) {
|
|
432
309
|
// 添加上传队列
|
|
@@ -477,12 +354,9 @@ function handlePhotoUpload(photoData: any) {
|
|
|
477
354
|
if (index !== -1) {
|
|
478
355
|
imageList.value[index].uid = result.data.id
|
|
479
356
|
imageList.value[index].id = result.data.id
|
|
480
|
-
imageList.value[index].f_downloadpath = result.data.f_downloadpath
|
|
481
|
-
imageList.value[index].photo_name = result.data.f_filename
|
|
482
|
-
imageList.value[index].isImage = true
|
|
483
357
|
delete imageList.value[index].message
|
|
484
358
|
imageList.value[index].status = 'done'
|
|
485
|
-
|
|
359
|
+
imageList.value[index].url = `${import.meta.env.VITE_APP_RESOURCE_PATH || ''}${result.data.f_downloadpath}`
|
|
486
360
|
}
|
|
487
361
|
}
|
|
488
362
|
else {
|
|
@@ -565,35 +439,54 @@ function deleteFileFunction(file: any) {
|
|
|
565
439
|
return false // 阻止van-uploader二次处理
|
|
566
440
|
}
|
|
567
441
|
|
|
442
|
+
const showActionSheet = ref(false)
|
|
568
443
|
const uploaderRef = ref()
|
|
569
444
|
|
|
445
|
+
const actionOptions = [
|
|
446
|
+
{ name: '拍照', key: 'camera' },
|
|
447
|
+
{ name: '上传', key: 'file' },
|
|
448
|
+
]
|
|
449
|
+
|
|
570
450
|
function handleUploadAreaClick() {
|
|
571
451
|
if (props.readonly)
|
|
572
452
|
return
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
453
|
+
const env = detectEnvironment()
|
|
454
|
+
// 微信内:直接走浏览器式文件选择,不弹出拍照/上传菜单
|
|
455
|
+
if (env.isWechat || env.isMiniprogram) {
|
|
456
|
+
openBrowserFilePicker()
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
if (props.attr?.uploadImage) {
|
|
460
|
+
showActionSheet.value = true
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
triggerCamera()
|
|
464
|
+
}
|
|
579
465
|
}
|
|
580
466
|
|
|
581
|
-
function
|
|
467
|
+
function handleActionSelect(option: any) {
|
|
582
468
|
showActionSheet.value = false
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
469
|
+
if (option.key === 'camera') {
|
|
470
|
+
triggerCamera()
|
|
471
|
+
}
|
|
472
|
+
else if (option.key === 'file') {
|
|
473
|
+
// 手机文件上传
|
|
474
|
+
mobileUtil.execute({
|
|
475
|
+
funcName: 'photoAlbum',
|
|
476
|
+
param: {},
|
|
477
|
+
callbackFunc: (result: any) => {
|
|
478
|
+
if (result.status === 'success') {
|
|
479
|
+
result.data?.photos.forEach((photo: any) => {
|
|
480
|
+
rePhotoCheck(photo)
|
|
481
|
+
})
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
// 手机文件选择失败,则走浏览器式文件选择
|
|
485
|
+
openBrowserFilePicker()
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
})
|
|
489
|
+
}
|
|
597
490
|
}
|
|
598
491
|
|
|
599
492
|
function getUploadStatus() {
|
|
@@ -614,18 +507,27 @@ function getUploadStatus() {
|
|
|
614
507
|
defineExpose({
|
|
615
508
|
getUploadStatus,
|
|
616
509
|
})
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* 组件卸载时清理:关闭 ActionSheet,清空图片列表以释放闭包引用
|
|
513
|
+
*/
|
|
514
|
+
onBeforeUnmount(() => {
|
|
515
|
+
showActionSheet.value = false
|
|
516
|
+
// 清空图片列表,释放闭包中对 props.attr 等对象的引用,防止内存泄漏
|
|
517
|
+
imageList.value = []
|
|
518
|
+
})
|
|
617
519
|
</script>
|
|
618
520
|
|
|
619
521
|
<template>
|
|
620
522
|
<div class="uploader-container">
|
|
621
|
-
<!--
|
|
523
|
+
<!-- 浏览器 / 微信 H5:统一隐藏文件选择(多选图片) -->
|
|
622
524
|
<input
|
|
623
|
-
ref="
|
|
525
|
+
ref="fileInputRef"
|
|
624
526
|
type="file"
|
|
625
527
|
multiple
|
|
626
528
|
accept="image/*"
|
|
627
|
-
|
|
628
|
-
@change="(e:any) =>
|
|
529
|
+
style="display:none"
|
|
530
|
+
@change="(e:any) => handleBrowserFiles(e.target.files)"
|
|
629
531
|
>
|
|
630
532
|
<van-uploader
|
|
631
533
|
ref="uploaderRef"
|
|
@@ -637,16 +539,6 @@ defineExpose({
|
|
|
637
539
|
:preview-image="true"
|
|
638
540
|
:before-delete="!props.readonly && props.attr?.addOrEdit !== 'readonly' && props.authority === 'admin' ? deleteFileFunction : undefined"
|
|
639
541
|
>
|
|
640
|
-
<template #preview-cover="{ index }">
|
|
641
|
-
<div
|
|
642
|
-
v-if="imageList[index] && !isFileItemImage(imageList[index])"
|
|
643
|
-
class="file-type-tile"
|
|
644
|
-
:style="{ background: getItemFileMeta(imageList[index]).color }"
|
|
645
|
-
>
|
|
646
|
-
<span class="file-type-tile__ext">{{ getItemFileMeta(imageList[index]).ext }}</span>
|
|
647
|
-
<span class="file-type-tile__label">{{ getItemFileMeta(imageList[index]).label }}</span>
|
|
648
|
-
</div>
|
|
649
|
-
</template>
|
|
650
542
|
<template #default>
|
|
651
543
|
<div
|
|
652
544
|
v-if="!props.readonly && props.mode !== '预览' && (imageList.length < props.attr?.acceptCount && props.attr?.addOrEdit !== 'readonly')"
|
|
@@ -662,37 +554,11 @@ defineExpose({
|
|
|
662
554
|
</van-uploader>
|
|
663
555
|
<ActionSheet
|
|
664
556
|
v-model:show="showActionSheet"
|
|
557
|
+
:actions="actionOptions"
|
|
665
558
|
teleport="body"
|
|
666
559
|
cancel-text="取消"
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
<button type="button" class="upload-source-sheet__item" @click="onPickCamera">
|
|
670
|
-
拍摄
|
|
671
|
-
</button>
|
|
672
|
-
<button type="button" class="upload-source-sheet__item" @click="onPickAlbum">
|
|
673
|
-
相册中选择
|
|
674
|
-
</button>
|
|
675
|
-
<button
|
|
676
|
-
v-if="useNativeDocumentPicker"
|
|
677
|
-
type="button"
|
|
678
|
-
class="upload-source-sheet__item"
|
|
679
|
-
@click="onPickDocumentNative"
|
|
680
|
-
>
|
|
681
|
-
文件上传
|
|
682
|
-
</button>
|
|
683
|
-
<label v-else class="upload-source-sheet__item upload-source-sheet__item--file">
|
|
684
|
-
文件上传
|
|
685
|
-
<input
|
|
686
|
-
ref="documentInputRef"
|
|
687
|
-
type="file"
|
|
688
|
-
multiple
|
|
689
|
-
:accept="documentAccept"
|
|
690
|
-
class="upload-source-sheet__file-input"
|
|
691
|
-
@change="onDocumentInputChange"
|
|
692
|
-
>
|
|
693
|
-
</label>
|
|
694
|
-
</div>
|
|
695
|
-
</ActionSheet>
|
|
560
|
+
@select="handleActionSelect"
|
|
561
|
+
/>
|
|
696
562
|
</div>
|
|
697
563
|
</template>
|
|
698
564
|
|
|
@@ -706,19 +572,6 @@ defineExpose({
|
|
|
706
572
|
// 该属性会影响表单布局
|
|
707
573
|
// gap: 16px;
|
|
708
574
|
}
|
|
709
|
-
|
|
710
|
-
/* WebView 内 display:none 会导致 file input 无法被程序化触发,改用视觉隐藏 */
|
|
711
|
-
.hidden-file-input {
|
|
712
|
-
position: fixed;
|
|
713
|
-
top: 0;
|
|
714
|
-
left: 0;
|
|
715
|
-
width: 1px;
|
|
716
|
-
height: 1px;
|
|
717
|
-
opacity: 0;
|
|
718
|
-
overflow: hidden;
|
|
719
|
-
pointer-events: none;
|
|
720
|
-
z-index: -1;
|
|
721
|
-
}
|
|
722
575
|
/* 让自定义上传按钮可点击:关闭 Vant 覆盖在上方的透明 input 的事件捕获 */
|
|
723
576
|
:deep(.custom-trigger-uploader .van-uploader__input) {
|
|
724
577
|
pointer-events: none;
|
|
@@ -756,74 +609,4 @@ defineExpose({
|
|
|
756
609
|
margin-top: 0;
|
|
757
610
|
}
|
|
758
611
|
}
|
|
759
|
-
|
|
760
|
-
.upload-source-sheet {
|
|
761
|
-
&__item {
|
|
762
|
-
display: block;
|
|
763
|
-
width: 100%;
|
|
764
|
-
padding: 14px 16px;
|
|
765
|
-
font-size: 16px;
|
|
766
|
-
line-height: 22px;
|
|
767
|
-
text-align: center;
|
|
768
|
-
background: #fff;
|
|
769
|
-
border: none;
|
|
770
|
-
color: #323233;
|
|
771
|
-
cursor: pointer;
|
|
772
|
-
position: relative;
|
|
773
|
-
box-sizing: border-box;
|
|
774
|
-
|
|
775
|
-
& + & {
|
|
776
|
-
border-top: 1px solid #ebedf0;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
&__item--file {
|
|
781
|
-
margin: 0;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
/* 用户手指直接点 input,WebView 才允许调起系统文件选择器(程序化 click 在 APK 内会被拦截) */
|
|
785
|
-
&__file-input {
|
|
786
|
-
position: absolute;
|
|
787
|
-
inset: 0;
|
|
788
|
-
width: 100%;
|
|
789
|
-
height: 100%;
|
|
790
|
-
opacity: 0;
|
|
791
|
-
cursor: pointer;
|
|
792
|
-
font-size: 0;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
.file-type-tile {
|
|
797
|
-
position: absolute;
|
|
798
|
-
inset: 0;
|
|
799
|
-
display: flex;
|
|
800
|
-
flex-direction: column;
|
|
801
|
-
align-items: center;
|
|
802
|
-
justify-content: center;
|
|
803
|
-
color: #fff;
|
|
804
|
-
border-radius: 8px;
|
|
805
|
-
padding: 4px;
|
|
806
|
-
box-sizing: border-box;
|
|
807
|
-
text-align: center;
|
|
808
|
-
pointer-events: none;
|
|
809
|
-
|
|
810
|
-
&__ext {
|
|
811
|
-
font-size: 16px;
|
|
812
|
-
font-weight: 700;
|
|
813
|
-
line-height: 1.2;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
&__label {
|
|
817
|
-
font-size: 10px;
|
|
818
|
-
margin-top: 2px;
|
|
819
|
-
opacity: 0.95;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
:deep(.custom-trigger-uploader) {
|
|
824
|
-
.van-uploader__preview {
|
|
825
|
-
position: relative;
|
|
826
|
-
overflow: hidden;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
612
|
</style>
|