跳转至

带看tim版SDK指导文档

本文档为 SDK 全量 API 与事件说明,面向接入方开发工程师。


目录

  1. 概述
  2. 安装与引入
  3. 快速开始
  4. 全量 API 参考
  5. 初始化配置
  6. 会议管理
  7. 带看会话
  8. 音频控制
  9. 消息与动作同步
  10. 成员管理
  11. 事件监听
  12. 销毁与清理
  13. 全量事件说明
  14. 类型定义全览
  15. 对接完整流程
  16. 多平台对接说明
  17. 后端接口说明
  18. 常见问题

1. 概述

vr-takelook-plugin 是一个面向 VR 全景带看场景的前端 SDK,提供以下核心能力:

  • IM 即时通信:基于腾讯云 TIM,实现成员管理、消息同步、自定义动作传输
  • RTC 实时音频:基于腾讯云 TRTC,实现多人语音通话
  • VR 同步浏览:通过自定义消息通道,实现多端 VR 画面实时同步
  • 多端桥接:自动适配 Web 浏览器、Android WebView、iOS WKWebView

SDK 采用 Facade 模式,对外通过 NestTakelook3(即 GuidedTourFacade 的别名)暴露统一的静态 API,调用方无需关心内部 TIM/TRTC 的初始化细节。


2. 引入

sdk文件地址

2.1 ES Module 引入(推荐)

vr-takelook-plugin位于demo项目的/src/services/takelook3/lib目录中

import { NestTakelook3 } from './vr-takelook-plugin';
// 或使用原始类名
import { GuidedTourFacade } from './vr-takelook-plugin';

2.2 UMD 引入(script 标签)

<script src="path/to/vr-takelook-plugin.umd.cjs"></script>
<script>
  const { NestTakelook3 } = window.VrTakelookPlugin;
</script>

2.4 TypeScript 类型导入

import type {
  GuidedTourConfig,
  StartTourOptions,
  GuidedTourStatus,
  GuidedTourMember,
  GuidedTourNetworkStatus,
  GuidedTourCustomAction,
  GuidedTourRoleConfig,
  GuidedTourBootstrapResult,
  GuidedTourBackendEndpoint,
} from 'vr-takelook-plugin';

3. 快速开始

import { NestTakelook3 } from 'vr-takelook-plugin';

// 第一步:设置服务参数
await NestTakelook3.setSDKParams('your-service-token', 'https://your-api.com');

`注意:your-service-token需要你调用开放平台接口获取,具体实现在带看3集成文档中有讲到`

// 第二步:拉取平台配置
await NestTakelook3.getConfig();

// 第三步:注册事件
NestTakelook3.onSdkReady(() => console.log('IM 就绪'));
NestTakelook3.onUserConnected((info) => console.log('已入会:', info));
NestTakelook3.onMemberJoin((member, members) => console.log('成员加入:', member));
NestTakelook3.onMemberExit((member, members) => console.log('成员退出:', member));
NestTakelook3.onEndTour((isMe, reason) => console.log('带看结束', isMe, reason));
NestTakelook3.onRemoteControllingNotify((action) => vrViewer.applyRemoteState(action)); // 同步远端VR相关行为

NestTakelook3.onSyncRemoteCustomActionToLocal((action) => {
  console.log('根据发送的参数,同步行为')
})

NestTakelook3.onNetworkStatusChanged((status) => console.log('网络变化:', status));

// 第四步:启动带看
await NestTakelook3.startTour({
  userId: 'user_001',
  callerUserId: 'agent_001',
  meetingId: '12345',
  roomId: '12345',
  imPlatform: 'web',
});

// 第五步:同步 VR 状态
NestTakelook3.syncLocalActionToRemote({ viewAngle: { x: 0, y: 90 }, sceneId: 'room-1' });

// 第六步:结束带看
await NestTakelook3.endTour('用户主动退出');

4. 全量 API 参考

所有 API 均通过 NestTakelook3 静态调用,GuidedTourFacade 是其类名别名,两者完全等价。


4.1 初始化配置

setSDKParams(serviceId, baseUrl)

设置 SDK 全局服务参数。必须在所有其他 API 调用之前执行。

await NestTakelook3.setSDKParams(serviceId: string, baseUrl: string): Promise<void>
参数 类型 必填 说明
serviceId string 众趣带看服务 Token,同时作为所有 HTTP 请求的 Authorization
baseUrl string 后端 API 基础 URL,例如 https://api.example.com

getConfig()

从后端拉取平台配置,返回包含 sdkAppId(TIM/TRTC 必需)和角色权限配置的对象。
建议在 setSDKParams 之后、startTour 之前调用。

const config = await NestTakelook3.getConfig(): Promise<unknown>

initAppRemoteSyncer()

