Files
mnote/design/表格方案.md
T

180 lines
12 KiB
Markdown
Raw Normal View History

2025-11-23 10:55:04 +08:00
# Wolai-clone 协同表格方案(v3.1)——Luckysheet 体验 × Supabase 数据底座
## 0. 背景与目标
- 当前 v3.0 架构已经完成 BlockNote + Yjs + Hocuspocus + Supabase Realtime 的基础协同能力,内置块类型仅支持「简单表格」,无法满足飞书/钉钉/金山文档级交互。
- 新版本需要让用户在文档任意位置插入「在线表格块」,可在文档内预览、可全屏编辑、可多人实时协作,并支持 Excel 级功能(公式、条件格式、批量粘贴等)。
- 同时,考虑到未来要把表格作为独立数据对象(多处引用、导出、AI/OCR 写入等),必须把表格数据与 BlockNote 主文档解耦,避免大表拖垮文档 CRDT。
## 1. 设计原则
1. **用户心智优先**:界面与交互最大程度贴近 Excel/WPS/飞书表格,复制粘贴零障碍。
2. **数据解耦**BlockNote 块只保存 `tableId` 与视图参数,真实表格数据落在 Supabase + 独立 Yjs SubDoc,确保性能、复用与权限扩展。
3. **协同一致性**:复用现有 Yjs/Hocuspocus/Supabase Realtime 通道,内嵌/全屏共享同一数据源,支持多人光标与冲突解决。
4. **渐进增强**:先满足核心编辑体验,再分阶段接入高级能力(导入导出、公式库、与 MinerU/AI 的互通)。
## 2. 技术选型与理由
| 维度 | 方案 | 理由 |
|------------------|--------------------------------|------|
| 表格 UI/编辑器 | Luckysheet (@dream-num) | Excel 级 UI/工具栏/公式/条件格式,复制粘贴兼容 WPS/飞书/钉钉,支持 headless 嵌入。 |
| 数据渲染内核 | Luckysheet + 虚拟滚动 | 经过大规模验证,500×100 也能稳定运行。 |
| 实时协作 | Yjs SubDoc + luckysheet-yjs-binding | 官方 binding,直接复用现有 Hocuspocus Provider;每张表格一个 SubDoc,避免主文档体积膨胀。 |
| 数据持久化 | Supabase Postgres (`document_tables`, `document_table_rows`) + 定时快照 | 让表格成为独立实体,可复用/引用/授权,同时方便后台 API、导出和 AI 写入。 |
| 内联预览 | 自研 React 轻量预览组件 + 行列摘要 | 保持文档流畅,默认展示前 5 行,支持 hover 工具条。 |
| 全屏编辑 | `<FullScreenTableEditor>` + Luckysheet | 提供 Excel 式工具栏、状态栏、协作者指示、暗黑模式。 |
| 公式引擎 | Luckysheet 内置 + HyperFormula(后续增强) | 先满足 SUM/IF 等高频场景,再按需升级。 |
> 放弃:自研 Data Grid(周期长)、Handsontable(商业授权)、AG-Grid(过重)、Tiptap Table(功能不足)。
## 3. 架构概览
```
┌───────────────────────────┐
│ Next.js 前端 │
│ ┌───────────────────────┐ │
│ │ BlockNote (文档 Y.Doc) │ │
│ │ └─ TableBlock │ │
│ │ attrs: {tableId} │ │
│ └───────────────────────┘ │
│ │引用 │
│ ┌───────────────────────┐ │
│ │ LuckysheetProvider │ │
│ │ └─ Y.SubDoc(tableId)│ │
│ │ └─ Supabase Realtime│ │
│ └───────────────────────┘ │
└────────────┬──────────────┘
│REST/RPC
┌────────────▼──────────────┐
│ FastAPI 后端 + Supabase │
│ ├─ document_tables │
│ ├─ document_table_rows │
│ ├─ RLS / Realtime 触发器 │
│ └─ 导出/导入/AI 接口 │
└───────────────────────────┘
```
- **BlockNote 主文档**:仅存 `tableId`、标题、视图配置等轻量信息。
- **表格 Y.SubDoc**:按需加载;Luckysheet 与之绑定,实现实时协作。
- **Supabase 数据**:提供持久化/查询/权限/导出能力;Yjs 定时把最新快照写入数据库,同时监听数据库变化以支持非 Web 客户端。
## 4. 数据模型
```sql
create table document_tables (
id uuid primary key default gen_random_uuid(),
workspace_id uuid not null references workspaces(id) on delete cascade,
document_id uuid not null references documents(id) on delete cascade,
title text not null default '未命名表格',
schema jsonb not null default '{}'::jsonb, -- 列定义/格式/宽度
view_preferences jsonb not null default '{}'::jsonb,
is_archived boolean not null default false,
snapshot jsonb,
last_synced_at timestamptz,
created_by uuid references profiles(id) on delete set null,
updated_by uuid references profiles(id) on delete set null,
created_at timestamptz not null default timezone('utc', now()),
updated_at timestamptz not null default timezone('utc', now())
);
create table document_table_rows (
id uuid primary key default gen_random_uuid(),
workspace_id uuid not null references workspaces(id) on delete cascade,
document_id uuid not null references documents(id) on delete cascade,
table_id uuid not null references document_tables(id) on delete cascade,
row_index integer not null, -- 稳定排序键
row_data jsonb not null default '{}'::jsonb, -- 单元格字典,按列 key 存储
row_hash text,
is_deleted boolean not null default false,
updated_by uuid references profiles(id) on delete set null,
created_at timestamptz not null default timezone('utc', now()),
updated_at timestamptz not null default timezone('utc', now())
);
```
- ✅(2025-02-22`supabase_local` 已执行建表 + 索引 + 触发器 + RLS,迁移文件:`supabase/migrations/20250222_add_document_tables.sql`
- RLS:沿用 `workspace_members``document_tables`/`document_table_rows` 在写入前通过触发器校验 workspace/document 一致,所有读写均需加入同一 workspace。
- 每张表格的实时编辑依赖 Yjs SubDoc;后台任务(导出 CSV/Excel、AI 填表、MinerU OCR 转表)通过 REST API 操作数据库。
- BlockNote 块示例:
```json
{
"id": "block_xxx",
"type": "table",
"props": {
"tableId": "d3d7-...",
"displayMode": "compact",
"previewRows": 5
}
}
```
## 5. 功能模块拆解
1. **块插入与预览**
- 斜杠命令 `/table`、工具栏按钮、AI 建议等入口。
- 默认创建 10×5 空表,预览显示前 5 行 + 列头,hover 显示快速操作(新建/全屏/复制链接)。
2. **全屏编辑器**
- Excel 风格工具栏(行列操作、合并、筛选、条件格式、公式插入、单元格格式)。
- 协作者状态:顶部显示头像、单元格边框高亮。
- 暗黑模式、移动端强制全屏。
3. **数据同步**
- Luckysheet ↔ Y.SubDoc:官方 binding。
- SubDoc ↔ Supabase:节流(1-2s)后写入 `document_tables`/`document_table_rows`;大表行级 diff。
- 后端提供导出 CSV/Excel、批量导入 API。
4. **高级能力**
- 条件格式模版、冻结行列、查看历史版本。
- 允许多个文档引用同一 `tableId`(只读/编辑权限可控)。
- AI/ MinerU 写表:后端接口 `/api/v1/tables/{id}/rows` 支持 append/merge。
## 6. 实施计划
| 阶段 | 时间 | 目标 | 关键任务 | 产出 / 验收 |
|------|------|------|----------|-------------|
| P0 需求巩固 | 0.5d | 明确体验 & 数据需求 | - 联合设计/产品确认预览、高级功能优先级<br>- 定义列类型 MVP(文本/数字/货币/日期/选择/复选/公式/引用)<br>- 确认与 MinerU/AI 的接口需求 | 更新 PRD + 交互稿 |
| P1 数据面准备 | 1d | 建 Supabase 表 & API | - 执行 SQL 建表 + RLS + 触发器(Supabase Realtime 通知)<br>- FastAPI 增加 `/tables` CRUD、`/tables/{id}/rows` 批量读写<br>- 单元测试 + API 文档 | 后端分支 + Swagger |
| P2 Yjs/Luckysheet 骨架 | 2d | 协同内核跑通 | - 引入 @dream-num/luckysheet、luckysheet-yjs-binding<br>- 实现 `<LuckysheetProvider>`,封装 SubDoc 生命周期、连接状态提示<br>- 支持单独打开表格编辑页(不依赖 BlockNote) | 可共享链接、协同无冲突 |
| P3 BlockNote 表格块 & 预览 | 2d | 文档内插入与缩略展示 | - 自定义 block schema + slash command<br>- 预览组件(前5行、hover toolbar、loading/错误态)<br>- 表格块和 Supabase 数据绑定(创建时写表、删除时软删) | 文档里可插入表格并跳转全屏 |
| P4 全屏编辑器 & 工具栏 | 3d | 飞书式编辑体验 | - `<FullScreenTableEditor>`:工具栏、状态栏、协作者列表<br>- 支持插入/删除/合并/冻结/条件格式/基础公式<br>- 完善暗黑/移动端 UI | 500×50 表格流畅、多端一致 |
| P5 持久化 & 性能 | 2d | 数据安全 & 优化 | - SubDoc → Supabase 节流、row-level diff<br>- 历史快照(定时写入 table_versions<br>- 预览模式虚拟滚动、全屏懒加载资源 | 刷新不丢数据、大表不卡顿 |
| P6 高级扩展 | 2d+ | 导入导出 & AI | - CSV/Excel 导入导出<br>- MinerU OCR 输出自动转表 + 写入<br>- 表格引用共享、权限控制 UI | E2E 测试 + 文档 |
> 若时间紧,可先完成 P0~P4 形成可用 MVP,之后再迭代 P5/P6。
**进展速记(2025-02-22**
- ✅ P1「执行 SQL 建表 + RLS + 触发器」已完成(详见上文数据模型 & 迁移文件),剩余 `/tables`/`/tables/{id}/rows` API + 单元测试待开发。
- ✅ P2「Luckysheet Provider + Yjs SubDoc 骨架」:Next.js 侧封装 `LuckysheetCanvas`,复用 Hocuspocus 通道,表格编辑自动广播到 Y.Doc 与 Supabase snapshot。
- ✅ P3「BlockNote 表格块 & 预览」:自定义 `tableBlock` + slash `/table`,支持在线创建、缩略展示、重命名、全屏入口。
- ✅ P4「全屏编辑器」:落地 `<TableEditorOverlay>`,集成 Luckysheet 工具栏、保存状态、协同提示,500×50 表格实测流畅。
## 7. 依赖与工具
```bash
pnpm add @dream-num/luckysheet luckysheet-yjs-binding @hocuspocus/provider yjs y-protocols zustand lucide-react
# 后端
pip install fastapi supabase-py psycopg[binary] uvicorn[standard]
```
- 设计稿:Figma(飞书表格参考)。
- 协同测试:Cypress + Playwright(多客户端场景)。
## 8. 验收清单
- [ ] `/table` 插入后出现缩略预览,支持命名/重命名。
- [ ] 多端同时编辑同一表,光标位置、单元格内容同步延迟 < 800ms。
- [ ] 刷新或重连后表格数据一致,Supabase 中有对应记录。
- [ ] 全屏编辑支持:插入/删除行列、调整列宽、冻结、合并、条件格式、公式 (SUM/AVERAGE/IF)。
- [ ] 支持从 Excel/WPS/飞书/钉钉直接粘贴,保留行列和基础格式。
- [ ] CSV/Excel 导出可用,至少支持 UTF-8。
- [ ] 移动端默认全屏,横屏提示可编辑。
## 9. 后续路线(与整体架构衔接)
1. **多视图**:基于同一 `tableId` 派生看板、画廊,复用 Supabase 数据。
2. **AI Assist**:支持「生成表格」「自动补数据」「智能公式建议」,后端复用 LightRAG + GPT-4o。
3. **OCR 写表**:MinerU 解析复杂表格后直接写入 `document_table_rows`,用户可在 Luckysheet 中二次确认。
4. **权限与共享**: 表格可独立分享/只读/协作,并在后台记录操作日志。
此方案兼顾用户熟悉的 Excel 体验与 Supabase 数据化能力,为后续多视图、AI、OCR 等模块铺平道路。接下来可按 P0~P4 推进 MVP,实现飞书级表格块体验。*** End Patch