@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,912 @@
|
|
|
1
|
+
# ChatContainer 聊天容器
|
|
2
|
+
|
|
3
|
+
> 能力域:对话搭建 | 导入:`import { ChatContainer } from '@blueking/chat-x'` | since 1.0.0
|
|
4
|
+
|
|
5
|
+
完整对话容器,组合消息列表、输入区、模型选择、快捷指令、执行摘要、分享选择和自定义 Tab。 透传 models / selectedModel;支持 welcomeTitle 与 #welcome。 侧栏展开/折叠由外部 v-model:asideCollapsed 严格受控(传入后以外部值为准,内部展开动作仅发 update 事件),不再依赖执行数据或文件产物有无;无数据时侧栏照常展开并展示空态。 侧栏固定从右侧展开(无 placement prop),且不再内置折叠按钮。 「文件产物」Tab 常驻挂载。 源码位置:src/components/chat-container/chat-container.vue。
|
|
6
|
+
|
|
7
|
+
**关联**:message-container(消息列表与滚动区域)、chat-input(对话输入与快捷指令入口)、model-selector(透传 models / selectedModel,在输入区展示模型选择器)、shortcut-render(快捷指令表单浮层)、execution-summary(执行摘要侧栏与定位)、selection-footer(多选分享底部操作栏)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# ChatContainer 聊天容器
|
|
12
|
+
|
|
13
|
+
> **能力域**:对话搭建 | **源码**:`src/components/chat-container/chat-container.vue`
|
|
14
|
+
|
|
15
|
+
顶层聊天容器,整合 `MessageContainer`(消息列表)、`ChatInput`(输入框)、`ExecutionSummary`(执行摘要)、`ShortcutRender`(快捷指令表单)和 `SelectionFooter`(多选操作栏),提供完整 AI 对话界面布局。
|
|
16
|
+
|
|
17
|
+
## 核心能力
|
|
18
|
+
|
|
19
|
+
- **分栏布局**:基于 `ResizeLayout`,侧栏固定从右侧展开且无内置折叠按钮;展开 / 折叠由外部通过 `v-model:asideCollapsed` 判断,容器只负责渲染与回写,不再依赖 `executionGroups`、`keyword` 等数据条件。无数据时侧栏照常展开,由各面板展示空态
|
|
20
|
+
- **顶部分割线**:容器自身不再绘制 `border-top`。侧栏展开时的顶栏分割线由业务 Header(如 AI 小鲸 `ai-header`)在 `asideCollapsed === false` 时绘制,以保证贯穿全宽
|
|
21
|
+
- **消息分组**:内置 `useMessageGroup`,自动分组、Tool 合并、Loading 注入
|
|
22
|
+
- **输入区状态推导**:对内 `messageStatus` 取 `inputStatus`——分组中存在 `LOADING_MESSAGE_ID`(`'__loading__'`)时用 `MessageStatus.Fetching`,否则用外部 `messageStatus`,保证「已发未流式」阶段也能停止、并避免重复发送
|
|
23
|
+
- **待审批发送阻塞**:存在 `AIDevToolApproval` 且为 `pending` / `draft` 时,输入区上方提示,并通过 `ChatInput.sendDisabledTip` 禁止发送
|
|
24
|
+
- **用户问题中断**:待回答 `UserQuestion` 时挂载 `UserQuestionCard`;结构化作答走 `onInterruptResume`,输入框直接发送走 `onSendMessage`(第三参数带 skip `payload` 与 `interrupt`),且不自动清空输入框
|
|
25
|
+
- **执行摘要 / 侧栏全屏 / 自定义 Tab**:侧栏展示工具调用与 FlowAgent 记录,支持搜索定位;Tab 栏可全屏;`useCustomTabProvider` 支持动态 Tab
|
|
26
|
+
- **模型选择**:透传 `models`、`v-model:selectedModel` 与 `@modelChange` 至 `ChatInput`,传入 `models` 后在发送按钮左侧展示 [ModelSelector](/components/input/model-selector)
|
|
27
|
+
- **输入框菜单**:`menuSources` 透传至 `ChatInput`,并自动把消息里的会话产物补进 `artifact` 分组;同时经 `useGlobalConfig` 下发给消息编辑态的内嵌输入框
|
|
28
|
+
- **资源引用注入**:通过 `useInputMentionProvider` 开放 `insertMention`,让消息区文件卡片与侧栏产物面板可把文件「@ 进输入框」
|
|
29
|
+
- **分享模式 / 渲染模式**:内置多选分享;`renderMode` 经 Provider 下传。`Share` 态开放侧栏只读查看,隐藏底部输入与「重试 / 跳过」等交互
|
|
30
|
+
- **字号主题**:`size` 为 `small`(默认 12px)/ `normal`(14px);根节点 `data-ai-size`,浮层同步 `document.body.dataset.aiSize`
|
|
31
|
+
- **消息时间时区**:`timezone` 经 `useGlobalConfig` 下传给 `MessageTime`,统一整个会话的时间展示时区;未配置时按浏览器时区
|
|
32
|
+
- **空状态欢迎页**:无消息时展示 Banner、`welcomeTitle`(默认「你好,我是小鲸」)与 `openingRemark`
|
|
33
|
+
|
|
34
|
+
## 组件结构
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
ai-chat-container(:data-ai-size="size")
|
|
38
|
+
├── Loading(chatLoading 时)
|
|
39
|
+
└── ResizeLayout
|
|
40
|
+
├── aside(侧边栏,固定右侧)
|
|
41
|
+
│ └── .ai-full-screen-wrapper(全屏目标容器,ref=fullScreenRef)
|
|
42
|
+
│ │ ├── Tab 标签页
|
|
43
|
+
│ │ │ ├── 执行情况(默认 Tab)
|
|
44
|
+
│ │ │ ├── 自定义 Tab × N(可关闭;标签可由 getSideTabRenderComponent 自定义)
|
|
45
|
+
│ │ │ └── #setting → 全屏/退出全屏 ToolBtn
|
|
46
|
+
│ │ ├── ExecutionSummary(执行情况 Tab 内容)
|
|
47
|
+
│ │ └── 自定义 Tab 组件(getSideRenderComponent 优先,否则 data.component;可注入 #locateButton)
|
|
48
|
+
└── main(主内容区)
|
|
49
|
+
├── MessageContainer(有消息时;#group / #message 可自定义)
|
|
50
|
+
├── 欢迎页(无消息时 .ai-welcome-content)
|
|
51
|
+
│ └── #welcome(默认:Banner + welcomeTitle + openingRemark;自定义则整块替换)
|
|
52
|
+
├── SelectionFooter(分享模式)
|
|
53
|
+
├── ShortcutRender(有快捷指令时)
|
|
54
|
+
└── ChatInput(透传 models / selectedModel / menuSources;interrupt 槽展示 UserQuestionCard / InputInfoAlert)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 基础用法
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<template>
|
|
61
|
+
<ChatContainer
|
|
62
|
+
v-model="inputValue"
|
|
63
|
+
:messages="messages"
|
|
64
|
+
:message-status="messageStatus"
|
|
65
|
+
:on-send-message="handleSendMessage"
|
|
66
|
+
:on-stop-sending="handleStopSending"
|
|
67
|
+
:on-agent-action="handleAgentAction"
|
|
68
|
+
:on-agent-feedback="handleAgentFeedback"
|
|
69
|
+
:on-user-action="handleUserAction"
|
|
70
|
+
@stop-streaming="handleStopStreaming"
|
|
71
|
+
/>
|
|
72
|
+
</template>
|
|
73
|
+
|
|
74
|
+
<script setup lang="ts">
|
|
75
|
+
import { ref } from 'vue';
|
|
76
|
+
import { ChatContainer, MessageStatus, type Message, type IToolBtn, type TagSchema } from '@blueking/chat-x';
|
|
77
|
+
|
|
78
|
+
const inputValue = ref('');
|
|
79
|
+
const messageStatus = ref(MessageStatus.Complete);
|
|
80
|
+
const messages = ref<Message[]>([]);
|
|
81
|
+
|
|
82
|
+
const handleSendMessage = async (content: string, docSchema: TagSchema) => {
|
|
83
|
+
messageStatus.value = MessageStatus.Streaming;
|
|
84
|
+
// ... 发送 AI 请求
|
|
85
|
+
messageStatus.value = MessageStatus.Complete;
|
|
86
|
+
};
|
|
87
|
+
const handleStopSending = async () => {
|
|
88
|
+
messageStatus.value = MessageStatus.Stop;
|
|
89
|
+
};
|
|
90
|
+
const handleAgentAction = async (tool: IToolBtn, messages: Message[]) => {
|
|
91
|
+
if (tool.id === 'like' || tool.id === 'unlike') {
|
|
92
|
+
return ['回答准确', '信息全面', '表达清晰'];
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const handleAgentFeedback = (tool: IToolBtn, messages: Message[], reasonList: string[], otherReason: string) => {
|
|
96
|
+
console.log('反馈:', tool.id, reasonList, otherReason);
|
|
97
|
+
};
|
|
98
|
+
const handleUserAction = async (tool: IToolBtn, message: Message) => {
|
|
99
|
+
console.log('用户操作:', tool.id);
|
|
100
|
+
};
|
|
101
|
+
const handleStopStreaming = () => {
|
|
102
|
+
messageStatus.value = MessageStatus.Stop;
|
|
103
|
+
};
|
|
104
|
+
</script>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**渲染效果**
|
|
108
|
+
|
|
109
|
+
## 字号主题
|
|
110
|
+
|
|
111
|
+
通过 `size` 切换两档字号主题。未传时默认为 `small`(12px 基准字号);设为 `normal` 时使用 14px 基准字号,并联动行高、间距与图标尺寸。
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<template>
|
|
115
|
+
<ChatContainer
|
|
116
|
+
v-model="inputValue"
|
|
117
|
+
:messages="messages"
|
|
118
|
+
message-status="complete"
|
|
119
|
+
size="normal"
|
|
120
|
+
:on-send-message="handleSendMessage"
|
|
121
|
+
/>
|
|
122
|
+
</template>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**渲染效果**(左右对比 `size="small"` 与 `size="normal"`)
|
|
126
|
+
|
|
127
|
+
> CSS 变量与档位取值详见 [主题配置 — 字号主题](../../theme/theme#字号主题)。
|
|
128
|
+
|
|
129
|
+
## 侧边栏与执行摘要
|
|
130
|
+
|
|
131
|
+
侧边栏默认包含「执行情况」Tab,展示所有工具调用和 FlowAgent 类型的 Activity 消息。支持关键词搜索过滤和点击定位到对话中的消息位置。Tab 标签内 `.ai-execution-summary-icon` 固定 16×16px,避免被 flex 压缩。
|
|
132
|
+
|
|
133
|
+
面板内的消息虽与对话流复用同一套渲染链路,但按**只读回看**呈现:`ExecutionSummary` 会 provide 面板上下文,FlowAgent 失败节点在面板内不展示「重试 / 跳过」,只保留「详情」;对话流内不受影响。详见 [ExecutionSummary](/components/agent/execution-summary)。
|
|
134
|
+
|
|
135
|
+
**展示条件**:侧栏是否渲染只取决于折叠态与是否存在可见 Tab —— `asideCollapsed === false` 且 `displayTabs` 非空即展开,与 `executionGroups`、`keyword`、是否有文件产物均无关。无执行数据时 `ExecutionSummary` 展示自身空态,无文件产物时 `FileArtifactPanel` 展示整块空态。`renderMode === Share` 分享态同样按折叠态展示侧栏(开放只读查看流程智能体详情/证据/执行情况),仅底部输入区保持隐藏。
|
|
136
|
+
|
|
137
|
+
**展开 / 折叠由外部判断(严格受控)**:只要传入了 `asideCollapsed`,折叠态就**一律以外部值为准**。容器内部的展开动作(点击文件卡片预览、`addCustomTab` 打开节点详情等)只发出 `update:asideCollapsed` 请求,外部不改值就不会展开 —— 所以务必用 `v-model:asideCollapsed` 绑定,只写 `:aside-collapsed` 会让这些内部入口失效。完全不传该 prop 时退化为组件内部状态(默认折叠),内部入口照常生效。容器不会因为数据变空而自动收起或重置自定义 Tab。
|
|
138
|
+
|
|
139
|
+
```vue
|
|
140
|
+
<template>
|
|
141
|
+
<!-- 业务方自行提供展开/收起入口;可复用包内 CollapsedAsideIcon -->
|
|
142
|
+
<button @click="asideCollapsed = !asideCollapsed">
|
|
143
|
+
{{ asideCollapsed ? '展开侧栏' : '收起侧栏' }}
|
|
144
|
+
</button>
|
|
145
|
+
<ChatContainer
|
|
146
|
+
v-model="inputValue"
|
|
147
|
+
v-model:aside-collapsed="asideCollapsed"
|
|
148
|
+
:messages="messages"
|
|
149
|
+
:message-status="messageStatus"
|
|
150
|
+
:on-send-message="handleSendMessage"
|
|
151
|
+
:on-agent-action="handleAgentAction"
|
|
152
|
+
@stop-streaming="handleStopStreaming"
|
|
153
|
+
@collapse-change="handleCollapseChange"
|
|
154
|
+
/>
|
|
155
|
+
</template>
|
|
156
|
+
|
|
157
|
+
<script setup lang="ts">
|
|
158
|
+
import { shallowRef } from 'vue';
|
|
159
|
+
// import { CollapsedAsideIcon } from '@blueking/chat-x';
|
|
160
|
+
|
|
161
|
+
const asideCollapsed = shallowRef(true);
|
|
162
|
+
|
|
163
|
+
const handleCollapseChange = (isCollapse: boolean, resizeAsideWidth: number) => {
|
|
164
|
+
console.log('侧边栏折叠:', isCollapse, '宽度:', resizeAsideWidth);
|
|
165
|
+
};
|
|
166
|
+
</script>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**渲染效果**(包含工具调用消息时,侧边栏自动展示「执行情况」)
|
|
170
|
+
|
|
171
|
+
侧边栏固定从**右侧**展开(内部 `ResizeLayout` 的 `placement` 恒为 `right`),不再支持左侧布局,也不再内置折叠按钮 —— 展开 / 收起入口由业务方自行提供,通过 `v-model:asideCollapsed` 驱动。
|
|
172
|
+
|
|
173
|
+
**展开 / 收起动画**:侧栏宽度做 `0.3s cubic-bezier(0.4, 0, 0.2, 1)` 过渡,内容同步淡入淡出;动画期间内容锁定展开态宽度(CSS 变量 `--resize-aside-width`)由外层裁切,避免被压缩重排。拖拽调宽时过渡自动关闭,保证跟手。
|
|
174
|
+
|
|
175
|
+
## 侧栏全屏
|
|
176
|
+
|
|
177
|
+
当侧栏 Tab 区域可见时,Tab 栏右侧(`#setting` 插槽)内置全屏切换按钮:
|
|
178
|
+
|
|
179
|
+
- 点击 **全屏** 图标:调用 `useFullScreen(fullScreenRef).enter()`,将 `.ai-full-screen-wrapper` 进入浏览器原生全屏
|
|
180
|
+
- 点击 **退出全屏** 图标:调用 `exit()` 退出;用户按 ESC 退出时 `isFullScreen` 也会自动同步
|
|
181
|
+
- 全屏状态下,侧栏内 `v-overflow-tips` 的 `appendTo` 会指向全屏容器,避免 tooltip 挂载到 `document.body` 后被全屏层遮挡
|
|
182
|
+
|
|
183
|
+
该能力由内部 `useFullScreen` composable 提供,详见 [useFullScreen](../../composables/use-full-screen.md)。
|
|
184
|
+
|
|
185
|
+
## 自定义 Tab
|
|
186
|
+
|
|
187
|
+
通过 `ref` 获取组件实例后,使用 `addCustomTab` / `removeCustomTab` 动态管理侧边栏 Tab。自定义 Tab 的生命周期由调用方掌控,容器不会因执行数据变空而自动清理(仅组件卸载时 `resetCustomTab`)。
|
|
188
|
+
|
|
189
|
+
### Tab 排序与显隐
|
|
190
|
+
|
|
191
|
+
`CustomTab` 支持 `order` / `visible` / `closable` 三个可选字段,用于控制 Tab 栏的排序与显隐:
|
|
192
|
+
|
|
193
|
+
| 字段 | 默认值 | 说明 |
|
|
194
|
+
| ---- | ------ | ---- |
|
|
195
|
+
| `order` | `100` | 排序权重,升序,越小越靠前。「执行情况」固定 `0`;FlowAgent「有效证据」固定 `10`(紧随执行情况),节点详情走默认 `100` |
|
|
196
|
+
| `visible` | `true` | 是否在 Tab 栏展示。`false` 时从栏内隐藏,但仍可被 `addCustomTab` / `selectCustomTab` 程序化选中;若被隐藏的 Tab 当前正被选中,则自动切到首个可见 Tab、内容不再渲染 |
|
|
197
|
+
| `closable` | `true` | 是否展示关闭按钮。「执行情况」强制不可关闭 |
|
|
198
|
+
|
|
199
|
+
- 排序为稳定排序,`order` 相同的 Tab 保持插入先后顺序。
|
|
200
|
+
- 「执行情况」Tab 的显隐统一由 `executionTabVisible` Prop 控制(见 Props 表),不通过 `visible` 字段配置。
|
|
201
|
+
- 同名(同 `name`)`addCustomTab` 会**合并更新**已有 Tab,可用于运行时调整 `order` / `visible` / `label`。
|
|
202
|
+
|
|
203
|
+
### 侧栏渲染扩展
|
|
204
|
+
|
|
205
|
+
应用层可通过以下 Props 覆盖默认 Tab 标签与侧栏内容区的渲染逻辑(例如 FlowAgent 节点详情使用业务自定义组件):
|
|
206
|
+
|
|
207
|
+
| Prop | 说明 |
|
|
208
|
+
| ---- | ---- |
|
|
209
|
+
| `getSideTabRenderComponent` | `(h, tab, { removeCustomTab }) => VNode \| undefined`。返回自定义 Tab 标签 VNode;未返回时使用默认图标 + `tab.label` + 关闭按钮 |
|
|
210
|
+
| `getSideRenderComponent` | `(h, props) => VNode \| undefined`。返回侧栏内容区组件 VNode;未返回时使用 `selectedTab.data.component` |
|
|
211
|
+
|
|
212
|
+
侧栏内容区实现上会以 **`selectedTab.name` 作为外层 `key`**,切换 Tab 时重建子树,避免插槽与局部状态残留;当前 Tab 的 `:is` 由内部 **`computed`** 根据 `getSideRenderComponent(h, selectedTab.data.props)` 与 `data.component` 解析,保证 Tab 切换或 `onCustomTabChange` 异步更新 props 后内容类型与数据一致。
|
|
213
|
+
|
|
214
|
+
```vue
|
|
215
|
+
<template>
|
|
216
|
+
<ChatContainer
|
|
217
|
+
:get-side-tab-render-component="renderSideTab"
|
|
218
|
+
:get-side-render-component="renderSidePanel"
|
|
219
|
+
...
|
|
220
|
+
/>
|
|
221
|
+
</template>
|
|
222
|
+
|
|
223
|
+
<script setup lang="ts">
|
|
224
|
+
import { h } from 'vue';
|
|
225
|
+
import type { CustomTab } from '@blueking/chat-x';
|
|
226
|
+
|
|
227
|
+
const renderSideTab = (createElement, tab, { removeCustomTab }) => {
|
|
228
|
+
if (tab.name.startsWith('custom-')) {
|
|
229
|
+
return createElement('span', {}, tab.label);
|
|
230
|
+
}
|
|
231
|
+
return undefined; // 走默认 Tab 标签
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const renderSidePanel = (createElement, props) => {
|
|
235
|
+
if (props?.has_confidence) {
|
|
236
|
+
return createElement(MyConfidencePanel, props);
|
|
237
|
+
}
|
|
238
|
+
return undefined; // 走 tab.data.component
|
|
239
|
+
};
|
|
240
|
+
</script>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 内置「文件产物」Tab
|
|
244
|
+
|
|
245
|
+
除「执行情况」外,容器内置一个常驻固定 Tab —— **「文件产物」**(`name: 'file-artifact'`),用于聚合预览当前会话具有 `outputId` 的助手产物、用户消息附件及输入框中上传成功的待发送附件(按 `outputId` 去重):
|
|
246
|
+
|
|
247
|
+
- **常驻挂载 / 默认选中**:容器初始化即通过 `ensureCustomTab` 挂上该 Tab(不展开侧栏);因 `order: -1` 排在 Tab 栏首位,在用户未主动切换过 Tab 时它就是侧栏的默认面板。不随产物有无增删,无产物时由面板展示整块空态
|
|
248
|
+
- **默认图标**:`ArtifactTabIcon`,16×16 线性折角文档,`fill` 走 `currentColor` 以继承 Tab 选中/默认色
|
|
249
|
+
- **主动打开**:点击 AI 回复中的文件卡片([ArtifactFileCard](/components/message/assistant-message))时,容器通过 `useArtifactPreviewProvider` 以 `outputId` 命中该文件,再 `addCustomTab` 展开侧栏并选中「文件产物」
|
|
250
|
+
- **排序 / 关闭**:`order: -1` 排在「执行情况」之前,`closable: false` 不可关闭
|
|
251
|
+
- **命中态维护**:产物列表为空时清空命中;命中项已不在列表时回落到第一个 `outputId`
|
|
252
|
+
- **内容**:由 [FileArtifactPanel](/components/message/file-artifact-panel) 渲染列表与下载头(无产物时为整块空态),预览委托内部 `ArtifactPreviewHost`;`download_url` / `preview_url` 通过 `onArtifactClick` 异步获取。文本类(`html` / `markdown` / `md` / `txt` / `json`)拉 `download_url` 正文直渲染(`md` 与 `markdown` 等价);其余类型用 `preview_url` iframe(一般为后台转好的 PDF)
|
|
253
|
+
- **状态管理**:命中、切换与 URL 缓存由 [useArtifactPreview](/composables/use-artifact-preview) 提供(Provider 在容器内、Consumer 在文件卡片 / 面板内);正文加载与分类型渲染由 Host 内部完成
|
|
254
|
+
- **未传 `onArtifactClick`**:下载按钮隐藏,预览区展示无数据
|
|
255
|
+
|
|
256
|
+
详见 [FileArtifactPanel 文件产物预览](/components/message/file-artifact-panel) 与 [useArtifactPreview 文件产物预览](/composables/use-artifact-preview)。
|
|
257
|
+
|
|
258
|
+
#### 接入示例
|
|
259
|
+
|
|
260
|
+
```vue
|
|
261
|
+
<template>
|
|
262
|
+
<ChatContainer
|
|
263
|
+
v-model="input"
|
|
264
|
+
:messages="messages"
|
|
265
|
+
:on-artifact-click="onArtifactClick"
|
|
266
|
+
@send-message="handleSend"
|
|
267
|
+
/>
|
|
268
|
+
</template>
|
|
269
|
+
|
|
270
|
+
<script setup lang="ts">
|
|
271
|
+
import { ref, shallowRef } from 'vue'
|
|
272
|
+
import {
|
|
273
|
+
ChatContainer,
|
|
274
|
+
MessageRole,
|
|
275
|
+
MessageStatus,
|
|
276
|
+
type AIFileInfo,
|
|
277
|
+
type Message,
|
|
278
|
+
} from '@blueking/chat-x'
|
|
279
|
+
|
|
280
|
+
const input = ref('')
|
|
281
|
+
const messages = shallowRef<Message[]>([
|
|
282
|
+
{
|
|
283
|
+
id: 'u1',
|
|
284
|
+
messageId: 'u1',
|
|
285
|
+
role: MessageRole.User,
|
|
286
|
+
status: MessageStatus.Complete,
|
|
287
|
+
content: '整理本周评审材料',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'a1',
|
|
291
|
+
messageId: 'a1',
|
|
292
|
+
uid: 'assistant-uid-1',
|
|
293
|
+
role: MessageRole.Assistant,
|
|
294
|
+
status: MessageStatus.Complete,
|
|
295
|
+
content: '已生成评审材料,点击卡片可在侧栏预览:',
|
|
296
|
+
property: {
|
|
297
|
+
artifacts: [
|
|
298
|
+
{ name: '周报.html', outputId: 'a-html', size: 10240, type: 'html' },
|
|
299
|
+
{ name: '说明.md', outputId: 'a-md', size: 8192, type: 'md' },
|
|
300
|
+
{ name: '配置.json', outputId: 'a-json', size: 2048, type: 'json' },
|
|
301
|
+
{ name: '立项.pdf', outputId: 'a-pdf', size: 204800, type: 'pdf' },
|
|
302
|
+
] satisfies AIFileInfo[],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
])
|
|
306
|
+
|
|
307
|
+
/** 文本类预览依赖 download_url;iframe 类依赖 preview_url */
|
|
308
|
+
const onArtifactClick = async (file: AIFileInfo) => {
|
|
309
|
+
const res = await api.getArtifactUrls(file.outputId)
|
|
310
|
+
return {
|
|
311
|
+
download_url: res.download_url,
|
|
312
|
+
preview_url: res.preview_url,
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const handleSend = () => {
|
|
317
|
+
/* ... */
|
|
318
|
+
}
|
|
319
|
+
</script>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 自定义 Tab 与「在对话中定位」
|
|
323
|
+
|
|
324
|
+
`addCustomTab` 的 `data` 可携带 **`messageUid`**(与对应活动消息的 `message.uid` 一致)。`ChatContainer` 在侧栏用 `<component :is="sideRenderComponent">`(内部计算属性,见上文「侧栏渲染扩展」)渲染自定义 Tab 时,会向子组件提供 **`locateButton` 插槽**:默认渲染「在对话中定位」按钮,点击后调用内部 `handleLocateMessageGroup(messageUid)`,优先滚动到主区域 `document.getElementById(messageUid)`;若不存在该节点,则在当前 `messageGroups` 中查找包含 `message.uid === messageUid` 的消息组,并滚动到该组的容器(`MessageGroup.uid` 作为组级 `id`)。
|
|
325
|
+
|
|
326
|
+
子组件若需展示该按钮,请在模板中声明 `<slot name="locateButton" />`(例如 FlowAgent 节点详情标题栏)。`FlowAgentContent` 等会在打开节点详情 Tab 时将 `messageUid` 写入 `data`,与 `ActivityMessage` 下传给内容区的 `message-uid` 对齐。
|
|
327
|
+
|
|
328
|
+
```vue
|
|
329
|
+
<template>
|
|
330
|
+
<ChatContainer
|
|
331
|
+
ref="chatContainerRef"
|
|
332
|
+
v-model="inputValue"
|
|
333
|
+
:messages="messages"
|
|
334
|
+
:message-status="messageStatus"
|
|
335
|
+
:on-custom-tab-change="handleCustomTabChange"
|
|
336
|
+
:on-send-message="handleSendMessage"
|
|
337
|
+
@stop-streaming="handleStopStreaming"
|
|
338
|
+
/>
|
|
339
|
+
</template>
|
|
340
|
+
|
|
341
|
+
<script setup lang="ts">
|
|
342
|
+
import { useTemplateRef } from 'vue';
|
|
343
|
+
import { ChatContainer, type CustomTab } from '@blueking/chat-x';
|
|
344
|
+
|
|
345
|
+
const chatContainerRef = useTemplateRef<InstanceType<typeof ChatContainer>>('chatContainerRef');
|
|
346
|
+
|
|
347
|
+
// 添加自定义 Tab(如 FlowAgent 节点详情)
|
|
348
|
+
const addNodeDetailTab = (nodeId: string, nodeName: string, messageUid?: string) => {
|
|
349
|
+
chatContainerRef.value?.addCustomTab({
|
|
350
|
+
name: `node-${nodeId}`,
|
|
351
|
+
label: nodeName,
|
|
352
|
+
data: {
|
|
353
|
+
component: MyNodeDetail, // 自定义组件(模板内需 <slot name="locateButton" /> 以展示侧栏「在对话中定位」)
|
|
354
|
+
props: { loading: true, data: {} },
|
|
355
|
+
messageUid, // 与活动消息 message.uid 一致时可省略;用于主对话定位
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// Tab 切换时加载数据
|
|
361
|
+
const handleCustomTabChange = async (tab: CustomTab) => {
|
|
362
|
+
const data = await fetchTabData(tab.name);
|
|
363
|
+
return data;
|
|
364
|
+
};
|
|
365
|
+
</script>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## 开场白
|
|
369
|
+
|
|
370
|
+
无消息时展示欢迎页:`welcomeTitle` 控制标题(未传时默认「你好,我是小鲸」),`openingRemark` 为开场白(支持 Markdown):
|
|
371
|
+
|
|
372
|
+
```vue
|
|
373
|
+
<template>
|
|
374
|
+
<ChatContainer
|
|
375
|
+
v-model="inputValue"
|
|
376
|
+
:messages="[]"
|
|
377
|
+
:message-status="messageStatus"
|
|
378
|
+
welcome-title="你好,我是小鲸"
|
|
379
|
+
opening-remark="你好!我是 AI 小鲸 🐳,可以帮你:\n\n- 编写和优化代码\n- 解答技术问题\n- 分析和调试错误"
|
|
380
|
+
:on-send-message="handleSendMessage"
|
|
381
|
+
/>
|
|
382
|
+
</template>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 自定义欢迎内容
|
|
386
|
+
|
|
387
|
+
通过 `#welcome` 可整块替换默认欢迎区,插槽参数为 `{ openingRemark, welcomeTitle }`:
|
|
388
|
+
|
|
389
|
+
```vue
|
|
390
|
+
<template>
|
|
391
|
+
<ChatContainer
|
|
392
|
+
v-model="inputValue"
|
|
393
|
+
:messages="[]"
|
|
394
|
+
:message-status="messageStatus"
|
|
395
|
+
welcome-title="欢迎使用 AI 助手"
|
|
396
|
+
opening-remark="选择一个快捷入口开始对话"
|
|
397
|
+
:on-send-message="handleSendMessage"
|
|
398
|
+
>
|
|
399
|
+
<template #welcome="{ openingRemark, welcomeTitle }">
|
|
400
|
+
<div class="my-welcome">
|
|
401
|
+
<h3>{{ welcomeTitle }}</h3>
|
|
402
|
+
<p>{{ openingRemark }}</p>
|
|
403
|
+
<div class="quick-actions">
|
|
404
|
+
<button @click="handleQuickAction('code')">写代码</button>
|
|
405
|
+
<button @click="handleQuickAction('debug')">调试</button>
|
|
406
|
+
<button @click="handleQuickAction('explain')">解释</button>
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
409
|
+
</template>
|
|
410
|
+
</ChatContainer>
|
|
411
|
+
</template>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
> **注意**:使用 `#welcome` 后将**整块替换**默认欢迎区(Banner、标题与开场白的 `ContentRender`),需自行编排完整欢迎页。
|
|
415
|
+
|
|
416
|
+
**渲染效果**
|
|
417
|
+
|
|
418
|
+
## 加载状态
|
|
419
|
+
|
|
420
|
+
`chatLoading` 为 `true` 时,整个容器显示 Loading 遮罩,适用于初始化加载场景(如拉取历史消息):
|
|
421
|
+
|
|
422
|
+
```vue
|
|
423
|
+
<template>
|
|
424
|
+
<ChatContainer
|
|
425
|
+
v-model="inputValue"
|
|
426
|
+
:messages="[]"
|
|
427
|
+
:chat-loading="true"
|
|
428
|
+
/>
|
|
429
|
+
</template>
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
**渲染效果**
|
|
433
|
+
|
|
434
|
+
## 流式输出
|
|
435
|
+
|
|
436
|
+
`messageStatus` 为 `streaming` 时,底部固定区域显示「停止生成」按钮,点击后触发 `@stop-streaming` 事件:
|
|
437
|
+
|
|
438
|
+
## 待审批阻塞发送
|
|
439
|
+
|
|
440
|
+
当会话中存在待审批的 AI Dev 工具审批中断时,`ChatContainer` 会在输入框上方展示提示,并禁用发送按钮。用户需要在审批卡片中点击「取消审批」或等待状态变化后,才能继续发送新消息。
|
|
441
|
+
|
|
442
|
+
```vue
|
|
443
|
+
<template>
|
|
444
|
+
<ChatContainer
|
|
445
|
+
v-model="inputValue"
|
|
446
|
+
:messages="messages"
|
|
447
|
+
message-status="complete"
|
|
448
|
+
:on-interrupt-resume="handleInterruptResume"
|
|
449
|
+
:on-send-message="handleSendMessage"
|
|
450
|
+
:on-stop-sending="handleStopSending"
|
|
451
|
+
/>
|
|
452
|
+
</template>
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**渲染效果**(待审批单存在时,输入区上方展示提示,发送按钮置灰)
|
|
456
|
+
|
|
457
|
+
## 用户问题中断
|
|
458
|
+
|
|
459
|
+
当会话中最近一条待处理 interrupt 包含 `InterruptReason.UserQuestion` 时,`ChatContainer` 会在 `ChatInput` 上方显示 [UserQuestionCard](/components/agent/user-question-card)(一次一题,标题栏可切换题目)。
|
|
460
|
+
|
|
461
|
+
- **结构化作答**:用户在卡片内逐题选择(单选可自动跳下一题),点击「完成」或「跳过」后通过 `onInterruptResume(payload, interrupt)` 回传 `UserQuestionResume`。
|
|
462
|
+
- **输入框发送**:用户也可在输入框直接点击发送;容器会调用 `onSendMessage(content, docSchema, options)`,其中 `options.interrupt` 为当前激活的 UserQuestion,`options.payload` 为 `buildSkipResumePayload` 生成的 skip resume(`status: 'cancelled'`,`answers: []`)。此时**不会自动清空**输入框,由业务侧在 `onSendMessage` 内决定如何处理 `content` 与中断恢复。
|
|
463
|
+
|
|
464
|
+
```vue
|
|
465
|
+
<template>
|
|
466
|
+
<ChatContainer
|
|
467
|
+
v-model="inputValue"
|
|
468
|
+
:messages="messages"
|
|
469
|
+
message-status="complete"
|
|
470
|
+
:on-interrupt-resume="handleInterruptResume"
|
|
471
|
+
:on-send-message="handleSendMessage"
|
|
472
|
+
/>
|
|
473
|
+
</template>
|
|
474
|
+
|
|
475
|
+
<script setup lang="ts">
|
|
476
|
+
import {
|
|
477
|
+
type OnInterruptResume,
|
|
478
|
+
type UserMessage,
|
|
479
|
+
type TagSchema,
|
|
480
|
+
type Interrupt,
|
|
481
|
+
type InterruptResume,
|
|
482
|
+
} from '@blueking/chat-x';
|
|
483
|
+
|
|
484
|
+
const handleInterruptResume: OnInterruptResume = async (payload, interrupt) => {
|
|
485
|
+
// UserQuestionCard 完成 / 跳过时 payload 为 UserQuestionResume
|
|
486
|
+
await resumeAgent({ interruptId: interrupt.id, resume: payload });
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const handleSendMessage = async (
|
|
490
|
+
content: UserMessage['content'],
|
|
491
|
+
docSchema: TagSchema,
|
|
492
|
+
options?: { interrupt?: Interrupt; payload?: InterruptResume },
|
|
493
|
+
) => {
|
|
494
|
+
if (options?.interrupt && options?.payload) {
|
|
495
|
+
// 存在 UserQuestion 时发送:附带 skip resume,content 仍为输入框文本
|
|
496
|
+
await resumeAgent({ interruptId: options.interrupt.id, resume: options.payload });
|
|
497
|
+
// 业务侧自行决定是否将 content 作为新用户消息继续发送
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
await sendMessage(content, docSchema);
|
|
501
|
+
};
|
|
502
|
+
</script>
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## 自定义消息组渲染
|
|
506
|
+
|
|
507
|
+
通过 `#group` 插槽可替换单个消息组的默认内容,透传至内部 `MessageContainer`。外层消息组容器(`id`、hover、选中背景)仍由 `MessageContainer` 管理。
|
|
508
|
+
|
|
509
|
+
> **注意**:提供 `#group` 后需自行编排组内全部 UI(Checkbox、消息列表、`MessageTools`);若只需替换单条消息,请使用 `#message` 插槽。详见 [MessageContainer 自定义消息组渲染](/components/setup/message-container#自定义消息组渲染)。
|
|
510
|
+
|
|
511
|
+
```vue
|
|
512
|
+
<ChatContainer
|
|
513
|
+
v-model="inputValue"
|
|
514
|
+
:messages="messages"
|
|
515
|
+
message-status="complete"
|
|
516
|
+
:on-send-message="handleSendMessage"
|
|
517
|
+
>
|
|
518
|
+
<template #group="{ group }">
|
|
519
|
+
<MyCustomGroup :group="group" />
|
|
520
|
+
</template>
|
|
521
|
+
</ChatContainer>
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### 自定义题目渲染
|
|
525
|
+
|
|
526
|
+
通过 `#interruptQuestion` slot 可覆盖输入区上方 `UserQuestionCard` 的默认选择题渲染,参数与 [UserQuestionCard](/components/agent/user-question-card) 的 `#question` 一致:
|
|
527
|
+
|
|
528
|
+
```vue
|
|
529
|
+
<ChatContainer
|
|
530
|
+
v-model="inputValue"
|
|
531
|
+
:messages="messages"
|
|
532
|
+
:on-interrupt-resume="handleInterruptResume"
|
|
533
|
+
:on-send-message="handleSendMessage"
|
|
534
|
+
>
|
|
535
|
+
<template #interruptQuestion="{ question, qIndex, answer, setAnswer, confirm }">
|
|
536
|
+
<MyCustomForm
|
|
537
|
+
:model="question"
|
|
538
|
+
@change="setAnswer"
|
|
539
|
+
@submit="confirm"
|
|
540
|
+
/>
|
|
541
|
+
</template>
|
|
542
|
+
</ChatContainer>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
## 分享模式
|
|
546
|
+
|
|
547
|
+
点击消息工具栏的「分享」按钮后进入分享模式,底部出现 `SelectionFooter` 操作栏:
|
|
548
|
+
|
|
549
|
+
```vue
|
|
550
|
+
<template>
|
|
551
|
+
<ChatContainer
|
|
552
|
+
v-model="inputValue"
|
|
553
|
+
:messages="messages"
|
|
554
|
+
:message-status="messageStatus"
|
|
555
|
+
:on-send-message="handleSendMessage"
|
|
556
|
+
:on-agent-action="handleAgentAction"
|
|
557
|
+
@confirm-share="handleConfirmShare"
|
|
558
|
+
@stop-streaming="handleStopStreaming"
|
|
559
|
+
/>
|
|
560
|
+
</template>
|
|
561
|
+
|
|
562
|
+
<script setup lang="ts">
|
|
563
|
+
import { type IToolBtn, type Message } from '@blueking/chat-x';
|
|
564
|
+
|
|
565
|
+
// 第二参数 source 为触发多选态的按钮对象,可据此区分 share / save 等不同确认场景
|
|
566
|
+
const handleConfirmShare = (selectedMessages: Message[], source?: IToolBtn) => {
|
|
567
|
+
if (source?.id === 'save') {
|
|
568
|
+
console.log('保存选中的消息:', selectedMessages);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
console.log('分享消息:', selectedMessages);
|
|
572
|
+
};
|
|
573
|
+
</script>
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### 自定义按钮触发多选(triggerSelection)
|
|
577
|
+
|
|
578
|
+
除内置「分享」外,任意自定义工具按钮标记 `triggerSelection: true` 后,点击即可复用同一套多选流程(勾选消息 → `SelectionFooter` 确认),确认时同样触发 `confirmShare`。配合 `messageTools` / `updateTools` / `userMessageTools`(合并规则见 [MessageContainer · 自定义消息工具栏](/components/setup/message-container))即可扩展如「保存」「收藏到空间」等批量操作,或隐藏用户消息上的编辑 / 删除。
|
|
579
|
+
|
|
580
|
+
```vue
|
|
581
|
+
<template>
|
|
582
|
+
<ChatContainer
|
|
583
|
+
v-model="inputValue"
|
|
584
|
+
:messages="messages"
|
|
585
|
+
message-status="complete"
|
|
586
|
+
:message-tools="customMessageTools"
|
|
587
|
+
:on-agent-action="handleAgentAction"
|
|
588
|
+
@confirm-share="handleConfirmShare"
|
|
589
|
+
/>
|
|
590
|
+
</template>
|
|
591
|
+
|
|
592
|
+
<script setup lang="ts">
|
|
593
|
+
import { DownloadIcon, type IToolBtn, type Message } from '@blueking/chat-x';
|
|
594
|
+
|
|
595
|
+
const customMessageTools: IToolBtn[] = [
|
|
596
|
+
// 追加「保存」按钮,点击进入多选态;确认走 confirmShare
|
|
597
|
+
{ id: 'save', name: '保存', description: '保存该回答', icon: DownloadIcon, triggerSelection: true },
|
|
598
|
+
];
|
|
599
|
+
|
|
600
|
+
const handleConfirmShare = (selectedMessages: Message[], source?: IToolBtn) => {
|
|
601
|
+
if (source?.id === 'save') {
|
|
602
|
+
// 处理「保存」批量确认
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
</script>
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
> `triggerSelection` 的按钮不会调用 `onAgentAction`,而是直接进入多选态;未标记该字段(且非 `share`)的按钮仍走 `onAgentAction`。
|
|
609
|
+
|
|
610
|
+
**渲染效果**(点击 AI 回复工具栏中的「分享」按钮进入多选模式)
|
|
611
|
+
|
|
612
|
+
**分享流程**:
|
|
613
|
+
|
|
614
|
+
1. 用户点击消息工具栏中的「分享」按钮(或任意 `triggerSelection: true` 的自定义按钮)
|
|
615
|
+
2. 进入多选模式,用户勾选要分享的消息
|
|
616
|
+
3. 底部 `SelectionFooter` 提供全选、取消、确认操作
|
|
617
|
+
4. 确认后触发 `confirmShare` 事件,携带选中的消息列表与触发按钮对象(`source`)
|
|
618
|
+
|
|
619
|
+
## 输入框菜单与资源引用
|
|
620
|
+
|
|
621
|
+
`menuSources` 继承自 [ChatInput](/components/input/chat-input),一份数组按 `type` 分发到 `/`、`@`、`\` 与左下角 + 号。容器在此之上做了三件事:
|
|
622
|
+
|
|
623
|
+
**1. 会话产物自动收集**:`menuSources` 中没有 `artifact` 条目时,容器用 [`collectMessageArtifacts`](/utils/#会话产物收集) 从 `messages` 里收集产物补进去——来源是具有 `outputId` 的助手消息 `property.artifacts` 与用户消息二进制附件,不再回退到 URL 或文件名。上传响应的 `path` 由输入框映射为 `outputId`,尚未发送的成功附件也会追加到菜单。手动传入的 `artifact.id` 必须是对应文件的 `outputId`。业务方自己传了 `artifact` 条目时以传入的为准,容器不再自动补。没有产物时 `@` / + 号菜单不会出现「会话产物」分组。
|
|
624
|
+
|
|
625
|
+
**2. 资源引用入口**:容器通过 [useInputMention](/composables/use-input-mention) 提供 `insertMention`,消息区的文件卡片与侧栏产物面板因此能直接把文件「@ 进输入框」,无需逐层透传输入框实例。没有输入框的场景(`Share` 只读态)自动不显示引用按钮。
|
|
626
|
+
|
|
627
|
+
**3. 编辑态菜单下发**:`menuSources` 经 [useGlobalConfig](/composables/use-global-config) 注入,用户消息进入编辑态时就地渲染的 `ChatInput` 也能拿到同一份数据源。`supportUpload` 同样下发,编辑态输入框也会出现内置「文件」项。
|
|
628
|
+
|
|
629
|
+
```vue
|
|
630
|
+
<template>
|
|
631
|
+
<ChatContainer
|
|
632
|
+
v-model="inputValue"
|
|
633
|
+
:menu-sources="menuSources"
|
|
634
|
+
:messages="messages"
|
|
635
|
+
:message-status="messageStatus"
|
|
636
|
+
:on-send-message="handleSendMessage"
|
|
637
|
+
/>
|
|
638
|
+
</template>
|
|
639
|
+
|
|
640
|
+
<script setup lang="ts">
|
|
641
|
+
import { ref } from 'vue';
|
|
642
|
+
import { ChatContainer, type IInputMenuItem } from '@blueking/chat-x';
|
|
643
|
+
|
|
644
|
+
// 只需提供智能体能力与知识库;会话产物由容器从 messages 自动收集
|
|
645
|
+
const menuSources = ref<IInputMenuItem[]>([
|
|
646
|
+
{ id: 'translate', type: 'skill', name: '翻译', description: '把选中的文本翻译成目标语言' },
|
|
647
|
+
{ id: 'database-server', type: 'mcp', name: 'database-server' },
|
|
648
|
+
{ id: 'kb-api', type: 'knowledgebase', name: 'API 接口文档' },
|
|
649
|
+
{ id: 'prompt-article', type: 'prompt', name: '写文章', content: '帮我写一篇关于 {topic} 的文章' },
|
|
650
|
+
]);
|
|
651
|
+
</script>
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
> 用户消息要把 `@` 选中的资源以标签形态回显,需要业务侧把 `onSendMessage` 的 `docSchema` 存进 `message.property.docSchema`,详见 [MentionText](/components/rendering/mention-text)。
|
|
655
|
+
|
|
656
|
+
## 模型选择
|
|
657
|
+
|
|
658
|
+
`ChatContainer` 将 `models`、`v-model:selected-model` 与 `@model-change` 透传至内部 [ChatInput](/components/input/chat-input)。传入 `models` 后,发送按钮左侧展示 [ModelSelector](/components/input/model-selector);选中值为模型的 `llm_name`,发送时可读取当前 `selectedModel`。
|
|
659
|
+
|
|
660
|
+
```vue
|
|
661
|
+
<template>
|
|
662
|
+
<ChatContainer
|
|
663
|
+
v-model="inputValue"
|
|
664
|
+
v-model:selected-model="selectedModel"
|
|
665
|
+
:messages="messages"
|
|
666
|
+
message-status="complete"
|
|
667
|
+
:models="models"
|
|
668
|
+
:on-send-message="handleSendMessage"
|
|
669
|
+
@model-change="handleModelChange"
|
|
670
|
+
/>
|
|
671
|
+
</template>
|
|
672
|
+
|
|
673
|
+
<script setup lang="ts">
|
|
674
|
+
import { ref } from 'vue';
|
|
675
|
+
import { ChatContainer, type IModelOption, type Message, type TagSchema } from '@blueking/chat-x';
|
|
676
|
+
|
|
677
|
+
const inputValue = ref('');
|
|
678
|
+
// 选中值为 llm_name
|
|
679
|
+
const selectedModel = ref('GPT-4');
|
|
680
|
+
const messages = ref<Message[]>([]);
|
|
681
|
+
const models: IModelOption[] = [
|
|
682
|
+
{ id: 1, llm_name: 'GPT-4', property: { support_thinking: true } },
|
|
683
|
+
{ id: 2, llm_name: 'Claude 3', property: { support_thinking_quick: true } },
|
|
684
|
+
{ id: 3, llm_name: 'DeepSeek', property: { support_vision: true } },
|
|
685
|
+
];
|
|
686
|
+
|
|
687
|
+
const handleSendMessage = async (content: string, docSchema: TagSchema) => {
|
|
688
|
+
// 发送时可读取 selectedModel.value
|
|
689
|
+
};
|
|
690
|
+
const handleModelChange = (model: IModelOption) => {
|
|
691
|
+
console.log('切换模型:', model);
|
|
692
|
+
};
|
|
693
|
+
</script>
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**渲染效果**(输入区发送按钮左侧可切换模型)
|
|
697
|
+
|
|
698
|
+
## 快捷指令
|
|
699
|
+
|
|
700
|
+
通过 `v-model:selectedShortcut` 管理快捷指令选中状态:
|
|
701
|
+
|
|
702
|
+
```vue
|
|
703
|
+
<template>
|
|
704
|
+
<ChatContainer
|
|
705
|
+
v-model="inputValue"
|
|
706
|
+
v-model:selected-shortcut="selectedShortcut"
|
|
707
|
+
:messages="messages"
|
|
708
|
+
:message-status="messageStatus"
|
|
709
|
+
:shortcuts="shortcuts"
|
|
710
|
+
:on-send-message="handleSendMessage"
|
|
711
|
+
@shortcut-close="handleShortcutClose"
|
|
712
|
+
@shortcut-submit="handleShortcutSubmit"
|
|
713
|
+
@stop-streaming="handleStopStreaming"
|
|
714
|
+
/>
|
|
715
|
+
</template>
|
|
716
|
+
|
|
717
|
+
<script setup lang="ts">
|
|
718
|
+
import { ref } from 'vue';
|
|
719
|
+
import { type Shortcut } from '@blueking/chat-x';
|
|
720
|
+
|
|
721
|
+
const selectedShortcut = ref<Shortcut | null>(null);
|
|
722
|
+
|
|
723
|
+
const handleShortcutClose = () => {
|
|
724
|
+
selectedShortcut.value = null;
|
|
725
|
+
};
|
|
726
|
+
const handleShortcutSubmit = (formModel: Record<string, unknown>) => {
|
|
727
|
+
console.log('快捷指令提交:', formModel);
|
|
728
|
+
};
|
|
729
|
+
</script>
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
## API
|
|
733
|
+
|
|
734
|
+
### Props
|
|
735
|
+
|
|
736
|
+
ChatContainer 的 Props 继承自 `ChatInputProps` 和 `MessageContainerProps`(排除 `enableSelection` 和 `messageGroups`),另外新增:
|
|
737
|
+
|
|
738
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
739
|
+
| ------------------------- | ---------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
740
|
+
| chatLoading | `boolean` | — | 整体加载状态,`true` 时显示 Loading 遮罩 |
|
|
741
|
+
| commonTippyOptions | `AITippyProps` | — | 通用 Tippy 配置,注入到所有使用 `v-overflow-tips` 的子组件。原样透传,仅全屏态强制把 `appendTo` 改为全屏容器;未传 `appendTo` 时不注入该字段,各浮层沿用自身默认挂载点 |
|
|
742
|
+
| executionTabVisible | `boolean` | `true` | 「执行情况」Tab 是否展示;为 `false` 时从 Tab 栏隐藏,若正被选中则切到首个可见 Tab |
|
|
743
|
+
| getSideRenderComponent | `(h, props?) => VNode \| undefined` | — | 自定义侧栏内容区渲染;未返回时使用 `selectedTab.data.component` |
|
|
744
|
+
| getSideTabRenderComponent | `(h, tab, { removeCustomTab }) => VNode \| undefined` | — | 自定义侧栏 Tab 标签渲染;未返回时使用默认图标 + 文案 + 关闭按钮 |
|
|
745
|
+
| models | `IModelOption[]` | — | 可选模型列表(继承自 ChatInput);传入后在发送按钮左侧展示 ModelSelector |
|
|
746
|
+
| openingRemark | `string` | — | 开场白,无消息时显示,支持 Markdown |
|
|
747
|
+
| resizeProps | `{ disabled?: boolean; initialDivide?: number \| string; max?: number; min?: number }` | — | 透传给内部 `ResizeLayout`;与默认 `collapsible: false`、`immediate: true`、`min: 400` 合并;`placement` 固定为 `right`,不可覆盖。**数字型** `initialDivide` 还会作为内部侧栏宽度初值(驱动 `--resize-main-width`,并在展开时作为 `collapseChange` 的 `width`);百分比等字符串则回退为 `400` |
|
|
748
|
+
| size | `'normal' \| 'small'` | `'small'` | 字号主题:`small` 12px / `normal` 14px;根节点设置 `data-ai-size` 并注入 `useGlobalConfig` |
|
|
749
|
+
| timezone | `string` | — | 消息时间展示所用的 IANA 时区名(如 `Asia/Shanghai`);经 `useGlobalConfig` 注入,未配置时按浏览器时区展示,详见 [MessageTime](/components/feedback/message-time) |
|
|
750
|
+
| welcomeTitle | `string` | — | 欢迎页标题;未传时默认展示「你好,我是小鲸」 |
|
|
751
|
+
| onCustomTabChange | `(tab: CustomTab) => Promise<any>` | — | 自定义 Tab 切换回调,返回值作为 Tab 组件 props |
|
|
752
|
+
| onArtifactClick | `(file: AIFileInfo) => Promise<{ download_url?: string; preview_url?: string }>` | — | 异步获取下载 / 预览链接(每次调用重新获取,无缓存;同文件并发去重)。文本类预览依赖 `download_url`,iframe 类依赖 `preview_url`;未传则隐藏下载、预览无数据 |
|
|
753
|
+
|
|
754
|
+
> 其余 Props(如 `messages`、`messageStatus`、`onSendMessage`、`menuSources`、`shortcuts`、`userMessageTools` 等)继承自 [ChatInput](/components/input/chat-input) 与 [MessageContainer](/components/setup/message-container)。`menuSources` 由容器补齐会话产物后再下发(见 [输入框菜单与资源引用](#输入框菜单与资源引用));`userMessageTools` 透传给内部 `MessageContainer`,用于按 id 覆盖或隐藏用户消息工具栏。
|
|
755
|
+
|
|
756
|
+
### v-model
|
|
757
|
+
|
|
758
|
+
| 属性名 | 类型 | 说明 |
|
|
759
|
+
| ---------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
760
|
+
| modelValue | `string \| TagSchema` | 输入框内容,支持纯文本或标签结构 |
|
|
761
|
+
| selectedShortcut | `Shortcut \| null` | 当前选中的快捷指令 |
|
|
762
|
+
| cite | `string` | 引用内容 |
|
|
763
|
+
| renderMode | `RenderMode` | 渲染模式(默认 `Chat`)。`Share` 开放侧栏只读查看并隐藏底部输入与交互操作;`Test` 隐藏分享按钮 |
|
|
764
|
+
| selectedModel | `string` | 当前选中模型的 `llm_name`,透传至 ChatInput 的 ModelSelector |
|
|
765
|
+
| asideCollapsed | `boolean` | 侧栏折叠态,**严格受控**:传入后一律以外部值为准,内部展开动作(文件预览、`addCustomTab`)只发 `update:asideCollapsed`,外部不改则不展开;不传时由组件内部自持(默认折叠) |
|
|
766
|
+
|
|
767
|
+
### Events
|
|
768
|
+
|
|
769
|
+
| 事件名 | 参数 | 说明 |
|
|
770
|
+
| -------------- | -------------------------------------- | ------------------------------------ |
|
|
771
|
+
| stopStreaming | — | 点击「停止生成」按钮 |
|
|
772
|
+
| shortcutClose | — | 关闭快捷指令表单 |
|
|
773
|
+
| shortcutSubmit | `(formModel: Record<string, unknown>)` | 提交快捷指令表单 |
|
|
774
|
+
| confirmShare | `(messages: Message[], source?: IToolBtn)` | 确认分享/多选,携带选中的消息与触发按钮对象(`source`,用于区分 share/save 等场景) |
|
|
775
|
+
| collapseChange | `(isCollapse: boolean, width: number)` | 侧边栏折叠/展开状态变化(含宽度,便于外层容器扩宽) |
|
|
776
|
+
| update:asideCollapsed | `(collapsed: boolean)` | 折叠态变更请求(`v-model:asideCollapsed`);受控时是否真的展开取决于外部是否更新该值 |
|
|
777
|
+
| selectShortcut | `(shortcut: Shortcut)` | 选择快捷指令(继承自 ChatInput) |
|
|
778
|
+
| deleteShortcut | — | 删除已选快捷指令(继承自 ChatInput) |
|
|
779
|
+
| deleteFile | `(file: Partial<UploadFile>)` | 取消输入框附件(继承自 ChatInput);业务方根据 `file.id` 调用删除接口,UI 立即移除,不等待结果 |
|
|
780
|
+
| modelChange | `(model: IModelOption)` | 切换模型(继承自 ChatInput) |
|
|
781
|
+
|
|
782
|
+
### Slots
|
|
783
|
+
|
|
784
|
+
| 插槽名 | 参数 | 说明 |
|
|
785
|
+
| ----------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
786
|
+
| codeHeader | `{ language: string; token: Token[] }` | 代码块头部自定义操作区域(定义于类型;透传链路同 MessageRender) |
|
|
787
|
+
| default | `{ messages, messageStatus, messageGroups, selectedUserMessages, messageToolsStatus, isShareMode, commonTippyOptions, handleAgentAction, onAgentFeedback, onInterruptResume, onUserAction, onUserInputConfirm, onUserShortcutConfirm }` | 自定义消息列表区域;未提供时渲染默认 `MessageContainer` |
|
|
788
|
+
| group | `{ group: MessageGroup }` | 自定义单个消息组,透传至 MessageContainer `#group`;替换默认 Checkbox、消息列表与工具栏 |
|
|
789
|
+
| interruptQuestion | `{ question, qIndex, answer, setAnswer, confirm }` | 自定义 UserQuestion 单题渲染,透传至 UserQuestionCard `#question` |
|
|
790
|
+
| message | `{ message, messageToolsStatus, onInterruptResume }` | 自定义单条消息;自定义中断消息时需继续透传 `onInterruptResume` |
|
|
791
|
+
| welcome | `{ openingRemark?: string; welcomeTitle?: string }` | 无消息时自定义欢迎页;传入则整块替换默认 Banner、标题与开场白 |
|
|
792
|
+
|
|
793
|
+
### Expose
|
|
794
|
+
|
|
795
|
+
| 方法/属性名 | 类型 | 说明 |
|
|
796
|
+
| --------------- | --------------------------- | -------------- |
|
|
797
|
+
| selectedTab | `Ref<CustomTab>` | 当前选中的 Tab |
|
|
798
|
+
| addCustomTab | `(tab: CustomTab) => void` | 添加自定义 Tab |
|
|
799
|
+
| removeCustomTab | `(tabName: string) => void` | 移除自定义 Tab |
|
|
800
|
+
| selectCustomTab | `(tab: CustomTab) => void` | 切换到指定 Tab |
|
|
801
|
+
| enterShareMode | `() => void` | 手动进入分享多选模式 |
|
|
802
|
+
| exitShareMode | `() => void` | 退出分享多选模式,并清空已选消息 |
|
|
803
|
+
|
|
804
|
+
## 渲染模式
|
|
805
|
+
|
|
806
|
+
通过 `v-model:render-mode` 控制容器的渲染行为。`ChatContainer` 会把当前 `renderMode` 注入给后代组件,供内容渲染根据场景收敛交互能力。
|
|
807
|
+
|
|
808
|
+
| `renderMode` | 侧边栏 Tab | 底部输入区域 | MessageTools 工具栏 | 说明 |
|
|
809
|
+
| ------------ | ---------------------- | --------------------------------- | --------------------- | -------------------------------- |
|
|
810
|
+
| `Chat` | 正常显示 | 正常显示(ChatInput / ShortcutRender / SelectionFooter) | 全部工具按钮 | 默认对话模式 |
|
|
811
|
+
| `Share` | 正常显示(开放只读查看) | **隐藏** | **隐藏**(多选模式) | 分享预览模式;开放流程智能体侧栏详情/证据/执行情况与耗时,仅隐藏「重试/跳过」等交互 |
|
|
812
|
+
| `Test` | 正常显示 | 正常显示 | 过滤掉「分享」按钮 | 测试/嵌入模式,隐藏分享入口 |
|
|
813
|
+
|
|
814
|
+
```vue
|
|
815
|
+
<template>
|
|
816
|
+
<ChatContainer
|
|
817
|
+
v-model="inputValue"
|
|
818
|
+
v-model:render-mode="renderMode"
|
|
819
|
+
:messages="messages"
|
|
820
|
+
:message-status="messageStatus"
|
|
821
|
+
:on-send-message="handleSendMessage"
|
|
822
|
+
/>
|
|
823
|
+
</template>
|
|
824
|
+
|
|
825
|
+
<script setup lang="ts">
|
|
826
|
+
import { shallowRef } from 'vue';
|
|
827
|
+
import { ChatContainer, RenderMode } from '@blueking/chat-x';
|
|
828
|
+
|
|
829
|
+
const renderMode = shallowRef<RenderMode>(RenderMode.Chat);
|
|
830
|
+
</script>
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
## 类型定义
|
|
834
|
+
|
|
835
|
+
```typescript
|
|
836
|
+
import {
|
|
837
|
+
ChatContainer,
|
|
838
|
+
RenderMode,
|
|
839
|
+
MessageRole,
|
|
840
|
+
type CustomTab,
|
|
841
|
+
type IModelOption,
|
|
842
|
+
type MessageGroup,
|
|
843
|
+
type Shortcut,
|
|
844
|
+
type Message,
|
|
845
|
+
} from '@blueking/chat-x';
|
|
846
|
+
|
|
847
|
+
// 消息组(由 useMessageGroup 生成)
|
|
848
|
+
interface MessageGroup {
|
|
849
|
+
checked: boolean;
|
|
850
|
+
isHover: boolean;
|
|
851
|
+
messages: Message[];
|
|
852
|
+
pause?: boolean;
|
|
853
|
+
startTime?: number;
|
|
854
|
+
type: MessageRole;
|
|
855
|
+
uid: string;
|
|
856
|
+
userMessageTitle?: number | string;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// 自定义 Tab(data 可与 messageUid 组合,供侧栏定位主对话)
|
|
860
|
+
interface CustomTab<T = Record<string, unknown>> {
|
|
861
|
+
label: string;
|
|
862
|
+
name: string;
|
|
863
|
+
icon?: string;
|
|
864
|
+
order?: number; // 缺省 100;「执行情况」固定 0
|
|
865
|
+
visible?: boolean; // 缺省 true;false 时栏内隐藏,仍可程序化选中
|
|
866
|
+
closable?: boolean; // 缺省 true;「执行情况」强制不可关闭
|
|
867
|
+
data?: T & { messageUid?: string; component?: Component; props?: Record<string, unknown> };
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// 模型选项(透传至 ChatInput / ModelSelector,贴合后端接口结构;能力标签由 property 派生)
|
|
871
|
+
interface IModelOption {
|
|
872
|
+
id: number;
|
|
873
|
+
llm_code: string;
|
|
874
|
+
llm_name: string; // 展示名,同时作为选中值
|
|
875
|
+
llm_type: string;
|
|
876
|
+
space_auth_mode: string;
|
|
877
|
+
user_auth_mode: string;
|
|
878
|
+
max_token_size: number;
|
|
879
|
+
property: {
|
|
880
|
+
support_thinking?: boolean; // → 深度思考
|
|
881
|
+
support_thinking_quick?: boolean; // → 快速思考
|
|
882
|
+
support_vision?: boolean; // → 图生文
|
|
883
|
+
[key: string]: unknown;
|
|
884
|
+
};
|
|
885
|
+
icon?: string;
|
|
886
|
+
description?: string;
|
|
887
|
+
base_model?: string;
|
|
888
|
+
tag_names?: string[];
|
|
889
|
+
disabled?: boolean; // 前端扩展字段
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// 快捷指令
|
|
893
|
+
interface Shortcut {
|
|
894
|
+
id: string;
|
|
895
|
+
name: string;
|
|
896
|
+
components?: ShortcutComponent[];
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
## 关联组件
|
|
901
|
+
|
|
902
|
+
- [MessageContainer](/components/setup/message-container) — 消息列表区域
|
|
903
|
+
- [ChatInput](/components/input/chat-input) — 输入与快捷指令
|
|
904
|
+
- [ModelSelector](/components/input/model-selector) — 模型选择器(透传 `models` / `selectedModel`)
|
|
905
|
+
- [ShortcutRender](/components/input/shortcut-render) — 快捷指令表单
|
|
906
|
+
- [ExecutionSummary](/components/agent/execution-summary) — 执行摘要侧栏
|
|
907
|
+
- [SelectionFooter](/components/input/selection-footer) — 多选操作栏
|
|
908
|
+
- [ToolBtn](/components/feedback/tool-btn) — 侧栏全屏按钮
|
|
909
|
+
- [useFullScreen](/composables/use-full-screen) — 侧栏全屏控制
|
|
910
|
+
- [useGlobalConfig](/composables/use-global-config) — 注入 `size`、`supportUpload`、`timezone` 与 `menuSources`
|
|
911
|
+
- [useInputMention](/composables/use-input-mention) — 开放「资源插入输入框」能力
|
|
912
|
+
- [主题配置](/theme/theme) — 字号主题 CSS 变量
|