手动初始化 App 原生桥接适配器(Android/iOS JSBridge)。
通常无需手动调用,startTour 内部会按需自动初始化。

NestTakelook3.initAppRemoteSyncer(): void

4.2 会议管理

createMeetingRoom(packageId)

向后端申请创建带看会议室,返回后端分配的会议信息。

const result = await NestTakelook3.createMeetingRoom(packageId: string): Promise<unknown>
参数 类型 必填 说明
packageId string 房源/项目 ID

callOut(userId, packageId)

发起带看呼叫。SDK 通知后端创建会议并触发对端推送,返回包含 meetingIdroomId 的对象。

const result = await NestTakelook3.callOut(userId: string, packageId: string): Promise<unknown>
参数 类型 必填 说明
userId string 发起呼叫的用户 ID
packageId string 房源/项目 ID

getUserSig(userId, packageId, noCheck?, roomId?)

从后端获取用户签名(userSig),用于 TIM/TRTC 鉴权。
startTour 在未提供 userSig 时会自动调用此方法,通常不需要手动调用。

const result = await NestTakelook3.getUserSig(
  userId: string,
  packageId: string,
  noCheck?: boolean,
  roomId?: string | number
): Promise<unknown>
参数 类型 必填 说明
userId string 用户 ID
packageId string 房源 ID
noCheck boolean 是否跳过后端用量校验
roomId string \| number 指定房间 ID

leaveConference(userId, roomId, noResponse?)

通知后端用户已离开会议。通常由 endTour 内部自动调用。

const result = await NestTakelook3.leaveConference(
  userId: string,
  roomId: string | number,
  noResponse?: boolean
): Promise<unknown>
参数 类型 必填 说明
userId string 用户 ID
roomId string \| number 房间 ID
noResponse boolean true 表示无人接通场景

getMeetingId()

获取当前会话的会议 ID。

const meetingId = NestTakelook3.getMeetingId(): string | number | undefined

getUserInfo(userId)

查询指定用户的信息。

const userInfo = await NestTakelook3.getUserInfo(userId: string): Promise<unknown>

4.3 带看会话

startTour(options, initCallback?)

最核心的方法。 启动完整的带看流程:初始化 IM → 登录 TIM → 加入群组 → 建立 RTC 连接。

await NestTakelook3.startTour(
  options: StartTourOptions,
  initCallback?: () => void
): Promise<void>

StartTourOptions 完整参数说明:

参数 类型 必填 说明
userId string 当前用户 ID
callerUserId string 带看发起者用户 ID(经纪人端即为自身,客户端填写经纪人 ID)
imPlatform 'app' \| 'web' \| 'mp' \| string 运行平台,决定 RTC 处理方式(详见下方说明)
meetingId string \| number 否* 会议 ID
roomId string \| number 否* TRTC 房间 ID
groupId string \| number TIM 群组 ID,默认与 roomId 相同
serviceId string 服务 Token,不填则使用 setSDKParams 中的值
packageId string 条件必填 房源 ID。未提供 userSig 时必填,SDK 将自动请求签名
userSig string 用户签名。若提供则跳过后端签名请求
sdkAppId string \| number TIM/TRTC AppId。若提供则优先使用此值
noAudioCall boolean true 则禁用语音通话(纯 VR 同步模式)
isInMPWeb boolean 是否运行在小程序内嵌 WebView 中
isWeChatMiniProgram boolean 是否为微信小程序环境
pattern string 呼叫模式,如 'callout'
ext string 自定义扩展参数,原样传递给后端
paintContainer string 画板容器 DOM 选择器(预留能力)
kfApiURL string 备选后端 API URL

* 当未提供 userSig 时,SDK 会调用 getUserSig 自动获取,此时 packageId 为必填项。
若已有完整的鉴权信息(userSigsdkAppIdroomId),可全部通过 options 传入,跳过后端请求。

imPlatform 对 RTC 行为的影响:

imPlatform RTC 行为
'web' SDK 内部管理 TRTC Web SDK,在浏览器中直接处理音频采集/播放
'app' SDK 通过 JSBridge 通知原生壳启动 TRTC,Web 层仅负责 IM 消息
'mp' 通过小程序桥接启动 RTC(预留能力)

initCallback 参数: 在 SDK 内部完成基础初始化后、真正连接 IM 之前回调,可用于更新 UI 进度。


endTour(reason?, shouldNotify?)

结束带看会话。依次执行:通知后端离会 → 销毁 RTC → 销毁 IM → 清理桥接。

await NestTakelook3.endTour(
  reason?: unknown,       // 结束原因(任意类型,会透传给 onEndTour 回调)
  shouldNotify?: boolean  // 是否触发 onEndTour 事件,默认 true
): Promise<void>

getGuidedTourStatus()

获取当前带看状态。

