|
@@ -10,30 +10,47 @@ import { objectOmit } from '@vueuse/core';
|
|
|
|
|
|
type OptionsItem = {
|
|
|
[name: string]: any;
|
|
|
+ children?: OptionsItem[];
|
|
|
disabled?: boolean;
|
|
|
label?: string;
|
|
|
value?: string;
|
|
|
};
|
|
|
|
|
|
interface Props {
|
|
|
- // 组件
|
|
|
+ /** 组件 */
|
|
|
component: VNode;
|
|
|
+ /** 是否将value从数字转为string */
|
|
|
numberToString?: boolean;
|
|
|
+ /** 获取options数据的函数 */
|
|
|
api?: (arg?: any) => Promise<OptionsItem[] | Record<string, any>>;
|
|
|
+ /** 传递给api的参数 */
|
|
|
params?: Record<string, any>;
|
|
|
+ /** 从api返回的结果中提取options数组的字段名 */
|
|
|
resultField?: string;
|
|
|
+ /** label字段名 */
|
|
|
labelField?: string;
|
|
|
+ /** children字段名,需要层级数据的组件可用 */
|
|
|
+ childrenField?: string;
|
|
|
+ /** value字段名 */
|
|
|
valueField?: string;
|
|
|
+ /** 组件接收options数据的属性名 */
|
|
|
+ optionsPropName?: string;
|
|
|
+ /** 是否立即调用api */
|
|
|
immediate?: boolean;
|
|
|
+ /** 每次`visibleEvent`事件发生时都重新请求数据 */
|
|
|
alwaysLoad?: boolean;
|
|
|
+ /** 在api请求之前的回调函数 */
|
|
|
beforeFetch?: AnyPromiseFunction<any, any>;
|
|
|
+ /** 在api请求之后的回调函数 */
|
|
|
afterFetch?: AnyPromiseFunction<any, any>;
|
|
|
+ /** 直接传入选项数据,也作为api返回空数据时的后备数据 */
|
|
|
options?: OptionsItem[];
|
|
|
- // 尾部插槽
|
|
|
+ /** 组件的插槽名称,用来显示一个"加载中"的图标 */
|
|
|
loadingSlot?: string;
|
|
|
- // 可见时触发的事件名
|
|
|
+ /** 触发api请求的事件名 */
|
|
|
visibleEvent?: string;
|
|
|
- modelField?: string;
|
|
|
+ /** 组件的v-model属性名,默认为modelValue。部分组件可能为value */
|
|
|
+ modelPropName?: string;
|
|
|
}
|
|
|
|
|
|
defineOptions({ name: 'ApiSelect', inheritAttrs: false });
|
|
@@ -41,6 +58,8 @@ defineOptions({ name: 'ApiSelect', inheritAttrs: false });
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
labelField: 'label',
|
|
|
valueField: 'value',
|
|
|
+ childrenField: '',
|
|
|
+ optionsPropName: 'options',
|
|
|
resultField: '',
|
|
|
visibleEvent: '',
|
|
|
numberToString: false,
|
|
@@ -50,7 +69,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
loadingSlot: '',
|
|
|
beforeFetch: undefined,
|
|
|
afterFetch: undefined,
|
|
|
- modelField: 'modelValue',
|
|
|
+ modelPropName: 'modelValue',
|
|
|
api: undefined,
|
|
|
options: () => [],
|
|
|
});
|
|
@@ -69,29 +88,34 @@ const loading = ref(false);
|
|
|
const isFirstLoaded = ref(false);
|
|
|
|
|
|
const getOptions = computed(() => {
|
|
|
- const { labelField, valueField, numberToString } = props;
|
|
|
+ const { labelField, valueField, childrenField, numberToString } = props;
|
|
|
|
|
|
- const data: OptionsItem[] = [];
|
|
|
const refOptionsData = unref(refOptions);
|
|
|
|
|
|
- for (const next of refOptionsData) {
|
|
|
- if (next) {
|
|
|
- const value = get(next, valueField);
|
|
|
- data.push({
|
|
|
- ...objectOmit(next, [labelField, valueField]),
|
|
|
- label: get(next, labelField),
|
|
|
+ function transformData(data: OptionsItem[]): OptionsItem[] {
|
|
|
+ return data.map((item) => {
|
|
|
+ const value = get(item, valueField);
|
|
|
+ return {
|
|
|
+ ...objectOmit(item, [labelField, valueField, childrenField]),
|
|
|
+ label: get(item, labelField),
|
|
|
value: numberToString ? `${value}` : value,
|
|
|
- });
|
|
|
- }
|
|
|
+ ...(childrenField && item[childrenField]
|
|
|
+ ? { children: transformData(item[childrenField]) }
|
|
|
+ : {}),
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
+ const data: OptionsItem[] = transformData(refOptionsData);
|
|
|
+
|
|
|
return data.length > 0 ? data : props.options;
|
|
|
});
|
|
|
|
|
|
const bindProps = computed(() => {
|
|
|
return {
|
|
|
- [props.modelField]: unref(modelValue),
|
|
|
- [`onUpdate:${props.modelField}`]: (val: string) => {
|
|
|
+ [props.modelPropName]: unref(modelValue),
|
|
|
+ [props.optionsPropName]: unref(getOptions),
|
|
|
+ [`onUpdate:${props.modelPropName}`]: (val: string) => {
|
|
|
modelValue.value = val;
|
|
|
},
|
|
|
...objectOmit(attrs, ['onUpdate:value']),
|
|
@@ -168,7 +192,6 @@ function emitChange() {
|
|
|
<component
|
|
|
:is="component"
|
|
|
v-bind="bindProps"
|
|
|
- :options="getOptions"
|
|
|
:placeholder="$attrs.placeholder"
|
|
|
>
|
|
|
<template v-for="item in Object.keys($slots)" #[item]="data">
|