# Chat 模块 — AI 智能对话页面

## 概述

AI 智能对话页面，支持三种对话模式：**知识库问答**、**智能体问答**、**模型聊天**。核心特性包括：SSE 流式响应、思考链(Thinking)展示、工具调用可视化、知识引用、AI UI 卡片（可嵌入外部交互）、图片上传、会话管理（创建/删除/重命名/分页加载）等。

---

## 文件结构

```
views/chat/
├── index.vue              # 主页面（1334行），状态中枢，消息流编排
├── types.ts               # 核心类型定义（BubbleMessage, ChatSseMessage, Conversation 等）
├── api/
│   └── chat.api.ts        # API 层封装（会话CRUD、聊天URL构建、选项数据获取）
├── composables/
│   └── useChatStream.ts   # SSE 流式请求（hook-fetch + SSE 解码）
├── components/
│   ├── ChatSidebar.vue    # 左侧会话列表（新建/选择/重命名/删除/无限滚动）
│   ├── ChatHeader.vue     # 顶部标题栏
│   ├── AddKbModal.vue     # 添加到知识库弹窗
│   └── SettingModal.vue   # 设置弹窗（预留）
└── param.md               # 后端 API 参数说明文档

components/Chat/           # 共享聊天组件（非 chat 视图专属）
├── MessageList.vue        # 消息列表渲染（BubbleList + Thinking + 工具卡片 + Markdown）
├── SMarkdown.vue          # Markdown 渲染（XMarkdown + kb/ai-ui-card/img 自定义 slot）
├── ChatInput.vue          # 输入框（Sender + 附件预览 + header/prefix/action 插槽）
└── AiUiCard.vue           # AI UI 卡片容器（iframe 嵌入外部 HTML 交互组件）
```

---

## 架构与数据流

```
用户输入
  ↓
index.vue handleSend()
  ├── createUserMessageWithAttachments() → messages.push(userMsg)
  ├── createAiMessage() → messages.push(aiMsg)
  ├── buildChatRequestBody() → 构建请求体（根据 targetType 决定参数）
  └── useChatStream.streamChat()
        ├── POST → /api/ai/chat/{agent-chat|knowledge-chat|model-chat}
        ├── SSE 流式接收 → parseSseBlock() → onChunk(ChatSseMessage)
        └── applyStructuredEventToMessage() → 按 response_type 分发到对应字段
              ├── agent_query / answer → message.answerText
              ├── thinking → message.thinking
              ├── tool_call → message.toolCalls[]
              ├── tool_result → message.toolResults[]
              ├── references → message.references[]
              ├── error → message.inlineErrors[]
              ├── session_title → conversations[].title
              └── complete → message.streamCompleted = true

消息渲染
  ↓
MessageList.vue → BubbleList
  ├── Thinking 组件（思考内容，Markdown 渲染）
  ├── 工具调用卡片（tool_call / tool_result）
  ├── SMarkdown（Markdown 内容 + 自定义标签）
  │     ├── <kb> → 知识引用 slot
  │     ├── <ai-ui-card> → AiUiCard 组件（iframe）
  │     └── <img> → el-image 预览
  └── 操作栏（复制、添加到知识库）
```

---

## 三种对话模式

| 模式       | targetType  | API 路径                      | 关键参数                                                    |
| ---------- | ----------- | ----------------------------- | ----------------------------------------------------------- |
| 知识库问答 | `knowledge` | `/api/ai/chat/knowledge-chat` | `knowledge_base_ids`, `knowledge_ids`                       |
| 智能体问答 | `agent`     | `/api/ai/chat/agent-chat`     | `agent_id`, `images`, `agent_enabled`, `web_search_enabled` |
| 模型聊天   | `model`     | `/api/ai/chat/model-chat`     | `summary_model_id`                                          |

**公共参数**：`session_id`, `query`, `summary_model_id`, `disable_title`, `enable_memory`, `channel`（固定 `web`）

---

## SSE 响应协议 (response_type)

| 类型            | 说明           | 附带数据                                                                              |
| --------------- | -------------- | ------------------------------------------------------------------------------------- |
| `agent_query`   | Agent 开始处理 | `assistant_message_id`, `session_id`                                                  |
| `thinking`      | 思考内容       | `content`                                                                             |
| `tool_call`     | 工具调用       | `data: { tool_call_id, tool_name, arguments }`                                        |
| `tool_result`   | 工具结果       | `data: { success, output, error, thought, duration_ms, content_items, display_type }` |
| `references`    | 知识引用       | `data: { knowledge_references: [...] }`                                               |
| `answer`        | 回答内容       | `content`                                                                             |
| `error`         | 错误信息       | `content`, `data.error`                                                               |
| `session_title` | 会话标题       | `data: { title, session_id }`                                                         |
| `complete`      | 流结束         | `data: { total_duration_ms, total_steps }`                                            |

**注意**：回答中可能包含 `<think>...</think>` 标签，需通过 `parseAnswerThinkState()` 提取思考内容。

---

## 核心状态

```typescript
// index.vue 主要响应式状态
const conversations = ref<Conversation[]>([])        // 会话列表（分页）
const sessionConfigMap = reactive<Record<string, ChatTargetConfig>>({})  // 每个会话的独立配置
const activeConversationId = ref('')                 // 当前激活会话
const activeTargetType = ref<ChatTargetType>('agent') // 当前对话模式
const messages = ref<BubbleMessage[]>([])            // 当前消息列表
const settingsDraft = reactive<ChatTargetConfig>(...) // 当前设置草稿（知识库/模型/智能体选择）
const currentAttachments = ref<WorkflowUploadFile[]>([]) // 当前附件
const isLoading = ref(false)                         // 流式请求中
```