const status = NestTakelook3.getGuidedTourStatus(): GuidedTourStatus
枚举值 数值 说明
GuidedTourStatus.Idle 0 空闲,未开始带看
GuidedTourStatus.Starting 1 正在初始化(startTour 调用后、连接成功前)
GuidedTourStatus.Connected 2 已连接,带看进行中
GuidedTourStatus.Ended 3 已结束

4.4 音频控制

voiceForbidden(userId, status)

控制指定用户的静音状态,并通过 IM 消息同步静音指令到远端。
仅在 App 模式下(imPlatform: 'app')有效,Web 模式下需自行处理本地麦克风。

NestTakelook3.voiceForbidden(
  userId: string,
  status: 0 | 1  // 0 = 取消静音, 1 = 静音
): void

setAudioRoute(route)

设置本地音频输出通道,控制声音从听筒还是扬声器播出。
仅在 App 模式下有效,iOS 原生壳需实现对应处理逻辑。

NestTakelook3.setAudioRoute(
  route: 0 | 1  // 0 = 听筒, 1 = 扬声器
): void

4.5 消息与动作同步

syncLocalActionToRemote(vrState)

将本地 VR 浏览状态(视角、当前场景等)通过 IM 群消息广播给所有远端参与者。
通常在 VR 渲染器的视角变化回调中持续调用。

NestTakelook3.syncLocalActionToRemote(
  vrState: Record<string, unknown> | undefined
): Record<string, unknown> | undefined

返回值为处理后实际发送的状态对象(或 undefined,如同步被禁用时)。


syncLocalCustomActionToRemote(action)

发送自定义业务动作到远端所有参与者(通过 IM 群消息广播)。
可用于实现任意业务联动逻辑,如切换房源、更换楼层等。

await NestTakelook3.syncLocalCustomActionToRemote(
  action: GuidedTourCustomAction
): Promise<void>
// 示例
await NestTakelook3.syncLocalCustomActionToRemote({
  action: 'business/changeHouse',
  payload: { houseId: 'house-002', floorId: 3 },
});

enableSyncLocalActionToRemote(enabled, isCaller?)

开启或关闭 VR 状态的自动同步功能。
建议在 startTour 完成(onUserConnected 触发)之前关闭,连接成功后再开启,避免在建连过程中产生无效消息。

NestTakelook3.enableSyncLocalActionToRemote(
  enabled: boolean,
  isCaller?: boolean  // 预留参数,当前版本未使用
): void

sendCmd(targetImUserId, msg)

向指定用户发送点对点自定义消息(TIM C2C 消息)。与 syncLocalCustomActionToRemote 的群发不同,此方法为单播。

await NestTakelook3.sendCmd(
  targetImUserId: string,  // 目标用户的 TIM userId
  msg: unknown             // 消息内容(任意可序列化对象)
): Promise<void>

receivedCmd(message)

将外部收到的消息注入 SDK 进行解析和分发。
适用于宿主应用有自己独立消息通道(非 SDK 管理的 TIM)的场景,可借助此方法将外部消息路由到 SDK 内部事件系统。

NestTakelook3.receivedCmd(message: unknown): void

4.6 成员管理

kickOutById(userId)

将指定用户踢出当前带看会话(发送 moderation/kickOut 指令)。

NestTakelook3.kickOutById(userId: string): void

getGroupMemberList(options)

查询当前 TIM 群组的成员列表,支持分页。

const result = await NestTakelook3.getGroupMemberList({
  groupID: string,   // 群组 ID
  count?: number,    // 每页数量
  offset?: number    // 分页偏移
}): Promise<unknown>

4.7 事件监听

所有 on* 方法均为注册事件监听,支持多次调用注册多个监听器(同一事件的多个回调均会触发)。 例外:onEndTour 每次注册前会清除之前注册的监听器,仅保留最新一个。

onSdkReady(cb)

TIM SDK 初始化完成并就绪时触发。表明 IM 连接已建立,可以进行消息收发。

NestTakelook3.onSdkReady(cb: () => void): void

onSdkNotReady(cb)

TIM SDK 变为未就绪状态时触发(如断线、登出)。收到此事件后应停止消息发送操作。

NestTakelook3.onSdkNotReady(cb: () => void): void

onUserConnected(cb)

当前用户加入 TIM 群组成功时触发,回调中携带本用户的基本信息。

NestTakelook3.onUserConnected(cb: (userInfo: {
  userID: string;
  nickName: string;
  avatar: string | undefined;
}) => void): void

onMemberJoin(cb)

有其他成员加入带看会话时触发。

NestTakelook3.onMemberJoin(cb: (
  member: GuidedTourMember,    // 新加入的成员信息
  members?: GuidedTourMember[] // 当前全量成员列表(包含新加入者)
) => void): void

onMemberExit(cb)

