af-mobile-client-vue3 1.4.13 → 1.4.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.4.13",
4
+ "version": "1.4.15",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -0,0 +1,312 @@
1
+ <script setup lang="ts">
2
+ import type { SignatureComponentExpose, SignatureComponentProps } from '@af-mobile-client-vue3/components/core/Signature/signature'
3
+ import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
4
+ import { showImagePreview, showToast } from 'vant'
5
+ import { defineEmits, defineProps, inject, ref } from 'vue'
6
+
7
+ const props = withDefaults(defineProps<SignatureComponentProps>(), {
8
+ label: '用户签字',
9
+ required: false,
10
+ disabled: false,
11
+ uploadMode: 'server',
12
+ imageList: null,
13
+ formReadonly: false,
14
+ isAsyncUpload: false,
15
+ })
16
+
17
+ const emit = defineEmits<{
18
+ signatureComplete: [data: any]
19
+ }>()
20
+
21
+ const signatureImage = ref<string>('')
22
+ const rawBase64Data = ref<string>('') // 保存原始base64数据
23
+ const hasSignature = ref(false)
24
+ const parentData: any = inject('provideParent')
25
+ const imageList = ref<Array<any>>(props.imageList ?? [])
26
+ console.log('imageList', imageList.value)
27
+ console.log('props.imageList', props.imageList)
28
+ // 处理签字后的上传
29
+ function getImageMimeType(fileName: string): string {
30
+ const ext = fileName.split('.').pop()?.toLowerCase()
31
+ if (ext === 'jpg' || ext === 'jpeg')
32
+ return 'image/jpeg'
33
+ if (ext === 'png')
34
+ return 'image/png'
35
+ if (ext === 'gif')
36
+ return 'image/gif'
37
+ return 'image/png' // 默认
38
+ }
39
+
40
+ function handleSignature() {
41
+ if (props.disabled)
42
+ return
43
+
44
+ mobileUtil.execute({
45
+ funcName: 'showSignaturePad',
46
+ param: {},
47
+ callbackFunc: (result: any) => {
48
+ console.log('签字结果:', result)
49
+ if (result.status === 'success') {
50
+ const mimeType = getImageMimeType(result.data.filePath)
51
+ // 保存原始base64数据
52
+ rawBase64Data.value = result.data.base64
53
+ // 添加base64图片前缀用于显示
54
+ signatureImage.value = `data:${mimeType};base64,${result.data.base64}`
55
+ hasSignature.value = true
56
+ const tempFile = {
57
+ uid: Date.now() + Math.random().toString(36).substr(2, 5),
58
+ name: result.data.filePath.split('/').pop(),
59
+ status: 'uploading',
60
+ message: '上传中...',
61
+ url: `data:${mimeType};base64,${result.data.base64}`,
62
+ isImage: true,
63
+ type: mimeType,
64
+ }
65
+ if (imageList.value) {
66
+ imageList.value = [tempFile]
67
+ }
68
+
69
+ const param = {
70
+ resUploadMode: props.uploadMode,
71
+ pathKey: 'Default',
72
+ formType: 'image',
73
+ useType: 'Default',
74
+ resUploadStock: '1',
75
+ filename: tempFile.name,
76
+ // 暂无size
77
+ // filesize: photoData.size,
78
+ f_operator: 'server',
79
+ imgPath: result.data.filePath,
80
+ urlPath: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
81
+ commonId: parentData?.commonId.value ?? '',
82
+ }
83
+ if (props.isAsyncUpload) {
84
+ // 添加上传队列
85
+ mobileUtil.execute({
86
+ funcName: 'queueUpload',
87
+ param,
88
+ callbackFunc: (res: any) => {
89
+ console.warn('上传结果', res)
90
+ let resStatus = 'success'
91
+ // 成功
92
+ if (res.data && res.data.enqueued) {
93
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
94
+ if (index !== -1) {
95
+ delete imageList.value[index].message
96
+ imageList.value[index].status = 'done'
97
+ imageList.value[index].photo_name = tempFile.name
98
+ imageList.value[index].type = mimeType
99
+ }
100
+ }
101
+ else {
102
+ resStatus = 'error'
103
+ // 上传失败,显示错误提示并清除图片
104
+ const errorMessage = res.message || res.error || '上传失败,请重试'
105
+ showToast({
106
+ message: errorMessage,
107
+ type: 'fail',
108
+ duration: 3000,
109
+ })
110
+ clearSignature()
111
+ }
112
+ emit('signatureComplete', {
113
+ status: resStatus,
114
+ data: imageList.value?.length > 0 ? imageList.value[0] : null,
115
+ })
116
+ },
117
+ })
118
+ }
119
+ else {
120
+ // 上传到服务器
121
+ mobileUtil.execute({
122
+ funcName: 'uploadResource',
123
+ param,
124
+ callbackFunc: (result: any) => {
125
+ const index = imageList.value.findIndex(item => item.uid === tempFile.uid)
126
+ if (result.status === 'success') {
127
+ if (index !== -1) {
128
+ imageList.value[index].uid = result.data.id
129
+ imageList.value[index].id = result.data.id
130
+ imageList.value[index].photo_name = result.data.f_filename
131
+ imageList.value[index].f_downloadpath = result.data.f_downloadpath
132
+ delete imageList.value[index].message
133
+ imageList.value[index].status = 'done'
134
+ }
135
+ }
136
+ else {
137
+ // 上传失败,显示错误提示并清除图片
138
+ const errorMessage = result.message || result.error || '上传失败,请重试'
139
+ showToast({
140
+ message: errorMessage,
141
+ type: 'fail',
142
+ duration: 3000,
143
+ })
144
+ clearSignature()
145
+ }
146
+ emit('signatureComplete', {
147
+ status: result.status,
148
+ data: result.data,
149
+ })
150
+ },
151
+ })
152
+ }
153
+ }
154
+ },
155
+ })
156
+ }
157
+
158
+ function clearSignature() {
159
+ signatureImage.value = ''
160
+ rawBase64Data.value = ''
161
+ hasSignature.value = false
162
+ imageList.value = []
163
+ emit('signatureComplete', {
164
+ base64: '',
165
+ status: 'cleared',
166
+ })
167
+ }
168
+
169
+ // 预览签字照片
170
+ function previewSignature() {
171
+ if (imageList.value) {
172
+ showImagePreview(imageList.value.map(item => item.url))
173
+ }
174
+ }
175
+
176
+ // 暴露方法供父组件调用
177
+ defineExpose<SignatureComponentExpose>({
178
+ clearSignature,
179
+ hasSignature: () => hasSignature.value,
180
+ getSignatureData: () => rawBase64Data.value, // 返回原始base64数据
181
+ previewSignature,
182
+ getSignatureList: () => imageList.value.map((item) => { return { photo_name: item.photo_name, id: item.id, f_downloadpath: item.f_downloadpath } }),
183
+ })
184
+ </script>
185
+
186
+ <template>
187
+ <van-field
188
+ center
189
+ name="signature"
190
+ :label="label"
191
+ :required="required"
192
+ >
193
+ <template #input>
194
+ <div class="signature-container">
195
+ <!-- 签字照片预览 -->
196
+ <div v-if="imageList.length > 0 || hasSignature" class="signature-preview">
197
+ <van-image
198
+ v-for="(item, index) in imageList"
199
+ :key="`${item}_${index}`"
200
+ :src="item.url"
201
+ alt="签字照片"
202
+ class="signature-image"
203
+ fit="contain"
204
+ :show-loading="true"
205
+ :show-error="true"
206
+ loading-icon="photo-o"
207
+ error-icon="warning-o"
208
+ @click="previewSignature"
209
+ />
210
+ </div>
211
+
212
+ <!-- 按钮行 -->
213
+ <div class="button-row">
214
+ <!-- 签字按钮 -->
215
+ <van-button
216
+ v-if="!props.formReadonly"
217
+ :disabled="disabled"
218
+ type="default"
219
+ size="small"
220
+ class="signature-btn dashed-btn"
221
+ @click="handleSignature"
222
+ >
223
+ <van-icon :name="hasSignature ? 'edit' : 'edit'" class="btn-icon" />
224
+ {{ imageList.length > 0 || hasSignature ? '重签' : '点击签字' }}
225
+ </van-button>
226
+
227
+ <!-- 清除按钮 -->
228
+ <van-button
229
+ v-if="(imageList.length > 0 || hasSignature) && !props.formReadonly"
230
+ type="default"
231
+ size="small"
232
+ class="dashed-btn clear-btn"
233
+ @click="clearSignature"
234
+ >
235
+ <van-icon name="delete" class="btn-icon" />
236
+ 清除
237
+ </van-button>
238
+ </div>
239
+ </div>
240
+ </template>
241
+ </van-field>
242
+ </template>
243
+
244
+ <style scoped lang="less">
245
+ .signature-container {
246
+ display: flex;
247
+ flex-direction: column;
248
+ gap: 12px;
249
+ width: 100%;
250
+ }
251
+
252
+ .signature-preview {
253
+ position: relative;
254
+ display: flex;
255
+ flex-direction: column;
256
+ align-items: center;
257
+ gap: 8px;
258
+
259
+ .signature-image {
260
+ max-width: 200px;
261
+ max-height: 120px;
262
+ border: 1px solid #e8e8e8;
263
+ border-radius: 4px;
264
+ cursor: pointer;
265
+ transition: all 0.2s ease;
266
+ }
267
+ }
268
+
269
+ // 按钮行样式
270
+ .button-row {
271
+ display: flex;
272
+ gap: 12px;
273
+ align-items: center;
274
+ justify-content: space-between;
275
+ }
276
+
277
+ .signature-btn {
278
+ flex-shrink: 0;
279
+ margin-right: auto;
280
+ }
281
+
282
+ // 虚线按钮样式
283
+ .dashed-btn {
284
+ border: 1px dashed #1989fa !important;
285
+ background-color: transparent !important;
286
+ color: #1989fa !important;
287
+ border-radius: 4px !important;
288
+
289
+ &:hover {
290
+ border-color: #0570d9 !important;
291
+ color: #0570d9 !important;
292
+ }
293
+
294
+ &:active {
295
+ border-color: #0366d6 !important;
296
+ color: #0366d6 !important;
297
+ background-color: rgba(25, 137, 250, 0.1) !important;
298
+ }
299
+
300
+ &:disabled {
301
+ border-color: #c8c9cc !important;
302
+ color: #c8c9cc !important;
303
+ background-color: transparent !important;
304
+ }
305
+ }
306
+
307
+ // 按钮图标样式
308
+ .btn-icon {
309
+ margin-right: 4px;
310
+ font-size: 14px;
311
+ }
312
+ </style>
@@ -0,0 +1,38 @@
1
+ // 签字结果接口
2
+ export interface SignatureResult {
3
+ status: 'success' | 'failed' | 'cancelled' | 'cleared'
4
+ base64?: string
5
+ message?: string
6
+ }
7
+
8
+ // 签字组件属性接口
9
+ export interface SignatureComponentProps {
10
+ label?: string
11
+ required?: boolean
12
+ disabled?: boolean
13
+ uploadMode?: string
14
+ imageList?: Array<any>
15
+ formReadonly?: boolean
16
+ isAsyncUpload?: boolean // 是否使用异步上传-异步上传后将不会返回file表的数据,需要通过上传时的文件名去t_files中查询
17
+ }
18
+
19
+ // 签字完成事件数据接口
20
+ export interface SignatureCompleteData {
21
+ base64: string
22
+ status: string
23
+ }
24
+
25
+ // 签字组件暴露的方法接口
26
+ export interface SignatureComponentExpose {
27
+ clearSignature: () => void
28
+ hasSignature: () => boolean
29
+ getSignatureData: () => string
30
+ previewSignature: () => void
31
+ getSignatureList: () => Array<any>
32
+ }
33
+
34
+ // base64图片工具类型
35
+ export interface Base64ImageData {
36
+ raw: string // 原始base64数据(不含前缀)
37
+ full: string // 完整的base64图片URL(含前缀)
38
+ }