@ramathibodi/nuxt-commons 0.1.56 → 0.1.58
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/document/TemplateBuilder.vue +133 -122
- package/dist/runtime/components/form/Pad.vue +39 -0
- package/dist/runtime/components/form/TableData.vue +180 -41
- package/dist/runtime/components/label/DateCount.vue +40 -0
- package/dist/runtime/components/label/Object.vue +14 -0
- package/dist/runtime/components/master/Autocomplete.vue +25 -108
- package/dist/runtime/components/master/Combobox.vue +59 -83
- package/dist/runtime/components/model/Autocomplete.vue +23 -234
- package/dist/runtime/components/model/Combobox.vue +70 -0
- package/dist/runtime/composables/document/template.d.ts +4 -1
- package/dist/runtime/composables/document/template.js +22 -15
- package/dist/runtime/composables/document/templateFormHidden.d.ts +1 -1
- package/dist/runtime/composables/document/templateFormHidden.js +5 -2
- package/dist/runtime/composables/document/templateFormTable.d.ts +3 -2
- package/dist/runtime/composables/document/templateFormTable.js +36 -40
- package/dist/runtime/composables/document/templateFormTableData.d.ts +2 -0
- package/dist/runtime/composables/document/templateFormTableData.js +21 -0
- package/dist/runtime/composables/graphqlModel.d.ts +6 -6
- package/dist/runtime/composables/graphqlModelItem.d.ts +4 -4
- package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
- package/dist/runtime/composables/lookupList.d.ts +44 -0
- package/dist/runtime/composables/lookupList.js +194 -0
- package/dist/runtime/composables/lookupListMaster.d.ts +28 -0
- package/dist/runtime/composables/lookupListMaster.js +61 -0
- package/dist/runtime/types/menu.d.ts +7 -4
- package/dist/runtime/utils/formatter.d.ts +1 -0
- package/dist/runtime/utils/formatter.js +19 -0
- package/dist/runtime/utils/object.d.ts +1 -0
- package/dist/runtime/utils/object.js +46 -1
- package/package.json +2 -1
|
@@ -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>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import {joinObject} from "../../utils/formatter";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
object: any,
|
|
6
|
+
attribute?: string,
|
|
7
|
+
separator?: string,
|
|
8
|
+
}
|
|
9
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
10
|
+
})
|
|
11
|
+
</script>
|
|
12
|
+
<template>
|
|
13
|
+
<span>{{joinObject(object,attribute,separator)}}</span>
|
|
14
|
+
</template>
|
|
@@ -1,95 +1,30 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import {VAutocomplete} from 'vuetify/components/VAutocomplete'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { VAutocomplete } from 'vuetify/components/VAutocomplete'
|
|
3
|
+
import { withDefaults } from 'vue'
|
|
4
|
+
import { useLookupListMaster, type MasterLikeProps } from '../../composables/lookupListMaster'
|
|
5
5
|
|
|
6
|
-
interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
|
|
7
|
-
sortBy?: 'itemCode' | 'itemValue'
|
|
8
|
-
showCode?: boolean
|
|
6
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {}
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
itemCodes?: string[]
|
|
12
|
-
lang?: 'TH' | 'EN'
|
|
13
|
-
fields?: Array<string | object>
|
|
14
|
-
noDataText?: string
|
|
15
|
-
filterText?: string
|
|
16
|
-
waitForFilter?: boolean
|
|
17
|
-
waitForFilterText?: string
|
|
18
|
-
|
|
19
|
-
meilisearch?: boolean
|
|
20
|
-
|
|
21
|
-
cache?: boolean | number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
8
|
+
const props = withDefaults(defineProps<Props & MasterLikeProps>(), {
|
|
25
9
|
sortBy: 'itemValue',
|
|
26
10
|
showCode: false,
|
|
27
|
-
|
|
28
11
|
lang: 'TH',
|
|
29
12
|
noDataText: 'ไม่พบข้อมูล',
|
|
30
13
|
waitForFilter: false,
|
|
31
|
-
|
|
32
14
|
meilisearch: false,
|
|
33
|
-
|
|
34
15
|
cache: false,
|
|
35
16
|
})
|
|
36
17
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let modelBy : Record<string,any> = {groupKey: props.groupKey}
|
|
48
|
-
if (props.filterText) {
|
|
49
|
-
modelBy["filterText"] = props.filterText
|
|
50
|
-
}
|
|
51
|
-
if (props.itemCodes && props.itemCodes.length>0) {
|
|
52
|
-
modelBy["itemCodes"] = props.itemCodes
|
|
53
|
-
}
|
|
54
|
-
return modelBy
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const computedModelSelectedItemBy = computed(()=>{
|
|
58
|
-
return {groupKey: props.groupKey}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
const computedFields = computed(()=>{
|
|
62
|
-
let fields : Array<string | object> = []
|
|
63
|
-
if (props.filterText) fields.push('filterText')
|
|
64
|
-
if (props.meilisearch) fields.push('_formatted')
|
|
65
|
-
return union(['itemCode', 'itemValue', 'itemValueAlternative'],fields,props.fields || [])
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const itemTitleField = computed(() => {
|
|
69
|
-
return (props.lang == 'TH') ? 'itemValue' : 'itemValueAlternative'
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const formatItemTitle = (item: any)=>{
|
|
73
|
-
if (props.meilisearch) {
|
|
74
|
-
return (props.showCode ? item.raw?._formatted?.itemCode+'-' : '')+(item.raw?._formatted?.[itemTitleField.value] || item.raw?._formatted?.itemValue || item.raw?._formatted?.itemCode)
|
|
75
|
-
} else {
|
|
76
|
-
return (props.showCode ? item.raw.itemCode+'-' : '')+(item.title || item.raw.itemValue || item.raw.itemCode)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const computedNoDataText = computed(() => {
|
|
81
|
-
if (props.waitForFilter && !props.filterText) return props.waitForFilterText
|
|
82
|
-
return props.noDataText
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
const computedSortBy = computed(()=>{
|
|
86
|
-
if (props.sortBy == 'itemValue') {
|
|
87
|
-
if (props.showCode) return ['itemCode']
|
|
88
|
-
else return [itemTitleField.value,'itemValue','itemCode']
|
|
89
|
-
} else {
|
|
90
|
-
return [props.sortBy]
|
|
91
|
-
}
|
|
92
|
-
})
|
|
18
|
+
const {
|
|
19
|
+
computedModelName,
|
|
20
|
+
computedModelBy,
|
|
21
|
+
computedModelSelectedItemBy,
|
|
22
|
+
computedFields,
|
|
23
|
+
itemTitleField,
|
|
24
|
+
computedNoDataText,
|
|
25
|
+
computedSortBy,
|
|
26
|
+
formatItemTitle,
|
|
27
|
+
} = useLookupListMaster(props)
|
|
93
28
|
</script>
|
|
94
29
|
|
|
95
30
|
<template>
|
|
@@ -109,43 +44,25 @@ const computedSortBy = computed(()=>{
|
|
|
109
44
|
:model-selected-item-by="computedModelSelectedItemBy"
|
|
110
45
|
model-selected-item-key="itemCodes"
|
|
111
46
|
>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
>
|
|
117
|
-
<slot
|
|
118
|
-
:name="name"
|
|
119
|
-
v-bind="((slotData || {}) as object)"
|
|
120
|
-
:operation="operation"
|
|
121
|
-
/>
|
|
47
|
+
<!-- pass-through slots -->
|
|
48
|
+
<!-- @ts-ignore -->
|
|
49
|
+
<template v-for="(_, name, index) in ($slots as {})" :key="index" #[name]="slotData">
|
|
50
|
+
<slot :name="name" v-bind="((slotData || {}) as object)" :operation="computedModelName" />
|
|
122
51
|
</template>
|
|
123
52
|
|
|
124
|
-
<template
|
|
125
|
-
|
|
126
|
-
#item="{ props, item }"
|
|
127
|
-
>
|
|
128
|
-
<v-list-item
|
|
129
|
-
v-bind="props"
|
|
130
|
-
:title="formatItemTitle(item)"
|
|
131
|
-
/>
|
|
53
|
+
<template v-if="!$slots.item && !props.meilisearch" #item="{ props: itemProps, item }">
|
|
54
|
+
<v-list-item v-bind="itemProps" :title="formatItemTitle(item)" />
|
|
132
55
|
</template>
|
|
133
56
|
|
|
134
|
-
<template
|
|
135
|
-
|
|
136
|
-
#item="{ props, item }"
|
|
137
|
-
>
|
|
138
|
-
<v-list-item v-bind="props">
|
|
57
|
+
<template v-if="!$slots.item && props.meilisearch" #item="{ props: itemProps, item }">
|
|
58
|
+
<v-list-item v-bind="itemProps">
|
|
139
59
|
<template #title v-if="item.raw?._formatted?.itemValue">
|
|
140
60
|
<span v-html="formatItemTitle(item)" class="meilisearch-item"></span>
|
|
141
61
|
</template>
|
|
142
62
|
</v-list-item>
|
|
143
63
|
</template>
|
|
144
64
|
|
|
145
|
-
<template
|
|
146
|
-
v-if="!$slots.selection && showCode"
|
|
147
|
-
#selection="{ item }"
|
|
148
|
-
>
|
|
65
|
+
<template v-if="!$slots.selection && props.showCode" #selection="{ item }">
|
|
149
66
|
{{ formatItemTitle(item) }}
|
|
150
67
|
</template>
|
|
151
68
|
</model-autocomplete>
|
|
@@ -153,4 +70,4 @@ const computedSortBy = computed(()=>{
|
|
|
153
70
|
|
|
154
71
|
<style>
|
|
155
72
|
.meilisearch-item em{font-weight:700}
|
|
156
|
-
</style>
|
|
73
|
+
</style>
|
|
@@ -1,97 +1,73 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import {VCombobox} from 'vuetify/components/VCombobox'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {useAlert} from '../../composables/alert'
|
|
6
|
-
import {useGraphQl} from '../../composables/graphql'
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { VCombobox } from 'vuetify/components/VCombobox'
|
|
3
|
+
import { withDefaults } from 'vue'
|
|
4
|
+
import { useLookupListMaster, type MasterLikeProps } from '../../composables/lookupListMaster'
|
|
7
5
|
|
|
8
|
-
interface Props extends /* @vue-ignore */ InstanceType<typeof VCombobox['$props']> {
|
|
9
|
-
modelValue?: string
|
|
10
|
-
groupKey: string
|
|
11
|
-
fields?: string[]
|
|
12
|
-
lang?: 'TH' | 'EN'
|
|
13
|
-
}
|
|
6
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VCombobox['$props']> {}
|
|
14
7
|
|
|
15
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
8
|
+
const props = withDefaults(defineProps<Props & MasterLikeProps>(), {
|
|
9
|
+
sortBy: 'itemValue',
|
|
10
|
+
showCode: false,
|
|
16
11
|
lang: 'TH',
|
|
12
|
+
noDataText: 'ไม่พบข้อมูล',
|
|
13
|
+
waitForFilter: false,
|
|
14
|
+
meilisearch: false,
|
|
15
|
+
cache: false,
|
|
17
16
|
})
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
items.value = result
|
|
30
|
-
}).catch((error) => {
|
|
31
|
-
alert?.addAlert({
|
|
32
|
-
message: `${error.message}`,
|
|
33
|
-
alertType: 'error',
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
watch(() => props.groupKey, () => {
|
|
39
|
-
query()
|
|
40
|
-
}, { immediate: true })
|
|
41
|
-
|
|
42
|
-
watch(selectedItem, (newValue) => {
|
|
43
|
-
emit('update:modelValue', newValue.itemCode)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
watch(
|
|
47
|
-
() => props.modelValue,
|
|
48
|
-
(newVal) => {
|
|
49
|
-
if (newVal && items.value.length > 0) {
|
|
50
|
-
const found = items.value.find(item => item.itemCode === newVal)
|
|
51
|
-
if (found) selectedItem.value = found
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
{ immediate: true }
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
const itemTitleField = computed(() => {
|
|
58
|
-
if (props.lang == 'TH') return 'itemValue'
|
|
59
|
-
else return 'itemValueAlternative'
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
query()
|
|
18
|
+
const {
|
|
19
|
+
computedModelName,
|
|
20
|
+
computedModelBy,
|
|
21
|
+
computedModelSelectedItemBy,
|
|
22
|
+
computedFields,
|
|
23
|
+
itemTitleField,
|
|
24
|
+
computedNoDataText,
|
|
25
|
+
computedSortBy,
|
|
26
|
+
formatItemTitle,
|
|
27
|
+
} = useLookupListMaster(props)
|
|
63
28
|
</script>
|
|
64
29
|
|
|
65
30
|
<template>
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
31
|
+
<model-combobox
|
|
32
|
+
:model-name="computedModelName"
|
|
33
|
+
:model-by="computedModelBy"
|
|
34
|
+
:fields="computedFields"
|
|
35
|
+
:sort-by="computedSortBy"
|
|
36
|
+
:item-title="itemTitleField"
|
|
37
|
+
item-value="itemCode"
|
|
38
|
+
:no-data-text="computedNoDataText"
|
|
39
|
+
:show-code="props.showCode"
|
|
40
|
+
:cache="props.cache && !props.meilisearch"
|
|
41
|
+
:server-search="props.meilisearch && !!computedModelName"
|
|
42
|
+
server-search-key="keyword"
|
|
43
|
+
model-selected-item="masterItemByGroupKeyAndItemCodeIn"
|
|
44
|
+
:model-selected-item-by="computedModelSelectedItemBy"
|
|
45
|
+
model-selected-item-key="itemCodes"
|
|
71
46
|
>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
>
|
|
77
|
-
<slot
|
|
78
|
-
:name="name"
|
|
79
|
-
v-bind="((slotData || {}) as object)"
|
|
80
|
-
:operation="operation"
|
|
81
|
-
/>
|
|
47
|
+
<!-- pass-through slots -->
|
|
48
|
+
<!-- @ts-ignore -->
|
|
49
|
+
<template v-for="(_, name, index) in ($slots as {})" :key="index" #[name]="slotData">
|
|
50
|
+
<slot :name="name" v-bind="((slotData || {}) as object)" :operation="computedModelName" />
|
|
82
51
|
</template>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
>
|
|
87
|
-
<v-list-item
|
|
88
|
-
v-bind="props"
|
|
89
|
-
:title="item.raw.itemValue"
|
|
90
|
-
/>
|
|
52
|
+
|
|
53
|
+
<template v-if="!$slots.item && !props.meilisearch" #item="{ props: itemProps, item }">
|
|
54
|
+
<v-list-item v-bind="itemProps" :title="formatItemTitle(item)" />
|
|
91
55
|
</template>
|
|
92
|
-
</v-combobox>
|
|
93
|
-
</template>
|
|
94
56
|
|
|
95
|
-
<
|
|
57
|
+
<template v-if="!$slots.item && props.meilisearch" #item="{ props: itemProps, item }">
|
|
58
|
+
<v-list-item v-bind="itemProps">
|
|
59
|
+
<template #title v-if="item.raw?._formatted?.itemValue">
|
|
60
|
+
<span v-html="formatItemTitle(item)" class="meilisearch-item"></span>
|
|
61
|
+
</template>
|
|
62
|
+
</v-list-item>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<template v-if="!$slots.selection && props.showCode" #selection="{ item }">
|
|
66
|
+
{{ formatItemTitle(item) }}
|
|
67
|
+
</template>
|
|
68
|
+
</model-combobox>
|
|
69
|
+
</template>
|
|
96
70
|
|
|
71
|
+
<style>
|
|
72
|
+
.meilisearch-item em{font-weight:700}
|
|
97
73
|
</style>
|
|
@@ -1,235 +1,36 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { VAutocomplete } from 'vuetify/components/VAutocomplete'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { watchDebounced } from '@vueuse/core'
|
|
6
|
-
import { useFuzzy } from '../../composables/utils/fuzzy'
|
|
7
|
-
import { useGraphQlOperation } from '../../composables/graphqlOperation'
|
|
3
|
+
import { defineModel, withDefaults } from 'vue'
|
|
4
|
+
import { useLookupList, type LookupProps } from '../../composables/lookupList'
|
|
8
5
|
|
|
9
|
-
interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
|
|
10
|
-
fuzzy?: boolean
|
|
11
|
-
sortBy?: string | string[]
|
|
12
|
-
showCode?: boolean
|
|
6
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {}
|
|
13
7
|
|
|
14
|
-
|
|
15
|
-
modelBy?: Record<string, any>
|
|
16
|
-
itemTitle: string
|
|
17
|
-
itemValue: string
|
|
18
|
-
fields?: Array<string | object>
|
|
19
|
-
cache?: boolean | number
|
|
20
|
-
|
|
21
|
-
serverSearch?: boolean
|
|
22
|
-
serverSearchKey?: string
|
|
23
|
-
searchSearchSort?: boolean
|
|
24
|
-
serverSearchDebounce?: number
|
|
25
|
-
|
|
26
|
-
serverSearchText?: string
|
|
27
|
-
serverSearchLoadingText?: string
|
|
28
|
-
|
|
29
|
-
modelSelectedItem?: string
|
|
30
|
-
modelSelectedItemBy?: Record<string, any>
|
|
31
|
-
modelSelectedItemKey?: string
|
|
32
|
-
|
|
33
|
-
filterKeys?: string|string[]
|
|
34
|
-
noDataText?: string
|
|
35
|
-
placeholder?: string
|
|
36
|
-
multiple?: boolean
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
8
|
+
const props = withDefaults(defineProps<Props & LookupProps>(), {
|
|
40
9
|
fuzzy: false,
|
|
41
10
|
showCode: false,
|
|
42
11
|
cache: false,
|
|
43
12
|
serverSearch: false,
|
|
44
13
|
searchSearchSort: false,
|
|
45
|
-
serverSearchText:
|
|
46
|
-
serverSearchLoadingText:
|
|
14
|
+
serverSearchText: 'Type to search…',
|
|
15
|
+
serverSearchLoadingText: 'Searching…',
|
|
47
16
|
serverSearchDebounce: 500,
|
|
48
17
|
})
|
|
49
18
|
|
|
50
19
|
const emit = defineEmits<{
|
|
51
|
-
(e: 'update:selectedObject', object: any|any[]): void
|
|
20
|
+
(e: 'update:selectedObject', object: any | any[]): void
|
|
52
21
|
}>()
|
|
53
22
|
|
|
54
|
-
const modelItems = ref<Array<any>>([])
|
|
55
|
-
const items = ref<Array<any>>([])
|
|
56
|
-
const searchData = ref<string>('')
|
|
57
|
-
|
|
58
|
-
const isLoading = ref(false)
|
|
59
|
-
const isErrorLoading = ref(false)
|
|
60
|
-
|
|
61
|
-
const queryFields = computed(() => {
|
|
62
|
-
let fieldsArray: any[] = [props.itemTitle, props.itemValue]
|
|
63
|
-
if (props.fields) fieldsArray = union(fieldsArray, props.fields)
|
|
64
|
-
return fieldsArray
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
const computedFilterKeys = computed(() => {
|
|
68
|
-
if (props.filterKeys) return props.filterKeys
|
|
69
|
-
return ['title','raw.'+props.itemValue]
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const computedPlaceholder = computed(() => {
|
|
73
|
-
if (!props.serverSearch) return props.placeholder
|
|
74
|
-
return isLoading.value ? props.serverSearchLoadingText : props.serverSearchText
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const computedNoDataText = computed(() => {
|
|
78
|
-
if (!props.serverSearch) return props.noDataText
|
|
79
|
-
if (isLoading.value) return props.serverSearchLoadingText
|
|
80
|
-
if (!searchData.value) return props.serverSearchText
|
|
81
|
-
return props.noDataText
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
let requestId = 0
|
|
85
|
-
|
|
86
|
-
async function loadItems() {
|
|
87
|
-
if (!props.modelName) {
|
|
88
|
-
modelItems.value = []
|
|
89
|
-
items.value = []
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const id = ++requestId
|
|
94
|
-
isLoading.value = true
|
|
95
|
-
|
|
96
|
-
const variables: Record<string, any> = { ...(props.modelBy || {}) }
|
|
97
|
-
if (props.serverSearch && props.serverSearchKey) {
|
|
98
|
-
variables[props.serverSearchKey] = searchData.value
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const result: any = await useGraphQlOperation('Query', props.modelName, queryFields.value, variables, props.cache)
|
|
103
|
-
|
|
104
|
-
if (id !== requestId) return
|
|
105
|
-
|
|
106
|
-
if (isArray(result)) {
|
|
107
|
-
modelItems.value = result
|
|
108
|
-
items.value = result
|
|
109
|
-
isErrorLoading.value = false
|
|
110
|
-
} else {
|
|
111
|
-
isErrorLoading.value = true
|
|
112
|
-
modelItems.value = []
|
|
113
|
-
items.value = []
|
|
114
|
-
}
|
|
115
|
-
} catch (_error) {
|
|
116
|
-
if (id !== requestId) return
|
|
117
|
-
isErrorLoading.value = true
|
|
118
|
-
modelItems.value = []
|
|
119
|
-
items.value = []
|
|
120
|
-
} finally {
|
|
121
|
-
if (id === requestId) isLoading.value = false
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function applyFuzzy() {
|
|
126
|
-
if (!props.fuzzy) return
|
|
127
|
-
|
|
128
|
-
if (isEmpty(searchData.value)) {
|
|
129
|
-
items.value = modelItems.value
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const results: any = useFuzzy(searchData.value, modelItems.value, queryFields.value)
|
|
134
|
-
const output: any[] = []
|
|
135
|
-
if (results.value?.length) {
|
|
136
|
-
for (let index = 0; index < results.value.length; index++) {
|
|
137
|
-
const result = results.value[index]
|
|
138
|
-
if (result?.item) output.push(result.item)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
items.value = output
|
|
142
|
-
}
|
|
143
|
-
|
|
144
23
|
const selectedItems = defineModel<any>()
|
|
145
|
-
const selectedItemsObject = ref<any[]>([])
|
|
146
|
-
async function syncSelectedFromModelValue() {
|
|
147
|
-
const modelValueData: any = selectedItems.value
|
|
148
|
-
const values = castArray(modelValueData).filter(value => value !== undefined && value !== null && value !== '')
|
|
149
|
-
|
|
150
|
-
if (!values.length) {
|
|
151
|
-
emit("update:selectedObject",(props.multiple) ? [] : undefined)
|
|
152
|
-
selectedItemsObject.value = []
|
|
153
|
-
return
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
let alreadyInObject : any[] = selectedItemsObject.value?.filter(item => values.includes(item?.[props.itemValue])) || []
|
|
157
|
-
|
|
158
|
-
const haveSet = new Set<any>([...alreadyInObject.map(item => item?.[props.itemValue])])
|
|
159
|
-
const missingValues: any[] = values.filter(value => !haveSet.has(value))
|
|
160
|
-
|
|
161
|
-
const stillMissing: any[] = []
|
|
162
|
-
for (const value of missingValues) {
|
|
163
|
-
const localHit = modelItems.value.find(item => item?.[props.itemValue] === value)
|
|
164
|
-
if (localHit) {
|
|
165
|
-
alreadyInObject.push(localHit)
|
|
166
|
-
haveSet.add(value)
|
|
167
|
-
} else {
|
|
168
|
-
stillMissing.push(value)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (stillMissing.length && props.modelSelectedItem) {
|
|
173
|
-
try {
|
|
174
|
-
const key = props.modelSelectedItemKey || 'id'
|
|
175
|
-
const variables: Record<string, any> = { ...(props.modelSelectedItemBy || {}) }
|
|
176
|
-
variables[key] = values
|
|
177
|
-
|
|
178
|
-
const result: any = await useGraphQlOperation('Query', props.modelSelectedItem, queryFields.value,variables,props.cache)
|
|
179
|
-
selectedItemsObject.value = castArray(result)
|
|
180
|
-
} catch (_error) {
|
|
181
|
-
void _error
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
selectedItemsObject.value = alreadyInObject
|
|
185
|
-
}
|
|
186
24
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
props.modelBy,
|
|
197
|
-
queryFields.value,
|
|
198
|
-
],
|
|
199
|
-
() => loadItems(),
|
|
200
|
-
{ immediate: true, deep: true },
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
watchDebounced(searchData,()=>{
|
|
204
|
-
if (props.serverSearch) loadItems()
|
|
205
|
-
else applyFuzzy()
|
|
206
|
-
},{ debounce: props.serverSearch ? props.serverSearchDebounce : 300, maxWait: 1500 })
|
|
207
|
-
|
|
208
|
-
watch(()=>modelItems.value, () => {
|
|
209
|
-
if (!props.serverSearch) applyFuzzy()
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
watch(()=>props.modelValue,()=>{
|
|
213
|
-
syncSelectedFromModelValue()
|
|
214
|
-
},{ immediate: true, deep: true })
|
|
215
|
-
|
|
216
|
-
const computedItems = computed(() => {
|
|
217
|
-
const sortByField = !props.sortBy || typeof props.sortBy === 'string'
|
|
218
|
-
? [props.sortBy || (props.showCode ? props.itemValue : props.itemTitle)]
|
|
219
|
-
: props.sortBy
|
|
220
|
-
|
|
221
|
-
const baseItems = (props.fuzzy || (props.serverSearch && !props.searchSearchSort)) && !isEmpty(searchData.value)
|
|
222
|
-
? items.value
|
|
223
|
-
: sortBy(items.value, sortByField)
|
|
224
|
-
|
|
225
|
-
for (const selectedItem of selectedItemsObject.value || []) {
|
|
226
|
-
if (!baseItems.find(existingItem => existingItem[props.itemValue] === selectedItem[props.itemValue])) {
|
|
227
|
-
baseItems.push(selectedItem)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return baseItems
|
|
232
|
-
})
|
|
25
|
+
const {
|
|
26
|
+
searchData,
|
|
27
|
+
computedItems,
|
|
28
|
+
computedFilterKeys,
|
|
29
|
+
computedPlaceholder,
|
|
30
|
+
computedNoDataText,
|
|
31
|
+
isLoading,
|
|
32
|
+
isErrorLoading,
|
|
33
|
+
} = useLookupList(props, emit, selectedItems)
|
|
233
34
|
</script>
|
|
234
35
|
|
|
235
36
|
<template>
|
|
@@ -247,34 +48,22 @@ const computedItems = computed(() => {
|
|
|
247
48
|
:no-data-text="computedNoDataText"
|
|
248
49
|
:multiple="props.multiple"
|
|
249
50
|
>
|
|
51
|
+
<!-- passthrough slots -->
|
|
250
52
|
<!-- @ts-ignore -->
|
|
251
|
-
<template
|
|
252
|
-
v-for="(_, name, index) in ($slots as {})"
|
|
253
|
-
:key="index"
|
|
254
|
-
#[name]="slotData"
|
|
255
|
-
>
|
|
53
|
+
<template v-for="(_, name, index) in ($slots as {})" :key="index" #[name]="slotData">
|
|
256
54
|
<slot :name="name" v-bind="((slotData || {}) as object)" />
|
|
257
55
|
</template>
|
|
258
56
|
|
|
259
|
-
<template
|
|
260
|
-
|
|
261
|
-
#item="{ props, item }"
|
|
262
|
-
>
|
|
263
|
-
<v-list-item
|
|
264
|
-
v-bind="props"
|
|
265
|
-
:title="(showCode ? item.value+'-' : '')+item.title"
|
|
266
|
-
/>
|
|
57
|
+
<template v-if="!$slots.item" #item="{ props: itemProps, item }">
|
|
58
|
+
<v-list-item v-bind="itemProps" :title="(props.showCode ? item.value + '-' : '') + item.title" />
|
|
267
59
|
</template>
|
|
268
60
|
|
|
269
|
-
<template
|
|
270
|
-
|
|
271
|
-
#selection="{ item }"
|
|
272
|
-
>
|
|
273
|
-
{{item.value+'-'+item.title}}
|
|
61
|
+
<template v-if="!$slots.selection && props.showCode" #selection="{ item }">
|
|
62
|
+
{{ item.value + '-' + item.title }}
|
|
274
63
|
</template>
|
|
275
64
|
|
|
276
65
|
<template v-if="isErrorLoading" #append>
|
|
277
66
|
<v-icon color="error">mdi mdi-alert</v-icon>
|
|
278
67
|
</template>
|
|
279
68
|
</v-autocomplete>
|
|
280
|
-
</template>
|
|
69
|
+
</template>
|