有成员退出带看会话时触发。

NestTakelook3.onMemberExit(cb: (
  member: GuidedTourMember,    // 退出的成员信息
  members?: GuidedTourMember[] // 当前全量成员列表(已移除退出者)
) => void): void

onEndTour(cb)

带看会话结束时触发。每次调用 onEndTour 会替换之前注册的回调,只保留最新注册的监听器。

NestTakelook3.onEndTour(cb: (
  isMe: boolean,    // true = 本端主动调用 endTour 触发;false = 被动结束(对方结束/被踢/断线)
  reason?: unknown  // 结束原因(由调用 endTour 的一方传入)
) => void): void

onVoiceForbidden(cb)

收到静音/取消静音指令时触发(来源于 voiceForbidden 发出的 IM 指令消息)。

NestTakelook3.onVoiceForbidden(cb: (
  userId: string,  // 被操作的用户 ID
  status: boolean  // true = 被静音;false = 取消静音
) => void): void

onKickOut(cb)

当前用户被踢出带看会话时触发(来源于另一端调用了 kickOutById)。

NestTakelook3.onKickOut(cb: () => void): void

onNetworkStatusChanged(cb)

网络质量或连接状态发生变化时触发。可用于展示网络信号指示器或弱网提示。

NestTakelook3.onNetworkStatusChanged(cb: (
  status?: GuidedTourNetworkStatus
) => void): void

GuidedTourNetworkStatus 结构:

字段 类型 说明
level 'good' \| 'normal' \| 'poor' \| 'offline' 网络质量等级
code string \| number 网络状态码
message string 状态描述

onSyncRemoteCustomActionToLocal(cb)

收到远端通过 syncLocalCustomActionToRemote 发送的自定义业务动作时触发。

NestTakelook3.onSyncRemoteCustomActionToLocal(cb: (
  action: GuidedTourCustomAction, // 动作内容(含 action 名和 payload)
  from?: string                   // 发送方的用户 ID
) => void): void

onRemoteControllingNotify(cb)

收到远端的 VR 浏览状态同步数据时触发(来源于远端调用 syncLocalActionToRemote)。
接收方应在此回调中将 action 数据应用到本地 VR 渲染器。

NestTakelook3.onRemoteControllingNotify(cb: (
  action?: unknown  // 远端发送的 VR 状态数据(与 syncLocalActionToRemote 传入的 vrState 一致)
) => void): void

onMessageToUI(cb)

SDK 内部需要向 UI 层传递通知、警告或错误信息时触发。可用于显示 Toast 提示等。

NestTakelook3.onMessageToUI(cb: (
  message: unknown  // 消息内容(通常为字符串或结构化对象)
) => void): void

4.8 销毁与清理

destroy()

完整销毁 SDK 实例,释放所有资源(IM、RTC、桥接),清除所有事件监听。
destroy 内部会自动调用 endTour,无需提前手动结束带看。

await NestTakelook3.destroy(): Promise<void>

5. 全量事件说明

以下是 SDK 事件总线(GuidedTourSignals)中定义的所有事件及其完整回调签名。
标注 ⚠️ 的事件目前在 Facade 上没有对应的 on* 方法,为内部事件或预留事件。

事件名 Facade 方法 回调签名 触发时机
onSdkReady onSdkReady () => void TIM SDK 初始化完成
onSdkNotReady onSdkNotReady () => void TIM SDK 断线/未就绪
onConnected ⚠️ 无 () => void 内部连接状态变为已连接(内部使用)
onEndTour onEndTour (isMe: boolean, reason?: unknown) => void 带看会话结束
onMemberJoin onMemberJoin (member: GuidedTourMember, members?: GuidedTourMember[]) => void 有成员加入
onMemberExit onMemberExit (member: GuidedTourMember, members?: GuidedTourMember[]) => void 有成员退出
onUserConnected onUserConnected (userInfo: { userID, nickName, avatar }) => void 当前用户加入群组成功
onVoiceForbidden onVoiceForbidden (userId: string, status: boolean) => void 收到静音指令
onKickOut onKickOut () => void 当前用户被踢出
onReceiveCard ⚠️ 无 (userId: string) => void 收到名片(预留功能)
onRequestPhone ⚠️ 无 (userId: string, receiverId: string) => void 收到电话请求(预留功能)
onResponsePhone ⚠️ 无 (userId: string, isApproved: boolean, number?: string) => void 电话请求响应(预留功能)
onNetworkStatusChanged onNetworkStatusChanged (status?: GuidedTourNetworkStatus) => void 网络状态变化
onSyncRemoteCustomActionToLocal onSyncRemoteCustomActionToLocal (message: GuidedTourCustomAction, from?: string) => void 收到远端自定义动作
onRemoteControllingNotify onRemoteControllingNotify (action?: unknown) => void 收到远端 VR 状态同步
onChangeHouse ⚠️ 无 (houseId: string) => void 收到切换房源指令(预留功能)
onMessageToUI onMessageToUI (message: unknown) => void SDK 向 UI 传递通知
onGuidePaintVisible ⚠️ 无 (visible: boolean) => void 画板显隐状态变化(预留功能)

