@blueking/chat-x 0.0.51-beta.5 → 0.0.52-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -8
- package/dist/ag-ui/types/contents.d.ts +2 -0
- package/dist/ag-ui/types/messages.d.ts +6 -1
- package/dist/common/constants.d.ts +2 -0
- package/dist/components/ai-buttons/add-menu-btn/add-menu-btn.vue.d.ts +24 -0
- package/dist/components/chat-content/collapsible-content/collapsible-content.vue.d.ts +28 -0
- package/dist/components/chat-content/file-content/upload-file-item.vue.d.ts +5 -2
- package/dist/components/chat-content/file-content/upload-image-item.vue.d.ts +1 -0
- package/dist/components/chat-content/flow-agent-content/use-flow-node-actions.d.ts +1 -1
- package/dist/components/chat-input/ai-slash-input/ai-slash-input.vue.d.ts +21 -10
- package/dist/components/chat-input/ai-slash-input/command.d.ts +6 -3
- package/dist/components/chat-input/ai-slash-input/constants.d.ts +2 -0
- package/dist/components/chat-input/ai-slash-input/use-menu-trigger.d.ts +17 -0
- package/dist/components/chat-input/build-default-placeholder.d.ts +9 -3
- package/dist/components/chat-input/chat-input.vue.d.ts +30 -18
- package/dist/components/chat-input/input-menu/constants.d.ts +60 -0
- package/dist/components/chat-input/input-menu/index.d.ts +4 -0
- package/dist/components/chat-input/input-menu/input-menu-option.vue.d.ts +14 -0
- package/dist/components/chat-input/input-menu/input-menu-panel.vue.d.ts +17 -0
- package/dist/components/chat-input/input-menu/use-input-menu.d.ts +22 -0
- package/dist/components/chat-input/model-selector/types.d.ts +3 -2
- package/dist/components/chat-message/message-container/message-container.vue.d.ts +1 -1
- package/dist/components/chat-message/user-message/user-message.vue.d.ts +1 -1
- package/dist/components/mention/create-mention-tippy.d.ts +27 -0
- package/dist/components/mention/index.d.ts +4 -0
- package/dist/components/{chat-input/ai-slash-input/ai-prompt-list/ai-prompt-list.vue.d.ts → mention/mention-popover.vue.d.ts} +3 -2
- package/dist/components/{chat-input/ai-slash-input/ai-skill-list/ai-skill-list.vue.d.ts → mention/mention-tag.vue.d.ts} +7 -3
- package/dist/components/{chat-input/ai-slash-input/ai-slash-menu/ai-slash-menu.vue.d.ts → mention/mention-text.vue.d.ts} +3 -3
- package/dist/components/resource-icon/index.d.ts +1 -0
- package/dist/components/resource-icon/resource-icon.vue.d.ts +11 -0
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/use-artifact-preview.d.ts +5 -2
- package/dist/composables/use-common.d.ts +0 -9
- package/dist/composables/use-global-config.d.ts +4 -0
- package/dist/composables/use-input-mention.d.ts +13 -0
- package/dist/composables/use-message-group.d.ts +1011 -72
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/input.d.ts +21 -0
- package/dist/icons/menu-item.d.ts +22 -0
- package/dist/index.css +1 -1
- package/dist/index.js +4953 -4429
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +13 -3
- package/dist/mcp/generated/docs/activity-message.md +3 -3
- package/dist/mcp/generated/docs/add-menu-btn.md +91 -0
- package/dist/mcp/generated/docs/ai-slash-input.md +72 -26
- package/dist/mcp/generated/docs/assistant-message.md +7 -1
- package/dist/mcp/generated/docs/chat-container.md +45 -6
- package/dist/mcp/generated/docs/chat-input.md +327 -483
- package/dist/mcp/generated/docs/collapsible-content.md +78 -0
- package/dist/mcp/generated/docs/constants.md +36 -3
- package/dist/mcp/generated/docs/execution-summary.md +1 -23
- package/dist/mcp/generated/docs/file-artifact-panel.md +24 -5
- package/dist/mcp/generated/docs/file-content.md +4 -0
- package/dist/mcp/generated/docs/file-upload-btn.md +15 -12
- package/dist/mcp/generated/docs/flow-agent-content.md +10 -12
- package/dist/mcp/generated/docs/input-menu-panel.md +166 -0
- package/dist/mcp/generated/docs/mention-tag.md +83 -0
- package/dist/mcp/generated/docs/mention-text.md +86 -0
- package/dist/mcp/generated/docs/message-container.md +1 -1
- package/dist/mcp/generated/docs/message-render.md +1 -1
- package/dist/mcp/generated/docs/message-tools.md +15 -4
- package/dist/mcp/generated/docs/messages.md +3 -0
- package/dist/mcp/generated/docs/model-selector.md +4 -2
- package/dist/mcp/generated/docs/overflow-tips.md +3 -3
- package/dist/mcp/generated/docs/resource-icon.md +102 -0
- package/dist/mcp/generated/docs/text-content.md +7 -3
- package/dist/mcp/generated/docs/theme.md +27 -35
- package/dist/mcp/generated/docs/use-artifact-preview.md +7 -3
- package/dist/mcp/generated/docs/use-command-selection.md +1 -1
- package/dist/mcp/generated/docs/use-container-scroll.md +2 -1
- package/dist/mcp/generated/docs/use-flow-node-actions.md +8 -15
- package/dist/mcp/generated/docs/use-global-config.md +11 -4
- package/dist/mcp/generated/docs/use-input-mention.md +119 -0
- package/dist/mcp/generated/docs/use-menu-keydown.md +15 -16
- package/dist/mcp/generated/docs/use-message-group.md +3 -6
- package/dist/mcp/generated/docs/user-message.md +39 -17
- package/dist/mcp/generated/index.json +233 -81
- package/dist/types/editor.d.ts +0 -29
- package/dist/types/index.d.ts +1 -0
- package/dist/types/input-menu.d.ts +46 -0
- package/dist/utils/artifact-tags.d.ts +36 -0
- package/dist/utils/collect-message-artifacts.d.ts +14 -0
- package/dist/utils/file.d.ts +3 -3
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/upload-file.d.ts +6 -1
- package/package.json +3 -2
- package/skills/blueking-chat-x/SKILL.md +143 -0
- package/skills/blueking-chat-x/references/_index.md +153 -0
- package/skills/blueking-chat-x/references/components/activity-layout.md +134 -0
- package/skills/blueking-chat-x/references/components/activity-message.md +486 -0
- package/skills/blueking-chat-x/references/components/add-menu-btn.md +88 -0
- package/skills/blueking-chat-x/references/components/ai-image.md +230 -0
- package/skills/blueking-chat-x/references/components/ai-loading.md +131 -0
- package/skills/blueking-chat-x/references/components/ai-selection.md +439 -0
- package/skills/blueking-chat-x/references/components/ai-slash-input.md +102 -0
- package/skills/blueking-chat-x/references/components/animation-text.md +202 -0
- package/skills/blueking-chat-x/references/components/assistant-message.md +504 -0
- package/skills/blueking-chat-x/references/components/chat-container.md +912 -0
- package/skills/blueking-chat-x/references/components/chat-input.md +614 -0
- package/skills/blueking-chat-x/references/components/cite-content.md +142 -0
- package/skills/blueking-chat-x/references/components/code-content.md +211 -0
- package/skills/blueking-chat-x/references/components/collapsible-content.md +76 -0
- package/skills/blueking-chat-x/references/components/common-error-content.md +73 -0
- package/skills/blueking-chat-x/references/components/content-render.md +233 -0
- package/skills/blueking-chat-x/references/components/delete-tool.md +191 -0
- package/skills/blueking-chat-x/references/components/desc-panel.md +162 -0
- package/skills/blueking-chat-x/references/components/detail-section.md +91 -0
- package/skills/blueking-chat-x/references/components/execution-summary.md +150 -0
- package/skills/blueking-chat-x/references/components/file-artifact-panel.md +307 -0
- package/skills/blueking-chat-x/references/components/file-content.md +340 -0
- package/skills/blueking-chat-x/references/components/file-icon.md +109 -0
- package/skills/blueking-chat-x/references/components/file-upload-btn.md +162 -0
- package/skills/blueking-chat-x/references/components/flow-agent-content.md +266 -0
- package/skills/blueking-chat-x/references/components/flow-agent-node-detail.md +236 -0
- package/skills/blueking-chat-x/references/components/highlight-keyword.md +146 -0
- package/skills/blueking-chat-x/references/components/image-content.md +182 -0
- package/skills/blueking-chat-x/references/components/image-preview-group.md +184 -0
- package/skills/blueking-chat-x/references/components/image-preview.md +226 -0
- package/skills/blueking-chat-x/references/components/info-message.md +144 -0
- package/skills/blueking-chat-x/references/components/input-attachment.md +49 -0
- package/skills/blueking-chat-x/references/components/input-info-alert.md +42 -0
- package/skills/blueking-chat-x/references/components/input-menu-panel.md +162 -0
- package/skills/blueking-chat-x/references/components/interrupt-message.md +212 -0
- package/skills/blueking-chat-x/references/components/key-value-content.md +128 -0
- package/skills/blueking-chat-x/references/components/knowledge-rag-content.md +122 -0
- package/skills/blueking-chat-x/references/components/latex-content.md +200 -0
- package/skills/blueking-chat-x/references/components/loading-message.md +192 -0
- package/skills/blueking-chat-x/references/components/markdown-content.md +232 -0
- package/skills/blueking-chat-x/references/components/mention-tag.md +79 -0
- package/skills/blueking-chat-x/references/components/mention-text.md +83 -0
- package/skills/blueking-chat-x/references/components/mermaid-content.md +189 -0
- package/skills/blueking-chat-x/references/components/message-container.md +645 -0
- package/skills/blueking-chat-x/references/components/message-loading.md +118 -0
- package/skills/blueking-chat-x/references/components/message-render.md +327 -0
- package/skills/blueking-chat-x/references/components/message-time.md +177 -0
- package/skills/blueking-chat-x/references/components/message-tools.md +427 -0
- package/skills/blueking-chat-x/references/components/model-selector.md +157 -0
- package/skills/blueking-chat-x/references/components/preview-toolbar.md +42 -0
- package/skills/blueking-chat-x/references/components/questions-container.md +85 -0
- package/skills/blueking-chat-x/references/components/reasoning-message.md +232 -0
- package/skills/blueking-chat-x/references/components/reference-content.md +135 -0
- package/skills/blueking-chat-x/references/components/reference-doc-content.md +109 -0
- package/skills/blueking-chat-x/references/components/resource-icon.md +98 -0
- package/skills/blueking-chat-x/references/components/scroll-btn.md +159 -0
- package/skills/blueking-chat-x/references/components/selection-footer.md +78 -0
- package/skills/blueking-chat-x/references/components/selection-question.md +88 -0
- package/skills/blueking-chat-x/references/components/shortcut-btn.md +204 -0
- package/skills/blueking-chat-x/references/components/shortcut-btns.md +266 -0
- package/skills/blueking-chat-x/references/components/shortcut-render.md +424 -0
- package/skills/blueking-chat-x/references/components/simple-table.md +101 -0
- package/skills/blueking-chat-x/references/components/text-content.md +80 -0
- package/skills/blueking-chat-x/references/components/tool-approval-card.md +183 -0
- package/skills/blueking-chat-x/references/components/tool-btn.md +317 -0
- package/skills/blueking-chat-x/references/components/tool-message.md +235 -0
- package/skills/blueking-chat-x/references/components/toolcall-render.md +348 -0
- package/skills/blueking-chat-x/references/components/user-feedback.md +233 -0
- package/skills/blueking-chat-x/references/components/user-message.md +444 -0
- package/skills/blueking-chat-x/references/components/user-question-answered-card.md +104 -0
- package/skills/blueking-chat-x/references/components/user-question-card.md +231 -0
- package/skills/blueking-chat-x/references/components/user-question-choice.md +105 -0
- package/skills/blueking-chat-x/references/components/user-question-option.md +42 -0
- package/skills/blueking-chat-x/references/components/vnode-renderer.md +122 -0
- package/skills/blueking-chat-x/references/composables/use-animation-text.md +196 -0
- package/skills/blueking-chat-x/references/composables/use-artifact-preview.md +235 -0
- package/skills/blueking-chat-x/references/composables/use-clipboard.md +203 -0
- package/skills/blueking-chat-x/references/composables/use-command-selection.md +150 -0
- package/skills/blueking-chat-x/references/composables/use-container-scroll.md +57 -0
- package/skills/blueking-chat-x/references/composables/use-custom-tab.md +158 -0
- package/skills/blueking-chat-x/references/composables/use-flow-node-actions.md +164 -0
- package/skills/blueking-chat-x/references/composables/use-full-screen.md +112 -0
- package/skills/blueking-chat-x/references/composables/use-global-config.md +155 -0
- package/skills/blueking-chat-x/references/composables/use-input-mention.md +115 -0
- package/skills/blueking-chat-x/references/composables/use-menu-keydown.md +161 -0
- package/skills/blueking-chat-x/references/composables/use-message-group.md +244 -0
- package/skills/blueking-chat-x/references/composables/use-observer-visible-list.md +188 -0
- package/skills/blueking-chat-x/references/composables/use-parent-scrolling.md +46 -0
- package/skills/blueking-chat-x/references/directives/index.md +61 -0
- package/skills/blueking-chat-x/references/directives/overflow-tips.md +205 -0
- package/skills/blueking-chat-x/references/icons/index.md +185 -0
- package/skills/blueking-chat-x/references/plugins/index.md +56 -0
- package/skills/blueking-chat-x/references/plugins/markdown-container.md +55 -0
- package/skills/blueking-chat-x/references/plugins/markdown-latex.md +205 -0
- package/skills/blueking-chat-x/references/plugins/markdown-mermaid.md +247 -0
- package/skills/blueking-chat-x/references/theme/theme.md +423 -0
- package/skills/blueking-chat-x/references/types/constants.md +340 -0
- package/skills/blueking-chat-x/references/types/index.md +120 -0
- package/skills/blueking-chat-x/references/types/interrupt.md +379 -0
- package/skills/blueking-chat-x/references/types/messages.md +556 -0
- package/skills/blueking-chat-x/references/types/schema.md +91 -0
- package/skills/blueking-chat-x/references/utils/index.md +196 -0
- package/skills/blueking-chat-x/scripts/generate-references.mjs +536 -0
- package/dist/components/chat-input/ai-slash-editor/ai-slash-editor.vue.d.ts +0 -28
- package/dist/components/chat-input/ai-slash-editor/theme.d.ts +0 -24
- package/dist/mcp/generated/docs/ai-prompt-list.md +0 -45
- package/dist/mcp/generated/docs/ai-skill-list.md +0 -75
- package/dist/mcp/generated/docs/ai-slash-editor.md +0 -43
- package/dist/mcp/generated/docs/ai-slash-menu.md +0 -42
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
# MessageContainer 消息列表容器
|
|
2
|
+
|
|
3
|
+
> 能力域:对话搭建 | 导入:`import { MessageContainer } from '@blueking/chat-x'` | since 1.0.0
|
|
4
|
+
|
|
5
|
+
负责消息分组渲染、滚动控制、工具栏和消息插槽透传。 源码位置:src/components/chat-message/message-container/message-container.vue。
|
|
6
|
+
|
|
7
|
+
**关联**:message-render(按组渲染每条消息时委托 MessageRender)、chat-input(常与 ChatInput 组合构成完整对话界面)、loading-message(末尾为用户消息时自动追加 Loading 消息组)、interrupt-message(透传 onInterruptResume;末条 interrupt 消息不触发组 hover)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# MessageContainer 消息容器
|
|
12
|
+
## 源码事实
|
|
13
|
+
|
|
14
|
+
- **源码位置**:`src/components/chat-message/message-container/message-container.vue`
|
|
15
|
+
- **能力域**:对话搭建
|
|
16
|
+
- **能力说明**:负责消息分组渲染、滚动控制、工具栏和消息插槽透传。
|
|
17
|
+
|
|
18
|
+
> **能力域**:对话搭建
|
|
19
|
+
|
|
20
|
+
消息列表容器组件,负责将原始的 `Message[]` 数组渲染为结构化的对话界面。核心能力:
|
|
21
|
+
|
|
22
|
+
- **消息分组**:将连续的非用户消息合并为一组,每组共享一个工具栏
|
|
23
|
+
- **Tool 消息关联**:自动将 `role: 'tool'` 消息注入到对应 Assistant 消息的 toolCall 中
|
|
24
|
+
- **Loading 自动注入**:末尾为用户消息时,自动追加 Loading 动画组
|
|
25
|
+
- **滚动管理**:`messageStatus` 为流式、等待响应或请求中(`streaming` / `pending` / `fetching`)时显示「停止生成」,离开底部时显示「返回底部」;`renderMode` 为 `Share` 时不显示「停止生成」。挂载时通过 `jumpToBottom()` 瞬时贴底,避免切换会话时从顶部平滑滚到底部的动画
|
|
26
|
+
- **多选模式**:支持按消息组勾选,用户消息与 AI 回复联动选中
|
|
27
|
+
|
|
28
|
+
## 基础用法
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<template>
|
|
32
|
+
<MessageContainer
|
|
33
|
+
:messages="messages"
|
|
34
|
+
message-status="complete"
|
|
35
|
+
:on-agent-action="handleAgentAction"
|
|
36
|
+
:on-agent-feedback="handleAgentFeedback"
|
|
37
|
+
:on-user-action="handleUserAction"
|
|
38
|
+
@stop-streaming="handleStopStreaming"
|
|
39
|
+
/>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script setup lang="ts">
|
|
43
|
+
import { ref } from 'vue';
|
|
44
|
+
import { MessageContainer, MessageRole, MessageStatus, type Message, type IToolBtn } from '@blueking/chat-x';
|
|
45
|
+
|
|
46
|
+
const messages = ref<Message[]>([
|
|
47
|
+
{
|
|
48
|
+
id: '1',
|
|
49
|
+
messageId: '1',
|
|
50
|
+
role: MessageRole.User,
|
|
51
|
+
content: '你好,请介绍一下 Vue 3',
|
|
52
|
+
status: MessageStatus.Complete,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: '2',
|
|
56
|
+
messageId: '2',
|
|
57
|
+
role: MessageRole.Assistant,
|
|
58
|
+
content: 'Vue 3 是一个渐进式 JavaScript 框架...',
|
|
59
|
+
status: MessageStatus.Complete,
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const handleAgentAction = async (tool: IToolBtn, messages: Message[]) => {
|
|
64
|
+
// copy 操作由 MessageContainer 内部处理,此处无需额外实现
|
|
65
|
+
if (tool.id === 'like' || tool.id === 'unlike') {
|
|
66
|
+
return ['回答准确', '信息全面', '表达清晰']; // 返回反馈原因列表
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const handleAgentFeedback = (tool: IToolBtn, messages: Message[], reasonList: string[], otherReason: string) => {
|
|
70
|
+
console.log('反馈:', tool.id, reasonList, otherReason);
|
|
71
|
+
};
|
|
72
|
+
const handleUserAction = async (tool: IToolBtn, message: Message) => {
|
|
73
|
+
console.log('用户消息操作:', tool.id, message);
|
|
74
|
+
};
|
|
75
|
+
const handleStopStreaming = () => {
|
|
76
|
+
console.log('停止流式输出');
|
|
77
|
+
};
|
|
78
|
+
</script>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**渲染效果**
|
|
82
|
+
|
|
83
|
+
## 消息分组机制
|
|
84
|
+
|
|
85
|
+
`MessageContainer` 在内部通过 `watchEffect` 将 `messages` 数组转换为消息组列表(`MessageGroup[]`)。分组规则如下:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
89
|
+
│ messages 原始数组(按顺序处理) │
|
|
90
|
+
└──────────────────────────────┬──────────────────────────────┘
|
|
91
|
+
│
|
|
92
|
+
┌────────────────┴────────────────┐
|
|
93
|
+
│ │
|
|
94
|
+
role === 'user' role === 'tool'
|
|
95
|
+
│ │
|
|
96
|
+
① 先把已累积的 assistantMessages ② 通过 toolCallId 找到对应的
|
|
97
|
+
推入 list 作为一组 AssistantMessage,将 tool
|
|
98
|
+
② 当前 user 消息单独成一组 消息注入 toolCall.toolMessage,
|
|
99
|
+
然后 continue(不单独渲染)
|
|
100
|
+
│
|
|
101
|
+
其他 role(assistant / reasoning /
|
|
102
|
+
activity / info / loading 等)
|
|
103
|
+
│
|
|
104
|
+
③ 累积到 assistantMessages
|
|
105
|
+
等待 user 消息触发分组
|
|
106
|
+
|
|
107
|
+
④ 遍历结束后,将剩余 assistantMessages 推入 list
|
|
108
|
+
每个 assistant 组计算 pause 字段:
|
|
109
|
+
pause = assistantMessages.some(m => m.property?.extra?.pause)
|
|
110
|
+
⑤ 如果最后一条消息 role === 'user' → 追加 Loading 消息组
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**关键细节**:
|
|
114
|
+
|
|
115
|
+
- 每个消息组外层 DOM 的 `id` 为 **`MessageGroup.uid`**(由 `useMessageGroup` 生成),供执行摘要「在对话中定位」、侧栏自定义 Tab 等场景滚动锚定
|
|
116
|
+
- `role: 'tool'` 消息**不会独立渲染**,而是被注入到对应 AssistantMessage 的 `toolCall.toolMessage` 字段
|
|
117
|
+
- 若 `toolMessage.error` 存在,AssistantMessage 的 `status` 会被强制设为 `MessageStatus.Error`
|
|
118
|
+
- 当消息组**最后一条**消息的 `role === 'interrupt'` 时,`mouseenter` **不会**设置 `isHover`,避免 AI 工具栏在审批卡片上误显
|
|
119
|
+
- `MessageTools` 工具栏只在 `type === 'assistant'` 的消息组底部渲染(不依赖鼠标悬停,始终可见),且满足以下条件时**不渲染**:
|
|
120
|
+
- `renderMode === RenderMode.Share`(分享预览模式)
|
|
121
|
+
- 消息组的 `pause` 为 `true`(来源于 `message.property?.extra?.pause`)
|
|
122
|
+
- 多选模式(`enableSelection`)开启且消息组不是 Loading 类型
|
|
123
|
+
- AI 消息组的时间通过 `MessageTools` 的 `#append` 插槽渲染在工具图标右侧,取值为组内**最后一条**带 `createdAt` 的消息(即本轮回答完成时间);组内 `reasoning` / `activity` 等子消息不单独展示时间,全组都没有 `createdAt` 时不展示
|
|
124
|
+
- `renderMode === RenderMode.Test` 时,工具栏会过滤掉「分享」按钮,其余正常
|
|
125
|
+
- `renderMode === RenderMode.Share` 时,`message-group-messages` 自动添加 `message-group-enabled-selection` 类名(与 `enableSelection: true` 一致的多选视觉效果)
|
|
126
|
+
- Loading 消息组的 `type` 是 `MessageRole.Loading`,不显示工具栏和多选 Checkbox
|
|
127
|
+
|
|
128
|
+
## DOM 定位标识
|
|
129
|
+
|
|
130
|
+
为方便业务方通过 `document.querySelector` 定位消息(埋点、自动化测试、外部滚动锚定等),渲染结构上固定输出两层标识:
|
|
131
|
+
|
|
132
|
+
| 层级 | 选择器 | 值 |
|
|
133
|
+
| -------- | ------------------------------------- | --------------------------------------------------- |
|
|
134
|
+
| 消息组 | `.message-group[data-message-group-id]` | `MessageGroup.uid`(与外层 `id` 同值) |
|
|
135
|
+
| 单条消息 | `.ai-message-item[data-message-id]` | `message.id`,缺失时回退 `message.uid`;两者都无则不输出该属性 |
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
// 定位某条消息
|
|
139
|
+
document.querySelector('[data-message-id="123"]');
|
|
140
|
+
// 定位某个消息组下的全部消息
|
|
141
|
+
document.querySelectorAll('[data-message-group-id="xxx"] [data-message-id]');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`.ai-message-item` 是 `MessageContainer` 统一包裹的容器,`#default` 插槽自定义渲染的消息同样被它包裹,因此无论用默认 `MessageRender` 还是自定义渲染,标识都一致存在。
|
|
145
|
+
|
|
146
|
+
## 等待响应(Loading 自动注入)
|
|
147
|
+
|
|
148
|
+
当 `messages` 末尾为 `role: 'user'` 时,自动追加 Loading 消息组,展示 AI 正在处理的加载动画(`renderMode` 为 `Share` 时不追加,且 `MessageContainer` 会过滤 Loading 组):
|
|
149
|
+
|
|
150
|
+
## 流式输出与停止生成
|
|
151
|
+
|
|
152
|
+
当 `messageStatus` 为 `streaming`、`pending`(等待首包)或 `fetching`(请求中、与末尾 Loading 占位一致)时,底部固定区域显示「停止生成」按钮(`stop-loading` 时按钮展示为正在停止),点击后触发 `@stop-streaming` 事件。`renderMode` 为 `Share` 时不显示「停止生成」按钮。
|
|
153
|
+
|
|
154
|
+
点击下方按钮体验完整的流式输出过程:
|
|
155
|
+
|
|
156
|
+
**流式输出完整示例**:
|
|
157
|
+
|
|
158
|
+
```vue
|
|
159
|
+
<template>
|
|
160
|
+
<MessageContainer
|
|
161
|
+
:messages="messages"
|
|
162
|
+
:message-status="messageStatus"
|
|
163
|
+
:on-agent-action="handleAgentAction"
|
|
164
|
+
:on-agent-feedback="handleAgentFeedback"
|
|
165
|
+
:on-user-action="handleUserAction"
|
|
166
|
+
@stop-streaming="handleStopStreaming"
|
|
167
|
+
/>
|
|
168
|
+
</template>
|
|
169
|
+
|
|
170
|
+
<script setup lang="ts">
|
|
171
|
+
import { ref } from 'vue';
|
|
172
|
+
import { MessageContainer, MessageRole, MessageStatus, type Message, type IToolBtn } from '@blueking/chat-x';
|
|
173
|
+
|
|
174
|
+
const messageStatus = ref<MessageStatus>(MessageStatus.Complete);
|
|
175
|
+
const messages = ref<Message[]>([]);
|
|
176
|
+
|
|
177
|
+
const sendMessage = async (userInput: string) => {
|
|
178
|
+
// 1. 推入用户消息
|
|
179
|
+
messages.value.push({
|
|
180
|
+
id: Date.now().toString(),
|
|
181
|
+
messageId: Date.now().toString(),
|
|
182
|
+
role: MessageRole.User,
|
|
183
|
+
content: userInput,
|
|
184
|
+
status: MessageStatus.Complete,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 2. 推入空 assistant 消息(触发 Loading 消失)
|
|
188
|
+
const assistantMsg: Message = {
|
|
189
|
+
id: (Date.now() + 1).toString(),
|
|
190
|
+
messageId: (Date.now() + 1).toString(),
|
|
191
|
+
role: MessageRole.Assistant,
|
|
192
|
+
content: '',
|
|
193
|
+
status: MessageStatus.Pending,
|
|
194
|
+
};
|
|
195
|
+
messages.value.push(assistantMsg);
|
|
196
|
+
messageStatus.value = MessageStatus.Streaming;
|
|
197
|
+
|
|
198
|
+
// 3. 逐步追加流式内容
|
|
199
|
+
for await (const chunk of fetchStream(userInput)) {
|
|
200
|
+
assistantMsg.content += chunk;
|
|
201
|
+
assistantMsg.status = MessageStatus.Streaming;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 4. 标记完成
|
|
205
|
+
assistantMsg.status = MessageStatus.Complete;
|
|
206
|
+
messageStatus.value = MessageStatus.Complete;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const handleStopStreaming = () => {
|
|
210
|
+
messageStatus.value = MessageStatus.Stop;
|
|
211
|
+
const last = messages.value.at(-1);
|
|
212
|
+
if (last) last.status = MessageStatus.Stop;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const handleAgentAction = async (tool: IToolBtn, messages: Message[]) => {
|
|
216
|
+
if (tool.id === 'like' || tool.id === 'unlike') {
|
|
217
|
+
return ['回答准确', '信息全面'];
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const handleAgentFeedback = (tool: IToolBtn, messages: Message[], reasonList: string[], otherReason: string) => {
|
|
221
|
+
console.log('反馈:', tool.id, reasonList);
|
|
222
|
+
};
|
|
223
|
+
const handleUserAction = async (tool: IToolBtn, message: Message) => {};
|
|
224
|
+
</script>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 错误状态
|
|
228
|
+
|
|
229
|
+
AI 回复状态为 `error` 时,消息以错误样式展示:
|
|
230
|
+
|
|
231
|
+
## 推理过程消息
|
|
232
|
+
|
|
233
|
+
`role: 'reasoning'` 消息会被归入当前 AI 消息组,带有折叠/展开效果和思考耗时展示:
|
|
234
|
+
|
|
235
|
+
```vue
|
|
236
|
+
<script setup lang="ts">
|
|
237
|
+
const messages = ref<Message[]>([
|
|
238
|
+
{
|
|
239
|
+
id: '1',
|
|
240
|
+
messageId: '1',
|
|
241
|
+
role: MessageRole.User,
|
|
242
|
+
content: '分析一下这段代码的问题',
|
|
243
|
+
status: MessageStatus.Complete,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: '2',
|
|
247
|
+
messageId: '2',
|
|
248
|
+
role: MessageRole.Reasoning,
|
|
249
|
+
content: ['首先,我需要理解代码意图...', '看起来是数据处理函数...', '发现几个潜在问题...'],
|
|
250
|
+
status: MessageStatus.Complete,
|
|
251
|
+
duration: 3500,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: '3',
|
|
255
|
+
messageId: '3',
|
|
256
|
+
role: MessageRole.Assistant,
|
|
257
|
+
content: '根据分析,存在以下问题:\n\n1. 变量命名不规范...',
|
|
258
|
+
status: MessageStatus.Complete,
|
|
259
|
+
},
|
|
260
|
+
]);
|
|
261
|
+
</script>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**渲染效果**
|
|
265
|
+
|
|
266
|
+
## 工具调用消息
|
|
267
|
+
|
|
268
|
+
`role: 'tool'` 消息通过 `toolCallId` 与对应 AssistantMessage 关联,被注入到 `toolCall.toolMessage` 后不再独立渲染:
|
|
269
|
+
|
|
270
|
+
```vue
|
|
271
|
+
<script setup lang="ts">
|
|
272
|
+
const messages = ref<Message[]>([
|
|
273
|
+
{ id: '1', messageId: '1', role: MessageRole.User, content: '查询北京天气', status: MessageStatus.Complete },
|
|
274
|
+
{
|
|
275
|
+
id: '2',
|
|
276
|
+
messageId: '2',
|
|
277
|
+
role: MessageRole.Assistant,
|
|
278
|
+
content: '好的,我来帮你查询。',
|
|
279
|
+
status: MessageStatus.Complete,
|
|
280
|
+
toolCalls: [
|
|
281
|
+
{
|
|
282
|
+
id: 'call_weather',
|
|
283
|
+
type: 'function',
|
|
284
|
+
function: { name: 'get_weather', arguments: '{"city": "北京"}', description: '查询天气信息' },
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
// role: 'tool' 消息通过 toolCallId 关联到上方 assistant 消息
|
|
289
|
+
{
|
|
290
|
+
id: '3',
|
|
291
|
+
messageId: '3',
|
|
292
|
+
role: MessageRole.Tool,
|
|
293
|
+
content: '{"temperature":25,"weather":"晴天"}',
|
|
294
|
+
status: MessageStatus.Complete,
|
|
295
|
+
toolCallId: 'call_weather',
|
|
296
|
+
duration: 1200,
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
id: '4',
|
|
300
|
+
messageId: '4',
|
|
301
|
+
role: MessageRole.Assistant,
|
|
302
|
+
content: '北京今天 **晴朗**,温度 **25°C**。',
|
|
303
|
+
status: MessageStatus.Complete,
|
|
304
|
+
},
|
|
305
|
+
]);
|
|
306
|
+
</script>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**渲染效果**
|
|
310
|
+
|
|
311
|
+
## Activity 知识检索消息
|
|
312
|
+
|
|
313
|
+
`role: 'activity'` 消息同样被归入 AI 消息组,与 assistant 消息一起渲染:
|
|
314
|
+
|
|
315
|
+
## 多轮对话
|
|
316
|
+
|
|
317
|
+
连续多轮问答,组件按角色自动分组,每个 AI 组独立显示工具栏:
|
|
318
|
+
|
|
319
|
+
## 自定义消息工具栏
|
|
320
|
+
|
|
321
|
+
`messageTools`(左侧)与 `updateTools`(右侧反馈区)用于在**内置工具的基础上做增量定制**,无需重写整份列表。二者分别与内置 `CONST_MESSAGE_TOOLS`、`CONST_UPDATE_TOOLS` 按 `id` 合并,规则如下:
|
|
322
|
+
|
|
323
|
+
- **覆盖**:`id` 命中内置项时,做字段级浅合并(仅覆盖传入的字段,其余保留),不新增条目
|
|
324
|
+
- **追加**:`id` 为内置列表中不存在的新值时,追加到该组末尾(如自定义「保存」「收藏」按钮)
|
|
325
|
+
- **隐藏**:传入 `{ id: 'xxx', hidden: true }` 可移除对应内置项(如隐藏「分享」)
|
|
326
|
+
- **自定义图标**:通过 `icon`(组件/VNode)为自定义按钮提供图标,优先级高于内置 `ToolIconsMap`
|
|
327
|
+
- 不传 `messageTools` / `updateTools` 时,各自使用内置默认列表
|
|
328
|
+
|
|
329
|
+
```vue
|
|
330
|
+
<template>
|
|
331
|
+
<MessageContainer
|
|
332
|
+
:messages="messages"
|
|
333
|
+
:message-groups="messageGroups"
|
|
334
|
+
message-status="complete"
|
|
335
|
+
:message-tools="customMessageTools"
|
|
336
|
+
:update-tools="customUpdateTools"
|
|
337
|
+
:on-agent-action="handleAgentAction"
|
|
338
|
+
@stop-streaming="handleStopStreaming"
|
|
339
|
+
/>
|
|
340
|
+
</template>
|
|
341
|
+
|
|
342
|
+
<script setup lang="ts">
|
|
343
|
+
import { MessageContainer, DownloadIcon, type IToolBtn, type Message } from '@blueking/chat-x';
|
|
344
|
+
|
|
345
|
+
const customMessageTools: IToolBtn[] = [
|
|
346
|
+
{ id: 'save', name: '保存', description: '保存该回答', icon: DownloadIcon }, // 追加新按钮
|
|
347
|
+
{ id: 'copy', description: '复制全文' }, // 覆盖内置 copy 的 description
|
|
348
|
+
{ id: 'share', hidden: true }, // 隐藏内置「分享」
|
|
349
|
+
];
|
|
350
|
+
const customUpdateTools: IToolBtn[] = [
|
|
351
|
+
{ id: 'collect', name: '收藏', description: '收藏到我的空间', icon: DownloadIcon },
|
|
352
|
+
];
|
|
353
|
+
|
|
354
|
+
const handleAgentAction = async (tool: IToolBtn, messages: Message[]) => {
|
|
355
|
+
if (tool.id === 'save') {
|
|
356
|
+
// 处理保存
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
</script>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
> **合并优先级**:`RenderMode.Test` 下仍会额外过滤掉「分享」按钮(即便合并后存在);即测试模式对 `share` 的过滤在自定义合并之后生效。
|
|
363
|
+
|
|
364
|
+
## 工具栏状态控制
|
|
365
|
+
|
|
366
|
+
通过 `messageToolsStatus` 控制消息工具栏的显示状态。常见用法:流式输出期间禁用工具栏:
|
|
367
|
+
|
|
368
|
+
```vue
|
|
369
|
+
<script setup lang="ts">
|
|
370
|
+
import { computed, ref } from 'vue';
|
|
371
|
+
import { MessageContainer, MessageStatus, MessageToolsStatus } from '@blueking/chat-x';
|
|
372
|
+
|
|
373
|
+
const messageStatus = ref(MessageStatus.Complete);
|
|
374
|
+
|
|
375
|
+
// 流式输出期间禁用工具栏,完成后恢复
|
|
376
|
+
const messageToolsStatus = computed(() =>
|
|
377
|
+
messageStatus.value === MessageStatus.Streaming ? MessageToolsStatus.Disabled : undefined,
|
|
378
|
+
);
|
|
379
|
+
</script>
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**三种状态对比**
|
|
383
|
+
|
|
384
|
+
| 状态值 | 说明 |
|
|
385
|
+
| ----------- | ------------------------------ |
|
|
386
|
+
| `undefined` | 默认,工具栏正常可用 |
|
|
387
|
+
| `disabled` | 工具栏显示但所有按钮不可点击 |
|
|
388
|
+
| `hidden` | 工具栏(`MessageTools`)不渲染 |
|
|
389
|
+
|
|
390
|
+
> **注意**:`messageToolsStatus` 同时透传给 `MessageRender`,控制用户消息中编辑、删除等按钮的状态。
|
|
391
|
+
|
|
392
|
+
## 消息多选
|
|
393
|
+
|
|
394
|
+
启用 `enableSelection` 后,每个消息组前显示 Checkbox,选中状态联动关联:
|
|
395
|
+
|
|
396
|
+
```vue
|
|
397
|
+
<template>
|
|
398
|
+
<MessageContainer
|
|
399
|
+
v-model:selected-user-messages="selectedUserMessages"
|
|
400
|
+
:messages="messages"
|
|
401
|
+
:message-status="messageStatus"
|
|
402
|
+
:enable-selection="true"
|
|
403
|
+
:on-agent-action="handleAgentAction"
|
|
404
|
+
:on-user-action="handleUserAction"
|
|
405
|
+
@stop-streaming="handleStopStreaming"
|
|
406
|
+
/>
|
|
407
|
+
<div v-if="selectedUserMessages.length > 0">
|
|
408
|
+
已选择 {{ selectedUserMessages.length }} 条消息
|
|
409
|
+
<button @click="selectedUserMessages = []">清空选择</button>
|
|
410
|
+
</div>
|
|
411
|
+
</template>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**多选特性**:
|
|
415
|
+
|
|
416
|
+
- `v-model:selected-user-messages` 仅包含选中的用户消息
|
|
417
|
+
- 选中用户消息组 → 其后紧邻的 AI 回复组视觉联动选中
|
|
418
|
+
- 选中 AI 回复组 → 其前紧邻的用户消息组联动选中
|
|
419
|
+
- 取消任一关联组 → 另一组同时取消
|
|
420
|
+
- 选中时消息组背景色变为 `#f5f7fa`
|
|
421
|
+
- 多选模式下用户消息工具栏自动隐藏
|
|
422
|
+
- Loading 消息组不显示 Checkbox
|
|
423
|
+
|
|
424
|
+
**渲染效果**(点击 Checkbox 体验多选)
|
|
425
|
+
|
|
426
|
+
## 自定义消息组渲染
|
|
427
|
+
|
|
428
|
+
使用 `#group` 插槽可替换单个消息组的默认内容(Checkbox、消息列表、`MessageTools`)。外层 `.message-group` 容器(含 `id`、hover 状态、选中背景色)仍由 `MessageContainer` 管理。
|
|
429
|
+
|
|
430
|
+
> **注意**:提供 `#group` 后需自行编排组内全部 UI;若只需替换单条消息,请使用 `#default` 插槽。
|
|
431
|
+
|
|
432
|
+
```vue
|
|
433
|
+
<template>
|
|
434
|
+
<MessageContainer
|
|
435
|
+
:messages="messages"
|
|
436
|
+
:message-groups="messageGroups"
|
|
437
|
+
message-status="complete"
|
|
438
|
+
:on-agent-action="handleAgentAction"
|
|
439
|
+
@stop-streaming="handleStopStreaming"
|
|
440
|
+
>
|
|
441
|
+
<template #group="{ group }">
|
|
442
|
+
<div class="custom-group">
|
|
443
|
+
<span class="custom-group-label">{{ group.type }}</span>
|
|
444
|
+
<div
|
|
445
|
+
v-for="message in group.messages"
|
|
446
|
+
:key="message.id"
|
|
447
|
+
class="custom-group-message"
|
|
448
|
+
>
|
|
449
|
+
{{ typeof message.content === 'string' ? message.content : JSON.stringify(message.content) }}
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
</template>
|
|
453
|
+
</MessageContainer>
|
|
454
|
+
</template>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**渲染效果**(简化自定义组布局,不含默认 Checkbox 与工具栏)
|
|
458
|
+
|
|
459
|
+
## 自定义消息渲染
|
|
460
|
+
|
|
461
|
+
使用默认插槽替换单条消息的渲染,插槽参数包含 `message`、`messageToolsStatus` 和 `onInterruptResume`:
|
|
462
|
+
|
|
463
|
+
```vue
|
|
464
|
+
<template>
|
|
465
|
+
<MessageContainer
|
|
466
|
+
:messages="messages"
|
|
467
|
+
:message-status="messageStatus"
|
|
468
|
+
:on-agent-action="handleAgentAction"
|
|
469
|
+
:on-user-action="handleUserAction"
|
|
470
|
+
@stop-streaming="handleStopStreaming"
|
|
471
|
+
>
|
|
472
|
+
<template #default="{ message, messageToolsStatus, onInterruptResume }">
|
|
473
|
+
<MyCustomMessage
|
|
474
|
+
:message="message"
|
|
475
|
+
:message-tools-status="messageToolsStatus"
|
|
476
|
+
:on-interrupt-resume="onInterruptResume"
|
|
477
|
+
/>
|
|
478
|
+
</template>
|
|
479
|
+
</MessageContainer>
|
|
480
|
+
</template>
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
> 使用默认插槽后,每条消息由自定义组件完全接管渲染,但**消息分组逻辑和工具栏(`MessageTools`)仍由 `MessageContainer` 管理**。
|
|
484
|
+
|
|
485
|
+
## 自定义工具栏 Tooltip 配置
|
|
486
|
+
|
|
487
|
+
通过 `messageToolsTippyOptions` 可以自定义消息工具栏中按钮 tooltip 的 Tippy 配置,透传给所有 `ToolBtn`。典型用法是修改 `appendTo` 避免 tooltip 被父容器 `overflow: hidden` 遮挡:
|
|
488
|
+
|
|
489
|
+
```vue
|
|
490
|
+
<template>
|
|
491
|
+
<!-- tooltip 挂载到触发元素的父节点,避免被滚动容器裁剪 -->
|
|
492
|
+
<MessageContainer
|
|
493
|
+
:messages="messages"
|
|
494
|
+
:message-tools-tippy-options="{ appendTo: 'parent' }"
|
|
495
|
+
:on-agent-action="handleAgentAction"
|
|
496
|
+
@stop-streaming="handleStopStreaming"
|
|
497
|
+
/>
|
|
498
|
+
</template>
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
> **注意**:`content`、`getReferenceClientRect`、`triggerTarget` 三个字段被排除,不可通过此 prop 覆盖。
|
|
502
|
+
|
|
503
|
+
## 用户消息编辑与快捷指令
|
|
504
|
+
|
|
505
|
+
通过 `onUserInputConfirm` 和 `onUserShortcutConfirm` 处理用户消息的编辑确认和快捷指令表单提交:
|
|
506
|
+
|
|
507
|
+
```vue
|
|
508
|
+
<template>
|
|
509
|
+
<MessageContainer
|
|
510
|
+
:messages="messages"
|
|
511
|
+
:message-status="messageStatus"
|
|
512
|
+
:on-agent-action="handleAgentAction"
|
|
513
|
+
:on-user-action="handleUserAction"
|
|
514
|
+
:on-user-input-confirm="handleUserInputConfirm"
|
|
515
|
+
:on-user-shortcut-confirm="handleUserShortcutConfirm"
|
|
516
|
+
@stop-streaming="handleStopStreaming"
|
|
517
|
+
/>
|
|
518
|
+
</template>
|
|
519
|
+
|
|
520
|
+
<script setup lang="ts">
|
|
521
|
+
import { MessageContainer, type Message, type TagSchema } from '@blueking/chat-x';
|
|
522
|
+
|
|
523
|
+
// 用户点击编辑并确认时触发
|
|
524
|
+
const handleUserInputConfirm = async (message: Message, content: UserMessage['content'], docSchema: TagSchema) => {
|
|
525
|
+
// message: 原始消息对象
|
|
526
|
+
// content: 编辑后的内容(字符串或富文本结构)
|
|
527
|
+
// docSchema: 引用文档结构
|
|
528
|
+
console.log('用户编辑确认:', message.id, content);
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// 用户提交快捷指令表单时触发
|
|
532
|
+
const handleUserShortcutConfirm = async (message: Message, formModel: Record<string, unknown>) => {
|
|
533
|
+
console.log('快捷指令提交:', message.id, formModel);
|
|
534
|
+
};
|
|
535
|
+
</script>
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## copy 操作内置处理
|
|
539
|
+
|
|
540
|
+
`MessageContainer` 内部对 `copy` 工具操作进行了特殊处理:当 `tool.id === 'copy'` 时,自动将当前消息组中所有**非 reasoning 消息**的内容拼接后复制到剪贴板,**无需在 `onAgentAction` 中自行实现**。
|
|
541
|
+
|
|
542
|
+
其他工具操作(`like`、`unlike`、`share` 等)仍正常转发给 `onAgentAction` 回调。
|
|
543
|
+
|
|
544
|
+
## 滚动控制
|
|
545
|
+
|
|
546
|
+
底部固定区域(`position: sticky; bottom: 12px`)根据条件显示两个按钮:
|
|
547
|
+
|
|
548
|
+
| 按钮 | 显示条件 | 点击行为 |
|
|
549
|
+
| ------------ | ---------------------------------------------------------------------------------------------- | ---------------------- |
|
|
550
|
+
| 「停止生成」 | `messageStatus` 为 `streaming`、`pending`、`fetching` 或 `stop-loading`(停止中 loading 态),且 `renderMode` 不为 `Share` | 触发 `@stop-streaming` |
|
|
551
|
+
| 「返回底部」 | `debouncedShowScrollBottomBtn`(距底部 > 100px,且防抖 300ms 后才显示/隐藏) | 平滑滚动到消息列表底部(显式 `toScrollBottom('smooth')`) |
|
|
552
|
+
|
|
553
|
+
> **防抖说明**:「返回底部」按钮的显隐使用 300ms 防抖,避免快速滚动时按钮频繁闪烁。隐藏时立即生效(无防抖),显示时延迟 300ms。
|
|
554
|
+
|
|
555
|
+
> **首屏 / 切换会话贴底**:`MessageContainer` 挂载时若已有消息组,会立即调用 `jumpToBottom()`,并在下一帧再补一次,避免历史消息渲染过程中出现「从顶部滚到底部」的动画。流式输出场景下的小幅跟随仍由 markdown 挂载触发的 `toScrollBottom()`(距底较近时走 smooth)完成。
|
|
556
|
+
## API
|
|
557
|
+
|
|
558
|
+
### Props
|
|
559
|
+
|
|
560
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
561
|
+
| ------------------------ | -------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
562
|
+
| messages | `Message[]` | — | **必填**,消息列表 |
|
|
563
|
+
| messageGroups | `MessageGroup[]` | — | 预计算的消息分组;传入时跳过内部分组逻辑,由 `ChatContainer` 通过 `useMessageGroup` 提供 |
|
|
564
|
+
| messageStatus | `MessageStatus` | — | 当前整体消息状态,控制底部「停止生成」按钮显示;`ChatContainer` 会结合末尾 Loading 占位推导 `fetching` 等再传入 |
|
|
565
|
+
| messageTools | `IToolBtn[]` | — | AI 消息左侧工具(复制/引用等)的自定义配置;按 `id` 与内置 `CONST_MESSAGE_TOOLS` 合并(覆盖同 id、追加新 id、`hidden` 过滤),详见「自定义消息工具栏」 |
|
|
566
|
+
| updateTools | `IToolBtn[]` | — | AI 消息右侧反馈工具(点赞/踩/删除等)的自定义配置;按 `id` 与内置 `CONST_UPDATE_TOOLS` 合并,规则同上 |
|
|
567
|
+
| userMessageTools | `IToolBtn[]` | — | 自定义用户消息工具组,透传给 `MessageRender` → `UserMessage`;按 id 与 `CONST_USER_MESSAGE_TOOLS` 合并,`{ id, hidden: true }` 可隐藏 |
|
|
568
|
+
| messageToolsStatus | `MessageToolsStatus` | — | 工具栏状态,透传给 `MessageTools` 和 `MessageRender` |
|
|
569
|
+
| messageToolsTippyOptions | `AITippyProps` | — | 透传给 `MessageTools` 和 `MessageRender`(进而透传给 `UserMessage` 的工具栏)的 Tippy 配置,用于自定义 tooltip 挂载点、位置等(如 `appendTo`、`placement`、`zIndex`) |
|
|
570
|
+
| enableSelection | `boolean` | `false` | 是否启用多选模式 |
|
|
571
|
+
| onAgentAction | `(tool: IToolBtn, messages: Message[]) => Promise<string[] \| void>` | — | AI 消息工具操作回调;`copy` 操作由内部处理,`like/unlike` 应返回反馈原因字符串数组 |
|
|
572
|
+
| onAgentFeedback | `(tool: IToolBtn, messages: Message[], reasonList: string[], otherReason: string) => void` | — | AI 消息反馈提交回调(点赞/踩选完原因后触发) |
|
|
573
|
+
| onUserAction | `(tool: IToolBtn, message: Message) => Promise<string[] \| void>` | — | 用户消息工具操作回调 |
|
|
574
|
+
| onUserInputConfirm | `(message: Message, content: UserMessage['content'], docSchema: TagSchema) => Promise<void>` | — | 用户编辑消息确认回调 |
|
|
575
|
+
| onUserShortcutConfirm | `(message: Message, formModel: Record<string, unknown>) => Promise<void>` | — | 用户快捷指令表单提交回调 |
|
|
576
|
+
| onInterruptResume | `OnInterruptResume` | — | AG-UI human-in-the-loop 中断响应回调,透传给 `MessageRender` → `InterruptMessageRender` |
|
|
577
|
+
| renderMode | `RenderMode` | — | 渲染模式。`Share` 模式下启用多选样式并隐藏工具栏与「停止生成」按钮;`Test` 模式下过滤掉「分享」按钮;不传或 `Chat` 为默认行为 |
|
|
578
|
+
|
|
579
|
+
### v-model
|
|
580
|
+
|
|
581
|
+
| 属性名 | 类型 | 说明 |
|
|
582
|
+
| -------------------- | ----------- | -------------------------------------------------- |
|
|
583
|
+
| selectedUserMessages | `Message[]` | 当前选中的用户消息列表(双向绑定,仅包含用户消息) |
|
|
584
|
+
|
|
585
|
+
### Events
|
|
586
|
+
|
|
587
|
+
| 事件名 | 参数 | 说明 |
|
|
588
|
+
| ------------- | ---- | -------------------------- |
|
|
589
|
+
| stopStreaming | — | 点击「停止生成」按钮时触发 |
|
|
590
|
+
|
|
591
|
+
### Slots
|
|
592
|
+
|
|
593
|
+
| 插槽名 | 参数 | 说明 |
|
|
594
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
595
|
+
| answeredQuestion | `{ item, index, status }` | 自定义 UserQuestion 已回答回显,透传给 MessageRender → InterruptMessageRender |
|
|
596
|
+
| default | `{ message: Message, messageToolsStatus?: MessageToolsStatus, onInterruptResume?: OnInterruptResume }` | 自定义单条消息渲染;消息分组外层容器与 `#group` 未覆盖时的工具栏仍由容器管理 |
|
|
597
|
+
| group | `{ group: MessageGroup }` | 自定义单个消息组内容,替换默认 Checkbox、消息列表与 `MessageTools`;外层组容器仍由组件管理 |
|
|
598
|
+
|
|
599
|
+
## 类型定义
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
import { MessageRole, MessageStatus, MessageToolsStatus, type Message, type MessageGroup, type IToolBtn } from '@blueking/chat-x';
|
|
603
|
+
|
|
604
|
+
// 消息组(由 useMessageGroup 生成,也可手动传入 messageGroups)
|
|
605
|
+
interface MessageGroup {
|
|
606
|
+
checked: boolean;
|
|
607
|
+
isHover: boolean;
|
|
608
|
+
messages: Message[];
|
|
609
|
+
pause?: boolean;
|
|
610
|
+
startTime?: number;
|
|
611
|
+
type: MessageRole;
|
|
612
|
+
uid: string;
|
|
613
|
+
userMessageTitle?: number | string;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// onAgentAction 回调类型
|
|
617
|
+
// messages 为当前消息组全部消息(可含 reasoning / activity 等)
|
|
618
|
+
// 返回 string[] 时用作 like/unlike 的反馈原因列表
|
|
619
|
+
type AgentActionCallback = (tool: IToolBtn, messages: Message[]) => Promise<string[] | void>;
|
|
620
|
+
|
|
621
|
+
// onAgentFeedback 回调类型
|
|
622
|
+
type AgentFeedbackCallback = (tool: IToolBtn, messages: Message[], reasonList: string[], otherReason: string) => void;
|
|
623
|
+
|
|
624
|
+
// onUserAction 回调类型
|
|
625
|
+
type UserActionCallback = (tool: IToolBtn, message: Message) => Promise<string[] | void>;
|
|
626
|
+
|
|
627
|
+
// 工具栏状态
|
|
628
|
+
enum MessageToolsStatus {
|
|
629
|
+
Disabled = 'disabled',
|
|
630
|
+
Hidden = 'hidden',
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// 消息角色 / 消息状态完整枚举见常量文档,勿在此维护副本
|
|
634
|
+
// MessageRole、MessageStatus → ../../types/constants
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
> `MessageRole` / `MessageStatus` 完整取值见 [常量枚举](../../types/constants)。
|
|
638
|
+
|
|
639
|
+
## 关联组件
|
|
640
|
+
|
|
641
|
+
- [MessageRender](/components/message/message-render) — 按组渲染每条消息时委托使用
|
|
642
|
+
- [MessageTime](/components/feedback/message-time) — AI 消息组工具栏右侧的时间
|
|
643
|
+
- [InterruptMessage 中断消息](/components/agent/interrupt-message) — `role: 'interrupt'` 的渲染与 `onInterruptResume` 透传
|
|
644
|
+
- [ChatInput](/components/input/chat-input) — 常与输入区组合构成完整对话界面
|
|
645
|
+
- [LoadingMessage](/components/message/loading-message) — 末尾为用户消息时自动追加加载组
|