---
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 实现。
