侧栏 Tab 自定义渲染
自 v2.1.4-beta.7 起,
ChatBot/AIBlueking支持将 FlowAgent 等场景下侧栏自定义 Tab 的内容区与标签栏交由业务方渲染,并与主对话区「在对话中定位」联动。
概述
带执行情况侧栏的对话布局中,除默认「执行情况」Tab 外,FlowAgent 等活动消息可通过 addCustomTab 打开节点详情等自定义 Tab。此前需在 ChatContainer 层配置 getSideRenderComponent 等能力;自 v2.1.4-beta.7 起,以下 Props 已透传到 ChatBot 与 AIBlueking,集成方无需再单独包裹 ChatContainer。
| 能力 | Prop | 说明 |
|---|---|---|
| 自定义侧栏内容 | getSideRenderComponent | 按当前 Tab 的 data.props 返回 VNode,覆盖 addCustomTab 注册的 data.component |
| 自定义 Tab 标签 | getSideTabRenderComponent | 自定义标签栏图标、文案、关闭按钮等 |
| 切换 Tab 时拉取详情 | onCustomTabChange | 异步返回详情数据,写入 data.props(含 loading、data);不传时 ChatBot 对 Flow 节点 Tab 走内置 getFlowAgentTaskNodeInfo |
与 消息自定义渲染 的区别:
- 消息自定义渲染:主对话区消息体内的
custom-component块 - 侧栏自定义渲染:右侧(或
placement指定侧)执行情况面板中的 Tab 内容与标签
底层机制与 @blueking/chat-x 的 ChatContainer 一致,详见 chat-x Wiki「自定义侧栏内容 / Tab 标签」。
数据流
sequenceDiagram
participant Flow as FlowAgentContent
participant Container as ChatContainer
participant App as 业务层 onCustomTabChange
participant UI as 侧栏内容组件
Flow->>Container: addCustomTab({ name, label, data: { component, props, messageUid } })
Container->>App: onTabChange → onCustomTabChange(tab)
Note over Container: props.loading=true
App-->>Container: 详情数据
Note over Container: props.loading=false, props.data=结果
Container->>UI: getSideRenderComponent(h, props) ?? data.component选中自定义 Tab 后,内容区渲染优先级为:
getSideRenderComponent(h, selectedTab.data.props)返回的 VNode- 否则使用
addCustomTab时传入的data.component,并v-bind="data.props"
快速开始
1. 定义侧栏内容组件
内容组件需根据 loading / data 展示骨架与详情,并在标题区保留 locateButton 插槽(由容器注入「在对话中定位」按钮):
<!-- CustomTabContent.vue -->
<template>
<div>
<header>
<h3>{{ loading ? '加载中…' : titleText }}</h3>
<slot name="locateButton" />
</header>
<div v-if="loading" class="skeleton" />
<dl v-else>
<dt>node_name</dt>
<dd>{{ nodeName }}</dd>
<!-- 使用 props.data 渲染业务字段 -->
</dl>
</div>
</template>
<script setup lang="ts">
defineProps<{
loading?: boolean;
nodeId?: string;
nodeName?: string;
taskId?: number;
taskName?: string;
data?: Record<string, unknown>;
}>();
</script>2. 封装渲染函数(推荐 composable)
// use-side-render-handlers.ts
import { h } from 'vue';
import CustomTabContent from './CustomTabContent.vue';
import type { GetSideRenderComponent, GetSideTabRenderComponent } from '@blueking/ai-blueking';
export function useSideRenderHandlers() {
const getSideRenderComponent: GetSideRenderComponent = (createElement, props) => {
const raw = props ?? {};
return createElement(CustomTabContent, {
loading: Boolean(raw.loading),
nodeId: typeof raw.node_id === 'string' ? raw.node_id : '',
nodeName: typeof raw.node_name === 'string' ? raw.node_name : '',
taskId: typeof raw.task_id === 'number' ? raw.task_id : undefined,
taskName: typeof raw.task_name === 'string' ? raw.task_name : '',
data:
typeof raw.data === 'object' && raw.data !== null && !Array.isArray(raw.data)
? (raw.data as Record<string, unknown>)
: {},
});
};
const getSideTabRenderComponent: GetSideTabRenderComponent = (createElement, tab, { removeCustomTab }) => {
// 仅自定义 Flow 节点 Tab(name 形如 task_id|node_id|node_name)
if (!tab.name.includes('|')) return undefined;
const [, , nodeName = ''] = tab.name.split('|');
return createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: '6px' } }, [
createElement('span', {}, nodeName || tab.label),
createElement('button', {
type: 'button',
onClick: (e: Event) => {
e.stopPropagation();
removeCustomTab(tab.name);
},
}, '×'),
]);
};
return { getSideRenderComponent, getSideTabRenderComponent };
}Flow 协议侧 data.props 常用 snake_case(如 task_id、node_name),在 getSideRenderComponent 内映射为组件 camelCase props 即可。
3. 挂到 ChatBot / AIBlueking
<template>
<ChatBot
url="/api/ai"
:get-side-render-component="getSideRenderComponent"
:get-side-tab-render-component="getSideTabRenderComponent"
@execution-panel-change="onExecutionPanelChange"
/>
</template>
<script setup lang="ts">
import { ChatBot } from '@blueking/ai-blueking';
import '@blueking/ai-blueking/style.css';
import { useSideRenderHandlers } from './use-side-render-handlers';
const { getSideRenderComponent, getSideTabRenderComponent } = useSideRenderHandlers();
function onExecutionPanelChange(isCollapse: boolean) {
// 侧栏展开/收起时,若外层有 draggable 容器,需同步布局(见注意事项)
}
</script>AIBlueking 用法相同,将上述三个 Prop 传给内层 ChatBot 即可。
onCustomTabChange(可选)
类型:
import type { OnCustomTabChange } from '@blueking/ai-blueking';
const onCustomTabChange: OnCustomTabChange = async (tab) => {
const { task_id, node_id } = tab.data?.props ?? {};
if (task_id == null || !node_id) return {};
const detail = await fetchMyNodeDetail(task_id, node_id);
return detail; // 合并进 tab.data.props.data,并置 loading=false
};| 场景 | 行为 |
|---|---|
未传 onCustomTabChange | ChatBot 对含 task_id、node_id 的 Flow 节点 Tab 自动调用 chatHelper.message.getFlowAgentTaskNodeInfo |
| 已传 | 完全由业务方返回详情,内置拉取不再执行 |
切换 Tab 时容器会先将 props.loading 设为 true,再 await onCustomTabChange(tab),最后合并 { loading: false, data: 返回值 }。
类型导出
从 @blueking/ai-blueking 引入:
import type {
GetSideRenderComponent,
GetSideTabRenderComponent,
OnCustomTabChange,
} from '@blueking/ai-blueking';与 FlowAgent / addCustomTab 的关系
- 打开 Tab:仍由
FlowAgentContent等内部调用useCustomTabConsumer().addCustomTab(),业务方通常无需手动调用 - 覆盖展示:通过
getSideRenderComponent/getSideTabRenderComponent统一替换默认的BkFlowNodeDetail与默认标签样式 - 定位联动:
addCustomTab的data.messageUid须与活动消息message.uid一致;内容组件保留#locateButton插槽
直接使用 ChatContainer 时,Prop 名称与行为相同,见 ChatBot API。
版本要求
本能力自 v2.1.4-beta.7 起在 ChatBot / AIBlueking 上提供;更低版本请直接使用 @blueking/chat-x 的 ChatContainer 同名 Props。
注意事项
外层 draggable 与侧栏展开
若 ChatBot 包在可拖拽面板内,监听 execution-panel-change,在侧栏因 addCustomTab 自动展开时同步外层布局(与 isCollapse 联动)。
执行情况 Tab 清空自定义 Tab
当 executionGroups 为空且搜索关键词为空时,ChatContainer 会 resetCustomTab,清空自定义 Tab 并折叠侧栏。
props 命名与 remount
侧栏内容使用 :key="selectedTab.name",切换 Tab 会重建子树。data.props 建议与后端 Flow 字段对齐(snake_case),在 getSideRenderComponent 中再映射为组件 props。
参考示例
仓库内 Playground(packages/ai-blueking/playground)提供完整示例:
- 页面:
views/SideRenderView.vue(路由/examples/side-render) - 组件:
components/side-render/CustomTabContent.vue、FlowAgentSideRenderDemo.vue - 逻辑:
components/side-render/use-side-render-handlers.ts
本地调试:在 ai-blueking 包目录执行 pnpm dev:ai,访问侧栏渲染示例页。
Standalone 宿主注意
自 v2.1.4-beta.8 起,非 Vue 宿主通过 @blueking/ai-blueking/standalone 集成时,getSideRenderComponent 的 createElement 必须使用本入口导出的 h:
import { mountChatBot, h } from '@blueking/ai-blueking/standalone';
mountChatBot('#root', {
props: {
url: '/api/',
getSideRenderComponent: (createElement, props) => {
// createElement 与 standalone 的 h 同源;也可直接写 h(...)
return h(MySidePanel, props);
},
},
});勿从 npm 的 vue 包单独 import { h },否则 VNode 与组件内部 runtime 不一致会导致渲染异常。
相关文档
- Standalone 非 Vue 宿主集成
- 消息自定义渲染 — 主对话区
custom-component块 - ChatBot API — 侧栏自定义渲染
- AIBlueking API
- 类型定义 — GetSideRenderComponent 等