@ramathibodi/nuxt-commons 0.1.36 → 0.1.38
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/dist/module.json +1 -1
- package/dist/runtime/components/ExportCSV.vue +51 -18
- package/dist/runtime/components/ImportCSV.vue +63 -12
- package/dist/runtime/components/form/Iterator.vue +256 -230
- package/dist/runtime/components/form/SignPad.vue +1 -1
- package/dist/runtime/components/form/Table.vue +164 -138
- package/dist/runtime/components/form/images/Capture.vue +83 -76
- package/dist/runtime/components/form/images/Edit.vue +19 -11
- package/package.json +2 -2
package/dist/module.json
CHANGED
|
@@ -1,43 +1,76 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import {ref} from 'vue'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
3
|
import * as XLSX from 'xlsx'
|
|
4
|
-
import {VBtn} from 'vuetify/components/VBtn'
|
|
5
|
-
import {useAlert} from '../composables/alert'
|
|
4
|
+
import { VBtn } from 'vuetify/components/VBtn'
|
|
5
|
+
import { useAlert } from '../composables/alert'
|
|
6
6
|
|
|
7
7
|
interface ExportButtonProps extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
|
|
8
8
|
fileName?: string
|
|
9
|
+
sheetName?: string
|
|
9
10
|
modelValue?: object[]
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const props = withDefaults(defineProps<ExportButtonProps>(), {
|
|
13
14
|
fileName: 'download',
|
|
15
|
+
sheetName: 'Sheet1',
|
|
14
16
|
})
|
|
15
17
|
|
|
16
18
|
const alert = useAlert()
|
|
17
19
|
const loading = ref(false)
|
|
18
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Triggers file export
|
|
23
|
+
*/
|
|
19
24
|
function exportFile() {
|
|
20
|
-
if (props.modelValue && Array.isArray(props.modelValue)) {
|
|
25
|
+
if (props.modelValue && Array.isArray(props.modelValue) && props.modelValue.length > 0) {
|
|
21
26
|
loading.value = true
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const workbook = XLSX.utils.book_new()
|
|
30
|
+
const worksheet = XLSX.utils.json_to_sheet(flattenNestedFields(props.modelValue))
|
|
31
|
+
const fileName = `${props.fileName}.xlsx`
|
|
32
|
+
|
|
33
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, props.sheetName)
|
|
34
|
+
XLSX.writeFile(workbook, fileName)
|
|
35
|
+
} catch (error: any) {
|
|
36
|
+
alert?.addAlert({ message: `Export failed: ${error.message}`, alertType: 'error' })
|
|
37
|
+
} finally {
|
|
38
|
+
loading.value = false
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
30
41
|
alert?.addAlert({ message: 'Invalid or no data to export', alertType: 'error' })
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Recursively flattens nested fields for export
|
|
47
|
+
* @param items - Array of objects to flatten
|
|
48
|
+
*/
|
|
49
|
+
function flattenNestedFields(items: any[]) {
|
|
50
|
+
return items.map((item: any) => {
|
|
51
|
+
return flattenObject(item)
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Recursively flattens an object, converting nested keys into dot-separated keys
|
|
57
|
+
* @param obj - Object to flatten
|
|
58
|
+
* @param parentKey - Parent key (for recursion)
|
|
59
|
+
* @param separator - Separator for nested keys
|
|
60
|
+
*/
|
|
61
|
+
function flattenObject(obj: any, parentKey = '', separator = '.') {
|
|
62
|
+
return Object.keys(obj).reduce((acc: any, key: string) => {
|
|
63
|
+
const newKey = parentKey ? `${parentKey}${separator}${key}` : key
|
|
64
|
+
const value = obj[key]
|
|
65
|
+
|
|
66
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
67
|
+
Object.assign(acc, flattenObject(value, newKey, separator))
|
|
68
|
+
} else {
|
|
69
|
+
acc[newKey] = typeof value === 'object' ? JSON.stringify(value) : value
|
|
38
70
|
}
|
|
39
|
-
|
|
40
|
-
|
|
71
|
+
|
|
72
|
+
return acc
|
|
73
|
+
}, {})
|
|
41
74
|
}
|
|
42
75
|
</script>
|
|
43
76
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import * as XLSX from 'xlsx'
|
|
3
|
-
import {ref} from 'vue'
|
|
4
|
-
import {useAlert} from '../composables/alert'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import { useAlert } from '../composables/alert'
|
|
5
5
|
|
|
6
6
|
const alert = useAlert()
|
|
7
7
|
const emit = defineEmits<{
|
|
@@ -23,15 +23,17 @@ function uploadedFile(files: File[] | File | undefined) {
|
|
|
23
23
|
|
|
24
24
|
const fileExtension = files.name.slice(files.name.lastIndexOf('.')).toLowerCase()
|
|
25
25
|
if (!['.xlsx', '.csv'].includes(fileExtension)) {
|
|
26
|
-
alert?.addAlert({ message:
|
|
26
|
+
alert?.addAlert({ message: `Please upload a file with .csv or .xlsx extension only (${files.name})`, alertType: 'error' })
|
|
27
27
|
return
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const reader = new FileReader()
|
|
31
31
|
reader.onload = (e: ProgressEvent<FileReader>) => {
|
|
32
32
|
const workbook = XLSX.read(e.target?.result)
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const parsedData = parseAndAggregateColumns(
|
|
34
|
+
XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]])
|
|
35
|
+
)
|
|
36
|
+
emit('import', parsedData)
|
|
35
37
|
loading.value = false
|
|
36
38
|
fileBtnRef.value.reset()
|
|
37
39
|
}
|
|
@@ -39,14 +41,63 @@ function uploadedFile(files: File[] | File | undefined) {
|
|
|
39
41
|
reader.readAsArrayBuffer(files)
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Recursively aggregates nested columns and properties.
|
|
46
|
+
* @param items - Array of data from the Excel sheet
|
|
47
|
+
*/
|
|
48
|
+
const parseAndAggregateColumns = (items: any[]) => {
|
|
49
|
+
return items.map((item: any) => {
|
|
50
|
+
const aggregatedItem: any = {}
|
|
51
|
+
|
|
52
|
+
for (const key in item) {
|
|
53
|
+
if (key.includes('.')) {
|
|
54
|
+
// Extract root key and subKey
|
|
55
|
+
const [rootKey, ...subKeys] = key.split('.')
|
|
56
|
+
|
|
57
|
+
// Recursively aggregate subKeys
|
|
58
|
+
aggregatedItem[rootKey] = aggregatedItem[rootKey] || {}
|
|
59
|
+
assignNestedValue(aggregatedItem[rootKey], subKeys, parseIfJson(item[key]))
|
|
60
|
+
} else {
|
|
61
|
+
// Directly assign root-level properties
|
|
62
|
+
aggregatedItem[key] = parseIfJson(item[key])
|
|
63
|
+
}
|
|
46
64
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
65
|
+
|
|
66
|
+
return aggregatedItem
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Recursively assigns a value to a nested key structure.
|
|
72
|
+
* @param obj - The object to assign to
|
|
73
|
+
* @param keys - Array of keys leading to the final property
|
|
74
|
+
* @param value - The value to assign
|
|
75
|
+
*/
|
|
76
|
+
const assignNestedValue = (obj: any, keys: string[], value: any) => {
|
|
77
|
+
const [currentKey, ...remainingKeys] = keys
|
|
78
|
+
if (remainingKeys.length === 0) {
|
|
79
|
+
obj[currentKey] = value
|
|
80
|
+
} else {
|
|
81
|
+
obj[currentKey] = obj[currentKey] || {}
|
|
82
|
+
assignNestedValue(obj[currentKey], remainingKeys, value)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Attempt to parse a value as JSON or array.
|
|
88
|
+
* @param value - The value to parse
|
|
89
|
+
*/
|
|
90
|
+
const parseIfJson = (value: any) => {
|
|
91
|
+
if (typeof value === 'string') {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(value)
|
|
94
|
+
} catch {
|
|
95
|
+
// If parsing fails, return the original value
|
|
96
|
+
return value
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return value
|
|
100
|
+
}
|
|
50
101
|
</script>
|
|
51
102
|
|
|
52
103
|
<template>
|