核心场景
一次完整的“主播连线”通常包含两个核心阶段,其整体流程如下:

实现步骤
步骤1:组件集成
步骤2:实现跨房连线
邀请方(主播 A)实现
1. 发起连线邀请
当主播A在界面上选择目标主播 B 并发起连线时,调用
requestHostConnection 方法。import { useCoHostState } from "@/uni_modules/tuikit-atomic-x/state/CoHostState"const liveID = 'xxx' // 主播 A 的房间 ID//通过 liveID 获取 CoHostState 的实例const { requestHostConnection } = useCoHostState(liveID)// 用户点击“连线”按钮,并选择了主播Bconst inviteHostB = (targetHostLiveId: string) => {requestHostConnection({liveID, // 主播 A 的房间 IDtargetHostLiveID: targetHostLiveID, // 主播 B 的房间 IDlayoutTemplate: 'HOST_DYNAMIC_GRID', // 选择一个布局模版timeout: 30, // 邀请超时时间success: () => {console.log("连线邀请已发送,等待对方处理...")},fail: (error) => {console.log("邀请发送失败", error)// 可以在这里进行弹窗提示}})}
2. 监听邀请结果
通过
addCoHostListener订阅对应的事件,您可以接收到主播 B 的处理结果。import { useCoHostState } from "@/uni_modules/tuikit-atomic-x/state/CoHostState"import { onMounted } from 'vue';const liveID = 'xxx' // 主播 A 的房间 ID//通过 liveID 获取 CoHostState 的实例const { addCoHostListener } = useCoHostState(liveID)onMounted(() => {addCoHostListener(liveID, 'onCoHostRequestAccepted', {callback: (event) => {const result = JSON.parse(event)console.log('主播 ${reult.invitee.userName} 同意了您的连线邀请')}})addCoHostListener(liveID, 'onCoHostRequestRejected', {callback: (event) => {const result = JSON.parse(event)console.log('主播 ${reult.invitee.userName} 拒绝了您的连线邀请')}})addCoHostListener(liveID, 'onCoHostRequestTimeout', {callback: () => {console.log('邀请超时,对方未回应')}})})
受邀方(主播 B)实现
1. 接收连线邀请
通过
addCoHostListener订阅 onCoHostRequestReceived 事件,主播B可以监听到来自主播 A 的邀请。import { useCoHostState } from "@/uni_modules/tuikit-atomic-x/state/CoHostState"import { onMounted } from 'vue';const liveID = 'xxx' // 主播 B 的房间 ID//通过 liveID 获取 CoHostState 的实例const { addCoHostListener } = useCoHostState(liveID)onMounted(() => {addCoHostListener(liveID, 'onCoHostRequestReceived', {callback: (event) => {const result = JSON.parse(event)console.log('收到主播 ${reult.inviter.userName} 的连线邀请')// 可以在这里进行弹窗提示}})})
2. 响应连线邀请
当主播 B 在弹出的对话框中做出选择后,调用相应的方法。
import { useCoHostState } from "@/uni_modules/tuikit-atomic-x/state/CoHostState"import { onMounted } from 'vue';const liveID = 'xxx' // 主播 B 的房间 ID//通过 liveID 获取 CoHostState 的实例const { acceptHostConnection, rejectHostConnection } = useCoHostState(liveID)const acceptInvitation = () => {acceptHostConnection({liveID,fromHostLiveId: 'xxx' // 从 onCoHostRequestReceived 事件中获取该数据})}const rejectInvitation = () => {rejectHostConnection({liveID,fromHostLiveId: 'xxx' // 从 onCoHostRequestReceived 事件中获取该数据})}
运行效果

完善 UI 细节
您可以通过修改源码的方式,在视频流画面上添加自定义视图,用于显示昵称、头像等信息,或在他们关闭摄像头时提供占位图,以优化视觉体验。
实现视频流画面的昵称显示
实现效果

