From 96ccca3d87e967f4c45086c182842166dd88ec12 Mon Sep 17 00:00:00 2001 From: chiguyong Date: Fri, 3 Jul 2026 13:49:57 +0800 Subject: [PATCH] docs(bitable-p0): add implementation plan for P0 UX polish + agent parity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ce-plan Deep plan (6 Implementation Units, 3 delivery phases): - Phase 1: U1 R5 design token system + vxe-table dependency declaration - Phase 2: U2-U5 R1-R4 frontend UX (inline field config, record drawer, view type switcher, grouping + conditional formatting) - Phase 3: U6 R15a BitableTool 4 new actions + DELETE /views endpoint 11 KTDs covering: CSS token layer, vxe-table ghost dependency fix, inline field configurator (hybrid vxe-table slot + custom component), record detail drawer (single column 480/640px), view type dropdown with disabled states, grouping + conditional format in View.config with backend Pydantic validation, BitableTool action registration (handlers dict + input_schema enum), X-Internal-Token ownership semantics, 3-phase delivery with config schema freeze for parallel U6. Phase 5.3 headless ce-doc-review (5 reviewers, 14 findings): - Applied 2 safe_auto (U6 verification method, U5→U6 dependency) - Applied 2 gated_auto (input_schema enum step, color_token→color_key) - Applied 5 P1 manual fixes (backend config validation, X-Internal-Token ownership, grouping+CF combo state, LoadingState/ErrorState justification, R3/R4 backend assumption verification) - 8 P2/P3 manual findings appended to Open Questions Origin: docs/brainstorms/2026-07-03-bitable-comparative-evaluation-requirements.md --- ...eat-bitable-p0-ux-and-agent-parity-plan.md | 521 ++++++++++++++++++ 1 file changed, 521 insertions(+) create mode 100644 docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md diff --git a/docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md b/docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md new file mode 100644 index 0000000..cd4b367 --- /dev/null +++ b/docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md @@ -0,0 +1,521 @@ +--- +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 技能 / 定时采集 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.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 图标:text→FileTextOutlined, number→NumberOutlined, date→CalendarOutlined, select→TagOutlined, multiselect→TagsOutlined, attachment→PaperClipOutlined, image→PictureOutlined, formula→FunctionOutlined, lookup→LinkOutlined +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` 时全屏覆盖)+ U5(ViewConfigPanel `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:1(WCAG 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 chip,attachment 显示文件名列表,image 显示缩略图网格,formula 显示只读计算结果,lookup 显示关联表字段值) +4. user-owned 字段可编辑(inline edit → upsert),agent-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` action(PATCH /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_by(422) + +**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(非 403,existence 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 端点中的核心 CRUD(create_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 层(数据分桶 + 折叠 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` + 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) | 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**: +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 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 实现。