38 KiB
| title | type | date | origin |
|---|---|---|---|
| feat: Bitable P0 UX Polish + Agent Parity | feat | 2026-07-03 | docs/brainstorms/2026-07-03-bitable-comparative-evaluation-requirements.md |
Bitable P0 UX Polish + Agent Parity
Summary
引入统一设计 token 系统、补齐 grid 视图 UX 缺口(内联字段配置、记录详情抽屉、视图类型切换、分组与条件格式)、闭合 agent 对等缺口(BitableTool 4 新动作 + DELETE /views 端点)。这是 bitable 从"功能残缺"到"grid 体验达飞书/Twenty 水准 + agent 对等"的 P0 交付,分 3 阶段:R5 token 基座 → R1-R4 前端 UX → R15a 后端+agent 对等。
Problem Frame
bitable 后端 v1 齐备(28 端点 + 公式引擎 + 三类采集),但前端产品形态残缺——grid 体验未达竞品水准(列头编辑跳抽屉、无记录详情、无分组/条件格式、视觉无统一设计语言),且 BitableTool 仅 6 动作 vs 28 REST 端点存在系统性 agent 孤儿风险。P0 聚焦"已存在功能的 UX 打磨 + agent 对等最高优先级子项",P1/P2 功能广度延后。
origin 文档(docs/brainstorms/2026-07-03-bitable-comparative-evaluation-requirements.md)已完成三向对比评估(agentkit vs Twenty vs 飞书)、差距分析、优先级建议,本 plan 承接其 P0 范围(R1/R2/R3/R4/R5/R15a)。
Scope
In Scope (P0)
- R5: 统一设计 token 系统(CSS 变量层:颜色/间距/圆角/字号)+ 字段类型图标 + 彩色 chip 升级
- R1: 列内联字段配置(列头菜单直接编辑,不跳右侧抽屉)
- R2: 记录详情侧边抽屉(行点击展开,全字段类型可视化展示与编辑)
- R3: 视图类型切换与创建(ViewSwitcher 暴露 5 种类型,禁用态标注"规划中")
- R4: grid 视图内分组与条件格式(多字段分组 + 规则自动着色)
- R15a: BitableTool 动作补全(6→10 动作)+ DELETE /views 后端端点
Out of Scope (Deferred to P1/P2)
- R6/R7: 看板/画廊视图实现(P1)
- R8: 字段类型扩展 17 项 + schema V3 迁移(P1)
- R9/R10: 公式库扩展 + 跨表关联增强(P1)
- R11: 甘特/表单视图(P2)
- R12/R13/R14: 自动化/仪表盘/细粒度权限(P2)
- R15b/R15c: NL→表 agent 技能 / 定时采集 UI(B 线,独立推进)
- 并发编辑 UX、实时协作(P1+)
- 大规模优化(列式存储/分区,v3)
Pre-conditions / Assumptions
- 后端 v1 齐备且稳定(28 端点 + 公式引擎 + 三类采集已就位)
ViewType枚举已有 5 种类型(grid/kanban/gantt/gallery/form)—— 后端无需改枚举CreateViewRequest已接受view_type字段 —— R3 纯前端UpdateViewRequest.config: dict可直接存group_by/conditional_formatting—— R4 后端无需改模型- vxe-table 4 作为 grid 实现基础(需先消除幽灵依赖)
- PostgreSQL 性能足以支撑 v1/v2 规模(<10 万行);大规模延后 v3
- P0 视觉风格对齐基于现有 Ant Design Vue + vxe-table 主题定制,不引入新 UI 库
Key Technical Decisions
KTD1. 设计 token 用 CSS 自定义属性层,不引入 Tailwind/Sass。 在 src/agentkit/server/frontend/src/styles/bitable-tokens.css 定义 :root 下的 CSS 变量(--bitable-color-*/--bitable-spacing-*/--bitable-radius-*/--bitable-font-*),通过 Ant Design Vue 的 ConfigProvider token 覆盖与 vxe-table 的 CSS 变量对接。理由:零依赖、运行时可覆盖、与现有 Ant Design Vue 4 token 系统兼容。颜色调色板 8 色预设,全部满足 WCAG AA 对比度(≥4.5:1)。
KTD2. vxe-table 幽灵依赖在 U1 显式声明。 src/agentkit/server/frontend/package.json 添加 vxe-table 与 vxe-pc-ui 版本声明(与主仓 node_modules 实际版本对齐),消除 hoisting 风险。这是 R1/R3/R4 实施的前置条件。
KTD3. R1 内联字段配置用 vxe-table header slot + 自定义 InlineFieldConfigurator 组件(混合方案)。 不用 vxe-table 内置编辑(功能受限),不自建完整列头组件(重复造轮子)。在 vxe-table 的 header slot 内渲染列名 + 下拉触发图标,下拉面板内嵌 InlineFieldConfigurator.vue(复用 FieldConfigForm.vue 的类型切换/选项管理逻辑,但不抽屉化)。FieldManagePanel.vue 保留作为批量管理入口。
KTD4. R2 记录详情抽屉单列纵向,宽度 480px(与 FieldManagePanel 一致),>10 字段时扩展至 640px。 不用双列紧凑(窄屏退化复杂、字段类型多时视觉混乱)。sticky header 显示记录标题,body 按字段顺序纵向排列,每字段一行(label + value)。attachment/image 显示缩略图,formula 显示只读计算结果,lookup 显示关联值。宽度断点通过 design token 控制(--bitable-drawer-width/--bitable-drawer-width-wide)。
KTD5. R3 视图类型选择用 Ant Design Vue Dropdown + disabled items。 ViewSwitcher.vue 的"新建视图"按钮改为 Dropdown,5 种类型列出,未实现的(kanban/gallery/gantt/form)设 disabled + tooltip "规划中"。点击 grid 调用 handleCreateView 传 view_type: 'grid'(不再硬编码)。后端 CreateViewRequest 已支持 view_type,无需后端改动。
KTD6. R4 分组与条件格式存入 View.config,后端用 Pydantic 校验 config 子结构。 group_by: [{field_id, direction}](最多 3 字段)+ conditional_formatting: [{field_id, operator, value, color_key}](7 运算符)。前端 ViewConfigPanel.vue 新增分组与条件格式编辑器,提交时 PUT 进 config dict。后端 UpdateViewRequest.config 已是 dict[str, Any],但新增 Pydantic 校验模型(GroupByItem/ConditionalFormatRule)在 service 层 validate config 子结构——operator 枚举校验、color_key 限定 8 色白名单、group_by 长度 ≤3。理由:agent 通过 BitableTool 的 update_view 动作可直接写 config,前端校验对 agent 路径无效,必须服务端 enforce(闭合 ce-doc-review 安全 finding)。颜色存语义 key('red'|'orange'|'yellow'|'green'|'blue'|'purple'|'gray'|'neutral'),前端 groupingRulesUtils.ts 提供 colorKeyToCssVar() 映射到 --bitable-cf-*,避免前端 CSS 实现细节持久化到数据库。
KTD7. R4 条件格式规则构建器用列表式(rule list),非向导式、非 DSL。 每条规则一行:字段选择 + 运算符选择 + 值输入 + 颜色选择(8 色 token 预设)+ 启用/禁用开关。规则按顺序优先(首条匹配 wins)。预览实时着色当前 grid 数据样本。
KTD8. R4 分组交互:默认展开、首字段优先排序、分组头显示聚合行。 分组头显示分组字段值 + 计数(+ 求和/平均值如果字段为 number)。多字段分组时层级嵌套,可折叠/展开。拖拽分组字段调整层级顺序(可选,P0 不强制)。
KTD9. R15a 后端新增 DELETE /views/{view_id} 端点,复用现有 ownership 检查模式。 路由 @router.delete("/views/{view_id}", status_code=204),调用 service.delete_view(view_id),前置 _check_table_ownership(通过 view → table 关联查 owner)。404-before-403 与现有 27 端点一致(见 docs/solutions/architecture-patterns/bitable-companion-service-security-reliability-patterns.md 模式 4)。X-Internal-Token 路径 ownership 语义:内部令牌绑定系统 agent 身份(user_id="__bitable_internal__"),所有写操作记审计日志。agent 通过 BitableTool 调用 _delete_view 时,X-Internal-Token 透传但 ownership 检查仍执行——内部令牌仅 bypass ownership(与现有 28 端点一致),不等于全权。Test scenario 验证:X-Internal-Token 调用 _delete_view 操作非自己资源时返回 404。
KTD10. R15a BitableTool 4 新动作与现有 6 动作同模式。 bitable_tool.py 新增 _create_view/_update_view/_update_field/_delete_view 4 个 async def,复用现有 _call_api 内部方法(JWT 或 X-Internal-Token 认证)。动作注册需同步更新两处:(1) execute() 内 handlers dict 添加 4 个映射;(2) input_schema.properties.action.enum 添加 4 个字符串(create_view/update_view/update_field/delete_view)。若只改 handlers 不改 enum,agent 的 LLM 看不到新动作可选值,4 个新动作实际不可调用。update_field 覆盖 R1 的 PATCH /fields/{id},create_view/update_view 覆盖 R3/R4 的视图操作,delete_view 覆盖新增的 DELETE 端点。
KTD11. 交付分 3 阶段,每阶段可独立验证。 Phase 1 (U1) token 基座 → Phase 2 (U2-U5) 前端 UX → Phase 3 (U6) 后端+agent。Phase 2 的 4 个 U-ID 可并行(共享 U1 token 但互不依赖),Phase 3 依赖 Phase 2 的 U4 + U5(create_view/update_view 的 view_type + group_by/conditional_formatting config 结构需与前端一致)。Phase 2 开始时即冻结 config schema 契约文档,U6 可与 U4/U5 并行实现,仅集成测试在 Phase 2 完成后。
Implementation Units
Phase 1 — Foundation
U1: R5 Design Token System + vxe-table Dependency Declaration
Goal: 建立统一设计 token 系统(CSS 变量层),重写 bitable 组件颜色/间距/圆角/字号,补齐字段类型图标与彩色 chip 升级,显式声明 vxe-table 依赖。这是 Phase 2 所有 UI 工作的前置基座。
Files:
src/agentkit/server/frontend/package.json— 添加vxe-table+vxe-pc-ui依赖声明src/agentkit/server/frontend/src/styles/bitable-tokens.css(new) — CSS 自定义属性定义src/agentkit/server/frontend/src/main.ts— import bitable-tokens.csssrc/agentkit/server/frontend/src/components/bitable/BitableGrid.vue— 替换硬编码颜色为 token 引用src/agentkit/server/frontend/src/components/bitable/ColumnHeaderMenu.vue— token 化src/agentkit/server/frontend/src/components/bitable/SelectDisplay.vue— chip 升级(8 色 token 调色板 + 对比度修复)src/agentkit/server/frontend/src/components/bitable/SelectCellEditor.vue— token 化src/agentkit/server/frontend/src/components/bitable/AttachmentCell.vue— token 化src/agentkit/server/frontend/src/components/bitable/ImageCell.vue— token 化src/agentkit/server/frontend/src/components/bitable/FileCard.vue— token 化src/agentkit/server/frontend/src/views/BitableFileListView.vue— token 化src/agentkit/server/frontend/src/views/BitableFileDetailView.vue— token 化src/agentkit/server/frontend/src/components/bitable/FieldTypeIcon.vue(new) — 9 种字段类型图标(Ant Design Outlined)src/agentkit/server/frontend/src/composables/useResponsiveBreakpoint.ts(new) — 768/1024/1440 断点 composablesrc/agentkit/server/frontend/src/components/bitable/LoadingState.vue(new) — 统一加载态(骨架屏)src/agentkit/server/frontend/src/components/bitable/ErrorState.vue(new) — 统一错误态(行内提示)
Approach:
- 在
bitable-tokens.css定义 4 类 token:- 颜色:
--bitable-color-primary/--bitable-color-bg/--bitable-color-text/--bitable-color-border/--bitable-cf-{red,orange,yellow,green,blue,purple,gray,neutral}(条件格式 8 色预设,全部 WCAG AA ≥4.5:1) - 间距:
--bitable-spacing-{xs,sm,md,lg,xl}(4/8/12/16/24px) - 圆角:
--bitable-radius-{sm,md,lg}(4/6/8px) - 字号:
--bitable-font-{xs,sm,md,lg}(12/13/14/16px) - 抽屉宽度:
--bitable-drawer-width: 480px/--bitable-drawer-width-wide: 640px
- 颜色:
- 逐组件 grep 硬编码 hex 颜色(
#[0-9a-fA-F]{3,6}),替换为 token var() FieldTypeIcon.vue映射 9 种类型到 Ant Design Outlined 图标:text→FileTextOutlined, number→NumberOutlined, date→CalendarOutlined, select→TagOutlined, multiselect→TagsOutlined, attachment→PaperClipOutlined, image→PictureOutlined, formula→FunctionOutlined, lookup→LinkOutlinedSelectDisplay.vuechip 配色从 8 色 token 调色板取色,每色确保 ≥4.5:1 对比度useResponsiveBreakpoint.ts暴露isMobile/isTablet/isDesktop响应式断点(768/1024/1440)LoadingState.vue+ErrorState.vue作为 bitable 内部统一加载/错误态组件(P0 提升理由:U3 RecordDetailDrawer 异步加载需骨架屏避免白屏、U2/U4 提交需 loading 态防重复点击。U2-U5 Test scenarios 强制引用这两个组件证明确实被消费)useResponsiveBreakpoint.ts在 U3(抽屉isMobile时全屏覆盖)+ U5(ViewConfigPanelisMobile时改底部抽屉)显式消费- R3/R4 后端假设验证(Phase 1 退出条件):grep + pytest round-trip 验证
CreateViewRequest接受view_type且UpdateViewRequest接受任意 config dict。若验证失败,Phase 2 需补后端工作项
Test scenarios:
- Given bitable-tokens.css, When grep
#[0-9a-fA-F]{3,6}in bitable components, Then 零硬编码 hex(全部用 var()) - Given FieldTypeIcon, When 渲染 9 种字段类型, Then 各有对应 Ant Design Outlined 图标
- Given SelectDisplay chip, When axe-core 扫描, Then 所有关键文本对比度 ≥4.5:1(WCAG AA)
- Given package.json, When
npm ls vxe-table vxe-pc-ui, Then 两者均为显式声明依赖(非 hoisting) - Given useResponsiveBreakpoint, When viewport < 768px, Then
isMobile === true - Given LoadingState, When 异步加载, Then 显示骨架屏(非 spinner)
- Given ErrorState, When 异步失败, Then 显示行内错误提示 + 重试按钮
Verification:
npm run typecheck通过npm run build:frontend通过- axe-core 扫描 bitable 页面对比度全绿
- grep 硬编码 hex 零匹配
Phase 2 — Frontend UX (depends on U1)
U2: R1 Inline Field Configuration
Goal: 列头菜单直接编辑字段(重命名/改类型/选项管理),不跳右侧 FieldManagePanel 抽屉。FieldManagePanel 保留作为批量管理入口。
Files:
src/agentkit/server/frontend/src/components/bitable/InlineFieldConfigurator.vue(new) — 内联字段配置面板(复用 FieldConfigForm 逻辑)src/agentkit/server/frontend/src/components/bitable/ColumnHeaderMenu.vue— 菜单重构:编辑 → 内联展开(非跳抽屉)src/agentkit/server/frontend/src/components/bitable/BitableGrid.vue— header slot 集成 InlineFieldConfiguratorsrc/agentkit/server/frontend/src/components/bitable/FieldManagePanel.vue— 保留,添加"批量管理"定位提示src/agentkit/server/frontend/src/stores/bitable.ts— 复用现有updateFieldactionsrc/agentkit/server/frontend/e2e/bitable-field-ops.spec.ts— extend: 内联编辑场景src/agentkit/server/frontend/src/helpers/fieldRenderUtils.ts(new) — 纯函数:字段类型校验、类型转换兼容性检查
Approach:
InlineFieldConfigurator.vue从FieldConfigForm.vue抽取核心逻辑(类型切换、选项管理、校验),以 inline panel 形式渲染(非抽屉)ColumnHeaderMenu.vue的"编辑"项改为 toggle InlineFieldConfigurator 在列头下方展开- 类型变更提交前调用
fieldRenderUtils.checkTypeCompatibility(oldType, newType, existingValues)—— 若现有值不可转换则显示警告 + 阻止提交 - 重命名/选项管理直接调用 store
updateField(fieldId, patch)→ PATCH /fields/{id} - 键盘可达:Tab 到列头菜单 → Enter 展开 → Tab 字段间切换 → Esc 关闭
Test scenarios:
- Given select 字段有 3 选项, When 点击列头菜单"编辑", Then InlineFieldConfigurator 内联展开(不跳右侧抽屉)
- Given 字段名变更提交, When PATCH /fields/{id}, Then 返回 200 + grid 在 1 帧内重渲染新标签
- Given 字段类型 text → number, When 现有记录值不可转换, Then 显示警告 + 阻止提交
- Given select 选项管理, When 新增/删除/重命名选项, Then 内联完成 + 提交后 chip 更新
- Given 键盘导航, When Tab 到列头菜单 → Enter, Then 展开 InlineFieldConfigurator + 焦点在首字段
- Given FieldManagePanel, When 从列头菜单"批量管理"打开, Then 仍可用(保留入口)
Verification:
npm run typecheck通过- e2e
bitable-field-ops.spec.ts内联编辑场景通过 - axe-core 键盘导航可达性通过
U3: R2 Record Detail Drawer
Goal: grid 行点击展开右侧详情抽屉,显示所有字段类型的可视化展示与编辑。单列纵向,480px(>10 字段扩展 640px)。
Files:
src/agentkit/server/frontend/src/components/bitable/RecordDetailDrawer.vue(new) — 记录详情抽屉src/agentkit/server/frontend/src/components/bitable/BitableGrid.vue— 行点击事件 → 打开抽屉src/agentkit/server/frontend/src/stores/bitable.ts—currentRecordstate +openRecordDetail/closeRecordDetailactionssrc/agentkit/server/frontend/src/api/bitable.ts— 复用现有queryRecords(按 record_id 查询)src/agentkit/server/frontend/e2e/bitable-record-drawer.spec.ts(new) — 抽屉 e2esrc/agentkit/server/frontend/src/helpers/recordDrawerUtils.ts(new) — 纯函数:字段值渲染格式化、attachment/image 缩略图 URL 生成
Approach:
RecordDetailDrawer.vue用 Ant Design Vuea-drawer,width绑定 design token(--bitable-drawer-width/--bitable-drawer-width-wide)- sticky header 显示记录标题字段值(首个 text 类型字段)
- body 按字段顺序纵向排列,每字段一行:
- label(字段名 + FieldTypeIcon)
- value(根据类型渲染:text/number/date 直接显示,select/multiselect 用 SelectDisplay chip,attachment 显示文件名列表,image 显示缩略图网格,formula 显示只读计算结果,lookup 显示关联表字段值)
- user-owned 字段可编辑(inline edit → upsert),agent-owned 字段只读
- 编辑提交调用 store
upsertRecord→ upsert 保留 agent 列(见 origin R2 验收标准) - Esc 关闭抽屉
Test scenarios:
- Given grid 行, When 用户点击行, Then 右侧抽屉展开显示所有字段(含 attachment/formula/lookup)
- Given 抽屉打开且字段为 attachment/image, When 渲染, Then 显示缩略图/预览(非原始 URL)
- Given 抽屉打开且字段为 formula, When 渲染, Then 显示计算结果(只读,不可编辑)
- Given 抽屉中编辑 user-owned 字段并提交, When 后端 upsert, Then agent 列不被覆盖
- Given 字段数 ≤ 10, When 抽屉渲染, Then 宽度 480px
- Given 字段数 > 10, When 抽屉渲染, Then 宽度 640px
- Given 抽屉打开, When 按 Esc, Then 抽屉关闭
Verification:
npm run typecheck通过- e2e
bitable-record-drawer.spec.ts通过 - upsert 保留 agent 列(现有 test_service.py 覆盖)
U4: R3 View Type Switcher
Goal: ViewSwitcher 支持选 grid/kanban/gallery/gantt/form,新建视图选类型(不再硬编码 grid)。未实现的以禁用态展示 + 标注"规划中"。
Files:
src/agentkit/server/frontend/src/components/bitable/ViewSwitcher.vue— "新建视图"改为 Dropdown + 5 类型选择src/agentkit/server/frontend/src/views/BitableFileDetailView.vue—handleCreateView接受view_type参数(不再硬编码 'grid')src/agentkit/server/frontend/src/stores/bitable.ts—createViewaction 传view_typesrc/agentkit/server/frontend/src/api/bitable.ts—createViewAPI 调用传view_typesrc/agentkit/server/frontend/e2e/bitable-view.spec.ts— extend: 类型选择场景src/agentkit/server/frontend/src/helpers/viewSwitcherUtils.ts(new) — 纯函数:视图类型元数据(label/icon/disabled/tooltip)
Approach:
viewSwitcherUtils.ts定义 5 种类型元数据:- grid: { label: '表格', icon: TableOutlined, disabled: false }
- kanban: { label: '看板', icon: AppstoreOutlined, disabled: true, tooltip: '规划中' }
- gallery: { label: '画廊', icon: PictureOutlined, disabled: true, tooltip: '规划中' }
- gantt: { label: '甘特', icon: BarChartOutlined, disabled: true, tooltip: '规划中' }
- form: { label: '表单', icon: FormOutlined, disabled: true, tooltip: '规划中' }
ViewSwitcher.vue的"新建视图"按钮改为a-dropdown,列出 5 类型,disabled 项设disabled+ tooltiphandleCreateView在BitableFileDetailView.vue接受viewType: ViewType参数,调用 storecreateView({ table_id, name, view_type })- store
createView调用 APIcreateView传view_type字段 - 后端
CreateViewRequest已支持view_type(line 181),无需后端改动
Test scenarios:
- Given ViewSwitcher, When 用户点击"新建视图", Then 显示 5 种类型选择(grid/kanban/gallery/gantt/form)
- Given kanban/gallery/gantt/form 未实现, When 渲染, Then 以禁用态展示 + 标注"规划中" tooltip
- Given 新建视图选 grid, When 创建, Then 调用 POST /views 传
view_type=grid(不再硬编码) - Given 禁用态类型, When 点击, Then 不触发创建 + 显示"规划中" tooltip
- Given 后端 CreateViewRequest, When 接收
view_type字段, Then 正确存储(现有后端已支持)
Verification:
npm run typecheck通过- e2e
bitable-view.spec.ts类型选择场景通过 - POST /views 请求体含
view_type字段
U5: R4 Grouping + Conditional Formatting
Goal: grid 视图内多字段分组(最多 3 字段)+ 条件格式规则自动着色(7 运算符)。分组与条件格式存入 View.config。
Files:
src/agentkit/server/frontend/src/components/bitable/ViewConfigPanel.vue— 新增分组 + 条件格式编辑器 tabsrc/agentkit/server/frontend/src/components/bitable/GroupingEditor.vue(new) — 分组编辑器(字段选择 + 排序 + 聚合)src/agentkit/server/frontend/src/components/bitable/ConditionalFormatEditor.vue(new) — 条件格式规则列表编辑器src/agentkit/server/frontend/src/components/bitable/BitableGrid.vue— 分组渲染 + 条件格式着色逻辑src/agentkit/server/frontend/src/stores/bitable.ts—updateViewConfigaction(PATCH /views config)src/agentkit/server/frontend/src/api/bitable.ts— 复用现有updateViewsrc/agentkit/server/frontend/e2e/bitable-grouping.spec.ts(new) — 分组 + 条件格式 e2esrc/agentkit/server/frontend/src/helpers/groupingRulesUtils.ts(new) — 纯函数:分组层级计算、聚合值计算、条件格式规则匹配tests/unit/bitable/test_grouping.py(new) — 后端 config 存储测试tests/unit/bitable/test_conditional_formatting.py(new) — 后端 config 存储测试
Approach:
- View.config schema(前端约定 + 后端 Pydantic 校验,见 KTD6):
{ group_by: [{ field_id: string, direction: 'asc' | 'desc' }], // 最多 3 项 conditional_formatting: [{ field_id: string, operator: 'equals' | 'not-equals' | 'contains' | 'is-empty' | 'greater-than' | 'less-than' | 'between', value: string, color_key: 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple' | 'gray' | 'neutral' }] } GroupingEditor.vue:字段多选(最多 3)+ 方向切换 + 拖拽排序层级ConditionalFormatEditor.vue:列表式规则编辑器,每行 = 字段 + 运算符 + 值 + 颜色(8 色 key 预设)+ 启用开关BitableGrid.vue分组渲染:按 group_by 字段层级嵌套,分组头显示字段值 + 计数 + number 字段聚合(SUM/AVG),默认展开,可折叠BitableGrid.vue条件格式着色:遍历 conditional_formatting 规则,首条匹配 wins,单元格背景色用var(colorKeyToCssVar(color_key))。组合态约定:分组 + 条件格式同时启用时,条件格式仅作用于数据单元格,分组头不着色(避免视觉冲突)- 提交时调用 store
updateViewConfig(viewId, { group_by, conditional_formatting })→ PATCH /views config - 后端 service 层用 Pydantic 校验 config 子结构(
GroupByItem/ConditionalFormatRule),拒绝非法 operator/color_key/超长 group_by(422)
Test scenarios:
- Given grid 视图, When 用户开启分组, Then 支持最多 3 字段分组(对标飞书/Twenty)
- Given 条件格式规则 operator=equals, When 单元格值匹配, Then 自动着色
- Given 两条规则匹配同一单元格, When 冲突, Then 首条规则优先(按用户排序)
- Given 着色, When 颜色来源审计, Then 全部来自 design token 调色板(8 色预设,无硬编码 hex)
- Given 分组头, When 渲染, Then 显示分组字段值 + 计数 + number 字段聚合(SUM/AVG)
- Given 多字段分组, When 渲染, Then 层级嵌套 + 可折叠/展开
- Given PATCH /views config, When 后端存储, Then config dict 含 group_by + conditional_formatting
- Given 7 运算符, When 测试 each, Then equals/not-equals/contains/is-empty/greater-than/less-than/between 全部正确匹配
Verification:
npm run typecheck通过- e2e
bitable-grouping.spec.ts通过 - pytest
test_grouping.py+test_conditional_formatting.py通过 - grep 着色 hex 零匹配(全部用 token)
Phase 3 — Agent Parity (depends on U4)
U6: R15a BitableTool 4 New Actions + DELETE /views Endpoint
Goal: BitableTool 从 6 动作扩展到 10 动作(新增 create_view/update_view/update_field/delete_view),后端新增 DELETE /views/{view_id} 端点,消除 agent 孤儿风险。
Files:
src/agentkit/server/routes/bitable.py— 新增DELETE /views/{view_id}端点(line ~660 后)src/agentkit/bitable/service.py— 新增delete_view(view_id)方法src/agentkit/bitable/repository.py— 新增delete_view(view_id)方法src/agentkit/tools/bitable_tool.py— 新增 4 个 async def:_create_view/_update_view/_update_field/_delete_view(line ~466 后)src/agentkit/server/frontend/src/api/bitable.ts— 新增deleteView(viewId)方法src/agentkit/server/frontend/src/components/bitable/ViewSwitcher.vue— 视图删除入口(调用 deleteView)tests/unit/bitable/test_bitable_tool.py— extend: 4 新动作测试tests/unit/bitable/test_routes.py— extend: DELETE /views 测试src/agentkit/server/frontend/e2e/bitable-agent-parity.spec.ts(new) — agent 对等 e2e
Approach:
- 后端
DELETE /views/{view_id}:- 路由:
@router.delete("/views/{view_id}", status_code=204) - 前置:通过 view_id 查 view → table_id →
_check_table_ownership(404-before-403) - 调用
service.delete_view(view_id)→repository.delete_view(view_id) - 返回 204 No Content
- 路由:
- BitableTool 4 新动作(复用现有
_call_api内部方法):_create_view(table_id, name, view_type, config)→ POST /tables/{table_id}/views_update_view(view_id, name, config)→ PATCH /views/{view_id}_update_field(field_id, name, type, config)→ PATCH /fields/{field_id}_delete_view(view_id)→ DELETE /views/{view_id}
- 动作注册:在 BitableTool 的
_ACTIONS字典(或等价注册机制)添加 4 项,总数 6→10 - 前端
api/bitable.ts新增deleteView(viewId)调用 DELETE /views/{view_id} ViewSwitcher.vue视图 tab 添加删除按钮(dropdown 菜单或右键),调用deleteView+ 二次确认
Test scenarios:
- Given BitableTool, When 调用
create_view/update_view/update_field/delete_view, Then 4 个新动作全部可用(6→10 动作) - Given 视图列表, When 用户点击删除, Then 调用 DELETE /views/{id} + 二次确认
- Given DELETE /views/{view_id}, When 视图不存在, Then 返回 404
- Given DELETE /views/{view_id}, When 无所有权, Then 返回 404(非 403,existence disclosure 防护)
- Given R3/R4 配置变更, When agent 调用
create_view/update_view传type/group_by/conditional_formatting, Then 配置成功写入(与 REST PATCH /views 等价) - Given 字段配置, When agent 调用
update_field, Then 与 PATCH /fields/{id} 等价 - Given BitableTool 动作清单, When 审计, Then 10 个动作覆盖 28 端点中的核心 CRUD(create_table/import_excel/import_database/collect_api/upsert_records/query_records/create_view/update_view/update_field/delete_view)
- Given 内部 token 认证, When agent 调用 BitableTool, Then X-Internal-Token 透传成功
Verification:
ruff check src/ && ruff format src/通过- pytest
test_bitable_tool.py+test_routes.py通过 - e2e
bitable-agent-parity.spec.ts通过 - BitableTool 动作数 10(验证
execute()内 handlers dict keys +input_schema.properties.action.enum均含 10 项)
Risks & Dependencies
Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| vxe-table 4 主题定制深度不足,token 覆盖不到所有组件样式 | 中 | 中 | U1 先做 token 映射 PoC,验证 vxe-table CSS 变量可覆盖;不行则用 wrapper 组件 + scoped CSS |
| 条件格式 7 运算符在 vxe-table 行渲染中性能差(大数据量) | 低 | 中 | P0 假设 <10k 行,首版不做虚拟滚动优化;若 >10k 行降级为禁用条件格式提示 |
| R4 分组渲染与 vxe-table 内置分组冲突 | 中 | 高 | 不用 vxe-table 内置分组,自建分组 wrapper 层(数据分桶 + 折叠 UI),grid 只渲染当前分组数据 |
| BitableTool 4 新动作的 X-Internal-Token 透传在 agent 编排场景失效 | 低 | 高 | U6 测试契约覆盖 agent 编排场景(redis 标记);复用现有 6 动作的认证模式 |
| design token 与 Ant Design Vue 4 ConfigProvider token 冲突 | 低 | 低 | bitable-tokens.css 用 --bitable-* 前缀,不覆盖 Ant Design 全局 token |
Dependencies
- U1 → U2/U3/U4/U5: Phase 2 所有 UI 工作依赖 U1 token 系统 + vxe-table 依赖声明
- U4 → U6: U6 的
create_view/update_view动作需与 U4 的 view_type 一致 - U5 → U6: U6 的
update_view动作需与 U5 的 group_by/conditional_formatting config 结构一致(Phase 2 开始时冻结 config schema 契约,U6 可并行实现) - 外部依赖: vxe-table 4 + vxe-pc-ui(版本与主仓 node_modules 对齐)、Ant Design Vue 4 Outlined 图标
- 既有约束: AGENTS.md 规则(no-emoji、Pydantic v2、no-any、pytest asyncio_mode=auto)、ruff check/format(py311,行宽 100)
System-Wide Impact
后端改动
- 新增端点:
DELETE /api/v1/bitable/views/{view_id}(U6)—— 第 29 个端点 - service/repository: 新增
delete_view方法(U6) - 无 schema 迁移: View.config 已是
dict[str, Any],group_by/conditional_formatting 作为 config key 存储,无需 V2→V3 迁移 - 无 FieldType 扩展: P0 不引入新字段类型(R8 延后 P1)
前端改动
- 新组件:
FieldTypeIcon/InlineFieldConfigurator/RecordDetailDrawer/GroupingEditor/ConditionalFormatEditor/LoadingState/ErrorState(7 个) - 新 composable:
useResponsiveBreakpoint - 新 helpers:
fieldRenderUtils/recordDrawerUtils/viewSwitcherUtils/groupingRulesUtils(4 个纯函数模块) - 新样式:
bitable-tokens.css(design token 层) - 改动组件:
BitableGrid/ColumnHeaderMenu/ViewSwitcher/ViewConfigPanel/SelectDisplay/FieldManagePanel+ 2 个 view(token 化 + 功能集成) - package.json: 显式声明 vxe-table + vxe-pc-ui
Agent 对等影响
- BitableTool 动作数 6→10,覆盖视图 CRUD + 字段更新
- agent 可通过 BitableTool 完成与人类等价的视图/字段操作(闭合 R15a 缺口)
- 为 R15b(NL→表 agent 编排)铺路:agent 可编排 create_table + create_field + create_view
测试影响
- 新增后端测试:
test_grouping.py/test_conditional_formatting.py+ extendtest_bitable_tool.py/test_routes.py - 新增前端 e2e:
bitable-record-drawer.spec.ts/bitable-grouping.spec.ts/bitable-agent-parity.spec.ts - 新增前端 helpers: 4 个纯函数模块(vitest 测试)
Test Strategy Summary
| U-ID | 后端单元测试 | 前端单元测试 | e2e 测试 | 集成标记 |
|---|---|---|---|---|
| U1 (R5) | - | helpers/designTokenAudit.ts(grep token 使用) |
bitable-view.spec.ts(visual regression) |
- |
| U2 (R1) | test_routes.py(PATCH /fields/{id}) |
helpers/fieldRenderUtils.ts |
bitable-field-ops.spec.ts(extend) |
- |
| U3 (R2) | test_service.py(upsert 保留 user 列) |
helpers/recordDrawerUtils.ts |
bitable-record-drawer.spec.ts(new) |
- |
| U4 (R3) | test_routes.py(POST /views type 参数) |
helpers/viewSwitcherUtils.ts |
bitable-view.spec.ts(extend) |
- |
| U5 (R4) | test_grouping.py + test_conditional_formatting.py(new) |
helpers/groupingRulesUtils.ts |
bitable-grouping.spec.ts(new) |
- |
| U6 (R15a) | test_bitable_tool.py(4 new actions)+ test_routes.py(DELETE /views) |
- | bitable-agent-parity.spec.ts(new) |
redis(notify_callback) |
测试约定: 后端 pytest(asyncio_mode=auto,标记 integration/redis/postgres);前端 vitest(纯函数抽 helpers/,不用 @vue/test-utils);e2e Playwright。
Agent 对等测试契约: U6 交付时附 BitableTool 4 新动作的契约测试,验证 agent 能完成与人类等价的视图/字段操作。
横切测试(适用所有 U-ID):
- WCAG AA 可访问性:axe-core 扫描键盘导航 + 对比度 + ARIA role
- 空状态:无字段/无记录/无视图的空状态文案
- design token 审计:grep 硬编码 hex 零匹配
Execution Posture
Posture signal: 标准 TDD(后端 pytest first,前端 helpers vitest first)。后端 DELETE /views 端点用 TDD(先写 test_routes.py 失败用例,再实现)。前端组件不强求 TDD(Vue 组件 TDD 投入产出比低),但纯函数 helpers 用 TDD。
Sequencing:
- Phase 1 (U1) —— token 基座,可独立验证
- Phase 2 (U2/U3/U4/U5) —— 4 个 U-ID 可并行(共享 U1 token 但互不依赖)
- Phase 3 (U6) —— 依赖 U4 的 view_type + U5 的 config 结构
Branch: feat/bitable-enhancement(当前分支)
Open Questions (Deferred to Execution)
以下来自 origin 文档 Outstanding Questions + ce-doc-review manual findings,P0 范围内可在实现时决策,P1/P2 延后:
P0 范围内(实现时决策)
- vxe-table 容量上限: P0 假设 <10k 行,若实测 >10k 行性能差则降级(禁用条件格式提示或虚拟滚动延后 P1)
- 条件格式规则构建器细节: 列表式(KTD7),预览实时着色当前 grid 数据样本
- 分组交互细节: 默认展开、首字段优先排序、分组头显示聚合行(KTD8)
- R2 抽屉宽度: 480px(≤10 字段)/ 640px(>10 字段)(KTD4)
- 禁用态视图路线图: tooltip "规划中"(hardcode,不读 config,P0 不做投票机制)
- 响应式断点: 768/1024/1440(U1 useResponsiveBreakpoint)
- 加载/错误状态: 骨架屏(LoadingState)+ 行内提示(ErrorState),U1 统一组件
延后 P1/P2(不在本 plan 范围)
- user 字段用户模型 (P1, R8): user_id 解析、跨表用户名查询
- C 先行优先级策略实证依据 (P1, product): 用户访谈/A-B 数据
- 并发编辑 UX (P1): agent + 用户双写入冲突解决、optimistic locking
- schema V3 双向关联回滚策略 (P3, R8): drop column vs 保留为 text
- R13 仪表盘图表库 buy-vs-build (P2): ECharts/Chart.js/AntV G2 选型
- R6 看板组件选型 (P1): 自建 vs 现成库
- R9 公式库第三方 parser (P1): 是否引入
From 2026-07-03 ce-plan Phase 5.3 headless ce-doc-review
ce-doc-review(5 reviewers)headless pass 的 manual findings,需实现时决策:
-
条件格式 WCAG 1.4.1 色盲可感知性 — U5 KTD7 (P1, design-lens, confidence 80)
条件格式仅靠颜色区分,8 色预设中红/绿/橙/黄对色盲用户不可区分。WCAG 1.4.1 要求颜色不能作为唯一区分手段。需实现时决策:(1) 规则增加可选
decoration字段(下划线/加粗/斜体/图标);(2) 单元格着色同时附加左侧色条 + 图标。最小方案:规则编辑器默认勾选"同时加粗文本"。 -
RecordDetailDrawer 加载/错误/404 状态 — U3 (P1, design-lens, confidence 78)
抽屉异步按 record_id 查询,需规定网络失败/记录被并发删除/权限丢失时的状态。U1 建了 LoadingState/ErrorState,U3 需强制引用:(1) 打开中显示骨架屏;(2) 查询 404 显示"记录不存在或已被删除" + 关闭按钮;(3) 查询 5xx 显示 ErrorState + 重试按钮。
-
新编辑器组件空状态 — U3/U5 (P2, design-lens, confidence 72)
RecordDetailDrawer 打开但记录 0 字段、GroupingEditor 可选字段 <1、ConditionalFormatEditor 0 条规则时的引导文案需定义。实现时补各组件空状态文案。
-
保存中按钮状态 — U2/U4 (P2, design-lens, confidence 70)
PATCH /fields、POST /views 提交期间按钮需 disabled + loading 图标,防重复点击。实现时在 U2 InlineFieldConfigurator + U4 ViewSwitcher 补 loading 态。
-
delete_view 硬删/软删 + 最后视图保护 — U6 (P2, security-lens, confidence 68)
硬删不可逆,agent 误调
_delete_view即数据丢失。需决策:(1) soft delete(deleted_at 标记)+ 可恢复;(2) hard delete 但禁止删除表的最后一个视图(返回 409 Conflict)。实现时在 U6 Test scenarios 补对应用例。 -
分组 wrapper 与 vxe-table 交互限制 — U5 Risks (P3, feasibility, confidence 70)
自建分组 wrapper 层会丢失跨分组多选、滚动位置、vxe-table 内置筛选与分组交互。P0 已知限制:不支持跨分组多选、折叠分组时虚拟滚动位置重置。后续 P1 优化。
-
U2 lookup 类型缺口 — U2 (P3, coherence, confidence 60)
U2 复用 FieldConfigForm 的 8 类型,lookup 字段内联编辑延后 R8(P1)。已知限制,不阻塞 P0。
-
vxe-pc-ui 依赖必要性 — U1 KTD2 (P2, feasibility, confidence 80)
KTD2 声明 vxe-pc-ui 但代码库仅 import vxe-table。需 U1 实施时验证 vxe-pc-ui 是否为 vxe-table v4 的 peer dependency。若非 peer dep 则移除,避免多余依赖。
Origin Traceability
本 plan 承接 origin 文档(docs/brainstorms/2026-07-03-bitable-comparative-evaluation-requirements.md)的 P0 范围:
| R-ID | origin 章节 | 本 plan U-ID | 交付阶段 |
|---|---|---|---|
| R5 | Priority Recommendations P0 | U1 | Phase 1 |
| R1 | Priority Recommendations P0 | U2 | Phase 2 |
| R2 | Priority Recommendations P0 | U3 | Phase 2 |
| R3 | Priority Recommendations P0 | U4 | Phase 2 |
| R4 | Priority Recommendations P0 | U5 | Phase 2 |
| R15a | Priority Recommendations P0 (agent 对等最高优先级子项) | U6 | Phase 3 |
origin 文档的 Acceptance Criteria (P0) + 横切验收标准(WCAG AA + 空状态)已映射到各 U-ID 的 Test scenarios。Agent 对等评估方法的 4 个新动作(create_view/update_view/update_field/delete_view)全部在 U6 实现。