@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
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* MrzReader captures passport/ID images from camera or upload, runs OCR + MRZ parsing,
|
|
4
|
+
* and emits normalized MRZ data once checksum validation passes.
|
|
5
|
+
*/
|
|
6
|
+
import { computed, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue'
|
|
7
|
+
import { useDevicesList, useUserMedia } from '@vueuse/core'
|
|
8
|
+
import { useAlert } from '../composables/alert'
|
|
9
|
+
import { useMrzReader, type MrzResult } from '../composables/useMrzReader'
|
|
10
|
+
|
|
11
|
+
const mrzReaderControl = ref<number | null>(null)
|
|
12
|
+
|
|
13
|
+
const alert = useAlert()
|
|
14
|
+
const isLoading = ref(false)
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
intervalMs?: number // Scan interval (ms) for periodic camera-frame OCR attempts.
|
|
18
|
+
scaleFactor?: number // Upscale factor applied before OCR to improve small-text readability.
|
|
19
|
+
useOpenCv?: boolean // Enables OpenCV-assisted region detection before OCR.
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
23
|
+
intervalMs: 900,
|
|
24
|
+
scaleFactor: 2,
|
|
25
|
+
useOpenCv: true,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits<{
|
|
29
|
+
(event: 'decode', value: MrzResult): void // Emitted when a valid MRZ payload is parsed.
|
|
30
|
+
(event: 'error', error: string | unknown): void // Emitted when camera/OCR/parsing fails.
|
|
31
|
+
}>()
|
|
32
|
+
|
|
33
|
+
const videoScreen = ref<HTMLVideoElement>()
|
|
34
|
+
|
|
35
|
+
const currentCameraId = ref<ConstrainDOMString | undefined>()
|
|
36
|
+
const { videoInputs: cameras } = useDevicesList({
|
|
37
|
+
requestPermissions: true,
|
|
38
|
+
constraints: { audio: false, video: true },
|
|
39
|
+
onUpdated() {
|
|
40
|
+
if (!cameras.value.find(camera => camera.deviceId === currentCameraId.value))
|
|
41
|
+
currentCameraId.value = cameras.value[0]?.deviceId
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const hasCamera = computed(() => !!currentCameraId.value)
|
|
46
|
+
|
|
47
|
+
const { stream, start: cameraStart, stop: cameraStop, enabled: cameraEnabled } = useUserMedia({
|
|
48
|
+
constraints: { video: { deviceId: currentCameraId.value } },
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
watchEffect(() => {
|
|
52
|
+
if (videoScreen.value)
|
|
53
|
+
videoScreen.value.srcObject = stream.value ?? null
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const mrzReader = useMrzReader({
|
|
57
|
+
scaleFactor: props.scaleFactor,
|
|
58
|
+
useOpenCv: props.useOpenCv,
|
|
59
|
+
lang: 'ocrb',
|
|
60
|
+
langPath: '/tesseract/',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
async function scanOnce() {
|
|
64
|
+
if (!videoScreen.value || !cameraEnabled.value) return
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const parsed = await mrzReader.decodeFromVideoElement(videoScreen.value)
|
|
68
|
+
if (parsed) {
|
|
69
|
+
emit('decode', parsed)
|
|
70
|
+
stopCamera()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
emit('error', err)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function startCamera() {
|
|
79
|
+
if (cameraEnabled.value) return
|
|
80
|
+
|
|
81
|
+
isLoading.value = true
|
|
82
|
+
cameraStart()
|
|
83
|
+
.then(() => {
|
|
84
|
+
if (mrzReaderControl.value) {
|
|
85
|
+
window.clearInterval(mrzReaderControl.value)
|
|
86
|
+
mrzReaderControl.value = null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
mrzReaderControl.value = window.setInterval(() => {
|
|
90
|
+
void scanOnce()
|
|
91
|
+
}, props.intervalMs)
|
|
92
|
+
})
|
|
93
|
+
.catch((err) => {
|
|
94
|
+
emit('error', err)
|
|
95
|
+
})
|
|
96
|
+
.finally(() => {
|
|
97
|
+
isLoading.value = false
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function stopCamera() {
|
|
102
|
+
if (mrzReaderControl.value) {
|
|
103
|
+
window.clearInterval(mrzReaderControl.value)
|
|
104
|
+
mrzReaderControl.value = null
|
|
105
|
+
}
|
|
106
|
+
if (cameraEnabled.value)
|
|
107
|
+
cameraStop()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function scanImageFile(selectedFile: File | File[] | undefined) {
|
|
111
|
+
if (!selectedFile) {
|
|
112
|
+
alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const file = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
|
|
117
|
+
|
|
118
|
+
void mrzReader.decodeFromImageFile(file)
|
|
119
|
+
.then((parsed) => {
|
|
120
|
+
if (parsed)
|
|
121
|
+
emit('decode', parsed)
|
|
122
|
+
else
|
|
123
|
+
alert?.addAlert({ message: 'MRZ not found or checksum invalid.', alertType: 'warning' })
|
|
124
|
+
})
|
|
125
|
+
.catch((err) => {
|
|
126
|
+
emit('error', err)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
onMounted(() => {
|
|
131
|
+
void mrzReader.ensureOpenCvReady()
|
|
132
|
+
startCamera()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
onBeforeUnmount(() => {
|
|
136
|
+
stopCamera()
|
|
137
|
+
})
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<template>
|
|
141
|
+
<v-card flat>
|
|
142
|
+
<v-card-text class="d-flex justify-center" v-if="isLoading">
|
|
143
|
+
<v-progress-circular indeterminate />
|
|
144
|
+
</v-card-text>
|
|
145
|
+
<v-card-text v-else>
|
|
146
|
+
<v-col v-if="hasCamera">
|
|
147
|
+
<div style="position: relative; display: inline-block; width: 100%;" :style="{ maxWidth: '1024px' }">
|
|
148
|
+
<video autoplay ref="videoScreen" width="100%" :style="{ maxWidth: '1024px' }"></video>
|
|
149
|
+
<div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
|
|
150
|
+
<FileBtn
|
|
151
|
+
accept="image/*"
|
|
152
|
+
icon="mdi mdi-image-plus"
|
|
153
|
+
icon-only
|
|
154
|
+
@update:model-value="scanImageFile"
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</v-col>
|
|
159
|
+
<v-col v-else>
|
|
160
|
+
<FileBtn
|
|
161
|
+
accept="image/*"
|
|
162
|
+
text="Upload Image"
|
|
163
|
+
@update:model-value="scanImageFile"
|
|
164
|
+
/>
|
|
165
|
+
</v-col>
|
|
166
|
+
</v-card-text>
|
|
167
|
+
</v-card>
|
|
168
|
+
</template>
|
|
@@ -1,61 +1,69 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SplitterPanel provides a resizable split layout panel for flexible, user-adjustable page composition.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import { ref } from 'vue'
|
|
7
|
+
import { VCard } from 'vuetify/components/VCard'
|
|
8
|
+
|
|
9
|
+
const isResizing = ref(false)
|
|
10
|
+
const pane1Width = ref('50%')
|
|
11
|
+
const containerRef = ref<HTMLElement>()
|
|
12
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VCard['$props']> {
|
|
13
|
+
height?: number | string // Fixed or minimum height applied to the component container.
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Public props accepted by SplitterPanel.
|
|
17
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
18
|
+
*/
|
|
19
|
+
const props = defineProps<Props>()
|
|
20
|
+
|
|
21
|
+
const startResize = () => {
|
|
22
|
+
isResizing.value = true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const stopResize = () => {
|
|
26
|
+
isResizing.value = false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const resize = (event: MouseEvent) => {
|
|
30
|
+
if (isResizing.value && containerRef.value) {
|
|
31
|
+
const containerRect = containerRef.value.getBoundingClientRect()
|
|
32
|
+
const newWidth = event.clientX - containerRect.left
|
|
33
|
+
pane1Width.value = `${(newWidth / containerRect.width) * 100}%`
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<v-card :="$attrs">
|
|
40
|
+
<v-sheet
|
|
41
|
+
border
|
|
42
|
+
:height="props.height"
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
ref="containerRef"
|
|
46
|
+
class="d-flex"
|
|
47
|
+
@mouseup="stopResize"
|
|
48
|
+
@mousemove="resize"
|
|
49
|
+
>
|
|
50
|
+
<v-sheet :width="pane1Width">
|
|
51
|
+
<slot name="left" />
|
|
52
|
+
</v-sheet>
|
|
53
|
+
<v-divider
|
|
54
|
+
:thickness="3"
|
|
55
|
+
vertical
|
|
56
|
+
class="cursor-move"
|
|
57
|
+
@mousedown="startResize"
|
|
58
|
+
/>
|
|
59
|
+
<v-sheet :width="`calc(100% - ${pane1Width})`">
|
|
60
|
+
<slot name="right" />
|
|
61
|
+
</v-sheet>
|
|
62
|
+
</div>
|
|
63
|
+
</v-sheet>
|
|
64
|
+
</v-card>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
59
67
|
<style scoped>
|
|
60
68
|
|
|
61
|
-
</style>
|
|
69
|
+
</style>
|
|
@@ -1,31 +1,39 @@
|
|
|
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
|
-
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* TabsGroup renders grouped tab content and keeps tab state consistent across controlled and uncontrolled usage.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import {VTabs} from 'vuetify/components'
|
|
7
|
+
|
|
8
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VTabs['$props']> {
|
|
9
|
+
flat?: boolean // Uses a flatter tab style with reduced elevation and borders.
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
defineOptions({
|
|
13
|
+
inheritAttrs: false,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Public props accepted by TabsGroup.
|
|
18
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
19
|
+
*/
|
|
20
|
+
const props = defineProps<Props>()
|
|
21
|
+
const currentTab = defineModel<string|number>()
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<v-card :flat="props.flat">
|
|
26
|
+
<v-tabs
|
|
27
|
+
v-bind="$attrs"
|
|
28
|
+
v-model="currentTab"
|
|
29
|
+
show-arrows
|
|
30
|
+
>
|
|
31
|
+
<slot name="tabs" />
|
|
32
|
+
</v-tabs>
|
|
33
|
+
<v-card-text>
|
|
34
|
+
<v-tabs-window v-model="currentTab">
|
|
35
|
+
<slot name="items" />
|
|
36
|
+
</v-tabs-window>
|
|
37
|
+
</v-card-text>
|
|
38
|
+
</v-card>
|
|
39
|
+
</template>
|
|
@@ -1,54 +1,66 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
emit('
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* TextBarcode renders barcode output from plain text and updates when bound values change.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import {ref, watch} from 'vue'
|
|
7
|
+
import {useAlert} from '../composables/alert'
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
modelValue?: string // Bound value for v-model synchronization with the parent component.
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Public props accepted by TextBarcode.
|
|
15
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
16
|
+
*/
|
|
17
|
+
const props = defineProps<Props>()
|
|
18
|
+
/**
|
|
19
|
+
* Custom events emitted by TextBarcode.
|
|
20
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
21
|
+
*/
|
|
22
|
+
const emit = defineEmits<{
|
|
23
|
+
(event: 'update:modelValue', value: string | undefined): void
|
|
24
|
+
(event: 'decode', value: string | undefined): void
|
|
25
|
+
}>()
|
|
26
|
+
const alert = useAlert()
|
|
27
|
+
|
|
28
|
+
const scanCode = ref<boolean>(false)
|
|
29
|
+
const currentValue = ref<string>()
|
|
30
|
+
|
|
31
|
+
const handleData = (data: string) => {
|
|
32
|
+
currentValue.value = data
|
|
33
|
+
scanCode.value = false
|
|
34
|
+
emit('decode',data)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleError = (error: string | unknown) => {
|
|
38
|
+
alert?.addAlert({ message: error as string, alertType: 'error' })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
watch(() => props.modelValue, () => {
|
|
42
|
+
currentValue.value = props.modelValue
|
|
43
|
+
}, { immediate: true })
|
|
44
|
+
|
|
45
|
+
watch(currentValue, (newValue) => {
|
|
46
|
+
emit('update:modelValue', newValue)
|
|
47
|
+
})
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<v-text-field
|
|
52
|
+
v-model="currentValue"
|
|
53
|
+
v-bind="$attrs"
|
|
54
|
+
append-inner-icon="mdi mdi-qrcode-scan"
|
|
55
|
+
@click:append-inner="scanCode = true"
|
|
56
|
+
/>
|
|
57
|
+
<v-dialog
|
|
58
|
+
v-model="scanCode"
|
|
59
|
+
width="auto"
|
|
60
|
+
>
|
|
61
|
+
<BarcodeReader
|
|
62
|
+
@decode="handleData"
|
|
63
|
+
@error="handleError"
|
|
64
|
+
/>
|
|
65
|
+
</v-dialog>
|
|
66
|
+
</template>
|