Commit 5b4a41ced412fe3623618791ffa3123a3a2cfcdc

Authored by Vben
1 parent e090689e

feat(api-select): add immediate option,close #430

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 <template> 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 <template #[item]="data" v-for="item in Object.keys($slots)"> 8 <template #[item]="data" v-for="item in Object.keys($slots)">
4 <slot :name="item" v-bind="data"></slot> 9 <slot :name="item" v-bind="data"></slot>
5 </template> 10 </template>
@@ -51,11 +56,13 @@ @@ -51,11 +56,13 @@
51 resultField: propTypes.string.def(''), 56 resultField: propTypes.string.def(''),
52 labelField: propTypes.string.def('label'), 57 labelField: propTypes.string.def('label'),
53 valueField: propTypes.string.def('value'), 58 valueField: propTypes.string.def('value'),
  59 + immediate: propTypes.bool.def(true),
54 }, 60 },
55 emits: ['options-change', 'change'], 61 emits: ['options-change', 'change'],
56 setup(props, { emit }) { 62 setup(props, { emit }) {
57 const options = ref<OptionsItem[]>([]); 63 const options = ref<OptionsItem[]>([]);
58 const loading = ref(false); 64 const loading = ref(false);
  65 + const isFirstLoad = ref(true);
59 const attrs = useAttrs(); 66 const attrs = useAttrs();
60 const { t } = useI18n(); 67 const { t } = useI18n();
61 68
@@ -78,7 +85,7 @@ @@ -78,7 +85,7 @@
78 }); 85 });
79 86
80 watchEffect(() => { 87 watchEffect(() => {
81 - fetch(); 88 + props.immediate && fetch();
82 }); 89 });
83 90
84 async function fetch() { 91 async function fetch() {
@@ -90,20 +97,32 @@ @@ -90,20 +97,32 @@
90 const res = await api(props.params); 97 const res = await api(props.params);
91 if (Array.isArray(res)) { 98 if (Array.isArray(res)) {
92 options.value = res; 99 options.value = res;
93 - emit('options-change', unref(options)); 100 + emitChange();
94 return; 101 return;
95 } 102 }
96 if (props.resultField) { 103 if (props.resultField) {
97 options.value = get(res, props.resultField) || []; 104 options.value = get(res, props.resultField) || [];
98 } 105 }
99 - emit('options-change', unref(options)); 106 + emitChange();
100 } catch (error) { 107 } catch (error) {
101 console.warn(error); 108 console.warn(error);
102 } finally { 109 } finally {
103 loading.value = false; 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 </script> 128 </script>