# collab-board **Repository Path**: YangLI1/collab-board ## Basic Information - **Project Name**: collab-board - **Description**: 一个面向小团队的实时协作白板,支持多人同时绘制、便签、文本、基础图形、光标感知、历史回放、权限分享和导出。 项目定位:Node.js 全栈学习 + 实时协作系统实战 + 求职项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-08 - **Last Updated**: 2026-11-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Collab Board Collab Board 是一个面向协作看板场景的本地优先白板应用,使用 Vue 3 + TypeScript + Vite 构建。项目目标场景是团队协作白板,当前已完成**单机版白板第一版**:可以在浏览器中绘制、添加便签/文字/图形、撤销重做、缩放平移,并把白板自动保存在本地。实时协作、后端接口、账号体系和素材库尚未接入。 > 当前版本不需要后端,打开页面即可使用。白板内容和主题设置只保存在当前浏览器的 `localStorage` 中,不会上传到服务器。 ## 功能概览 ### 画布编辑 - 提供选择、移动画布、自由绘制、橡皮擦、便签、文字、矩形、椭圆、箭头 9 种工具,并带有对应快捷键。 - 自由绘制会按指针轨迹记录折线点;便签、文字和几何图形需要按住左键拖动创建,拖动不足 4px 时视为点击,不会误建元素。 - 支持选择、拖动和删除元素;选中元素显示虚线选框,自由绘制按折线包围盒显示选框。 - 双击便签或文字进入编辑,编辑框失焦或按 `Ctrl + Enter` 时结束;文字按固定字符数自动换行(便签 15、文字 24)。 - 撤销/重做基于完整白板快照,最多保留 50 步;撤销/重做后会清除当前选中状态。 - 画布使用 SVG `viewBox` 实现平移和缩放,缩放范围为 35%–250%,支持按钮和 `Ctrl/⌘ + 滚轮` 操作。 - 支持修改白板标题、导出 SVG、清空画布(清空前会确认,且可撤销)。 - 桌面端为浮岛式工具栏,窄屏下工具栏移到底部并收起低频工具,适配触屏操作。 ### 本地保存与主题 - 白板标题和元素变化后,经过 350ms 防抖自动写入 `localStorage`,顶部显示“正在保存…”“已保存在本机”或“保存失败”等状态。 - 读取本地数据时会做结构校验;数据损坏或浏览器禁止存储时,会提示并以空白画布打开,而不是直接崩溃。 - 支持浅色、深色、跟随系统三种外观模式,主题选择会持久化,并在白板页与关于页之间共享。 - 深色模式通过根节点 `.dark` 作用域和 CSS 变量覆盖实现,同时监听系统 `prefers-color-scheme` 变化。 ### 页面 | 路由 | 页面 | 说明 | | -------- | ------ | -------------------------------------------------------- | | `/` | 白板 | 主视图,全屏显示并隐藏顶部导航 | | `/about` | 关于 | 介绍当前技术栈,并提供与白板页共享的主题切换 | | 其他路径 | 重定向 | 未匹配路径统一跳转回 `/`,路由切换时同步浏览器标签页标题 | 分享按钮目前只弹出说明:在线协作服务尚未连接,白板仅保存在本机。素材库面板同样是占位实现,点击“浏览公共素材库”只会给出未连接的提示。 ## 技术栈 | 范围 | 技术 | | ----------------- | -------------------------------------------------------------------------- | | Monorepo 与包管理 | pnpm workspace、catalog 统一版本 | | 前端 | Vue 3.5、TypeScript 6、Vite 8 | | 路由 | Vue Router 4(History 模式,路由懒加载) | | 状态管理 | Pinia 3(当前仅主题状态) | | UI 组件 | Naive UI 2.45(按需引入,主题经 `src/theme/naive.ts` 统一配置) | | 画布与样式 | 原生 SVG、SCSS(Sass)、CSS 变量 | | 代码质量 | ESLint 10 扁平配置、typescript-eslint、eslint-plugin-vue、Prettier、CSpell | | Git 工作流 | Husky、lint-staged、Commitizen(cz-git)、Commitlint | | 测试 | Vitest + @vue/test-utils(jsdom 单元测试,`@vitest/coverage-v8` 覆盖率) | > `tailwindcss`、`postcss`、`autoprefixer` 已写入 `apps/frontend/web/package.json`,但仓库中没有 PostCSS/Tailwind 配置,源码也未引用这些方案;UI 交互组件已迁移到 Naive UI(按需引入),画布仍由手写 SVG 与 SCSS 实现。 ## 开发环境要求 - Node.js `>= 24.9.0` - pnpm `>= 12.8.1`(仓库通过 `packageManager` 和 `devEngines` 声明 `12.8.1`) - Git `.npmrc` 开启了 `engine-strict=true`;仓库同时通过 `packageManager` 和 `devEngines` 约束 pnpm 版本。建议先确认本机版本: ```bash node --version pnpm --version ``` ## 快速开始 前端已纳入 pnpm workspace(`apps/frontend/*`),依赖统一由根目录安装,不需要再进入子目录单独安装,也不需要使用 `--ignore-workspace`: ```bash # 在仓库根目录安装全部 workspace 依赖 pnpm install # 启动前端开发服务器 pnpm --filter @collab-board/web dev ``` Vite 启动后会在终端输出访问地址,通常为 `http://localhost:5173`;端口被占用时会自动尝试其他端口。 构建与本地预览: ```bash pnpm --filter @collab-board/web build pnpm --filter @collab-board/web preview ``` `build` 会先执行 `vue-tsc -b` 做类型检查,再由 Vite 输出到 `apps/frontend/web/dist`。也可以单独运行类型检查: ```bash pnpm --filter @collab-board/web exec vue-tsc --noEmit -p tsconfig.app.json ``` ## 常用命令 以下根级命令均在仓库根目录执行: | 命令 | 说明 | | ---------------------- | -------------------------------------------------------------- | | `pnpm lint:eslint` | 对仓库中的 TypeScript、JavaScript 和 Vue 文件运行 ESLint | | `pnpm lint:prettier` | 按 Prettier 配置格式化匹配文件(会直接写入修改) | | `pnpm lint:spellcheck` | 使用 CSpell 检查源码、配置等文件 | | `pnpm precommit` | 对暂存文件运行 lint-staged(CSpell、Prettier、ESLint) | | `pnpm commit` | 使用 Commitizen 交互式生成符合 Conventional Commits 的提交信息 | | `pnpm test` | 运行前端单元测试(转发到 `@collab-board/web` 的 `test:unit`) | 前端子包还提供: ```bash pnpm --filter @collab-board/web dev pnpm --filter @collab-board/web build pnpm --filter @collab-board/web preview ``` 也可以把 `--filter @collab-board/web` 换成 `--dir apps/frontend/web`。 ## 项目结构 ```text . ├── apps/ │ ├── backend/ # 后端占位包,src/ 目前为空 │ └── frontend/web/ # Vue 3 白板前端 │ ├── public/ # favicon.svg、icons.svg │ ├── src/ │ │ ├── composables/ # 编辑器编排、画布交互、历史、持久化 │ │ ├── config/ # 工具定义、元素默认值、存储 key │ │ ├── router/ # 路由表与页面标题同步 │ │ ├── stores/ # Pinia 主题 store │ │ ├── styles/ # reset、主题 token、共享样式占位符 │ │ ├── types/ # Tool、BoardElement 等领域类型 │ │ ├── utils/ # 快照校验、文本换行、折线边界 │ │ ├── views/ │ │ │ ├── about/ # 关于页 │ │ │ └── board/ # 白板页及其组件 │ │ ├── App.vue # 应用外壳、导航、主题生命周期 │ │ └── main.ts # 应用入口 │ ├── index.html │ ├── vite.config.ts # @ 别名与 SCSS loadPaths │ └── tsconfig*.json # 应用/Node/根引用配置 ├── docs/ # 工程化学习笔记 ├── packages/ │ └── eslint-config/ # 可组合的共享 ESLint 预设 │ └── presets/ # base、typescript、vue、ignores ├── .husky/ # Git hooks ├── .cspell/ # 自定义词典 ├── eslint.config.js # 根 ESLint 配置:按目录组合预设 ├── package.json # 根脚本、工具依赖与版本约束 ├── pnpm-workspace.yaml # workspace、catalog、依赖构建脚本策略 ├── tsconfig.json # TypeScript 通用严格配置 ├── prettier.config.js ├── cspell.json ├── commitlint.config.js └── .lintstagedrc.js ``` ## 架构说明 ### 启动与渲染链路 ```text main.ts └─ App.vue(初始化/释放主题监听,白板路由隐藏顶部导航) └─ vue-router ├─ / views/board/index.vue │ └─ useWhiteboardEditor() │ ├─ useCanvasInteractions() │ ├─ useBoardHistory() │ └─ useBoardStorage() └─ /about views/about/index.vue(复用 Pinia 主题 store) ``` 白板页以 `useWhiteboardEditor` 作为状态与行为编排层,把画布、工具栏、菜单、对话框等展示组件需要的 ref 和操作集中返回,避免把业务逻辑散落在模板中。 ### 核心模块 | 模块 | 职责 | | -------------------------------------- | -------------------------------------------------------------------------- | | `composables/useWhiteboardEditor.ts` | 白板状态、工具切换、快捷键、缩放、导出 SVG、清空画布及组件生命周期 | | `composables/useCanvasInteractions.ts` | 指针手势状态机:创建、移动、平移、自由绘制;处理拖拽阈值与负方向宽高归一化 | | `composables/useBoardHistory.ts` | 基于完整快照的撤销/重做,限制 50 步 | | `composables/useBoardStorage.ts` | 读取并校验本地白板,监听标题/元素变化并防抖保存 | | `stores/theme.ts` | 主题模式、系统深色偏好监听、`localStorage` 持久化 | | `types/board.ts` | `Tool`、`ElementKind`、`BoardElement`、`BoardSnapshot`、`ThemeMode` 等类型 | | `config/boardTools.ts` | 工具清单、移动端工具集合、元素默认值与存储 key | | `utils/board.ts` | 快照深拷贝、本地数据校验、文本换行、折线包围盒计算 | | `views/board/components/` | 画布、顶部栏、工具栏、画布控制、主菜单、素材库、帮助/分享对话框等展示组件 | ### 数据模型 ```ts type Tool = 'select' | 'hand' | 'draw' | 'eraser' | 'sticky' | 'text' | 'rectangle' | 'ellipse' | 'arrow' type ElementKind = Exclude type BoardElement = { id: string kind: ElementKind x: number y: number width: number height: number text: string color: string points?: { x: number; y: number }[] } ``` 便签、文字、矩形和椭圆用 `x/y/width/height` 描述包围盒;箭头把 `width/height` 当作终点相对起点的偏移;自由绘制额外使用 `points` 保存折线轨迹。画布将 `viewSize`、`zoom` 和 `viewOrigin` 换算成 SVG `viewBox`,因此缩放和平移不会修改元素自身坐标。 ### 数据流 ```text PointerEvent → BoardCanvas → useCanvasInteractions → elements / viewOrigin / zoom │ ├─→ BoardCanvas 重新渲染 └─→ useBoardStorage(350ms 防抖)→ localStorage ``` ### 本地存储 | Key | 内容 | | ----------------------------------- | ---------------------------------- | | `collab-board:local-board:v1` | `{ title, elements }` 的 JSON 快照 | | `collab-board:local-board:v1:theme` | `light`、`dark` 或 `system` | 当前只有本地持久化,没有账号、服务端同步或多人协作。清除浏览器站点数据会丢失白板;需要备份时应通过菜单导出 SVG。 ## 使用说明与快捷键 快捷键在输入框或编辑框获得焦点时不生效;在编辑文字时按 `Esc` 会先退出输入状态。 | 操作 | 方式 | | ------------- | ------------------------------------------ | | 选择工具 | `V` | | 移动画布工具 | `H` | | 自由绘制 | `P`(按下即开始记录轨迹) | | 橡皮擦 | `E`(点击元素删除) | | 便签 | `N` | | 文字 | `T` | | 矩形 | `R` | | 椭圆 | `O` | | 箭头 | `A` | | 创建元素 | 选择对应工具后,在画布上按住左键拖动 | | 编辑便签/文字 | 双击元素 | | 结束文字编辑 | `Ctrl + Enter`、点击其他位置或让编辑框失焦 | | 删除选中元素 | `Delete` 或 `Backspace` | | 撤销 | `Ctrl/⌘ + Z` | | 重做 | `Ctrl/⌘ + Shift + Z` 或 `Ctrl/⌘ + Y` | | 缩放 | `Ctrl/⌘ + 滚轮`,或左下角 `+` / `−` 按钮 | | 重置缩放 | 点击左下角百分比 | | 平移画布 | 选择 `H` 工具拖动,或按住 `空格` 拖动 | | 关闭浮层 | `Esc` | | 导出 SVG | 左上角菜单 → 导出 SVG | | 清空画布 | 左上角菜单 → 清空画布(可撤销) | | 切换主题 | 左上角菜单 → 外观,或在关于页切换 | ## 已知限制与后续方向 **当前限制:** - 没有后端服务、账号、权限和实时协作;分享入口只显示“在线协作服务尚未连接”。 - 素材库只有空状态与占位提示,尚未实现素材保存、复用和公共素材浏览。 - 白板只保存在当前浏览器,换设备或清除站点数据后无法恢复,也没有导入功能。 - 尚未接入 CI 流水线;本地单元测试已就绪(`pnpm test` 运行 Vitest)。 - Tailwind CSS、PostCSS、Autoprefixer 已声明但未接入,后续需要决定接入还是移除(Naive UI 已接入并用于交互组件)。 - 菜单中的 `Ctrl/⌘ + Shift + E` 导出快捷键尚未实现,导出需要走菜单。 **可继续演进的方向:** 1. 补齐 `apps/backend` 服务,提供白板持久化、分享链接和权限控制。 2. 接入 WebSocket 或 CRDT,实现多人实时协作、光标与在线状态。 3. 完成素材库的保存、分类和复用能力。 4. 补充端到端测试,并配置 CI 流水线(单元测试已就绪)。 5. 根据实际样式方案决定 Tailwind CSS、PostCSS 等剩余依赖的接入或清理。 ## 开发规范 ### 代码与类型 - 使用 Vue 3 Composition API 和 `