TUIKit 是基于腾讯云 Chat SDK 的一款 Vue 3 UI 组件库,它提供了一些通用的 UI 组件,包含会话、聊天、群组等功能。本文介绍如何快速集成 TUIKit 并实现核心功能。
说明:
您好!我们正在开发一款即时通讯产品 UIKit,它基于腾讯云 Chat SDK,是一款 Vue3 UI 组件库,能帮助您快速集成并实现核心功能。
您的反馈我们会高度重视,本问卷填写仅需要 30 秒!诚邀您的参与。

关键概念
chat-uikit-vue3 主要分为 ConversationList、Chat、MessageList、ChatHeader、MessageInput、ChatSetting、Search、Contact 等核心 UI 组件,每个 UI 组件负责展示不同的内容。
1. ConversationList 提供会话列表组件。
2. Chat 提供会话的容器组件。
3. MessageList 提供会话的消息列表组件。
4. ChatHeader 提供会话的头部信息组件。
5. MessageInput 提供输入框组件。
6. ChatSetting 提供单聊和群聊的管理组件。
7. Search 提供云端搜索组件。
8. Contact 提供联系人组件。
前提条件
Vue.js@^3.0.0
TypeScript@^5.0.0
Node.js(Node.js ≥ 20.0.0,建议使用目前的 LTS v22 版本)
创建项目
使用 Vite 创建一个新的名称为 chat-example 的 Vue3 项目。
说明:
高版本 Vite 需要使用最新的 Node.js 版本。
npm create vite@latest

下载并导入组件
创建项目完成后,切换到项目所在目录。
cd chat-examplenpm install
步骤1:安装依赖
npm i @tencentcloud/chat-uikit-vue3
步骤2:引入组件
注意:
以下代码中未填入
SDKAppID、userID 和 userSig,需在 步骤3 中获取相关信息后进行替换。为了尊重版权,下图所示的默认小黄脸表情包版权属于腾讯云,您可以通过升级至 IM 企业版套餐 免费使用该表情包。