6. 类型定义全览

以下所有类型均可从 vr-takelook-plugin 导入。

GuidedTourStatus(枚举)

enum GuidedTourStatus {
  Idle = 0,       // 空闲,未开始
  Starting = 1,   // 正在初始化
  Connected = 2,  // 已连接,带看中
  Ended = 3,      // 已结束
}

GuidedTourConfig(接口)

SDK 全局配置结构。

interface GuidedTourConfig {
  serviceId?: string;                                           // 服务 Token
  baseUrl?: string;                                            // 后端基础 URL
  sdkAppId?: string | number;                                  // TIM/TRTC AppId
  endpoints?: Partial<Record<GuidedTourBackendEndpoint, string>>; // 自定义接口路径
  requestHeaders?: Record<string, string>;                     // 自定义请求头
  imPlatform?: 'app' | 'web' | 'mp' | string;                // 运行平台
}

StartTourOptions(接口)

startTour 方法的参数类型。

interface StartTourOptions {
  userId: string;                        // 当前用户 ID(必填)
  callerUserId: string;                  // 带看发起者 ID(必填)
  imPlatform: 'app' | 'web' | 'mp' | string; // 运行平台(必填)
  serviceId?: string;                    // 服务 Token
  packageId?: string;                    // 房源 ID
  userSig?: string;                      // 用户签名
  sdkAppId?: string | number;           // TIM/TRTC AppId
  meetingId?: string | number;          // 会议 ID
  roomId?: string | number;             // TRTC 房间 ID
  groupId?: string | number;            // TIM 群组 ID(默认与 roomId 相同)
  noAudioCall?: boolean;                 // 是否禁用语音
  isInMPWeb?: boolean;                   // 是否在小程序 WebView 中
  isWeChatMiniProgram?: boolean;         // 是否为微信小程序环境
  pattern?: string;                      // 呼叫模式
  ext?: string;                          // 自定义扩展参数
  paintContainer?: string;               // 画板容器选择器(预留)
  kfApiURL?: string;                     // 备选后端 URL
}

GuidedTourMember(接口)

带看会话成员信息。

interface GuidedTourMember {
  userId: string;                    // 用户 ID
  imUserId?: string;                 // TIM 用户 ID
  role?: string;                     // 角色(如 agent/customer)
  ext?: Record<string, unknown>;     // 扩展信息
}

GuidedTourNetworkStatus(接口)

网络状态信息。

interface GuidedTourNetworkStatus {
  level?: 'good' | 'normal' | 'poor' | 'offline'; // 质量等级
  code?: string | number;                          // 状态码
  message?: string;                                // 描述信息
}

GuidedTourCustomAction(接口)

自定义业务动作结构。

interface GuidedTourCustomAction {
  action: string;                       // 动作标识符(如 'business/switchScene')
  payload?: Record<string, unknown>;    // 动作携带的数据
}

GuidedTourBootstrapResult(接口)

SDK 启动结果,包含完整的连接参数。

interface GuidedTourBootstrapResult {
  sdkAppId: number;                         // TIM/TRTC AppId
  userSig: string;                          // 用户签名
  meetingId: string;                        // 会议 ID
  roomId: string | number;                  // 房间 ID
  groupId: string | number;                 // TIM 群组 ID
  userId: string;                           // 用户 ID
  targetId?: string;                        // 目标用户 ID
  conversationType?: 'C2C' | 'GROUP';       // 会话类型
  roleConfig?: GuidedTourRoleConfig;        // 角色配置
  raw?: unknown;                            // 原始后端响应
}

GuidedTourRoleConfig(接口)

角色权限配置。

interface GuidedTourRoleConfig {
  [key: string]: unknown;
}

GuidedTourBackendEndpoint(类型)

后端接口端点标识符。

type GuidedTourBackendEndpoint =
  | 'getConfig'          // 获取平台配置
  | 'createMeetingRoom'  // 创建会议室
  | 'callOut'            // 发起呼叫
  | 'getUserSig'         // 获取用户签名
  | 'sendHeartBeat'      // 心跳保活
  | 'leaveConference'    // 离开会议
  | 'queryCustomAgentList' // 查询经纪人列表
  | 'getUserInfo';       // 获取用户信息

7. 对接完整流程

7.1 调用顺序时序图

