From a8c47863115908a353318783df4094fb0f279fcb Mon Sep 17 00:00:00 2001 From: Netfan Date: Sat, 12 Apr 2025 14:02:35 +0800 Subject: [PATCH] feat: api-component support autoSelect prop (#5931) * feat: api-component support autoSelect prop * docs: add version requirement --- .../common-ui/vben-api-component.md | 50 +++++++++++-------- .../api-component/api-component.vue | 36 ++++++++++++- playground/src/views/examples/form/basic.vue | 1 + 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/docs/src/components/common-ui/vben-api-component.md b/docs/src/components/common-ui/vben-api-component.md index dac916bf..17b8d306 100644 --- a/docs/src/components/common-ui/vben-api-component.md +++ b/docs/src/components/common-ui/vben-api-component.md @@ -131,26 +131,36 @@ function fetchApi(): Promise> { ### Props -| 属性名 | 描述 | 类型 | 默认值 | -| --- | --- | --- | --- | -| modelValue(v-model) | 当前值 | `any` | - | -| component | 欲包装的组件(以下称为目标组件) | `Component` | - | -| numberToString | 是否将value从数字转为string | `boolean` | `false` | -| api | 获取数据的函数 | `(arg?: any) => Promise>` | - | -| params | 传递给api的参数 | `Record` | - | -| resultField | 从api返回的结果中提取options数组的字段名 | `string` | - | -| labelField | label字段名 | `string` | `label` | -| childrenField | 子级数据字段名,需要层级数据的组件可用 | `string` | `` | -| valueField | value字段名 | `string` | `value` | -| optionsPropName | 目标组件接收options数据的属性名称 | `string` | `options` | -| modelPropName | 目标组件的双向绑定属性名,默认为modelValue。部分组件可能为value | `string` | `modelValue` | -| immediate | 是否立即调用api | `boolean` | `true` | -| alwaysLoad | 每次`visibleEvent`事件发生时都重新请求数据 | `boolean` | `false` | -| beforeFetch | 在api请求之前的回调函数 | `AnyPromiseFunction` | - | -| afterFetch | 在api请求之后的回调函数 | `AnyPromiseFunction` | - | -| options | 直接传入选项数据,也作为api返回空数据时的后备数据 | `OptionsItem[]` | - | -| visibleEvent | 触发重新请求数据的事件名 | `string` | - | -| loadingSlot | 目标组件的插槽名称,用来显示一个"加载中"的图标 | `string` | - | +| 属性名 | 描述 | 类型 | 默认值 | 版本要求 | +| --- | --- | --- | --- | --- | +| modelValue(v-model) | 当前值 | `any` | - | - | +| component | 欲包装的组件(以下称为目标组件) | `Component` | - | - | +| numberToString | 是否将value从数字转为string | `boolean` | `false` | - | +| api | 获取数据的函数 | `(arg?: any) => Promise>` | - | - | +| params | 传递给api的参数 | `Record` | - | - | +| resultField | 从api返回的结果中提取options数组的字段名 | `string` | - | - | +| labelField | label字段名 | `string` | `label` | - | +| childrenField | 子级数据字段名,需要层级数据的组件可用 | `string` | `` | - | +| valueField | value字段名 | `string` | `value` | - | +| optionsPropName | 目标组件接收options数据的属性名称 | `string` | `options` | - | +| modelPropName | 目标组件的双向绑定属性名,默认为modelValue。部分组件可能为value | `string` | `modelValue` | - | +| immediate | 是否立即调用api | `boolean` | `true` | - | +| alwaysLoad | 每次`visibleEvent`事件发生时都重新请求数据 | `boolean` | `false` | - | +| beforeFetch | 在api请求之前的回调函数 | `AnyPromiseFunction` | - | - | +| afterFetch | 在api请求之后的回调函数 | `AnyPromiseFunction` | - | - | +| options | 直接传入选项数据,也作为api返回空数据时的后备数据 | `OptionsItem[]` | - | - | +| visibleEvent | 触发重新请求数据的事件名 | `string` | - | - | +| loadingSlot | 目标组件的插槽名称,用来显示一个"加载中"的图标 | `string` | - | - | +| autoSelect | 自动设置选项 | `'first' \| 'last' \| 'none'\| false` | `false` | >5.5.4 | + +#### autoSelect 自动设置选项 + +如果当前值为undefined,在选项数据成功加载之后,自动从备选项中选择一个作为当前值。默认值为`false`,即不自动选择选项。注意:该属性不应用于多选组件。可选值有: + +- `first`:自动选择第一个选项 +- `last`:自动选择最后一个选项 +- `one`:有且仅有一个选项时,自动选择它 +- false:不自动选择选项 ### Methods diff --git a/packages/effects/common-ui/src/components/api-component/api-component.vue b/packages/effects/common-ui/src/components/api-component/api-component.vue index f4f6acd5..b3b33025 100644 --- a/packages/effects/common-ui/src/components/api-component/api-component.vue +++ b/packages/effects/common-ui/src/components/api-component/api-component.vue @@ -54,6 +54,14 @@ interface Props { visibleEvent?: string; /** 组件的v-model属性名,默认为modelValue。部分组件可能为value */ modelPropName?: string; + /** + * 自动选择 + * - `first`:自动选择第一个选项 + * - `last`:自动选择最后一个选项 + * - `one`: 当请求的结果只有一个选项时,自动选择该选项 + * - false:不自动选择(默认) + */ + autoSelect?: 'first' | 'last' | 'one' | false; } defineOptions({ name: 'ApiComponent', inheritAttrs: false }); @@ -74,6 +82,7 @@ const props = withDefaults(defineProps(), { afterFetch: undefined, modelPropName: 'modelValue', api: undefined, + autoSelect: false, options: () => [], }); @@ -81,7 +90,7 @@ const emit = defineEmits<{ optionsChange: [OptionsItem[]]; }>(); -const modelValue = defineModel({ default: '' }); +const modelValue = defineModel({ default: undefined }); const attrs = useAttrs(); const innerParams = ref({}); @@ -194,6 +203,31 @@ watch( ); function emitChange() { + if ( + modelValue.value === undefined && + props.autoSelect && + unref(getOptions).length > 0 + ) { + let firstOption; + switch (props.autoSelect) { + case 'first': { + firstOption = unref(getOptions)[0]; + break; + } + case 'last': { + firstOption = unref(getOptions)[unref(getOptions).length - 1]; + break; + } + case 'one': { + if (unref(getOptions).length === 1) { + firstOption = unref(getOptions)[0]; + } + break; + } + } + + if (firstOption) modelValue.value = firstOption[props.valueField]; + } emit('optionsChange', unref(getOptions)); } const componentRef = ref(); diff --git a/playground/src/views/examples/form/basic.vue b/playground/src/views/examples/form/basic.vue index 5d734d43..75e868d8 100644 --- a/playground/src/views/examples/form/basic.vue +++ b/playground/src/views/examples/form/basic.vue @@ -74,6 +74,7 @@ const [BaseForm, baseFormApi] = useVbenForm({ }, // 菜单接口 api: getAllMenusApi, + autoSelect: 'first', }, // 字段名 fieldName: 'api',