**会话配置持久化**：

- `sessionConfigMap` 内存缓存每个会话的配置
- 选择会话时通过 `getConversationConfig()` 恢复配置（缓存 > 历史 > 默认）
- `cloneConfig()` 深拷贝配置对象避免引用污染

---

## AiUiCard 卡片系统

`<ai-ui-card>` 是 AI 返回的特殊 HTML 标签，XMarkdown 自动将其转为 slot，由 `AiUiCard.vue` 渲染。

**渲染方式**：iframe 嵌入外部 HTML 页面

- 通过 `window.BpmTools.$$make_ai_card_item_iframe(html)` 获取 iframe URL（宿主环境注入）
- iframe 内容自适应高度：`ResizeObserver` + `MutationObserver` 监听 DOM 变化
- 卡片提交事件：iframe `postMessage` → `AiUiCard.vue` → `emit('submit')` → `MessageList` → `index.vue`

**通信协议**：

```javascript
// iframe 内向父页面发送数据
window.parent.postMessage({ type: 'ai-ui-card', data: '用户填写的JSON字符串' }, '*')
```

**示例 AI 输出**：

```html
<ai-ui-card type="system-login" event="ask">
	{"loginType": "3", "loginWay": "All", "usn": "", "pwd": ""}
</ai-ui-card>
```

---

## 关键组件 API

### MessageList.vue

```typescript
// Props
messages: BubbleMessage[]
loading: boolean
copy?: boolean         // 是否显示复制按钮（默认 true）
canAddToKb?: boolean   // 是否显示添加到知识库按钮（默认 true）

// Emits
retry: [message: BubbleMessage]
addToKb: [text: string]
'card-submit': [text: string]

// Expose
scrollToTop(): void
scrollToBottom(): void
isNearBottom(): boolean
scrollToBubble(index: number): void
```

### ChatInput.vue

```typescript
// Props（v-model）
modelValue: string
loading: boolean
attachments: WorkflowUploadFile[]

// Emits
submit: [content: string]
cancel: []

// Slots
#header     — 知识库/知识选择区域
#prefix-extra — 智能体选择、附件上传按钮
#action     — 模型选择
```

### SMarkdown.vue

```typescript
// Props（透传 XMarkdown）
markdown: string
customAttrs?: Record<string, any>
allowHtml?: boolean
enableLatex?: boolean
enableBreaks?: boolean

// Emits
'card-submit': [payload: { type: string; event: string; data: Record<string, any> }]

// 自定义标签 slot
#kb        — 知识引用
#ai-ui-card — AI UI 卡片
#img       — 图片预览
```

---

## 自动滚动机制

MessageList 实现了智能自动滚动：

1. **监听滚动容器**：通过 `getScrollContainer()` 获取 `.el-bubble-list` 容器
2. **判断是否贴近底部**：`getDistanceToBottom() <= 120px` 时启用自动滚动
3. **用户手动上滚**：禁用自动滚动，不再强制追底
4. **ResizeObserver**：布局变化时保持追底（限时 1200ms）
5. **多次延迟滚动**：`[60ms, 180ms, 360ms]` 三次延迟确保异步渲染完成
6. **加载历史消息时**：直接在会话底部，无需滚动
7. **会话切换时**：滚动到会话最底部
8. **发起新的对话时**：滚动到会话最底部

---

## 会话管理

- **分页加载**：`currentPage` + `pageSize=20`，`hasMore` 控制是否还有下一页
- **无限滚动**：`ChatSidebar` 滚动到底部触发 `@load-more`
- **新建会话**：调用 `createSession()` → 刷新列表 → 自动选中
- **删除会话**：`ElMessageBox.confirm` → `deleteSession()` → 刷新
- **重命名**：弹出 `el-dialog` → `updateSessionName()`
- **历史消息加载**：`getSessionMessages(sessionId)` → `parseHistoryRecord()` 解析

---

## 国际化 (i18n)

所有文本通过 `useI18n()` 的 `t()` 函数获取，key 前缀为 `pages.chat.*`。

常用 key：

- `pages.chat.newChat` / `pages.chat.newConversation`
- `pages.chat.history` / `pages.chat.noHistory`
- `pages.chat.rename` / `pages.chat.delete`
- `pages.chat.toolCallTitle` / `pages.chat.toolResultTitle` / `pages.chat.thinkTitle`
- `pages.chat.emptyTitle` / `pages.chat.emptySubtitle`
- `pages.chat.selectAgentPlaceholder` / `pages.chat.selectModelPlaceholder`
- `pages.chat.copySuccess` / `pages.chat.failedTag`

---

## 开发约定

1. **消息数据结构**：`BubbleMessage` 是唯一的消息类型，所有字段可选，通过 `role: 'user' | 'ai'` 区分
2. **流式数据**：只通过 `useChatStream` 处理，不要在组件中直接调用 SSE API
3. **Markdown 渲染**：所有 Markdown 内容必须通过 `SMarkdown` 组件，不要直接使用 `v-html`
4. **暗黑模式**：样式使用 CSS 变量（`var(--text-primary)`, `var(--bg-container)`, `var(--border-light)` 等），确保暗黑模式兼容
5. **组件通信**：使用 `emit` 向上传递事件，避免 `provide/inject` 传递业务数据
6. **API 层**：所有后端调用集中在 `api/chat.api.ts`，使用 `@repo/api-service` 的封装模块
7. **配置隔离**：每个会话维护独立的 `ChatTargetConfig`，切换会话时通过 `syncSettingsDraft()` 同步
