vben
2021-08-24 56a966cfbf8db5b29a42185f0f25a0e800c30dbb
提交 | 用户 | age
ac1a36 1 <template>
5b4a41 2   <Select
V 3     @dropdownVisibleChange="handleFetch"
4     v-bind="attrs"
fa64fc 5     @change="handleChange"
5b4a41 6     :options="getOptions"
V 7     v-model:value="state"
8   >
ac1a36 9     <template #[item]="data" v-for="item in Object.keys($slots)">
b1f317 10       <slot :name="item" v-bind="data || {}"></slot>
ac1a36 11     </template>
V 12     <template #suffixIcon v-if="loading">
13       <LoadingOutlined spin />
14     </template>
15     <template #notFoundContent v-if="loading">
16       <span>
17         <LoadingOutlined spin class="mr-1" />
18         {{ t('component.form.apiSelectNotFound') }}
19       </span>
20     </template>
21   </Select>
22 </template>
23 <script lang="ts">
50207a 24   import { defineComponent, PropType, ref, watchEffect, computed, unref, watch } from 'vue';
ac1a36 25   import { Select } from 'ant-design-vue';
V 26   import { isFunction } from '/@/utils/is';
27   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
28   import { useAttrs } from '/@/hooks/core/useAttrs';
c5f257 29   import { get, omit } from 'lodash-es';
ac1a36 30   import { LoadingOutlined } from '@ant-design/icons-vue';
V 31   import { useI18n } from '/@/hooks/web/useI18n';
116a1f 32   import { propTypes } from '/@/utils/propTypes';
ac1a36 33
V 34   type OptionsItem = { label: string; value: string; disabled?: boolean };
35
36   export default defineComponent({
116a1f 37     name: 'ApiSelect',
ac1a36 38     components: {
V 39       Select,
40       LoadingOutlined,
41     },
b67cf2 42     inheritAttrs: false,
ac1a36 43     props: {
37c574 44       value: propTypes.oneOfType([
V 45         propTypes.object,
46         propTypes.number,
47         propTypes.string,
48         propTypes.array,
49       ]),
5d51d4 50       numberToString: propTypes.bool,
ac1a36 51       api: {
a305e5 52         type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
ac1a36 53         default: null,
V 54       },
116a1f 55       // api params
ac1a36 56       params: {
V 57         type: Object as PropType<Recordable>,
8b2e0f 58         default: () => ({}),
ac1a36 59       },
116a1f 60       // support xxx.xxx.xx
V 61       resultField: propTypes.string.def(''),
62       labelField: propTypes.string.def('label'),
63       valueField: propTypes.string.def('value'),
5b4a41 64       immediate: propTypes.bool.def(true),
ac1a36 65     },
9c2f3f 66     emits: ['options-change', 'change'],
V 67     setup(props, { emit }) {
ac1a36 68       const options = ref<OptionsItem[]>([]);
V 69       const loading = ref(false);
5b4a41 70       const isFirstLoad = ref(true);
fa64fc 71       const emitData = ref<any[]>([]);
ac1a36 72       const attrs = useAttrs();
V 73       const { t } = useI18n();
74
75       // Embedded in the form, just use the hook binding to perform form verification
fa64fc 76       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
116a1f 77
V 78       const getOptions = computed(() => {
5d51d4 79         const { labelField, valueField, numberToString } = props;
116a1f 80
V 81         return unref(options).reduce((prev, next: Recordable) => {
82           if (next) {
5d51d4 83             const value = next[valueField];
116a1f 84             prev.push({
V 85               label: next[labelField],
5d51d4 86               value: numberToString ? `${value}` : value,
c5f257 87               ...omit(next, [labelField, valueField]),
116a1f 88             });
V 89           }
90           return prev;
91         }, [] as OptionsItem[]);
92       });
ac1a36 93
V 94       watchEffect(() => {
5b4a41 95         props.immediate && fetch();
ac1a36 96       });
V 97
50207a 98       watch(
99         () => props.params,
100         () => {
101           !unref(isFirstLoad) && fetch();
102         },
56a966 103         { deep: true },
50207a 104       );
105
ac1a36 106       async function fetch() {
V 107         const api = props.api;
108         if (!api || !isFunction(api)) return;
9cf070 109         options.value = [];
ac1a36 110         try {
V 111           loading.value = true;
112           const res = await api(props.params);
113           if (Array.isArray(res)) {
114             options.value = res;
5b4a41 115             emitChange();
ac1a36 116             return;
V 117           }
118           if (props.resultField) {
119             options.value = get(res, props.resultField) || [];
120           }
5b4a41 121           emitChange();
ac1a36 122         } catch (error) {
V 123           console.warn(error);
124         } finally {
125           loading.value = false;
126         }
127       }
5b4a41 128
V 129       async function handleFetch() {
130         if (!props.immediate && unref(isFirstLoad)) {
131           await fetch();
132           isFirstLoad.value = false;
133         }
134       }
135
136       function emitChange() {
897bed 137         emit('options-change', unref(getOptions));
5b4a41 138       }
V 139
fa64fc 140       function handleChange(_, ...args) {
V 141         emitData.value = args;
142       }
143
144       return { state, attrs, getOptions, loading, t, handleFetch, handleChange };
ac1a36 145     },
V 146   });
147 </script>