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 +1 -1
- package/src/components/core/Signature/SignatureComponent.vue +312 -0
- package/src/components/core/Signature/signature.ts +38 -0
- package/src/components/core/XSelect/index.vue +211 -211
- package/src/components/data/XBadge/index.vue +1 -1
- package/src/components/data/XFormGroup/doc/DeviceForm.vue +1 -1
- package/src/components/data/XFormGroup/doc/UserForm.vue +1 -1
- package/src/components/data/XFormItem/index.vue +1 -3
- package/src/components/data/XOlMap/types.ts +1 -1
- package/src/components/data/XReportGrid/XReportDemo.vue +33 -33
- package/src/components/data/XReportGrid/print.js +184 -184
- package/src/stores/modules/setting.ts +1 -0
- package/src/utils/timeUtil.ts +27 -27
- package/src/views/component/XCellListView/index.vue +1 -78
- package/src/views/component/XFormView/index.vue +0 -1
- package/src/views/component/XOlMapView/XLocationPicker/index.vue +118 -118
- package/src/views/user/login/LoginForm.vue +5 -9
package/package.json
CHANGED
|
@@ -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
|
+
}
|