Skip to main content

交叉表

交叉表是一个比较底层的 React 组件,仅提供相应表格结构的渲染能力。注意交叉表...

  • 没有 透视能力
  • 没有 收拢展开功能

主要特性#

  • 不依赖特定组件库,可独立使用
  • 简单、一致的 API 与渲染模型:左树 + 上树 => 表格
  • 高性能:数据量较大时,自动开启虚拟滚动

基本用法#

在简单的场景下,交叉表的用法很简单:

  • 通过 leftTree 描述表格左侧的树状结构;
  • 通过 topTree 描述表格上方的树状结构;
  • 通过 getValue / render 来定义每个单元格的内容;
  • 交叉表组件会根据 leftTree/topTree 来渲染表格结构,并调用 getValue / render 渲染单元格的内容
<CrossTable  // 推荐为交叉表设置一个默认列宽  defaultColumnWidth={100}  leftTree={leftTree}  topTree={topTree}  getValue={(leftNode, topNode) => {    // 可选的自定义的取数逻辑,针对每个单元格都会调用一次    // leftNode 表示当前单元格对应的左侧树节点,topNode 是对应的上方树节点  }}  render={(value, leftNode, topNode) => {    // 可选的 自定义的渲染逻辑    return value  }}/>
预览点击展开或收拢

2021年 养猪计划

源码点击展开或收拢

leftTree 的结构#

交叉表左侧的树(leftTree)是一个树节点的数组,每个树节点的结构如下:

/** 交叉表左侧或上方的树节点基类 */interface CrossTreeNode {  key: string  value: string  title?: ReactNode  data?: any  hidden?: boolean  children?: CrossTreeNode[]}
/** 交叉表左侧树状结构的树节点 */interface LeftCrossTreeNode extends CrossTreeNode {  children?: CrossTreeNode[]}
  • 主要属性:
    • key 用于在树中唯一标记一个节点;
    • value 表示节点中的文本值;
    • children 为子节点数组;
  • 可选属性:
    • title 若非空,被渲染在页面中时,节点内容将由 title 提供
      • 在不是页面渲染的情况下(例如导出到文件),title 将被忽略
    • hidden 表示是否隐藏节点
    • data 为附加在节点上的数据
      • data 中可以放任何内容
      • 放在节点中的数据,可在渲染单元格时取出,用于决定渲染结果

topTree 的结构#

topTree 的结构与 leftTree 类似,但其树节点的配置较为丰富,结构如下:

/** 交叉表上方树状结构的树节点 * 列的名称现由 value 字段提供,故从 ArtColumnStaticPart 移除了 name 字段 */interface TopCrossTreeNode extends CrossTreeNode, Omit<ArtColumnStaticPart, 'name'> {  children?: TopCrossTreeNode[]}

上方的树节点继承(extends)了左侧树节点的结构,拥有相同的 key/value/data/title/hidden/children 字段。

交叉表底层使用了 BaseTable 进行渲染,而 BaseTable 中主要的表格配置是放在每一列之上的。在 topTree 中,每个叶子节点都对应的了表格中的一列,所以每个节点除了包含 key/value/data/children 之外,还包含了部分列配置(即 ArtColumnStaticPart)。ArtColumnStaticPart 的结构如下:

  • name 列的名称;注意因为树节点已经含有 value 字段,故 name 字段在 TopCrossTreeNode 中是不起作用的)
  • code 在数据中的字段 code;注意交叉表使用自定义的 getValue/render 进行取值或渲染,会屏蔽掉 code 的默认取值方式
  • title 列标题,如果该字段非空,在渲染时会覆盖 value 字段
  • width 列的宽度
  • align 单元格中的文本或内容的 对其方向
  • hidden 是否隐藏;不推荐为交叉表的数据列设置隐藏
  • lock 是否锁列;不推荐为交叉表的数据列设置锁列
  • headerCellProps 表头单元格的 props
  • features 功能开关

其他 props#

交叉表的底层依赖了 BaseTable,故两者的 props 大部分是相同的。两者的不同点具体如下:

  • 交叉表没有 dataSource 和 columns
    • 表格结构由 leftTree 和 rightTree 提供,而单元格内容由 getValue 提供
    • 单元格渲染内容可使用 render 进行自定义;单元格的 props(即表格内的 td 元素)可使用 getCellProps 进行自定义
  • 交叉表没有 primaryKey
    • 交叉表左侧树中每个节点都有一个唯一的 key 值,故不再需要上层指定 primaryKey
  • 其他新增的 props
    • 交叉表使用 leftMetaColumns 来描述 leftTree 所处的列的配置
      • 交叉表渲染时,左侧的树会占据表格的前几列,并自动设置 lock=true,leftMetaColumns 可用于自定义这些列
    • leftTotalNode:当 leftTree 为空时,leftTotalNode 用于渲染「总计行」,避免表格中没有数据行
    • topTotalNode:当 topTree 为空时,topTotalNode 用于渲染「总计列」,避免表格中没有数据列

