@ramathibodi/nuxt-commons 0.1.57 → 0.1.59
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/form/Birthdate.vue +35 -156
- package/dist/runtime/components/form/Date.vue +37 -10
- 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/form/TableData.vue +1 -1
- package/dist/runtime/components/label/DateCount.vue +40 -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/document/templateFormTable.js +1 -1
- 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
|
@@ -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>
|
|
@@ -17,6 +17,8 @@ interface Props extends /* @vue-ignore */ InstanceType<typeof VTextField['$props
|
|
|
17
17
|
pickerOnly?: boolean
|
|
18
18
|
flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[]
|
|
19
19
|
rules?: typeof VTextField['rules']
|
|
20
|
+
|
|
21
|
+
defaultDate?: boolean | string
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -24,6 +26,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
24
26
|
format: 'shortDate',
|
|
25
27
|
pickerOnly: false,
|
|
26
28
|
flow: () => [],
|
|
29
|
+
|
|
30
|
+
defaultDate: false,
|
|
27
31
|
})
|
|
28
32
|
|
|
29
33
|
const emit = defineEmits(['update:modelValue'])
|
|
@@ -155,8 +159,29 @@ watch(selectedDate, (newValue) => {
|
|
|
155
159
|
emit('update:modelValue', newValue)
|
|
156
160
|
})
|
|
157
161
|
|
|
158
|
-
watch(() => props.modelValue, () => {
|
|
159
|
-
|
|
162
|
+
watch(() => props.modelValue, (newValue,oldValue) => {
|
|
163
|
+
if (!oldValue && !newValue && props.defaultDate) {
|
|
164
|
+
if (!oldValue && !newValue && props.defaultDate) {
|
|
165
|
+
let defaultDate: string | null = null;
|
|
166
|
+
|
|
167
|
+
if (props.defaultDate === true) {
|
|
168
|
+
defaultDate = Datetime().now().toFormat('yyyy-MM-dd', 'EN');
|
|
169
|
+
} else if (typeof props.defaultDate === 'string' &&/^[+-]?\d+$/.test(props.defaultDate.trim())) {
|
|
170
|
+
defaultDate = Datetime().now().luxonDateTime
|
|
171
|
+
.plus({ days: Number(props.defaultDate) })
|
|
172
|
+
.toFormat('yyyy-MM-dd');
|
|
173
|
+
} else {
|
|
174
|
+
const dateTime = Datetime().fromString(props.defaultDate)
|
|
175
|
+
if (dateTime.luxonDateTime.isValid) defaultDate = dateTime.toFormat('yyyy-MM-dd')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
updateDate(defaultDate);
|
|
179
|
+
} else {
|
|
180
|
+
updateDate(newValue ?? null);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
updateDate(props.modelValue || null)
|
|
184
|
+
}
|
|
160
185
|
}, { immediate: true })
|
|
161
186
|
|
|
162
187
|
function toggleMenuOpen(trigger: string) {
|
|
@@ -196,14 +221,16 @@ defineExpose({
|
|
|
196
221
|
@click:clear="handleTextFieldClear"
|
|
197
222
|
@click="toggleMenuOpen('textField')"
|
|
198
223
|
>
|
|
199
|
-
<template #append="
|
|
200
|
-
<v-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
224
|
+
<template #append="appendProps">
|
|
225
|
+
<slot name="append" v-bind="appendProps" :toggleMenuOpen="toggleMenuOpen" :activatorProps="activatorProps">
|
|
226
|
+
<v-icon
|
|
227
|
+
:disabled="appendProps.isReadonly?.value || appendProps.isDisabled?.value"
|
|
228
|
+
v-bind="activatorProps"
|
|
229
|
+
@click="toggleMenuOpen('icon')"
|
|
230
|
+
>
|
|
231
|
+
fa:fa-regular fa-calendar
|
|
232
|
+
</v-icon>
|
|
233
|
+
</slot>
|
|
207
234
|
</template>
|
|
208
235
|
</v-text-field>
|
|
209
236
|
</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">
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { DateTime } from "luxon";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
|
|
5
|
+
type Locale = "th" | "en" | "en-US" | "th-TH";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
modelValue: DateTime;
|
|
9
|
+
endDate?: DateTime;
|
|
10
|
+
locale?: Locale;
|
|
11
|
+
zeroBase?: boolean; // true = start at 0, false = start at 1
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
+
locale: "th",
|
|
16
|
+
zeroBase: true,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const normalizeLocale = (locale: Locale): "en" | "th" =>
|
|
20
|
+
locale.startsWith("en") ? "en" : "th";
|
|
21
|
+
|
|
22
|
+
const countDate = computed(() => {
|
|
23
|
+
const base = props.endDate ?? DateTime.now();
|
|
24
|
+
const baseLocale = normalizeLocale(props.locale);
|
|
25
|
+
|
|
26
|
+
let days = Math.floor(base.diff(props.modelValue, "days").days ?? 0);
|
|
27
|
+
|
|
28
|
+
if (!props.zeroBase) days += 1;
|
|
29
|
+
|
|
30
|
+
if (baseLocale === "en") {
|
|
31
|
+
return days === 1 ? "1 day" : `${days} days`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return `${days} วัน`;
|
|
35
|
+
});
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<span>{{ countDate }}</span>
|
|
40
|
+
</template>
|
|
@@ -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
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
processDefaultTemplate,
|
|
4
4
|
useDocumentTemplate
|
|
5
5
|
} from "./template.js";
|
|
6
|
-
import { cloneDeep } from "lodash-es";
|
|
6
|
+
import { cloneDeep, isString, isArray } from "lodash-es";
|
|
7
7
|
export function autoActionHeader(tableHeader) {
|
|
8
8
|
let autoTableHeader = cloneDeep(tableHeader);
|
|
9
9
|
if (!autoTableHeader.some((h) => h.key === "action")) autoTableHeader.push({ title: "Action", key: "action", width: "100px" });
|
|
@@ -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>>;
|