From cbd0442ffca2747bed99b3683ab358f95e8574c4 Mon Sep 17 00:00:00 2001 From: roymondchen Date: Mon, 19 Oct 2026 17:08:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(form):=20=E6=94=AF=E6=8C=81=20table/group-?= =?UTF-8?q?list=20=E8=A1=8C=E6=93=8D=E4=BD=9C=E6=8C=89=E9=92=AE=E6=96=87?= =?UTF-8?q?=E6=A1=88=E4=B8=8E=20props=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通过 ActionButtonConfig 统一 delete/copy/move/operate 等按钮的 text 与 props,并补充对应单测。 --- packages/form-schema/src/base.ts | 25 ++ .../form/src/containers/GroupListItem.vue | 19 +- .../src/containers/table/ActionsColumn.vue | 18 +- .../src/containers/table/useTableColumns.ts | 10 +- packages/form/src/utils/actionButton.ts | 35 +++ .../unit/containers/ActionsColumn.spec.ts | 97 +++++++- .../tests/unit/containers/GroupList.spec.ts | 79 +++++++ .../unit/containers/useTableColumns.spec.ts | 218 ++++++++++++++++++ .../tests/unit/utils/actionButton.spec.ts | 21 ++ 9 files changed, 505 insertions(+), 17 deletions(-) create mode 100644 packages/form/src/utils/actionButton.ts create mode 100644 packages/form/tests/unit/containers/useTableColumns.spec.ts create mode 100644 packages/form/tests/unit/utils/actionButton.spec.ts diff --git a/packages/form-schema/src/base.ts b/packages/form-schema/src/base.ts index 20b7d76e..9c23f225 100644 --- a/packages/form-schema/src/base.ts +++ b/packages/form-schema/src/base.ts @@ -832,6 +832,14 @@ export interface AddButtonConfig { } // #endregion AddButtonConfig +// #region ActionButtonConfig +/** + * 行操作按钮。与 `addButtonConfig` 相同:`text` 是文案,`props` 透传给按钮。 + * 不支持吸底。未配置 `text` 时使用内置中文。 + */ +export type ActionButtonConfig = Omit; +// #endregion ActionButtonConfig + // #region GroupListHeaderConfig /** group-list 卡片标题;仅 sticky 开启时吸顶 */ export interface GroupListHeaderConfig { @@ -862,6 +870,16 @@ export interface TableGroupListCommonConfig extends FormItem { /** 新增后滚动到最后一项(group-list 形态,默认关闭) */ scrollLastItemIntoView?: boolean; addButtonConfig?: AddButtonConfig; + /** 删除按钮,text 默认「删除」 */ + deleteButtonConfig?: ActionButtonConfig; + /** 复制按钮,text 默认「复制」 */ + copyButtonConfig?: ActionButtonConfig; + /** 上移按钮,text 默认「上移」。仅 group-list 形态 */ + moveUpButtonConfig?: ActionButtonConfig; + /** 下移按钮,text 默认「下移」。仅 group-list 形态 */ + moveDownButtonConfig?: ActionButtonConfig; + /** 移动至按钮,text 默认「移动至」。仅 group-list 形态 */ + moveToButtonConfig?: ActionButtonConfig; /** group-list 形态的标题吸顶(默认关闭);table 形态忽略 */ header?: GroupListHeaderConfig; } @@ -898,6 +916,11 @@ export interface TableConfig extends TableGroupListCommonConfig { showIndex?: boolean; /** 操作栏宽度 */ operateColWidth?: number | string; + /** + * 操作列。`text` 是列标题,默认「操作」。 + * `props` 透传给列(如 width、align、fixed、minWidth),可覆盖 operateColWidth / fixed。 + */ + operateColConfig?: ActionButtonConfig; pagination?: boolean; /** 表格标题前缀 ,新增xxx,暂无xxx数据,表明表格内容是什么 */ titlePrefix?: string; @@ -919,6 +942,8 @@ export interface TableConfig extends TableGroupListCommonConfig { dropActionButtonIcon?: any; copyActionButtonIcon?: any; deleteActionButtonIcon?: any; + /** 拖动排序按钮,text 作为提示,默认「拖动排序」 */ + dropSortButtonConfig?: ActionButtonConfig; /** 是否显示全屏按钮 */ enableFullscreen?: boolean; fixed?: boolean | 'left' | 'right'; diff --git a/packages/form/src/containers/GroupListItem.vue b/packages/form/src/containers/GroupListItem.vue index 371f81f4..518e6238 100644 --- a/packages/form/src/containers/GroupListItem.vue +++ b/packages/form/src/containers/GroupListItem.vue @@ -19,8 +19,9 @@ type="primary" :icon="DocumentCopy" :disabled="disabled" + v-bind="config.copyButtonConfig?.props" @click="copyHandler" - >复制{{ actionButtonText(config.copyButtonConfig, 'copy') }} @@ -58,8 +61,9 @@ type="primary" :icon="Position" :disabled="disabled" + v-bind="config.moveToButtonConfig?.props" @click="moveSpecifyLocationVisible = true" - >移动至{{ actionButtonText(config.moveToButtonConfig, 'moveTo') }}
@@ -81,7 +85,10 @@ - + @@ -122,6 +130,7 @@ import { ArrowDown, ArrowRight, Bottom, Delete, DocumentCopy, Position, Top } fr import { TMagicButton, TMagicCard, TMagicIcon, TMagicInputNumber, TMagicPopover, TMagicTooltip } from '@tmagic/design'; import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema'; +import { actionButtonText } from '../utils/actionButton'; import { appendProp, filterFunction } from '../utils/form'; import { getGroupListRowConfig } from '../utils/tableGroupList'; diff --git a/packages/form/src/containers/table/ActionsColumn.vue b/packages/form/src/containers/table/ActionsColumn.vue index 82416772..16dd1a5d 100644 --- a/packages/form/src/containers/table/ActionsColumn.vue +++ b/packages/form/src/containers/table/ActionsColumn.vue @@ -1,11 +1,16 @@ @@ -44,6 +51,7 @@ import { cloneDeep } from 'lodash-es'; import { TMagicButton, TMagicTooltip } from '@tmagic/design'; import type { FormState, TableConfig } from '@form/schema'; +import { actionButtonText } from '@form/utils/actionButton'; const emit = defineEmits(['change']); diff --git a/packages/form/src/containers/table/useTableColumns.ts b/packages/form/src/containers/table/useTableColumns.ts index 1c892ca2..382225e2 100644 --- a/packages/form/src/containers/table/useTableColumns.ts +++ b/packages/form/src/containers/table/useTableColumns.ts @@ -5,6 +5,7 @@ import { type TableColumnOptions, TMagicIcon, TMagicTooltip } from '@tmagic/desi import type { FormItemConfig, FormState } from '@tmagic/form-schema'; import type { ContainerChangeEventData } from '@form/schema'; +import { actionButtonText } from '@form/utils/actionButton'; import { isGlobalFlat } from '@form/utils/config'; import { appendProp, display as displayFunc, getDataByPage, sortArray } from '@form/utils/form'; import { isTableColumnRendered, makeTableColumnConfig } from '@form/utils/tableGroupList'; @@ -112,11 +113,12 @@ export const useTableColumns = ( const actionColumn = { props: { - label: '操作', - fixed: actionFixed, - - width: props.config.operateColWidth ?? (props.config.dropSortHandle && props.config.dropSort ? 132 : 112), align: 'center', + fixed: actionFixed, + width: props.config.operateColWidth ?? (props.config.dropSortHandle && props.config.dropSort ? 132 : 112), + // props 覆盖列上的默认属性(宽度、对齐、fixed 等),标题仍以 text 为准 + ...props.config.operateColConfig?.props, + label: actionButtonText(props.config.operateColConfig, 'operate'), }, cell: ({ row, $index }: any) => h(ActionsColumn, { diff --git a/packages/form/src/utils/actionButton.ts b/packages/form/src/utils/actionButton.ts new file mode 100644 index 00000000..76fdf6fa --- /dev/null +++ b/packages/form/src/utils/actionButton.ts @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making TMagicEditor available. + * + * Copyright (C) 2025 Tencent. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ActionButtonConfig } from '@tmagic/form-schema'; + +/** table / group-list 行操作的内置文案。空 text 与未配置一样回退到这里,与 addButtonConfig 一致 */ +export const DEFAULT_ACTION_BUTTON_TEXT = { + operate: '操作', + delete: '删除', + copy: '复制', + dropSort: '拖动排序', + moveUp: '上移', + moveDown: '下移', + moveTo: '移动至', +} as const; + +export const actionButtonText = ( + config: ActionButtonConfig | undefined, + key: keyof typeof DEFAULT_ACTION_BUTTON_TEXT, +): string => config?.text || DEFAULT_ACTION_BUTTON_TEXT[key]; diff --git a/packages/form/tests/unit/containers/ActionsColumn.spec.ts b/packages/form/tests/unit/containers/ActionsColumn.spec.ts index 9f17a756..32587f0c 100644 --- a/packages/form/tests/unit/containers/ActionsColumn.spec.ts +++ b/packages/form/tests/unit/containers/ActionsColumn.spec.ts @@ -29,14 +29,25 @@ const createMForm = (context: Record): FormState => () => context, ); -const mountColumn = (config: any, context: Record = { env: 'prod' }) => - mount(ActionsColumn as any, { +const COMPONENT_PROP_KEYS = ['flat', 'disabled', 'sortKey', 'model'] as const; + +const mountColumn = (config: any, context: Record = { env: 'prod' }) => { + const tableConfig = { ...config }; + const extra: Record = {}; + for (const key of COMPONENT_PROP_KEYS) { + if (key in tableConfig) { + extra[key] = tableConfig[key]; + delete tableConfig[key]; + } + } + + return mount(ActionsColumn as any, { global: { plugins: [ElementPlus as any, MagicForm as any], provide: { mForm: createMForm(context) }, }, props: { - config: { type: 'table', name: 'list', ...config }, + config: { type: 'table', name: 'list', ...tableConfig }, model: { list: [{ text: 'a' }] }, name: 'list', prop: 'list', @@ -44,8 +55,10 @@ const mountColumn = (config: any, context: Record = { env: 'prod' } pageSize: 10, index: 0, row: { text: 'a' }, + ...extra, }, }); +}; describe('ActionsColumn', () => { test('copyable 函数收到 model / index / prop', async () => { @@ -75,4 +88,82 @@ describe('ActionsColumn', () => { expect(wrapper.text()).not.toContain('复制'); }); + + test('未配置按钮文案时使用默认操作名称', async () => { + const wrapper = mountColumn({ flat: true, dropSort: true, dropSortHandle: true }); + await nextTick(); + + expect(wrapper.text()).toContain('删除'); + expect(wrapper.text()).toContain('复制'); + expect(wrapper.findComponent({ name: 'TMTooltip' }).props('content')).toBe('拖动排序'); + }); + + test('按钮配置与 addButtonConfig 一样,用 text 改名称、props 透传', async () => { + const wrapper = mountColumn({ + flat: true, + dropSort: true, + dropSortHandle: true, + deleteButtonConfig: { text: '移除', props: { class: 'custom-delete', type: 'warning', size: 'large' } }, + copyButtonConfig: { text: '克隆', props: { plain: true } }, + dropSortButtonConfig: { text: '拖拽换序', props: { class: 'custom-drag' } }, + }); + await nextTick(); + + expect(wrapper.text()).toContain('移除'); + expect(wrapper.text()).toContain('克隆'); + expect(wrapper.text()).not.toContain('删除'); + expect(wrapper.text()).not.toContain('复制'); + expect(wrapper.find('.custom-delete').exists()).toBe(true); + expect(wrapper.find('.custom-drag').exists()).toBe(true); + const deleteButton = wrapper.findAllComponents({ name: 'TMButton' }).find((btn) => btn.props('type') === 'warning'); + expect(deleteButton?.props('size')).toBe('large'); + const copyButton = wrapper.findAllComponents({ name: 'TMButton' }).find((btn) => btn.text().includes('克隆')); + expect(copyButton?.find('button').classes()).toContain('is-plain'); + expect(wrapper.findComponent({ name: 'TMTooltip' }).props('content')).toBe('拖拽换序'); + expect(wrapper.findAll('button').map((btn) => btn.attributes('title'))).toEqual( + expect.arrayContaining(['移除', '克隆']), + ); + }); + + test('delete 函数返回 false 时不展示删除,点击删除会移除当前行', async () => { + const deleteFn = vi.fn(() => false); + const hidden = mountColumn({ delete: deleteFn, flat: true }); + await nextTick(); + expect(deleteFn).toHaveBeenCalled(); + expect((hidden.findAll('button')[0].element as HTMLElement).style.display).toBe('none'); + + const wrapper = mountColumn({ flat: true }); + await nextTick(); + await wrapper.findAll('button')[0].trigger('click'); + expect(wrapper.emitted('change')?.[0][0]).toEqual([]); + }); + + test('disabled 时点击删除不改数据,复制会沿 sortKey 递减', async () => { + const blocked = mountColumn({ disabled: true, flat: true }); + await nextTick(); + await blocked.find('button').trigger('click'); + expect(blocked.emitted('change')).toBeUndefined(); + + const wrapper = mountColumn({ + flat: true, + sortKey: 'order', + copyable: true, + }); + await wrapper.setProps({ + model: { + list: [ + { text: 'a', order: 3 }, + { text: 'b', order: 1 }, + ], + }, + }); + await nextTick(); + const copyButton = wrapper.findAll('button').find((btn) => btn.text().includes('复制')); + await copyButton?.trigger('click'); + expect(wrapper.emitted('change')?.[0][0]).toEqual([ + { text: 'a', order: 3 }, + { text: 'b', order: 1 }, + { text: 'a', order: 0 }, + ]); + }); }); diff --git a/packages/form/tests/unit/containers/GroupList.spec.ts b/packages/form/tests/unit/containers/GroupList.spec.ts index f17e2ce3..cc074e54 100644 --- a/packages/form/tests/unit/containers/GroupList.spec.ts +++ b/packages/form/tests/unit/containers/GroupList.spec.ts @@ -661,6 +661,85 @@ describe('GroupList container', () => { expect(wrapper.find('.m-fields-group-list.is-header-sticky').exists()).toBe(false); }); + test('行操作按钮用 text 改名称,形态与 addButtonConfig 相同', async () => { + const wrapper = mountForm( + [ + { + type: 'group-list', + name: 'list', + copyable: true, + movable: true, + moveSpecifyLocation: true, + titleKey: 'text', + titlePrefix: '条目', + copyButtonConfig: { text: '克隆', props: { class: 'custom-copy', type: 'success' } }, + moveUpButtonConfig: { text: '前移', props: { class: 'custom-up' } }, + moveDownButtonConfig: { text: '后移', props: { class: 'custom-down' } }, + moveToButtonConfig: { text: '挪到', props: { class: 'custom-move' } }, + deleteButtonConfig: { text: '移除', props: { class: 'custom-remove' } }, + items: [{ name: 'text', type: 'text', text: 'text' }], + }, + ], + { list: [{ text: 'a' }, { text: 'b' }] }, + ); + await nextTick(); + + expect(wrapper.text()).toContain('克隆'); + expect(wrapper.text()).toContain('前移'); + expect(wrapper.text()).toContain('后移'); + expect(wrapper.text()).toContain('挪到'); + expect(wrapper.text()).not.toContain('复制'); + expect(wrapper.find('.custom-copy').exists()).toBe(true); + expect(wrapper.find('.custom-up').exists()).toBe(true); + expect(wrapper.find('.custom-down').exists()).toBe(true); + expect(wrapper.find('.custom-move').exists()).toBe(true); + expect(wrapper.find('.custom-remove').exists()).toBe(true); + expect( + wrapper + .findAllComponents({ name: 'TMButton' }) + .find((btn) => btn.text().includes('克隆')) + ?.props('type'), + ).toBe('success'); + expect(wrapper.findComponent({ name: 'TMTooltip' }).props('content')).toBe('移除 条目 1: a'); + + await wrapper.find('.expand-button').trigger('click'); + await wrapper + .findAll('button') + .find((btn) => btn.text().includes('挪到')) + ?.trigger('click'); + await wrapper + .findAll('button') + .find((btn) => btn.text().includes('取消')) + ?.trigger('click'); + await wrapper + .findAll('button') + .find((btn) => btn.text().includes('确认')) + ?.trigger('click'); + await nextTick(); + expect((wrapper.vm as any).values.list.map((item: { text: string }) => item.text)).toEqual(['a', 'b']); + }); + + test('delete 函数可隐藏单行删除,title 函数作为卡片标题', async () => { + const deleteFn = vi.fn(() => false); + const wrapper = mountForm( + [ + { + type: 'group-list', + name: 'list', + delete: deleteFn, + title: () => '自定义标题', + items: [{ name: 'text', type: 'text', text: 'text' }], + }, + ], + { list: [{ text: 'a' }] }, + ); + await nextTick(); + + expect(deleteFn).toHaveBeenCalled(); + expect((wrapper.find('.delete-button').element as HTMLElement).style.display).toBe('none'); + expect(wrapper.text()).toContain('自定义标题'); + }); + test('嵌套列表各自按 header.sticky 决定是否吸顶', async () => { const wrapper = mountForm( [ diff --git a/packages/form/tests/unit/containers/useTableColumns.spec.ts b/packages/form/tests/unit/containers/useTableColumns.spec.ts new file mode 100644 index 00000000..43debb63 --- /dev/null +++ b/packages/form/tests/unit/containers/useTableColumns.spec.ts @@ -0,0 +1,218 @@ +/* + * Tencent is pleased to support the open source community by making TMagicEditor available. + * + * Copyright (C) 2025 Tencent. + */ +import { describe, expect, test, vi } from 'vitest'; +import { defineComponent, h, nextTick, reactive } from 'vue'; +import { mount } from '@vue/test-utils'; + +import { useTableColumns } from '@form/containers/table/useTableColumns'; +import { setConfig } from '@form/utils/config'; + +const Harness = defineComponent({ + name: 'ColumnsHarness', + props: { + tableProps: { type: Object, required: true }, + }, + setup(props) { + const events: any[] = []; + const { columns } = useTableColumns( + props.tableProps as any, + (...args: any[]) => { + events.push(args); + }, + props.tableProps.currentPage, + props.tableProps.pageSize, + props.tableProps.modelName, + ); + return { columns, events }; + }, + render() { + return h('div'); + }, +}); + +const createState = (overrides: Record = {}) => { + const currentPage = overrides.currentPage ?? { value: 0 }; + const pageSize = overrides.pageSize ?? { value: 10 }; + const modelName = overrides.modelName ?? { value: 'list' }; + return reactive({ + model: { list: [{ text: 'a' }, { text: 'b' }] }, + lastValues: { list: [{ text: 'old-a' }, { text: 'old-b' }] }, + name: 'list', + prop: 'list', + showIndex: false, + sort: false, + config: { + type: 'table', + name: 'list', + items: [{ name: 'text', label: '文本', type: 'text' }], + }, + currentPage, + pageSize, + modelName, + ...overrides, + config: { + type: 'table', + name: 'list', + items: [{ name: 'text', label: '文本', type: 'text' }], + ...(overrides.config || {}), + }, + }); +}; + +const mountColumns = (state: ReturnType, mForm?: any) => + mount(Harness, { + props: { tableProps: state }, + global: mForm ? { provide: { mForm } } : undefined, + }); + +const labels = (wrapper: ReturnType) => + (wrapper.vm as any).columns.map((column: any) => column.props.label ?? column.props.type); + +describe('useTableColumns', () => { + test('操作列标题默认为「操作」,operateColConfig.text 可替换', async () => { + setConfig({}); + const defaults = mountColumns(createState()); + await nextTick(); + expect(labels(defaults)).toContain('操作'); + + const custom = mountColumns( + createState({ + config: { + operateColWidth: 112, + fixed: 'left', + operateColConfig: { text: '动作', props: { width: 200, align: 'left', minWidth: 80, fixed: 'right' } }, + }, + }), + ); + await nextTick(); + const operateCol = (custom.vm as any).columns.find((column: any) => column.props.label === '动作'); + expect(operateCol).toBeTruthy(); + expect(labels(custom)).not.toContain('操作'); + expect(operateCol.props).toMatchObject({ width: 200, align: 'left', minWidth: 80, fixed: 'right' }); + }); + + test('展开列、选择列、排序列、序号列与数据列按配置拼出', async () => { + setConfig({ flat: true }); + const itemExtra = vi.fn(() => 'extra'); + const titleTip = vi.fn(() => '提示'); + const selection = vi.fn(() => true); + const mForm = { initValues: { list: [] }, values: { list: [{ text: 'form' }] }, $emit: vi.fn() }; + const state = createState({ + sort: true, + showIndex: true, + config: { + itemExtra, + selection, + showIndex: true, + flat: undefined, + fixed: 'right', + pagination: true, + items: [ + { name: 'hidden', type: 'hidden' }, + { name: 'skip', label: '跳过', display: false }, + { name: 'text', label: '文本', titleTip, itemsFunction: (row: any) => [{ name: 'inner', text: row.text }] }, + ], + }, + }); + const wrapper = mountColumns(state, mForm); + await nextTick(); + + const { columns } = wrapper.vm as any; + expect(labels(wrapper)).toEqual(['expand', 'selection', '排序', '序号', '文本', '操作']); + + const extraVnode = columns[0].cell({ $index: 1 }); + expect(itemExtra).toHaveBeenCalled(); + expect(extraVnode.props.innerHTML).toBe('extra'); + expect(extraVnode.props.class).toBe('m-form-tip'); + + const sortVnode = columns[2].cell({ $index: 1 }); + sortVnode.props.onSwap(1, 0); + expect(mForm.$emit).toHaveBeenCalledWith('field-change', expect.any(Array)); + expect((wrapper.vm as any).events.at(-1)[0]).toBe('change'); + + expect(String(columns[3].cell({ $index: 1 }).children)).toBe('2'); + + const dataVnode = columns[4].cell({ row: { text: 'a' }, $index: 0 }); + expect(dataVnode.props.config.items).toEqual([{ name: 'inner', text: 'a' }]); + expect(dataVnode.props.lastValues).toEqual({ text: 'old-a' }); + dataVnode.props.onChange({ text: 'next' }, { changeRecords: [] }); + dataVnode.props.onAddDiffCount(); + expect((wrapper.vm as any).events.map((event: any[]) => event[0])).toEqual( + expect.arrayContaining(['change', 'addDiffCount']), + ); + + const titleVnode = columns[4].title(); + expect(titleTip).toHaveBeenCalled(); + expect(titleVnode).toBeTruthy(); + + const actionVnode = columns[5].cell({ row: { text: 'a' }, $index: 0 }); + expect(actionVnode.props.flat).toBe(true); + actionVnode.props.onChange([{ text: 'only' }]); + expect((wrapper.vm as any).events.at(-1)).toEqual(['change', [{ text: 'only' }]]); + + const dropped = mountColumns( + createState({ + config: { + fixed: false, + dropSort: true, + itemExtra: 'plain', + selection: false, + pagination: false, + items: [{ name: 'text', label: '文本', titleTip: '静态提示' }], + }, + }), + mForm, + ); + await nextTick(); + + const nextColumns = (dropped.vm as any).columns; + expect(nextColumns[0].props.label).toBe('操作'); + expect(nextColumns[0].props.fixed).toBeUndefined(); + expect(nextColumns.some((column: any) => column.props.type === 'expand')).toBe(false); + const plain = nextColumns.find((column: any) => column.props.prop === 'text'); + expect(plain.props.class).toBe('el-table__column--dropable'); + expect(plain.title()).toBeTruthy(); + expect(plain.cell({ row: { text: 'a' }, $index: 0 }).props.lastValues).toEqual({ text: 'old-a' }); + + setConfig({}); + }); + + test('选择函数返回 false、非法 fixed、全局非 flat 时不铺平操作列', async () => { + setConfig({}); + const state = createState({ + sort: true, + model: { list: [{ text: 'only' }] }, + lastValues: {}, + config: { + selection: () => false, + fixed: 'center', + items: [{ name: 'text', label: '文本', display: () => false }], + }, + }); + const wrapper = mountColumns(state); + await nextTick(); + + const { columns } = wrapper.vm as any; + expect(columns.map((column: any) => column.props.label ?? column.props.type)).toEqual(['操作']); + expect(columns[0].props.fixed).toBe('left'); + const actionVnode = columns[0].cell({ row: { text: 'only' }, $index: 0 }); + expect(actionVnode.props.flat).toBeUndefined(); + + const plain = mountColumns( + createState({ + lastValues: {}, + config: { + fixed: 'left', + items: [{ name: 'text', label: '文本' }], + }, + }), + ); + await nextTick(); + const dataColumn = (plain.vm as any).columns.find((column: any) => column.props.prop === 'text'); + expect(dataColumn.cell({ row: { text: 'only' }, $index: 0 }).props.lastValues).toBeUndefined(); + expect(dataColumn.title).toBeUndefined(); + }); +}); diff --git a/packages/form/tests/unit/utils/actionButton.spec.ts b/packages/form/tests/unit/utils/actionButton.spec.ts new file mode 100644 index 00000000..8e569d6c --- /dev/null +++ b/packages/form/tests/unit/utils/actionButton.spec.ts @@ -0,0 +1,21 @@ +/* + * Tencent is pleased to support the open source community by making TMagicEditor available. + * + * Copyright (C) 2025 Tencent. + */ +import { describe, expect, test } from 'vitest'; + +import { actionButtonText, DEFAULT_ACTION_BUTTON_TEXT } from '@form/utils/actionButton'; + +describe('actionButtonText', () => { + test('未配置时返回内置文案', () => { + expect(actionButtonText(undefined, 'operate')).toBe(DEFAULT_ACTION_BUTTON_TEXT.operate); + expect(actionButtonText({}, 'delete')).toBe('删除'); + }); + + test('text 覆盖内置文案,空字符串回退', () => { + expect(actionButtonText({ text: '克隆' }, 'copy')).toBe('克隆'); + expect(actionButtonText({ text: '' }, 'moveUp')).toBe('上移'); + expect(actionButtonText({ text: '前移' }, 'moveDown')).toBe('前移'); + }); +});