Skip to content

094. 抽屉服务、通用表单与 AI 填表

学习目标

这一节为表单编辑服务做准备,并实现 AI 填表能力。

学完后,你应该能理解:

  • 抽屉服务和弹窗服务为什么类似;
  • 弹窗或抽屉为什么要在组件卸载时自动清理;
  • 通用表单如何复用表格列配置;
  • 单元格编辑逻辑为什么要抽成公共函数;
  • 表单编辑服务如何基于抽屉服务封装;
  • 聊天助手如何封装成服务;
  • 字段提示词如何帮助 AI 填表;
  • AI 返回内容如何写回表单。

表单编辑依赖单选

表单编辑通常需要当前行。

当前行来自单选模块。

所以前面先做了单选。

当用户选中一行后,表单编辑服务就可以拿到这条数据,打开抽屉表单进行编辑。

表单新建也类似。

只是它没有已有行数据,而是打开一份默认新数据。

弹窗服务卸载清理

之前封装过弹窗服务。

这一节先优化它。

优化点是:组件卸载时,自动关闭该组件打开的弹窗。

否则可能出现这种情况:

  1. 页面打开一个弹窗;
  2. 用户切换路由;
  3. 页面组件已经卸载;
  4. 弹窗还留在屏幕上。

这会造成体验和状态问题。

所以服务内部维护一个关闭函数集合。

组件卸载时,统一执行这些关闭函数。

抽屉服务

抽屉服务和弹窗服务很像。

区别是底层组件从 Modal 换成 Drawer。

它们都需要处理:

  • 打开;
  • 关闭;
  • 标题;
  • 内容;
  • 底部按钮;
  • 确认回调;
  • 取消回调;
  • 异步确认;
  • 组件卸载清理。

所以抽屉服务可以复用弹窗服务的设计思路。

异步确认

抽屉里的确认按钮可能要执行异步逻辑。

例如表单校验、保存接口、文件上传。

如果确认回调返回 false,抽屉不关闭。

这适合表单校验失败的场景。

例如:

ts
onOk: async () => {
  const valid = await validate()
  return valid
}

校验失败就留在当前抽屉,让用户继续修改。

通用表单组件

表格列配置不仅能渲染表格,也能渲染表单。

如果页面字段已经按列配置定义了一遍,就不应该再为表单重复定义一遍。

所以课程里封装通用表单组件。

它可以根据列配置生成表单字段。

例如:

  • 输入框列生成输入框;
  • 下拉列生成选择器;
  • 日期列生成日期选择;
  • 文本域列生成文本域;
  • 不可编辑列只展示内容。

单元格编辑逻辑抽离

表格单元格和通用表单都需要判断字段怎么编辑。

如果逻辑写在单元格组件里,表单组件就无法复用。

所以把公共逻辑抽成函数。

它负责计算:

  • 当前字段是否可编辑;
  • 当前字段校验规则;
  • 编辑状态下如何渲染;
  • 非编辑状态下如何渲染;
  • 传给 Form.Item 的参数;
  • 表单编辑时的渲染内容。

这样表格行编辑和通用表单能共用字段协议。

默认值填充逻辑抽离

表格渲染列时,要给字段填默认值。

通用表单渲染字段时,也要给字段填默认值。

所以默认值填充逻辑也要抽成公共函数。

否则表格和表单会出现行为不一致。

例如同一个下拉列,在表格里有默认 options 处理,表单里却没有。

抽出来之后,两边都调用同一个默认值处理函数。

通用表单布局

通用表单支持列数配置。

例如一列、两列、三列。

它不直接使用 Ant Design 默认布局。

而是自己控制表单项排列。

这样同一套字段配置可以适配不同抽屉宽度和页面布局。

例如:

tsx
<AutoForm columns={columns} columnCount={2} />

字段会按两列展示。

Form 实例暴露

通用表单内部有 Form 实例。

外部需要调用它来:

  • 校验;
  • 读取值;
  • 设置值;
  • 重置。

所以组件通过 ref 或传入 ref 对象,把内部 Form 能力暴露出去。

例如表单服务点击确定时,就要调用通用表单的校验方法。

表单编辑服务

表单编辑服务基于抽屉服务封装。

它接收字段配置和数据对象。

然后在抽屉里渲染通用表单。

用户点击确定时:

  1. 调用表单校验;
  2. 校验通过后读取数据;
  3. 把数据传给调用方;
  4. 关闭抽屉。

如果校验失败,抽屉不关闭。

