ApiSelect组件支持分页下拉方案 #7883

pull/8091/head
JEECG 2025-04-09 09:40:58 +08:00
parent 73a5f64d7e
commit 83b1c8692e
1 changed files with 73 additions and 18 deletions

View File

@ -1,5 +1,12 @@
<template>
<Select @dropdownVisibleChange="handleFetch" v-bind="attrs_" @change="handleChange" :options="getOptions" v-model:value="state">
<Select
v-bind="attrs_"
v-model:value="state"
:options="getOptions"
@change="handleChange"
@dropdownVisibleChange="handleFetch"
@popupScroll="handlePopupScroll"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
@ -24,9 +31,10 @@
import { LoadingOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { propTypes } from '/@/utils/propTypes';
import { isNumber } from '/@/utils/is';
type OptionsItem = { label: string; value: string; disabled?: boolean };
// https://help.jeecg.com/ui/apiSelect#pageconfig%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE
export default defineComponent({
name: 'ApiSelect',
components: {
@ -35,7 +43,7 @@
},
inheritAttrs: false,
props: {
value: [Array, Object, String, Number],
value: [Array, String, Number],
numberToString: propTypes.bool,
api: {
type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
@ -46,6 +54,11 @@
type: Object as PropType<Recordable>,
default: () => ({}),
},
//
pageConfig: {
type: Object as PropType<Recordable>,
default: () => ({ isPage: false }),
},
// support xxx.xxx.xx
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
@ -60,7 +73,15 @@
const emitData = ref<any[]>([]);
const attrs = useAttrs();
const { t } = useI18n();
// update-begin--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
const hasMore = ref(true);
const pagination = ref({
pageNo: 1,
pageSize: 10,
total: 0,
});
const defPageConfig = { isPage: false, pageField: 'pageNo', pageSizeField: 'pageSize', totalField: 'total', listField: 'records' };
// update-end--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
// Embedded in the form, just use the hook binding to perform form verification
const [state, setState] = useRuleFormItem(props, 'value', 'change', emitData);
// update-begin--author:liaozhiyang---date:20230830---forQQYUN-6308
@ -114,7 +135,7 @@
},
{ deep: true }
);
//
//
watchEffect(() => {
props.value && handleFetch();
});
@ -122,17 +143,33 @@
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
options.value = [];
// update-begin--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
if (!props.pageConfig.isPage || pagination.value.pageNo == 1) {
options.value = [];
}
try {
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
let { isPage, pageField, pageSizeField, totalField, listField } = { ...defPageConfig, ...props.pageConfig };
let params = isPage
? { ...props.params, [pageField]: pagination.value.pageNo, [pageSizeField]: pagination.value.pageSize }
: { ...props.params };
// update-end--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
const res = await api(params);
if (isPage) {
// update-begin--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
options.value = [...options.value, ...res[listField]];
pagination.value.total = res[totalField] || 0;
hasMore.value = res[totalField] ? options.value.length < res[totalField] : res[listField] < pagination.value.pageSize;
// update-end--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
} else {
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
}
emitChange();
} catch (error) {
@ -151,9 +188,17 @@
function initValue() {
let value = props.value;
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
state.value = value.split(',');
// update-begin--author:liaozhiyang---date:20250407---forissues/8037
if (unref(attrs).mode == 'multiple') {
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
state.value = value.split(',');
} else if (isNumber(value)) {
state.value = [value];
}
} else {
state.value = value;
}
// update-end--author:liaozhiyang---date:20250407---forissues/8037
}
async function handleFetch() {
@ -171,8 +216,18 @@
vModalValue && vModalValue(_);
emitData.value = args;
}
return { state, attrs_, attrs, getOptions, loading, t, handleFetch, handleChange };
// update-begin--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
//
function handlePopupScroll(e) {
const { scrollTop, scrollHeight, clientHeight } = e.target;
const isNearBottom = scrollHeight - scrollTop <= clientHeight + 20;
if (props.pageConfig.isPage && isNearBottom && hasMore.value && !loading.value) {
pagination.value.pageNo += 1;
fetch();
}
}
// update-end--author:liusq---date:20250407---forQQYUN-11831ApiSelect #7883
return { state, attrs_, attrs, getOptions, loading, t, handleFetch, handleChange, handlePopupScroll };
},
});
</script>