Skip to content

Commit 5b4a41c

Browse files
committed
feat(api-select): add immediate option,close #430
1 parent e090689 commit 5b4a41c

File tree

2 files changed

+151
-5
lines changed

2 files changed

+151
-5
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<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+
>
38
<template #[item]="data" v-for="item in Object.keys($slots)">
49
<slot :name="item" v-bind="data"></slot>
510
</template>
@@ -51,11 +56,13 @@
5156
resultField: propTypes.string.def(''),
5257
labelField: propTypes.string.def('label'),
5358
valueField: propTypes.string.def('value'),
59+
immediate: propTypes.bool.def(true),
5460
},
5561
emits: ['options-change', 'change'],
5662
setup(props, { emit }) {
5763
const options = ref<OptionsItem[]>([]);
5864
const loading = ref(false);
65+
const isFirstLoad = ref(true);
5966
const attrs = useAttrs();
6067
const { t } = useI18n();
6168
@@ -78,7 +85,7 @@
7885
});
7986
8087
watchEffect(() => {
81-
fetch();
88+
props.immediate && fetch();
8289
});
8390
8491
async function fetch() {
@@ -90,20 +97,32 @@
9097
const res = await api(props.params);
9198
if (Array.isArray(res)) {
9299
options.value = res;
93-
emit('options-change', unref(options));
100+
emitChange();
94101
return;
95102
}
96103
if (props.resultField) {
97104
options.value = get(res, props.resultField) || [];
98105
}
99-
emit('options-change', unref(options));
106+
emitChange();
100107
} catch (error) {
101108
console.warn(error);
102109
} finally {
103110
loading.value = false;
104111
}
105112
}
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 };
107126
},
108127
});
109128
</script>

0 commit comments

Comments
 (0)