From 2b1fcb038b80cec13cbb68934eda81dabb55f65c Mon Sep 17 00:00:00 2001 From: Caisin Date: Sun, 12 Apr 2026 14:29:18 +0800 Subject: [PATCH] feat(common-ui): add labelFn support to ApiComponent (#7801) * feat: allow api-component labels to be derived from option data ApiComponent already normalizes option records into the label/value shape used by consuming controls, but label text could only come from a single field. Add labelFn so callers can build labels from the full option record while keeping labelField as the fallback path. This keeps the change inside the existing component instead of introducing a wrapper, and it also normalizes direct options through the same transform path as API-loaded options for consistent behavior. Constraint: Must extend the existing ApiComponent API instead of adding a second Constraint: wrapper component Rejected: Add a separate ApiLabelComponent wrapper | Rejected: extra surface area for one option-mapping concern Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep labelFn as a presentation transform and preserve labelField Directive: fallback for existing callers Tested: pnpm exec eslint api-component.vue index.ts types.ts Tested: pnpm exec vue-tsc --noEmit -p packages/effects/common-ui/tsconfig.json Not-tested: runtime integration in consuming select/tree-select components * Update packages/effects/common-ui/src/components/api-component/api-component.vue Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../api-component/api-component.vue | 25 +++++++++++-------- .../src/components/api-component/index.ts | 1 + .../src/components/api-component/types.ts | 4 +++ 3 files changed, 20 insertions(+), 10 deletions(-) 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 45ec260f4..2c6d69317 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 @@ -17,6 +17,7 @@ defineOptions({ name: 'ApiComponent', inheritAttrs: false }); const props = withDefaults(defineProps(), { labelField: 'label', valueField: 'value', + labelFn: undefined, disabledField: 'disabled', childrenField: '', optionsPropName: 'options', @@ -54,33 +55,37 @@ const hasPendingRequest = ref(false); const getOptions = computed(() => { const { labelField, + labelFn, valueField, disabledField, childrenField, numberToString, } = props; - const refOptionsData = unref(refOptions); - - function transformData(data: OptionsItem[]): OptionsItem[] { + function transformData(data: OptionsItem[] = []): OptionsItem[] { return data.map((item) => { const value = get(item, valueField); - const disabled = get(item, disabledField); + const children = childrenField ? get(item, childrenField) : item.children; return { - ...objectOmit(item, [labelField, valueField, disabled, childrenField]), - label: get(item, labelField), + ...objectOmit(item, [ + labelField, + valueField, + disabledField, + ...(childrenField ? [childrenField] : []), + ]), + label: labelFn ? labelFn(item) : get(item, labelField), value: numberToString ? `${value}` : value, disabled: get(item, disabledField), - ...(childrenField && item[childrenField] - ? { children: transformData(item[childrenField]) } + ...(Array.isArray(children) && children.length > 0 + ? { children: transformData(children) } : {}), }; }); } - const data: OptionsItem[] = transformData(refOptionsData); + const data = transformData(unref(refOptions)); - return data.length > 0 ? data : props.options; + return data.length > 0 ? data : transformData(props.options); }); const bindProps = computed(() => { diff --git a/packages/effects/common-ui/src/components/api-component/index.ts b/packages/effects/common-ui/src/components/api-component/index.ts index 097858698..173b30d1c 100644 --- a/packages/effects/common-ui/src/components/api-component/index.ts +++ b/packages/effects/common-ui/src/components/api-component/index.ts @@ -1,5 +1,6 @@ export { default as ApiComponent } from './api-component.vue'; export type { + ApiComponentLabelFn, ApiComponentOptionsItem, ApiComponentProps, ApiComponentSharedProps, diff --git a/packages/effects/common-ui/src/components/api-component/types.ts b/packages/effects/common-ui/src/components/api-component/types.ts index 000636a7f..a771f78a5 100644 --- a/packages/effects/common-ui/src/components/api-component/types.ts +++ b/packages/effects/common-ui/src/components/api-component/types.ts @@ -10,6 +10,8 @@ export type ApiComponentOptionsItem = { value?: number | string; }; +export type ApiComponentLabelFn = (item: ApiComponentOptionsItem) => string; + export interface ApiComponentProps { /** 组件 */ component: Component; @@ -23,6 +25,8 @@ export interface ApiComponentProps { resultField?: string; /** label字段名 */ labelField?: string; + /** 通过选项数据自定义label */ + labelFn?: ApiComponentLabelFn; /** children字段名,需要层级数据的组件可用 */ childrenField?: string; /** value字段名 */