Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve ApiSelect component #5075

Merged
merged 2 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion apps/web-antd/src/adapter/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'ApiSelect'
| 'ApiTreeSelect'
| 'AutoComplete'
| 'Checkbox'
| 'CheckboxGroup'
Expand Down Expand Up @@ -88,7 +89,23 @@ async function initComponentAdapter() {
component: Select,
loadingSlot: 'suffixIcon',
visibleEvent: 'onDropdownVisibleChange',
modelField: 'value',
modelPropName: 'value',
},
slots,
);
},
ApiTreeSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: TreeSelect,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange',
},
slots,
);
Expand Down
19 changes: 18 additions & 1 deletion apps/web-ele/src/adapter/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'ApiSelect'
| 'ApiTreeSelect'
| 'Checkbox'
| 'CheckboxGroup'
| 'DatePicker'
Expand Down Expand Up @@ -77,7 +78,23 @@ async function initComponentAdapter() {
...attrs,
component: ElSelectV2,
loadingSlot: 'loading',
visibleEvent: 'onDropdownVisibleChange',
visibleEvent: 'onVisibleChange',
},
slots,
);
},
ApiTreeSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: ElTreeSelect,
props: { label: 'label', children: 'children' },
nodeKey: 'value',
loadingSlot: 'loading',
optionsPropName: 'data',
visibleEvent: 'onVisibleChange',
},
slots,
);
Expand Down
39 changes: 39 additions & 0 deletions apps/web-ele/src/views/demos/form/basic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Page } from '@vben/common-ui';
import { ElButton, ElCard, ElCheckbox, ElMessage } from 'element-plus';

import { useVbenForm } from '#/adapter/form';
import { getAllMenusApi } from '#/api';

const [Form, formApi] = useVbenForm({
commonConfig: {
Expand All @@ -21,6 +22,44 @@ const [Form, formApi] = useVbenForm({
ElMessage.success(`表单数据:${JSON.stringify(values)}`);
},
schema: [
{
// 组件需要在 #/adapter.ts内注册,并加上类型
component: 'ApiSelect',
// 对应组件的参数
componentProps: {
// 菜单接口转options格式
afterFetch: (data: { name: string; path: string }[]) => {
return data.map((item: any) => ({
label: item.name,
value: item.path,
}));
},
// 菜单接口
api: getAllMenusApi,
placeholder: '请选择',
},
// 字段名
fieldName: 'api',
// 界面显示的label
label: 'ApiSelect',
},
{
component: 'ApiTreeSelect',
// 对应组件的参数
componentProps: {
// 菜单接口
api: getAllMenusApi,
childrenField: 'children',
// 菜单接口转options格式
labelField: 'name',
placeholder: '请选择',
valueField: 'path',
},
// 字段名
fieldName: 'apiTree',
// 界面显示的label
label: 'ApiTreeSelect',
},
{
component: 'Input',
fieldName: 'string',
Expand Down
20 changes: 19 additions & 1 deletion apps/web-naive/src/adapter/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'ApiSelect'
| 'ApiTreeSelect'
| 'Checkbox'
| 'CheckboxGroup'
| 'DatePicker'
Expand Down Expand Up @@ -74,7 +75,24 @@ async function initComponentAdapter() {
...props,
...attrs,
component: NSelect,
modelField: 'value',
modelPropName: 'value',
},
slots,
);
},
ApiTreeSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: NTreeSelect,
nodeKey: 'value',
loadingSlot: 'arrow',
keyField: 'value',
modelPropName: 'value',
optionsPropName: 'options',
visibleEvent: 'onVisibleChange',
},
slots,
);
Expand Down
39 changes: 39 additions & 0 deletions apps/web-naive/src/views/demos/form/basic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Page } from '@vben/common-ui';
import { NButton, NCard, useMessage } from 'naive-ui';

import { useVbenForm } from '#/adapter/form';
import { getAllMenusApi } from '#/api';

const message = useMessage();
const [Form, formApi] = useVbenForm({
Expand All @@ -20,6 +21,44 @@ const [Form, formApi] = useVbenForm({
message.success(`表单数据:${JSON.stringify(values)}`);
},
schema: [
{
// 组件需要在 #/adapter.ts内注册,并加上类型
component: 'ApiSelect',
// 对应组件的参数
componentProps: {
// 菜单接口转options格式
afterFetch: (data: { name: string; path: string }[]) => {
return data.map((item: any) => ({
label: item.name,
value: item.path,
}));
},
// 菜单接口
api: getAllMenusApi,
placeholder: '请选择',
},
// 字段名
fieldName: 'api',
// 界面显示的label
label: 'ApiSelect',
},
{
component: 'ApiTreeSelect',
// 对应组件的参数
componentProps: {
// 菜单接口
api: getAllMenusApi,
childrenField: 'children',
// 菜单接口转options格式
labelField: 'name',
placeholder: '请选择',
valueField: 'path',
},
// 字段名
fieldName: 'apiTree',
// 界面显示的label
label: 'ApiTreeSelect',
},
{
component: 'Input',
fieldName: 'string',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,56 @@ 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 });

const props = withDefaults(defineProps<Props>(), {
labelField: 'label',
valueField: 'value',
childrenField: '',
optionsPropName: 'options',
mynetfan marked this conversation as resolved.
Show resolved Hide resolved
resultField: '',
visibleEvent: '',
numberToString: false,
Expand All @@ -50,7 +69,7 @@ const props = withDefaults(defineProps<Props>(), {
loadingSlot: '',
beforeFetch: undefined,
afterFetch: undefined,
modelField: 'modelValue',
modelPropName: 'modelValue',
api: undefined,
options: () => [],
});
Expand All @@ -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']),
Expand Down Expand Up @@ -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">
Expand Down
19 changes: 18 additions & 1 deletion playground/src/adapter/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'ApiSelect'
| 'ApiTreeSelect'
| 'AutoComplete'
| 'Checkbox'
| 'CheckboxGroup'
Expand Down Expand Up @@ -88,7 +89,23 @@ async function initComponentAdapter() {
...attrs,
component: Select,
loadingSlot: 'suffixIcon',
modelField: 'value',
modelPropName: 'value',
visibleEvent: 'onVisibleChange',
},
slots,
);
},
ApiTreeSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: TreeSelect,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange',
},
slots,
Expand Down
17 changes: 17 additions & 0 deletions playground/src/views/examples/form/basic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@ const [BaseForm, baseFormApi] = useVbenForm({
// 界面显示的label
label: 'ApiSelect',
},
{
component: 'ApiTreeSelect',
// 对应组件的参数
componentProps: {
// 菜单接口
api: getAllMenusApi,
childrenField: 'children',
// 菜单接口转options格式
labelField: 'name',
placeholder: '请选择',
valueField: 'path',
},
// 字段名
fieldName: 'apiTree',
// 界面显示的label
label: 'ApiTreeSelect',
},
{
component: 'InputPassword',
componentProps: {
Expand Down
Loading