发起方(经纪人端)                    SDK                           后端                   接收方(客户端)
       │                             │                              │                          │
       │  1. setSDKParams()          │                              │                          │
       │ ─────────────────────────>  │                              │                          │
       │  2. getConfig()             │                              │                          │
       │ ─────────────────────────>  │  ── GET /config/ ──────────> │                          │
       │                             │  <── { sdkAppId, ... } ───── │                          │
       │  3. 注册所有事件监听          │                              │                          │
       │ ─────────────────────────>  │                              │                          │
       │  4. callOut(userId, pkgId)  │                              │                          │
       │ ─────────────────────────>  │  ── POST /start/ ──────────> │                          │
       │                             │  <── { meetingId, roomId } ── │  ── 推送通知 ──────────> │
       │                             │                              │                          │
       │  5. startTour(options)      │                              │                          │
       │ ─────────────────────────>  │  ── POST /usersig/ ────────> │                          │
       │                             │  <── { userSig } ─────────── │                          │
       │                             │  [TIM login + joinGroup]     │                          │
       │                             │  [TRTC enterRoom]            │                          │
       │  <── onSdkReady ────────── │                              │                          │
       │  <── onUserConnected ───── │                              │                          │
       │                             │                              │   startTour(options)     │
       │                             │                              │  <───────────────────────│
       │  <── onMemberJoin ──────── │ ◄───────────────── TIM 群消息  │                          │
       │                             │                              │                          │
       │  6. syncLocalActionToRemote │                              │                          │
       │ ─────────────────────────>  │  ── TIM 群消息 ───────────────────────────────────────> │
       │                             │                              │                          │
       │  <── onRemoteControlling ─ │ ◄───────────────── TIM 群消息 │                          │
       │                             │                              │                          │
       │  7. sendHeartBeat()(每30s)│  ── POST /heartbeat/ ──────> │                          │
       │                             │                              │                          │
       │  8. endTour()               │                              │                          │
       │ ─────────────────────────>  │  ── POST /user_left/ ──────> │                          │
       │                             │  [TIM quit + logout]         │                          │
       │                             │  [TRTC exitRoom]             │                          │
       │  <── onEndTour ──────────── │                              │                          │

7.2 发起方(经纪人端)完整对接代码

import { NestTakelook3 } from 'vr-takelook-plugin';

let heartbeatTimer: ReturnType<typeof setInterval> | null = null;

async function initTour() {
  // 步骤一:设置服务参数
  await NestTakelook3.setSDKParams('your-service-token', 'https://your-api.com');

  // 步骤二:拉取平台配置
  await NestTakelook3.getConfig();

  // 步骤三:注册事件监听(建议在 startTour 之前注册)
  NestTakelook3.onSdkReady(() => {
    console.log('IM 就绪');
    // 可在此更新 UI 状态
  });

  NestTakelook3.onSdkNotReady(() => {
    console.log('IM 断线');
  });

  NestTakelook3.onUserConnected((userInfo) => {
    console.log('已入会:', userInfo.userID);
    // 连接成功后再开启 VR 状态同步
    NestTakelook3.enableSyncLocalActionToRemote(true);
  });

  NestTakelook3.onMemberJoin((member, members) => {
    console.log('成员加入:', member.userId);
    updateMemberList(members);
  });

  NestTakelook3.onMemberExit((member, members) => {
    console.log('成员退出:', member.userId);
    updateMemberList(members);
  });

  NestTakelook3.onEndTour((isMe, reason) => {
    if (heartbeatTimer) clearInterval(heartbeatTimer);
    console.log('带看结束:', isMe ? '主动结束' : '被动结束', reason);
    navigateToEndScreen();
  });

  NestTakelook3.onVoiceForbidden((userId, status) => {
    console.log(`用户 ${userId} ${status ? '已被静音' : '取消静音'}`);
    updateMuteIcon(userId, status);
  });

  NestTakelook3.onKickOut(() => {
    console.log('被踢出会话');
    navigateToHome();
  });

  NestTakelook3.onNetworkStatusChanged((status) => {
    if (status?.level === 'poor' || status?.level === 'offline') {
      showNetworkWarning(status.level);
    }
  });

  NestTakelook3.onRemoteControllingNotify((action) => {
    // 收到对方 VR 操作,同步到本地渲染器
    if (action) vrViewer.applyRemoteState(action);
  });

  NestTakelook3.onSyncRemoteCustomActionToLocal((action, from) => {
    switch (action.action) {
      case 'business/changeHouse':
        loadHouse(action.payload?.houseId as string);
        break;
      case 'business/switchScene':
        switchScene(action.payload?.sceneId as string);
        break;
    }
  });

  NestTakelook3.onMessageToUI((message) => {
    showToast(String(message));
  });

  // 步骤四:发起呼叫
  const callResult: any = await NestTakelook3.callOut('agent_001', 'package_001');

  // 步骤五:禁用同步(连接成功前不发送无效消息)
  NestTakelook3.enableSyncLocalActionToRemote(false);

  // 步骤六:启动带看
  await NestTakelook3.startTour({
    userId: 'agent_001',
    callerUserId: 'agent_001',
    meetingId: callResult.meetingId,
    roomId: callResult.roomId,
    imPlatform: 'web',
  });

  // 步骤七:启动心跳保活
  heartbeatTimer = setInterval(() => {
    const meetingId = NestTakelook3.getMeetingId();
    if (meetingId) {
      NestTakelook3.sendHeartBeat('agent_001', meetingId);
    }
  }, 30000);
}

