af-mobile-client-vue3 1.6.46 → 1.6.48

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.46",
4
+ "version": "1.6.48",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -21,6 +21,7 @@ interface WatermarkConfig {
21
21
  format?: string
22
22
  customLines?: string[]
23
23
  enableRePhotoCheck?: boolean
24
+ blurThreshold?: string | number
24
25
  }
25
26
 
26
27
  const props = defineProps({
@@ -180,6 +181,7 @@ function triggerCamera() {
180
181
  if (watermark.alpha !== undefined && String(watermark.alpha) !== '')
181
182
  param.watermarkAlpha = watermark.alpha
182
183
  }
184
+ param.blurThreshold = (props.attr as any)?.watermark?.blurThreshold ?? 30
183
185
  return param
184
186
  })(),
185
187
  callbackFunc: (result: any) => {
@@ -40,6 +40,7 @@ const fileSource = shallowRef<string | ArrayBuffer>('')
40
40
  const textContent = ref('')
41
41
  const imageSrc = ref('')
42
42
  const videoSrc = ref('')
43
+ const audioSrc = ref('')
43
44
  const openUrl = ref('')
44
45
  const downloadName = ref('')
45
46
  const loading = ref(false)
@@ -66,24 +67,36 @@ async function init(options: FilePreviewInitOptions) {
66
67
  fileSource.value = ''
67
68
  imageSrc.value = ''
68
69
  videoSrc.value = ''
70
+ audioSrc.value = ''
69
71
 
70
72
  try {
71
73
  switch (previewKind.value) {
72
74
  case 'image':
73
75
  imageSrc.value = openUrl.value
74
76
  break
77
+
75
78
  case 'video':
76
79
  videoSrc.value = openUrl.value
77
80
  break
81
+
82
+ case 'audio':
83
+ audioSrc.value = openUrl.value
84
+ break
85
+
78
86
  case 'text':
79
87
  textContent.value = await fetchResourceText(openUrl.value)
80
88
  break
89
+
81
90
  case 'pdf':
82
91
  case 'docx':
92
+ case 'doc':
83
93
  case 'xlsx':
94
+ case 'xls':
84
95
  fileSource.value = await fetchResourceArrayBuffer(openUrl.value)
85
96
  break
86
- default:
97
+
98
+ case 'unsupported':
99
+ // 未知格式,直接用 iframe 尝试打开,无需预加载内容
87
100
  break
88
101
  }
89
102
  }
@@ -95,11 +108,6 @@ async function init(options: FilePreviewInitOptions) {
95
108
  }
96
109
  }
97
110
 
98
- function openExternal() {
99
- if (openUrl.value)
100
- window.open(openUrl.value, '_blank')
101
- }
102
-
103
111
  function handleDownload() {
104
112
  if (!openUrl.value)
105
113
  return
@@ -139,14 +147,16 @@ defineExpose({
139
147
  class="office-preview"
140
148
  @error="onRenderError"
141
149
  />
150
+ <!-- docx / doc 共用同一渲染器 -->
142
151
  <VueOfficeDocx
143
- v-else-if="previewKind === 'docx' && fileSource"
152
+ v-else-if="(previewKind === 'docx' || previewKind === 'doc') && fileSource"
144
153
  :src="fileSource"
145
154
  class="office-preview"
146
155
  @error="onRenderError"
147
156
  />
157
+ <!-- xlsx / xls 共用同一渲染器 -->
148
158
  <VueOfficeExcel
149
- v-else-if="previewKind === 'xlsx' && fileSource"
159
+ v-else-if="(previewKind === 'xlsx' || previewKind === 'xls') && fileSource"
150
160
  :src="fileSource"
151
161
  class="office-preview"
152
162
  @error="onRenderError"
@@ -157,8 +167,26 @@ defineExpose({
157
167
  <div v-else-if="previewKind === 'video'" class="preview-video-wrapper">
158
168
  <video class="preview-video" controls :src="videoSrc" />
159
169
  </div>
170
+ <div v-else-if="previewKind === 'audio'" class="preview-audio-wrapper">
171
+ <audio class="preview-audio" controls :src="audioSrc" />
172
+ </div>
160
173
  <pre v-else-if="previewKind === 'text'" class="preview-text">{{ textContent }}</pre>
161
- <VanEmpty v-else description="暂不支持在线预览该格式">
174
+ <!-- 未知格式:用 iframe 尝试直接打开,系统/浏览器自行决定如何处理 -->
175
+ <div v-else-if="previewKind === 'unsupported'" class="preview-iframe-wrapper">
176
+ <iframe
177
+ :src="openUrl"
178
+ class="preview-iframe"
179
+ sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
180
+ @error="onRenderError"
181
+ />
182
+ <div class="preview-iframe-fallback">
183
+ <span>若文件无法显示,请</span>
184
+ <VanButton size="small" type="primary" plain @click="handleDownload">
185
+ 下载查看
186
+ </VanButton>
187
+ </div>
188
+ </div>
189
+ <VanEmpty v-else description="文件路径异常">
162
190
  <VanButton v-if="openUrl" size="small" type="primary" @click="handleDownload">
163
191
  下载
164
192
  </VanButton>
@@ -232,4 +260,43 @@ defineExpose({
232
260
  line-height: 1.5;
233
261
  color: #333;
234
262
  }
263
+
264
+ .preview-audio-wrapper {
265
+ display: flex;
266
+ align-items: center;
267
+ justify-content: center;
268
+ padding: 32px 16px;
269
+ min-height: 120px;
270
+ }
271
+
272
+ .preview-audio {
273
+ width: 100%;
274
+ max-width: 480px;
275
+ }
276
+
277
+ .preview-iframe-wrapper {
278
+ display: flex;
279
+ flex-direction: column;
280
+ width: 100%;
281
+ height: 100%;
282
+ flex: 1;
283
+ }
284
+
285
+ .preview-iframe {
286
+ width: 100%;
287
+ flex: 1;
288
+ min-height: 200px;
289
+ border: none;
290
+ }
291
+
292
+ .preview-iframe-fallback {
293
+ display: flex;
294
+ align-items: center;
295
+ gap: 8px;
296
+ padding: 8px 12px;
297
+ font-size: 12px;
298
+ color: #999;
299
+ flex-shrink: 0;
300
+ border-top: 1px solid #f0f0f0;
301
+ }
235
302
  </style>
@@ -68,22 +68,28 @@ export function getFileTypeMeta(name?: string): FileTypeMeta {
68
68
  }
69
69
  }
70
70
 
71
- export type PreviewFileKind = 'pdf' | 'docx' | 'xlsx' | 'image' | 'video' | 'text' | 'unsupported'
71
+ export type PreviewFileKind = 'pdf' | 'docx' | 'doc' | 'xlsx' | 'xls' | 'image' | 'video' | 'audio' | 'text' | 'unsupported'
72
72
 
73
73
  /** 附件预览类型(vue-office / 原生标签) */
74
74
  export function getPreviewFileKind(name?: string): PreviewFileKind {
75
75
  const ext = getFileExtension(name)
76
76
  if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'ico', 'tif', 'tiff', 'avif'].includes(ext))
77
77
  return 'image'
78
- if (['mp4', 'webm', 'mov', 'm4v'].includes(ext))
78
+ if (['mp4', 'webm', 'mov', 'm4v', 'avi', 'mkv', 'flv', 'rmvb'].includes(ext))
79
79
  return 'video'
80
+ if (['mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a', 'wma'].includes(ext))
81
+ return 'audio'
80
82
  if (ext === 'pdf')
81
83
  return 'pdf'
82
- if (ext === 'docx' || ext === 'doc')
84
+ if (ext === 'docx')
83
85
  return 'docx'
84
- if (ext === 'xls' || ext === 'xlsx')
86
+ if (ext === 'doc')
87
+ return 'doc'
88
+ if (ext === 'xlsx')
85
89
  return 'xlsx'
86
- if (ext === 'txt')
90
+ if (ext === 'xls')
91
+ return 'xls'
92
+ if (['txt', 'md', 'log', 'csv', 'json', 'xml', 'yaml', 'yml', 'ini', 'conf', 'properties'].includes(ext))
87
93
  return 'text'
88
94
  return 'unsupported'
89
95
  }
@@ -63,7 +63,8 @@ const photoSignatureComponentMap = {
63
63
  photoSignature,
64
64
  qinHuaSignature: QinHuaSignature,
65
65
  }
66
-
66
+ // 水印配置
67
+ const blurThreshold = ref<number>(30)
67
68
  // 操作人信息
68
69
  const checkerInfo = useUserStore().getUserInfo()
69
70
  const safecheckStore = useSafecheckStore()
@@ -502,7 +503,11 @@ onBeforeMount(() => {
502
503
 
503
504
  // 统一转交到 continuePageInit 决定是否弹窗
504
505
  continuePageInit()
505
-
506
+ getConfigByName('PhotoWatermarkConfig', (result) => {
507
+ if (result) {
508
+ blurThreshold.value = result?.blurThreshold || 30
509
+ }
510
+ }, 'af-safecheck')
506
511
  // 获取对应aiKeyToFieldMap
507
512
  getConfigByName('AICheckConfig', (res: any) => {
508
513
  aiKeyToFieldMap.value = res?.aiKeyToFieldMap || {
@@ -2824,6 +2829,7 @@ provide('provideParent', {
2824
2829
  :is-view-mode="isReadOnly"
2825
2830
  :extra-data="row?.extraData || {}"
2826
2831
  :ai-synced-info-map="syncedItemInfoMap"
2832
+ :blur-threshold="blurThreshold"
2827
2833
  @value-change="(selVal, selObj, currentVal, payload) => { valueChange(currentVal, pIndex, payload) }"
2828
2834
  @device-deleted="cleanupDeletedDeviceData"
2829
2835
  @success-file-list="successFileList"
@@ -24,6 +24,7 @@ const props = defineProps<{
24
24
  maxCount: number // 该步骤允许的最大拍照数量
25
25
  isLast: boolean // 是否为最后一个步骤
26
26
  enableUpload?: boolean // 是否启用上传功能(true时点击拍照按钮先弹出选择)
27
+ blurThreshold?: number // 图片模糊度阈值
27
28
  }>()
28
29
 
29
30
  /**
@@ -151,10 +152,14 @@ function triggerCamera() {
151
152
  return
152
153
  }
153
154
 
155
+ const param = {
156
+ blurThreshold: props.blurThreshold ?? 30,
157
+ }
158
+ console.warn('param', JSON.stringify(param))
154
159
  // 调用Native方法打开相机
155
160
  mobileUtil.execute({
156
161
  funcName: 'takePicture',
157
- param: {},
162
+ param,
158
163
  // 拍照成功后的回调函数
159
164
  callbackFunc: (result: any) => {
160
165
  // 检查返回状态是否成功
@@ -81,6 +81,10 @@ const props = defineProps({
81
81
  type: Object,
82
82
  default: () => ({}),
83
83
  },
84
+ blurThreshold: {
85
+ type: Number,
86
+ default: 30,
87
+ },
84
88
  })
85
89
 
86
90
  /**
@@ -126,6 +130,7 @@ const photoAiRecognitionConfig = ref<any>(null)
126
130
  /** 从父组件传入的已同步属性信息映射,透传给PhotoRecognitionInfo用于标记哪些属性已同步并可点击跳转 */
127
131
  const syncedItemInfoMap = computed(() => props.aiSyncedInfoMap)
128
132
 
133
+ const blurThreshold = computed(() => props.blurThreshold)
129
134
  // ========================================
130
135
  // 计算属性
131
136
  // ========================================
@@ -196,6 +201,7 @@ onMounted(() => {
196
201
  */
197
202
  function init() {
198
203
  const fromConfig = props.extraData?.checkAiSlotFrom || 'photoAiRecognitionForm'
204
+ console.warn('props', JSON.stringify(props))
199
205
  console.warn('fromConfig', fromConfig)
200
206
  getConfigByName('CheckAiSlotFromDic', (res: any) => {
201
207
  console.warn('CheckAiSlotFromDic', res)
@@ -765,6 +771,7 @@ defineExpose({
765
771
  :max-count="Number(step.acceptCount ?? 0)"
766
772
  :is-last="step.model === stepColumns[stepColumns.length - 1]?.model"
767
773
  :enable-upload="step.enableUpload"
774
+ :blur-threshold="blurThreshold"
768
775
  @capture="(file) => handleCaptureSuccess(step, file)"
769
776
  @next="handleNextStep"
770
777
  @prev="handlePrevStep"
@@ -18,6 +18,7 @@ interface WatermarkConfig {
18
18
  alpha?: number
19
19
  userName?: string
20
20
  enableRePhotoCheck?: boolean
21
+ blurThreshold?: number | string
21
22
  }
22
23
 
23
24
  const props = defineProps({
@@ -178,6 +179,8 @@ function triggerCamera() {
178
179
  }
179
180
  }
180
181
 
182
+ param.blurThreshold = props.attr?.watermark?.blurThreshold ?? 30
183
+
181
184
  mobileUtil.execute({
182
185
  funcName: 'takePicture',
183
186
  param,