af-mobile-client-vue3 1.6.47 → 1.6.49

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.47",
4
+ "version": "1.6.49",
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) => {
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * kkFileView 预览子组件
4
+ * 通过 iframe 加载 kkFileView 服务端预览页面
5
+ * 适用于 office 渲染器无法处理的格式(如 .doc / .xls 旧格式)或 office 渲染失败的降级场景
6
+ */
7
+ import { getConfigByNameAsync } from '@af-mobile-client-vue3/services/api/common'
8
+ import { Base64 } from 'js-base64'
9
+ import { Loading as VanLoading } from 'vant'
10
+ import { onMounted, ref } from 'vue'
11
+
12
+ const props = defineProps<{
13
+ /** 文件原始路径(相对路径或完整 URL) */
14
+ filePath: string
15
+ }>()
16
+
17
+ const iframeUrl = ref('')
18
+ const iframeLoading = ref(true)
19
+
20
+ function isHttp(path: string) {
21
+ return /^https?:\/\//i.test(path)
22
+ }
23
+
24
+ /** 读取 kkFileView 服务配置,拼接预览 URL */
25
+ async function buildPreviewUrl() {
26
+ try {
27
+ const res: any = await getConfigByNameAsync('previewDocServiceConfig')
28
+ if (!res?.previewDocService || !res?.fileServer)
29
+ throw new Error('previewDocServiceConfig 配置缺失')
30
+
31
+ const sourceUrl = isHttp(props.filePath)
32
+ ? props.filePath
33
+ : `${res.fileServer}${props.filePath}`
34
+
35
+ // kkFileView 要求 url 参数为 Base64 编码后再 encodeURIComponent
36
+ iframeUrl.value = `${res.previewDocService}${encodeURIComponent(Base64.encode(sourceUrl))}`
37
+ }
38
+ catch (e) {
39
+ console.warn('[KkPreview] 配置加载失败,降级直接加载原始地址', e)
40
+ // 服务未配置时,直接把原始地址塞给 iframe
41
+ iframeUrl.value = isHttp(props.filePath) ? props.filePath : ''
42
+ }
43
+ }
44
+
45
+ onMounted(buildPreviewUrl)
46
+ </script>
47
+
48
+ <template>
49
+ <div class="preview-doc-container">
50
+ <VanLoading v-if="iframeLoading" class="kk-loading" type="spinner" vertical>
51
+ 加载中...
52
+ </VanLoading>
53
+ <iframe
54
+ v-if="iframeUrl"
55
+ v-show="!iframeLoading"
56
+ :src="iframeUrl"
57
+ width="100%"
58
+ height="100%"
59
+ frameborder="0"
60
+ @load="iframeLoading = false"
61
+ />
62
+ </div>
63
+ </template>
64
+
65
+ <style scoped>
66
+ .preview-doc-container {
67
+ height: 100%;
68
+ position: relative;
69
+ }
70
+
71
+ .kk-loading {
72
+ position: absolute;
73
+ inset: 0;
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ z-index: 1;
78
+ }
79
+ </style>
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 媒体预览子组件
4
+ * 处理 image / video / audio 三种类型,直接用原生 HTML 标签渲染
5
+ */
6
+ import type { PreviewFileKind } from '@af-mobile-client-vue3/utils/fileType'
7
+
8
+ defineProps<{
9
+ kind: PreviewFileKind
10
+ src: string
11
+ }>()
12
+ </script>
13
+
14
+ <template>
15
+ <div v-if="kind === 'image'" class="media-wrapper">
16
+ <img :src="src" alt="预览图片" class="media-image">
17
+ </div>
18
+
19
+ <div v-else-if="kind === 'video'" class="media-wrapper media-dark">
20
+ <video class="media-video" controls :src="src" />
21
+ </div>
22
+
23
+ <div v-else-if="kind === 'audio'" class="media-audio-wrapper">
24
+ <audio class="media-audio" controls :src="src" />
25
+ </div>
26
+ </template>
27
+
28
+ <style scoped>
29
+ .media-wrapper {
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ flex: 1;
34
+ min-height: 200px;
35
+ }
36
+
37
+ .media-dark {
38
+ background: #000;
39
+ }
40
+
41
+ .media-image {
42
+ max-width: 100%;
43
+ max-height: 100%;
44
+ object-fit: contain;
45
+ }
46
+
47
+ .media-video {
48
+ width: 100%;
49
+ max-height: 100%;
50
+ border: none;
51
+ }
52
+
53
+ .media-audio-wrapper {
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ padding: 32px 16px;
58
+ }
59
+
60
+ .media-audio {
61
+ width: 100%;
62
+ max-width: 480px;
63
+ }
64
+ </style>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Office 文档预览子组件
4
+ * 使用 @vue-office 系列库渲染 pdf / docx / xlsx / xls / doc
5
+ * 渲染失败时 emit 'error',由父组件决定是否降级到 kk 预览
6
+ */
7
+ import type { PreviewFileKind } from '@af-mobile-client-vue3/utils/fileType'
8
+ import { defineAsyncComponent } from 'vue'
9
+
10
+ const props = defineProps<{
11
+ kind: PreviewFileKind
12
+ src: string | ArrayBuffer
13
+ }>()
14
+
15
+ const emit = defineEmits<{
16
+ (e: 'error'): void
17
+ }>()
18
+
19
+ // 按需异步加载,避免影响首屏
20
+ const VueOfficePdf = defineAsyncComponent(() =>
21
+ import('@vue-office/pdf/lib/v3/vue-office-pdf.mjs').then(m => m.default),
22
+ )
23
+
24
+ const VueOfficeDocx = defineAsyncComponent(async () => {
25
+ await import('@vue-office/docx/lib/v3/index.css')
26
+ return import('@vue-office/docx/lib/v3/vue-office-docx.mjs').then(m => m.default)
27
+ })
28
+
29
+ const VueOfficeExcel = defineAsyncComponent(async () => {
30
+ await import('@vue-office/excel/lib/v3/index.css')
31
+ return import('@vue-office/excel/lib/v3/vue-office-excel.mjs').then(m => m.default)
32
+ })
33
+ </script>
34
+
35
+ <template>
36
+ <VueOfficePdf
37
+ v-if="kind === 'pdf'"
38
+ :src="src"
39
+ class="office-viewer"
40
+ @error="emit('error')"
41
+ />
42
+ <!-- docx / doc 共用 docx 渲染器;.doc 若解析失败会触发 error 降级到 kk -->
43
+ <VueOfficeDocx
44
+ v-else-if="kind === 'docx' || kind === 'doc'"
45
+ :src="src"
46
+ class="office-viewer"
47
+ @error="emit('error')"
48
+ />
49
+ <!-- xlsx / xls 共用 excel 渲染器 -->
50
+ <VueOfficeExcel
51
+ v-else-if="kind === 'xlsx' || kind === 'xls'"
52
+ :src="src"
53
+ class="office-viewer"
54
+ @error="emit('error')"
55
+ />
56
+ </template>
57
+
58
+ <style scoped>
59
+ .office-viewer {
60
+ width: 100%;
61
+ flex: 1;
62
+ min-height: 200px;
63
+ overflow: auto;
64
+ }
65
+ </style>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 纯文本预览子组件
4
+ * 直接渲染文本内容,保留空白和换行
5
+ */
6
+ defineProps<{
7
+ content: string
8
+ }>()
9
+ </script>
10
+
11
+ <template>
12
+ <pre class="text-preview">{{ content }}</pre>
13
+ </template>
14
+
15
+ <style scoped>
16
+ .text-preview {
17
+ margin: 0;
18
+ padding: 12px;
19
+ white-space: pre-wrap;
20
+ word-break: break-word;
21
+ font-size: 13px;
22
+ line-height: 1.5;
23
+ color: #333;
24
+ overflow: auto;
25
+ flex: 1;
26
+ }
27
+ </style>
@@ -1,4 +1,14 @@
1
1
  <script setup lang="ts">
2
+ /**
3
+ * 文件预览组件(主入口)
4
+ *
5
+ * 渲染策略:
6
+ * 1. image / video / audio → MediaPreview(原生标签)
7
+ * 2. text → TextPreview(<pre>)
8
+ * 3. pdf / docx / doc / xlsx / xls → OfficePreview(@vue-office)
9
+ * └─ @vue-office 渲染失败 → 自动降级到 KkPreview(kkFileView iframe)
10
+ * 4. doc 旧格式 / unsupported → 直接走 KkPreview
11
+ */
2
12
  import type { PreviewFileKind } from '@af-mobile-client-vue3/utils/fileType'
3
13
  import { downloadAttachment } from '@af-mobile-client-vue3/utils/fileDownload'
4
14
  import { getPreviewFileKind } from '@af-mobile-client-vue3/utils/fileType'
@@ -9,96 +19,78 @@ import {
9
19
  resolveResourceUrl,
10
20
  } from '@af-mobile-client-vue3/utils/resourceUrl'
11
21
  import { Button as VanButton, Empty as VanEmpty, Loading as VanLoading } from 'vant'
12
- import { defineAsyncComponent, ref, shallowRef } from 'vue'
22
+ import { ref, shallowRef } from 'vue'
23
+ import KkPreview from './components/KkPreview.vue'
24
+ import MediaPreview from './components/MediaPreview.vue'
25
+ import OfficePreview from './components/OfficePreview.vue'
26
+ import TextPreview from './components/TextPreview.vue'
13
27
 
14
28
  export interface FilePreviewInitOptions {
15
29
  /** 后端 f_downloadpath 或完整 URL */
16
30
  path: string
17
- /** 下载时使用的文件名 */
31
+ /** 下载时使用的文件名(可选,默认从路径提取) */
18
32
  name?: string
19
33
  }
20
34
 
21
- const VueOfficePdf = defineAsyncComponent(async () => {
22
- const mod = await import('@vue-office/pdf/lib/v3/vue-office-pdf.mjs')
23
- return mod.default
24
- })
35
+ /** office 类型集合,这些类型优先走 @vue-office,失败降级 kk */
36
+ const OFFICE_KINDS: PreviewFileKind[] = ['pdf', 'docx', 'xlsx']
25
37
 
26
- const VueOfficeDocx = defineAsyncComponent(async () => {
27
- await import('@vue-office/docx/lib/v3/index.css')
28
- const mod = await import('@vue-office/docx/lib/v3/vue-office-docx.mjs')
29
- return mod.default
30
- })
38
+ /**
39
+ * 直接走 kk 预览的格式,不尝试 @vue-office
40
+ * - doc 旧二进制格式,@vue-office 无法解析
41
+ * - xls 旧二进制格式,@vue-office 解析效果差
42
+ * - unsupported 未知格式,交给 kkFileView 服务端判断
43
+ */
44
+ const KK_ONLY_KINDS: PreviewFileKind[] = ['doc', 'xls', 'unsupported']
31
45
 
32
- const VueOfficeExcel = defineAsyncComponent(async () => {
33
- await import('@vue-office/excel/lib/v3/index.css')
34
- const mod = await import('@vue-office/excel/lib/v3/vue-office-excel.mjs')
35
- return mod.default
36
- })
37
-
38
- const previewKind = ref<PreviewFileKind>('unsupported')
46
+ const kind = ref<PreviewFileKind>('unsupported')
39
47
  const fileSource = shallowRef<string | ArrayBuffer>('')
40
48
  const textContent = ref('')
41
- const imageSrc = ref('')
42
- const videoSrc = ref('')
43
- const audioSrc = ref('')
44
- const openUrl = ref('')
49
+ const mediaSrc = ref('')
50
+ const rawUrl = ref('') // 文件原始 URL(kk 预览和下载共用)
51
+ const rawPath = ref('') // 文件相对路径(kk 预览用)
45
52
  const downloadName = ref('')
46
53
  const loading = ref(false)
47
54
  const errorMsg = ref('')
55
+ /** true = office 渲染失败,已降级到 kk 预览 */
56
+ const officeFailed = ref(false)
48
57
 
49
- function onRenderError() {
50
- errorMsg.value = '文件预览渲染失败,可尝试下载查看'
58
+ /** office 渲染失败回调,切换到 kk 预览 */
59
+ function onOfficeFailed() {
60
+ officeFailed.value = true
51
61
  }
52
62
 
53
63
  async function init(options: FilePreviewInitOptions) {
54
64
  const path = getPreviewPath(options.path)
55
65
  if (!path) {
56
66
  errorMsg.value = '文件路径为空'
57
- previewKind.value = 'unsupported'
58
67
  return
59
68
  }
60
69
 
61
- openUrl.value = resolveResourceUrl(path)
70
+ // 重置所有状态
71
+ kind.value = getPreviewFileKind(path)
72
+ rawUrl.value = resolveResourceUrl(path)
73
+ rawPath.value = path
62
74
  downloadName.value = options.name || path.split('/').pop()?.split('?')[0] || '附件'
63
- previewKind.value = getPreviewFileKind(path)
64
75
  loading.value = true
65
76
  errorMsg.value = ''
66
- textContent.value = ''
77
+ officeFailed.value = false
67
78
  fileSource.value = ''
68
- imageSrc.value = ''
69
- videoSrc.value = ''
70
- audioSrc.value = ''
79
+ textContent.value = ''
80
+ mediaSrc.value = ''
71
81
 
72
82
  try {
73
- switch (previewKind.value) {
74
- case 'image':
75
- imageSrc.value = openUrl.value
76
- break
77
-
78
- case 'video':
79
- videoSrc.value = openUrl.value
80
- break
81
-
82
- case 'audio':
83
- audioSrc.value = openUrl.value
84
- break
85
-
86
- case 'text':
87
- textContent.value = await fetchResourceText(openUrl.value)
88
- break
89
-
90
- case 'pdf':
91
- case 'docx':
92
- case 'doc':
93
- case 'xlsx':
94
- case 'xls':
95
- fileSource.value = await fetchResourceArrayBuffer(openUrl.value)
96
- break
97
-
98
- case 'unsupported':
99
- // 未知格式,直接用 iframe 尝试打开,无需预加载内容
100
- break
83
+ if (kind.value === 'image' || kind.value === 'video' || kind.value === 'audio') {
84
+ mediaSrc.value = rawUrl.value
85
+ }
86
+ else if (kind.value === 'text') {
87
+ textContent.value = await fetchResourceText(rawUrl.value)
88
+ }
89
+ else if (OFFICE_KINDS.includes(kind.value)) {
90
+ // 预加载文件二进制,交给 @vue-office 渲染
91
+ fileSource.value = await fetchResourceArrayBuffer(rawUrl.value)
101
92
  }
93
+ // doc / unsupported 直接走 KkPreview,无需预加载
102
94
  }
103
95
  catch (e: any) {
104
96
  errorMsg.value = e?.message || '文件加载失败'
@@ -109,85 +101,67 @@ async function init(options: FilePreviewInitOptions) {
109
101
  }
110
102
 
111
103
  function handleDownload() {
112
- if (!openUrl.value)
113
- return
114
- downloadAttachment({ url: openUrl.value, name: downloadName.value })
104
+ downloadAttachment({ url: rawUrl.value, name: downloadName.value })
115
105
  }
116
106
 
117
- defineExpose({
118
- init,
119
- download: handleDownload,
120
- })
107
+ defineExpose({ init, download: handleDownload })
121
108
  </script>
122
109
 
123
110
  <template>
124
111
  <div class="file-preview-root">
125
- <div v-if="openUrl && !loading" class="preview-toolbar">
112
+ <!-- 悬浮下载按钮,定位在预览区右上角 -->
113
+ <div v-if="rawUrl && !loading" class="preview-download-btn">
126
114
  <VanButton size="small" type="primary" plain icon="down" @click="handleDownload">
127
115
  下载
128
116
  </VanButton>
129
117
  </div>
130
118
 
119
+ <!-- 加载中 -->
131
120
  <VanLoading v-if="loading" class="preview-loading" type="spinner" vertical>
132
121
  加载中...
133
122
  </VanLoading>
134
123
 
124
+ <!-- 加载/解析出错 -->
135
125
  <VanEmpty v-else-if="errorMsg" :description="errorMsg">
136
- <template v-if="openUrl" #default>
137
- <VanButton size="small" type="primary" @click="handleDownload">
138
- 下载
139
- </VanButton>
140
- </template>
126
+ <VanButton v-if="rawUrl" size="small" type="primary" @click="handleDownload">
127
+ 下载
128
+ </VanButton>
141
129
  </VanEmpty>
142
130
 
143
131
  <template v-else>
144
- <VueOfficePdf
145
- v-if="previewKind === 'pdf' && fileSource"
146
- :src="fileSource"
147
- class="office-preview"
148
- @error="onRenderError"
132
+ <!-- 媒体:图片 / 视频 / 音频 -->
133
+ <MediaPreview
134
+ v-if="kind === 'image' || kind === 'video' || kind === 'audio'"
135
+ :kind="kind"
136
+ :src="mediaSrc"
149
137
  />
150
- <!-- docx / doc 共用同一渲染器 -->
151
- <VueOfficeDocx
152
- v-else-if="(previewKind === 'docx' || previewKind === 'doc') && fileSource"
153
- :src="fileSource"
154
- class="office-preview"
155
- @error="onRenderError"
138
+
139
+ <!-- 纯文本 -->
140
+ <TextPreview
141
+ v-else-if="kind === 'text'"
142
+ :content="textContent"
156
143
  />
157
- <!-- xlsx / xls 共用同一渲染器 -->
158
- <VueOfficeExcel
159
- v-else-if="(previewKind === 'xlsx' || previewKind === 'xls') && fileSource"
144
+
145
+ <!--
146
+ Office 文档:先走 @vue-office
147
+ 失败(officeFailed)或 doc/unsupported 直接走 KkPreview
148
+ -->
149
+ <OfficePreview
150
+ v-else-if="OFFICE_KINDS.includes(kind) && fileSource && !officeFailed"
151
+ :kind="kind"
160
152
  :src="fileSource"
161
- class="office-preview"
162
- @error="onRenderError"
153
+ @error="onOfficeFailed"
163
154
  />
164
- <div v-else-if="previewKind === 'image'" class="preview-image-wrapper">
165
- <img :src="imageSrc" alt="image" class="preview-image">
166
- </div>
167
- <div v-else-if="previewKind === 'video'" class="preview-video-wrapper">
168
- <video class="preview-video" controls :src="videoSrc" />
169
- </div>
170
- <div v-else-if="previewKind === 'audio'" class="preview-audio-wrapper">
171
- <audio class="preview-audio" controls :src="audioSrc" />
172
- </div>
173
- <pre v-else-if="previewKind === 'text'" class="preview-text">{{ textContent }}</pre>
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>
155
+
156
+ <!-- kk 预览:office 降级 或 doc/unsupported 直接走 -->
157
+ <KkPreview
158
+ v-else-if="officeFailed || KK_ONLY_KINDS.includes(kind)"
159
+ :file-path="rawPath"
160
+ />
161
+
162
+ <!-- 兜底:路径异常 -->
189
163
  <VanEmpty v-else description="文件路径异常">
190
- <VanButton v-if="openUrl" size="small" type="primary" @click="handleDownload">
164
+ <VanButton v-if="rawUrl" size="small" type="primary" @click="handleDownload">
191
165
  下载
192
166
  </VanButton>
193
167
  </VanEmpty>
@@ -200,103 +174,25 @@ defineExpose({
200
174
  width: 100%;
201
175
  height: 100%;
202
176
  min-height: 200px;
203
- position: relative;
204
- overflow: auto;
205
- background: #fff;
206
177
  display: flex;
207
178
  flex-direction: column;
179
+ background: #fff;
180
+ overflow: hidden;
181
+ /* 作为悬浮按钮的定位基准 */
182
+ position: relative;
208
183
  }
209
184
 
210
- .preview-toolbar {
211
- display: flex;
212
- justify-content: flex-end;
213
- padding: 8px 12px 0;
214
- flex-shrink: 0;
215
- }
216
-
217
- .preview-loading {
185
+ .preview-download-btn {
218
186
  position: absolute;
219
- inset: 0;
220
- display: flex;
221
- align-items: center;
222
- justify-content: center;
223
- z-index: 1;
187
+ top: 10px;
188
+ right: 12px;
189
+ z-index: 10;
224
190
  }
225
191
 
226
- .office-preview {
227
- width: 100%;
228
- min-height: 200px;
229
- height: 100%;
230
- }
231
-
232
- .preview-image-wrapper,
233
- .preview-video-wrapper {
234
- display: flex;
235
- align-items: center;
236
- justify-content: center;
237
- min-height: 200px;
238
- height: 100%;
239
- background: #000;
240
- }
241
-
242
- .preview-image {
243
- max-width: 100%;
244
- max-height: 100%;
245
- }
246
-
247
- .preview-video {
248
- width: 100%;
249
- max-height: 100%;
250
- border: none;
251
- background: #000;
252
- }
253
-
254
- .preview-text {
255
- margin: 0;
256
- padding: 12px;
257
- white-space: pre-wrap;
258
- word-break: break-word;
259
- font-size: 13px;
260
- line-height: 1.5;
261
- color: #333;
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%;
192
+ .preview-loading {
287
193
  flex: 1;
288
- min-height: 200px;
289
- border: none;
290
- }
291
-
292
- .preview-iframe-fallback {
293
194
  display: flex;
294
195
  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;
196
+ justify-content: center;
301
197
  }
302
198
  </style>
@@ -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,
@@ -1,31 +1,33 @@
1
- <script setup lang="ts">
2
- import FilePreview from '@af-mobile-client-vue3/components/data/FilePreview/index.vue'
3
- import { onMounted, ref } from 'vue'
4
-
5
- const previewDocType = ref('pdf')
6
- const previewDocUrl = ref('/resource/af-revenue/pdf/e0a35f3414444d009cbce020af2e617d.pdf')
7
-
8
- const filePreviewRef = ref<InstanceType<typeof FilePreview> | null>(null)
9
-
10
- onMounted(() => {
11
- filePreviewRef.value?.init({
12
- path: '/resource/af-revenue/pdf/e0a35f3414444d009cbce020af2e617d.pdf',
13
- })
14
- })
15
- </script>
16
-
17
- <template>
18
- <div>
19
- <FilePreview
20
- ref="filePreviewRef"
21
- :key="`${previewDocType}-${previewDocUrl}`"
22
- class="doc-preview-file"
23
- />
24
- </div>
25
- </template>
26
-
27
- <style scoped lang="less">
28
- .doc-preview-file {
29
- height: 90vh;
30
- }
31
- </style>
1
+ <script setup lang="ts">
2
+ import FilePreview from '@af-mobile-client-vue3/components/data/FilePreview/index.vue'
3
+ import { onMounted, ref } from 'vue'
4
+
5
+ const previewDocType = ref('docx')
6
+ // /resource/af-revenue/word/62c684cd70ce4583aa1d6bf21164d465.doc
7
+ // /resource/af-revenue/word/46a87ec0a05143f98061a1ba43f19ed2.docx
8
+ const previewDocUrl = ref('/resource/af-revenue/word/62c684cd70ce4583aa1d6bf21164d465.doc')
9
+
10
+ const filePreviewRef = ref<InstanceType<typeof FilePreview> | null>(null)
11
+
12
+ onMounted(() => {
13
+ filePreviewRef.value?.init({
14
+ path: '/resource/af-revenue/word/62c684cd70ce4583aa1d6bf21164d465.doc',
15
+ })
16
+ })
17
+ </script>
18
+
19
+ <template>
20
+ <div>
21
+ <FilePreview
22
+ ref="filePreviewRef"
23
+ :key="`${previewDocType}-${previewDocUrl}`"
24
+ class="doc-preview-file"
25
+ />
26
+ </div>
27
+ </template>
28
+
29
+ <style scoped lang="less">
30
+ .doc-preview-file {
31
+ height: 90vh;
32
+ }
33
+ </style>