feat(form): 支持 table/group-list 行操作按钮文案与 props 配置

通过 ActionButtonConfig 统一 delete/copy/move/operate 等按钮的 text 与 props,并补充对应单测。
This commit is contained in:
roymondchen 2026-10-19 17:08:02 +08:00
parent 8ac1a38601
commit cbd0442ffc
9 changed files with 505 additions and 17 deletions

View File

@ -832,6 +832,14 @@ export interface AddButtonConfig {
}
// #endregion AddButtonConfig
// #region ActionButtonConfig
/**
* 行操作按钮。与 `addButtonConfig` 相同:`text` 是文案,`props` 透传给按钮。
* 不支持吸底。未配置 `text` 时使用内置中文。
*/
export type ActionButtonConfig = Omit<AddButtonConfig, 'sticky'>;
// #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<T = never> 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<T = never> extends TableGroupListCommonConfig {
dropActionButtonIcon?: any;
copyActionButtonIcon?: any;
deleteActionButtonIcon?: any;
/** 拖动排序按钮,text 作为提示,默认「拖动排序」 */
dropSortButtonConfig?: ActionButtonConfig;
/** 是否显示全屏按钮 */
enableFullscreen?: boolean;
fixed?: boolean | 'left' | 'right';

View File

@ -19,8 +19,9 @@
type="primary"
:icon="DocumentCopy"
:disabled="disabled"
v-bind="config.copyButtonConfig?.props"
@click="copyHandler"
>复制</TMagicButton
>{{ actionButtonText(config.copyButtonConfig, 'copy') }}</TMagicButton
>
<template v-if="movable && !isCompare">
@ -30,8 +31,9 @@
size="default"
:disabled="disabled"
:icon="Top"
v-bind="config.moveUpButtonConfig?.props"
@click="changeOrder(-1)"
>上移</TMagicButton
>{{ actionButtonText(config.moveUpButtonConfig, 'moveUp') }}</TMagicButton
>
<TMagicButton
v-show="index !== length - 1"
@ -39,8 +41,9 @@
size="default"
:disabled="disabled"
:icon="Bottom"
v-bind="config.moveDownButtonConfig?.props"
@click="changeOrder(1)"
>下移</TMagicButton
>{{ actionButtonText(config.moveDownButtonConfig, 'moveDown') }}</TMagicButton
>
</template>
@ -58,8 +61,9 @@
type="primary"
:icon="Position"
:disabled="disabled"
v-bind="config.moveToButtonConfig?.props"
@click="moveSpecifyLocationVisible = true"
>移动至</TMagicButton
>{{ actionButtonText(config.moveToButtonConfig, 'moveTo') }}</TMagicButton
>
</template>
<div>
@ -81,7 +85,10 @@
</TMagicPopover>
<!-- v-if 必须挂在 Tooltip 上:子节点为 false 时 ElOnlyChild 会报 no valid child node found -->
<TMagicTooltip v-if="!isCompare" :content="`删除 ${title}`">
<TMagicTooltip
v-if="!isCompare"
:content="`${actionButtonText(config.deleteButtonConfig, 'delete')} ${title}`"
>
<TMagicButton
v-show="showDelete"
size="default"
@ -89,6 +96,7 @@
class="delete-button"
:icon="Delete"
:disabled="disabled"
v-bind="config.deleteButtonConfig?.props"
@click="removeHandler"
></TMagicButton>
</TMagicTooltip>
@ -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';

View File

@ -1,11 +1,16 @@
<template>
<slot name="operateCol" :scope="{ $index: index, row: row }"></slot>
<TMagicTooltip v-if="config.dropSort && config.dropSortHandle" content="拖动排序" placement="left-start">
<TMagicTooltip
v-if="config.dropSort && config.dropSortHandle"
:content="actionButtonText(config.dropSortButtonConfig, 'dropSort')"
placement="left-start"
>
<TMagicButton
size="small"
link
:class="{ 'tmagic-form-table-drag-target': config.dropSortHandle }"
:icon="config.dropActionButtonIcon || Sort"
v-bind="config.dropSortButtonConfig?.props"
>
</TMagicButton>
</TMagicTooltip>
@ -15,11 +20,12 @@
size="small"
type="danger"
link
title="删除"
:title="actionButtonText(config.deleteButtonConfig, 'delete')"
:icon="flat ? undefined : config.deleteActionButtonIcon || Delete"
v-bind="config.deleteButtonConfig?.props"
@click="removeHandler(index + 1 + currentPage * pageSize - 1)"
>
<template v-if="flat">删除</template>
<template v-if="flat">{{ actionButtonText(config.deleteButtonConfig, 'delete') }}</template>
</TMagicButton>
<TMagicButton
@ -27,12 +33,13 @@
link
size="small"
type="primary"
title="复制"
:title="actionButtonText(config.copyButtonConfig, 'copy')"
:icon="flat ? undefined : config.copyActionButtonIcon || DocumentCopy"
:disabled="disabled"
v-bind="config.copyButtonConfig?.props"
@click="copyHandler(index + 1 + currentPage * pageSize - 1)"
>
<template v-if="flat">复制</template>
<template v-if="flat">{{ actionButtonText(config.copyButtonConfig, 'copy') }}</template>
</TMagicButton>
</template>
@ -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']);

View File

@ -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, {

View File

@ -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];

View File

@ -29,14 +29,25 @@ const createMForm = (context: Record<string, any>): FormState =>
() => context,
);
const mountColumn = (config: any, context: Record<string, any> = { env: 'prod' }) =>
mount(ActionsColumn as any, {
const COMPONENT_PROP_KEYS = ['flat', 'disabled', 'sortKey', 'model'] as const;
const mountColumn = (config: any, context: Record<string, any> = { env: 'prod' }) => {
const tableConfig = { ...config };
const extra: Record<string, any> = {};
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<string, any> = { 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 },
]);
});
});

View File

@ -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(
[

View File

@ -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<string, any> = {}) => {
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<typeof createState>, mForm?: any) =>
mount(Harness, {
props: { tableProps: state },
global: mForm ? { provide: { mForm } } : undefined,
});
const labels = (wrapper: ReturnType<typeof mountColumns>) =>
(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(() => '<em>extra</em>');
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('<em>extra</em>');
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();
});
});

View File

@ -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('前移');
});
});