@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,379 +1,391 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
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
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
items.value =
|
|
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
|
-
|
|
127
|
-
items.value[index
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (callback) callback.done()
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function
|
|
135
|
-
const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
|
|
136
|
-
|
|
137
|
-
if (index
|
|
138
|
-
const temp = items.value[index
|
|
139
|
-
items.value[index
|
|
140
|
-
items.value[index] = temp
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (callback) callback.done()
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function
|
|
147
|
-
const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
|
|
148
|
-
|
|
149
|
-
if (index
|
|
150
|
-
const
|
|
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
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
#
|
|
305
|
-
>
|
|
306
|
-
<
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
>
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
<v-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
<template #
|
|
357
|
-
<slot
|
|
358
|
-
name="
|
|
359
|
-
v-bind="slotData"
|
|
360
|
-
/>
|
|
361
|
-
</template>
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
</
|
|
374
|
-
</
|
|
375
|
-
</
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* FormTable is a schema-driven form field component that binds model data, renders field UI, and emits normalized updates.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
import {VDataTable} from 'vuetify/components/VDataTable'
|
|
7
|
+
import {VInput} from 'vuetify/components/VInput'
|
|
8
|
+
import {computed, defineOptions,defineExpose, nextTick, ref, useAttrs, watch, useTemplateRef} from 'vue'
|
|
9
|
+
import {omit} from 'lodash-es'
|
|
10
|
+
import {useDialog} from "../../composables/dialog"
|
|
11
|
+
import type {FormDialogCallback} from '../../types/formDialog'
|
|
12
|
+
import { useLocalStorageModel, type PersistSlimProps } from '../../composables/localStorageModel'
|
|
13
|
+
|
|
14
|
+
defineOptions({
|
|
15
|
+
inheritAttrs: false,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
|
|
19
|
+
title: string // Toolbar title and default export file name shown for this table section.
|
|
20
|
+
noDataText?: string // Text rendered by the data table when `items` is empty after filtering/loading.
|
|
21
|
+
modelValue?: Record<string, any>[] // Source rows from parent; component normalizes, mutates order, then emits updated rows.
|
|
22
|
+
modelKey?: string // Unique row key used for update/delete/reorder; missing keys are auto-generated incrementally.
|
|
23
|
+
dialogWidth?: string | number // Passed to row edit dialog width to control normal (non-fullscreen) modal size.
|
|
24
|
+
dialogMaxWidth?: string | number // Upper width bound for the edit dialog on large screens.
|
|
25
|
+
dialogHeight?: string | number // Passed to row edit dialog height to control vertical editing space.
|
|
26
|
+
dialogMaxHeight?: string | number // Upper height bound for the edit dialog before internal scrolling.
|
|
27
|
+
dialogFullscreen?: boolean // Default open mode for row editor dialog (`true` opens full screen).
|
|
28
|
+
initialData?: Record<string, any> // Seed object merged into new rows before user input.
|
|
29
|
+
toolbarColor?: string // Vuetify color applied to toolbar and default action buttons.
|
|
30
|
+
importable?: boolean // Enables Import button and `@import` flow for adding multiple rows from file.
|
|
31
|
+
exportable?: boolean // Enables Export button for current `items` dataset.
|
|
32
|
+
insertable?: boolean // Enables Add button and creation flow for new rows.
|
|
33
|
+
searchable?: boolean // Shows toolbar search input and binds keyword to table filtering.
|
|
34
|
+
inputPad?: boolean // Enables embedded pad editing workflow instead of relying only on dialog.
|
|
35
|
+
inputPadOnly?: boolean // Uses pad editor exclusively and skips dialog open/close behavior.
|
|
36
|
+
saveAndStay?: boolean // Forwards to child editor to keep it open after save (batch editing).
|
|
37
|
+
stringFields?: Array<string> // Dot-path fields preserved as string during import/export flattening and parsing.
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Public props accepted by FormTable.
|
|
42
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
43
|
+
*/
|
|
44
|
+
const props = withDefaults(defineProps<Props & PersistSlimProps>(), {
|
|
45
|
+
noDataText: 'ไม่พบข้อมูล',
|
|
46
|
+
dialogFullscreen: false,
|
|
47
|
+
modelKey: 'id',
|
|
48
|
+
toolbarColor: 'primary',
|
|
49
|
+
importable: true,
|
|
50
|
+
exportable: true,
|
|
51
|
+
insertable: true,
|
|
52
|
+
searchable: true,
|
|
53
|
+
inputPad: false,
|
|
54
|
+
inputPadOnly: false,
|
|
55
|
+
saveAndStay: false,
|
|
56
|
+
stringFields: ()=>[],
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Custom events emitted by FormTable.
|
|
61
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
62
|
+
*/
|
|
63
|
+
const emit = defineEmits(['update:modelValue','open:dialog','close:dialog'])
|
|
64
|
+
const attrs = useAttrs()
|
|
65
|
+
const plainAttrs = computed(() => {
|
|
66
|
+
return omit(attrs, ['modelValue', 'onUpdate:modelValue'])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const inputRef = useTemplateRef<VInput>("inputRef")
|
|
70
|
+
|
|
71
|
+
const items = ref<Record<string, any>[]>([])
|
|
72
|
+
const search = ref<string>()
|
|
73
|
+
const currentItem = ref<Record<string, any> | undefined>(undefined)
|
|
74
|
+
|
|
75
|
+
useLocalStorageModel(items,props)
|
|
76
|
+
|
|
77
|
+
function setSearch(keyword: string) {
|
|
78
|
+
search.value = keyword
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const isDialogOpen = ref<boolean>(false)
|
|
82
|
+
|
|
83
|
+
watch(() => props.modelValue, (newValue) => {
|
|
84
|
+
if (!Array.isArray(newValue) || !newValue.every(item => typeof item === 'object')) {
|
|
85
|
+
items.value = []
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
let maxKey = 0
|
|
89
|
+
|
|
90
|
+
newValue.forEach((item) => {
|
|
91
|
+
if (!item.hasOwnProperty(props.modelKey)) {
|
|
92
|
+
maxKey = Math.max(maxKey, ...newValue.map(i => i[props.modelKey] || 0))
|
|
93
|
+
item[props.modelKey] = maxKey + 1
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
items.value = newValue
|
|
98
|
+
}
|
|
99
|
+
}, { immediate: true })
|
|
100
|
+
|
|
101
|
+
watch(items, (newValue) => {
|
|
102
|
+
emit('update:modelValue', newValue)
|
|
103
|
+
}, { deep: true })
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
function createItem(item: Record<string, any>, callback?: FormDialogCallback) {
|
|
107
|
+
if (items.value.length > 0) item[props.modelKey] = Math.max(...items.value.map(i => i[props.modelKey] || 0)) + 1
|
|
108
|
+
else item[props.modelKey] = 1
|
|
109
|
+
|
|
110
|
+
items.value.push(item)
|
|
111
|
+
|
|
112
|
+
if (callback && callback.setData) callback.setData(item)
|
|
113
|
+
if (callback) callback.done()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function importItems(importItems: Record<string, any>[], callback?: FormDialogCallback) {
|
|
117
|
+
importItems.forEach((item) => {
|
|
118
|
+
createItem(item)
|
|
119
|
+
})
|
|
120
|
+
if (callback) callback.done()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function updateItem(newItem: Record<string, any>, callback?: FormDialogCallback) {
|
|
124
|
+
const index = items.value.findIndex(item => item[props.modelKey] === newItem[props.modelKey])
|
|
125
|
+
|
|
126
|
+
if (index !== -1) {
|
|
127
|
+
items.value[index] = newItem
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (callback && callback.setData) callback.setData(newItem)
|
|
131
|
+
if (callback) callback.done()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function moveUpItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
|
|
135
|
+
const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
|
|
136
|
+
|
|
137
|
+
if (index > 0) {
|
|
138
|
+
const temp = items.value[index - 1]
|
|
139
|
+
items.value[index - 1] = items.value[index]
|
|
140
|
+
items.value[index] = temp
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (callback) callback.done()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function moveDownItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
|
|
147
|
+
const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey])
|
|
148
|
+
|
|
149
|
+
if (index >= 0 && index < items.value.length - 1) {
|
|
150
|
+
const temp = items.value[index + 1]
|
|
151
|
+
items.value[index + 1] = items.value[index]
|
|
152
|
+
items.value[index] = temp
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (callback) callback.done()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function moveToItem(currentItem: Record<string, any>, callback?: FormDialogCallback) {
|
|
159
|
+
const index = items.value.findIndex(item => item[props.modelKey] === currentItem[props.modelKey]);
|
|
160
|
+
|
|
161
|
+
if (index !== -1) {
|
|
162
|
+
const newPosition = prompt("Enter the new position (0-based index):");
|
|
163
|
+
const parsedPosition = parseInt(<string>newPosition, 10);
|
|
164
|
+
|
|
165
|
+
if (isNaN(parsedPosition) || parsedPosition < 0 || parsedPosition >= items.value.length) {
|
|
166
|
+
alert("Invalid position entered. Please enter a number between 0 and " + (items.value.length - 1));
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const [temp] = items.value.splice(index, 1);
|
|
171
|
+
|
|
172
|
+
items.value.splice(parsedPosition, 0, temp);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (callback) callback.done();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function deleteItem(deleteItem: Record<string, any>, callback?: FormDialogCallback) {
|
|
179
|
+
const index = items.value.findIndex(item => item[props.modelKey] === deleteItem[props.modelKey])
|
|
180
|
+
|
|
181
|
+
if (index !== -1) {
|
|
182
|
+
let confirm = await useDialog().confirm({message: "Do you want to delete record?"})
|
|
183
|
+
if (confirm) {
|
|
184
|
+
items.value.splice(index, 1)
|
|
185
|
+
if (callback) callback.done()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function openDialog(item?: object) {
|
|
191
|
+
if (props.inputPadOnly) inputPadRef.value?.setOriginalData(item)
|
|
192
|
+
else {
|
|
193
|
+
currentItem.value = item
|
|
194
|
+
nextTick(() => {
|
|
195
|
+
isDialogOpen.value = true
|
|
196
|
+
emit('open:dialog',item)
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const inputPadRef = ref()
|
|
201
|
+
|
|
202
|
+
const operation = ref({ openDialog, createItem, updateItem, deleteItem, moveUpItem, moveDownItem,moveToItem,setSearch })
|
|
203
|
+
|
|
204
|
+
const isValid = computed(()=>{
|
|
205
|
+
return inputRef.value?.isValid
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
const errorMessages = computed(()=>{
|
|
209
|
+
return inputRef.value?.errorMessages
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
defineExpose({
|
|
213
|
+
errorMessages,
|
|
214
|
+
isValid,
|
|
215
|
+
reset: ()=>inputRef.value?.reset(),
|
|
216
|
+
resetValidation : ()=>inputRef.value?.resetValidation(),
|
|
217
|
+
validate : ()=>inputRef.value?.validate(),
|
|
218
|
+
operation,
|
|
219
|
+
items
|
|
220
|
+
})
|
|
221
|
+
</script>
|
|
222
|
+
|
|
223
|
+
<template>
|
|
224
|
+
<v-input v-model="items" v-bind="plainAttrs" ref="inputRef">
|
|
225
|
+
<template #default="{isReadonly,isDisabled}">
|
|
226
|
+
<v-container fluid class="ma-0 pa-0">
|
|
227
|
+
<v-card>
|
|
228
|
+
<slot
|
|
229
|
+
name="header"
|
|
230
|
+
:items="items"
|
|
231
|
+
:operation="operation"
|
|
232
|
+
>
|
|
233
|
+
<VToolbar :color="toolbarColor">
|
|
234
|
+
<v-row
|
|
235
|
+
justify="end"
|
|
236
|
+
class="ma-1"
|
|
237
|
+
dense
|
|
238
|
+
no-gutters
|
|
239
|
+
align="center"
|
|
240
|
+
>
|
|
241
|
+
<v-col cols="7">
|
|
242
|
+
<VToolbarTitle class="pl-3">
|
|
243
|
+
<slot name="title">
|
|
244
|
+
{{ title }}
|
|
245
|
+
</slot>
|
|
246
|
+
</VToolbarTitle>
|
|
247
|
+
</v-col>
|
|
248
|
+
<v-col cols="5">
|
|
249
|
+
<slot name="search" :items="items" :operation="operation" v-if="props.searchable">
|
|
250
|
+
<VTextField
|
|
251
|
+
v-model="search"
|
|
252
|
+
class="justify-end w-100"
|
|
253
|
+
density="compact"
|
|
254
|
+
hide-details
|
|
255
|
+
placeholder="ค้นหา"
|
|
256
|
+
clearable
|
|
257
|
+
variant="solo"
|
|
258
|
+
/>
|
|
259
|
+
</slot>
|
|
260
|
+
</v-col>
|
|
261
|
+
</v-row>
|
|
262
|
+
|
|
263
|
+
<VToolbarItems>
|
|
264
|
+
<slot name="toolbarItems" :items="items" :operation="operation"/>
|
|
265
|
+
<ImportCSV
|
|
266
|
+
v-if="props.importable && !(isReadonly.value || isDisabled.value)"
|
|
267
|
+
icon="mdi:mdi-file-upload"
|
|
268
|
+
variant="flat"
|
|
269
|
+
@import="importItems"
|
|
270
|
+
:color="toolbarColor"
|
|
271
|
+
:stringFields="props.stringFields"
|
|
272
|
+
/>
|
|
273
|
+
<ExportCSV
|
|
274
|
+
v-if="props.exportable && items.length && !(isReadonly.value || isDisabled.value)"
|
|
275
|
+
icon="mdi:mdi-file-download"
|
|
276
|
+
variant="flat"
|
|
277
|
+
:file-name="title"
|
|
278
|
+
:model-value="items"
|
|
279
|
+
:color="toolbarColor"
|
|
280
|
+
:stringFields="props.stringFields"
|
|
281
|
+
/>
|
|
282
|
+
<VBtn
|
|
283
|
+
v-if="props.insertable && !props.inputPadOnly && !(isReadonly.value || isDisabled.value)"
|
|
284
|
+
:color="toolbarColor"
|
|
285
|
+
prepend-icon="mdi:mdi-plus"
|
|
286
|
+
variant="flat"
|
|
287
|
+
@click="openDialog()"
|
|
288
|
+
>
|
|
289
|
+
add
|
|
290
|
+
</VBtn>
|
|
291
|
+
</VToolbarItems>
|
|
292
|
+
</VToolbar>
|
|
293
|
+
</slot>
|
|
294
|
+
<v-data-table
|
|
295
|
+
v-bind="plainAttrs"
|
|
296
|
+
color="primary"
|
|
297
|
+
:items="items"
|
|
298
|
+
:search="search"
|
|
299
|
+
>
|
|
300
|
+
<!-- @ts-ignore -->
|
|
301
|
+
<template
|
|
302
|
+
v-for="(_, name, index) in ($slots as {})"
|
|
303
|
+
:key="index"
|
|
304
|
+
#[name]="slotData"
|
|
305
|
+
>
|
|
306
|
+
<slot
|
|
307
|
+
:name="name"
|
|
308
|
+
v-bind="((slotData || {}) as object)"
|
|
309
|
+
:operation="operation"
|
|
310
|
+
:isReadonly="isReadonly"
|
|
311
|
+
:isDisabled="isDisabled"
|
|
312
|
+
/>
|
|
313
|
+
</template>
|
|
314
|
+
<template
|
|
315
|
+
v-if="!$slots['item.operation'] && !(isReadonly.value || isDisabled.value)"
|
|
316
|
+
#item.operation="props"
|
|
317
|
+
>
|
|
318
|
+
<v-icon density="compact" :disabled="props.index==0" @click="moveUpItem(props.item)">mdi:mdi-arrow-up-thick</v-icon>
|
|
319
|
+
<v-icon density="compact" @click="moveToItem(props.item)">fa:fas fa-arrow-right-to-bracket</v-icon>
|
|
320
|
+
<v-icon density="compact" :disabled="props.index==items.length-1" @click="moveDownItem(props.item)">mdi:mdi-arrow-down-thick</v-icon>
|
|
321
|
+
</template>
|
|
322
|
+
<template
|
|
323
|
+
v-if="!$slots['item.action'] && !(isReadonly.value || isDisabled.value)"
|
|
324
|
+
#item.action="{ item }"
|
|
325
|
+
>
|
|
326
|
+
<v-btn
|
|
327
|
+
variant="flat"
|
|
328
|
+
density="compact"
|
|
329
|
+
icon="mdi:mdi-note-edit"
|
|
330
|
+
@click="openDialog(item)"
|
|
331
|
+
/>
|
|
332
|
+
<v-btn
|
|
333
|
+
variant="flat"
|
|
334
|
+
density="compact"
|
|
335
|
+
icon="mdi:mdi-delete"
|
|
336
|
+
@click="deleteItem(item)"
|
|
337
|
+
/>
|
|
338
|
+
</template>
|
|
339
|
+
</v-data-table>
|
|
340
|
+
<FormDialog
|
|
341
|
+
v-model="isDialogOpen"
|
|
342
|
+
:title="title"
|
|
343
|
+
:fullscreen="dialogFullscreen"
|
|
344
|
+
:initial-data="initialData"
|
|
345
|
+
:form-data="currentItem"
|
|
346
|
+
@create="createItem"
|
|
347
|
+
@update="updateItem"
|
|
348
|
+
@afterLeave="emit('close:dialog')"
|
|
349
|
+
:saveAndStay="saveAndStay"
|
|
350
|
+
:width="dialogWidth"
|
|
351
|
+
:height="dialogHeight"
|
|
352
|
+
:max-width="dialogMaxWidth"
|
|
353
|
+
:max-height="dialogMaxHeight"
|
|
354
|
+
v-if="!props.inputPadOnly"
|
|
355
|
+
>
|
|
356
|
+
<template #default="slotData">
|
|
357
|
+
<slot
|
|
358
|
+
name="form"
|
|
359
|
+
v-bind="slotData"
|
|
360
|
+
/>
|
|
361
|
+
</template>
|
|
362
|
+
<template #title="slotData">
|
|
363
|
+
<slot
|
|
364
|
+
name="formTitle"
|
|
365
|
+
v-bind="slotData"
|
|
366
|
+
/>
|
|
367
|
+
</template>
|
|
368
|
+
<template #action="slotData">
|
|
369
|
+
<slot
|
|
370
|
+
name="formAction"
|
|
371
|
+
v-bind="slotData"
|
|
372
|
+
/>
|
|
373
|
+
</template>
|
|
374
|
+
</FormDialog>
|
|
375
|
+
</v-card>
|
|
376
|
+
<slot name="inputPad" :operation="operation" :isReadonly="isReadonly" :isDisabled="isDisabled">
|
|
377
|
+
<v-sheet border rounded class="mt-1" v-if="inputPad && !(isReadonly.value || isDisabled.value)">
|
|
378
|
+
<form-action-pad ref="inputPadRef" :title="props.title" @create="createItem" @update="updateItem" :initial-data="initialData">
|
|
379
|
+
<template #default="slotData">
|
|
380
|
+
<slot
|
|
381
|
+
name="form"
|
|
382
|
+
v-bind="slotData"
|
|
383
|
+
/>
|
|
384
|
+
</template>
|
|
385
|
+
</form-action-pad>
|
|
386
|
+
</v-sheet>
|
|
387
|
+
</slot>
|
|
388
|
+
</v-container>
|
|
389
|
+
</template>
|
|
390
|
+
</v-input>
|
|
391
|
+
</template>
|