@ramathibodi/nuxt-commons 0.1.74 → 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 -115
- package/dist/module.json +1 -1
- package/dist/runtime/components/Alert.vue +58 -58
- package/dist/runtime/components/BarcodeReader.vue +130 -130
- package/dist/runtime/components/ExportCSV.vue +110 -110
- package/dist/runtime/components/FileBtn.vue +79 -79
- package/dist/runtime/components/ImportCSV.vue +151 -151
- package/dist/runtime/components/MrzReader.vue +168 -168
- package/dist/runtime/components/SplitterPanel.vue +67 -67
- package/dist/runtime/components/TabsGroup.vue +39 -39
- package/dist/runtime/components/TextBarcode.vue +66 -66
- package/dist/runtime/components/device/IdCardButton.vue +95 -95
- package/dist/runtime/components/device/IdCardWebSocket.vue +207 -207
- package/dist/runtime/components/device/Scanner.vue +350 -350
- package/dist/runtime/components/dialog/Confirm.vue +112 -112
- package/dist/runtime/components/dialog/Host.vue +88 -88
- package/dist/runtime/components/dialog/Index.vue +84 -84
- package/dist/runtime/components/dialog/Loading.vue +51 -51
- package/dist/runtime/components/dialog/default/Confirm.vue +112 -112
- package/dist/runtime/components/dialog/default/Loading.vue +60 -60
- package/dist/runtime/components/dialog/default/Notify.vue +82 -82
- package/dist/runtime/components/dialog/default/Printing.vue +46 -46
- package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -144
- package/dist/runtime/components/document/Form.vue +50 -50
- package/dist/runtime/components/document/TemplateBuilder.vue +536 -536
- package/dist/runtime/components/form/ActionPad.vue +156 -156
- package/dist/runtime/components/form/Birthdate.vue +116 -116
- package/dist/runtime/components/form/CheckboxGroup.vue +99 -99
- package/dist/runtime/components/form/CodeEditor.vue +45 -45
- package/dist/runtime/components/form/Date.vue +270 -270
- package/dist/runtime/components/form/DateTime.vue +220 -220
- package/dist/runtime/components/form/Dialog.vue +178 -178
- package/dist/runtime/components/form/EditPad.vue +157 -157
- package/dist/runtime/components/form/File.vue +295 -295
- package/dist/runtime/components/form/Hidden.vue +44 -44
- package/dist/runtime/components/form/Iterator.vue +538 -538
- package/dist/runtime/components/form/Login.vue +143 -143
- package/dist/runtime/components/form/Pad.vue +399 -399
- package/dist/runtime/components/form/SignPad.vue +226 -226
- package/dist/runtime/components/form/System.vue +34 -34
- package/dist/runtime/components/form/Table.vue +391 -391
- package/dist/runtime/components/form/TableData.vue +236 -236
- package/dist/runtime/components/form/Time.vue +177 -177
- package/dist/runtime/components/form/images/Capture.vue +245 -245
- package/dist/runtime/components/form/images/Edit.vue +133 -133
- package/dist/runtime/components/form/images/Field.vue +331 -331
- package/dist/runtime/components/form/images/Pad.vue +54 -54
- package/dist/runtime/components/label/Date.vue +37 -37
- package/dist/runtime/components/label/DateAgo.vue +102 -102
- package/dist/runtime/components/label/DateCount.vue +152 -152
- package/dist/runtime/components/label/Field.vue +111 -111
- package/dist/runtime/components/label/FormatMoney.vue +37 -37
- package/dist/runtime/components/label/Mask.vue +46 -46
- package/dist/runtime/components/label/Object.vue +21 -21
- package/dist/runtime/components/master/Autocomplete.vue +89 -89
- package/dist/runtime/components/master/Combobox.vue +88 -88
- package/dist/runtime/components/master/RadioGroup.vue +90 -90
- package/dist/runtime/components/master/Select.vue +70 -70
- package/dist/runtime/components/master/label.vue +55 -55
- package/dist/runtime/components/model/Autocomplete.vue +91 -91
- package/dist/runtime/components/model/Combobox.vue +90 -90
- package/dist/runtime/components/model/Pad.vue +114 -114
- package/dist/runtime/components/model/Select.vue +78 -84
- package/dist/runtime/components/model/Table.vue +370 -370
- package/dist/runtime/components/model/iterator.vue +497 -497
- package/dist/runtime/components/model/label.vue +58 -58
- package/dist/runtime/components/pdf/Print.vue +75 -75
- package/dist/runtime/components/pdf/View.vue +146 -146
- package/dist/runtime/composables/dialog.d.ts +1 -1
- 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/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/package.json +131 -131
- package/scripts/enrich-vue-docs-from-ai.mjs +197 -197
- package/scripts/generate-ai-summary.mjs +321 -321
- package/scripts/generate-composables-md.mjs +129 -129
- package/scripts/postInstall.cjs +70 -70
- package/templates/.codegen/codegen.ts +32 -32
- package/templates/.codegen/plugin-schema-object.js +161 -161
package/README.md
CHANGED
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
# @ramathibodi/nuxt-commons
|
|
2
|
-
|
|
3
|
-
Nuxt 3 module that provides shared runtime building blocks for Rama projects:
|
|
4
|
-
- globally auto-imported UI components
|
|
5
|
-
- auto-imported composables
|
|
6
|
-
- runtime plugins for permission/dialog/default behavior
|
|
7
|
-
- utility and lab exports
|
|
8
|
-
|
|
9
|
-
## Install
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# pnpm
|
|
13
|
-
pnpm add -D @ramathibodi/nuxt-commons
|
|
14
|
-
|
|
15
|
-
# yarn
|
|
16
|
-
yarn add --dev @ramathibodi/nuxt-commons
|
|
17
|
-
|
|
18
|
-
# npm
|
|
19
|
-
npm install --save-dev @ramathibodi/nuxt-commons
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Nuxt Setup
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
// nuxt.config.ts
|
|
26
|
-
export default defineNuxtConfig({
|
|
27
|
-
modules: ['@ramathibodi/nuxt-commons'],
|
|
28
|
-
})
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## What The Module Registers
|
|
32
|
-
|
|
33
|
-
Configured in `src/module.ts`:
|
|
34
|
-
- Components from `src/runtime/components` (global + prefixed)
|
|
35
|
-
- Composables from `src/runtime/composables/**` (auto-imported)
|
|
36
|
-
- Plugins:
|
|
37
|
-
- `src/runtime/plugins/permission.ts`
|
|
38
|
-
- `src/runtime/plugins/dialogManager.ts` (client)
|
|
39
|
-
- `src/runtime/plugins/clientConfig.ts` (client)
|
|
40
|
-
- `src/runtime/plugins/default.ts` (client)
|
|
41
|
-
- Type templates from `src/runtime/types/*.d.ts`
|
|
42
|
-
|
|
43
|
-
## Runtime Config
|
|
44
|
-
|
|
45
|
-
This module uses public runtime config at:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
runtimeConfig.public['nuxt-commons']
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Module options are merged into this key by module setup.
|
|
52
|
-
|
|
53
|
-
## Development
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
# Install dependencies
|
|
57
|
-
npm install
|
|
58
|
-
|
|
59
|
-
# Prepare stubs/types
|
|
60
|
-
npm run dev:prepare
|
|
61
|
-
|
|
62
|
-
# Run playground
|
|
63
|
-
npm run dev
|
|
64
|
-
|
|
65
|
-
# Build playground
|
|
66
|
-
npm run dev:build
|
|
67
|
-
|
|
68
|
-
# Lint and test
|
|
69
|
-
npm run lint
|
|
70
|
-
npm run test
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Documentation
|
|
74
|
-
|
|
75
|
-
Generate documentation artifacts from source:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
# Vue component API docs (vue-docgen)
|
|
79
|
-
npm run docs:api:components
|
|
80
|
-
|
|
81
|
-
# Composable API docs (typedoc)
|
|
82
|
-
npm run docs:api:composables
|
|
83
|
-
|
|
84
|
-
# Both API docs
|
|
85
|
-
npm run docs:api
|
|
86
|
-
|
|
87
|
-
# AI-focused summary + type index
|
|
88
|
-
npm run docs:ai:summary
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
`docs:api:components` now runs a post-process step that enriches missing prop/event descriptions and missing type/default cells in generated markdown using `docs/ai-summary.json`.
|
|
92
|
-
|
|
93
|
-
Documentation config files:
|
|
94
|
-
- [`docs/vue-docgen.config.cjs`](docs/vue-docgen.config.cjs) for component docs generation
|
|
95
|
-
- [`docs/typedoc.json`](docs/typedoc.json) for composable docs generation
|
|
96
|
-
- [`docs/typedoc.tsconfig.json`](docs/typedoc.tsconfig.json) for docs-only TypeScript scope
|
|
97
|
-
|
|
98
|
-
Generated outputs:
|
|
99
|
-
- [`docs/components/`](docs/components/) (vue-docgen output)
|
|
100
|
-
- [`docs/composables/`](docs/composables/) (typedoc output)
|
|
101
|
-
- [`docs/ai-summary.md`](docs/ai-summary.md) (compact AI-oriented markdown summary)
|
|
102
|
-
- [`docs/ai-summary.json`](docs/ai-summary.json) (machine-readable component/composable metadata)
|
|
103
|
-
- [`docs/type-index.json`](docs/type-index.json) (type index for tooling/AI pipelines)
|
|
104
|
-
|
|
105
|
-
`docs:ai:summary` generates:
|
|
106
|
-
- `docs/ai-summary.md`
|
|
107
|
-
- `docs/ai-summary.json`
|
|
108
|
-
- `docs/type-index.json`
|
|
109
|
-
|
|
110
|
-
## Publish Notes
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
npm run dev:build
|
|
114
|
-
npm publish --access public
|
|
115
|
-
```
|
|
1
|
+
# @ramathibodi/nuxt-commons
|
|
2
|
+
|
|
3
|
+
Nuxt 3 module that provides shared runtime building blocks for Rama projects:
|
|
4
|
+
- globally auto-imported UI components
|
|
5
|
+
- auto-imported composables
|
|
6
|
+
- runtime plugins for permission/dialog/default behavior
|
|
7
|
+
- utility and lab exports
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# pnpm
|
|
13
|
+
pnpm add -D @ramathibodi/nuxt-commons
|
|
14
|
+
|
|
15
|
+
# yarn
|
|
16
|
+
yarn add --dev @ramathibodi/nuxt-commons
|
|
17
|
+
|
|
18
|
+
# npm
|
|
19
|
+
npm install --save-dev @ramathibodi/nuxt-commons
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Nuxt Setup
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// nuxt.config.ts
|
|
26
|
+
export default defineNuxtConfig({
|
|
27
|
+
modules: ['@ramathibodi/nuxt-commons'],
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## What The Module Registers
|
|
32
|
+
|
|
33
|
+
Configured in `src/module.ts`:
|
|
34
|
+
- Components from `src/runtime/components` (global + prefixed)
|
|
35
|
+
- Composables from `src/runtime/composables/**` (auto-imported)
|
|
36
|
+
- Plugins:
|
|
37
|
+
- `src/runtime/plugins/permission.ts`
|
|
38
|
+
- `src/runtime/plugins/dialogManager.ts` (client)
|
|
39
|
+
- `src/runtime/plugins/clientConfig.ts` (client)
|
|
40
|
+
- `src/runtime/plugins/default.ts` (client)
|
|
41
|
+
- Type templates from `src/runtime/types/*.d.ts`
|
|
42
|
+
|
|
43
|
+
## Runtime Config
|
|
44
|
+
|
|
45
|
+
This module uses public runtime config at:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
runtimeConfig.public['nuxt-commons']
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Module options are merged into this key by module setup.
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Install dependencies
|
|
57
|
+
npm install
|
|
58
|
+
|
|
59
|
+
# Prepare stubs/types
|
|
60
|
+
npm run dev:prepare
|
|
61
|
+
|
|
62
|
+
# Run playground
|
|
63
|
+
npm run dev
|
|
64
|
+
|
|
65
|
+
# Build playground
|
|
66
|
+
npm run dev:build
|
|
67
|
+
|
|
68
|
+
# Lint and test
|
|
69
|
+
npm run lint
|
|
70
|
+
npm run test
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Documentation
|
|
74
|
+
|
|
75
|
+
Generate documentation artifacts from source:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Vue component API docs (vue-docgen)
|
|
79
|
+
npm run docs:api:components
|
|
80
|
+
|
|
81
|
+
# Composable API docs (typedoc)
|
|
82
|
+
npm run docs:api:composables
|
|
83
|
+
|
|
84
|
+
# Both API docs
|
|
85
|
+
npm run docs:api
|
|
86
|
+
|
|
87
|
+
# AI-focused summary + type index
|
|
88
|
+
npm run docs:ai:summary
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`docs:api:components` now runs a post-process step that enriches missing prop/event descriptions and missing type/default cells in generated markdown using `docs/ai-summary.json`.
|
|
92
|
+
|
|
93
|
+
Documentation config files:
|
|
94
|
+
- [`docs/vue-docgen.config.cjs`](docs/vue-docgen.config.cjs) for component docs generation
|
|
95
|
+
- [`docs/typedoc.json`](docs/typedoc.json) for composable docs generation
|
|
96
|
+
- [`docs/typedoc.tsconfig.json`](docs/typedoc.tsconfig.json) for docs-only TypeScript scope
|
|
97
|
+
|
|
98
|
+
Generated outputs:
|
|
99
|
+
- [`docs/components/`](docs/components/) (vue-docgen output)
|
|
100
|
+
- [`docs/composables/`](docs/composables/) (typedoc output)
|
|
101
|
+
- [`docs/ai-summary.md`](docs/ai-summary.md) (compact AI-oriented markdown summary)
|
|
102
|
+
- [`docs/ai-summary.json`](docs/ai-summary.json) (machine-readable component/composable metadata)
|
|
103
|
+
- [`docs/type-index.json`](docs/type-index.json) (type index for tooling/AI pipelines)
|
|
104
|
+
|
|
105
|
+
`docs:ai:summary` generates:
|
|
106
|
+
- `docs/ai-summary.md`
|
|
107
|
+
- `docs/ai-summary.json`
|
|
108
|
+
- `docs/type-index.json`
|
|
109
|
+
|
|
110
|
+
## Publish Notes
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm run dev:build
|
|
114
|
+
npm publish --access public
|
|
115
|
+
```
|
package/dist/module.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
/**
|
|
3
|
-
* Alert displays runtime alert messages and bridges alert-store entries to a consistent UI variant system.
|
|
4
|
-
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
-
*/
|
|
6
|
-
import { ref, watch } from 'vue'
|
|
7
|
-
import { isEmpty } from 'lodash-es'
|
|
8
|
-
import { useAlert } from '../composables/alert'
|
|
9
|
-
import type { AlertItem } from '../types/alert'
|
|
10
|
-
|
|
11
|
-
const isAlertOpen = ref(false)
|
|
12
|
-
const timeout = ref(3000)
|
|
13
|
-
const alert = useAlert()
|
|
14
|
-
const currentItem = ref<AlertItem | undefined>()
|
|
15
|
-
|
|
16
|
-
const renewAlert = () => {
|
|
17
|
-
if (alert?.hasAlert()) {
|
|
18
|
-
currentItem.value = alert?.takeAlert()
|
|
19
|
-
isAlertOpen.value = true
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
currentItem.value = undefined
|
|
23
|
-
isAlertOpen.value = false
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
watch(() => alert?.hasAlert(), (hasAlert) => {
|
|
28
|
-
if (hasAlert) {
|
|
29
|
-
renewAlert()
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<template>
|
|
35
|
-
<VSnackbar
|
|
36
|
-
v-if="currentItem"
|
|
37
|
-
v-model="isAlertOpen"
|
|
38
|
-
:timeout="timeout"
|
|
39
|
-
location="center"
|
|
40
|
-
multi-line
|
|
41
|
-
variant="text"
|
|
42
|
-
@update:model-value="renewAlert()"
|
|
43
|
-
>
|
|
44
|
-
<!-- @vue-expected-error Type conversion problem -->
|
|
45
|
-
<VAlert
|
|
46
|
-
v-model="isAlertOpen"
|
|
47
|
-
closable
|
|
48
|
-
:type="currentItem.alertType"
|
|
49
|
-
elevation="2"
|
|
50
|
-
theme="dark"
|
|
51
|
-
variant="flat"
|
|
52
|
-
v-bind="currentItem.alertIcon ? { icon: currentItem.alertIcon } : {}"
|
|
53
|
-
@click:close="renewAlert()"
|
|
54
|
-
>
|
|
55
|
-
{{ currentItem.statusCode ? currentItem.statusCode + ' ' : '' }} {{ currentItem.message }} {{ !isEmpty(currentItem.data) ? currentItem.data : '' }}
|
|
56
|
-
</VAlert>
|
|
57
|
-
</VSnackbar>
|
|
58
|
-
</template>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* Alert displays runtime alert messages and bridges alert-store entries to a consistent UI variant system.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import { ref, watch } from 'vue'
|
|
7
|
+
import { isEmpty } from 'lodash-es'
|
|
8
|
+
import { useAlert } from '../composables/alert'
|
|
9
|
+
import type { AlertItem } from '../types/alert'
|
|
10
|
+
|
|
11
|
+
const isAlertOpen = ref(false)
|
|
12
|
+
const timeout = ref(3000)
|
|
13
|
+
const alert = useAlert()
|
|
14
|
+
const currentItem = ref<AlertItem | undefined>()
|
|
15
|
+
|
|
16
|
+
const renewAlert = () => {
|
|
17
|
+
if (alert?.hasAlert()) {
|
|
18
|
+
currentItem.value = alert?.takeAlert()
|
|
19
|
+
isAlertOpen.value = true
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
currentItem.value = undefined
|
|
23
|
+
isAlertOpen.value = false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
watch(() => alert?.hasAlert(), (hasAlert) => {
|
|
28
|
+
if (hasAlert) {
|
|
29
|
+
renewAlert()
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<VSnackbar
|
|
36
|
+
v-if="currentItem"
|
|
37
|
+
v-model="isAlertOpen"
|
|
38
|
+
:timeout="timeout"
|
|
39
|
+
location="center"
|
|
40
|
+
multi-line
|
|
41
|
+
variant="text"
|
|
42
|
+
@update:model-value="renewAlert()"
|
|
43
|
+
>
|
|
44
|
+
<!-- @vue-expected-error Type conversion problem -->
|
|
45
|
+
<VAlert
|
|
46
|
+
v-model="isAlertOpen"
|
|
47
|
+
closable
|
|
48
|
+
:type="currentItem.alertType"
|
|
49
|
+
elevation="2"
|
|
50
|
+
theme="dark"
|
|
51
|
+
variant="flat"
|
|
52
|
+
v-bind="currentItem.alertIcon ? { icon: currentItem.alertIcon } : {}"
|
|
53
|
+
@click:close="renewAlert()"
|
|
54
|
+
>
|
|
55
|
+
{{ currentItem.statusCode ? currentItem.statusCode + ' ' : '' }} {{ currentItem.message }} {{ !isEmpty(currentItem.data) ? currentItem.data : '' }}
|
|
56
|
+
</VAlert>
|
|
57
|
+
</VSnackbar>
|
|
58
|
+
</template>
|
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
/**
|
|
3
|
-
* BarcodeReader starts and stops barcode scans, then emits parsed scan results back to parent workflows.
|
|
4
|
-
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
-
*/
|
|
6
|
-
import {BrowserMultiFormatReader} from '@zxing/browser'
|
|
7
|
-
import {type IScannerControls} from '@zxing/browser/esm'
|
|
8
|
-
import type {Exception, Result} from '@zxing/library'
|
|
9
|
-
import {computed, onBeforeUnmount, onMounted, ref, watchEffect} from 'vue'
|
|
10
|
-
import {useAlert} from '../composables/alert'
|
|
11
|
-
import {useDevicesList, useUserMedia} from "@vueuse/core";
|
|
12
|
-
|
|
13
|
-
const barcodeReader = new BrowserMultiFormatReader()
|
|
14
|
-
const barcodeReaderControl = ref()
|
|
15
|
-
|
|
16
|
-
const alert = useAlert()
|
|
17
|
-
const isLoading = ref<boolean>(false)
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Custom events emitted by BarcodeReader.
|
|
21
|
-
* Parents can listen to these events to react to user actions and internal state changes.
|
|
22
|
-
*/
|
|
23
|
-
const emit = defineEmits<{
|
|
24
|
-
(event: 'decode', barcodeValue: string): void
|
|
25
|
-
(event: 'error', error: string | unknown): void
|
|
26
|
-
}>()
|
|
27
|
-
|
|
28
|
-
const videoScreen = ref<HTMLVideoElement>()
|
|
29
|
-
|
|
30
|
-
const currentCameraId = ref<ConstrainDOMString | undefined>()
|
|
31
|
-
const { videoInputs: cameras } = useDevicesList({
|
|
32
|
-
requestPermissions: true,
|
|
33
|
-
constraints: { audio: false, video: true },
|
|
34
|
-
onUpdated() {
|
|
35
|
-
if (!cameras.value.find(camera => camera.deviceId === currentCameraId.value))
|
|
36
|
-
currentCameraId.value = cameras.value[0]?.deviceId
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
const hasCamera = computed(()=>{ return !!currentCameraId.value })
|
|
40
|
-
|
|
41
|
-
const { stream, start: cameraStart, stop: cameraStop, enabled: cameraEnabled } = useUserMedia({
|
|
42
|
-
constraints: { video: { deviceId: currentCameraId.value}},
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
watchEffect(() => {
|
|
46
|
-
if (videoScreen.value) videoScreen.value.srcObject = (stream.value) ? stream.value! : null
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
function startCamera() {
|
|
50
|
-
if (!cameraEnabled.value) {
|
|
51
|
-
isLoading.value = true
|
|
52
|
-
cameraStart().then(()=>{
|
|
53
|
-
barcodeReader.decodeFromVideoDevice(currentCameraId.value, videoScreen.value, (result: Result | undefined, error: Exception | undefined, controls: IScannerControls) => {
|
|
54
|
-
if (result) {
|
|
55
|
-
emit('decode', result.getText())
|
|
56
|
-
cameraStop()
|
|
57
|
-
}
|
|
58
|
-
}).then((result: any)=>{
|
|
59
|
-
barcodeReaderControl.value = result
|
|
60
|
-
})
|
|
61
|
-
}).finally(()=>{
|
|
62
|
-
isLoading.value = false
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function stopCamera() {
|
|
68
|
-
if (barcodeReaderControl.value) barcodeReaderControl.value.stop()
|
|
69
|
-
if (cameraEnabled.value) cameraStop()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function scanImageFile(selectedFile: File | File[] | undefined) {
|
|
73
|
-
if (!selectedFile) {
|
|
74
|
-
alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const scanImageSingleFile: File = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
|
|
79
|
-
|
|
80
|
-
const reader = new FileReader()
|
|
81
|
-
reader.onload = async (event) => {
|
|
82
|
-
try {
|
|
83
|
-
const result = await barcodeReader.decodeFromImageUrl(event.target?.result as string)
|
|
84
|
-
emit('decode', result.getText())
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
void e
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
reader.readAsDataURL(scanImageSingleFile)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
onMounted( () => {
|
|
94
|
-
startCamera()
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
onBeforeUnmount( () => {
|
|
98
|
-
stopCamera()
|
|
99
|
-
})
|
|
100
|
-
</script>
|
|
101
|
-
|
|
102
|
-
<template>
|
|
103
|
-
<v-card flat>
|
|
104
|
-
<v-card-text class="d-flex justify-center" v-if="isLoading">
|
|
105
|
-
<v-progress-circular indeterminate></v-progress-circular>
|
|
106
|
-
</v-card-text>
|
|
107
|
-
<v-card-text v-else>
|
|
108
|
-
<v-col v-if="hasCamera">
|
|
109
|
-
<div style="position: relative; display: inline-block; width: 100%;" :style="{maxWidth: '1024px'}">
|
|
110
|
-
<video autoplay ref="videoScreen" width="100%" :style="{maxWidth:'1024px'}"></video>
|
|
111
|
-
<div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
|
|
112
|
-
<FileBtn
|
|
113
|
-
accept="image/*"
|
|
114
|
-
icon="mdi mdi-image-plus"
|
|
115
|
-
icon-only
|
|
116
|
-
@update:model-value="scanImageFile"
|
|
117
|
-
/>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
</v-col>
|
|
121
|
-
<v-col v-else>
|
|
122
|
-
<FileBtn
|
|
123
|
-
accept="image/*"
|
|
124
|
-
text="Upload Image"
|
|
125
|
-
@update:model-value="scanImageFile"
|
|
126
|
-
/>
|
|
127
|
-
</v-col>
|
|
128
|
-
</v-card-text>
|
|
129
|
-
</v-card>
|
|
130
|
-
</template>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* BarcodeReader starts and stops barcode scans, then emits parsed scan results back to parent workflows.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import {BrowserMultiFormatReader} from '@zxing/browser'
|
|
7
|
+
import {type IScannerControls} from '@zxing/browser/esm'
|
|
8
|
+
import type {Exception, Result} from '@zxing/library'
|
|
9
|
+
import {computed, onBeforeUnmount, onMounted, ref, watchEffect} from 'vue'
|
|
10
|
+
import {useAlert} from '../composables/alert'
|
|
11
|
+
import {useDevicesList, useUserMedia} from "@vueuse/core";
|
|
12
|
+
|
|
13
|
+
const barcodeReader = new BrowserMultiFormatReader()
|
|
14
|
+
const barcodeReaderControl = ref()
|
|
15
|
+
|
|
16
|
+
const alert = useAlert()
|
|
17
|
+
const isLoading = ref<boolean>(false)
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Custom events emitted by BarcodeReader.
|
|
21
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
22
|
+
*/
|
|
23
|
+
const emit = defineEmits<{
|
|
24
|
+
(event: 'decode', barcodeValue: string): void
|
|
25
|
+
(event: 'error', error: string | unknown): void
|
|
26
|
+
}>()
|
|
27
|
+
|
|
28
|
+
const videoScreen = ref<HTMLVideoElement>()
|
|
29
|
+
|
|
30
|
+
const currentCameraId = ref<ConstrainDOMString | undefined>()
|
|
31
|
+
const { videoInputs: cameras } = useDevicesList({
|
|
32
|
+
requestPermissions: true,
|
|
33
|
+
constraints: { audio: false, video: true },
|
|
34
|
+
onUpdated() {
|
|
35
|
+
if (!cameras.value.find(camera => camera.deviceId === currentCameraId.value))
|
|
36
|
+
currentCameraId.value = cameras.value[0]?.deviceId
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
const hasCamera = computed(()=>{ return !!currentCameraId.value })
|
|
40
|
+
|
|
41
|
+
const { stream, start: cameraStart, stop: cameraStop, enabled: cameraEnabled } = useUserMedia({
|
|
42
|
+
constraints: { video: { deviceId: currentCameraId.value}},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
watchEffect(() => {
|
|
46
|
+
if (videoScreen.value) videoScreen.value.srcObject = (stream.value) ? stream.value! : null
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
function startCamera() {
|
|
50
|
+
if (!cameraEnabled.value) {
|
|
51
|
+
isLoading.value = true
|
|
52
|
+
cameraStart().then(()=>{
|
|
53
|
+
barcodeReader.decodeFromVideoDevice(currentCameraId.value, videoScreen.value, (result: Result | undefined, error: Exception | undefined, controls: IScannerControls) => {
|
|
54
|
+
if (result) {
|
|
55
|
+
emit('decode', result.getText())
|
|
56
|
+
cameraStop()
|
|
57
|
+
}
|
|
58
|
+
}).then((result: any)=>{
|
|
59
|
+
barcodeReaderControl.value = result
|
|
60
|
+
})
|
|
61
|
+
}).finally(()=>{
|
|
62
|
+
isLoading.value = false
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function stopCamera() {
|
|
68
|
+
if (barcodeReaderControl.value) barcodeReaderControl.value.stop()
|
|
69
|
+
if (cameraEnabled.value) cameraStop()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function scanImageFile(selectedFile: File | File[] | undefined) {
|
|
73
|
+
if (!selectedFile) {
|
|
74
|
+
alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const scanImageSingleFile: File = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
|
|
79
|
+
|
|
80
|
+
const reader = new FileReader()
|
|
81
|
+
reader.onload = async (event) => {
|
|
82
|
+
try {
|
|
83
|
+
const result = await barcodeReader.decodeFromImageUrl(event.target?.result as string)
|
|
84
|
+
emit('decode', result.getText())
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
void e
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
reader.readAsDataURL(scanImageSingleFile)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
onMounted( () => {
|
|
94
|
+
startCamera()
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
onBeforeUnmount( () => {
|
|
98
|
+
stopCamera()
|
|
99
|
+
})
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<v-card flat>
|
|
104
|
+
<v-card-text class="d-flex justify-center" v-if="isLoading">
|
|
105
|
+
<v-progress-circular indeterminate></v-progress-circular>
|
|
106
|
+
</v-card-text>
|
|
107
|
+
<v-card-text v-else>
|
|
108
|
+
<v-col v-if="hasCamera">
|
|
109
|
+
<div style="position: relative; display: inline-block; width: 100%;" :style="{maxWidth: '1024px'}">
|
|
110
|
+
<video autoplay ref="videoScreen" width="100%" :style="{maxWidth:'1024px'}"></video>
|
|
111
|
+
<div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
|
|
112
|
+
<FileBtn
|
|
113
|
+
accept="image/*"
|
|
114
|
+
icon="mdi mdi-image-plus"
|
|
115
|
+
icon-only
|
|
116
|
+
@update:model-value="scanImageFile"
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</v-col>
|
|
121
|
+
<v-col v-else>
|
|
122
|
+
<FileBtn
|
|
123
|
+
accept="image/*"
|
|
124
|
+
text="Upload Image"
|
|
125
|
+
@update:model-value="scanImageFile"
|
|
126
|
+
/>
|
|
127
|
+
</v-col>
|
|
128
|
+
</v-card-text>
|
|
129
|
+
</v-card>
|
|
130
|
+
</template>
|