af-mobile-client-vue3 1.6.3 → 1.6.5

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.6.3",
4
+ "version": "1.6.5",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -1,312 +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, Button as VanButton, Field as VanField, Icon as VanIcon, Image as VanImage } from 'vant'
5
- import { 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
- <VanField
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
- <VanImage
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
- <VanButton
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
- <VanIcon :name="hasSignature ? 'edit' : 'edit'" class="btn-icon" />
224
- {{ imageList.length > 0 || hasSignature ? '重签' : '点击签字' }}
225
- </VanButton>
226
-
227
- <!-- 清除按钮 -->
228
- <VanButton
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
- <VanIcon name="delete" class="btn-icon" />
236
- 清除
237
- </VanButton>
238
- </div>
239
- </div>
240
- </template>
241
- </VanField>
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>
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, Button as VanButton, Field as VanField, Icon as VanIcon, Image as VanImage } from 'vant'
5
+ import { 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
+ <VanField
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
+ <VanImage
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
+ <VanButton
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
+ <VanIcon :name="hasSignature ? 'edit' : 'edit'" class="btn-icon" />
224
+ {{ imageList.length > 0 || hasSignature ? '重签' : '点击签字' }}
225
+ </VanButton>
226
+
227
+ <!-- 清除按钮 -->
228
+ <VanButton
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
+ <VanIcon name="delete" class="btn-icon" />
236
+ 清除
237
+ </VanButton>
238
+ </div>
239
+ </div>
240
+ </template>
241
+ </VanField>
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>