会中聊天功能允许会议内的成员通过文字、表情等形式进行实时互动。该功能基于
tuikit-atomicx-vue3 的 IM 组件能力实现,为用户提供便捷的即时通讯体验。适用场景
在线研讨会:主讲人进行视频演讲时,参会者可以通过文字提问或讨论。
远程协作:团队成员在沟通时,可发送文字、图片、文件等。
直播教学:学生可以在聊天区发送弹幕或问题,老师实时查看并解答,增强课堂互动性。
前提条件
在接入功能前,请确保满足以下条件:
环境依赖:项目已正确安装并引入
tuikit-atomicx-vue3。实现会中聊天功能
本功能提供两种集成方案,您可以根据业务需求选择最适合的一种:
方案一(推荐):使用 UI 组件快速集成。直接引入
tuikit-atomicx-vue3 提供的消息列表组件(MessageList)和消息输入组件(MessageInput),开发成本最低。方案二(高级):使用底层 API 自定义集成。基于 atomicx-core SDK API useMessageListState 和 useMessageInputState 状态钩子自行实现 UI 和交互逻辑,灵活度最高。

方案一:使用 UI 组件快速集成
tuikit-atomicx-vue3 提供了两个核心组件:MessageList: 消息列表组件,展示聊天记录,支持消息的复制、撤回、删除等操作。
MessageInput: 消息输入组件,支持文本、表情、图片、文件发送,并可根据禁言状态自动禁用。
步骤1:数据初始化
说明:
GROUP 为固定前缀,setActiveConversation 参数必须为 `GROUP${roomId}`,否则无法正常收发消息。GROUP 为 IM SDK 群组会话的固定前缀(参考 IM 会话类型)。房间聊天基于 IM 群组实现,因此会话 ID 必须为 `GROUP${roomId}` 格式。import { watch } from 'vue';import { useConversationListState } from 'tuikit-atomicx-vue3/chat';import { useRoomState } from 'tuikit-atomicx-vue3/room';const { currentRoom } = useRoomState();const { setActiveConversation } = useConversationListState();// 监听房间 ID 变化,自动切换到对应的 IM 群组会话watch(() => currentRoom.value?.roomId, (roomId) => {if (roomId) {// 关键步骤:设置当前活跃会话 ID,规则为 "GROUP" + 房间IDsetActiveConversation(`GROUP${roomId}`);}}, { immediate: true });
步骤2: 使用组件
注意:
<template><UIKitProvider theme="light" language="zh-CN"><div class="room-chat-container"><!-- 消息列表组件 --><!-- messageActionList: 配置长按消息支持的操作,例如复制、撤回、删除 --><MessageListclass="message-list":messageActionList="['copy', 'recall', 'delete']"/><!-- 消息输入组件 --><!-- disabled: 当用户被禁言时禁用输入框 --><!-- hideSendButton: 可选,隐藏发送按钮,使用回车发送 --><MessageInputclass="message-input":placeholder="inputPlaceholder":disabled="isMessageDisabled"hideSendButton/></div></UIKitProvider></template><script setup lang="ts">import { computed } from 'vue';import { UIKitProvider } from '@tencentcloud/uikit-base-component-vue3';import { MessageInput, MessageList } from 'tuikit-atomicx-vue3/chat';import { useRoomParticipantState } from 'tuikit-atomicx-vue3/room';const { localParticipant } = useRoomParticipantState();// 获取当前用户的禁言状态,isMessageDisabled 由房间后台实时同步,无需前端主动轮询,前端只需绑定该状态即可自动响应禁言变更。const isMessageDisabled = computed(() => localParticipant.value?.isMessageDisabled);// 根据状态动态显示占位符提示const inputPlaceholder = computed(() =>isMessageDisabled.value ? '您已被禁言' : '请输入消息...');</script><style scoped>.room-chat-container{display:flex;flex-direction:column;height:100%;padding:8px;gap:8px}.message-list{flex:1;overflow:hidden}.message-input{flex-shrink:0;border:1px solid #e0e0e0;border-radius:8px}</style>
步骤3:处理未读消息计数(可选)
import { ref, watch } from 'vue';import { useMessageListState } from 'tuikit-atomicx-vue3/chat';const { messageList } = useMessageListState();const unreadCount = ref(0);const isChatWindowOpen = ref(false); // 需根据您的业务逻辑维护此状态watch(() => messageList.value?.length, (newLength, oldLength) => {// 如果是首次加载或数据为空,不进行计数if (!newLength || newLength === 0) return;// 仅当聊天窗口关闭且收到新消息时,增加未读计数if (!isChatWindowOpen.value && newLength > (oldLength || 0)) {unreadCount.value += (newLength - (oldLength || 0));}});// 当用户打开聊天窗口时,清空未读计数const openChatWindow = () => {isChatWindowOpen.value = true;unreadCount.value = 0;};
方案二:使用底层 API 自定义集成
注意:
MessageInputState 默认支持与 @tiptap/vue-3 编辑器集成。如果您仅需要简单的文本输入框(例如 <input> 或 <textarea>),请忽略 setEditorInstance、setContent 等与编辑器实例绑定的 API,直接使用 updateRawValue 和 sendMessage 即可。步骤1:数据初始化
说明:
GROUP 为固定前缀,setActiveConversation 参数必须为 `GROUP${roomId}`,否则无法正常收发消息。在房间切换或退房时,建议重置或清空活跃会话,避免消息错发至错误会话。
import { watch } from 'vue';import { useConversationListState } from 'tuikit-atomicx-vue3/chat';import { useRoomState } from 'tuikit-atomicx-vue3/room';const { currentRoom } = useRoomState();const { setActiveConversation } = useConversationListState();// 监听房间 ID 变化,自动切换到对应的 IM 群组会话watch(() => currentRoom.value?.roomId, (roomId) => {if (roomId) {// 关键步骤:设置当前活跃会话 ID,规则为 "GROUP" + 房间IDsetActiveConversation(`GROUP${roomId}`);}}, { immediate: true });
步骤2:获取消息列表
import { useMessageListState } from 'tuikit-atomicx-vue3/chat';const {messageList, // 响应式消息列表loadMoreOlderMessage, // 加载更多历史消息方法hasMoreOlderMessage, // 是否还有更多历史消息} = useMessageListState();// 示例:渲染消息列表// <div v-for="msg in messageList" :key="msg.ID">// {{ msg.payload.text }}// </div>
步骤3:发送消息
import { useMessageInputState } from 'tuikit-atomicx-vue3/chat';const {inputRawValue, // 响应式数据:当前输入内容updateRawValue, // 方法:更新输入内容(会自动触发"对方正在输入"状态)sendMessage // 方法:发送消息} = useMessageInputState();const handleSend = async () => {// 校验非空if (!inputRawValue.value) return;try {// 1. 发送消息// 方式 A:发送当前 inputRawValue 的内容await sendMessage();// 方式 B:直接发送指定文本(不依赖 inputRawValue)// await sendMessage('Hello World');console.log('发送成功');// 2. 发送成功后清空输入框// 关键点:非富文本模式下,务必使用 updateRawValue('') 清空// 请勿使用 setContent(''),该方法仅在绑定了 editor 实例后生效updateRawValue('');} catch (error) {console.error('发送失败', error);}};// 示例:绑定原生输入框// <input// :value="inputRawValue"// @input="(e) => updateRawValue(e.target.value)"// @keyup.enter="handleSend"// />
步骤4:加载历史消息
注意:
在加载历史消息后,通常需要手动调整滚动条位置,以保持当前浏览视角不发生跳变。
import { useMessageListState } from 'tuikit-atomicx-vue3/chat';const {hasMoreOlderMessage,loadMoreOlderMessage} = useMessageListState();const loadHistory = async () => {if (hasMoreOlderMessage.value) {// 记录加载前的滚动高度// const previousHeight = scrollContainer.value.scrollHeight;await loadMoreOlderMessage();// DOM 更新后恢复滚动位置// nextTick(() => {// const currentHeight = scrollContainer.value.scrollHeight;// scrollContainer.value.scrollTop += (currentHeight - previousHeight);// });}};
步骤5:高级功能(已读回执)
const {setEnableReadReceipt, // 开启/关闭已读回执} = useMessageListState();// 开启已读回执setEnableReadReceipt(true);
开发注意事项
禁言状态同步
localParticipant.isMessageDisabled 是由房间管理员控制的。当管理员开启“全员禁言”或单独禁言某用户时,该属性会自动更新。请务必将此属性绑定到输入框的 disabled 状态上,以在前端层面限制用户发言。会话切换时机
确保
setActiveConversation 在进房成功后且 roomId 有值时调用。如果在进房前调用,可能会因为 SDK 尚未初始化完毕或未登录而失败。容器布局要求(重要)
消息列表组件内部处理了滚动逻辑,因此父容器必须有固定的高度(例如
height: 100% 或具体像素值)并设置 overflow: hidden,否则会导致列表无限拉长、无法滚动。消息类型处理
messageList 中包含多种类型的消息(文本、图片、文件等),自定义渲染时需根据 msg.type 进行区分处理。输入状态管理
使用
updateRawValue 更新输入内容会自动触发 "对方正在输入" 的状态通知(如果支持)。如果仅使用 inputRawValue.value = 'xxx' 可能不会触发该状态。历史消息滚动维持
在自定义开发中,调用
loadMoreOlderMessage 加载历史消息后,DOM 高度会增加。为了防止视图跳动,需要在加载前记录 scrollHeight,加载后(nextTick)计算高度差并调整 scrollTop。新消息滚动策略
建议实现“智能滚动”逻辑:当用户位于列表底部时,收到新消息自动滚动到底部;当用户正在浏览历史消息时,保持当前位置不动,并显示“有新消息”的提示气泡。
会话 ID 格式要求
调用
setActiveConversation 时,必须使用 `GROUP${roomId}` 格式(GROUP 为固定前缀)。这是 IM SDK 群组会话的标准格式,不可自定义或省略前缀,否则无法正常收发消息。常见问题
切到群组会话失败或消息列表无内容?
确保已登录并进房成功后再调用
setActiveConversation('GROUP' + roomId);未登录或 roomId 为空会导致失败。消息收不到或列表空白?
检查当前活跃会话是否正确(
GROUP 前缀 + roomId),以及网络/鉴权是否正常;必要时重新设置活跃会话 setActiveConversation 刷新。未读计数不清零?
需要手动维护未读计数逻辑。打开聊天窗口时,或收到新消息但窗口关闭时进行计数更新。
被禁言仍能点击发送?
请确保前端 UI 根据
isMessageDisabled 状态禁用了发送按钮和输入框。虽然服务端也会拦截,但前端禁用体验更好。自定义头像/昵称展示?
可通过
MessageList 插槽或自定义 Message 组件,优先显示房间内的 nameCard,并自定义头像样式等。自定义方案如何发送图片或文件?
sendMessage 方法支持传入 InputContent[] 数组。您可以构造包含 type: 'image' 或 type: 'file' 以及对应 file 对象的数组来实现富媒体消息发送。import { useMessageInputState } from 'tuikit-atomicx-vue3/chat';const { sendMessage } = useMessageInputState();// 示例:发送图片// const file = ...; // 从 input type="file" 获取的 File 对象await sendMessage([{ type: 'image', file }]);
如何修改 UI 组件的默认样式?
tuikit-atomicx-vue3 的组件类名通常保持稳定。您可以使用 CSS 深度选择器(例如 Vue 的 :deep())来覆盖默认样式。例如 .message-list :deep(.tui-message-bubble) { background-color: #f0f0f0; }。