@ramathibodi/nuxt-commons 0.1.58 → 0.1.60
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/ImportCSV.vue +2 -1
- package/dist/runtime/components/form/Birthdate.vue +35 -156
- package/dist/runtime/components/form/Date.vue +48 -12
- package/dist/runtime/components/form/DateTime.vue +19 -6
- package/dist/runtime/components/form/Dialog.vue +0 -2
- package/dist/runtime/components/form/Table.vue +8 -0
- package/dist/runtime/components/master/Select.vue +43 -85
- package/dist/runtime/components/model/Select.vue +62 -0
- package/dist/runtime/components/model/Table.vue +8 -0
- package/dist/runtime/composables/lookupList.d.ts +7 -5
- package/dist/runtime/composables/lookupList.js +2 -2
- package/dist/runtime/composables/lookupListMaster.d.ts +4 -2
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -102,7 +102,8 @@ const assignNestedValue = (obj: any, keys: string[], value: any) => {
|
|
|
102
102
|
const parseIfJson = (value: any) => {
|
|
103
103
|
if (typeof value === 'string') {
|
|
104
104
|
try {
|
|
105
|
-
|
|
105
|
+
let parsedValue = JSON.parse(value)
|
|
106
|
+
return (parsedValue==value) ? value : parsedValue
|
|
106
107
|
} catch {
|
|
107
108
|
// If parsing fails, return the original value
|
|
108
109
|
return value
|
|
@@ -1,37 +1,30 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import Datepicker from '@vuepic/vue-datepicker'
|
|
3
2
|
import '@vuepic/vue-datepicker/dist/main.css'
|
|
4
3
|
import {computed, nextTick, onMounted, ref, watch, watchEffect} from 'vue'
|
|
5
|
-
import {
|
|
4
|
+
import {VTextField} from "vuetify/components/VTextField";
|
|
5
|
+
import {isArray, isString} from "lodash-es";
|
|
6
|
+
import {useRules} from "../../composables/utils/validation";
|
|
6
7
|
|
|
7
8
|
interface IDobPrecision {
|
|
8
9
|
yearMonthDay: string
|
|
9
10
|
yearMonth: string
|
|
10
11
|
year: string
|
|
11
12
|
}
|
|
13
|
+
|
|
12
14
|
interface Props {
|
|
13
|
-
readonly?: boolean
|
|
14
|
-
locale?: 'TH' | 'EN'
|
|
15
|
-
format?: dateFormat | string
|
|
16
|
-
modelValue?: string | null
|
|
17
|
-
holiday?: object[] | undefined
|
|
18
|
-
minDate?: Date | string
|
|
19
|
-
maxDate?: Date | string
|
|
20
|
-
pickerOnly?: boolean
|
|
21
15
|
flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[]
|
|
22
16
|
dobPrecisionText?: IDobPrecision
|
|
17
|
+
rules?: typeof VTextField['rules']
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
const props = withDefaults(defineProps<Props>(), {
|
|
26
|
-
readonly: false,
|
|
27
|
-
locale: 'TH',
|
|
28
|
-
format: 'shortDate',
|
|
29
|
-
pickerOnly: false,
|
|
30
21
|
flow: () => ['year', 'month', 'calendar'],
|
|
31
22
|
})
|
|
32
23
|
|
|
33
24
|
const emit = defineEmits(['update:modelValue'])
|
|
34
25
|
|
|
26
|
+
const modelValue = defineModel<string>()
|
|
27
|
+
|
|
35
28
|
const dobPrecisionText = computed(() => {
|
|
36
29
|
if (props.dobPrecisionText) return props.dobPrecisionText
|
|
37
30
|
else return {
|
|
@@ -61,161 +54,47 @@ const changeDobPrecision = () => {
|
|
|
61
54
|
}
|
|
62
55
|
}
|
|
63
56
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const hasTextFieldInput = ref(false)
|
|
70
|
-
|
|
71
|
-
function handleTextFieldFocus(event: Event) {
|
|
72
|
-
isTextFieldFocused.value = true
|
|
73
|
-
nextTick(() => {
|
|
74
|
-
(event.target as HTMLInputElement).select()
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function handleTextFieldInput() {
|
|
79
|
-
if (!hasTextFieldInput.value) {
|
|
80
|
-
hasTextFieldInput.value = true
|
|
81
|
-
isMenuOpen.value = false
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function handleTextFieldBlur() {
|
|
86
|
-
if (hasTextFieldInput.value) {
|
|
87
|
-
updateDate(displayedDate.value)
|
|
88
|
-
hasTextFieldInput.value = false
|
|
89
|
-
}
|
|
90
|
-
isTextFieldFocused.value = false
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function handleTextFieldEnterKey() {
|
|
94
|
-
handleTextFieldBlur()
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function updateDatePicker(dateString: string | null) {
|
|
98
|
-
isMenuOpen.value = false
|
|
99
|
-
updateDate(dateString)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function updateDate(dateString: string | null) {
|
|
103
|
-
const dateTime = Datetime().fromString(dateString, undefined, props.locale)
|
|
104
|
-
if (!dateTime.luxonDateTime.isValid) {
|
|
105
|
-
displayedDate.value = null
|
|
106
|
-
selectedDate.value = null
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
if (dobPrecisionSelected.value=="yearMonth") {
|
|
110
|
-
dateTime.luxonDateTime = dateTime.luxonDateTime.startOf('month')
|
|
111
|
-
}
|
|
112
|
-
if (dobPrecisionSelected.value=="year") {
|
|
113
|
-
dateTime.luxonDateTime = dateTime.luxonDateTime.startOf('year')
|
|
114
|
-
}
|
|
115
|
-
selectedDate.value = dateTime.toFormat('yyyy-MM-dd', 'EN')
|
|
116
|
-
displayedDate.value = selectedDate.value
|
|
57
|
+
const computedRules = computed(() => {
|
|
58
|
+
if (props.rules && isArray(props.rules)) {
|
|
59
|
+
return props.rules.includes("DateHappen") ? props.rules : props.rules.concat("DateHappen")
|
|
60
|
+
} else {
|
|
61
|
+
return ['DateHappen']
|
|
117
62
|
}
|
|
63
|
+
})
|
|
118
64
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
function formatDate(dateString: string | null) {
|
|
123
|
-
if (!dateString) return null
|
|
124
|
-
|
|
125
|
-
let displayFormat = props.format
|
|
65
|
+
const dobFormat = computed(() => {
|
|
66
|
+
let displayFormat : string|undefined = undefined
|
|
126
67
|
if (dobPrecisionSelected.value=="yearMonth") {
|
|
127
68
|
displayFormat = "MMM yyyy"
|
|
128
69
|
}
|
|
129
70
|
if (dobPrecisionSelected.value=="year") {
|
|
130
71
|
displayFormat = "yyyy"
|
|
131
72
|
}
|
|
132
|
-
|
|
133
|
-
const dateTime = Datetime().fromString(dateString, undefined, props.locale)
|
|
134
|
-
return dateTime.toFormat(displayFormat, props.locale)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function handleTextFieldClear() {
|
|
138
|
-
resetDatePicker()
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function resetDatePicker() {
|
|
142
|
-
selectedDate.value = null
|
|
143
|
-
displayedDate.value = null
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
watchEffect(() => {
|
|
147
|
-
if (!isTextFieldFocused.value && selectedDate.value) {
|
|
148
|
-
displayedDate.value = formatDate(selectedDate.value)
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
displayedDate.value = selectedDate.value
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
watch(selectedDate, (newValue) => {
|
|
156
|
-
emit('update:modelValue', newValue)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
watch(() => props.modelValue, () => {
|
|
160
|
-
updateDate(props.modelValue || null)
|
|
161
|
-
}, { immediate: true })
|
|
162
|
-
|
|
163
|
-
watch(dobPrecisionSelected,()=>{
|
|
164
|
-
updateDate(selectedDate.value)
|
|
73
|
+
return displayFormat
|
|
165
74
|
})
|
|
166
|
-
|
|
167
|
-
function toggleMenuOpen(trigger: string) {
|
|
168
|
-
if ((trigger === 'textField' && props.pickerOnly) || (trigger === 'icon' && !props.pickerOnly)) {
|
|
169
|
-
isMenuOpen.value = true
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
75
|
</script>
|
|
173
76
|
|
|
174
77
|
<template>
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
78
|
+
<FormDate
|
|
79
|
+
v-model="modelValue"
|
|
80
|
+
:flow="props.flow"
|
|
81
|
+
:format="dobFormat"
|
|
82
|
+
:rules="computedRules"
|
|
178
83
|
>
|
|
179
|
-
<template #
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
@click="toggleMenuOpen('textField')"
|
|
84
|
+
<template #append="{ isReadonly, isDisabled, activatorProps, toggleMenuOpen}">
|
|
85
|
+
<span
|
|
86
|
+
style="cursor: pointer;"
|
|
87
|
+
class="font-weight-medium"
|
|
88
|
+
@click="(isReadonly.value || isDisabled.value) ? undefined : changeDobPrecision()"
|
|
89
|
+
>{{ dobPrecisionDisplay }}</span>
|
|
90
|
+
|
|
91
|
+
<v-icon
|
|
92
|
+
:disabled="isReadonly.value || isDisabled.value"
|
|
93
|
+
v-bind="activatorProps"
|
|
94
|
+
@click="toggleMenuOpen('icon')"
|
|
191
95
|
>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
style="cursor: pointer;"
|
|
195
|
-
class="font-weight-medium"
|
|
196
|
-
@click="changeDobPrecision"
|
|
197
|
-
>{{ dobPrecisionDisplay }}</span>
|
|
198
|
-
|
|
199
|
-
<v-icon
|
|
200
|
-
v-bind="activatorProps"
|
|
201
|
-
@click="toggleMenuOpen('icon')"
|
|
202
|
-
>
|
|
203
|
-
fa:fa-regular fa-calendar
|
|
204
|
-
</v-icon>
|
|
205
|
-
</template>
|
|
206
|
-
</v-text-field>
|
|
96
|
+
fa:fa-regular fa-calendar
|
|
97
|
+
</v-icon>
|
|
207
98
|
</template>
|
|
208
|
-
|
|
209
|
-
v-model="selectedDate"
|
|
210
|
-
model-type="yyyy-MM-dd"
|
|
211
|
-
:enable-time-picker="false"
|
|
212
|
-
:flow="flow"
|
|
213
|
-
:min-date="props.minDate"
|
|
214
|
-
:max-date="props.maxDate"
|
|
215
|
-
auto-apply
|
|
216
|
-
inline
|
|
217
|
-
:locale="locale"
|
|
218
|
-
@update:model-value="updateDatePicker"
|
|
219
|
-
/>
|
|
220
|
-
</v-menu>
|
|
99
|
+
</FormDate>
|
|
221
100
|
</template>
|
|
@@ -3,9 +3,10 @@ import { ref, watch, watchEffect, nextTick, defineExpose, computed} from 'vue'
|
|
|
3
3
|
import { VTextField } from 'vuetify/components/VTextField'
|
|
4
4
|
import Datepicker from '@vuepic/vue-datepicker'
|
|
5
5
|
import '@vuepic/vue-datepicker/dist/main.css'
|
|
6
|
-
import {isArray, isString} from "lodash-es";
|
|
6
|
+
import { isArray, isString } from "lodash-es";
|
|
7
7
|
import { type dateFormat, Datetime } from '../../utils/datetime'
|
|
8
8
|
import { useRules } from "../../composables/utils/validation";
|
|
9
|
+
import { th } from 'date-fns/locale';
|
|
9
10
|
|
|
10
11
|
interface Props extends /* @vue-ignore */ InstanceType<typeof VTextField['$props']> {
|
|
11
12
|
locale?: 'TH' | 'EN'
|
|
@@ -17,6 +18,8 @@ interface Props extends /* @vue-ignore */ InstanceType<typeof VTextField['$props
|
|
|
17
18
|
pickerOnly?: boolean
|
|
18
19
|
flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[]
|
|
19
20
|
rules?: typeof VTextField['rules']
|
|
21
|
+
|
|
22
|
+
defaultDate?: boolean | string
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -24,6 +27,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
24
27
|
format: 'shortDate',
|
|
25
28
|
pickerOnly: false,
|
|
26
29
|
flow: () => [],
|
|
30
|
+
|
|
31
|
+
defaultDate: false,
|
|
27
32
|
})
|
|
28
33
|
|
|
29
34
|
const emit = defineEmits(['update:modelValue'])
|
|
@@ -155,8 +160,29 @@ watch(selectedDate, (newValue) => {
|
|
|
155
160
|
emit('update:modelValue', newValue)
|
|
156
161
|
})
|
|
157
162
|
|
|
158
|
-
watch(() => props.modelValue, () => {
|
|
159
|
-
|
|
163
|
+
watch(() => props.modelValue, (newValue,oldValue) => {
|
|
164
|
+
if (!oldValue && !newValue && props.defaultDate) {
|
|
165
|
+
if (!oldValue && !newValue && props.defaultDate) {
|
|
166
|
+
let defaultDate: string | null = null;
|
|
167
|
+
|
|
168
|
+
if (props.defaultDate === true) {
|
|
169
|
+
defaultDate = Datetime().now().toFormat('yyyy-MM-dd', 'EN');
|
|
170
|
+
} else if (typeof props.defaultDate === 'string' &&/^[+-]?\d+$/.test(props.defaultDate.trim())) {
|
|
171
|
+
defaultDate = Datetime().now().luxonDateTime
|
|
172
|
+
.plus({ days: Number(props.defaultDate) })
|
|
173
|
+
.toFormat('yyyy-MM-dd');
|
|
174
|
+
} else {
|
|
175
|
+
const dateTime = Datetime().fromString(props.defaultDate)
|
|
176
|
+
if (dateTime.luxonDateTime.isValid) defaultDate = dateTime.toFormat('yyyy-MM-dd')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
updateDate(defaultDate);
|
|
180
|
+
} else {
|
|
181
|
+
updateDate(newValue ?? null);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
updateDate(props.modelValue || null)
|
|
185
|
+
}
|
|
160
186
|
}, { immediate: true })
|
|
161
187
|
|
|
162
188
|
function toggleMenuOpen(trigger: string) {
|
|
@@ -196,14 +222,16 @@ defineExpose({
|
|
|
196
222
|
@click:clear="handleTextFieldClear"
|
|
197
223
|
@click="toggleMenuOpen('textField')"
|
|
198
224
|
>
|
|
199
|
-
<template #append="
|
|
200
|
-
<v-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
225
|
+
<template #append="appendProps">
|
|
226
|
+
<slot name="append" v-bind="appendProps" :toggleMenuOpen="toggleMenuOpen" :activatorProps="activatorProps">
|
|
227
|
+
<v-icon
|
|
228
|
+
:disabled="appendProps.isReadonly?.value || appendProps.isDisabled?.value"
|
|
229
|
+
v-bind="activatorProps"
|
|
230
|
+
@click="toggleMenuOpen('icon')"
|
|
231
|
+
>
|
|
232
|
+
fa:fa-regular fa-calendar
|
|
233
|
+
</v-icon>
|
|
234
|
+
</slot>
|
|
207
235
|
</template>
|
|
208
236
|
</v-text-field>
|
|
209
237
|
</template>
|
|
@@ -217,7 +245,15 @@ defineExpose({
|
|
|
217
245
|
auto-apply
|
|
218
246
|
inline
|
|
219
247
|
:locale="locale"
|
|
248
|
+
:format-locale="(locale=='TH') ? th : undefined"
|
|
220
249
|
@update:model-value="updateDatePicker"
|
|
221
|
-
|
|
250
|
+
>
|
|
251
|
+
<template #year="{value}" v-if="locale=='TH'">
|
|
252
|
+
{{value+543}}
|
|
253
|
+
</template>
|
|
254
|
+
<template #year-overlay-value="{value}" v-if="locale=='TH'">
|
|
255
|
+
{{value+543}}
|
|
256
|
+
</template>
|
|
257
|
+
</Datepicker>
|
|
222
258
|
</v-menu>
|
|
223
259
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {ref, computed, watch, watchEffect, nextTick, useAttrs} from 'vue'
|
|
3
|
+
import {union, isBoolean, isArray, isString, omit} from 'lodash-es'
|
|
4
4
|
import { VTextField } from 'vuetify/components/VTextField'
|
|
5
5
|
import { type dateFormat, Datetime } from '../../utils/datetime'
|
|
6
6
|
import { useRules } from '../../composables/utils/validation'
|
|
@@ -39,6 +39,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
39
39
|
|
|
40
40
|
const emit = defineEmits(['update:modelValue'])
|
|
41
41
|
|
|
42
|
+
const attrs = useAttrs()
|
|
43
|
+
const plainAttrs = computed(() => {
|
|
44
|
+
return omit(attrs, ['modelValue', 'onUpdate:modelValue','defaultDate','defaultDateTime','fixedDate'])
|
|
45
|
+
})
|
|
46
|
+
|
|
42
47
|
const dateRef = ref<VTextField>()
|
|
43
48
|
const timeRef = ref<VTextField>()
|
|
44
49
|
|
|
@@ -109,8 +114,16 @@ function reset() {
|
|
|
109
114
|
}
|
|
110
115
|
} else {
|
|
111
116
|
if (props.defaultDate) {
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
if (props.defaultDate === true) {
|
|
118
|
+
datePart.value = Datetime().now().toFormat('yyyy-MM-dd', 'EN');
|
|
119
|
+
} else if (typeof props.defaultDate === 'string' &&/^[+-]?\d+$/.test(props.defaultDate.trim())) {
|
|
120
|
+
datePart.value = Datetime().now().luxonDateTime
|
|
121
|
+
.plus({ days: Number(props.defaultDate) })
|
|
122
|
+
.toFormat('yyyy-MM-dd');
|
|
123
|
+
} else {
|
|
124
|
+
const dateTime = Datetime().fromString(props.defaultDate)
|
|
125
|
+
if (dateTime.luxonDateTime.isValid) datePart.value = dateTime.toFormat('yyyy-MM-dd')
|
|
126
|
+
}
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
}
|
|
@@ -175,7 +188,7 @@ watch(() => props.modelValue, () => {
|
|
|
175
188
|
:disabled="fixedDate"
|
|
176
189
|
:min-date="minDate"
|
|
177
190
|
:max-date="maxDate"
|
|
178
|
-
v-bind="
|
|
191
|
+
v-bind="plainAttrs"
|
|
179
192
|
/>
|
|
180
193
|
</v-col>
|
|
181
194
|
<v-col cols="4">
|
|
@@ -187,7 +200,7 @@ watch(() => props.modelValue, () => {
|
|
|
187
200
|
:enable-seconds="enableSeconds"
|
|
188
201
|
:picker-only="pickerOnly"
|
|
189
202
|
:readonly="readonly"
|
|
190
|
-
v-bind="
|
|
203
|
+
v-bind="plainAttrs"
|
|
191
204
|
/>
|
|
192
205
|
</v-col>
|
|
193
206
|
</v-row>
|
|
@@ -4,7 +4,6 @@ import {cloneDeep, isEqual} from 'lodash-es'
|
|
|
4
4
|
import type {FormDialogCallback} from '../../types/formDialog'
|
|
5
5
|
|
|
6
6
|
interface Props {
|
|
7
|
-
maxWidth?: number | string
|
|
8
7
|
fullscreen?: boolean
|
|
9
8
|
title?: string
|
|
10
9
|
initialData?: object
|
|
@@ -104,7 +103,6 @@ watch(() => isShowing.value, (newValue) => {
|
|
|
104
103
|
<v-dialog
|
|
105
104
|
v-model="isShowing"
|
|
106
105
|
:fullscreen="fullscreen"
|
|
107
|
-
:max-width="maxWidth"
|
|
108
106
|
persistent
|
|
109
107
|
scrollable
|
|
110
108
|
>
|
|
@@ -15,6 +15,10 @@ interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props
|
|
|
15
15
|
noDataText?: string
|
|
16
16
|
modelValue?: Record<string, any>[]
|
|
17
17
|
modelKey?: string
|
|
18
|
+
dialogWidth?: string | number
|
|
19
|
+
dialogMaxWidth?: string | number
|
|
20
|
+
dialogHeight?: string | number
|
|
21
|
+
dialogMaxHeight?: string | number
|
|
18
22
|
dialogFullscreen?: boolean
|
|
19
23
|
initialData?: Record<string, any>
|
|
20
24
|
toolbarColor?: string
|
|
@@ -327,6 +331,10 @@ defineExpose({
|
|
|
327
331
|
@update="updateItem"
|
|
328
332
|
@afterLeave="emit('close:dialog')"
|
|
329
333
|
:saveAndStay="saveAndStay"
|
|
334
|
+
:width="dialogWidth"
|
|
335
|
+
:height="dialogHeight"
|
|
336
|
+
:max-width="dialogMaxWidth"
|
|
337
|
+
:max-height="dialogMaxHeight"
|
|
330
338
|
v-if="!props.inputPadOnly"
|
|
331
339
|
>
|
|
332
340
|
<template #default="slotData">
|
|
@@ -1,96 +1,54 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {useGraphQl} from '../../composables/graphql'
|
|
6
|
-
import {useAlert} from '../../composables/alert'
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { VCombobox } from 'vuetify/components/VCombobox'
|
|
3
|
+
import { withDefaults } from 'vue'
|
|
4
|
+
import { useLookupListMaster, type StaticMasterLikeProps } from '../../composables/lookupListMaster'
|
|
7
5
|
|
|
8
|
-
interface Props extends /* @vue-ignore */ InstanceType<typeof
|
|
9
|
-
modelValue?: string
|
|
10
|
-
groupKey: string
|
|
11
|
-
fields?: string[]
|
|
12
|
-
lang?: 'TH' | 'EN'
|
|
13
|
-
}
|
|
14
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
-
lang: 'TH',
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
const emit = defineEmits(['update:modelValue'])
|
|
19
|
-
const items = ref<Array<any>>([])
|
|
20
|
-
const alert = useAlert()
|
|
21
|
-
const selectedItem = ref<any>()
|
|
22
|
-
function query() {
|
|
23
|
-
const variables: Record<string, any> = { groupKey: { value: props.groupKey, required: true } }
|
|
24
|
-
let fields: any[] = ['itemCode', 'itemValue', 'itemValueAlternative']
|
|
25
|
-
if (props.fields) fields = concat(fields, props.fields)
|
|
26
|
-
|
|
27
|
-
useGraphQl().queryPromise('masterItemByGroupKey', fields, variables).then((result: any) => {
|
|
28
|
-
items.value = result
|
|
29
|
-
}).catch((error) => {
|
|
30
|
-
alert?.addAlert({
|
|
31
|
-
message: `${error.message}`,
|
|
32
|
-
alertType: 'error',
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
watch(() => props.groupKey, () => {
|
|
38
|
-
query()
|
|
39
|
-
}, { immediate: true })
|
|
40
|
-
|
|
41
|
-
watch(selectedItem, (newValue) => {
|
|
42
|
-
emit('update:modelValue', newValue.itemCode)
|
|
43
|
-
})
|
|
6
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VCombobox['$props']> {}
|
|
44
7
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
{ immediate: true }
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
const itemTitleField = computed(() => {
|
|
57
|
-
if (props.lang == 'TH') return 'itemValue'
|
|
58
|
-
else return 'itemValueAlternative'
|
|
8
|
+
const props = withDefaults(defineProps<Props & StaticMasterLikeProps>(), {
|
|
9
|
+
sortBy: 'itemValue',
|
|
10
|
+
showCode: false,
|
|
11
|
+
lang: 'TH',
|
|
12
|
+
noDataText: 'ไม่พบข้อมูล',
|
|
13
|
+
waitForFilter: false,
|
|
14
|
+
cache: false,
|
|
59
15
|
})
|
|
60
16
|
|
|
61
|
-
|
|
17
|
+
const {
|
|
18
|
+
computedModelName,
|
|
19
|
+
computedModelBy,
|
|
20
|
+
computedFields,
|
|
21
|
+
itemTitleField,
|
|
22
|
+
computedNoDataText,
|
|
23
|
+
computedSortBy,
|
|
24
|
+
formatItemTitle,
|
|
25
|
+
} = useLookupListMaster(props)
|
|
62
26
|
</script>
|
|
63
27
|
|
|
64
28
|
<template>
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
29
|
+
<model-combobox
|
|
30
|
+
:model-name="computedModelName"
|
|
31
|
+
:model-by="computedModelBy"
|
|
32
|
+
:fields="computedFields"
|
|
33
|
+
:sort-by="computedSortBy"
|
|
34
|
+
:item-title="itemTitleField"
|
|
35
|
+
item-value="itemCode"
|
|
36
|
+
:no-data-text="computedNoDataText"
|
|
37
|
+
:show-code="props.showCode"
|
|
38
|
+
:cache="props.cache"
|
|
70
39
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
>
|
|
76
|
-
<slot
|
|
77
|
-
:name="name"
|
|
78
|
-
v-bind="((slotData || {}) as object)"
|
|
79
|
-
:operation="operation"
|
|
80
|
-
/>
|
|
40
|
+
<!-- pass-through slots -->
|
|
41
|
+
<!-- @ts-ignore -->
|
|
42
|
+
<template v-for="(_, name, index) in ($slots as {})" :key="index" #[name]="slotData">
|
|
43
|
+
<slot :name="name" v-bind="((slotData || {}) as object)" :operation="computedModelName" />
|
|
81
44
|
</template>
|
|
82
|
-
<template
|
|
83
|
-
v-if="!$slots.item"
|
|
84
|
-
#item="{ props, item }"
|
|
85
|
-
>
|
|
86
|
-
<v-list-item
|
|
87
|
-
v-bind="props"
|
|
88
|
-
:title="item.raw.itemValue"
|
|
89
|
-
/>
|
|
90
|
-
</template>
|
|
91
|
-
</v-select>
|
|
92
|
-
</template>
|
|
93
45
|
|
|
94
|
-
<
|
|
46
|
+
<template v-if="!$slots.item" #item="{ props: itemProps, item }">
|
|
47
|
+
<v-list-item v-bind="itemProps" :title="formatItemTitle(item)" />
|
|
48
|
+
</template>
|
|
95
49
|
|
|
96
|
-
|
|
50
|
+
<template v-if="!$slots.selection && props.showCode" #selection="{ item }">
|
|
51
|
+
{{ formatItemTitle(item) }}
|
|
52
|
+
</template>
|
|
53
|
+
</model-combobox>
|
|
54
|
+
</template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { VCombobox } from 'vuetify/components/VCombobox'
|
|
3
|
+
import { defineModel, withDefaults } from 'vue'
|
|
4
|
+
import { useLookupList, type StaticLookupProps } from '../../composables/lookupList'
|
|
5
|
+
|
|
6
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VCombobox['$props']> {}
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(defineProps<Props & StaticLookupProps>(), {
|
|
9
|
+
fuzzy: false,
|
|
10
|
+
showCode: false,
|
|
11
|
+
cache: false,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const emit = defineEmits<{
|
|
15
|
+
(e: 'update:selectedObject', object: any | any[]): void
|
|
16
|
+
}>()
|
|
17
|
+
|
|
18
|
+
const selectedItems = defineModel<any>()
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
computedItems,
|
|
22
|
+
computedFilterKeys,
|
|
23
|
+
computedPlaceholder,
|
|
24
|
+
computedNoDataText,
|
|
25
|
+
isLoading,
|
|
26
|
+
isErrorLoading,
|
|
27
|
+
} = useLookupList(props, emit, selectedItems)
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<v-combobox
|
|
32
|
+
v-model="selectedItems"
|
|
33
|
+
v-bind="$attrs"
|
|
34
|
+
:items="computedItems"
|
|
35
|
+
:filter-keys="computedFilterKeys"
|
|
36
|
+
:item-title="props.itemTitle"
|
|
37
|
+
:item-value="props.itemValue"
|
|
38
|
+
:loading="isLoading"
|
|
39
|
+
:placeholder="computedPlaceholder"
|
|
40
|
+
:no-data-text="computedNoDataText"
|
|
41
|
+
:multiple="props.multiple"
|
|
42
|
+
:return-object="false"
|
|
43
|
+
>
|
|
44
|
+
<!-- passthrough slots -->
|
|
45
|
+
<!-- @ts-ignore -->
|
|
46
|
+
<template v-for="(_, name, index) in ($slots as {})" :key="index" #[name]="slotData">
|
|
47
|
+
<slot :name="name" v-bind="((slotData || {}) as object)" />
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<template v-if="!$slots.item" #item="{ props: itemProps, item }">
|
|
51
|
+
<v-list-item v-bind="itemProps" :title="(props.showCode ? item.value + '-' : '') + item.title" />
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<template v-if="!$slots.selection && props.showCode" #selection="{ item }">
|
|
55
|
+
{{ item.value + '-' + item.title }}
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<template v-if="isErrorLoading" #append>
|
|
59
|
+
<v-icon color="error">mdi mdi-alert</v-icon>
|
|
60
|
+
</template>
|
|
61
|
+
</v-combobox>
|
|
62
|
+
</template>
|
|
@@ -16,6 +16,10 @@ defineOptions({
|
|
|
16
16
|
interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
|
|
17
17
|
title: string
|
|
18
18
|
noDataText?: string
|
|
19
|
+
dialogWidth?: string | number
|
|
20
|
+
dialogMaxWidth?: string | number
|
|
21
|
+
dialogHeight?: string | number
|
|
22
|
+
dialogMaxHeight?: string | number
|
|
19
23
|
dialogFullscreen?: boolean
|
|
20
24
|
initialData?: Record<string, any>
|
|
21
25
|
toolbarColor?: string
|
|
@@ -302,6 +306,10 @@ defineExpose({ reload,operation })
|
|
|
302
306
|
@afterLeave="emit('close:dialog')"
|
|
303
307
|
:saveAndStay="saveAndStay"
|
|
304
308
|
:readonly="isDialogReadonly"
|
|
309
|
+
:width="dialogWidth"
|
|
310
|
+
:height="dialogHeight"
|
|
311
|
+
:max-width="dialogMaxWidth"
|
|
312
|
+
:max-height="dialogMaxHeight"
|
|
305
313
|
>
|
|
306
314
|
<template #default="slotData">
|
|
307
315
|
<slot
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Ref } from 'vue';
|
|
2
|
-
export interface
|
|
2
|
+
export interface StaticLookupProps {
|
|
3
3
|
fuzzy?: boolean;
|
|
4
4
|
sortBy?: string | string[];
|
|
5
5
|
showCode?: boolean;
|
|
@@ -9,16 +9,18 @@ export interface LookupProps {
|
|
|
9
9
|
itemValue: string;
|
|
10
10
|
fields?: Array<string | object>;
|
|
11
11
|
cache?: boolean | number;
|
|
12
|
+
filterKeys?: string | string[];
|
|
13
|
+
noDataText?: string;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
multiple?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface LookupProps extends StaticLookupProps {
|
|
12
18
|
serverSearch?: boolean;
|
|
13
19
|
serverSearchKey?: string;
|
|
14
20
|
searchSearchSort?: boolean;
|
|
15
21
|
serverSearchDebounce?: number;
|
|
16
22
|
serverSearchText?: string;
|
|
17
23
|
serverSearchLoadingText?: string;
|
|
18
|
-
filterKeys?: string | string[];
|
|
19
|
-
noDataText?: string;
|
|
20
|
-
placeholder?: string;
|
|
21
|
-
multiple?: boolean;
|
|
22
24
|
modelSelectedItem?: string;
|
|
23
25
|
modelSelectedItemBy?: Record<string, any>;
|
|
24
26
|
modelSelectedItemKey?: string;
|
|
@@ -128,7 +128,7 @@ export function useLookupList(props, emit, selectedItems) {
|
|
|
128
128
|
}
|
|
129
129
|
emit("update:selectedObject", props.multiple ? selectedItemsObject.value : selectedItemsObject.value[0]);
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
watchDebounced(
|
|
132
132
|
() => [
|
|
133
133
|
props.modelName,
|
|
134
134
|
props.serverSearch,
|
|
@@ -138,7 +138,7 @@ export function useLookupList(props, emit, selectedItems) {
|
|
|
138
138
|
queryFields.value
|
|
139
139
|
],
|
|
140
140
|
() => loadItems(),
|
|
141
|
-
{ immediate: true, deep: true }
|
|
141
|
+
{ immediate: true, deep: true, debounce: 30, maxWait: 200 }
|
|
142
142
|
);
|
|
143
143
|
watchDebounced(
|
|
144
144
|
searchData,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type Lang = 'TH' | 'EN';
|
|
2
2
|
export type MasterSortBy = 'itemCode' | 'itemValue';
|
|
3
|
-
export interface
|
|
3
|
+
export interface StaticMasterLikeProps {
|
|
4
4
|
sortBy?: MasterSortBy;
|
|
5
5
|
showCode?: boolean;
|
|
6
6
|
groupKey: string;
|
|
@@ -11,9 +11,11 @@ export interface MasterLikeProps {
|
|
|
11
11
|
filterText?: string;
|
|
12
12
|
waitForFilter?: boolean;
|
|
13
13
|
waitForFilterText?: string;
|
|
14
|
-
meilisearch?: boolean;
|
|
15
14
|
cache?: boolean | number;
|
|
16
15
|
}
|
|
16
|
+
export interface MasterLikeProps extends StaticMasterLikeProps {
|
|
17
|
+
meilisearch?: boolean;
|
|
18
|
+
}
|
|
17
19
|
export declare function useLookupListMaster(props: MasterLikeProps): {
|
|
18
20
|
computedModelName: import("vue").ComputedRef<string>;
|
|
19
21
|
computedModelBy: import("vue").ComputedRef<Record<string, any>>;
|