fischer-agentkit/docs/plans/2026-07-03-001-feat-bitable...

522 lines
38 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "feat: Bitable P0 UX Polish + Agent Parity"
type: feat
date: 2026-07-03
origin: 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 技能 / 定时采集 UIB 线,独立推进)
- 并发编辑 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` "新建视图"按钮改为 Dropdown5 种类型列出未实现的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 不改 enumagent 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) 后端+agentPhase 2 4 U-ID 可并行共享 U1 token 但互不依赖Phase 3 依赖 Phase 2 U4 + U5create_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.css
- `src/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 断点 composable
- `src/agentkit/server/frontend/src/components/bitable/LoadingState.vue` (new) 统一加载态骨架屏
- `src/agentkit/server/frontend/src/components/bitable/ErrorState.vue` (new) 统一错误态行内提示
**Approach:**
1. `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`
2. 逐组件 grep 硬编码 hex 颜色`#[0-9a-fA-F]{3,6}`替换为 token var()
3. `FieldTypeIcon.vue` 映射 9 种类型到 Ant Design Outlined 图标textFileTextOutlined, numberNumberOutlined, dateCalendarOutlined, selectTagOutlined, multiselectTagsOutlined, attachmentPaperClipOutlined, imagePictureOutlined, formulaFunctionOutlined, lookupLinkOutlined
4. `SelectDisplay.vue` chip 配色从 8 token 调色板取色每色确保 4.5:1 对比度
5. `useResponsiveBreakpoint.ts` 暴露 `isMobile`/`isTablet`/`isDesktop` 响应式断点768/1024/1440
6. `LoadingState.vue` + `ErrorState.vue` 作为 bitable 内部统一加载/错误态组件**P0 提升理由**U3 RecordDetailDrawer 异步加载需骨架屏避免白屏U2/U4 提交需 loading 态防重复点击U2-U5 Test scenarios 强制引用这两个组件证明确实被消费
7. `useResponsiveBreakpoint.ts` U3抽屉 `isMobile` 时全屏覆盖+ U5ViewConfigPanel `isMobile` 时改底部抽屉显式消费
8. **R3/R4 后端假设验证**Phase 1 退出条件grep + pytest round-trip 验证 `CreateViewRequest` 接受 `view_type` `UpdateViewRequest` 接受任意 config dict若验证失败Phase 2 需补后端工作项
**Test scenarios:**
1. Given bitable-tokens.css, When grep `#[0-9a-fA-F]{3,6}` in bitable components, Then 零硬编码 hex全部用 var()
2. Given FieldTypeIcon, When 渲染 9 种字段类型, Then 各有对应 Ant Design Outlined 图标
3. Given SelectDisplay chip, When axe-core 扫描, Then 所有关键文本对比度 4.5:1WCAG AA
4. Given package.json, When `npm ls vxe-table vxe-pc-ui`, Then 两者均为显式声明依赖 hoisting
5. Given useResponsiveBreakpoint, When viewport < 768px, Then `isMobile === true`
6. Given LoadingState, When 异步加载, Then 显示骨架屏 spinner
7. 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 集成 InlineFieldConfigurator
- `src/agentkit/server/frontend/src/components/bitable/FieldManagePanel.vue` 保留添加"批量管理"定位提示
- `src/agentkit/server/frontend/src/stores/bitable.ts` 复用现有 `updateField` action
- `src/agentkit/server/frontend/e2e/bitable-field-ops.spec.ts` extend: 内联编辑场景
- `src/agentkit/server/frontend/src/helpers/fieldRenderUtils.ts` (new) 纯函数字段类型校验类型转换兼容性检查
**Approach:**
1. `InlineFieldConfigurator.vue` `FieldConfigForm.vue` 抽取核心逻辑类型切换选项管理校验 inline panel 形式渲染非抽屉
2. `ColumnHeaderMenu.vue` "编辑"项改为 toggle InlineFieldConfigurator 在列头下方展开
3. 类型变更提交前调用 `fieldRenderUtils.checkTypeCompatibility(oldType, newType, existingValues)` —— 若现有值不可转换则显示警告 + 阻止提交
4. 重命名/选项管理直接调用 store `updateField(fieldId, patch)` PATCH /fields/{id}
5. 键盘可达Tab 到列头菜单 Enter 展开 Tab 字段间切换 Esc 关闭
**Test scenarios:**
1. Given select 字段有 3 选项, When 点击列头菜单"编辑", Then InlineFieldConfigurator 内联展开不跳右侧抽屉
2. Given 字段名变更提交, When PATCH /fields/{id}, Then 返回 200 + grid 1 帧内重渲染新标签
3. Given 字段类型 text number, When 现有记录值不可转换, Then 显示警告 + 阻止提交
4. Given select 选项管理, When 新增/删除/重命名选项, Then 内联完成 + 提交后 chip 更新
5. Given 键盘导航, When Tab 到列头菜单 Enter, Then 展开 InlineFieldConfigurator + 焦点在首字段
6. 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``currentRecord` state + `openRecordDetail`/`closeRecordDetail` actions
- `src/agentkit/server/frontend/src/api/bitable.ts` — 复用现有 `queryRecords`(按 record_id 查询)
- `src/agentkit/server/frontend/e2e/bitable-record-drawer.spec.ts` (new) — 抽屉 e2e
- `src/agentkit/server/frontend/src/helpers/recordDrawerUtils.ts` (new) — 纯函数字段值渲染格式化、attachment/image 缩略图 URL 生成
**Approach:**
1. `RecordDetailDrawer.vue` 用 Ant Design Vue `a-drawer``width` 绑定 design token`--bitable-drawer-width` / `--bitable-drawer-width-wide`
2. sticky header 显示记录标题字段值(首个 text 类型字段)
3. body 按字段顺序纵向排列,每字段一行:
- label字段名 + FieldTypeIcon
- value根据类型渲染text/number/date 直接显示select/multiselect 用 SelectDisplay chipattachment 显示文件名列表image 显示缩略图网格formula 显示只读计算结果lookup 显示关联表字段值)
4. user-owned 字段可编辑inline edit → upsertagent-owned 字段只读
5. 编辑提交调用 store `upsertRecord` → upsert 保留 agent 列(见 origin R2 验收标准)
6. Esc 关闭抽屉
**Test scenarios:**
1. Given grid 行, When 用户点击行, Then 右侧抽屉展开显示所有字段(含 attachment/formula/lookup
2. Given 抽屉打开且字段为 attachment/image, When 渲染, Then 显示缩略图/预览(非原始 URL
3. Given 抽屉打开且字段为 formula, When 渲染, Then 显示计算结果(只读,不可编辑)
4. Given 抽屉中编辑 user-owned 字段并提交, When 后端 upsert, Then agent 列不被覆盖
5. Given 字段数 ≤ 10, When 抽屉渲染, Then 宽度 480px
6. Given 字段数 > 10, When 抽屉渲染, Then 宽度 640px
7. 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``createView` action 传 `view_type`
- `src/agentkit/server/frontend/src/api/bitable.ts``createView` API 调用传 `view_type`
- `src/agentkit/server/frontend/e2e/bitable-view.spec.ts` — extend: 类型选择场景
- `src/agentkit/server/frontend/src/helpers/viewSwitcherUtils.ts` (new) — 纯函数视图类型元数据label/icon/disabled/tooltip
**Approach:**
1. `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: '规划中' }
2. `ViewSwitcher.vue` 的"新建视图"按钮改为 `a-dropdown`,列出 5 类型disabled 项设 `disabled` + tooltip
3. `handleCreateView``BitableFileDetailView.vue` 接受 `viewType: ViewType` 参数,调用 store `createView({ table_id, name, view_type })`
4. store `createView` 调用 API `createView``view_type` 字段
5. 后端 `CreateViewRequest` 已支持 `view_type`line 181无需后端改动
**Test scenarios:**
1. Given ViewSwitcher, When 用户点击"新建视图", Then 显示 5 种类型选择grid/kanban/gallery/gantt/form
2. Given kanban/gallery/gantt/form 未实现, When 渲染, Then 以禁用态展示 + 标注"规划中" tooltip
3. Given 新建视图选 grid, When 创建, Then 调用 POST /views 传 `view_type=grid`(不再硬编码)
4. Given 禁用态类型, When 点击, Then 不触发创建 + 显示"规划中" tooltip
5. 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` — 新增分组 + 条件格式编辑器 tab
- `src/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``updateViewConfig` actionPATCH /views config
- `src/agentkit/server/frontend/src/api/bitable.ts` — 复用现有 `updateView`
- `src/agentkit/server/frontend/e2e/bitable-grouping.spec.ts` (new) — 分组 + 条件格式 e2e
- `src/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:**
1. View.config schema前端约定 + 后端 Pydantic 校验,见 KTD6
```typescript
{
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'
}]
}
```
2. `GroupingEditor.vue`:字段多选(最多 3+ 方向切换 + 拖拽排序层级
3. `ConditionalFormatEditor.vue`:列表式规则编辑器,每行 = 字段 + 运算符 + 值 + 颜色8 色 key 预设)+ 启用开关
4. `BitableGrid.vue` 分组渲染:按 group_by 字段层级嵌套,分组头显示字段值 + 计数 + number 字段聚合SUM/AVG默认展开可折叠
5. `BitableGrid.vue` 条件格式着色:遍历 conditional_formatting 规则,首条匹配 wins单元格背景色用 `var(colorKeyToCssVar(color_key))`。**组合态约定**:分组 + 条件格式同时启用时,条件格式仅作用于数据单元格,分组头不着色(避免视觉冲突)
6. 提交时调用 store `updateViewConfig(viewId, { group_by, conditional_formatting })` → PATCH /views config
7. 后端 service 层用 Pydantic 校验 config 子结构(`GroupByItem`/`ConditionalFormatRule`),拒绝非法 operator/color_key/超长 group_by422
**Test scenarios:**
1. Given grid 视图, When 用户开启分组, Then 支持最多 3 字段分组(对标飞书/Twenty
2. Given 条件格式规则 operator=equals, When 单元格值匹配, Then 自动着色
3. Given 两条规则匹配同一单元格, When 冲突, Then 首条规则优先(按用户排序)
4. Given 着色, When 颜色来源审计, Then 全部来自 design token 调色板8 色预设,无硬编码 hex
5. Given 分组头, When 渲染, Then 显示分组字段值 + 计数 + number 字段聚合SUM/AVG
6. Given 多字段分组, When 渲染, Then 层级嵌套 + 可折叠/展开
7. Given PATCH /views config, When 后端存储, Then config dict 含 group_by + conditional_formatting
8. 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:**
1. 后端 `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
2. 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}
3. 动作注册:在 BitableTool 的 `_ACTIONS` 字典(或等价注册机制)添加 4 项,总数 6→10
4. 前端 `api/bitable.ts` 新增 `deleteView(viewId)` 调用 DELETE /views/{view_id}
5. `ViewSwitcher.vue` 视图 tab 添加删除按钮dropdown 菜单或右键),调用 `deleteView` + 二次确认
**Test scenarios:**
1. Given BitableTool, When 调用 `create_view`/`update_view`/`update_field`/`delete_view`, Then 4 个新动作全部可用6→10 动作)
2. Given 视图列表, When 用户点击删除, Then 调用 DELETE /views/{id} + 二次确认
3. Given DELETE /views/{view_id}, When 视图不存在, Then 返回 404
4. Given DELETE /views/{view_id}, When 无所有权, Then 返回 404非 403existence disclosure 防护)
5. Given R3/R4 配置变更, When agent 调用 `create_view`/`update_view` 传 `type`/`group_by`/`conditional_formatting`, Then 配置成功写入(与 REST PATCH /views 等价)
6. Given 字段配置, When agent 调用 `update_field`, Then 与 PATCH /fields/{id} 等价
7. Given BitableTool 动作清单, When 审计, Then 10 个动作覆盖 28 端点中的核心 CRUDcreate_table/import_excel/import_database/collect_api/upsert_records/query_records/create_view/update_view/update_field/delete_view
8. 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 层(数据分桶 + 折叠 UIgrid 只渲染当前分组数据 |
| 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/formatpy311行宽 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 个 viewtoken 化 + 功能集成)
- **package.json**: 显式声明 vxe-table + vxe-pc-ui
### Agent 对等影响
- BitableTool 动作数 6→10覆盖视图 CRUD + 字段更新
- agent 可通过 BitableTool 完成与人类等价的视图/字段操作(闭合 R15a 缺口)
- 为 R15bNL→表 agent 编排铺路agent 可编排 create_table + create_field + create_view
### 测试影响
- 新增后端测试: `test_grouping.py`/`test_conditional_formatting.py` + extend `test_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 | redisnotify_callback |
**测试约定**: 后端 pytestasyncio_mode=auto标记 integration/redis/postgres前端 vitest纯函数抽 helpers/,不用 @vue/test-utilse2e 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 失败用例,再实现)。前端组件不强求 TDDVue 组件 TDD 投入产出比低),但纯函数 helpers 用 TDD。
**Sequencing**:
1. Phase 1 (U1) —— token 基座,可独立验证
2. Phase 2 (U2/U3/U4/U5) —— 4 个 U-ID 可并行(共享 U1 token 但互不依赖)
3. Phase 3 (U6) —— 依赖 U4 的 view_type + U5 的 config 结构
**Branch**: `feat/bitable-enhancement`(当前分支)
---
## Open Questions (Deferred to Execution)
以下来自 origin 文档 Outstanding Questions + ce-doc-review manual findingsP0 范围内可在实现时决策P1/P2 延后:
### P0 范围内(实现时决策)
- **vxe-table 容量上限**: P0 假设 <10k 若实测 >10k 行性能差则降级(禁用条件格式提示或虚拟滚动延后 P1
- **条件格式规则构建器细节**: 列表式KTD7预览实时着色当前 grid 数据样本
- **分组交互细节**: 默认展开、首字段优先排序、分组头显示聚合行KTD8
- **R2 抽屉宽度**: 480px≤10 字段)/ 640px>10 字段KTD4
- **禁用态视图路线图**: tooltip "规划中"hardcode不读 configP0 不做投票机制)
- **响应式断点**: 768/1024/1440U1 useResponsiveBreakpoint
- **加载/错误状态**: 骨架屏LoadingState+ 行内提示ErrorStateU1 统一组件
### 延后 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-review5 reviewersheadless 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/ErrorStateU3 需强制引用:(1) 打开中显示骨架屏;(2) 查询 404 显示"记录不存在或已被删除" + 关闭按钮;(3) 查询 5xx 显示 ErrorState + 重试按钮。
- **新编辑器组件空状态** — U3/U5 (P2, design-lens, confidence 72)
RecordDetailDrawer 打开但记录 0 字段、GroupingEditor 可选字段 <1ConditionalFormatEditor 0 条规则时的引导文案需定义实现时补各组件空状态文案
- **保存中按钮状态** U2/U4 (P2, design-lens, confidence 70)
PATCH /fieldsPOST /views 提交期间按钮需 disabled + loading 图标防重复点击实现时在 U2 InlineFieldConfigurator + U4 ViewSwitcher loading
- **delete_view 硬删/软删 + 最后视图保护** U6 (P2, security-lens, confidence 68)
硬删不可逆agent 误调 `_delete_view` 即数据丢失需决策(1) soft deletedeleted_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 字段内联编辑延后 R8P1)。已知限制不阻塞 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 scenariosAgent 对等评估方法的 4 个新动作create_view/update_view/update_field/delete_view全部在 U6 实现