diff --git a/packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts b/packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts new file mode 100644 index 0000000000..993e89ef0d --- /dev/null +++ b/packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts @@ -0,0 +1,297 @@ +import { get, pick } from 'lodash'; +import { type SpreadSheet, type S2Options, OriginEventType } from '../../src'; +import { createFederatedMouseEvent, createPivotSheet } from '../util/helpers'; + +const s2Options: S2Options = { + width: 600, + height: 400, +}; + +describe('HeaderActionIcons Tests', () => { + let s2: SpreadSheet; + + beforeEach(async () => { + s2 = createPivotSheet(s2Options); + await s2.render(); + }); + + afterEach(() => { + s2.destroy(); + }); + + test('should render custom header action icons', async () => { + const rowCellDisplayConditionFn = jest.fn(); + const colCellDisplayConditionFn = jest.fn(); + const cornerCellDisplayConditionFn = jest.fn(); + + s2.setOptions({ + headerActionIcons: [ + { + icons: ['Plus'], + belongsCell: 'rowCell', + displayCondition: rowCellDisplayConditionFn, + }, + { + icons: ['Trend'], + belongsCell: 'colCell', + displayCondition: colCellDisplayConditionFn, + }, + { + icons: ['Minus'], + belongsCell: 'cornerCell', + displayCondition: cornerCellDisplayConditionFn, + }, + ], + }); + + await s2.render(false); + + [ + ...s2.facet.getRowCells(), + ...s2.facet.getColCells(), + ...s2.facet.getCornerCells(), + ].forEach((cell) => { + expect(cell.getActionIcons()).toHaveLength(1); + }); + + expect(rowCellDisplayConditionFn).toHaveBeenCalled(); + expect(colCellDisplayConditionFn).toHaveBeenCalled(); + expect(cornerCellDisplayConditionFn).toHaveBeenCalled(); + }); + + test('should custom icon fill color', async () => { + s2.setOptions({ + headerActionIcons: [ + { + icons: [ + { + name: 'Plus', + position: 'right', + fill: 'red', + }, + ], + belongsCell: 'rowCell', + }, + ], + }); + + await s2.render(false); + + s2.facet.getRowCells().forEach((cell) => { + expect(get(cell.getActionIcons()[0], 'cfg.fill')).toEqual('red'); + }); + }); + + test('should default hide icon', async () => { + const innerDefaultHide = jest.fn(() => true); + const defaultHide = jest.fn(() => false); + + s2.setOptions({ + headerActionIcons: [ + { + icons: [ + { + name: 'Plus', + position: 'right', + defaultHide: innerDefaultHide, + }, + ], + belongsCell: 'rowCell', + defaultHide, + }, + ], + }); + + await s2.render(false); + + s2.facet.getRowCells().forEach((cell) => { + expect(cell.getActionIcons()[0].parsedStyle.visibility).toEqual('hidden'); + }); + + expect(innerDefaultHide).toHaveBeenCalled(); + expect(defaultHide).not.toHaveBeenCalled(); + }); + + test('should not render icons by displayCondition', async () => { + const innerDisplayCondition = jest.fn(() => false); + const displayCondition = jest.fn(() => true); + + s2.setOptions({ + headerActionIcons: [ + { + icons: [ + { + name: 'Plus', + position: 'right', + displayCondition: innerDisplayCondition, + }, + ], + belongsCell: 'rowCell', + displayCondition, + }, + ], + }); + + await s2.render(false); + + s2.facet.getRowCells().forEach((cell) => { + expect(cell.getActionIcons()).toBeEmpty(); + }); + + expect(innerDisplayCondition).toHaveBeenCalled(); + expect(displayCondition).not.toHaveBeenCalled(); + }); + + test('should render multiple custom position', async () => { + s2.setOptions({ + headerActionIcons: [ + { + icons: [ + { + name: 'Plus', + position: 'right', + }, + { + name: 'Trend', + position: 'left', + }, + ], + belongsCell: 'rowCell', + }, + ], + }); + + await s2.render(false); + + const positions = s2.facet.getRowCells().map((cell) => { + return cell + .getActionIcons() + .map((icon) => pick(icon.iconImageShape.attributes, ['x', 'y'])); + }); + + expect(positions).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "x": 9, + "y": 24.5, + }, + Object { + "x": 51, + "y": 24.5, + }, + ], + Array [ + Object { + "x": 158, + "y": 9.5, + }, + Object { + "x": 200, + "y": 9.5, + }, + ], + Array [ + Object { + "x": 158, + "y": 39.5, + }, + Object { + "x": 200, + "y": 39.5, + }, + ], + ] + `); + }); + + test('should not render icons by displayCondition1', async () => { + const onPlusClick = jest.fn(); + const onPlusHover = jest.fn(); + const onTrendClick = jest.fn(); + const onTrendHover = jest.fn(); + + const onClick = jest.fn(); + const onHover = jest.fn(); + + s2.setOptions({ + headerActionIcons: [ + { + icons: [ + { + name: 'Plus', + position: 'right', + onClick: onPlusClick, + onHover: onPlusHover, + }, + { + name: 'Trend', + position: 'right', + onClick: onTrendClick, + onHover: onTrendHover, + }, + ], + belongsCell: 'rowCell', + onClick, + onHover, + }, + ], + }); + + await s2.render(false); + + const events = [onTrendClick, onTrendHover, onClick, onHover]; + + const plusIcon = s2.facet.getRowCells()[0].getActionIcons()[0]; + + plusIcon.dispatchEvent( + createFederatedMouseEvent(s2, OriginEventType.CLICK), + ); + + events.forEach((fn) => { + expect(fn).not.toHaveBeenCalled(); + }); + + expect(onPlusClick).toHaveBeenCalledTimes(1); + }); + + test('should render default right position', async () => { + s2.setOptions({ + headerActionIcons: [ + { + icons: ['Trend'], + belongsCell: 'rowCell', + }, + ], + }); + + await s2.render(false); + + const positions = s2.facet + .getRowCells() + .map((cell) => pick(cell.getActionIcons()[0], ['cfg.x', 'cfg.y'])); + + expect(positions).toMatchInlineSnapshot(` + Array [ + Object { + "cfg": Object { + "x": 37, + "y": 24.5, + }, + }, + Object { + "cfg": Object { + "x": 186, + "y": 9.5, + }, + }, + Object { + "cfg": Object { + "x": 186, + "y": 39.5, + }, + }, + ] + `); + }); +}); diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts index 6452f1ffea..333bf2940c 100644 --- a/packages/s2-core/src/cell/base-cell.ts +++ b/packages/s2-core/src/cell/base-cell.ts @@ -41,7 +41,7 @@ import { CellClipBox, CellBorderPosition, type InteractionStateTheme, - type FullyIconName, + type HeaderActionNameOptions, type IconPosition, type InternalFullyTheme, type InternalFullyCellTheme, @@ -607,7 +607,7 @@ export abstract class BaseCell extends Group { }; } - public getIconConditionResult(): FullyIconName | undefined { + public getIconConditionResult(): HeaderActionNameOptions | undefined { const iconCondition = this.findFieldCondition( this.conditions?.icon, ) as IconCondition; diff --git a/packages/s2-core/src/cell/data-cell.ts b/packages/s2-core/src/cell/data-cell.ts index a07ef22650..eac193a9db 100644 --- a/packages/s2-core/src/cell/data-cell.ts +++ b/packages/s2-core/src/cell/data-cell.ts @@ -9,7 +9,7 @@ import { import { CellBorderPosition, CellClipBox, - type FullyIconName, + type HeaderActionNameOptions, type IconCondition, type InteractionStateTheme, } from '../common/interface'; @@ -241,7 +241,7 @@ export class DataCell extends BaseCell { // 此时 name 是什么值都无所谓,因为后面会根据 mappingResult 来决定 name: '', position: getIconPosition(iconCondition), - } as FullyIconName); + } as HeaderActionNameOptions); this.groupedIcons = groupIconsByPosition([], iconCfg); } diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts index 496cabec00..e90fbd2658 100644 --- a/packages/s2-core/src/cell/header-cell.ts +++ b/packages/s2-core/src/cell/header-cell.ts @@ -1,6 +1,6 @@ import type { - FederatedPointerEvent as CanvasEvent, DisplayObject, + FederatedPointerEvent as CanvasEvent, PointLike, } from '@antv/g'; import { @@ -15,7 +15,6 @@ import { map, merge, } from 'lodash'; -import { renderIcon } from '../utils/g-renders'; import { BaseCell } from '../cell/base-cell'; import { CellType, @@ -25,14 +24,15 @@ import { } from '../common/constant'; import { InteractionStateName } from '../common/constant/interaction'; import { GuiIcon } from '../common/icons'; +import type { GuiIconCfg } from '../common/icons/gui-icon'; import type { CellMeta, Condition, + ConditionMappingResult, FormatResult, - FullyIconName, HeaderActionIconOptions, + HeaderActionNameOptions, InternalFullyHeaderActionIcon, - ConditionMappingResult, TextTheme, } from '../common/interface'; import type { BaseHeaderConfig } from '../facet/header'; @@ -42,6 +42,7 @@ import { getActionIconConfig, groupIconsByPosition, } from '../utils/cell/header-cell'; +import { renderIcon } from '../utils/g-renders'; import { getSortTypeIcon } from '../utils/sort-action'; export abstract class HeaderCell extends BaseCell { @@ -57,7 +58,7 @@ export abstract class HeaderCell extends BaseCell { protected hasDefaultHiddenIcon: boolean; - protected conditionIconMappingResult: FullyIconName | undefined; + protected conditionIconMappingResult: HeaderActionNameOptions | undefined; /** left icon 绘制起始坐标 */ protected leftIconPosition: PointLike; @@ -157,11 +158,9 @@ export abstract class HeaderCell extends BaseCell { return false; } - protected getActionIconsCount() { - return this.groupedIcons.left.length + this.groupedIcons.right.length; - } - - protected getActionIconStyle() { + protected getActionIconStyle( + options: Partial, + ): Partial { const { icon } = this.getStyle()!; const conditionStyle = this.getTextConditionMappingResult(); const defaultTextFill = conditionStyle?.fill || this.getTextStyle().fill!; @@ -169,8 +168,9 @@ export abstract class HeaderCell extends BaseCell { return { width: icon?.size, height: icon?.size, - // 主题 icon 颜色配置优先,若无则默认为文本条件格式颜色优先 - fill: icon?.fill || defaultTextFill, + // 优先级: 单个 icon 颜色配置 > 全部 icon 颜色配置 > 主题 icon 颜色配置 > 文本默认颜色 + fill: options?.fill || icon?.fill || defaultTextFill, + cursor: 'pointer', }; } @@ -180,14 +180,13 @@ export abstract class HeaderCell extends BaseCell { } protected addActionIcon(options: HeaderActionIconOptions) { - const { x, y, iconName, defaultHide, onClick, onHover, isSortIcon } = - options; + const { x, y, name, defaultHide, onClick, onHover, isSortIcon } = options; const icon = new GuiIcon({ - name: iconName, + ...this.getActionIconStyle(options), + name, x, y, - ...this.getActionIconStyle(), }); // 默认隐藏,hover 可见 @@ -197,7 +196,7 @@ export abstract class HeaderCell extends BaseCell { this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_HOVER, event); onHover?.({ hovering: true, - iconName, + name, meta: this.meta, event, }); @@ -207,27 +206,27 @@ export abstract class HeaderCell extends BaseCell { this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_HOVER_OFF, event); onHover?.({ hovering: false, - iconName, + name, meta: this.meta, event, }); }); - if (isSortIcon) { - icon.addEventListener('click', (event: CanvasEvent) => { - this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_CLICK, event); + icon.addEventListener('click', (event: CanvasEvent) => { + this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_CLICK, event); + + if (isSortIcon) { this.spreadsheet.handleGroupSort(event, this.meta); + + return; + } + + onClick?.({ + name, + meta: this.meta, + event, }); - } else { - icon.addEventListener('click', (event: CanvasEvent) => { - this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_CLICK, event); - onClick?.({ - iconName, - meta: this.meta, - event, - }); - }); - } + }); this.actionIcons.push(icon); this.appendChild(icon); @@ -257,10 +256,10 @@ export abstract class HeaderCell extends BaseCell { this.conditionIconShape = renderIcon(this, { x, y, - name: icon?.name!, + name: icon.name, width: size, height: size, - fill: icon?.fill, + fill: icon.fill, }); this.addConditionIconShape(this.conditionIconShape); @@ -270,22 +269,24 @@ export abstract class HeaderCell extends BaseCell { const { onClick, onHover, defaultHide, isSortIcon } = this.actionIconConfig!; + const defaultHideHandler = icon.defaultHide ?? defaultHide; const iconDefaultHide = - typeof defaultHide === 'function' - ? defaultHide(this.meta, icon.name) - : defaultHide; + typeof defaultHideHandler === 'function' + ? defaultHideHandler(this.meta, icon.name) + : defaultHideHandler; if (iconDefaultHide) { this.hasDefaultHiddenIcon = true; } this.addActionIcon({ - iconName: icon.name, + name: icon.name, + fill: icon.fill, x, y, defaultHide: iconDefaultHide, - onClick, - onHover, + onClick: icon.onClick ?? onClick, + onHover: icon.onHover ?? onHover, isSortIcon, }); }); @@ -451,4 +452,8 @@ export abstract class HeaderCell extends BaseCell { public getTreeIcon() { return this.treeIcon; } + + public getActionIcons() { + return this.actionIcons; + } } diff --git a/packages/s2-core/src/common/interface/basic.ts b/packages/s2-core/src/common/interface/basic.ts index 4ddf65caec..2618198ea4 100644 --- a/packages/s2-core/src/common/interface/basic.ts +++ b/packages/s2-core/src/common/interface/basic.ts @@ -1,5 +1,5 @@ import type { FederatedPointerEvent as Event, Group } from '@antv/g'; -import type { CellType } from '../../common/constant'; +import type { MergedCell } from '../../cell'; import type { CustomTreeNode, Data, @@ -13,13 +13,13 @@ import type { CellData } from '../../data-set/cell-data'; import type { BaseHeaderConfig, Frame } from '../../facet/header'; import type { Node } from '../../facet/layout/node'; import type { SpreadSheet } from '../../sheet-type'; -import type { MergedCell } from '../../cell'; +import type { CellType } from '../constant'; import type { S2CellType } from './interaction'; import type { DataItem } from './s2DataConfig'; export type { GetCellMeta, LayoutResult } from './facet'; -/* +/** * 第二个参数在以下情况会传入: * 1. data cell 格式化 * 2. copy/export @@ -228,19 +228,29 @@ export interface Pagination { } export interface CustomSVGIcon { - /** icon 名称 */ + /** + * icon 名称 + */ name: string; /** - * 1、base 64 - * 2、svg本地文件(兼容老方式,可以改颜色) - * 3、线上支持的图片地址 + * @example 1、base64 + * @example 2. svg本地文件 (兼容老方式, 可以改颜色) + + import Icon from 'path/to/xxx.svg' + + => { name: 'iconA', svg: Icon } + => { name: 'iconB', svg: '...' } + + * @example 3. 线上支持的图片地址 + 带后缀: https://gw.alipayobjects.com/zos/antfincdn/gu1Fsz3fw0/filter%26sort_filter.svg + 无后缀: https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*5nsESLuvc_EAAAAAAAAAAAAADmJ7AQ/original */ svg: string; } export interface HeaderIconClickParams { - iconName: string; + name: string; meta: Node; event?: Event; } @@ -251,44 +261,86 @@ export interface HeaderIconHoverParams extends HeaderIconClickParams { hovering: boolean; } -export interface HeaderActionIconOptions { - iconName: string; +export interface HeaderActionIconOptions extends HeaderActionIconBaseOptions { + fill?: string; + name: string; x: number; y: number; - onClick?: (headerIconClickParams: HeaderIconClickParams) => void; - onHover?: (headerIconHoverParams: HeaderIconHoverParams) => void; isSortIcon?: boolean; - defaultHide?: boolean; } -export type FullyIconName = { - name: string; - position: IconPosition; +export type HeaderActionNameOptions = HeaderActionIconBaseOptions & { + /** + * icon 颜色配置 + * @description 优先级: 单个 icon > 主题 icon 配置 > 文本颜色 + */ fill?: string; + + /** + * icon 名称 + */ + name: string; + + /** + * icon 相对文本的位置 + * 可选: 'left' | 'right' + */ + position?: IconPosition; + + /** + * 是否是条件格式的 icon + */ isConditionIcon?: boolean; }; -export type ActionIconName = string | FullyIconName; -export interface HeaderActionIcon { +export type HeaderActionName = + | string + | Omit; + +export interface HeaderActionIconBaseOptions { /** - * 已注册的 icon 类型或自定义的 icon 类型名 - * 如果是 string[], 则默认 icon 位置为右侧 + * 是否默认隐藏, 开启后 hover 后才显示, 关闭后则始终显示, 可根据当前单元格信息动态判断 + * @example defaultHide: (meta, iconName) => meta.id === 'xxx' + * @default false */ - icons: ActionIconName[]; - // 所属的 cell 类型 - belongsCell: Omit; - /** 是否默认隐藏, true 为 hover后显示, false 为一直显示 */ defaultHide?: boolean | ((meta: Node, iconName: string) => boolean); - /** 是否展示当前 iconNames 配置的 icon */ + + /** + * 是否展示, 可根据当前单元格信息动态判断 + * @example displayCondition: (meta, iconName) => !meta.isTotals + */ displayCondition?: (mete: Node, iconName: string) => boolean; - /** 点击回调函数 */ + + /** + * 点击回调函数 + */ onClick?: (headerIconClickParams: HeaderIconClickParams) => void; - /** 悬停回调函数 */ + + /** + * 悬停回调函数 + */ onHover?: (headerIconHoverParams: HeaderIconHoverParams) => void; } +export interface HeaderActionIcon extends HeaderActionIconBaseOptions { + /** + * 内置 icon 或通过 @customSVGIcons 自定义的 icon 名称 + * 如果是 string[], 则默认 icon 位置为右侧 + * @see https://s2.antv.antgroup.com/manual/advanced/custom/custom-icon + * @example icons: ['iconNameA', 'iconNameB'] + * @example icons: [{ name: 'iconNameA', position: "left", fill: "red" }] + */ + icons: HeaderActionName[]; + + /** + * 所属的 cell 类型, 即当前 icon 展示在哪种类型单元格中 + * @example belongsCell: 'rowCell' + */ + belongsCell: Omit; +} + export interface InternalFullyHeaderActionIcon extends HeaderActionIcon { - icons: FullyIconName[]; + icons: HeaderActionNameOptions[]; isSortIcon?: boolean; } diff --git a/packages/s2-core/src/common/interface/theme.ts b/packages/s2-core/src/common/interface/theme.ts index bea0b7994f..5a39ef5db0 100644 --- a/packages/s2-core/src/common/interface/theme.ts +++ b/packages/s2-core/src/common/interface/theme.ts @@ -1,7 +1,7 @@ import type { LineStyleProps } from '@antv/g'; +import type { CellType } from '../../common/constant/interaction'; import type { InteractionStateName } from '../constant'; import type { PALETTE_MAP } from '../constant/theme'; -import type { CellType } from '../../common/constant/interaction'; import type { DeepRequired } from './util'; // 文本内容的水平对齐方式, 默认 left diff --git a/packages/s2-core/src/utils/cell/header-cell.ts b/packages/s2-core/src/utils/cell/header-cell.ts index 9e7758d1c6..f3968efb4b 100644 --- a/packages/s2-core/src/utils/cell/header-cell.ts +++ b/packages/s2-core/src/utils/cell/header-cell.ts @@ -1,20 +1,20 @@ import { find, groupBy, isEmpty, isEqual, merge } from 'lodash'; import type { - FullyIconName, + HeaderActionNameOptions, IconPosition, IconTheme, InternalFullyHeaderActionIcon, } from '../../common/interface'; import { CellType, EXTRA_FIELD } from '../../common/constant'; import type { - ActionIconName, + HeaderActionName, FormatResult, HeaderActionIcon, } from '../../common/interface/basic'; import type { Node } from '../../facet/layout/node'; const normalizeIcons = ( - icons: ActionIconName[], + icons: HeaderActionName[], position: IconPosition = 'right', ) => icons.map((icon) => { @@ -53,13 +53,13 @@ const shouldShowActionIcons = ( return false; } - if (!displayCondition) { - // 没有展示条件参数默认全展示 - return true; - } - // 有任意 iconName 命中展示,则使用当前 headerActionIcon config - return icons.some((icon) => displayCondition(meta, icon.name)); + return icons.some( + (icon) => + icon.displayCondition?.(meta, icon.name) ?? + displayCondition?.(meta, icon.name) ?? + true, + ); }; /** @@ -85,22 +85,21 @@ export const getActionIconConfig = ( } // 使用配置的 displayCondition 进一步筛选需要展示的 icon - let nextIcons = iconConfig.icons; - - if (iconConfig.displayCondition) { - nextIcons = nextIcons.filter((iconName) => - iconConfig.displayCondition?.(meta, iconName.name), - ); - } + const displayIcons = iconConfig.icons.filter( + (icon) => + icon.displayCondition?.(meta, icon.name) ?? + iconConfig.displayCondition?.(meta, icon.name) ?? + true, + ); return { ...iconConfig, - icons: nextIcons, + icons: displayIcons, }; }; export const getIconTotalWidth = ( - icons: FullyIconName[] = [], + icons: HeaderActionNameOptions[] = [], iconTheme: IconTheme, ): number => { if (isEmpty(icons)) { @@ -117,12 +116,12 @@ export const getIconTotalWidth = ( }; export type GroupedIcons = { - [key in IconPosition]: FullyIconName[]; + [key in IconPosition]: HeaderActionNameOptions[]; }; export const groupIconsByPosition = ( - icons: FullyIconName[] = [], - conditionIcon?: FullyIconName, + icons: HeaderActionNameOptions[] = [], + conditionIcon?: HeaderActionNameOptions, ) => { const groupedIcons = merge( { diff --git a/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap b/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap index c92cd07497..cf15fc38d6 100644 --- a/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap +++ b/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap @@ -65,6 +65,11 @@ exports[`DrillDown Component Tests should render component 1`] = ` +
+ extra +
@@ -138,7 +143,6 @@ exports[`DrillDown Component Tests should render component 1`] = ` No data
- extra
    { test('get mobile default option', async () => { let s2: SpreadSheet; render( - { @@ -34,7 +34,7 @@ describe('MobileSheet Tests', () => { test('get mobile default fragment', () => { const { asFragment } = render( - { let customTooltipInstance; render( - { let customTooltipInstance; render( - { let s2: SpreadSheet; render( - { diff --git a/packages/s2-react/playground/config.ts b/packages/s2-react/playground/config.ts index 658de99b15..aa6f993a05 100644 --- a/packages/s2-react/playground/config.ts +++ b/packages/s2-react/playground/config.ts @@ -266,8 +266,6 @@ export const s2Options: SheetComponentOptions = { ], }, }, - - conditions: s2ConditionsOptions, style: { rowCell: { height: 50, diff --git a/packages/s2-react/playground/index.tsx b/packages/s2-react/playground/index.tsx index 6bb5fb2b85..211807fcb9 100644 --- a/packages/s2-react/playground/index.tsx +++ b/packages/s2-react/playground/index.tsx @@ -201,7 +201,8 @@ function MainLayout() { const onColCellClick = (cellInfo: TargetCellInfo) => { logHandler('onColCellClick')(cellInfo); - if (!options.showDefaultHeaderActionIcon) { + + if (showCustomTooltip) { const { event } = cellInfo; s2Ref.current?.showTooltip({ @@ -345,7 +346,7 @@ function MainLayout() { destroyInactiveTabPane > - + @@ -565,8 +566,8 @@ function MainLayout() { onChange={setShowTotals} /> { updateOptions({ @@ -917,89 +918,6 @@ function MainLayout() { }); }} /> - { - updateOptions({ - frozen: { - rowHeader: checked, - }, - }); - }} - disabled={sheetType === 'table'} - /> - - { - updateOptions({ - showSeriesNumber: checked, - }); - }} - /> - - - { - updateOptions({ - showDefaultHeaderActionIcon: checked, - }); - }} - /> - { - updateOptions({ - tooltip: { - enable: checked, - }, - }); - }} - /> - - { - updateOptions({ - interaction: { - linkFields: checked - ? ['province', 'city', 'number'] - : [], - }, - }); - }} - /> 透视表树状模式默认行头展开层级配置

    }>