@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,83 +1,95 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* DeviceIdCardButton 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, useAttrs } from 'vue'
|
|
7
|
+
import { VBtn } from 'vuetify/components/VBtn'
|
|
8
|
+
import { useAlert } from '../../composables/alert'
|
|
9
|
+
import { useHostAgent, type PatientRegisterPayload } from '../../composables/hostAgent'
|
|
10
|
+
|
|
11
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
|
|
12
|
+
/** If true -> call /idcard/infoAndPhoto, else /idcard/info */
|
|
13
|
+
withPhoto?: boolean // Includes portrait/photo data in ID card read results.
|
|
14
|
+
/** Optional reader name/query string */
|
|
15
|
+
reader?: string | null // Preferred hardware reader identifier to connect or switch to.
|
|
16
|
+
/** Auto-disable button while reading */
|
|
17
|
+
disableWhileLoading?: boolean // Disables the trigger button while a read operation is in progress.
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Public props accepted by DeviceIdCardButton.
|
|
22
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
23
|
+
*/
|
|
24
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
25
|
+
withPhoto: false,
|
|
26
|
+
reader: null,
|
|
27
|
+
disableWhileLoading: true,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Custom events emitted by DeviceIdCardButton.
|
|
32
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
33
|
+
*/
|
|
34
|
+
const emit = defineEmits<{
|
|
35
|
+
(e: 'read', payload: PatientRegisterPayload): void
|
|
36
|
+
(e: 'error', err: unknown): void
|
|
37
|
+
(e: 'loading', loading: boolean): void
|
|
38
|
+
}>()
|
|
39
|
+
|
|
40
|
+
const attrs = useAttrs()
|
|
41
|
+
const host = useHostAgent()
|
|
42
|
+
|
|
43
|
+
const loading = ref(false)
|
|
44
|
+
const disabled = computed(() => (props.disableWhileLoading ? loading.value : false))
|
|
45
|
+
|
|
46
|
+
async function onClick(ev: MouseEvent) {
|
|
47
|
+
// allow parent to prevent if they attach their own click handler and call preventDefault
|
|
48
|
+
if (ev.defaultPrevented) return
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
loading.value = true
|
|
52
|
+
emit('loading', true)
|
|
53
|
+
|
|
54
|
+
const payload = props.withPhoto
|
|
55
|
+
? await host.getIdCardInfoAndPhoto(props.reader ?? undefined)
|
|
56
|
+
: await host.getIdCardInfo(props.reader ?? undefined)
|
|
57
|
+
|
|
58
|
+
emit('read', payload)
|
|
59
|
+
} catch (e: any) {
|
|
60
|
+
emit('error', e)
|
|
61
|
+
|
|
62
|
+
const msg = e?.data?.detail || e?.data?.title || e?.message || 'ID card read failed'
|
|
63
|
+
useAlert()?.addAlert({ message: msg, alertType: 'error' })
|
|
64
|
+
} finally {
|
|
65
|
+
loading.value = false
|
|
66
|
+
emit('loading', false)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<!--
|
|
73
|
+
Extends Vuetify VBtn:
|
|
74
|
+
- All unknown attrs (size, class, etc.) pass through via v-bind="attrs"
|
|
75
|
+
- You can override content via default slot; label+icon is fallback
|
|
76
|
+
-->
|
|
77
|
+
<v-btn
|
|
78
|
+
v-bind="attrs"
|
|
79
|
+
class="idcard-btn"
|
|
80
|
+
:loading="loading"
|
|
81
|
+
:disabled="disabled || (attrs as any).disabled"
|
|
82
|
+
@click="onClick"
|
|
83
|
+
>
|
|
84
|
+
<template
|
|
85
|
+
v-for="(_, name, index) in ($slots as {})"
|
|
86
|
+
:key="index"
|
|
87
|
+
#[name]="slotData"
|
|
88
|
+
>
|
|
89
|
+
<slot
|
|
90
|
+
:name="name"
|
|
91
|
+
v-bind="((slotData || {}) as object)"
|
|
92
|
+
/>
|
|
93
|
+
</template>
|
|
94
|
+
</v-btn>
|
|
95
|
+
</template>
|
|
@@ -1,195 +1,207 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
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
|
-
|
|
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
|
-
if
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
case '
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* DeviceIdCardWebSocket 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 { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
7
|
+
import { type IdCardWsServerMessage, type PatientRegisterPayload } from '../../composables/hostAgent'
|
|
8
|
+
import { useHostAgentWs } from "../../composables/hostAgentWs";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
/** Subscribe withPhoto=true => server will push payload that includes photo when available */
|
|
12
|
+
withPhoto?: boolean // Includes portrait/photo data in ID card read results.
|
|
13
|
+
/** Optional reader name; null/undefined means "any/default" */
|
|
14
|
+
reader?: string | null // Preferred hardware reader identifier to connect or switch to.
|
|
15
|
+
/** Auto-connect on mount (default true) */
|
|
16
|
+
autoConnect?: boolean // Connects to the device WebSocket automatically on mount.
|
|
17
|
+
/** Auto-subscribe after connect (default true) */
|
|
18
|
+
autoSubscribe?: boolean // Subscribes to live reader events automatically after connect.
|
|
19
|
+
/** If true, try to reconnect when socket closes unexpectedly */
|
|
20
|
+
autoReconnect?: boolean // Retries connection automatically when the socket closes unexpectedly.
|
|
21
|
+
/** Reconnect delay in ms */
|
|
22
|
+
reconnectDelayMs?: number // Delay in milliseconds before each reconnect attempt.
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Public props accepted by DeviceIdCardWebSocket.
|
|
27
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
28
|
+
*/
|
|
29
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
30
|
+
withPhoto: false,
|
|
31
|
+
reader: null,
|
|
32
|
+
autoConnect: true,
|
|
33
|
+
autoSubscribe: true,
|
|
34
|
+
autoReconnect: false,
|
|
35
|
+
reconnectDelayMs: 1000,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Custom events emitted by DeviceIdCardWebSocket.
|
|
40
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
41
|
+
*/
|
|
42
|
+
const emit = defineEmits<{
|
|
43
|
+
(e: 'connected'): void
|
|
44
|
+
(e: 'disconnected', info?: { code?: number; reason?: string }): void
|
|
45
|
+
(e: 'subscribed', info?: { reader?: string | null; withPhoto?: boolean }): void
|
|
46
|
+
(e: 'unsubscribed'): void
|
|
47
|
+
(e: 'inserted', payload: PatientRegisterPayload): void
|
|
48
|
+
(e: 'removed'): void
|
|
49
|
+
(e: 'error', err: unknown): void
|
|
50
|
+
(e: 'message', msg: IdCardWsServerMessage): void
|
|
51
|
+
}>()
|
|
52
|
+
|
|
53
|
+
const host = useHostAgentWs()
|
|
54
|
+
|
|
55
|
+
const reconnectTimer = ref<ReturnType<typeof setTimeout> | null>(null)
|
|
56
|
+
const mounted = ref(false)
|
|
57
|
+
const manuallyDisconnected = ref(false)
|
|
58
|
+
|
|
59
|
+
function clearReconnect() {
|
|
60
|
+
if (reconnectTimer.value) {
|
|
61
|
+
clearTimeout(reconnectTimer.value)
|
|
62
|
+
reconnectTimer.value = null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function connectAndSubscribe() {
|
|
67
|
+
manuallyDisconnected.value = false
|
|
68
|
+
host.connectIdCardWs()
|
|
69
|
+
|
|
70
|
+
// subscribe immediately; safe because connectIdCardWs sets handlers then WS opens soon
|
|
71
|
+
// but we must wait until OPEN to send; so we subscribe on "open" event in handler below.
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function safeDisconnect() {
|
|
75
|
+
manuallyDisconnected.value = true
|
|
76
|
+
clearReconnect()
|
|
77
|
+
try {
|
|
78
|
+
// best-effort unsubscribe (only if connected)
|
|
79
|
+
if (host.wsConnected.value) {
|
|
80
|
+
try { host.unsubscribeIdCardWs() } catch { /* ignore */ }
|
|
81
|
+
}
|
|
82
|
+
} catch { /* ignore */ }
|
|
83
|
+
host.disconnectIdCardWs()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const off = host.onIdCardWsEvent((ev) => {
|
|
87
|
+
if (ev.type === 'open') {
|
|
88
|
+
emit('connected')
|
|
89
|
+
|
|
90
|
+
if (props.autoSubscribe) {
|
|
91
|
+
try {
|
|
92
|
+
host.subscribeIdCardWs({ reader: props.reader ?? null, withPhoto: props.withPhoto })
|
|
93
|
+
} catch (e) {
|
|
94
|
+
emit('error', e)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (ev.type === 'close') {
|
|
101
|
+
emit('disconnected', { code: ev.code, reason: ev.reason })
|
|
102
|
+
|
|
103
|
+
if (props.autoReconnect && mounted.value && !manuallyDisconnected.value) {
|
|
104
|
+
clearReconnect()
|
|
105
|
+
reconnectTimer.value = setTimeout(() => {
|
|
106
|
+
try {
|
|
107
|
+
connectAndSubscribe()
|
|
108
|
+
} catch (e) {
|
|
109
|
+
emit('error', e)
|
|
110
|
+
}
|
|
111
|
+
}, props.reconnectDelayMs)
|
|
112
|
+
}
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (ev.type === 'error') {
|
|
117
|
+
emit('error', ev.error)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (ev.type === 'message') {
|
|
122
|
+
const msg = ev.data
|
|
123
|
+
emit('message', msg)
|
|
124
|
+
|
|
125
|
+
switch (msg.type) {
|
|
126
|
+
case 'monitor.subscribed':
|
|
127
|
+
if (msg.ok) emit('subscribed', { reader: msg.reader ?? null, withPhoto: !!msg.withPhoto })
|
|
128
|
+
break
|
|
129
|
+
case 'monitor.unsubscribed':
|
|
130
|
+
emit('unsubscribed')
|
|
131
|
+
break
|
|
132
|
+
case 'card.inserted':
|
|
133
|
+
emit('inserted', msg.payload)
|
|
134
|
+
break
|
|
135
|
+
case 'card.removed':
|
|
136
|
+
emit('removed')
|
|
137
|
+
break
|
|
138
|
+
case 'card.error':
|
|
139
|
+
case 'error':
|
|
140
|
+
emit('error', msg)
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
onMounted(() => {
|
|
147
|
+
mounted.value = true
|
|
148
|
+
if (props.autoConnect) connectAndSubscribe()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
onBeforeUnmount(() => {
|
|
152
|
+
mounted.value = false
|
|
153
|
+
try { off?.() } catch { /* ignore */ }
|
|
154
|
+
safeDisconnect()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* If props change while connected, switch subscription behavior.
|
|
159
|
+
* - withPhoto changes => re-subscribe (server treats subscribe twice as error), so do unsubscribe -> subscribe
|
|
160
|
+
* - reader changes => use switchReader action when possible; if not connected, just store for next subscribe
|
|
161
|
+
*/
|
|
162
|
+
watch(
|
|
163
|
+
() => props.reader,
|
|
164
|
+
(r) => {
|
|
165
|
+
if (!mounted.value) return
|
|
166
|
+
if (!host.wsConnected.value) return
|
|
167
|
+
if (!host.wsSubscribed.value) return
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
host.switchReaderIdCardWs(r ?? null)
|
|
171
|
+
} catch (e) {
|
|
172
|
+
emit('error', e)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
watch(
|
|
178
|
+
() => props.withPhoto,
|
|
179
|
+
(withPhoto) => {
|
|
180
|
+
if (!mounted.value) return
|
|
181
|
+
if (!host.wsConnected.value) return
|
|
182
|
+
|
|
183
|
+
// Server disallows re-subscribe without unsubscribe first.
|
|
184
|
+
// We do a clean unsubscribe -> resubscribe sequence.
|
|
185
|
+
try {
|
|
186
|
+
if (host.wsSubscribed.value) {
|
|
187
|
+
host.unsubscribeIdCardWs()
|
|
188
|
+
}
|
|
189
|
+
} catch { /* ignore */ }
|
|
190
|
+
|
|
191
|
+
// resubscribe after a tick; give server a moment to process unsubscribe
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
try {
|
|
194
|
+
if (host.wsConnected.value) {
|
|
195
|
+
host.subscribeIdCardWs({ reader: props.reader ?? null, withPhoto })
|
|
196
|
+
}
|
|
197
|
+
} catch (e) {
|
|
198
|
+
emit('error', e)
|
|
199
|
+
}
|
|
200
|
+
}, 0)
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<template>
|
|
206
|
+
<!-- Intentionally blank (headless component) -->
|
|
207
|
+
</template>
|