mirror of
https://gitee.com/JavaLionLi/plus-ui.git
synced 2026-09-13 07:43:43 +08:00
feat(RemoteSelect): 新增远程搜索选择组件
This commit is contained in:
parent
0870ce1751
commit
e9653ebcbc
147
src/components/RemoteSelect/index.vue
Normal file
147
src/components/RemoteSelect/index.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selectedValue"
|
||||
:placeholder="placeholder"
|
||||
:clearable="clearable"
|
||||
filterable
|
||||
:remote="isRemote"
|
||||
:remote-method="remoteMethod"
|
||||
:loading="loading"
|
||||
style="width: 100%"
|
||||
@change="handleChange"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item[valueKey]" :label="item[labelKey]" :value="item[valueKey]" />
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import request from '@/utils/request';
|
||||
|
||||
interface Props {
|
||||
/** 列表数据请求地址 */
|
||||
url: string;
|
||||
/** 选项显示文字的字段名 */
|
||||
labelKey?: string;
|
||||
/** 选项值的字段名 */
|
||||
valueKey?: string;
|
||||
/** 远程搜索时的查询参数字段名 */
|
||||
searchKey?: string;
|
||||
/** 额外的固定请求参数 */
|
||||
params?: Record<string, any>;
|
||||
/** 绑定值 */
|
||||
modelValue?: string | number;
|
||||
/** 占位提示文字 */
|
||||
placeholder?: string;
|
||||
/** 是否可清空 */
|
||||
clearable?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
labelKey: 'label',
|
||||
valueKey: 'value',
|
||||
searchKey: 'keyword',
|
||||
params: () => ({}),
|
||||
placeholder: '请选择',
|
||||
clearable: true
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
const selectedValue = ref<string | number | undefined>(props.modelValue);
|
||||
const options = ref<Record<string, any>[]>([]);
|
||||
const originalOptions = ref<Record<string, any>[]>([]);
|
||||
const loading = ref(false);
|
||||
const isRemote = ref(false);
|
||||
|
||||
let searchTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
/** 获取列表数据 */
|
||||
const fetchData = async (keyword?: string) => {
|
||||
if (!props.url) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const queryParams: Record<string, any> = {
|
||||
pageNum: 1,
|
||||
pageSize: 500,
|
||||
...props.params
|
||||
};
|
||||
if (keyword) {
|
||||
queryParams[props.searchKey] = keyword;
|
||||
}
|
||||
|
||||
const res: any = await request({
|
||||
url: props.url,
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
|
||||
const rows = res.data.rows || res.data || [];
|
||||
options.value = rows;
|
||||
|
||||
// 首次加载(无关键词)时判断是否启用远程搜索
|
||||
if (keyword === undefined) {
|
||||
isRemote.value = res.total > 500;
|
||||
}
|
||||
|
||||
// 缓存无关键词时的数据,用于远程搜索清空时恢复
|
||||
if (!keyword) {
|
||||
originalOptions.value = rows;
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 远程搜索方法 */
|
||||
const remoteMethod = (query: string) => {
|
||||
if (!isRemote.value) return;
|
||||
|
||||
// 空查询时恢复原始数据,不发起新请求
|
||||
if (!query) {
|
||||
options.value = originalOptions.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (searchTimer) {
|
||||
clearTimeout(searchTimer);
|
||||
}
|
||||
|
||||
searchTimer = setTimeout(() => {
|
||||
fetchData(query);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
/** 值变化事件 */
|
||||
const handleChange = (val: any) => {
|
||||
emit('update:modelValue', val);
|
||||
const selected = options.value.find((item) => item[props.valueKey] === val);
|
||||
emit('change', val, selected);
|
||||
};
|
||||
|
||||
/** 监听外部 modelValue 变化 */
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
selectedValue.value = newVal;
|
||||
}
|
||||
);
|
||||
|
||||
/** 监听 url 或 params 变化,重新加载数据 */
|
||||
watch(
|
||||
() => `${props.url}|${JSON.stringify(props.params)}`,
|
||||
() => {
|
||||
fetchData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
options,
|
||||
loading
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user