- 项目概述
项目名称: Web Inbox
项目简介: 一个基于 Web 的笔记/剪贴板应用。用户登录后,可以快速粘贴文本和截图,所有内容会自动实时同步到 Appwrite 后端。应用界面简洁,注重快速输入和信息捕捉的流畅体验,核心交互模仿 Heynote,但增加了图片粘贴功能。
核心功能:
用户认证: 基于 Appwrite 的邮件/密码注册和登录。
内容块(Block)系统: 每个笔记条目是一个独立的 “Block”,可以是文本或图片。
实时同步: 所有内容的创建、编辑、删除都会实时同步到 Appwrite 后端。
文本粘贴: 支持直接在页面上粘贴文本,自动创建一个新的文本 Block。
截图/图片粘贴: 支持直接粘贴剪贴板中的图片,自动上传并创建一个新的图片 Block。
键盘快捷键:
Enter: 在当前 Block 下方创建一个新的空白文本 Block 并聚焦。
Shift + Enter: 在当前文本 Block 内换行。
内容删除: 用户可以删除任意 Block,后端数据同步删除。
技术栈:
前端: React (使用 Vite 初始化)
样式: Tailwind CSS
后端 (BaaS): Appwrite (云版本或自托管)
Appwrite SDK: appwrite (Web)
- Appwrite 后端配置
这是项目的第一步,为前端提供数据存储和认证服务。
2.1. 创建 Appwrite 项目
登录你的 Appwrite Console。
创建一个新项目,命名为 web-inbox。
在项目设置中,添加一个新的 Web 平台。将 hostname 设为 localhost 用于本地开发,之后部署时再添加生产环境的域名。
2.2. 配置认证 (Auth)
导航到 Auth 服务。
在 Settings 中,启用 Email/Password 登录方式。
2.3. 配置数据库 (Database)
导航到 Databases 服务。
创建一个新的数据库,命名为 Primary Database,ID 设为 primary_db。
在该数据库中,创建一个新的集合 (Collection),命名为 Blocks,ID 设为 blocks。
为 Blocks 集合配置属性 (Attributes):
content (string, size: 10000, 非必需): 用于存储文本内容。
type (enum, elements: text, image, 必需, default: text): 用于区分 Block 类型。
order (integer, 必需): 用于排序 Block 的显示顺序。
userId (string, size: 255, 必需): 关联到创建该 Block 的用户 ID。
fileId (string, size: 255, 非必需): 如果 type 是 image,则存储 Appwrite Storage 中对应的文件 ID。
为 Blocks 集合配置权限 (Permissions):
目标: 用户只能操作自己的数据。
在集合的 Settings 标签页下,找到 Permissions。
添加一个 Role: role:member。
为这个 Role 勾选所有权限:Create, Read, Update, Delete。
这将确保登录用户 (member) 只能对自己创建的文档进行增删改查。
2.4. 配置存储 (Storage)
导航到 Storage 服务。
创建一个新的存储桶 (Bucket),命名为 Images,ID 设为 images。
为 Images 存储桶配置权限:
与数据库类似,添加一个 Role: role:member。
勾选所有权限:Create, Read, Update, Delete。
这样用户就只能上传、查看和删除自己的图片。
- 前端项目结构与实现方案
这是提供给 AI 代码编辑器的主要部分。
3.1. 项目初始化
Bash
使用 Vite 创建 React + SWC 项目
npm create vite@latest web-inbox-app — —template react-swc cd web-inbox-app
安装依赖
npm install npm install appwrite react-router-dom tailwindcss postcss autoprefixer npm install -D @tailwindcss/forms
初始化 Tailwind CSS
npx tailwindcss init -p 接下来,请 AI 根据 Tailwind CSS 官方文档配置 tailwind.config.js 和 index.css。
3.2. 目录结构
src/ |— api/ | |— appwrite.js # Appwrite 客户端初始化和所有 API 调用封装 |— components/ | |— AuthForm.jsx # 登录/注册表单组件 | |— Block.jsx # 核心组件,渲染单个文本或图片 Block | |— Inbox.jsx # 主界面,管理所有 Block 的容器 | |— Loader.jsx # 加载指示器 |— context/ | |— AuthContext.jsx # 全局管理用户认证状态 |— hooks/ | |— useDebounce.js # 用于文本输入自动保存的防抖 Hook |— pages/ | |— AuthPage.jsx # 登录/注册页面 | |— InboxPage.jsx # 应用主页面 |— App.jsx # 路由配置 |— main.jsx # 应用入口 3.3. 核心逻辑实现
Step 1: Appwrite 客户端封装 (src/api/appwrite.js)
任务: 初始化 Appwrite SDK 客户端,并导出所有与 Appwrite 交互的函数。
代码要点:
导入 Client, Account, Databases, Storage, ID。
创建并配置 Client 实例(setEndpoint, setProject)。
导出 account, databases, storage 实例。
封装函数:login(email, password), register(email, password, name), logout(), getCurrentUser()。
封装数据库函数:getBlocks(), createBlock(data), updateBlock(id, data), deleteBlock(id)。
封装存储函数:uploadImage(file), getImagePreview(fileId), deleteImage(fileId)。
Step 2: 认证流程 (src/context/AuthContext.jsx 和 src/pages/AuthPage.jsx)
任务: 创建一个全局的 AuthContext,用于存储用户信息和加载状态。构建一个受保护的路由,只有登录用户才能访问主页。
AuthContext.jsx 要点:
使用 createContext 和 useContext。
AuthProvider 组件中,使用 useState 管理 user 和 isLoading 状态。
使用 useEffect 在应用加载时调用 getCurrentUser() 检查会话。
提供 login, register, logout 方法,这些方法会调用 appwrite.js 中的函数并更新 user 状态。
App.jsx 要点:
使用 react-router-dom 设置路由。
/login 路由指向 AuthPage。
/ 根路由指向一个私有路由组件,该组件检查 AuthContext 中的 user。如果用户存在,渲染 InboxPage;否则重定向到 /login。
Step 3: 主界面与 Block 渲染 (src/pages/InboxPage.jsx 和 src/components/Inbox.jsx)
任务: 获取并展示用户的所有 Blocks。处理全局粘贴事件。
InboxPage.jsx 要点:
包含一个简单的 Header,带有 “Logout” 按钮。
主要内容区域渲染
Inbox.jsx 要点:
状态管理: 使用 useState 管理一个 blocks 数组。
数据获取: 在 useEffect 中调用 appwrite.js 的 getBlocks(),获取数据后按 order 排序并更新 blocks 状态。
粘贴事件处理:
给主容器 div 添加 onPaste 事件监听器。
在 handlePaste 函数中,检查 event.clipboardData.items。
如果内容是图片 (item.type.includes(‘image’)):
阻止默认粘贴行为。
获取文件 const file = item.getAsFile()。
调用 uploadImage(file) 上传到 Appwrite Storage。
上传成功后,获取 fileId。
调用 createBlock({ type: ‘image’, fileId, userId, order: … }) 创建新的图片 Block。
更新本地 blocks 状态,将新 Block 添加到列表末尾。
如果内容是文本,可以允许其默认粘贴到聚焦的 textarea 中,或者拦截后创建新的文本 Block。
渲染逻辑:
map 遍历 blocks 数组,为每个 block 渲染一个
传递 block 数据、更新函数和删除函数作为 props。
Step 4: 核心 Block 组件 (src/components/Block.jsx)
任务: 这是最复杂的组件,负责单个 Block 的显示、编辑、键盘事件和删除。
Props: block, onUpdate, onDelete, onCreateNew。
条件渲染:
如果 block.type === ‘image’:
在 useEffect 中,使用 getImagePreview(block.fileId) 获取图片 URL。
渲染一个 标签。
提供一个删除按钮,点击时调用 onDelete(block.$id, block.fileId)。
如果 block.type === ‘text’:
渲染一个 textarea 组件 (推荐使用自适应高度的 textarea)。
本地状态: const [content, setContent] = useState(block.content)。
自动保存:
使用 useDebounce hook。
在 useEffect 中监听 debouncedContent 的变化。
当 debouncedContent 改变且与 block.content 不同时,调用 onUpdate(block.$id, { content: debouncedContent })。
键盘事件 (onKeyDown):
if (e.key === ‘Enter’ && !e.shiftKey):
阻止默认行为 e.preventDefault()。
调用父组件传递的 onCreateNew(currentBlockOrder) 函数,以在当前 Block 下方创建新 Block。
if (e.key === ‘Enter’ && e.shiftKey):
正常换行,无需处理。
提供一个删除按钮,点击调用 onDelete(block.$id, null)。
- 开发流程建议 (给 AI)
初始化项目: 运行上述 npm create 和 npm install 命令,并设置好 Tailwind CSS。
构建 Appwrite API 层: 创建 src/api/appwrite.js,并根据第 2 节的后端配置,填入 Project ID 和 API Endpoint,并实现所有必要的 API 函数。
构建认证系统:
创建 AuthContext。
创建 AuthPage.jsx 和 AuthForm.jsx 组件,实现登录注册 UI 和逻辑。
在 App.jsx 中设置路由和私有路由逻辑。
构建主界面骨架:
创建 InboxPage.jsx 和 Inbox.jsx。
在 Inbox.jsx 中,暂时使用静态假数据渲染 Block 列表,以验证布局。
实现核心 Block 组件:
先实现文本 Block 的渲染和编辑功能。
集成 useDebounce Hook 实现自动保存。
实现 onKeyDown 处理器以支持 Enter 和 Shift + Enter。
连接数据流:
在 Inbox.jsx 中,替换假数据为真实的 getBlocks() API 调用。
将 onUpdate, onDelete, onCreateNew 等函数传递给
实现图片粘贴与上传:
在 Inbox.jsx 中实现 onPaste 事件监听器。
实现图片上传到 Appwrite Storage 和创建图片 Block 的逻辑。
在 Block.jsx 中实现图片类型的渲染逻辑。
样式和收尾:
使用 Tailwind CSS 为所有组件添加简洁的样式。
添加加载状态(Loader)和错误处理,提升用户体验。
这份详细的规划方案提供了清晰的目标、数据结构、组件划分和逻辑流程,AI 代码编辑器可以基于此方案,一步一步地生成高质量、结构化的代码。