实现方式
创建自定义 UI 覆盖层组件 (
ParticipantOverlay.vue)这个组件是我们的“UI贴纸”,它只负责根据数据渲染UI,不关心视频。
在您的
components 目录下,创建一个新文件 ParticipantOverlay.vue。将以下代码复制到文件中。
<template><view class="overlay-container"><!-- 遍历 seatList,为每个麦位成员创建独立的UI容器 --><viewv-for="participant in seatList":key="participant.userInfo.userID"class="participant-ui-container":style="getParticipantStyle(participant)"><!-- 条件渲染:根据摄像头状态显示不同UI --><!-- 1. 当摄像头关闭时,显示居中的头像和昵称 --><view v-if="participant.userInfo.cameraStatus === 'OFF'" class="avatar-placeholder"><image class="avatar-image" :src="participant.userInfo.userAvatar || '/static/default-avatar.png'" mode="aspectFill"></image><text class="avatar-name">{{ participant.userInfo.userName || participant.userInfo.userID }}</text></view><!-- 2. 当摄像头开启时,显示左下角的昵称条 --><view v-if="participant.userInfo.cameraStatus === 'ON'" class="nickname-bar"><text class="nickname-text">{{ participant.userInfo.userName || participant.userInfo.userID }}</text></view></view></view></template><script setup>import { computed } from 'vue';// 1. 接收来自父组件的核心数据:seatList 和 scaleconst props = defineProps({seatList: {type: Array,default: () => []},scale: {type: Object,default: () => ({ scaleX: 1, scaleY: 1 })}});// 2. 定义一个方法来计算每个成员UI容器的精确位置和大小const getParticipantStyle = (participant) => {if (!participant || !participant.region) return {};return {position: 'absolute',left: `${participant.region.x * props.scale.scaleX}px`,top: `${participant.region.y * props.scale.scaleY}px`,width: `${participant.region.w * props.scale.scaleX}px`,height: `${participant.region.h * props.scale.scaleY}px`,};};</script><style scoped>.overlay-container {pointer-events: none;}.participant-ui-container {overflow: hidden;}.avatar-placeholder {display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: #2E323A;}.avatar-image {width: 160rpx;height: 160rpx;border-radius: 80rpx;}.avatar-name {margin-top: 20rpx;font-size: 28rpx;color: #FFFFFF;}.nickname-bar {position: absolute;left: 10rpx;bottom: 10rpx;background-color: rgba(0, 0, 0, 0.5);padding: 4rpx 16rpx;border-radius: 20rpx;}.nickname-text {color: #FFFFFF;font-size: 24rpx;}</style>
步骤 2:在直播间页面中组合所有组件
这是将视频层和
UI 层“叠”在一起的关键步骤,同时需要解决 nvue 的布局问题。打开您的直播间页面文件
按照以下结构和样式进行修改。
<template><view class="page-container"><view class="live-container"><!-- 底层:视频渲染层 --><LiveCoreView:liveID="xxx" // 对应直播间的 liveID:viewType="xxx" // 主播端: PUSH_VIEW, 观众端: PLAY_VIEWclass="video-layer"/><!-- 上层:自定义UI覆盖层 --><ParticipantOverlay:seatList="seatList":scale="scale"class="ui-layer"/></view><!-- 页面的其他UI,如底部的操作栏 --><!-- <view class="bottom-controls">...</view> --></view></template><script setup>import { ref, onMounted } from 'vue';import ParticipantOverlay from '@/components/ParticipantOverlay.vue';import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';const liveID = 'xxx' // 当前直播间的 liveIDconst { seatList } = useLiveSeatState(liveID);const scale = ref({ scaleX: 1, scaleY: 1 });onMounted(() => {// 在此获取并设置正确的 scale 值。// 这是保证UI对齐的关键。请根据您的项目实际逻辑实现。// 示例:const { windowWidth } = uni.getSystemInfoSync();const designWidth = 750; // rpx 设计稿宽度scale.value.scaleX = windowWidth / designWidth;scale.value.scaleY = scale.value.scaleX;});</script><style scoped>.page-container {flex: 1;background-color: #000;}.live-container {flex: 1;position: relative;}.video-layer,.ui-layer {position: absolute;top: 0;left: 0;right: 0;bottom: 0;}.video-layer {z-index: 1;}.ui-layer {z-index: 2;}</style>
API 文档
常见问题
为什么发起了连线邀请,对方却没收到?
请检查
targetHostLiveID 是否正确,并且对方直播间处于正常开播状态。检查网络连接是否通畅,邀请信令有30秒的默认超时时间。