@ramathibodi/nuxt-commons 0.1.73 → 0.1.75
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/README.md +115 -96
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/components/Alert.vue +58 -54
- package/dist/runtime/components/BarcodeReader.vue +130 -122
- package/dist/runtime/components/ExportCSV.vue +110 -102
- package/dist/runtime/components/FileBtn.vue +79 -67
- package/dist/runtime/components/ImportCSV.vue +151 -139
- package/dist/runtime/components/MrzReader.vue +168 -0
- package/dist/runtime/components/SplitterPanel.vue +67 -59
- package/dist/runtime/components/TabsGroup.vue +39 -31
- package/dist/runtime/components/TextBarcode.vue +66 -54
- package/dist/runtime/components/device/IdCardButton.vue +95 -83
- package/dist/runtime/components/device/IdCardWebSocket.vue +207 -195
- package/dist/runtime/components/device/Scanner.vue +350 -338
- package/dist/runtime/components/dialog/Confirm.vue +112 -100
- package/dist/runtime/components/dialog/Host.vue +88 -84
- package/dist/runtime/components/dialog/Index.vue +84 -72
- package/dist/runtime/components/dialog/Loading.vue +51 -39
- package/dist/runtime/components/dialog/default/Confirm.vue +112 -100
- package/dist/runtime/components/dialog/default/Loading.vue +60 -48
- package/dist/runtime/components/dialog/default/Notify.vue +82 -70
- package/dist/runtime/components/dialog/default/Printing.vue +46 -34
- package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -132
- package/dist/runtime/components/document/Form.vue +50 -42
- package/dist/runtime/components/document/TemplateBuilder.vue +536 -524
- package/dist/runtime/components/form/ActionPad.vue +156 -144
- package/dist/runtime/components/form/Birthdate.vue +116 -104
- package/dist/runtime/components/form/CheckboxGroup.vue +99 -87
- package/dist/runtime/components/form/CodeEditor.vue +45 -37
- package/dist/runtime/components/form/Date.vue +270 -258
- package/dist/runtime/components/form/DateTime.vue +220 -208
- package/dist/runtime/components/form/Dialog.vue +178 -166
- package/dist/runtime/components/form/EditPad.vue +157 -145
- package/dist/runtime/components/form/File.vue +295 -283
- package/dist/runtime/components/form/Hidden.vue +44 -32
- package/dist/runtime/components/form/Iterator.vue +538 -526
- package/dist/runtime/components/form/Login.vue +143 -131
- package/dist/runtime/components/form/Pad.vue +399 -387
- package/dist/runtime/components/form/SignPad.vue +226 -218
- package/dist/runtime/components/form/System.vue +34 -26
- package/dist/runtime/components/form/Table.vue +391 -379
- package/dist/runtime/components/form/TableData.vue +236 -224
- package/dist/runtime/components/form/Time.vue +177 -165
- package/dist/runtime/components/form/images/Capture.vue +245 -237
- package/dist/runtime/components/form/images/Edit.vue +133 -121
- package/dist/runtime/components/form/images/Field.vue +331 -320
- package/dist/runtime/components/form/images/Pad.vue +54 -42
- package/dist/runtime/components/label/Date.vue +37 -29
- package/dist/runtime/components/label/DateAgo.vue +102 -94
- package/dist/runtime/components/label/DateCount.vue +152 -144
- package/dist/runtime/components/label/Field.vue +111 -103
- package/dist/runtime/components/label/FormatMoney.vue +37 -29
- package/dist/runtime/components/label/Mask.vue +46 -38
- package/dist/runtime/components/label/Object.vue +21 -13
- package/dist/runtime/components/master/Autocomplete.vue +89 -81
- package/dist/runtime/components/master/Combobox.vue +88 -80
- package/dist/runtime/components/master/RadioGroup.vue +90 -78
- package/dist/runtime/components/master/Select.vue +70 -62
- package/dist/runtime/components/master/label.vue +55 -47
- package/dist/runtime/components/model/Autocomplete.vue +91 -79
- package/dist/runtime/components/model/Combobox.vue +90 -78
- package/dist/runtime/components/model/Pad.vue +114 -102
- package/dist/runtime/components/model/Select.vue +78 -72
- package/dist/runtime/components/model/Table.vue +370 -358
- package/dist/runtime/components/model/iterator.vue +497 -489
- package/dist/runtime/components/model/label.vue +58 -50
- package/dist/runtime/components/pdf/Print.vue +75 -63
- package/dist/runtime/components/pdf/View.vue +146 -134
- package/dist/runtime/composables/alert.d.ts +4 -0
- package/dist/runtime/composables/api.d.ts +4 -0
- package/dist/runtime/composables/dialog.d.ts +1 -1
- package/dist/runtime/composables/document/templateFormHidden.d.ts +4 -0
- package/dist/runtime/composables/graphql.d.ts +1 -1
- package/dist/runtime/composables/graphqlModel.d.ts +9 -9
- package/dist/runtime/composables/graphqlModelItem.d.ts +7 -7
- package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
- package/dist/runtime/composables/localStorageModel.d.ts +4 -0
- package/dist/runtime/composables/lookupList.d.ts +4 -0
- package/dist/runtime/composables/menu.d.ts +4 -0
- package/dist/runtime/composables/useMrzReader.d.ts +48 -0
- package/dist/runtime/composables/useMrzReader.js +423 -0
- package/dist/runtime/composables/useTesseract.d.ts +16 -0
- package/dist/runtime/composables/useTesseract.js +45 -0
- package/dist/runtime/composables/userPermission.d.ts +1 -1
- package/dist/runtime/labs/Calendar.vue +99 -99
- package/dist/runtime/labs/form/EditMobile.vue +152 -152
- package/dist/runtime/labs/form/TextFieldMask.vue +43 -43
- package/dist/runtime/plugins/clientConfig.d.ts +1 -1
- package/dist/runtime/plugins/default.d.ts +1 -1
- package/dist/runtime/plugins/dialogManager.d.ts +1 -1
- package/dist/runtime/plugins/permission.d.ts +1 -1
- package/dist/runtime/types/alert.d.ts +11 -11
- package/dist/runtime/types/clientConfig.d.ts +13 -13
- package/dist/runtime/types/dialogManager.d.ts +35 -35
- package/dist/runtime/types/formDialog.d.ts +5 -5
- package/dist/runtime/types/graphqlOperation.d.ts +23 -23
- package/dist/runtime/types/menu.d.ts +31 -31
- package/dist/runtime/types/modules.d.ts +7 -7
- package/dist/runtime/types/permission.d.ts +13 -13
- package/dist/runtime/utils/asset.d.ts +2 -0
- package/dist/runtime/utils/asset.js +49 -0
- package/package.json +131 -122
- package/scripts/enrich-vue-docs-from-ai.mjs +197 -0
- package/scripts/generate-ai-summary.mjs +321 -0
- package/scripts/generate-composables-md.mjs +129 -0
- package/scripts/postInstall.cjs +70 -70
- package/templates/.codegen/codegen.ts +32 -32
- package/templates/.codegen/plugin-schema-object.js +161 -161
- package/templates/public/tesseract/mrz.traineddata.gz +0 -0
- package/templates/public/tesseract/ocrb.traineddata.gz +0 -0
|
@@ -1,338 +1,350 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
scannerOptions.value.paperSource === PAPER_SOURCE.Duplex
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
<v-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* DeviceScanner bridges UI actions with host-agent hardware/device operations and emits runtime scan/read results.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import { computed, ref, watch, reactive } from 'vue'
|
|
7
|
+
import type { Base64File } from '../../composables/assetFile'
|
|
8
|
+
import { useAlert } from '../../composables/alert'
|
|
9
|
+
import {
|
|
10
|
+
useHostAgent,
|
|
11
|
+
PAPER_SOURCE,
|
|
12
|
+
BIT_DEPTH,
|
|
13
|
+
type ScanRequest,
|
|
14
|
+
type ScanResult,
|
|
15
|
+
} from '../../composables/hostAgent'
|
|
16
|
+
|
|
17
|
+
const alert = useAlert()
|
|
18
|
+
const host = useHostAgent()
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Custom events emitted by DeviceScanner.
|
|
22
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
23
|
+
*/
|
|
24
|
+
const emit = defineEmits<{
|
|
25
|
+
(e: 'scan', files: Base64File[]): void
|
|
26
|
+
}>()
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
feeder?: boolean // Uses ADF feeder mode instead of flatbed scanning.
|
|
30
|
+
duplex?: boolean // Scans both sides of pages when feeder mode supports duplex.
|
|
31
|
+
dpi?: number // Scan resolution in DPI for image quality and file size tradeoff.
|
|
32
|
+
quality?: number // Compression/quality value used when exporting scanned images.
|
|
33
|
+
/** UI string: 'color' | 'grey' | 'bw' */
|
|
34
|
+
color?: string // Vuetify color name applied to the visual element.
|
|
35
|
+
maxSize?: number // Maximum allowed output size (MB) before upload is blocked.
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Public props accepted by DeviceScanner.
|
|
40
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
41
|
+
*/
|
|
42
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
43
|
+
feeder: false,
|
|
44
|
+
duplex: false,
|
|
45
|
+
dpi: 200,
|
|
46
|
+
quality: 80,
|
|
47
|
+
color: 'color',
|
|
48
|
+
maxSize: 5,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* ✅ Model directly matches HostAgent ScanRequest
|
|
53
|
+
*/
|
|
54
|
+
const scannerOptions = ref<ScanRequest>({
|
|
55
|
+
dpi: props.dpi,
|
|
56
|
+
quality: props.quality,
|
|
57
|
+
paperSource: props.duplex
|
|
58
|
+
? PAPER_SOURCE.Duplex
|
|
59
|
+
: props.feeder
|
|
60
|
+
? PAPER_SOURCE.Feeder
|
|
61
|
+
: PAPER_SOURCE.Flatbed,
|
|
62
|
+
bitDepth:
|
|
63
|
+
props.color === 'bw'
|
|
64
|
+
? BIT_DEPTH.BlackAndWhite
|
|
65
|
+
: props.color === 'grey'
|
|
66
|
+
? BIT_DEPTH.Grayscale
|
|
67
|
+
: BIT_DEPTH.Color,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
watch(() => props.dpi, () => {
|
|
71
|
+
if (props.dpi) scannerOptions.value.dpi = props.dpi
|
|
72
|
+
})
|
|
73
|
+
watch(() => props.quality, () => {
|
|
74
|
+
if (props.quality) scannerOptions.value.quality = props.quality
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Mapping
|
|
79
|
+
* - Flatbed => feeder=false, duplex=false
|
|
80
|
+
* - Feeder => feeder=true, duplex=false
|
|
81
|
+
* - Duplex => feeder=true, duplex=true
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
// duplex is true only when paperSource is Duplex
|
|
85
|
+
const uiDuplex = computed<boolean>({
|
|
86
|
+
get: () => scannerOptions.value.paperSource === PAPER_SOURCE.Duplex,
|
|
87
|
+
set(v) {
|
|
88
|
+
if (v) {
|
|
89
|
+
// duplex ON => MUST be feeder => paperSource Duplex
|
|
90
|
+
scannerOptions.value.paperSource = PAPER_SOURCE.Duplex
|
|
91
|
+
} else {
|
|
92
|
+
// duplex OFF => if currently duplex, fall back to Feeder (because feeder is still on)
|
|
93
|
+
// user can still later turn feeder off to go Flatbed
|
|
94
|
+
if (scannerOptions.value.paperSource === PAPER_SOURCE.Duplex) {
|
|
95
|
+
scannerOptions.value.paperSource = PAPER_SOURCE.Feeder
|
|
96
|
+
}
|
|
97
|
+
// if already Feeder/Flatbed, do nothing
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// feeder is true when paperSource is Feeder or Duplex
|
|
103
|
+
const uiFeeder = computed<boolean>({
|
|
104
|
+
get: () =>
|
|
105
|
+
scannerOptions.value.paperSource === PAPER_SOURCE.Feeder ||
|
|
106
|
+
scannerOptions.value.paperSource === PAPER_SOURCE.Duplex,
|
|
107
|
+
set(v) {
|
|
108
|
+
if (v) {
|
|
109
|
+
// feeder ON => keep duplex state if duplex already on, else go Feeder
|
|
110
|
+
scannerOptions.value.paperSource = uiDuplex.value ? PAPER_SOURCE.Duplex : PAPER_SOURCE.Feeder
|
|
111
|
+
} else {
|
|
112
|
+
// feeder OFF => duplex must be OFF too => go Flatbed
|
|
113
|
+
scannerOptions.value.paperSource = PAPER_SOURCE.Flatbed
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
const uiColor = computed<string>({
|
|
120
|
+
get() {
|
|
121
|
+
const b = scannerOptions.value.bitDepth
|
|
122
|
+
if (b === BIT_DEPTH.BlackAndWhite) return 'bw'
|
|
123
|
+
if (b === BIT_DEPTH.Grayscale) return 'grey'
|
|
124
|
+
return 'color'
|
|
125
|
+
},
|
|
126
|
+
set(v) {
|
|
127
|
+
const vv = (v || 'color').toLowerCase()
|
|
128
|
+
scannerOptions.value.bitDepth =
|
|
129
|
+
vv === 'bw'
|
|
130
|
+
? BIT_DEPTH.BlackAndWhite
|
|
131
|
+
: vv === 'grey' || vv === 'gray'
|
|
132
|
+
? BIT_DEPTH.Grayscale
|
|
133
|
+
: BIT_DEPTH.Color
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
watch(() => props.duplex, () => {
|
|
138
|
+
uiDuplex.value = props.duplex
|
|
139
|
+
})
|
|
140
|
+
watch(() => props.feeder, () => {
|
|
141
|
+
uiFeeder.value = props.feeder
|
|
142
|
+
})
|
|
143
|
+
watch(() => props.color, () => {
|
|
144
|
+
if (props.color) uiColor.value = props.color
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
function scanResultsToBase64Files(results: ScanResult[]): Base64File[] {
|
|
148
|
+
return results.map((r, idx) => ({
|
|
149
|
+
fileName: `scan_${idx + 1}.png`,
|
|
150
|
+
base64String: r.base64String.startsWith('data:')
|
|
151
|
+
? r.base64String
|
|
152
|
+
: `data:image/png;base64,${r.base64String}`,
|
|
153
|
+
}))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const isScanning = ref(false)
|
|
157
|
+
|
|
158
|
+
async function performScan() {
|
|
159
|
+
try {
|
|
160
|
+
isScanning.value = true
|
|
161
|
+
const results = await host.scan(scannerOptions.value)
|
|
162
|
+
emit('scan', scanResultsToBase64Files(results || []))
|
|
163
|
+
} catch (e: any) {
|
|
164
|
+
const msg = e?.data?.detail || e?.data?.title || e?.message || 'Scan failed'
|
|
165
|
+
alert?.addAlert({ message: msg, alertType: 'error' })
|
|
166
|
+
} finally {
|
|
167
|
+
isScanning.value = false
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* ✅ Upload helpers (restored)
|
|
173
|
+
*/
|
|
174
|
+
function fileToBase64(file: File) {
|
|
175
|
+
const maxSize = props.maxSize * 1048576
|
|
176
|
+
|
|
177
|
+
return new Promise<Base64File>((resolve, reject) => {
|
|
178
|
+
if (file.size > maxSize) reject(`File (${file.name}) size exceeds the ${props.maxSize} MB limit.`)
|
|
179
|
+
|
|
180
|
+
const reader = new FileReader()
|
|
181
|
+
reader.onload = (event) => {
|
|
182
|
+
resolve({ fileName: file.name, base64String: event.target?.result as string })
|
|
183
|
+
}
|
|
184
|
+
reader.onerror = reject
|
|
185
|
+
reader.readAsDataURL(file)
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function performFileUpload(files: File | File[] | undefined) {
|
|
190
|
+
if (!files) return
|
|
191
|
+
|
|
192
|
+
const allFiles = Array.isArray(files) ? files : [files]
|
|
193
|
+
const base64Promises = allFiles.map(fileToBase64)
|
|
194
|
+
|
|
195
|
+
Promise.all(base64Promises)
|
|
196
|
+
.then((base64Strings) => emit('scan', base64Strings))
|
|
197
|
+
.catch((error) => alert?.addAlert({ message: String(error), alertType: 'error' }))
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Slot props grouping (so you can do ops.performScan etc.)
|
|
202
|
+
*/
|
|
203
|
+
const scannerOptionsProps = computed(() => scannerOptions.value)
|
|
204
|
+
const configHelper = reactive({
|
|
205
|
+
get feeder() { return uiFeeder.value },
|
|
206
|
+
set feeder(v: boolean) { uiFeeder.value = v },
|
|
207
|
+
|
|
208
|
+
get duplex() { return uiDuplex.value },
|
|
209
|
+
set duplex(v: boolean) { uiDuplex.value = v },
|
|
210
|
+
|
|
211
|
+
get color() { return uiColor.value },
|
|
212
|
+
set color(v: string) { uiColor.value = v },
|
|
213
|
+
})
|
|
214
|
+
const operation = { performScan, performFileUpload, fileToBase64}
|
|
215
|
+
</script>
|
|
216
|
+
|
|
217
|
+
<template>
|
|
218
|
+
<div class="scanner">
|
|
219
|
+
<slot :scannerOptions="scannerOptionsProps" :configHelper="configHelper" :operation="operation" :isScanning="isScanning">
|
|
220
|
+
<v-card>
|
|
221
|
+
<v-card-text>
|
|
222
|
+
<form-pad v-model="scannerOptions">
|
|
223
|
+
<template #default>
|
|
224
|
+
<!-- Upload -->
|
|
225
|
+
<v-row>
|
|
226
|
+
<v-col cols="12">
|
|
227
|
+
<p>Upload a New Files</p>
|
|
228
|
+
</v-col>
|
|
229
|
+
|
|
230
|
+
<v-col cols="12">
|
|
231
|
+
<file-btn
|
|
232
|
+
@update:modelValue="performFileUpload"
|
|
233
|
+
block
|
|
234
|
+
multiple
|
|
235
|
+
variant="tonal"
|
|
236
|
+
rounded="xl"
|
|
237
|
+
>
|
|
238
|
+
<v-icon>mdi mdi-tray-arrow-up</v-icon>
|
|
239
|
+
Upload file
|
|
240
|
+
</file-btn>
|
|
241
|
+
</v-col>
|
|
242
|
+
</v-row>
|
|
243
|
+
|
|
244
|
+
<!-- Divider -->
|
|
245
|
+
<v-row>
|
|
246
|
+
<v-col cols="12" style="text-align: center;">
|
|
247
|
+
<p>Or</p>
|
|
248
|
+
</v-col>
|
|
249
|
+
</v-row>
|
|
250
|
+
|
|
251
|
+
<!-- Scan title -->
|
|
252
|
+
<v-row>
|
|
253
|
+
<v-col cols="12">
|
|
254
|
+
<p>Scan a New Files</p>
|
|
255
|
+
</v-col>
|
|
256
|
+
</v-row>
|
|
257
|
+
|
|
258
|
+
<!-- Scan controls -->
|
|
259
|
+
<v-row>
|
|
260
|
+
<v-col cols="12" class="py-0">
|
|
261
|
+
<v-switch
|
|
262
|
+
color="primary"
|
|
263
|
+
v-model="uiFeeder"
|
|
264
|
+
label="Enable feeder"
|
|
265
|
+
hide-details
|
|
266
|
+
density="compact"
|
|
267
|
+
/>
|
|
268
|
+
</v-col>
|
|
269
|
+
|
|
270
|
+
<v-col cols="12" class="py-0">
|
|
271
|
+
<v-switch
|
|
272
|
+
color="primary"
|
|
273
|
+
v-model="uiDuplex"
|
|
274
|
+
label="Enable duplex"
|
|
275
|
+
hide-details
|
|
276
|
+
density="compact"
|
|
277
|
+
/>
|
|
278
|
+
</v-col>
|
|
279
|
+
|
|
280
|
+
<v-col cols="12">
|
|
281
|
+
<p>choose color mode</p>
|
|
282
|
+
|
|
283
|
+
<v-btn-toggle
|
|
284
|
+
v-model="uiColor"
|
|
285
|
+
variant="tonal"
|
|
286
|
+
mandatory
|
|
287
|
+
>
|
|
288
|
+
<v-btn value="color" prepend-icon="mdi mdi-palette-outline">
|
|
289
|
+
<span>Color</span>
|
|
290
|
+
</v-btn>
|
|
291
|
+
|
|
292
|
+
<v-btn value="grey" prepend-icon="mdi mdi-circle">
|
|
293
|
+
<span>Grayscale</span>
|
|
294
|
+
</v-btn>
|
|
295
|
+
|
|
296
|
+
<v-btn value="bw" prepend-icon="mdi mdi-circle-half-full">
|
|
297
|
+
<span>Black&White</span>
|
|
298
|
+
</v-btn>
|
|
299
|
+
</v-btn-toggle>
|
|
300
|
+
</v-col>
|
|
301
|
+
|
|
302
|
+
<v-col cols="12">
|
|
303
|
+
<v-slider
|
|
304
|
+
v-model="scannerOptions.dpi"
|
|
305
|
+
:max="200"
|
|
306
|
+
:min="100"
|
|
307
|
+
:ticks="{ 100: 'Low', 150: 'Standard', 200: 'High' }"
|
|
308
|
+
show-ticks="always"
|
|
309
|
+
step="50"
|
|
310
|
+
tick-size="4"
|
|
311
|
+
label="Resolution"
|
|
312
|
+
hide-details
|
|
313
|
+
/>
|
|
314
|
+
</v-col>
|
|
315
|
+
|
|
316
|
+
<v-col cols="12">
|
|
317
|
+
<v-slider
|
|
318
|
+
v-model="scannerOptions.quality"
|
|
319
|
+
:max="100"
|
|
320
|
+
:min="60"
|
|
321
|
+
:ticks="{ 60: 'Low', 80: 'Standard', 100: 'High' }"
|
|
322
|
+
show-ticks="always"
|
|
323
|
+
step="5"
|
|
324
|
+
tick-size="4"
|
|
325
|
+
label="Quality"
|
|
326
|
+
hide-details
|
|
327
|
+
/>
|
|
328
|
+
</v-col>
|
|
329
|
+
|
|
330
|
+
<v-col cols="12">
|
|
331
|
+
<v-btn
|
|
332
|
+
@click="performScan"
|
|
333
|
+
:loading="isScanning"
|
|
334
|
+
:disabled="isScanning"
|
|
335
|
+
block
|
|
336
|
+
variant="tonal"
|
|
337
|
+
rounded="xl"
|
|
338
|
+
>
|
|
339
|
+
<v-icon>mdi mdi-scanner</v-icon>
|
|
340
|
+
Start Scanning
|
|
341
|
+
</v-btn>
|
|
342
|
+
</v-col>
|
|
343
|
+
</v-row>
|
|
344
|
+
</template>
|
|
345
|
+
</form-pad>
|
|
346
|
+
</v-card-text>
|
|
347
|
+
</v-card>
|
|
348
|
+
</slot>
|
|
349
|
+
</div>
|
|
350
|
+
</template>
|