---
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 组件