// VR 渲染器视角变化时调用
function onVrViewChange(state: Record<string, unknown>) {
  NestTakelook3.syncLocalActionToRemote(state);
}

// 发送自定义业务动作
async function onChangeHouse(houseId: string) {
  await NestTakelook3.syncLocalCustomActionToRemote({
    action: 'business/changeHouse',
    payload: { houseId },
  });
}

// 结束带看
async function stopTour() {
  if (heartbeatTimer) clearInterval(heartbeatTimer);
  await NestTakelook3.endTour('用户主动结束');
  // 或完全销毁:await NestTakelook3.destroy();
}

7.3 接收方(客户端)完整对接代码

import { NestTakelook3 } from 'vr-takelook-plugin';

async function joinTour(meetingId: string, roomId: string, myUserId: string) {
  // 接收方不需要调用 callOut,meetingId/roomId 通过 App 推送或 URL 参数获取

  await NestTakelook3.setSDKParams('your-service-token', 'https://your-api.com');
  await NestTakelook3.getConfig();

  NestTakelook3.onSdkReady(() => { /* IM 就绪 */ });
  NestTakelook3.onUserConnected((info) => {
    NestTakelook3.enableSyncLocalActionToRemote(true);
  });
  NestTakelook3.onMemberJoin((member) => { /* 经纪人加入 */ });
  NestTakelook3.onEndTour((isMe, reason) => { /* 带看结束 */ });

  // 接收方核心事件:接收经纪人的 VR 控制指令
  NestTakelook3.onRemoteControllingNotify((action) => {
    vrViewer.applyRemoteState(action);
  });

  NestTakelook3.onSyncRemoteCustomActionToLocal((action, from) => {
    // 处理经纪人发送的业务动作
  });

  await NestTakelook3.startTour({
    userId: myUserId,
    callerUserId: 'agent_001', // 经纪人 ID
    meetingId,
    roomId,
    imPlatform: 'app',         // 客户端通常在 App WebView 中
    packageId: 'package_001',  // 若无 userSig 则必填
  });
}

8. 多平台对接说明

