Files
mnote/design/表格方案.md
T
2025-11-23 10:55:04 +08:00

180 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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