Commit 5b4a41ced412fe3623618791ffa3123a3a2cfcdc
1 parent
e090689e
feat(api-select): add immediate option,close #430
Showing
2 changed files
with
151 additions
and
5 deletions
src/components/Form/src/components/ApiSelect copy.vue
0 → 100644
1 | +<template> | |
2 | + <Select v-bind="attrs" :options="getOptions" v-model:value="state" @focus="handleFetch"> | |
3 | + <template #[item]="data" v-for="item in Object.keys($slots)"> | |
4 | + <slot :name="item" v-bind="data"></slot> | |
5 | + </template> | |
6 | + <template #suffixIcon v-if="loading"> | |
7 | + <LoadingOutlined spin /> | |
8 | + </template> | |
9 | + <template #notFoundContent v-if="loading"> | |
10 | + <span> | |
11 | + <LoadingOutlined spin class="mr-1" /> | |
12 | + {{ t('component.form.apiSelectNotFound') }} | |
13 | + </span> | |
14 | + </template> | |
15 | + </Select> | |
16 | +</template> | |
17 | +<script lang="ts"> | |
18 | + import { defineComponent, PropType, ref, watchEffect, computed, unref } from 'vue'; | |
19 | + import { Select } from 'ant-design-vue'; | |
20 | + import { isFunction } from '/@/utils/is'; | |
21 | + import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | |
22 | + import { useAttrs } from '/@/hooks/core/useAttrs'; | |
23 | + import { get } from 'lodash-es'; | |
24 | + | |
25 | + import { LoadingOutlined } from '@ant-design/icons-vue'; | |
26 | + import { useI18n } from '/@/hooks/web/useI18n'; | |
27 | + import { propTypes } from '/@/utils/propTypes'; | |
28 | + | |
29 | + type OptionsItem = { label: string; value: string; disabled?: boolean }; | |
30 | + | |
31 | + export default defineComponent({ | |
32 | + name: 'ApiSelect', | |
33 | + components: { | |
34 | + Select, | |
35 | + LoadingOutlined, | |
36 | + }, | |
37 | + inheritAttrs: false, | |
38 | + props: { | |
39 | + value: propTypes.string, | |
40 | + numberToString: propTypes.bool, | |
41 | + api: { | |
42 | + type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>, | |
43 | + default: null, | |
44 | + }, | |
45 | + // api params | |
46 | + params: { | |
47 | + type: Object as PropType<Recordable>, | |
48 | + default: () => {}, | |
49 | + }, | |
50 | + // support xxx.xxx.xx | |
51 | + resultField: propTypes.string.def(''), | |
52 | + labelField: propTypes.string.def('label'), | |
53 | + valueField: propTypes.string.def('value'), | |
54 | + immediate: propTypes.bool.def(true), | |
55 | + }, | |
56 | + emits: ['options-change', 'change'], | |
57 | + setup(props, { emit }) { | |
58 | + const options = ref<OptionsItem[]>([]); | |
59 | + const loading = ref(false); | |
60 | + const isFirstLoad = ref(true); | |
61 | + const attrs = useAttrs(); | |
62 | + const { t } = useI18n(); | |
63 | + | |
64 | + // Embedded in the form, just use the hook binding to perform form verification | |
65 | + const [state] = useRuleFormItem(props); | |
66 | + | |
67 | + const getOptions = computed(() => { | |
68 | + const { labelField, valueField, numberToString } = props; | |
69 | + | |
70 | + return unref(options).reduce((prev, next: Recordable) => { | |
71 | + if (next) { | |
72 | + const value = next[valueField]; | |
73 | + prev.push({ | |
74 | + label: next[labelField], | |
75 | + value: numberToString ? `${value}` : value, | |
76 | + }); | |
77 | + } | |
78 | + return prev; | |
79 | + }, [] as OptionsItem[]); | |
80 | + }); | |
81 | + | |
82 | + watchEffect(() => { | |
83 | + if (isFirstLoad.value) { | |
84 | + props.immediate && fetch(); | |
85 | + } else { | |
86 | + fetch(); | |
87 | + } | |
88 | + }); | |
89 | + | |
90 | + async function fetch() { | |
91 | + const api = props.api; | |
92 | + if (!api || !isFunction(api)) return; | |
93 | + | |
94 | + try { | |
95 | + loading.value = true; | |
96 | + const res = await api(props.params); | |
97 | + if (Array.isArray(res)) { | |
98 | + options.value = res; | |
99 | + emitChange(); | |
100 | + return; | |
101 | + } | |
102 | + if (props.resultField) { | |
103 | + options.value = get(res, props.resultField) || []; | |
104 | + } | |
105 | + emitChange(); | |
106 | + } catch (error) { | |
107 | + console.warn(error); | |
108 | + } finally { | |
109 | + loading.value = false; | |
110 | + } | |
111 | + } | |
112 | + | |
113 | + async function handleFetch() { | |
114 | + if (!props.immediate) { | |
115 | + await fetch(); | |
116 | + } | |
117 | + isFirstLoad.value = false; | |
118 | + } | |
119 | + | |
120 | + function emitChange() { | |
121 | + emit('options-change', unref(options)); | |
122 | + } | |
123 | + | |
124 | + return { state, attrs, getOptions, loading, t, handleFetch }; | |
125 | + }, | |
126 | + }); | |
127 | +</script> | ... | ... |
src/components/Form/src/components/ApiSelect.vue
1 | 1 | <template> |
2 | - <Select v-bind="attrs" :options="getOptions" v-model:value="state"> | |
2 | + <Select | |
3 | + @dropdownVisibleChange="handleFetch" | |
4 | + v-bind="attrs" | |
5 | + :options="getOptions" | |
6 | + v-model:value="state" | |
7 | + > | |
3 | 8 | <template #[item]="data" v-for="item in Object.keys($slots)"> |
4 | 9 | <slot :name="item" v-bind="data"></slot> |
5 | 10 | </template> |
... | ... | @@ -51,11 +56,13 @@ |
51 | 56 | resultField: propTypes.string.def(''), |
52 | 57 | labelField: propTypes.string.def('label'), |
53 | 58 | valueField: propTypes.string.def('value'), |
59 | + immediate: propTypes.bool.def(true), | |
54 | 60 | }, |
55 | 61 | emits: ['options-change', 'change'], |
56 | 62 | setup(props, { emit }) { |
57 | 63 | const options = ref<OptionsItem[]>([]); |
58 | 64 | const loading = ref(false); |
65 | + const isFirstLoad = ref(true); | |
59 | 66 | const attrs = useAttrs(); |
60 | 67 | const { t } = useI18n(); |
61 | 68 | |
... | ... | @@ -78,7 +85,7 @@ |
78 | 85 | }); |
79 | 86 | |
80 | 87 | watchEffect(() => { |
81 | - fetch(); | |
88 | + props.immediate && fetch(); | |
82 | 89 | }); |
83 | 90 | |
84 | 91 | async function fetch() { |
... | ... | @@ -90,20 +97,32 @@ |
90 | 97 | const res = await api(props.params); |
91 | 98 | if (Array.isArray(res)) { |
92 | 99 | options.value = res; |
93 | - emit('options-change', unref(options)); | |
100 | + emitChange(); | |
94 | 101 | return; |
95 | 102 | } |
96 | 103 | if (props.resultField) { |
97 | 104 | options.value = get(res, props.resultField) || []; |
98 | 105 | } |
99 | - emit('options-change', unref(options)); | |
106 | + emitChange(); | |
100 | 107 | } catch (error) { |
101 | 108 | console.warn(error); |
102 | 109 | } finally { |
103 | 110 | loading.value = false; |
104 | 111 | } |
105 | 112 | } |
106 | - return { state, attrs, getOptions, loading, t }; | |
113 | + | |
114 | + async function handleFetch() { | |
115 | + if (!props.immediate && unref(isFirstLoad)) { | |
116 | + await fetch(); | |
117 | + isFirstLoad.value = false; | |
118 | + } | |
119 | + } | |
120 | + | |
121 | + function emitChange() { | |
122 | + emit('options-change', unref(options)); | |
123 | + } | |
124 | + | |
125 | + return { state, attrs, getOptions, loading, t, handleFetch }; | |
107 | 126 | }, |
108 | 127 | }); |
109 | 128 | </script> | ... | ... |