mirror of
https://gitee.com/JavaLionLi/plus-ui.git
synced 2026-09-13 07:43:43 +08:00
148 lines
3.2 KiB
Vue
148 lines
3.2 KiB
Vue
<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>
|