feat: ApiSelect增加远程搜索功能
This commit is contained in:
parent
95abe06107
commit
56c5dce99f
|
|
@ -3,6 +3,7 @@
|
||||||
@dropdown-visible-change="handleFetch"
|
@dropdown-visible-change="handleFetch"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
|
@search="debounceSearchFn"
|
||||||
:options="getOptions"
|
:options="getOptions"
|
||||||
v-model:value="state"
|
v-model:value="state"
|
||||||
>
|
>
|
||||||
|
|
@ -20,26 +21,37 @@
|
||||||
</template>
|
</template>
|
||||||
</Select>
|
</Select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PropType, ref, computed, unref, watch } from 'vue';
|
import { computed, PropType, ref, unref, watch } from 'vue';
|
||||||
import { Select } from 'ant-design-vue';
|
import { Select } from 'ant-design-vue';
|
||||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||||
import { isFunction } from '@/utils/is';
|
import { isEmpty, isFunction } from '@/utils/is';
|
||||||
import { useRuleFormItem } from '@/hooks/component/useFormItem';
|
import { useRuleFormItem } from '@/hooks/component/useFormItem';
|
||||||
import { get, omit, isEqual } from 'lodash-es';
|
import { assignIn, get, isEqual, omit } from 'lodash-es';
|
||||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||||
import { useI18n } from '@/hooks/web/useI18n';
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
import { propTypes } from '@/utils/propTypes';
|
import { propTypes } from '@/utils/propTypes';
|
||||||
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
type OptionsItem = { label?: string; value?: string; disabled?: boolean; [name: string]: any };
|
type OptionsItem = { label?: string; value?: string; disabled?: boolean; [name: string]: any };
|
||||||
|
|
||||||
|
type ApiSearchOption = {
|
||||||
|
// 待搜索字段名
|
||||||
|
searchName?: string;
|
||||||
|
// 搜索前置方法
|
||||||
|
beforeFetch?: (value?: string) => Promise<string>;
|
||||||
|
// 拦截方法
|
||||||
|
interceptFetch?: (value?: string) => Promise<boolean>;
|
||||||
|
};
|
||||||
|
|
||||||
defineOptions({ name: 'ApiSelect', inheritAttrs: false });
|
defineOptions({ name: 'ApiSelect', inheritAttrs: false });
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
value: { type: [Array, Object, String, Number] as PropType<SelectValue> },
|
value: { type: [Array, Object, String, Number] as PropType<SelectValue> },
|
||||||
numberToString: propTypes.bool,
|
numberToString: propTypes.bool,
|
||||||
api: {
|
api: {
|
||||||
type: Function as PropType<(arg?: any) => Promise<OptionsItem[] | Recordable<any>>>,
|
type: Function as PropType<(arg?: any) => Promise<OptionsItem[] | Recordable>>,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
// api params
|
// api params
|
||||||
|
|
@ -54,6 +66,10 @@
|
||||||
type: Array<OptionsItem>,
|
type: Array<OptionsItem>,
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
|
apiSearch: {
|
||||||
|
type: Object as PropType<ApiSearchOption>,
|
||||||
|
default: () => null,
|
||||||
|
},
|
||||||
beforeFetch: {
|
beforeFetch: {
|
||||||
type: Function as PropType<Fn>,
|
type: Function as PropType<Fn>,
|
||||||
default: null,
|
default: null,
|
||||||
|
|
@ -72,6 +88,7 @@
|
||||||
// 首次是否加载过了
|
// 首次是否加载过了
|
||||||
const isFirstLoaded = ref(false);
|
const isFirstLoaded = ref(false);
|
||||||
const emitData = ref<OptionsItem[]>([]);
|
const emitData = ref<OptionsItem[]>([]);
|
||||||
|
const searchParams = ref<any>({});
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
// Embedded in the form, just use the hook binding to perform form verification
|
// Embedded in the form, just use the hook binding to perform form verification
|
||||||
|
|
@ -110,16 +127,29 @@
|
||||||
{ deep: true, immediate: props.immediate },
|
{ deep: true, immediate: props.immediate },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => searchParams.value,
|
||||||
|
(value, oldValue) => {
|
||||||
|
if (isEmpty(value) || isEqual(value, oldValue)) return;
|
||||||
|
(async () => {
|
||||||
|
await fetch();
|
||||||
|
searchParams.value = {};
|
||||||
|
})();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: props.immediate },
|
||||||
|
);
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
let { api, beforeFetch, afterFetch, params, resultField } = props;
|
let { api, beforeFetch, afterFetch, params, resultField } = props;
|
||||||
if (!api || !isFunction(api) || loading.value) return;
|
if (!api || !isFunction(api) || loading.value) return;
|
||||||
optionsRef.value = [];
|
optionsRef.value = [];
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
let apiParams = assignIn({}, params, searchParams.value);
|
||||||
if (beforeFetch && isFunction(beforeFetch)) {
|
if (beforeFetch && isFunction(beforeFetch)) {
|
||||||
params = (await beforeFetch(params)) || params;
|
apiParams = (await beforeFetch(apiParams)) || apiParams;
|
||||||
}
|
}
|
||||||
let res = await api(params);
|
let res = await api(apiParams);
|
||||||
if (afterFetch && isFunction(afterFetch)) {
|
if (afterFetch && isFunction(afterFetch)) {
|
||||||
res = (await afterFetch(res)) || res;
|
res = (await afterFetch(res)) || res;
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +182,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let debounceSearchFn = useDebounceFn(handleSearch, 500);
|
||||||
|
|
||||||
|
async function handleSearch(value: any) {
|
||||||
|
if (!props.apiSearch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { searchName, beforeFetch, interceptFetch } = props.apiSearch;
|
||||||
|
if (!searchName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value || undefined;
|
||||||
|
if (beforeFetch && isFunction(beforeFetch)) {
|
||||||
|
value = (await beforeFetch(value)) || value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interceptFetch && isFunction(interceptFetch)) {
|
||||||
|
if (!(await interceptFetch(value))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchParams.value = {
|
||||||
|
[searchName]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function emitChange() {
|
function emitChange() {
|
||||||
emit('options-change', unref(getOptions));
|
emit('options-change', unref(getOptions));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue