切换日光/暗黑模式
090. 按钮钩子变量与基础新建行
学习目标
这一节开始实现表格的新建、复制、删除相关能力。
当前重点是按钮体系和基础新建行。
学完后,你应该能理解:
- 为什么按钮也要设计成可扩展模块;
- 什么是钩子变量;
- 按钮配置里需要哪些字段;
- 为什么编辑状态下要覆盖按钮栏;
- 为什么新建行时要调整分页大小;
- 如何生成一条默认新行;
- 新建行为什么要立即进入编辑状态。
新建功能前的页面整理
开始实现新建功能前,先整理测试页面。
原首页里有很多测试代码。
这些测试配置会影响后面调试。
所以把测试页面备份出来,再把首页清理成更适合演示新建功能的页面。
这也是写复杂功能前常见的做法:
先把测试环境收干净。
否则你很难判断问题来自新功能,还是之前残留的测试代码。
新建、复制、删除的关系
表格里会有几类操作:
- 新建;
- 复制;
- 删除;
- 行内编辑;
- 表单编辑;
- 智能新建。
复制本质上也是一种新建。
区别是它会用已有行的数据作为初始值。
所以实现复制前,要先有基础新建能力。
按钮为什么也要模块化
通用表格里的按钮不是固定的。
不同项目可能需要不同按钮。
例如:
- 新建;
- 批量删除;
- 导出;
- 导入;
- 智能填写;
- 自定义业务按钮。
所以按钮不能写死在搜索栏里。
它也应该像渲染内容一样,通过模块注册进去。
这就引出了钩子变量。
钩子变量
前面讲过渲染钩子。
渲染钩子保存的是渲染内容。
钩子变量保存的是配置数据。
按钮列表就是一个典型钩子变量。
每个模块可以往按钮钩子变量里追加自己的按钮配置。
最后由按钮栏组件统一渲染。
钩子变量和渲染钩子的相同点
它们都有几个共同点:
- 每次渲染前清空;
- 模块执行时重新追加;
- 可以接受空值;
- 最后统一过滤、排序、使用;
- 可以作为缓存计算的依赖。
清空很重要。
如果不清空,每次渲染都会继续追加,按钮数量会越来越多。
按钮配置类型
按钮配置需要描述按钮的行为和展示。
常见字段包括:
key:按钮标识;label:按钮文本;seq:按钮顺序;icon:按钮图标;onClick:点击回调;disabled:是否禁用;dropdown:是否放进更多菜单;type:按钮类型,例如主按钮。
这些配置不是某一个按钮专用。
它是所有按钮模块共同遵守的协议。
外部按钮和下拉按钮
按钮分两类。
第一类直接展示在工具栏上。
第二类收进“更多”下拉菜单。
按钮栏组件会根据配置拆分这两类按钮。
外部按钮直接渲染。
下拉按钮放进菜单里渲染。
这样按钮多的时候,工具栏不会挤成一团。
下拉按钮记忆
更多按钮里会记住上次点击的按钮。
例如用户上次点击的是“表单新建”。
下次更多按钮可以优先显示或继续使用这个动作。
这属于交互优化。
它不是新建功能的核心,但能让按钮栏更像成熟组件。
编辑状态下的按钮栏
普通状态下,按钮栏显示新建、更多等按钮。
编辑状态下,按钮栏需要换成另一套按钮。
例如:
- 继续新建;
- 取消;
- 保存。
所以状态模块里新增了一个 overrideButtonContent。
当它有值时,按钮栏渲染这个值。
当它为空时,按钮栏渲染普通按钮配置。
为什么要覆盖按钮栏
不同模块可能有自己的临时状态。
例如:
- 行内新建;
- 批量删除;
- 导出选择;
- 批量编辑。
这些状态下,工具栏应该显示当前动作相关按钮。
比如批量删除时,可以显示“确认删除”和“取消选择”。
所以按钮栏需要被模块临时接管。
这比给按钮栏写很多 if 判断更灵活。
默认按钮显示配置
编辑、复制、删除按钮是否显示,来自配置。
例如:
- 是否显示编辑按钮;
- 是否显示复制按钮;
- 是否显示删除按钮;
- 是否显示新建按钮。
这些配置需要有默认值。
否则默认是 false,按钮就都不显示。
真实系统里,这些值可能来自页面权限。
例如某个角色是否允许新增、编辑、删除。
当前先写默认值,后面可以接权限配置。
操作列按钮
操作列里也要读取这些配置。
如果关闭编辑按钮,就不显示编辑。
如果关闭删除按钮,就不显示删除。
复制按钮依赖新建能力。
如果没有开启新建,复制也应该禁用或隐藏。
因为复制最终还是要走新建流程。
分页大小和编辑状态
新建行时,表格当前页可能已经满了。
例如每页 5 条。
如果新增一条数据后仍然只显示 5 条,多出来的新行可能看不见。
所以编辑状态下,需要把表格分页大小临时调大。
逻辑是取当前页大小和数据长度之间的较大值。
这样新增行能留在当前页面可见区域。
搜索栏占位
搜索栏左侧有搜索框和其他按钮。
右侧有表格操作按钮。
中间需要一个自动撑开的占位元素。
它使用 flex: 1。
这样操作按钮会被推到右边。
这也是常见工具栏布局方式。
按钮模块
新建相关按钮由按钮模块注册。
按钮模块会往按钮钩子变量里添加:
- 行内新建按钮;
- 表单新建按钮;
- 智能新建按钮;
- 其他操作按钮。
其中智能新建依赖后面的 AI 能力。
当前先把按钮结构放进去,后续再接具体逻辑。
基础新建按钮
行内新建按钮点击后,会调用表格对象上的 createRecord 方法。
这个方法负责创建一条新行数据。
按钮本身不关心新行怎么生成。
它只触发表格能力。
这符合模块设计原则:UI 触发动作,数据生成逻辑放在表格方法里。
createRecord
createRecord 可以接收一条或多条初始数据。
如果传入对象,就用它作为新行基础值。
如果没有传入,就从配置里的 defaultNewRow 生成。
defaultNewRow 可以是对象,也可以是函数。
函数还可以是异步函数。
这样项目可以从后端或其他服务拿到默认新行数据。
默认新行 ID
新建行必须有 ID。
因为表格渲染需要稳定 key。
ID 有两种来源。
第一种是后端生成真实 ID。
这适合复杂业务。
例如新建前就要先拿到编号或主键。
第二种是前端生成临时 ID。
例如:
ts
new_xxx1
保存时再清理临时 ID,由后端生成真实 ID。
defaultNewRowId
配置里可以控制是否从后端获取默认新行 ID。
如果开启,就调用 ID 生成方法拿真实 ID。
如果没有开启,就生成前端临时 ID。
这让通用表格能适配不同业务。
有的业务只保存时需要 ID。
有的业务创建草稿时就必须有 ID。
添加新行到数据源
生成新行后,需要把它插入表格数据。
可以理解成:
ts
setData(prev => [...newRows, ...prev])1
具体插入前面还是后面,取决于产品设计。
当前重点是:新行必须进入表格数据源。
否则界面不会显示。
标记新行编辑状态
新行加入数据源后,还要标记为新建数据。
同时让它进入编辑状态。
所以要更新新建 ID 映射。
例如:
ts
createIdMapper[newRow.id] = true1
这样行组件就会把它渲染成可编辑行。
用户点击新建后,应该直接看到可编辑表单,而不是一条普通空行。
取消功能暂未完成
这一节里基础新建已经能显示新行。
但取消逻辑还没完全处理。
新建后点击取消,需要删除临时新行。
这个会继续往后补。
当前阶段先确保:
- 按钮出现;
- 点击按钮能生成新行;
- 新行有 ID;
- 新行进入表格;
- 新行进入编辑状态。
这一节的核心
按钮体系是通用表格扩展的一部分。
它不是简单地在 JSX 里写几个按钮。
这一节完成了几件关键事:
- 定义按钮配置协议;
- 创建按钮钩子变量;
- 让模块可以追加按钮;
- 让按钮栏支持普通状态和覆盖状态;
- 给默认配置补上按钮显示控制;
- 为新建行准备默认数据和 ID;
- 点击新建后,把新行插入表格并进入编辑状态。
后续复制、取消、批量保存和智能新建都会建立在这个按钮体系上。