@ramathibodi/nuxt-commons 0.1.14 → 0.1.16
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 +96 -96
- package/dist/module.json +1 -1
- package/dist/runtime/components/Alert.vue +53 -53
- package/dist/runtime/components/BarcodeReader.vue +98 -98
- package/dist/runtime/components/ExportCSV.vue +55 -55
- package/dist/runtime/components/FileBtn.vue +62 -62
- package/dist/runtime/components/ImportCSV.vue +64 -64
- package/dist/runtime/components/SplitterPanel.vue +59 -59
- package/dist/runtime/components/TabsGroup.vue +32 -32
- package/dist/runtime/components/TextBarcode.vue +52 -52
- package/dist/runtime/components/dialog/Confirm.vue +100 -100
- package/dist/runtime/components/dialog/Index.vue +72 -72
- package/dist/runtime/components/dialog/Loading.vue +39 -39
- package/dist/runtime/components/document/TemplateBuilder.vue +203 -216
- package/dist/runtime/components/form/Birthdate.vue +216 -216
- package/dist/runtime/components/form/CodeEditor.vue +37 -37
- package/dist/runtime/components/form/Date.vue +163 -163
- package/dist/runtime/components/form/DateTime.vue +107 -107
- package/dist/runtime/components/form/Dialog.vue +138 -138
- package/dist/runtime/components/form/File.vue +187 -187
- package/dist/runtime/components/form/Hidden.vue +32 -32
- package/dist/runtime/components/form/Login.vue +131 -131
- package/dist/runtime/components/form/Pad.vue +217 -217
- package/dist/runtime/components/form/SignPad.vue +186 -186
- package/dist/runtime/components/form/Table.vue +294 -266
- package/dist/runtime/components/form/Time.vue +158 -158
- package/dist/runtime/components/form/images/Capture.vue +230 -230
- package/dist/runtime/components/form/images/Edit.vue +114 -114
- package/dist/runtime/components/label/Date.vue +29 -29
- package/dist/runtime/components/label/Field.vue +42 -42
- package/dist/runtime/components/label/FormatMoney.vue +29 -29
- package/dist/runtime/components/label/Mask.vue +38 -38
- package/dist/runtime/components/master/Autocomplete.vue +159 -159
- package/dist/runtime/components/master/Combobox.vue +84 -84
- package/dist/runtime/components/master/RadioGroup.vue +78 -78
- package/dist/runtime/components/master/Select.vue +82 -82
- package/dist/runtime/components/model/Pad.vue +122 -122
- package/dist/runtime/components/model/Table.vue +242 -240
- package/dist/runtime/components/model/iterator.vue +312 -312
- package/dist/runtime/components/pdf/Print.vue +63 -63
- package/dist/runtime/components/pdf/View.vue +104 -104
- package/dist/runtime/composables/graphqlModel.mjs +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/types/alert.d.ts +11 -11
- package/dist/runtime/types/formDialog.d.ts +4 -4
- package/dist/runtime/types/graphqlOperation.d.ts +23 -23
- package/dist/runtime/types/menu.d.ts +25 -25
- package/dist/runtime/types/modules.d.ts +7 -7
- package/package.json +120 -119
- package/scripts/postInstall.cjs +70 -70
- package/templates/.codegen/codegen.ts +32 -32
- package/templates/.codegen/plugin-schema-object.js +161 -154
package/README.md
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Get your module up and running quickly.
|
|
3
|
-
|
|
4
|
-
Find and replace all on all files (CMD+SHIFT+F):
|
|
5
|
-
- Name: Ramahis Common Component
|
|
6
|
-
- Package name: @ramahis/common-components
|
|
7
|
-
-->
|
|
8
|
-
|
|
9
|
-
# @ramathibodi/nuxt-commons
|
|
10
|
-
|
|
11
|
-
[![npm version][npm-version-src]][npm-version-href]
|
|
12
|
-
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
13
|
-
[![License][license-src]][license-href]
|
|
14
|
-
[![Nuxt][nuxt-src]][nuxt-href]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## Quick Setup
|
|
18
|
-
|
|
19
|
-
1. Add `'@ramathibodi/nuxt-commons'` dependency to your project
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# Using pnpm
|
|
23
|
-
pnpm add -D @ramathibodi/nuxt-commons
|
|
24
|
-
|
|
25
|
-
# Using yarn
|
|
26
|
-
yarn add --dev @ramathibodi/nuxt-commons
|
|
27
|
-
|
|
28
|
-
# Using npm
|
|
29
|
-
npm install --save-dev @ramathibodi/nuxt-commons
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
2. Add `my-module` to the `modules` section of `nuxt.config.ts`
|
|
33
|
-
|
|
34
|
-
```js
|
|
35
|
-
export default defineNuxtConfig({
|
|
36
|
-
modules: [
|
|
37
|
-
'@ramathibodi/nuxt-commons'
|
|
38
|
-
]
|
|
39
|
-
})
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
That's it! You can now use My Module in your Nuxt app ✨
|
|
43
|
-
|
|
44
|
-
## Development
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
# Install dependencies
|
|
48
|
-
npm install
|
|
49
|
-
|
|
50
|
-
# Generate type stubs
|
|
51
|
-
npm run dev:prepare
|
|
52
|
-
|
|
53
|
-
# Develop with the playground
|
|
54
|
-
npm run dev
|
|
55
|
-
|
|
56
|
-
# Build the playground
|
|
57
|
-
npm run dev:build
|
|
58
|
-
|
|
59
|
-
# Run ESLint
|
|
60
|
-
npm run lint
|
|
61
|
-
|
|
62
|
-
# Run Vitest
|
|
63
|
-
npm run test
|
|
64
|
-
npm run test:watch
|
|
65
|
-
|
|
66
|
-
# Release new version
|
|
67
|
-
npm run release
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## NPM Login
|
|
71
|
-
```bash
|
|
72
|
-
npm login
|
|
73
|
-
```
|
|
74
|
-
- จะส่ง link url มาให้เข้าไปที่ url login npm
|
|
75
|
-
|
|
76
|
-
## NPM PUBLISH
|
|
77
|
-
```bash
|
|
78
|
-
1. ตรวจสอบ source code, ตรวจสอบ Branch ว่าเป็น master แล้ว จากนั้นตรวจสอบ version ของ npm ว่ามีการเปลี่ยนและไม่ชนกับ version บน npm
|
|
79
|
-
2. npm run dev:build
|
|
80
|
-
3. npm publish --access public
|
|
81
|
-
4. set token ใน link ที่ npm ให้มา
|
|
82
|
-
5. เสร็จสิ้น
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
<!-- Badges -->
|
|
86
|
-
[npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
87
|
-
[npm-version-href]: https://npmjs.com/package/my-module
|
|
88
|
-
|
|
89
|
-
[npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
90
|
-
[npm-downloads-href]: https://npmjs.com/package/my-module
|
|
91
|
-
|
|
92
|
-
[license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
93
|
-
[license-href]: https://npmjs.com/package/my-module
|
|
94
|
-
|
|
95
|
-
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
|
|
96
|
-
[nuxt-href]: https://nuxt.com
|
|
1
|
+
<!--
|
|
2
|
+
Get your module up and running quickly.
|
|
3
|
+
|
|
4
|
+
Find and replace all on all files (CMD+SHIFT+F):
|
|
5
|
+
- Name: Ramahis Common Component
|
|
6
|
+
- Package name: @ramahis/common-components
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @ramathibodi/nuxt-commons
|
|
10
|
+
|
|
11
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
12
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
13
|
+
[![License][license-src]][license-href]
|
|
14
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Quick Setup
|
|
18
|
+
|
|
19
|
+
1. Add `'@ramathibodi/nuxt-commons'` dependency to your project
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Using pnpm
|
|
23
|
+
pnpm add -D @ramathibodi/nuxt-commons
|
|
24
|
+
|
|
25
|
+
# Using yarn
|
|
26
|
+
yarn add --dev @ramathibodi/nuxt-commons
|
|
27
|
+
|
|
28
|
+
# Using npm
|
|
29
|
+
npm install --save-dev @ramathibodi/nuxt-commons
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. Add `my-module` to the `modules` section of `nuxt.config.ts`
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
export default defineNuxtConfig({
|
|
36
|
+
modules: [
|
|
37
|
+
'@ramathibodi/nuxt-commons'
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That's it! You can now use My Module in your Nuxt app ✨
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Install dependencies
|
|
48
|
+
npm install
|
|
49
|
+
|
|
50
|
+
# Generate type stubs
|
|
51
|
+
npm run dev:prepare
|
|
52
|
+
|
|
53
|
+
# Develop with the playground
|
|
54
|
+
npm run dev
|
|
55
|
+
|
|
56
|
+
# Build the playground
|
|
57
|
+
npm run dev:build
|
|
58
|
+
|
|
59
|
+
# Run ESLint
|
|
60
|
+
npm run lint
|
|
61
|
+
|
|
62
|
+
# Run Vitest
|
|
63
|
+
npm run test
|
|
64
|
+
npm run test:watch
|
|
65
|
+
|
|
66
|
+
# Release new version
|
|
67
|
+
npm run release
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## NPM Login
|
|
71
|
+
```bash
|
|
72
|
+
npm login
|
|
73
|
+
```
|
|
74
|
+
- จะส่ง link url มาให้เข้าไปที่ url login npm
|
|
75
|
+
|
|
76
|
+
## NPM PUBLISH
|
|
77
|
+
```bash
|
|
78
|
+
1. ตรวจสอบ source code, ตรวจสอบ Branch ว่าเป็น master แล้ว จากนั้นตรวจสอบ version ของ npm ว่ามีการเปลี่ยนและไม่ชนกับ version บน npm
|
|
79
|
+
2. npm run dev:build
|
|
80
|
+
3. npm publish --access public
|
|
81
|
+
4. set token ใน link ที่ npm ให้มา
|
|
82
|
+
5. เสร็จสิ้น
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
<!-- Badges -->
|
|
86
|
+
[npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
87
|
+
[npm-version-href]: https://npmjs.com/package/my-module
|
|
88
|
+
|
|
89
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
90
|
+
[npm-downloads-href]: https://npmjs.com/package/my-module
|
|
91
|
+
|
|
92
|
+
[license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
93
|
+
[license-href]: https://npmjs.com/package/my-module
|
|
94
|
+
|
|
95
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
|
|
96
|
+
[nuxt-href]: https://nuxt.com
|
package/dist/module.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { ref, watch } from 'vue'
|
|
3
|
-
import { isEmpty } from 'lodash-es'
|
|
4
|
-
import { useAlert } from '../composables/alert'
|
|
5
|
-
import type { AlertItem } from '../types/alert'
|
|
6
|
-
|
|
7
|
-
const isAlertOpen = ref(false)
|
|
8
|
-
const timeout = ref(3000)
|
|
9
|
-
const alert = useAlert()
|
|
10
|
-
const currentItem = ref<AlertItem | undefined>()
|
|
11
|
-
|
|
12
|
-
const renewAlert = () => {
|
|
13
|
-
if (alert?.hasAlert()) {
|
|
14
|
-
currentItem.value = alert?.takeAlert()
|
|
15
|
-
isAlertOpen.value = true
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
currentItem.value = undefined
|
|
19
|
-
isAlertOpen.value = false
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
watch(() => alert?.hasAlert(), (hasAlert) => {
|
|
24
|
-
if (hasAlert) {
|
|
25
|
-
renewAlert()
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<template>
|
|
31
|
-
<VSnackbar
|
|
32
|
-
v-if="currentItem"
|
|
33
|
-
v-model="isAlertOpen"
|
|
34
|
-
:timeout="timeout"
|
|
35
|
-
location="center"
|
|
36
|
-
multi-line
|
|
37
|
-
variant="text"
|
|
38
|
-
@update:model-value="renewAlert()"
|
|
39
|
-
>
|
|
40
|
-
<!-- @vue-expected-error Type conversion problem -->
|
|
41
|
-
<VAlert
|
|
42
|
-
v-model="isAlertOpen"
|
|
43
|
-
closable
|
|
44
|
-
:type="currentItem.alertType"
|
|
45
|
-
elevation="2"
|
|
46
|
-
theme="dark"
|
|
47
|
-
variant="flat"
|
|
48
|
-
@click:close="renewAlert()"
|
|
49
|
-
>
|
|
50
|
-
{{ currentItem.statusCode ? currentItem.statusCode + ' ' : '' }} {{ currentItem.message }} {{ !isEmpty(currentItem.data) ? currentItem.data : '' }}
|
|
51
|
-
</VAlert>
|
|
52
|
-
</VSnackbar>
|
|
53
|
-
</template>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, watch } from 'vue'
|
|
3
|
+
import { isEmpty } from 'lodash-es'
|
|
4
|
+
import { useAlert } from '../composables/alert'
|
|
5
|
+
import type { AlertItem } from '../types/alert'
|
|
6
|
+
|
|
7
|
+
const isAlertOpen = ref(false)
|
|
8
|
+
const timeout = ref(3000)
|
|
9
|
+
const alert = useAlert()
|
|
10
|
+
const currentItem = ref<AlertItem | undefined>()
|
|
11
|
+
|
|
12
|
+
const renewAlert = () => {
|
|
13
|
+
if (alert?.hasAlert()) {
|
|
14
|
+
currentItem.value = alert?.takeAlert()
|
|
15
|
+
isAlertOpen.value = true
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
currentItem.value = undefined
|
|
19
|
+
isAlertOpen.value = false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
watch(() => alert?.hasAlert(), (hasAlert) => {
|
|
24
|
+
if (hasAlert) {
|
|
25
|
+
renewAlert()
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<VSnackbar
|
|
32
|
+
v-if="currentItem"
|
|
33
|
+
v-model="isAlertOpen"
|
|
34
|
+
:timeout="timeout"
|
|
35
|
+
location="center"
|
|
36
|
+
multi-line
|
|
37
|
+
variant="text"
|
|
38
|
+
@update:model-value="renewAlert()"
|
|
39
|
+
>
|
|
40
|
+
<!-- @vue-expected-error Type conversion problem -->
|
|
41
|
+
<VAlert
|
|
42
|
+
v-model="isAlertOpen"
|
|
43
|
+
closable
|
|
44
|
+
:type="currentItem.alertType"
|
|
45
|
+
elevation="2"
|
|
46
|
+
theme="dark"
|
|
47
|
+
variant="flat"
|
|
48
|
+
@click:close="renewAlert()"
|
|
49
|
+
>
|
|
50
|
+
{{ currentItem.statusCode ? currentItem.statusCode + ' ' : '' }} {{ currentItem.message }} {{ !isEmpty(currentItem.data) ? currentItem.data : '' }}
|
|
51
|
+
</VAlert>
|
|
52
|
+
</VSnackbar>
|
|
53
|
+
</template>
|
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { BrowserMultiFormatReader } from '@zxing/browser'
|
|
3
|
-
import { type IScannerControls } from '@zxing/browser/esm'
|
|
4
|
-
import type { Exception, Result } from '@zxing/library'
|
|
5
|
-
import { ref, onMounted } from 'vue'
|
|
6
|
-
import { useAlert } from '../composables/alert'
|
|
7
|
-
|
|
8
|
-
const videoElementRef = ref<HTMLVideoElement | null>(null)
|
|
9
|
-
const barcodeReader = new BrowserMultiFormatReader()
|
|
10
|
-
const alert = useAlert()
|
|
11
|
-
const hasCameraAvailable = ref(false)
|
|
12
|
-
|
|
13
|
-
const emit = defineEmits<{
|
|
14
|
-
(event: 'decode', barcodeValue: string): void
|
|
15
|
-
(event: 'error', error: string | unknown): void
|
|
16
|
-
}>()
|
|
17
|
-
|
|
18
|
-
async function checkCameraAvailability() {
|
|
19
|
-
const devices = await navigator.mediaDevices.enumerateDevices()
|
|
20
|
-
hasCameraAvailable.value = devices.some(device => device.kind === 'videoinput')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function startCamera() {
|
|
24
|
-
try {
|
|
25
|
-
const videoInputDevices = await BrowserMultiFormatReader.listVideoInputDevices()
|
|
26
|
-
if (videoInputDevices.length === 0) {
|
|
27
|
-
alert?.addAlert({ message: 'No camera devices found.', alertType: 'error' })
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const selectedDeviceId = videoInputDevices[0].deviceId
|
|
32
|
-
barcodeReader.decodeFromVideoDevice(selectedDeviceId, videoElementRef.value, (result: Result | undefined, error: Exception | undefined, controls: IScannerControls) => {
|
|
33
|
-
if (result) {
|
|
34
|
-
emit('decode', result.getText())
|
|
35
|
-
controls.stop()
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
emit('error', error)
|
|
41
|
-
alert?.addAlert({ message: 'Error starting camera.', alertType: 'error' })
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function scanImageFile(selectedFile: File | File[] | undefined) {
|
|
46
|
-
if (!selectedFile) {
|
|
47
|
-
alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const reader = new FileReader()
|
|
52
|
-
reader.onload = async (event) => {
|
|
53
|
-
try {
|
|
54
|
-
const result = await barcodeReader.decodeFromImageUrl(event.target?.result as string)
|
|
55
|
-
emit('decode', result.getText())
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
alert?.addAlert({ message: 'Unable to read barcode from image.', alertType: 'error' })
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
const scanImageSingleFile: File = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
|
|
62
|
-
reader.readAsDataURL(scanImageSingleFile)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
onMounted(async () => {
|
|
66
|
-
await checkCameraAvailability()
|
|
67
|
-
if (hasCameraAvailable.value) await startCamera()
|
|
68
|
-
})
|
|
69
|
-
</script>
|
|
70
|
-
|
|
71
|
-
<template>
|
|
72
|
-
<v-card flat>
|
|
73
|
-
<v-card-item>
|
|
74
|
-
<v-col v-if="hasCameraAvailable">
|
|
75
|
-
<video
|
|
76
|
-
ref="videoElementRef"
|
|
77
|
-
autoplay
|
|
78
|
-
style="max-width: 1024px"
|
|
79
|
-
/>
|
|
80
|
-
<div style="z-index: 2000; position: relative; bottom: 84px; left: 550px">
|
|
81
|
-
<FileBtn
|
|
82
|
-
accept="image/*"
|
|
83
|
-
icon="mdi mdi-image-plus"
|
|
84
|
-
icon-only
|
|
85
|
-
@update:model-value="scanImageFile"
|
|
86
|
-
/>
|
|
87
|
-
</div>
|
|
88
|
-
</v-col>
|
|
89
|
-
<v-col v-else>
|
|
90
|
-
<FileBtn
|
|
91
|
-
accept="image/*"
|
|
92
|
-
text="Upload Image"
|
|
93
|
-
@update:model-value="scanImageFile"
|
|
94
|
-
/>
|
|
95
|
-
</v-col>
|
|
96
|
-
</v-card-item>
|
|
97
|
-
</v-card>
|
|
98
|
-
</template>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { BrowserMultiFormatReader } from '@zxing/browser'
|
|
3
|
+
import { type IScannerControls } from '@zxing/browser/esm'
|
|
4
|
+
import type { Exception, Result } from '@zxing/library'
|
|
5
|
+
import { ref, onMounted } from 'vue'
|
|
6
|
+
import { useAlert } from '../composables/alert'
|
|
7
|
+
|
|
8
|
+
const videoElementRef = ref<HTMLVideoElement | null>(null)
|
|
9
|
+
const barcodeReader = new BrowserMultiFormatReader()
|
|
10
|
+
const alert = useAlert()
|
|
11
|
+
const hasCameraAvailable = ref(false)
|
|
12
|
+
|
|
13
|
+
const emit = defineEmits<{
|
|
14
|
+
(event: 'decode', barcodeValue: string): void
|
|
15
|
+
(event: 'error', error: string | unknown): void
|
|
16
|
+
}>()
|
|
17
|
+
|
|
18
|
+
async function checkCameraAvailability() {
|
|
19
|
+
const devices = await navigator.mediaDevices.enumerateDevices()
|
|
20
|
+
hasCameraAvailable.value = devices.some(device => device.kind === 'videoinput')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function startCamera() {
|
|
24
|
+
try {
|
|
25
|
+
const videoInputDevices = await BrowserMultiFormatReader.listVideoInputDevices()
|
|
26
|
+
if (videoInputDevices.length === 0) {
|
|
27
|
+
alert?.addAlert({ message: 'No camera devices found.', alertType: 'error' })
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const selectedDeviceId = videoInputDevices[0].deviceId
|
|
32
|
+
barcodeReader.decodeFromVideoDevice(selectedDeviceId, videoElementRef.value, (result: Result | undefined, error: Exception | undefined, controls: IScannerControls) => {
|
|
33
|
+
if (result) {
|
|
34
|
+
emit('decode', result.getText())
|
|
35
|
+
controls.stop()
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
emit('error', error)
|
|
41
|
+
alert?.addAlert({ message: 'Error starting camera.', alertType: 'error' })
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function scanImageFile(selectedFile: File | File[] | undefined) {
|
|
46
|
+
if (!selectedFile) {
|
|
47
|
+
alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const reader = new FileReader()
|
|
52
|
+
reader.onload = async (event) => {
|
|
53
|
+
try {
|
|
54
|
+
const result = await barcodeReader.decodeFromImageUrl(event.target?.result as string)
|
|
55
|
+
emit('decode', result.getText())
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
alert?.addAlert({ message: 'Unable to read barcode from image.', alertType: 'error' })
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const scanImageSingleFile: File = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
|
|
62
|
+
reader.readAsDataURL(scanImageSingleFile)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onMounted(async () => {
|
|
66
|
+
await checkCameraAvailability()
|
|
67
|
+
if (hasCameraAvailable.value) await startCamera()
|
|
68
|
+
})
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<v-card flat>
|
|
73
|
+
<v-card-item>
|
|
74
|
+
<v-col v-if="hasCameraAvailable">
|
|
75
|
+
<video
|
|
76
|
+
ref="videoElementRef"
|
|
77
|
+
autoplay
|
|
78
|
+
style="max-width: 1024px"
|
|
79
|
+
/>
|
|
80
|
+
<div style="z-index: 2000; position: relative; bottom: 84px; left: 550px">
|
|
81
|
+
<FileBtn
|
|
82
|
+
accept="image/*"
|
|
83
|
+
icon="mdi mdi-image-plus"
|
|
84
|
+
icon-only
|
|
85
|
+
@update:model-value="scanImageFile"
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</v-col>
|
|
89
|
+
<v-col v-else>
|
|
90
|
+
<FileBtn
|
|
91
|
+
accept="image/*"
|
|
92
|
+
text="Upload Image"
|
|
93
|
+
@update:model-value="scanImageFile"
|
|
94
|
+
/>
|
|
95
|
+
</v-col>
|
|
96
|
+
</v-card-item>
|
|
97
|
+
</v-card>
|
|
98
|
+
</template>
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import {ref, withDefaults} from 'vue'
|
|
3
|
-
import * as XLSX from 'xlsx'
|
|
4
|
-
import {VBtn} from 'vuetify/components/VBtn'
|
|
5
|
-
import {useAlert} from '../composables/alert'
|
|
6
|
-
|
|
7
|
-
interface ExportButtonProps extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
|
|
8
|
-
fileName?: string
|
|
9
|
-
modelValue?: object[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const props = withDefaults(defineProps<ExportButtonProps>(), {
|
|
13
|
-
fileName: 'download',
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
const alert = useAlert()
|
|
17
|
-
const loading = ref(false)
|
|
18
|
-
|
|
19
|
-
function exportFile() {
|
|
20
|
-
if (props.modelValue && Array.isArray(props.modelValue)) {
|
|
21
|
-
loading.value = true
|
|
22
|
-
const workbook = XLSX.utils.book_new()
|
|
23
|
-
const worksheet = XLSX.utils.json_to_sheet(props.modelValue)
|
|
24
|
-
const fileName = `${props.fileName}.xlsx`
|
|
25
|
-
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
|
|
26
|
-
XLSX.writeFile(workbook, fileName)
|
|
27
|
-
loading.value = false
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
alert?.addAlert({ message: 'Invalid or no data to export', alertType: 'error' })
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
</script>
|
|
34
|
-
|
|
35
|
-
<template>
|
|
36
|
-
<VBtn
|
|
37
|
-
v-bind="$attrs"
|
|
38
|
-
color="primary"
|
|
39
|
-
:loading="loading"
|
|
40
|
-
:disabled="loading"
|
|
41
|
-
text="Export CSV"
|
|
42
|
-
@click="exportFile"
|
|
43
|
-
>
|
|
44
|
-
<template
|
|
45
|
-
v-for="(_, name, index) in ($slots as {})"
|
|
46
|
-
:key="index"
|
|
47
|
-
#[name]="slotData"
|
|
48
|
-
>
|
|
49
|
-
<slot
|
|
50
|
-
:name="name"
|
|
51
|
-
v-bind="((slotData || {}) as object)"
|
|
52
|
-
/>
|
|
53
|
-
</template>
|
|
54
|
-
</VBtn>
|
|
55
|
-
</template>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import {ref, withDefaults} from 'vue'
|
|
3
|
+
import * as XLSX from 'xlsx'
|
|
4
|
+
import {VBtn} from 'vuetify/components/VBtn'
|
|
5
|
+
import {useAlert} from '../composables/alert'
|
|
6
|
+
|
|
7
|
+
interface ExportButtonProps extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
|
|
8
|
+
fileName?: string
|
|
9
|
+
modelValue?: object[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<ExportButtonProps>(), {
|
|
13
|
+
fileName: 'download',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const alert = useAlert()
|
|
17
|
+
const loading = ref(false)
|
|
18
|
+
|
|
19
|
+
function exportFile() {
|
|
20
|
+
if (props.modelValue && Array.isArray(props.modelValue)) {
|
|
21
|
+
loading.value = true
|
|
22
|
+
const workbook = XLSX.utils.book_new()
|
|
23
|
+
const worksheet = XLSX.utils.json_to_sheet(props.modelValue)
|
|
24
|
+
const fileName = `${props.fileName}.xlsx`
|
|
25
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
|
|
26
|
+
XLSX.writeFile(workbook, fileName)
|
|
27
|
+
loading.value = false
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
alert?.addAlert({ message: 'Invalid or no data to export', alertType: 'error' })
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<VBtn
|
|
37
|
+
v-bind="$attrs"
|
|
38
|
+
color="primary"
|
|
39
|
+
:loading="loading"
|
|
40
|
+
:disabled="loading"
|
|
41
|
+
text="Export CSV"
|
|
42
|
+
@click="exportFile"
|
|
43
|
+
>
|
|
44
|
+
<template
|
|
45
|
+
v-for="(_, name, index) in ($slots as {})"
|
|
46
|
+
:key="index"
|
|
47
|
+
#[name]="slotData"
|
|
48
|
+
>
|
|
49
|
+
<slot
|
|
50
|
+
:name="name"
|
|
51
|
+
v-bind="((slotData || {}) as object)"
|
|
52
|
+
/>
|
|
53
|
+
</template>
|
|
54
|
+
</VBtn>
|
|
55
|
+
</template>
|