feat: 新增开发规划

This commit is contained in:
duanshuwen
2026-04-14 07:36:40 +08:00
parent 66db6c462e
commit b3f07c4cfe
8 changed files with 1050 additions and 34 deletions

248
ChatPageMigrationPlan.md Normal file
View File

@@ -0,0 +1,248 @@
# Chat 对话功能迁移重构计划
## 参考来源
- **源文件**: `ClawX/src/pages/Chat/index.tsx` 及其子组件
- **目标文件**: `zn-ai/src/pages/home/index.vue``zn-ai/src/pages/home/ChatBox.vue`
## 源实现思路分析
`ClawX/src/pages/Chat/index.tsx` 的核心架构:
1. **状态层集中化**所有聊天状态messages、sending、loading、error、streamingMessage 等)托管在 `useChatStore`Zustand页面组件只负责订阅与渲染。
2. **组件原子化**
- `ChatMessage`单条消息渲染markdown、thinking、tool_use、附件图片
- `ChatInput`:输入框 + 发送/停止按钮。
- `ChatToolbar`会话选择器、thinking 显隐切换、刷新按钮。
- `ExecutionGraphCard`:用户消息后的任务执行可视化卡片。
3. **流式消息处理**:通过 `streamingMessage``streamingTools` 实现未落库前的增量渲染;`pendingFinal` 标识等待最终响应的状态。
4. **生命周期管理**
- 切走页面时调用 `cleanupEmptySession()` 清理空会话。
- 加载历史消息时保留乐观用户消息,避免闪烁。
- 轮询 + 安全超时机制防止消息卡死。
5. **错误与加载态**
- 底部 `error` 条显示全局错误并可一键清除。
- `minLoading` 在 history 加载时显示透明遮罩 + LoadingSpinner。
- 发送中显示 `TypingIndicator` / `ActivityIndicator`
6. **WelcomeScreen**:当 `messages.length === 0 && !sending` 时展示欢迎页 + 快捷操作按钮。
---
## 迁移目标
`ChatBox.vue` 中沉淀的 880+ 行“上帝组件”逻辑解耦,引入 **Pinia Store + 原子组件** 的 ClawX 式架构,同时保留 zn-ai 现有的视觉风格(蓝色主题、头像布局、输入框样式)。
---
## 实现步骤
### 1. 提取 Pinia Chat Store状态层迁移
新建 `zn-ai/src/store/chat.ts`,将 `ChatBox.vue` 中的以下逻辑迁入 Store
| ChatBox.vue 现有逻辑 | Store 对应设计 |
|---|---|
| `chatMsgList` | `state.messages` |
| `isSendingMessage` | `state.sending` |
| `isSessionActive` | `state.pendingFinal` / `state.loading` |
| WebSocket 管理(`initWebSocket``sendWebSocketMessage` | Store Actions`initConnection``sendMessage``stopRun` |
| `pendingMap` / `pendingTimeouts` 超时回退 | Store 内部 Map + 定时器(不暴露给 UI |
| `handleWebSocketMessage` | Store Action`handleStreamEvent` |
| `loadConversationMessages` | Store Action`loadHistory(sessionId)` |
| `createConversationRequest` | Store Action`createSession()` |
| `resetConversation` / `cleanup` | Store Action`resetSession()` |
**Store 核心 State 设计**
```ts
interface ChatState {
messages: ChatMessage[]; // 当前会话消息列表
sending: boolean; // 是否正在发送/等待回复
loading: boolean; // 是否正在加载历史
error: string | null; // 全局错误提示
streamingMessage: Partial<ChatMessage> | null; // 流式增量消息
currentSessionId: string; // 当前会话 ID
}
```
> **说明**zn-ai 当前使用 WebSocket 直联后端ClawX 使用 Gateway RPC本次迁移**只搬架构、不搬协议**Store 内部仍继续使用现有的 `WebSocketManager`。
### 2. 组件拆分UI 层迁移)
`ChatBox.vue` 按职责拆成以下子组件(放置于 `zn-ai/src/pages/home/components/chat/`
#### 2.1 `ChatMessage.vue`
职责:渲染单条消息。
- Props`msg: ChatMessage``isStreaming?: boolean`
- 复用现有 `ChatRoleMe.vue``ChatRoleAI.vue``ChatAvatar.vue``ChatNameTime.vue``ChatAttach.vue``ChatAIMark.vue``ChatOperation.vue` 的能力。
- **新增**:支持 `streamingMessage` 的渲染(内容可能未完成)。
#### 2.2 `ChatInput.vue`(由现有 `ChatInputArea.vue` 升级)
职责:输入框 + 发送/停止按钮。
- Props`modelValue: string``sending: boolean``disabled?: boolean`
- Events`send``stop``update:modelValue``attach`
- **新增**:当 `sending = true` 时按钮显示停止图标并触发 `stop`
#### 2.3 `ChatEmpty.vue`WelcomeScreen 等价物)
职责:空会话欢迎页。
- 迁移现有 `ChatBox.vue` 中的引导页 DOM大标题“你好我今天能帮你什么
- 可保留现有的 `TaskCenter` 插槽或快捷操作按钮区域。
#### 2.4 `ChatErrorBar.vue`
职责:底部错误条。
- Props`error: string | null`
- Events`dismiss`
- 样式参考 ClawX 的红色背景条:`bg-red-50` + 左侧 `AlertCircle` 图标 + 右侧“Dismiss”按钮。
#### 2.5 `ChatLoadingOverlay.vue`(可选)
职责history 加载时透明遮罩 + LoadingSpinner。
- 若 zn-ai 现有 `ChatLoading.vue` 已满足,可直接复用。
### 3. ChatBox.vue 瘦身改造
改造后 `ChatBox.vue` 只承担:**Store 订阅 + 组件拼装 + 少量 prop 透传**。
```html
<template>
<div class="flex flex-col h-full py-6 px-6 overflow-hidden">
<!-- 空状态 -->
<ChatEmpty v-if="isEmpty" />
<!-- 消息列表 -->
<div v-else ref="listRef" class="flex-1 overflow-y-auto py-6 space-y-6">
<div
v-for="msg in chatStore.messages"
:key="msg.messageId"
class="flex items-start gap-3"
:class="msg.messageRole === MessageRole.ME ? 'justify-end' : 'justify-start'"
>
<ChatAvatar v-if="msg.messageRole === MessageRole.AI" :src="aiAvatar" />
<ChatMessage :msg="msg" />
<ChatAvatar v-if="msg.messageRole === MessageRole.ME" :src="userAvatar" />
</div>
<!-- 流式消息占位(未落库前的 AI 回复) -->
<div v-if="chatStore.streamingMessage" class="flex items-start gap-3 justify-start">
<ChatAvatar :src="aiAvatar" />
<ChatMessage :msg="chatStore.streamingMessage" is-streaming />
</div>
<!-- 发送中 Indicator -->
<ChatTypingIndicator v-if="showTypingIndicator" />
</div>
<!-- 错误条 -->
<ChatErrorBar :error="chatStore.error" @dismiss="chatStore.clearError()" />
<!-- 输入区 -->
<div class="flex flex-col gap-3 mt-4">
<ChatInput
v-model="inputMessage"
:sending="chatStore.sending"
@send="onSend"
@stop="chatStore.stopRun()"
@attach="onAttach"
/>
</div>
</div>
</template>
```
```ts
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useChatStore } from '@store/chat';
import { MessageRole } from './model/ChatModel';
// ... 引入各子组件
const chatStore = useChatStore();
const inputMessage = ref('');
const isEmpty = computed(() =>
chatStore.messages.length === 0 && !chatStore.sending && !chatStore.streamingMessage
);
const showTypingIndicator = computed(() =>
chatStore.sending && !chatStore.streamingMessage
);
const onSend = () => {
const text = inputMessage.value.trim();
if (!text) return;
chatStore.sendMessage(text);
inputMessage.value = '';
};
</script>
```
### 4. 流式消息与 Indicator 支持
在 Store 的 WebSocket `onMessage` 回调中:
1. **首次收到内容**:创建 `streamingMessage`AI 角色、初始内容)。
2. **后续收到增量**`streamingMessage.messageContent += data.content`
3. **收到 finish**:将 `streamingMessage` 推入 `messages[]`,然后清空 `streamingMessage`,结束 `sending`
4. **异常/超时**:清空 `streamingMessage` 并设置 `error`
**新增 `ChatTypingIndicator.vue`**(参考 ClawX `TypingIndicator`
- 左侧 AI 头像,右侧三个跳动的圆点,用于 `sending && !streamingMessage` 时占位。
### 5. 生命周期与页面级整合(`home/index.vue`
`home/index.vue` 中补充 Store 生命周期绑定:
```ts
import { useChatStore } from '@store/chat';
const chatStore = useChatStore();
onMounted(() => {
chatStore.initConnection();
});
onBeforeUnmount(() => {
chatStore.resetSession();
});
```
> 当用户从首页切到其他页面时,调用 `resetSession()` 关闭 WebSocket、清理定时器避免后台继续接收消息。
### 6. 历史消息加载与空会话清理
- **选择历史会话**`ChatHistory.vue``@select-chat` 事件触发 `chatStore.loadHistory(conversationId)`
- **新建对话**`@new-chat` 触发 `chatStore.createSession()`,然后清空 `messages`
- **切页清理**Store 中实现 `cleanupEmptySession()`,如果当前会话没有任何消息且不是历史来源,则自动重置 `conversationId`,避免产生幽灵会话。
### 7. 样式与兼容性保持
- **颜色主题**:继续使用 zn-ai 现有的 `#2B7FFF` 蓝色高亮、`#E5E8EE` 边框色。
- **头像布局**左右头像顺序不变AI 左、用户右)。
- **输入框样式**`ChatInput` 保留现有的圆角、阴影、发送按钮样式。
- **引导页**:保留现有的 `TaskCenter` 和欢迎文案。
---
## 涉及文件
| 新建/修改 | 文件路径 | 说明 |
|---|---|---|
| 新建 | `zn-ai/src/store/chat.ts` | Pinia Chat Store承载状态与 WebSocket 逻辑 |
| 新建 | `zn-ai/src/pages/home/components/chat/ChatMessage.vue` | 单条消息渲染(聚合现有原子组件) |
| 新建 | `zn-ai/src/pages/home/components/chat/ChatEmpty.vue` | 空会话欢迎页 |
| 新建 | `zn-ai/src/pages/home/components/chat/ChatErrorBar.vue` | 底部全局错误提示条 |
| 新建 | `zn-ai/src/pages/home/components/chat/ChatTypingIndicator.vue` | 发送中跳动圆点 |
| 升级 | `zn-ai/src/pages/home/components/ChatInputArea.vue` | 增加 `sending` 态与 `stop` 事件 |
| 重构 | `zn-ai/src/pages/home/ChatBox.vue` | 大幅瘦身,仅负责拼装子组件 |
| 调整 | `zn-ai/src/pages/home/index.vue` | 引入 Store 生命周期、事件透传 |
---
## 验收标准
- [ ] `ChatBox.vue` 代码量从 800+ 行降至 150 行以内,不再包含 WebSocket 细节。
- [ ] 新建 `chat.ts` Pinia Store 能正常初始化 WebSocket、发送消息、接收回复。
- [ ] 用户发送消息后,输入框清空,列表底部出现 `ChatTypingIndicator`;收到首包内容后切换为 `ChatMessage` 流式渲染。
- [ ] 收到完成标识后,流式消息固化到消息列表,状态恢复正常。
- [ ] 发生超时或错误时,底部出现 `ChatErrorBar`,可点击清除。
- [ ] 切换历史会话时,通过 Store 加载历史消息并正确渲染。
- [ ] 新建对话时,列表回到空状态(`ChatEmpty`)。
- [ ] 离开首页时 WebSocket 被正确关闭,无后台消息泄漏。