👊 Pro ArrayTable

Pro ArrayTable 是一个增强版本的 ArrayTable, 增加了以下属性与功能

  • ✅ schema 拓展: RowExpand / Toolbar / Footer
  • ✅ 列配置: 排序与拖动调整列宽
  • ✅ 可编辑弹窗, 使用事件委托优化
  • ✅ 常用功能配置简化与增强 (pagination/rowSelection/expandable)
  • ✅ expandable.defaultExpandedAllRows 现在支持惰性加载啦!
  • ✅ onSortEnd 支持 (from, end, arrayField) => Promise 回调!
  • ✅ expandable.expandedRowKeys / rowSelection.selectedRowKeys 现在会根据页码调整进行智能重置!

设计思路

ProArrayTable 的目的就是简化常用表格操作, 增强 Schema 能力, 同时, 对异步操作友好;

ArrayTable 本身是对本地数据的一个编辑方案, 缺少一些异步的支持, 比如 onSort/onMoveUp/onMoveDown/onRemove 等操作都没有拦截, 缺少类似 immber Patches 处理, 不好回退, 这在处理远程数据的时候就非常难受了; 同时, 一些额外的功能, 列选择/列展开无法使用 Schema 语法, 限制了JSON Schema 表现力.

弹出层编辑方案设计

在过往使用 formily 的过程中, 表格中的弹窗编辑一个是个痛点, 基本上都是在使用 FormDialog 做的处理, 除了再 ShadowForm 中提到的 FormDialog/FormDrwaer 自身的问题, 在实际使用中, 一直把 Formdialog 渲染在 eolumn 的render 中, 这导致了大量的 Modal 被反复创建销毁。

为什么不用事件委托?

  1. 需要在表单外部处理委托代码(但其实没有深入尝试, 欢迎讨论)
  2. 另一个主要原因是 formdialog 中的表单实例生命周期难以理解(现在想起来可能是因为 initialvalues 迷惑行为加上理解问题导致)

但总之, 我想到了更好的办法。

还是事件委托, 但 builtin, 使用一个内置的 DelegateContex 来管理表单中的 click 事件, 添加 data-属性作为可代理标志, 并添加行信息来区分来源。

单看事件委托, 属实平平无奇, 但与 ShadowForm 结合着用才能显现出巨大的抛瓦

属性说明

属性说明类型默认值
resizeable可拖拽列宽功能开关, 在 Columns 也可以单独设置booleanfalse
onSortEnd拖动排序回调, reject 则拒绝执行排序 `(from: number, to: number, arrayField: ArrayField) => voidPromise<void>`--
settings动态列排序功能开关booleantrue
slice分页性能优化开关, 默认开启booleantrue
rowSelection可选列选择功能, 配置简化, 提供功能钩子, 默认关闭true| TableProps['rowSelection']--
pagination分页功能, 配置简化, 提供功能钩子, 默认开启, false 显式关闭false| TableProps['pagination']{hideOnSingle:true}
expandable行展开功能, 配置简化, 提供功能钩子配置, 默认关闭TableProps['expandable']--
RowExpandSchame 形式的 行展开----
ToolbarSchame 形式的自定义表头部分内容, 自定义组件名称必须包含 Toolbar----
FooterSchame 形式的自定义表头部分内容, 自定义组件名称必须包含 Footer----

代码案例: 基本使用

代码案例:可编辑弹窗

ArrayBase 仍然适用的API

  • ArrayBase.useArray
  • ArrayBase.useIndex
  • ArrayBase.useRecord

不支持表格树形数据的展示, 但你可以这样迂回一下

// 使用这个组件
const Indent = () => {
  const record = ArrayBase.useRecord?.();
  const indent = Array.from({ length: record?.indent }).fill("..").join(" ");
  return <span style={{ padding: "0 8px " }}>{indent}</span>;
};

const fluttenTree = (tree: Menu[], indent = 0) => {
  return tree.reduce<(Menu & { indent: number })[]>((flat, item) => {
    flat.push({
      ...item,
      // 添加 indent
      indent,
      // 记得去掉 children
      children: undefined,
    });
    if (item.children) {
      flat.push(...fluttenTree(item.children, indent + 1));
    }
    return flat;
  }, []);
};

ProArrayTable.useTablePagination 获取分页信息

export interface ITableSelectionContext {
  current: number;
  pageSize: number;
  total: number;
  setPagination: React.Dispatch<
    React.SetStateAction<{ current: number; pageSize: number }>
  >;
}

ProArrayTable.useTableExpandable 获取 Expandable 展开行信息

export interface ITableExpandableContext {
  // 分页页码变更会重置
  expandedRowKeys: RowKey[];
  setExpandedRowKeys: React.Dispatch<React.SetStateAction<RowKey[]>>;
}