这样业务页面只需要调用一个服务,就能打开表单编辑抽屉。

聊天助手服务

AI 填表需要一个聊天入口。

之前 AI 简历里已经有聊天助手组件。

这一节把它封装成服务。

调用服务时,会打开一个抽屉。

抽屉里渲染聊天助手组件。

服务接收:

  • 提示词;
  • AI 返回消息处理函数;
  • 抽屉配置。

这样 AI 助手不再固定写在某个页面里。

需要时可以随时通过服务打开。

流式消息兼容

流式接口最后返回的 chunk 可能只包含 token 使用量。

它的 choices 可能为空。

所以处理流式消息时,要先判断是否有有效内容。

有内容再读取文本。

否则可能在最后一段 token 信息上报错。

这属于 AI 接口集成里常见的小坑。

字段提示词

AI 填表不是把字段名简单丢给模型。

每种字段类型需要提供自己的提示词。

例如下拉列要告诉 AI:

  • 字段名;
  • 字段标题;
  • 字段数据类型;
  • 可选项有哪些;
  • 应该返回选项值。

日期列要告诉 AI 返回日期格式。

开关列要告诉 AI 返回真值或假值。

这样 AI 才能输出能直接写回表单的数据。

各类字段提示

这一节补齐了更多列类型。

包括:

  • 输入框;
  • 下拉选择;
  • 开关;
  • 日期;
  • 日期时间;
  • 图片;
  • 数字;
  • 文本;
  • 文本域。

不同列不仅要定义显示和编辑方式,还要定义 AI 如何理解它。

这就是 getDescriptionPrompt 这类函数的作用。

标准列不进入表单

有些列是标准列。

例如:

  • 索引列;
  • 多选列;
  • 操作列;
  • 自动撑开列。

这些列不应该进入通用表单。

所以列配置里会有标准列标记。

通用表单生成字段时会过滤掉这些列。

否则表单里会出现序号、操作按钮这类不应该编辑的内容。

AI 填表提示词

AI 填表提示词会告诉模型:

  • 它是数据解析助手;
  • 必须返回对象;
  • 只返回可编辑字段;
  • 每个字段应该按字段提示理解;
  • 必须用固定标记包裹数据;
  • 无法提取时要说明原因。

例如要求输出:

text
data start
{ ... }
data end

这样前端可以稳定提取中间内容。

样例很重要

提示词里还会提供样例。

样例告诉模型:

  • 输入字段是什么;
  • 用户描述是什么;
  • 期望输出是什么。

对于 AI 开发来说,样例非常关键。

样例越清楚,模型越容易按预期输出。

这也是从传统前端开发进入 AI 功能开发时需要特别适应的地方。

在表单服务里加入 AI 填表

表单编辑抽屉左下角增加一个“AI 填表”按钮。

按钮点击后打开聊天助手服务。

流程是:

  1. 根据当前表单字段生成提示词;
  2. 打开助手抽屉;
  3. 用户输入描述或上传图片;
  4. AI 返回带标记的数据;
  5. 前端提取标记中间的 JSON;
  6. 解析成对象;
  7. 调用表单实例的 setFieldsValue
  8. 关闭助手抽屉。

这样 AI 结果会直接填进当前表单。

支持文本和图片

AI 填表既可以处理文字描述,也可以处理图片。

例如用户发一段文字,AI 从文字中提取字段值。

也可以发截图,AI 从截图中识别内容并填表。

这依赖前面多模态聊天能力。

在业务系统里,这种能力常用于从截图、聊天记录、表格图片中快速录入数据。

不一定需要智能体

这一节的 AI 填表没有使用复杂智能体。

它只是:

  • 构造提示词;
  • 调用模型;
  • 解析结果;
  • 写回表单。

很多业务 AI 功能都是这种形态。

只有任务非常复杂、需要多步工具调用和决策时,才需要智能体。

对初学者来说,可以先从这种“模型加业务表单”的功能理解 AI 开发。

这一节的核心

这一节把表格字段体系扩展到了通用表单和 AI 填表。

关键链路是:

  • 抽屉服务承载表单;
  • 通用表单复用列配置;
  • 字段编辑逻辑和默认值逻辑抽成公共能力;
  • 表单服务负责校验和返回数据;
  • 助手服务负责打开 AI 聊天;
  • 字段提示词负责告诉 AI 如何输出;
  • AI 输出解析后写回 Form。

这让普通表单拥有了 AI 辅助录入能力。

AI Agent 课程学习文档。