概述
ChatHeader 聊天头部组件是聊天界面的顶部导航组件,用于显示当前会话的基本信息。能够展示会话标题、头像、在线状态、输入状态提示等信息。组件采用容器组件模式,提供了灵活的插槽机制用于自定义左右两侧的操作区域。
Props 速查表
字段 | 类型 | 默认值 | 描述 |
string | undefined | 自定义标题,未提供时使用会话信息。 | |
string | undefined | 自定义头像图片 URL。 | |
boolean | false | 是否显示用户状态。 | |
bool | false | 是否展示音视频通话按钮 |
Slot 速查表
名称 | 描述 |
ChatHeader 左侧自定义内容区域。 | |
ChatHeader 右侧自定义内容区域。 |

Props 详细介绍
title
类型:
string
描述:自定义会话标题,当不提供此属性时,组件会自动根据会话类型显示相应的标题(单聊显示用户昵称或备注,群聊显示群名称),默认值为
undefined
。avatarUrl
类型:
string
描述:自定义头像图片的 URL 地址,当不提供此属性时,组件会自动根据会话类型显示相应的头像(单聊显示用户头像,群聊显示群头像),默认值为
undefined
。enableUserStatus
类型:
boolean
描述:是否启用并显示用户在线状态功能,仅在单聊模式下生效,开启后会显示用户的在线/离线状态,默认值为
false
。enableCall
类型:
boolean
描述:是否启用并显示音视频通话按钮,开启后会显示音视频通话的按钮,默认值为
false
。注意:显示按钮并不意味着可以发起通话,发起音视频通话的前提是集成了 CallKit,集成 CallKit 可以参考集成 TUICallKit。
Slots 详细介绍
ChatHeaderLeft
描述:头部左侧自定义内容区域插槽,通常用于放置返回按钮、菜单按钮等导航控件。
示例: 自定义左侧导航区域
<template><ChatHeader><template #ChatHeaderLeft><!-- 返回按钮, 清除激活的会话 --><buttonclass="nav-button"@click="handleBack">⬅️</button></template></ChatHeader></template><script setup lang="ts">import { ChatHeader, useConversationListState } from '@tencentcloud/chat-uikit-vue3';const { setActiveConversation } = useConversationListState();const handleBack = () => {// 设置激活的会话ID为空setActiveConversation('');};</script><style scoped>.header-left-actions {display: flex;align-items: center;gap: 8px;}.nav-button {width: 32px;height: 32px;border: none;background: transparent;border-radius: 4px;cursor: pointer;display: flex;align-items: center;justify-content: center;transition: background-color 0.2s;}.nav-button:hover {background-color: rgba(0, 0, 0, 0.05);}</style>
ChatHeaderRight
描述:头部右侧自定义内容区域插槽,通常用于放置功能按钮、操作菜单等控件。
示例: 通过 ChatHeaderRight 插槽 和抽屉组件集成 ChatSetting 组件
<template><div class="chat-container"><Chat><ChatHeader><template #ChatHeaderRight><!-- 设置按钮,点击打开聊天设置面板 --><buttonclass="nav-button"@click="setIsSettingOpen(true)">⚙️</button></template></ChatHeader><MessageList /><MessageInput /></Chat><Drawer:open="isChatSettingOpen"@close="setIsSettingOpen(false)"><ChatSetting style="flex: 1;" /></Drawer></div></template><script setup lang="ts">import {Chat,ChatHeader,MessageList,MessageInput,ChatSetting} from '@tencentcloud/chat-uikit-vue3';import { Drawer } from './components/Drawer.vue';// 状态管理const isChatSettingOpen = ref(false);const setIsSettingOpen = (isOpen: boolean) => {isChatSettingOpen.value = isOpen;};</script><style scoped>.chat-container {display: flex;flex-direction: column;height: 100vh;}</style>
<script setup lang="ts">import { computed, nextTick, ref, watch } from 'vue';import type { PropType } from 'vue';const props = defineProps({open: { type: Boolean, required: true },placement: {type: String as PropType<'right' | 'left' | 'bottom'>,default: 'right',},duration: { type: Number, default: 300 },container: {type: [String, Object] as PropType<string | HTMLElement>,default: 'body',},zIndex: { type: Number, default: 1000 },maskClosable: { type: Boolean, default: true },});const emit = defineEmits<{(e: 'close'): void;}>();const isRendered = ref(false);const isVisible = ref(false);const isAnimating = ref(false);const containerTarget = computed(() => props.container ?? 'body');const cssVars = computed(() => ({'--duration': `${props.duration}ms`,'--z-index': String(props.zIndex),}));watch(() => props.open,async (nextOpen) => {if (nextOpen) {// open drawerif (!isRendered.value) {isRendered.value = true;}// wait for DOM renderawait nextTick();// force repaint, ensure initial state is appliedrequestAnimationFrame(() => {isVisible.value = true;isAnimating.value = true;});} else {// close drawerisVisible.value = false;isAnimating.value = true;}},{ immediate: true },);function onMaskClick() {if (props.maskClosable) {emit('close');}}function handlePanelTransitionEnd(e: TransitionEvent) {if (e.propertyName !== 'transform') {return;}isAnimating.value = false;if (!isVisible.value) {isRendered.value = false;}}function handleMaskTransitionEnd(e: TransitionEvent) {if (e.propertyName !== 'opacity') {// mask animation end}}</script><template><Teleport :to="containerTarget"><divv-if="isRendered":class="[$style.wrapper, isVisible ? $style['wrapper-open'] : '']":style="cssVars"><div:class="[$style.mask, isVisible ? $style['mask-open'] : '']"@click="onMaskClick"@transitionend="handleMaskTransitionEnd"/><div:class="[$style.panel,$style[`from-${props.placement}`],isVisible ? $style['panel-open'] : '']"@click.stop@transitionend="handlePanelTransitionEnd"><slot /></div></div></Teleport></template><style module lang="scss">.wrapper{position:fixed;inset:0;z-index:var(--z-index);pointer-events:none;}.wrapper-open{pointer-events:auto;}.mask{position:absolute;inset:0;background:rgba(0,0,0,0.45);opacity:0;transition:opacity var(--duration) ease;pointer-events:auto;}.mask-open{opacity:1;}.panel{position:absolute;background:var(--bg-color-operate);max-width:100%;max-height:100%;transition:transform var(--duration) ease;pointer-events:auto;box-shadow:0 8px 16px rgba(0,0,0,0.15);display:flex;flex-direction:row;}.from-right{top:0;right:0;height:100%;width:320px;transform:translateX(100%);}.from-left{top:0;left:0;height:100%;width:320px;transform:translateX(-100%);}.from-bottom{left:0;bottom:0;width:100%;height:40%;transform:translateY(100%);}.panel-open.from-right,.panel-open.from-left,.panel-open.from-bottom{transform:translate(0,0);}</style>
集成 TUICallKit
<script lang="ts" setup>import { Teleport } from 'vue';import { TUICallKit } from '@tencentcloud/call-uikit-vue';<script><template><UIKitProvider><Teleport to="body"><TUICallKitstyle="position: fixed;width: 600px;height: 400px;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 1000;"/></Teleport>// other code ...</UIKitProvider><template>