ProArrayTable.useTableRowSelection 获取列选择信息

export interface ITableSelectionContext {
  selectedRowKeys: RowKey[];
  setSelectedRowKeys: React.Dispatch<React.SetStateAction<RowKey[]>>;
}

ProArrayTable.useProArrayTableContext 获取数组所有上下文信息

export const useProArrayTableContext = () => {
  const array = ArrayBase.useArray();
  const pagination = useContext(TablePaginationContext);
  const rowSelection = useContext(TableRowSelectionContext);
  const expanedable = useContext(TableExpandableContext);
  return { array, pagination, rowSelection, expanedable };
};

ProArrayTable.DelegateAction 代理动作触发器

代理动作触发器, 给子组件打标记, Table Wrapper 的 click 事件绑定进行分发处理, 通过 act 与 ShadowForm 弹窗系列关联

{
  /** 动作标志, 与 ShadowModal 对应  */
  act: string;
  /** 当前列所属 index, 默认使用 ArrayBase.useIndex() 获取当前数据对应位置 */
  index?: number;
  /** 数据初始化加载器, 默认使用 field.record 当前模型所对应 record */
  initLoader?: (record?: any) => object | Promise<object>;
  /**
   * 具体元素, 但必须有标签包装, 因为会被追加 data-属性字段标志给事件委托来识别
   * 不填写的情况下, 使用 Button 标签包装 field.title
   */
  children?: React.ReactElement;
}

ShadowModal/ShadowDrawer 通用属性

鉴于这套组件在 ProArrayTable 中使用频率颇高, 融合了 IShadowFormOptions ,弹窗 属性组合成一个组件, 并在回调种注入当前数组的 Context

{
  /** 动作标志, 与 ShadowModal 对应, 不填写的话,取当前 schema 的 name  */
  act?: string;
  /** 取消事件 */
  onCancel?: (
    ctx: ReturnType<typeof ProArrayTable.useProArrayTableContext>,
  ) => void | Promise<void>;
  /** 子表单提交事件, 可以做点什么 */
  onOk?: (
    data: any,
    ctx: ReturnType<typeof ProArrayTable.useProArrayTableContext>,
  ) => void | Promise<void>;
} & IShadowFormOptions

ProArrayTable.ShadowModal 表格共享弹窗, 结合 ShadowForm#DelegateAction 使用

除了 onOk,onCancel antd/Modal 其他属性

ProArrayTable.ShadowDrawer 表格共享抽屉, 结合 ShaodwForm#DelegateAction 使用

Welcome PR.

ProArrayTable.ProAddition 弹窗/抽屉形式的可编辑内容新增组件

高级自增组件, 提供了弹窗/抽屉形式的空调编辑内容, 这个本质上是一个内置了一个 ShadowForm的 Modal/Drawer

{
  /** 子表单Schema */
  schema: ISchema,
  /** createForm 创建的表单实例, 有自定义 effects 等可以通过自定义一个 form 这个传入 */
  form?: ReturnType<typeof createForm>,
  /** createSchemaField 函数配置 */
  schemaFieldOptions?: Parameters<typeof createSchemaField>[0],
  /** 弹窗形式: Modal 或 Drawer, 默认 modal  */
  popType: "modal" | "drawer"
  /** 其他 Modal / Drawer 原始属性 */
  ...others: React.ComponentProps<typeof Modal | typeof Drawer>
}

ProArrayTable.Column 表格列

追加了可拖拽列宽配置开关, 优先级 > Table 上的 resizeable 属性

其他与 @formily/antd 中的 ArrayTable.Column 表现一致

{
  // 是否可拖拽调整列宽
  resizeable?: boolean
  // ... 其他参考 @formily/antd ArrayBase.Column  配置
}

ArrayBase 基础事件添加 Promise 支持, 影响组件列表如下

export interface IProArrayTableBaseMixins {
  onSortEnd?: (
    form: number,
    to: number,
    arrayField: ArrayField,
  ) => void | Promise<void>;
  onAdd?: (
    toIndex: number,
    value: any,
    arrayField: ArrayField,
  ) => void | Promise<void>;
  onCopy?: (
    distIndex: number,
    value: any,
    arrayField: ArrayField,
  ) => void | Promise<void>;
  onRemove?: (index: number, arrayField: ArrayField) => void | Promise<void>;
  onMoveUp?: (index: number, arrayField: ArrayField) => void | Promise<void>;
  onMoveDown?: (index: number, arrayField: ArrayField) => void | Promise<void>;
}
  • ProArrayTable.SortHandle
  • ProArrayTable.Remove
  • ProArrayTable.Copy
  • ProArrayTable.MoveDown
  • ProArrayTable.MoveUp
  • ProArrayTable.Index