2.1 搭建主界面
例如:在
src/App.vue 页面中写入以下代码:<template><UIKitProvider language="zh-CN" theme="light"><div class="chat-layout"><!-- 左侧导航 --><SideTab :active-tab="activeTab" @tab-change="handleTabChange" /><!-- 中间列表 --><div class="conversation-list-panel"><ConversationList v-show="activeTab === 'conversations'" /><ContactList v-show="activeTab === 'contacts'" /></div><!-- 右侧聊天 --><Chatv-show="activeTab === 'conversations'"class="chat-content-panel":PlaceholderEmpty="EmptyChatTpl"><ChatHeader><template #ChatHeaderRight><button class="icon-button" @click="isChatSettingShow = !isChatSettingShow"><IconMenu size="20" /></button></template></ChatHeader><MessageList /><MessageInput class="message-input-container"><template #headerToolbar><div class="message-toolbar"><div class="message-toolbar-actions"><EmojiPicker /><FilePicker /><VideoPicker /><ImagePicker /><!-- 参考文档开通音视频通话服务 --><!-- <AudioCallPicker /><VideoCallPicker /> --></div><button class="icon-button" @click="isSearchInChatShow = !isSearchInChatShow"><IconSearch size="20" /></button></div></template></MessageInput><!-- 聊天设置侧边栏 --><div v-show="isChatSettingShow" class="chat-sidebar" :class="{ dark: theme === 'dark' }"><div class="chat-sidebar-header"><span class="chat-sidebar-title">设置</span><button class="icon-button" @click="isChatSettingShow = false">✕</button></div><ChatSetting /></div><!-- 会话内搜索侧边栏 --><div v-show="isSearchInChatShow" class="chat-sidebar" :class="{ dark: theme === 'dark' }"><div class="chat-sidebar-header"><span class="chat-sidebar-title">群搜索</span><button class="icon-button" @click="isSearchInChatShow = false">✕</button></div><Search :variant="VariantType.EMBEDDED" /></div></Chat><!-- 联系人详情 --><ContactInfov-show="activeTab === 'contacts'"class="contact-detail-panel"@send-message="handleSendMessage"/></div></UIKitProvider></template><script setup lang="ts">import { ref, h, watch } from 'vue';import {Chat, Search, ChatHeader, MessageList, MessageInput, ContactList, ContactInfo, ChatSetting, UIKitProvider, ConversationList, useUIKit, useLoginState, useConversationListState, EmojiPicker, FilePicker, VideoPicker, ImagePicker, VariantType, AudioCallPicker, VideoCallPicker,} from '@tencentcloud/chat-uikit-vue3';import { IconMenu, IconSearch, TUIToast } from '@tencentcloud/uikit-base-component-vue3';import SideTab from './components/SideTab.vue';const { theme } = useUIKit();const { login } = useLoginState();const { setActiveConversation, activeConversation } = useConversationListState();const activeTab = ref<'conversations' | 'contacts'>('conversations');const isChatSettingShow = ref(false);const isSearchInChatShow = ref(false);// 切换会话时关闭会话内侧边栏watch(() => activeConversation.value?.conversationID, (newVal, oldVal) => {if (newVal !== oldVal) {isChatSettingShow.value = false;isSearchInChatShow.value = false;}});// 登录login({sdkAppId: 0, // SDKAppID, number 类型userId: '', // UserID, string 类型userSig: '', // userSig, string 类型}).then(() => {const userID = 'administrator';const conversationID = `C2C${userID}`;setActiveConversation(conversationID);}).catch((err) => {TUIToast.error({message: err.message,duration: 5000,});});const handleTabChange = (tab: 'conversations' | 'contacts') => {activeTab.value = tab;};const handleSendMessage = () => {activeTab.value = 'conversations';};// 空会话占位组件const EmptyChatTpl = h('div', { class: 'empty-placeholder' }, [h('div', { class: 'empty-icon' }, '💬'),h('div', { class: 'empty-title' }, '暂无会话'),h('div', { class: 'empty-subtitle' }, '选择一个会话开始聊天'),]);</script><style scoped>.chat-layout{width:calc(100vw - 64px);height:calc(100vh - 64px);max-width:1600px;max-height:900px;display:flex;border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,0.3),0 0 0 1px rgba(255,255,255,0.1);overflow:hidden;backdrop-filter:blur(10px);}.conversation-list-panel{width:300px;display:flex;flex-direction:column;border-right:1px solid var(--stroke-color-primary);overflow:hidden;}.chat-content-panel{flex:1;}.contact-detail-panel{flex:1;}.icon-button{padding:4px 6px;display:flex;align-items:center;justify-content:center;border:none;background:transparent;border-radius:4px;font-size:20px;color:var(--text-color-primary);cursor:pointer;transition:background-color 0.2s;outline:none;}.icon-button:focus{outline:none;}.icon-button:hover{background-color:var(--button-color-secondary-hover);}.icon-button:active{background-color:var(--button-color-secondary-active);}.message-toolbar{display:flex;justify-content:space-between;align-items:center;}.message-toolbar-actions{display:flex;align-items:center;gap:4px;}.chat-sidebar{position:absolute;right:0;top:0;bottom:0;min-width:300px;max-width:400px;display:flex;flex-direction:column;background-color:var(--bg-color-operate);box-shadow:var(--shadow-color) 0 0 10px;overflow:auto;z-index:1000;}.chat-sidebar.dark{box-shadow:-4px 0 16px rgba(0,0,0,0.4),-1px 0 0 rgba(255,255,255,0.1);}.chat-sidebar-header{position:sticky;top:0;display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background-color:var(--bg-color-operate);border-bottom:1px solid var(--stroke-color-primary);z-index:10;}.chat-sidebar-title{font-size:16px;font-weight:500;color:var(--text-color-primary);}.empty-placeholder{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;background-color:var(--bg-color-operate);border-left:1px solid rgba(0,0,0,0.08);color:#adb5bd;}.empty-icon{font-size:64px;opacity:0.3;}.empty-title{font-size:16px;font-weight:600;color:#6c757d;}.empty-subtitle{font-size:14px;color:#868e96;}img[alt="empty"]{display:none;}.message-input-container{border-top:1px solid var(--stroke-color-primary);}.call-kit{position:fixed;top:50%;left:50%;z-index:999;transform:translate(-50%,-50%);max-width:800px;max-height:600px;}</style><style>#app{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%) !important;width:100vw !important;height:100vh !important;text-align:start !important;max-width:none !important;display:flex !important;align-items:center !important;justify-content:center !important}</style>
2.2 引入侧边导航
在
src/component 文件夹中新建一个 SideTab.vue 的文件,并写入以下内容:<template><div class="side-tab" :class="{ dark: isDark }"><!-- 用户头像 --><div class="avatar-wrapper"><Avatar class="avatar" :src="loginUserInfo?.avatarUrl" /><div class="tooltip"><div class="tooltip-name">{{ loginUserInfo?.userName || '未命名' }}</div><div class="tooltip-id">ID: {{ loginUserInfo?.userId }}</div></div></div><!-- Tab 切换 --><div class="tabs"><divclass="tab-item":class="{ active: props.activeTab === 'conversations' }"@click="handleTabChange('conversations')"title="会话"><IconChatNew size="24" /></div><divclass="tab-item":class="{ active: props.activeTab === 'contacts' }"@click="handleTabChange('contacts')"title="联系人"><IconContacts size="24" /></div></div></div></template><script lang="ts" setup>import { computed } from 'vue';import { useLoginState, useUIKit, Avatar } from '@tencentcloud/chat-uikit-vue3';import { IconChatNew, IconContacts } from '@tencentcloud/uikit-base-component-vue3';const { theme } = useUIKit();const { loginUserInfo } = useLoginState();const isDark = computed(() => theme.value === 'dark' || theme.value === 'serious');interface Props {activeTab?: 'conversations' | 'contacts';}const props = withDefaults(defineProps<Props>(), {activeTab: 'conversations'});const emit = defineEmits<{tabChange: [tab: 'conversations' | 'contacts'];}>();const handleTabChange = (tab: 'conversations' | 'contacts') => {emit('tabChange', tab);};</script><style scoped>.side-tab{width:72px;height:100vh;background:var(--bg-color-function);display:flex;flex-direction:column;align-items:center;padding:20px 0;transition:background 0.3s;}.avatar-wrapper{position:relative;margin-bottom:24px;cursor:pointer;}.avatar-wrapper:hover:deep(.avatar){transform:scale(1.05);box-shadow:0 4px 12px rgba(0,0,0,0.15);}.tooltip{position:absolute;left:60px;top:50%;transform:translateY(-50%);padding:8px 12px;background:rgba(0,0,0,0.85);color:#fff;border-radius:6px;white-space:nowrap;opacity:0;visibility:hidden;pointer-events:none;transition:all 0.3s;z-index:1000;}.tooltip::before{content:'';position:absolute;left:-6px;top:50%;transform:translateY(-50%);border:6px solid transparent;border-right-color:rgba(0,0,0,0.85);}.avatar-wrapper:hover .tooltip{opacity:1;visibility:visible;}.tooltip-name{font-size:14px;font-weight:500;margin-bottom:4px;}.tooltip-id{font-size:12px;opacity:0.8;}.tabs{display:flex;flex-direction:column;gap:16px;}.tab-item{width:48px;height:48px;display:flex;align-items:center;justify-content:center;border-radius:12px;cursor:pointer;transition:all 0.3s;color:var(--text-color-primary);}.tab-item:hover{background:rgba(0,0,0,0.05);}.tab-item.active{background:var(--button-color-primary-default);color:var(--text-color-button);}.side-tab.dark{background:#1a1a1a;}.side-tab.dark .avatar-wrapper:hover:deep(.avatar){box-shadow:0 4px 12px rgba(255,255,255,0.2);}.side-tab.dark .tooltip{background:rgba(255,255,255,0.95);color:#1a1a1a;}.side-tab.dark .tooltip::before{border-right-color:rgba(255,255,255,0.95);}.side-tab.dark .tab-item:hover{background:rgba(255,255,255,0.1);}.side-tab.dark .tab-item.active{background:#1890ff;color:#fff;}</style>
步骤3:获取 SDKAppID、userID 和 userSig
在上一步的
src/App.vue文件中的 login 函数,填入登录信息 SDKAppID、userID 和 userSig。// 登录login({sdkAppId : , // SDKAppID, number 类型userId: '', // UserID, string 类型userSig: '', // userSig, string 类型});
参数 | 类型 | 说明 |
SDKAppID | Number | 说明: SDKAppID 是腾讯云 IM 客户应用的唯一标识。我们建议每一个独立的 App 都申请一个新的 SDKAppID。不同 SDKAppID 之间的消息是天然隔离的,不能互通。 |
userID | String | 用户的唯一标识符,由您定义,只能包含大小写字母(a-z,A-Z)、数字(0-9)、下划线和连字符。 |
userSig | String | 用户登录即时通信 IM 的密码,其本质是对 UserID 等信息加密后得到的密文。 说明: |
注意:
SDKAppID:在 即时通信 IM 控制台 > 应用管理 单击创建新应用,获取 SDKAppID。

userID:单击 即时通信 IM 控制台 > 消息服务 Chat > 账号管理,切换至目标应用所在账号,您可以创建 2~3 个账号用于体验单聊、群聊的功能。

userSig:单击 即时通信 IM 控制台 > 开发工具 > UserSig生成校验,切换至目标应用所在账号,填写创建的 UserID,即可生成 UserSig。

运行和测试
使用以下命令运行项目
npm run dev
注意:
1. 请确保 步骤3 代码中 SDKAppID、userID 和 userSig 均已成功替换,如未替换将会导致项目表现异常。
2. userID 和 userSig 为一一对应关系。
3. 如遇到项目启动失败,请检查 开发环境要求 是否满足。
集成更多高级特性
音视频通话
1. 安装
@tencentcloud/call-uikit-vue 依赖。npm install @tencentcloud/call-uikit-vue
2. 从
@tencentcloud/call-uikit-vue 导出 TUICallKit,并挂载到 DOM 节点上。在
src/App.vue 文件中继续补充下面的代码:// src/App.vue<template><UIKitProvider language="zh-CN" theme="light"><!-- 挂载音视频通话核心组件 --><TUICallKit class="call-kit" />...</UIKitProvider><template><script setup lang="ts">// 导入音视频通话核心组件import { TUICallKit } from '@tencentcloud/call-uikit-vue';</script>
3. 打开
<MessageInput /> 组件中 #headerToolbar 插槽内的注释。<MessageInput class="message-input-container"><template #headerToolbar><div class="message-toolbar"><div class="message-toolbar-actions"><EmojiPicker /><FilePicker /><VideoPicker /><ImagePicker /><AudioCallPicker /><VideoCallPicker /></div><button class="icon-button" @click="isSearchInChatShow = !isSearchInChatShow"><IconSearch size="20" /></button></div></template></MessageInput>
4. 拨打语音通话。

云端搜索
说明:
搜索,在客服、社交、在线教育、在线医疗、OA 等场景下是刚需功能,可帮助用户快速查找群组、用户、消息,提升产品使用体验和用户粘性。
由于 Web 平台本地存储特殊性等原因,Vue 无法实现本地搜索,为了更好的满足对于搜索能力的需求,推出了云端搜索能力。云端搜索功能支持全局搜索和会话内搜索,同时支持搜索群组、用户和消息。
云端搜索在“步骤 2”中已经默认集成,如果需要关闭云端搜索功能,请参考以下代码:
1. 设置
ConversationList 的 enableSearch 属性为 false<template><ConversationList v-show="activeTab === 'conversations'" :enable-search="false" /></template>
2. 注释会话内搜索侧边栏相关的代码
<template><Chat><!-- <div v-show="isSearchInChatShow" class="chat-sidebar" :class="{ dark: theme === 'dark' }"><div class="chat-sidebar-header"><span class="chat-sidebar-title">搜索</span><button class="icon-button" @click="isSearchInChatShow = false">✕</button></div><Search :variant="VariantType.EMBEDDED" /></div> --></Chat></template>
常见问题
什么是 UserSig?如何生成 UserSig?
UserSig 是用户登录即时通信 IM 的密码,其本质是对 UserID 等信息加密后得到的密文。UserSig 签发方式是将 UserSig 的计算代码集成到您的服务端,并提供面向项目的接口,在需要 UserSig 时由您的项目向业务服务器发起请求获取动态 UserSig。更多详情请参见 服务端生成 UserSig。
注意:
我能不能使用第三方组件库,例如 Element-Plus?
核心组件之间的粘连代码可以使用其他组件库,这一点在示例代码中也可以看到,例如您可以将
<ChatSetting /> 封装成全屏抽屉组件。但是核心组件内部已经存在的组件暂时不支持修改。const isChatSettingShow = ref(false);<el-drawerv-model="isChatSettingShow"title="设置"><ChatSetting /></el-drawer>