feat: 初始化 React + shadcn/ui 项目 - 列阵工作台重构

This commit is contained in:
admin
2026-06-02 00:46:16 +08:00
commit d962256165
68 changed files with 17092 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
## 1. 产品概述
列阵(Array)人类工作台——一个让人机协作变得自然的实时工作台。AI Worker 和 Human Worker 在同一个任务中通过消息实时协作,人类通过本前端查看任务、发送消息、上传附件、评价 AI 表现。
- 目标用户:需要与 AI 协作完成任务的碳基工作者(运营、标注、审核、开发等)
- 核心价值:让人机协作像人类之间聊天一样自然,同时保留任务管理的结构化能力
## 2. 核心功能
### 2.1 用户角色
| 角色 | 注册方式 | 核心权限 |
|------|----------|----------|
| Human Worker | API Key / 用户名密码 / 邀请码注册 | 创建任务、发送消息、上传附件、评价 AI、管理组织 |
| AI Worker | 后端自动注册 | 自动接收任务、回复消息、提交结果 |
| 组织 Admin | 组织创建者自动获得 | 管理成员、分类、邀请码 |
### 2.2 功能模块
1. **登录页**:API Key 登录、用户名密码登录、注册新账户、邀请码注册
2. **主工作台**:三栏布局——成员面板、任务列表、聊天详情
3. **消息系统**:公共消息、私密消息、@提及、引用回复、附件上传、乐观消息
4. **任务管理**:创建任务、状态流转、参与者管理、分类筛选、置顶
5. **组织管理**:组织切换、成员管理、邀请码、角色权限
6. **个人中心**:资料编辑、快捷键设置、主题切换、语言切换
7. **评价系统**:任务完成后对 AI 评分、能力标签、评价汇总
### 2.3 页面详情
| 页面 | 模块 | 功能描述 |
|------|------|----------|
| 登录页 | 服务器地址输入 | 输入后端 API 地址,支持历史记录 |
| 登录页 | API Key 登录 | 输入 API Key 直接登录 |
| 登录页 | 用户名密码登录 | 切换到密码模式登录 |
| 登录页 | 注册表单 | 名称、用户名、密码、邀请码、组织、位置、简介、标签、能力 |
| 主工作台 | 成员面板 | 显示在线/离线 AI 和 Human Worker,点击发起对话 |
| 主工作台 | 任务列表 | 全部/我发起的/我参与的 Tab,分类筛选,置顶,排序 |
| 主工作台 | 聊天详情 | 消息流、引用回复、@提及、私密消息、附件、状态切换 |
| 主工作台 | 任务抽屉 | 参与者列表、添加/移除成员、评价入口 |
| 组织管理 | 组织切换器 | 顶栏下拉切换组织 |
| 组织管理 | 组织详情弹窗 | 成员列表、邀请码管理、角色变更、重命名 |
| 个人中心 | 资料编辑 | 名称、简介、组织、位置、标签、能力、最大并发 |
| 个人中心 | 快捷键设置 | 发送键(Enter/Ctrl+Enter)、换行键配置 |
| 评价系统 | 评价提交 | 1-5 星评分、能力标签、评论、建议 |
| 评价系统 | 评价汇总 | 平均分、总评价数、常见标签、评价列表 |
## 3. 核心流程
### 3.1 登录与初始化
用户打开应用 → 输入服务器地址和凭据 → SDK 认证获取 JWT → 加载 Workers/Tasks/Categories → 建立 WebSocket 连接 → 进入主工作台
```mermaid
flowchart TD
A["用户打开应用"] --> B{"本地有凭据?"}
B -->|是| C["自动登录"]
B -->|否| D["显示登录页"]
D --> E["输入凭据"]
E --> F["SDK 认证"]
C --> F
F --> G{"认证成功?"}
G -->|否| D
G -->|是| H["加载 Workers + Tasks"]
H --> I["建立 WebSocket"]
I --> J["进入主工作台"]
```
### 3.2 消息收发
用户选择任务 → 加载历史消息 → 输入回复 → 乐观插入 pending 消息 → API 发送 → 成功移除 pending / 失败显示重试 → WebSocket 推送新消息 → 更新消息列表
```mermaid
flowchart TD
A["选择任务"] --> B["加载历史消息"]
B --> C["用户输入回复"]
C --> D["乐观插入 pending 消息"]
D --> E["API 发送"]
E --> F{"发送成功?"}
F -->|是| G["移除 pending"]
F -->|否| H["标记 failed,显示重试"]
G --> I["WS 推送确认"]
I --> J["更新消息列表"]
```
### 3.3 人机协作
人类创建任务 → 邀请 AI Worker → AI 自动接收 → AI 发送处理中消息 → AI 提交结果 → 人类查看结果 → 人类评价 AI 表现
```mermaid
flowchart TD
A["人类创建任务"] --> B["邀请 AI Worker"]
B --> C["AI 自动接收任务"]
C --> D["AI 发送处理中消息"]
D --> E["AI 提交结果"]
E --> F["人类查看结果"]
F --> G["人类评价 AI"]
```
## 4. 用户界面设计
### 4.1 设计风格
- **主色调**:深空蓝(#0f172a)为底,电光蓝(#3b82f6)为强调色,搭配 zinc 灰阶
- **按钮风格**:圆角(rounded-lg),主按钮实色填充,次按钮边框+hover 填充
- **字体**:JetBrains Mono(代码/ID)+ Noto Sans SC(中文正文)+ Geist(英文正文)
- **布局风格**:三栏布局,左栏 240px 固定,中栏 flex 自适应,右栏 480px 可拖拽
- **图标**:Lucide React 图标库
- **动画**:消息入场 slide-up + fade,状态切换 scale 微动,LED 矩阵空状态动画
### 4.2 页面设计概览
| 页面 | 模块 | UI 元素 |
|------|------|---------|
| 登录页 | 整体 | 居中卡片,深色渐变背景,LED Logo 动画,表单居中 |
| 主工作台 | 顶栏 | Logo + 品牌名 + 组织切换 + 身份徽章 + 主题/语言/刷新/退出按钮 + WS 状态灯 |
| 主工作台 | 成员面板 | 搜索框 + 在线/离线分组 + AI/Human 标签 + 在线状态圆点 |
| 主工作台 | 任务列表 | Tab 栏 + 分类筛选 + 任务卡片(标题+最后消息+参与者头像+未读徽章+置顶图标) |
| 主工作台 | 聊天详情 | 标题栏(可编辑)+ 状态/优先级徽章 + 公共/私密 Tab + 消息流 + 回复输入栏 |
| 主工作台 | 消息气泡 | AI 左对齐蓝色渐变头像 + Human 右对齐绿色头像 + 系统消息居中灰色胶囊 |
| 主工作台 | 附件渲染 | 图片内联预览 + 视频缩略图+播放 + 音频播放器 + 文件卡片 |
| 组织管理 | 组织详情 | Sheet 侧滑面板 + 成员列表 + 邀请码表格 + 角色下拉 |
| 个人中心 | 资料编辑 | Dialog 弹窗 + 表单字段 + 标签输入 + 快捷键选择器 |
| 评价系统 | 评价提交 | Dialog 弹窗 + 星级评分 + 标签输入 + 文本域 |
| 评价系统 | 评价汇总 | Dialog 弹窗 + 统计卡片 + 标签云 + 评价列表 |
### 4.3 响应式设计
- **桌面端(≥1024px)**:三栏并排,右栏可拖拽调整宽度
- **平板端(768~1023px)**:左栏收为窄竖条图标栏,中栏+右栏双栏
- **移动端(<768px)**:单栏切换,任务列表和聊天详情互斥显示,底部导航
### 4.4 空状态设计
- 无任务时:16×16 LED 矩阵动画(贪吃蛇、俄罗斯方块、烟花、文字滚动等)
- 无消息时:引导卡片"添加参与者开始协作"
- 无通知时:简洁空状态图标+文案
+459
View File
@@ -0,0 +1,459 @@
## 1. 架构设计
```mermaid
flowchart TB
subgraph Frontend["前端 (React + Vite)"]
UI["shadcn/ui 组件层"]
Pages["页面层"]
Hooks["Hooks 层"]
Store["Zustand 状态管理"]
SDK["array-sdk 通信层"]
end
subgraph Backend["后端 (FastAPI + SQLite)"]
API["REST API"]
WS["WebSocket"]
end
UI --> Pages
Pages --> Hooks
Hooks --> Store
Hooks --> SDK
Store --> SDK
SDK --> API
SDK --> WS
```
## 2. 技术说明
- **前端框架**:React 18 + TypeScript + Vite 6
- **UI 组件库**:shadcn/ui(Radix UI + Tailwind CSS)
- **状态管理**:Zustand(轻量、无 boilerplate、支持中间件)
- **样式方案**:Tailwind CSS 4 + CSS 变量主题系统
- **图标**:Lucide React
- **动画**:Framer Motion
- **Markdown 渲染**:react-markdown + remark-gfm + rehype-highlight
- **HTTP/WebSocket**:array-sdk(从旧项目迁移,纯 JS 无框架依赖)
- **路由**:React Router DOM v7(Hash 模式,兼容静态部署)
- **国际化**:自定义 i18n hook(中/英双语)
- **PWA**:vite-plugin-pwa
- **项目初始化**:vite-init react-ts 模板
## 3. 路由定义
| 路由 | 用途 |
|------|------|
| `/` | 登录页(未登录时)或主工作台(已登录时) |
| `/task/:taskId` | 主工作台,自动选中指定任务 |
## 4. API 定义
本项目为纯前端,所有 API 调用通过 array-sdk 与后端通信。SDK 封装层提供以下接口:
### 4.1 认证
```typescript
interface AuthAPI {
login(serverUrl: string, apiKey: string): Promise<Worker>
loginByPassword(serverUrl: string, username: string, password: string): Promise<Worker>
register(serverUrl: string, name: string, username: string, password: string, profile?: RegisterProfile): Promise<Worker>
autoLogin(): Promise<Worker | null>
logout(): void
}
```
### 4.2 Worker
```typescript
interface WorkerAPI {
getWorkers(params?: { orgId?: string }): Promise<Worker[]>
getWorkerProfile(workerId: string): Promise<Worker>
updateProfile(data: Partial<WorkerProfile>): Promise<Worker>
getWorkerWorkload(workerId: string): Promise<Workload>
discoverWorkers(options: DiscoverOptions): Promise<DiscoverResult>
}
```
### 4.3 Task
```typescript
interface TaskAPI {
getTasks(params?: { orgId?: string; categoryId?: string }): Promise<Task[]>
getTask(taskId: string): Promise<Task>
createTask(title: string, participantIds: string[], priority?: string, extra?: CreateTaskExtra): Promise<Task>
updateTaskStatus(taskId: string, status: TaskStatus): Promise<Task>
updateTaskTitle(taskId: string, title: string): Promise<Task>
addParticipant(taskId: string, workerId: string): Promise<Participant[]>
removeParticipant(taskId: string, workerId: string): Promise<void>
}
```
### 4.4 Message
```typescript
interface MessageAPI {
getMessages(taskId: string, scope?: string, includePrivate?: boolean, limit?: number, page?: number): Promise<Message[]>
sendMessage(taskId: string, content: string, options?: SendMessageOptions): Promise<Message>
sendMessageWithAttachments(taskId: string, content: string, fileIds: string[], options?: SendMessageOptions): Promise<Message>
}
```
### 4.5 Attachment
```typescript
interface AttachmentAPI {
uploadAttachment(taskId: string, file: File): Promise<Attachment>
listAttachments(taskId: string): Promise<{ attachments: Attachment[]; total: number }>
deleteAttachment(taskId: string, attachmentId: string): Promise<void>
getAttachmentUrl(taskId: string, attachmentId: string): string
}
```
### 4.6 Organization
```typescript
interface OrganizationAPI {
listMyOrganizations(): Promise<Organization[]>
getOrganization(orgId: string): Promise<Organization>
createOrganization(name: string, description?: string): Promise<Organization>
updateOrganization(orgId: string, data: Partial<Organization>): Promise<Organization>
createInvite(orgId: string, options?: InviteOptions): Promise<OrgInvite>
listInvites(orgId: string): Promise<OrgInvite[]>
joinOrganization(inviteCode: string): Promise<JoinResult>
listOrgMembers(orgId: string): Promise<OrgMember[]>
removeOrgMember(orgId: string, workerId: string): Promise<void>
updateMemberRole(orgId: string, workerId: string, role: 'admin' | 'member'): Promise<OrgMember>
renameOrgMember(orgId: string, workerId: string, name: string): Promise<OrgMember>
}
```
### 4.7 Category
```typescript
interface CategoryAPI {
createCategory(name: string, orgId?: string): Promise<Category>
listCategories(orgId?: string): Promise<Category[]>
updateCategory(categoryId: string, opts: Partial<Category>): Promise<Category>
deleteCategory(categoryId: string): Promise<boolean>
}
```
### 4.8 Review
```typescript
interface ReviewAPI {
createReview(workerId: string, params: CreateReviewParams): Promise<Review>
getWorkerReviews(workerId: string, options?: PaginationOptions): Promise<ReviewListResult>
getReviewSummary(workerId: string): Promise<ReviewSummary>
}
```
### 4.9 Notification
```typescript
interface NotificationAPI {
getNotifications(isRead?: boolean): Promise<Notification[]>
markNotificationRead(notificationId: string): Promise<void>
markAllNotificationsRead(): Promise<void>
}
```
### 4.10 WebSocket
```typescript
interface WebSocketAPI {
startWebSocket(callbacks: WSCallbacks): () => void
stopWebSocket(): void
}
interface WSCallbacks {
onEvent: (event: WSEvent) => void
onConnected: () => void
onDisconnected: () => void
onError: (err: Error) => void
}
type WSEventType = 'task.message' | 'participant.joined' | 'task.updated' | 'worker_status_changed' | 'tasks.pending_recovery'
```
## 5. 数据模型
```mermaid
erDiagram
Organization ||--o{ Category : "has"
Organization ||--o{ OrgMember : "has"
Organization ||--o{ OrgInvite : "has"
Worker ||--o{ Task : "creates"
Worker ||--o{ Review : "receives"
Task ||--o{ Participant : "has"
Task ||--o{ Message : "has"
Task ||--o{ Attachment : "has"
Task }o--|| Category : "belongs_to"
Message }o--o| Message : "reply_to"
Message }o--o| Worker : "private_to"
Worker {
string id PK
string name
string type
string status
string organization
string location
string description
string[] tags
object capabilities
number maxConcurrentTasks
}
Task {
string id PK
string title
string description
string status
string priority
string creator_id FK
string org_id FK
string category_id FK
datetime created_at
datetime last_message_at
}
Message {
string id PK
string task_id FK
string sender_id FK
string content
string type
string reply_to_id FK
string private_to_id FK
datetime created_at
}
Attachment {
string id PK
string task_id FK
string filename
string content_type
number size
string url
string thumbnail_url
number width
number height
number duration
}
Review {
string id PK
string worker_id FK
string task_id FK
string reviewer_id FK
number rating
string[] capability_tags
string comment
string suggestion
}
```
## 6. 项目目录结构
```
human-adapter-react/
├── src/
│ ├── components/ # UI 组件
│ │ ├── ui/ # shadcn/ui 基础组件
│ │ ├── layout/ # 布局组件
│ │ │ ├── TopBar.tsx
│ │ │ ├── WorkersPanel.tsx
│ │ │ ├── TaskList.tsx
│ │ │ └── Resizer.tsx
│ │ ├── chat/ # 聊天相关组件
│ │ │ ├── ChatDetail.tsx
│ │ │ ├── MessageList.tsx
│ │ │ ├── MessageBubble.tsx
│ │ │ ├── MessageInput.tsx
│ │ │ ├── MentionPopup.tsx
│ │ │ ├── ReplyPreview.tsx
│ │ │ ├── AttachmentRenderer.tsx
│ │ │ ├── DMTab.tsx
│ │ │ └── ScrollToBottom.tsx
│ │ ├── task/ # 任务相关组件
│ │ │ ├── TaskCard.tsx
│ │ │ ├── TaskComposer.tsx
│ │ │ ├── TaskDrawer.tsx
│ │ │ ├── StatusSelector.tsx
│ │ │ └── ParticipantManager.tsx
│ │ ├── org/ # 组织相关组件
│ │ │ ├── OrgSwitcher.tsx
│ │ │ ├── OrgDetailSheet.tsx
│ │ │ └── InviteManager.tsx
│ │ ├── profile/ # 个人中心组件
│ │ │ ├── ProfileDialog.tsx
│ │ │ └── ShortcutSettings.tsx
│ │ ├── review/ # 评价组件
│ │ │ ├── ReviewDialog.tsx
│ │ │ └── ReviewSummary.tsx
│ │ ├── auth/ # 登录组件
│ │ │ └── LoginPage.tsx
│ │ └── shared/ # 共享组件
│ │ ├── LedLogo.tsx
│ │ ├── LedMatrix.tsx
│ │ ├── RobotIcon.tsx
│ │ ├── WorkerAvatar.tsx
│ │ ├── Toast.tsx
│ │ └── PwaInstallBanner.tsx
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useAuth.ts
│ │ ├── useMessages.ts
│ │ ├── useWorkers.ts
│ │ ├── useTasks.ts
│ │ ├── useOrganization.ts
│ │ ├── useNotifications.ts
│ │ ├── useReviews.ts
│ │ ├── useProfile.ts
│ │ ├── useWebSocket.ts
│ │ ├── useI18n.ts
│ │ ├── useTheme.ts
│ │ ├── useAutoScroll.ts
│ │ └── useDrafts.ts
│ ├── stores/ # Zustand Store
│ │ ├── authStore.ts
│ │ ├── appStore.ts
│ │ └── uiStore.ts
│ ├── lib/ # 工具库
│ │ ├── sdk.ts # array-sdk 封装
│ │ ├── api.ts # API 薄封装层
│ │ ├── i18n.ts # 国际化字典
│ │ ├── utils.ts # 通用工具函数
│ │ └── constants.ts # 常量定义
│ ├── pages/ # 页面
│ │ ├── LoginPage.tsx
│ │ └── WorkbenchPage.tsx
│ ├── App.tsx
│ ├── main.tsx
│ └── index.css # Tailwind 入口 + CSS 变量
├── public/
├── sdk-js/ # array-sdk 源码(从旧项目迁移)
│ ├── src/
│ │ ├── client.ts
│ │ ├── adapter.ts
│ │ └── index.ts
│ └── package.json
├── .trae/documents/
├── package.json
├── tsconfig.json
├── vite.config.ts
├── tailwind.config.js
├── components.json # shadcn/ui 配置
└── postcss.config.js
```
## 7. 关键架构决策
### 7.1 状态管理:Zustand vs Context
选择 Zustand 的理由:
- 无 Provider 包裹,任何组件直接 `useStore()` 即可
- 支持 `subscribeWithSelector` 中间件,精确控制重渲染
- 支持 `persist` 中间件,自动持久化到 localStorage
- 比 Redux 少 90% boilerplate,比 Jotai 更适合复杂状态
```typescript
// appStore.ts 示例
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface AppState {
workers: Worker[]
tasks: Task[]
selectedTaskId: string | null
wsConnected: boolean
setWorkers: (workers: Worker[]) => void
setTasks: (tasks: Task[]) => void
selectTask: (id: string | null) => void
}
export const useAppStore = create<AppState>()(
persist(
(set) => ({
workers: [],
tasks: [],
selectedTaskId: null,
wsConnected: false,
setWorkers: (workers) => set({ workers }),
setTasks: (tasks) => set({ tasks }),
selectTask: (id) => set({ selectedTaskId: id }),
}),
{ name: 'array-app-store', partialize: (s) => ({ selectedTaskId: s.selectedTaskId }) }
)
)
```
### 7.2 消息系统:自定义 Hook
不使用 Vercel AI SDK 的 `useChat()`,因为:
- 本项目的消息模型更复杂(私密消息、@提及、引用回复、附件)
- WebSocket 事件格式是后端自定义的
- 需要精确控制乐观消息的生命周期
自定义 `useMessages` Hook 借鉴 AI SDK 的状态机思想:
```typescript
type MessageStatus = 'idle' | 'sending' | 'streaming' | 'error'
interface UseMessagesReturn {
messages: Message[]
pendingMessages: PendingMessage[]
isLoading: boolean
isAtBottom: boolean
sendMessage: (content: string, options?: SendOptions) => Promise<void>
retryMessage: (pendingId: string) => Promise<void>
cancelMessage: (pendingId: string) => void
loadMore: () => Promise<void>
hasMore: boolean
scrollToBottom: () => void
}
```
### 7.3 主题系统:Tailwind CSS 变量
```css
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--accent: 221.2 83.2% 53.3%;
/* ... shadcn 默认变量 */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--accent: 217.2 91.2% 59.8%;
}
```
切换主题只需在 `<html>` 上添加/移除 `dark` 类,所有组件自动响应。
### 7.4 SDK 迁移策略
array-sdk 是纯 JavaScript,无框架依赖,直接迁移:
- `sdk-js/` 目录整体复制到新项目
- `package.json` 中 `"array-sdk": "file:./sdk-js"` 保持不变
- API 封装层 `lib/api.ts` 从旧项目 `api/index.js` 翻译为 TypeScript
- 所有方法签名添加 TypeScript 类型
### 7.5 组件拆分原则
旧项目 TaskDetail.vue 72KB → 拆分为 8+ 个子组件:
| 旧代码位置 | 新组件 | 职责 |
|------------|--------|------|
| 消息流模板 | `MessageList.tsx` | 消息列表渲染 + 时间分割线 |
| 单条消息 | `MessageBubble.tsx` | 消息气泡 + 头像 + 引用 + 附件 |
| 回复输入栏 | `MessageInput.tsx` | 输入框 + 发送按钮 + 快捷键 |
| @提及弹窗 | `MentionPopup.tsx` | 搜索 + 选择参与者 |
| 私密消息 | `DMTab.tsx` | 私聊通道列表 + 消息 |
| 附件渲染 | `AttachmentRenderer.tsx` | 图片/视频/音频/文件智能渲染 |
| 任务抽屉 | `TaskDrawer.tsx` | 参与者 + 详情 + 评价 |
| 状态选择器 | `StatusSelector.tsx` | 状态下拉切换 |