8.1 Web 浏览器模式(imPlatform: 'web'

SDK 内部完整管理 TIM + TRTC,无需宿主应用做额外处理。

await NestTakelook3.startTour({
  ...options,
  imPlatform: 'web',
});

注意: Web 模式下浏览器需要麦克风权限,请在调用前确保获取了 getUserMedia 授权。


8.2 Android App(WebView)模式(imPlatform: 'app'

IM 在 Web 层运行,RTC(音频采集/播放)委托给原生壳通过 JSBridge 实现。

Web → Native 调用协议:

window.VRViewer.postMessage(JSON.stringify({
  action: 'enterRoom',  // 或其他 action
  params: { roomId, userId, userSig, sdkAppId }
}))

Native → Web 回调协议(原生需注入以下函数):

函数名 参数 触发时机
window.KFMessageListeners.onEnterRoom(result) 进房结果对象 进入 TRTC 房间成功/失败
window.KFMessageListeners.onExitRoom(reason) 退出原因 离开 TRTC 房间
window.KFMessageListeners.onError(errCode, errMsg, extInfo) 错误信息 TRTC 错误
window.KFMessageListeners.onRemoteUserEnterRoom(userId) 用户 ID 远端用户进房
window.KFMessageListeners.onRemoteUserLeaveRoom(userId, reason) 用户 ID, 原因 远端用户退房
window.KFMessageListeners.onFirstAudioFrame(userId) 用户 ID 首帧音频到达
window.KFMessageListeners.onNetworkQuality(...) 网络质量数据 网络质量上报
window.KFMessageListeners.onExitVr() 退出 VR(用于触发结束带看)

8.3 iOS App(WKWebView)模式(imPlatform: 'app'

与 Android 相同,区别在于 JSBridge 通道不同:

Web → Native 调用协议:

window.webkit.messageHandlers.VRViewer.postMessage({
  action: 'enterRoom',
  params: { roomId, userId, userSig, sdkAppId }
})

Native → Web 回调协议与 Android 相同,使用 window.KFMessageListeners.*

iOS 独有能力: setAudioRoute 方法可控制听筒/扬声器切换。


8.4 微信小程序 WebView 模式(imPlatform: 'mp'

预留能力,当前框架已搭建,具体实现以实际版本为准,当前暂无


9. 后端接口说明

SDK 依赖的后端接口列表:

接口标识 方法 默认路径 说明
getConfig GET /api/v2/op/openapi/takelook/tencent/config/ 获取平台配置(sdkAppId 等)
callOut POST /api/v2/op/openapi/takelook/tencent/start/ 发起呼叫,返回 meetingId/roomId
getUserSig POST /api/v2/op/openapi/takelook/tencent/usersig/ 获取用户签名
sendHeartBeat POST /api/v2/op/openapi/takelook/tencent/heartbeat/ 心跳保活
leaveConference POST /api/v2/op/openapi/takelook/tencent/user_left/ 离开会议
createMeetingRoom POST /api/v2/op/openapi/takelook/tencent/rooms/allocate/ 创建会议室
queryCustomAgentList POST /api/v2/op/openapi/takelook/tencent/agent_list/ 查询经纪人列表
getUserInfo GET /api/v2/op/openapi/takelook/tencent/user_info/ 获取用户信息

公共请求约定:

  • 所有请求携带 Authorization: {serviceId} 请求头
  • POST 请求 Content-Type 为 application/json
  • 响应支持直接返回业务数据,或 { data: { ... } } 包裹格式

10. 常见问题

注意: 由于 SDK 本身包含了腾讯 RTC 相关的依赖,部署环境时需要保证域名使用 HTTPS 协议才能正常使用此插件。

Q1:startTour 报错 "Failed to normalize guided tour bootstrap result"

原因: SDK 无法从后端返回中提取 sdkAppIduserSigroomIdmeetingId

解决: - 确认已正确调用 setSDKParamsgetConfig - 检查后端 getUserSig 接口是否返回了 userSigsdkAppId 字段 - 或在 startTour 参数中直接提供 userSigsdkAppIdroomId 跳过后端请求


Q2:App 模式下没有声音

原因: imPlatform: 'app' 时音频由原生壳管理,SDK 本身不处理音频。

排查: - Android:确认原生壳实现了 window.VRViewer.postMessage,且正确处理了 enterRoom 消息 - iOS:确认实现了 window.webkit.messageHandlers.VRViewer - 检查 onEnterRoom 回调是否返回了成功状态 - 检查原生侧是否有麦克风/扬声器权限


Q3:onMemberJoin / onMemberExit 不触发

原因: 成员事件依赖 TIM 群消息推送。

排查: - 确认 roomId/groupId 正确,双方加入了同一个 TIM 群组 - 确认服务端已广播 takelook_member_joined / takelook_member_left 类型的群消息 - 确认 TIM 群类型为 Meeting(SDK 默认使用 Meeting 类型群组)


Q4:如何实现仅 VR 同步、不开启语音?

await NestTakelook3.startTour({
  ...options,
  noAudioCall: true,  // 禁用语音,仅同步 VR 画面
});

Q5:如何避免连接建立前发送无效 VR 同步消息?

// startTour 前关闭同步
NestTakelook3.enableSyncLocalActionToRemote(false);

// 连接成功后再开启
NestTakelook3.onUserConnected(() => {
  NestTakelook3.enableSyncLocalActionToRemote(true);
});

Q6:宿主应用有自己的消息通道,如何与 SDK 共用?

// 方式一:将外部收到的消息注入 SDK 解析
externalChannel.onMessage((msg) => {
  NestTakelook3.receivedCmd(msg);
});

// 方式二:用 sendCmd 发送点对点消息(不经过 SDK 群消息通道)
await NestTakelook3.sendCmd('target_user_id', { type: 'custom', data: '...' });

附录 A:内部消息协议

SDK 所有 TIM 消息均封装在统一信封中:

{
  "namespace": "nest-takelook3",
  "category": "cmd | viewerAction | customAction",
  "payload": { }
}
category 说明 对应 API
cmd 底层指令(静音、踢人等) sendCmd(), voiceForbidden(), kickOutById()
viewerAction VR 浏览状态同步 syncLocalActionToRemote()
customAction 自定义业务动作 syncLocalCustomActionToRemote()

SDK 自动过滤 namespace ≠ 'nest-takelook3' 的消息,不影响宿主应用自身的 TIM 消息处理。


附录 B:SDK 内置保留动作名

以下动作名为 SDK 内部使用,接入方自定义动作时请勿使用这些名称作为前缀:

动作名 说明 触发 API
member/mute 静音指令 voiceForbidden() 内部发送
moderation/kickOut 踢出指令 kickOutById() 内部发送
viewer/syncHouseState VR 状态同步 syncLocalActionToRemote() 内部发送