交叉表的 props 具体如下:

interface CrossTableProps extends Omit<BaseTableProps, 'dataSource' | 'columns' | 'primaryKey'> {  leftTree: LeftCrossTreeNode[]  topTree: TopCrossTreeNode[]  leftMetaColumns?: CrossTableLeftMetaColumn[]  leftTotalNode?: LeftCrossTreeNode  topTotalNode?: TopCrossTreeNode
  getValue(leftNode: LeftCrossTreeNode, topNode: TopCrossTreeNode, leftDepth: number, topDepth: number): any
  render?(    value: any,    leftNode: LeftCrossTreeNode,    topNode: TopCrossTreeNode,    leftDepth: number,    topDepth: number,  ): ReactNode
  getCellProps?(    value: any,    leftNode: LeftCrossTreeNode,    topNode: TopCrossTreeNode,    leftDepth: number,    topDepth: number,  ): CellProps}
export interface CrossTableLeftMetaColumn extends Omit<ArtColumnStaticPart, 'hidden'> {  /** 自定义渲染方法 */  render?(leftNode: LeftCrossTreeNode, leftDepth: number): ReactNode
  /** 自定义的获取单元格 props 的方法 */  getCellProps?(leftNode: LeftCrossTreeNode, leftDepth: number): CellProps}

不要被长长的 TypeScript 类型代码吓到,交叉表的 props 其实和 BaseTable 差别不大。

复杂场景下的交叉表#

交叉表的左树/上树的结构被设计为 { key, value, children } 的简单结构,方便上层调用者能够从不同的数据源中生成 leftTree/topTree。

在复杂场景下,需要上层根据业务需求手动生成 leftTree/topTree/getValue。ali-react-table 也提供了一定的透视能力,可在交叉表的基础上实现前端聚合的透视表,可供使用和参考。

例如,对 leftTree 进行如下处理,可以关闭左侧自动单元格合并功能:

// 注意这个时候每个节点的 key 都要不同const leftTree = [  { key: 'forenoon-9', value: '上午', children: [{ key: '9', value: '9:00-10:00' }] },  { key: 'forenoon-10', value: '上午', children: [{ key: '10', value: '10:00-11:00' }] },  { key: 'forenoon-11', value: '上午', children: [{ key: '11', value: '11:00-12:00' }] },  // other nodes...]
预览点击展开或收拢

2021年 养猪计划

源码点击展开或收拢

交叉表定制#

交叉表的底层仍是 <BaseTable />

对交叉表进行定制时,可以先通过 buildCrossTable 根据交叉表的输入生成 dataSource / columns,进行一些自定义的处理,然后再将处理好的数据传给 BaseTable,并注意为 BaseTable 设置 primaryKey={ROW_KEY}

可以使用 pipeline 进行表格功能拓展(具体可以参考这个示例),但因为交叉表本身很复杂,只有一部分比较简单的 pipeline feature 可以和交叉表一起工作,例如 列范围高亮.

import { BaseTable } from 'ali-react-table'import { buildCrossTable, ROW_KEY } from 'ali-react-table/pivot'
function MyComplexTable() {  // 将「传给 CrossTable 的参数」传给 buildCrossTable  const { dataSource, columns } = buildCrossTable({    leftTree,    topTree,    getValue,    // 这里也支持其他的 CrossTable 的 props:    // leftTotalNode, topTotalNode, leftMetaColumns, render, getCellProps  })
  // 在这里可以对 dataSource 和 columns 进行一定的处理后,再将数据传递给 BaseTable
  return (    <BaseTable      className="my-complex-table"      primaryKey={ROW_KEY}      defaultColumnWidth={100}      dataSource={dataSource}      columns={columns}    />  )}

columnOffsetrowOffset#

danger

columnOffsetrowOffset 仍是一个实验性的 API,可能在将来会发生变更.

交叉表的左侧部分自带了单元格合并功能,而单元格合并功能依赖于 rowIndex/colIndex,所以在处理 dataSource/columns 的过程中需要注意左侧部分的整体位置发生了变化(例如在表格左侧多了一列用于展示每一行的序号).

如果左侧部分整体位置将发生变化,在调用 buildCrossTable(...) 时就需要设置相应的 options.rowOffset / options.columnOffset 来声明最终交叉表部分的行列偏移量。

在下面的这个示例中,我们在交叉表的左侧新增了两列(多选一列、序号一列),在调用时的代码为 buildCrossTable({ columnOffset:2 })