--- title: "feat: Bitable UI Completeness — Stage 1 (File Layer + Default Fields + In-Table Field Ops + Select Editor)" type: feat date: 2026-06-29 origin: docs/brainstorms/2026-06-29-bitable-ui-completeness-requirements.md --- # Bitable UI Completeness — Stage 1 ## Summary 引入"多维表格文件"作为最上级容器,重构 `文件 → 数据表 → 字段/记录` 三层骨架,补齐新建表默认字段、表内列头字段操作、select 下拉编辑器。这是 4 阶段完善计划的第一阶段,聚焦让 bitable 从"功能残缺"到"用户主动建表体验可用"。 ## Problem Frame bitable 后端 v1 齐备但前端产品形态残缺(见 origin 文档 Problem Frame)。三类核心缺口导致无法正常使用:缺最上级文件容器、新建表无默认字段、表内不能直接管理字段。此外 select 编辑器仍是文本输入。Stage 1 解决这四件事,让用户能完成"建文件 → 建表 → 表内配字段 → 填数据"的基本闭环。 --- ## Requirements ### 后端文件层 R1. 引入 BitableFile 实体作为最上级容器。Table 通过 `file_id` 外键归属文件。文件自有元数据(name/icon/description/owner_user_id),支持 CRUD。文件级 ownership 检查复用现有 `_check_table_ownership` 模式(见 `docs/solutions/architecture-patterns/bitable-companion-service-security-reliability-patterns.md` 模式 4)。 R2. 新建数据表时自动创建 5 个默认字段:标题(text,owner=user)、状态(select,预设"未开始/进行中/已完成"3 选项,owner=user)、日期(date,owner=user)、创建人(text,owner=agent)、创建时间(datetime,owner=agent)。默认字段遵循 Field Ownership 模型——agent-owned 字段由系统在记录创建时自动填充。 ### 前端文件层与导航 R3. 三层导航层级:`文件列表 → 文件详情(含多张表)→ 表内(字段/记录/视图)`。文件列表是 `/bitable` 的新默认页,显示用户拥有的所有文件卡片。点文件卡片进入文件详情,左侧 sidebar 显示该文件的表列表,右侧显示选中表的 grid。 R4. 文件 CRUD UI:文件列表页有"新建文件"按钮(弹窗输入名称/选图标/选描述);文件卡片支持重命名、删除(带确认);文件详情 topbar 显示文件名与返回按钮。 ### 前端表内体验 R5. 表内字段操作走列头下拉菜单。点 grid 列头弹出菜单:重命名、修改字段类型、隐藏、删除。不再依赖右侧"字段管理"弹层作为唯一入口(FieldManagePanel 保留作为批量管理入口)。删除字段需二次确认(会删除该列所有数据)。 R6. select/multiselect 字段编辑器使用下拉选项(带颜色标签),替换当前 `VxeInput` 文本输入。选项从字段 config.options 读取,编辑时显示为可搜索下拉,选中后写入 record value。 --- ## Key Technical Decisions **KTD1. BitableFile 作为独立实体,Table 加 `file_id` 外键。** 不采用"轻量分组"方案(Table 加 group_name 字段)。理由:文件需要自己的 ownership/CRUD/元数据,轻量分组会在后续权限/共享能力上撞天花板。当前 schema V1 用 `create_all` 模式,无现有数据需迁移,引入文件层是干净的新增(origin KD2)。 **KTD2. Schema 升级用 `_SCHEMA_VERSION` 递增 + 启动时迁移,不引入 alembic。** 现有 `src/agentkit/bitable/db.py` 已有 `_SCHEMA_VERSION = 1` 与预留的 `_apply_v2_migration` 注释位。文件层引入升级到 V2:创建 `bitable_files` 表,给 `bitable_tables` 加 `file_id` 列。启动时 `init_db()` 检测版本并执行迁移。保持与现有模式一致,不为单次迁移引入 alembic 全套。 **KTD3. 默认字段在 service 层 `create_table` 内创建,不在数据库层用 trigger。** service 层 `create_table` 在创建 Table 后立即批量创建 5 个默认 Field。理由:可测试、可复用、不依赖数据库特定语法。agent-owned 字段(创建人/创建时间)在 `create_record` 时由 service 自动填充当前 user_id 与 timestamp。 **KTD4. 前端路由重构为嵌套结构。** `/bitable` → 文件列表;`/bitable/:fileId` → 文件详情(含表列表 sidebar);`/bitable/:fileId/:tableId` → 表内。当前 `BitableView.vue` 拆为 `BitableFileListView.vue` + `BitableFileDetailView.vue`。表内视图复用现有 `BitableGrid` + `ViewSwitcher` 等组件。 **KTD5. 列头下拉菜单用 vxe-table 的 header slot + Ant Design Vue Dropdown。** 不自建列头组件。vxe-table 支持自定义 header slot,在 slot 内渲染列名 + 下拉触发图标。菜单项调用现有 store 的 `updateField`/`deleteField` action(需新增)。 **KTD6. select 编辑器用 vxe-table 自定义编辑器 + Ant Design Vue Select。** 注册一个 `SelectCellEditor` 自定义编辑器,内部用 `a-select`(带搜索、颜色标签)。选项从 `field.config.options` 读取。multiselect 用 `mode="multiple"`。替换 `BitableGrid.vue` 中 select/multiselect 的 `editRender: { name: 'VxeInput' }`。 --- ## High-Level Technical Design ### 三层导航数据流 ```mermaid flowchart TB A[/bitable 文件列表] --> B[选文件卡片] B --> C[/bitable/:fileId 文件详情] C --> D[左侧 sidebar 选表] D --> E[/bitable/:fileId/:tableId 表内] E --> F[BitableGrid + ViewSwitcher] C --> G[新建表 - 自带默认字段] ``` ### 后端文件层 schema 变更 ```mermaid flowchart LR F[bitable_files] --1:N--> T[bitable_tables] T --1:N--> FL[bitable_fields] T --1:N--> R[bitable_records] T --1:N--> V[bitable_views] ``` `bitable_files`: id, name, icon, description, owner_user_id, created_at, updated_at `bitable_tables`: 新增 `file_id` 外键列 ### 默认字段创建时序 ```mermaid sequenceDiagram participant U as 用户 participant API as REST API participant S as BitableService participant R as Repository U->>API: POST /files/{file_id}/tables {name} API->>S: create_table(file_id, name) S->>R: create Table record S->>S: _build_default_fields(table_id) S->>R: create_records_batch 5 fields S-->>API: Table + 5 Fields API-->>U: 201 Created ``` --- ## Implementation Units ### U1. Backend: BitableFile entity + schema V2 migration **Goal:** 引入 BitableFile Pydantic 模型 + ORM model + repository + service + REST endpoints,升级 schema 到 V2。 **Requirements:** R1 **Dependencies:** 无(基础单元) **Files:** - `src/agentkit/bitable/models.py` — 新增 `BitableFile` Pydantic 模型 - `src/agentkit/bitable/db.py` — 新增 `FileModel` ORM;`_SCHEMA_VERSION = 2`;实现 `_apply_v2_migration`(创建 files 表,给 tables 加 file_id 列);`Table` 加 `file_id` 字段 - `src/agentkit/bitable/repository.py` — 新增 `FileRepository`(CRUD + list_by_owner) - `src/agentkit/bitable/service.py` — 新增 `BitableService` 的文件方法(create_file/list_files/get_file/update_file/delete_file);`create_table` 改签名加 `file_id` 参数 - `src/agentkit/server/routes/bitable.py` — 新增 `/files` 端点(POST/GET/GET/:id/PUT/:id/DELETE/:id);`/files/{file_id}/tables` POST 端点;文件级 ownership 检查 `_check_file_ownership` - `tests/unit/bitable/test_file_crud.py` — 新建测试文件 **Approach:** - `BitableFile` 模型字段:id, name, icon (emoji 字符串), description, owner_user_id, created_at, updated_at - 文件 ownership 检查复用 `_check_table_ownership` 模式(solutions doc 模式 4):404 before 403,internal token bypass - `delete_file` 级联删除该文件下所有 tables(及其 fields/records/views)—— 复用现有 `delete_table` 逻辑循环 - schema V2 迁移用 `ALTER TABLE bitable_tables ADD COLUMN file_id VARCHAR` + `CREATE TABLE bitable_files` - 现有无 file_id 的 table 在迁移时创建一个"默认文件"并归属之(防御性,虽然 verifier 确认无现有数据) **Patterns to follow:** - `docs/solutions/architecture-patterns/bitable-companion-service-security-reliability-patterns.md` 模式 1(服务隔离)、模式 4(IDOR ownership 检查) - 现有 `_check_table_ownership` in `src/agentkit/server/routes/bitable.py` **Test scenarios:** - Happy path: 创建文件 → 获取 → 列表 → 更新 → 删除 - Edge case: 删除文件时级联删除其下所有表(验证 tables/fields/records 都被清理) - Error path: 非文件 owner 访问返回 404(不泄露存在性) - Error path: internal token bypass ownership 检查 - Integration: 创建文件 → 在文件下创建 table → table.file_id 正确关联 - Covers AE1. 新建文件"销售管线"(文件创建部分) **Verification:** `pytest tests/unit/bitable/test_file_crud.py -v` 全绿;`ruff check src/agentkit/bitable/` 无 lint 错误。 --- ### U2. Backend: Default fields on table creation **Goal:** `create_table` 自动创建 5 个默认字段;agent-owned 字段在 `create_record` 时自动填充。 **Requirements:** R2 **Dependencies:** U1(create_table 签名变更) **Files:** - `src/agentkit/bitable/service.py` — `create_table` 内调用 `_create_default_fields(table_id, owner_user_id)`;`create_record` 内自动填充 agent-owned 字段(创建人 = owner_user_id,创建时间 = now) - `src/agentkit/bitable/models.py` — 新增 `DEFAULT_FIELD_TEMPLATES` 常量(5 个默认字段定义) - `tests/unit/bitable/test_default_fields.py` — 新建测试文件 **Approach:** - `DEFAULT_FIELD_TEMPLATES`:5 个字段定义,含 name/type/owner/config - 状态 select 字段 config: `{"options": [{"label":"未开始","value":"not_started","color":"default"},{"label":"进行中","value":"in_progress","color":"processing"},{"label":"已完成","value":"done","color":"success"}]}` - `_create_default_fields` 用 `repository.create_records_batch` 一次创建 5 个 Field - `create_record` 检查 table 的 agent-owned 字段,若 values 未提供则自动填充 **Patterns to follow:** - 现有 `create_table` in `src/agentkit/bitable/service.py` - CONCEPTS.md Field Ownership 定义 **Test scenarios:** - Happy path: 创建 table → 验证返回 5 个默认字段(名称/类型/owner 正确) - Happy path: 状态字段 config.options 有 3 个预设选项 - Happy path: 创建 record → 创建人字段自动填充 owner_user_id - Happy path: 创建 record → 创建时间字段自动填充当前 timestamp - Edge case: 用户传 record values 时覆盖创建人字段 → agent-owned 字段不被用户覆盖 - Covers AE1. 新建表"客户"(默认字段部分) **Verification:** `pytest tests/unit/bitable/test_default_fields.py -v` 全绿。 --- ### U3. Frontend: File layer store + API client + navigation restructure **Goal:** 前端文件层 API client + store + 三层导航视图重构。 **Requirements:** R3, R4 **Dependencies:** U1(后端文件 API) **Files:** - `src/agentkit/server/frontend/src/api/bitable.ts` — 新增 `IBitableFile` 类型 + file CRUD API 函数 - `src/agentkit/server/frontend/src/stores/bitable.ts` — 新增 `files` state + `loadFiles/createFile/updateFile/deleteFile` actions;`loadTables` 改为按 fileId 过滤 - `src/agentkit/server/frontend/src/views/BitableFileListView.vue` — 新建:文件列表页(卡片网格 + 新建按钮) - `src/agentkit/server/frontend/src/views/BitableFileDetailView.vue` — 新建:文件详情页(topbar + 左侧表列表 sidebar + 右侧表内 grid) - `src/agentkit/server/frontend/src/views/BitableView.vue` — 删除或改为 redirect 到 `/bitable` - `src/agentkit/server/frontend/src/router/index.ts` — 重构 `/bitable` 路由为嵌套:`/bitable`(文件列表)、`/bitable/:fileId`(文件详情)、`/bitable/:fileId/:tableId`(表内,可选拆为子路由或 query) - `src/agentkit/server/frontend/src/components/bitable/FileCard.vue` — 新建:文件卡片组件(图标+名称+表数量+描述) - `src/agentkit/server/frontend/src/components/bitable/FileCreateModal.vue` — 新建:创建文件弹窗 - `src/agentkit/server/frontend/src/components/bitable/TableViewList.vue` — 修改:props 加 `fileId`,emit create 时带 fileId **Approach:** - `BitableFileListView` 布局:顶部"新建文件"按钮 + 卡片网格(每卡片显示 icon/name/表数量/描述/操作菜单) - `BitableFileDetailView` 布局:复用现有 `BitableView.vue` 的 topbar + sidebar + main 结构,但 topbar 显示文件名 + 返回按钮,sidebar 显示该文件的表列表 - 路由用嵌套 children:`/bitable` → FileListView;`/bitable/:fileId` → FileDetailView(内部根据是否选中 table 渲染 grid 或 placeholder) - 文件删除用 `a-popconfirm` 二次确认 **Patterns to follow:** - 现有 `BitableView.vue` 的 topbar + sidebar + main 布局 - 现有 `TableCreateModal.vue` 的弹窗模式 - Ant Design Vue Card / Modal / Popconfirm 组件 **Test scenarios:** - Happy path: 访问 /bitable → 显示文件列表(空态有引导) - Happy path: 点"新建文件" → 弹窗 → 输入名称 → 创建 → 列表刷新显示新卡片 - Happy path: 点文件卡片 → 跳转 /bitable/:fileId → 显示文件详情(sidebar 表列表) - Happy path: 在文件详情点"新建表" → 表创建后 sidebar 显示 - Edge case: 删除文件 → popconfirm 确认 → 列表移除 - Error path: 访问不存在的 fileId → 显示 404 或回退到列表 - Covers AE1. 新建文件"销售管线"(前端流程) **Verification:** `npm run typecheck` 通过;`npm run dev` 手动验证三层导航;e2e 测试在 U6 覆盖。 --- ### U4. Frontend: In-table field operations (column header dropdown) **Goal:** grid 列头下拉菜单支持重命名/改类型/隐藏/删除字段。 **Requirements:** R5 **Dependencies:** U3(store 重构) **Files:** - `src/agentkit/server/frontend/src/components/bitable/BitableGrid.vue` — 修改:列 header slot 渲染列名 + 下拉触发图标;移除 select/multiselect 的 `VxeInput` editRender(在 U5 完成) - `src/agentkit/server/frontend/src/components/bitable/ColumnHeaderMenu.vue` — 新建:列头菜单组件(重命名/改类型/隐藏/删除) - `src/agentkit/server/frontend/src/components/bitable/FieldConfigForm.vue` — 修改:支持重命名与改类型(可能复用现有表单) - `src/agentkit/server/frontend/src/stores/bitable.ts` — 新增 `updateField(fieldId, patch)` + `deleteField(fieldId)` + `hideField(fieldId, viewId)` actions **Approach:** - vxe-table column 配置加 `header-class-name` + 使用 `header` slot 自定义渲染 - header slot 内:列名文本 + 一个 `...` 图标触发 `a-dropdown` - 菜单项: - 重命名 → 弹 `a-modal` 输入新名称 → 调 `store.updateField` - 修改类型 → 弹 `a-modal` 选新类型 → 调 `store.updateField`(注意:改类型可能清空 config) - 隐藏 → 调 `store.hideField`(写入当前 view 的 hidden_fields) - 删除 → `a-popconfirm` 确认 → 调 `store.deleteField` - 删除字段会级联删除该字段的所有 record values(后端已支持) **Patterns to follow:** - vxe-table header slot 文档 - Ant Design Vue Dropdown / Modal / Popconfirm **Test scenarios:** - Happy path: 点列头 → 菜单弹出 4 项 - Happy path: 重命名 → 输入新名称 → 表头实时更新 - Happy path: 隐藏字段 → 该列从 grid 消失(仍存在于字段管理面板) - Happy path: 删除字段 → popconfirm 确认 → 该列与数据消失 - Edge case: 删除主键字段 → 提示不允许或确认后清除 primary_key_field_id - Covers AE1. 用户点"标题"列头下拉 → 选"重命名"改为"公司名" **Verification:** `npm run typecheck` 通过;手动验证列头菜单 4 个操作。 --- ### U5. Frontend: select/multiselect dropdown editor **Goal:** select/multiselect 字段编辑器从文本输入改为下拉选项(带颜色标签)。 **Requirements:** R6 **Dependencies:** U4(BitableGrid 已修改) **Files:** - `src/agentkit/server/frontend/src/components/bitable/SelectCellEditor.vue` — 新建:自定义编辑器组件(用 a-select) - `src/agentkit/server/frontend/src/components/bitable/BitableGrid.vue` — 修改:select/multiselect 列的 `editRender` 指向自定义编辑器;移除 `ponytail: select editor uses text input` 注释 - `src/agentkit/server/frontend/src/stores/bitable.ts` — 可能需要 `getFieldOptions(fieldId)` getter **Approach:** - 注册 vxe-table 自定义编辑器 `SelectCellEditor`: - props: `field`(含 config.options) - 渲染 `a-select`:`show-search`、`label-in-value`、选项带颜色 tag - multiselect 用 `mode="multiple"` - `BitableGrid.vue` column 配置:select/multiselect 的 `editRender: { name: 'SelectCellEditor', options: field.config.options }` - 选项颜色用 `a-tag :color="option.color"` 渲染 **Patterns to follow:** - vxe-table 自定义编辑器文档 - Ant Design Vue Select + Tag **Test scenarios:** - Happy path: 双击 select 单元格 → 下拉弹出 3 个选项(带颜色) - Happy path: 选一个选项 → 单元格值更新为 option.value - Happy path: multiselect 字段 → 可多选 - Happy path: 搜索功能 → 输入文字过滤选项 - Edge case: 字段无 options config → 下拉为空 + 提示 - Covers R6 **Verification:** `npm run typecheck` 通过;手动验证 select 编辑器交互。 --- ### U6. E2E test coverage expansion **Goal:** 扩展 e2e 测试覆盖 Stage 1 关键流程。 **Requirements:** Success Criteria(E2E 覆盖) **Dependencies:** U3, U4, U5(前端功能完成) **Files:** - `src/agentkit/server/frontend/e2e/bitable-view.spec.ts` — 修改:保留 B1/B2,扩展覆盖文件导航 - `src/agentkit/server/frontend/e2e/bitable-file-flow.spec.ts` — 新建:文件 CRUD + 三层导航流程 - `src/agentkit/server/frontend/e2e/bitable-field-ops.spec.ts` — 新建:列头字段操作 + select 编辑器 **Approach:** - `bitable-file-flow.spec.ts`: - 访问 /bitable → 验证文件列表渲染 - 新建文件 → 验证卡片出现 - 进入文件 → 验证 sidebar 表列表 - 新建表 → 验证默认字段显示 - 切换表 → 验证 grid 切换 - `bitable-field-ops.spec.ts`: - 点列头 → 验证菜单弹出 - 重命名 → 验证表头更新 - 隐藏 → 验证列消失 - select 编辑器 → 验证下拉交互 **Patterns to follow:** - 现有 `bitable-view.spec.ts` 的 B1/B2 模式 - Playwright 测试模式 **Test scenarios:** - Happy path: 文件列表 → 新建文件 → 进入 → 新建表(含默认字段)→ 切换表(完整流程) - Happy path: 列头重命名 + 隐藏 + select 编辑器交互 - Smoke: /bitable 不白屏、核心元素可见(保留 B1/B2) **Verification:** `npx playwright test e2e/bitable-*.spec.ts` 全绿。 --- ## Scope Boundaries ### 本次范围内(Stage 1) R1-R6 全部 + U1-U6 全部。这是 origin 文档 4 阶段中的阶段一。 ### Deferred to Follow-Up Work 以下 R 在 origin 文档中定义,本次不实现,留给后续 lfg 调用: - **Stage 2(R7-R10)**:三类采集入口前端 UI(Excel/DB/API)、Agent 写入反馈 UI - **Stage 3(R11-R13)**:看板视图、画廊视图、公式编辑器增强 - **Stage 4(R14-R17)**:权限模型、自动化触发器、表单视图、甘特视图 ### Outside this product's identity (从 origin 文档继承)通用电子表格、ETL/数据管道平台、BI 仪表盘、知识库 RAG 替代。 --- ## System-Wide Impact - **数据模型变更**:新增 `bitable_files` 表,`bitable_tables` 加 `file_id` 列。schema V1 → V2 启动时迁移。 - **API 边界**:新增 `/api/v1/bitable/files` 端点组。现有 `/tables` 端点改为 `/files/{file_id}/tables`(保留旧端点兼容性,标记 deprecated)。 - **前端路由**:`/bitable` 从单视图重构为嵌套路由。现有书签需重定向。 - **领域词汇**:CONCEPTS.md 需补"多维表格文件/BitableFile"条目。 --- ## Risks & Dependencies - **风险**:schema V2 迁移若失败可能导致启动卡住。缓解:迁移用 `try/except` 包裹,失败时 log 并继续(向后兼容 V1 表为"默认文件"归属)。 - **风险**:vxe-table header slot 与自定义编辑器的集成可能在版本差异下行为不一致。缓解:U4/U5 实现时先写最小验证 demo。 - **依赖**:U2 依赖 U1(create_table 签名变更)。U3 依赖 U1(前端调用后端文件 API)。U4/U5 依赖 U3(store 重构)。U6 依赖 U3/U4/U5。 - **依赖**:现有 `docs/solutions/architecture-patterns/bitable-companion-service-security-reliability-patterns.md` 的 10 个模式必须遵守(特别是 IDOR ownership 检查、batch 操作、async I/O)。 --- ## Open Questions ### Resolve During Implementation - 文件图标用 emoji 字符串还是预设图标集?默认用 emoji 字符串(前端用 `a-input` 输入),实现时确认。 - 现有 `/tables` 端点是否保留?保留并标记 deprecated,新端点 `/files/{file_id}/tables` 为推荐路径。 - `BitableView.vue` 删除还是保留为 redirect?改为 redirect 到 `/bitable`。 ### Deferred to Implementation - vxe-table header slot 的具体 API 形态(版本相关,实现时查文档确认)。 - select 编辑器在 multiselect 下的值序列化格式(数组 vs 逗号分隔字符串,复用现有后端约定)。 --- ## Sources & Research - **Origin requirements**: `docs/brainstorms/2026-06-29-bitable-ui-completeness-requirements.md` - **安全/可靠性模式**: `docs/solutions/architecture-patterns/bitable-companion-service-security-reliability-patterns.md`(10 个模式,本次实现必须遵守) - **现有后端实现**: `src/agentkit/bitable/{models,db,repository,service}.py` + `src/agentkit/server/routes/bitable.py` - **现有前端实现**: `src/agentkit/server/frontend/src/views/BitableView.vue` + `src/agentkit/server/frontend/src/components/bitable/*.vue` + `src/agentkit/server/frontend/src/stores/bitable.ts` - **领域词汇**: `CONCEPTS.md` Bitable/Field Ownership/Recalc 定义 - **飞书多维表格范式**: 文件→表→字段/记录三层;列头下拉管理字段;新建表默认字段 - **twentyhq/twenty 布局参考**: [https://github.com/twentyhq/twenty](https://github.com/twentyhq/twenty) - **vxe-table 4 文档**: header slot 与自定义编辑器 - **Ant Design Vue 4**: Card/Modal/Dropdown/Select/Popconfirm/Tag 组件