跳转至

AI 讲房集成指南

本文档面向接入众趣 zqsdk 的开发者,说明如何在自己的项目中实现「AI 讲房」功能。按步骤完成能力判断、事件监听、方法调用与 UI 搭建即可完成集成。


一、功能简介与前置条件

1.1 什么是 AI 讲房

AI 讲房是众趣 SDK 的「新版讲房」能力:按预设段落(小区内部、周边配套、户型解析、房源信息)自动播放讲解,并同步当前段落与进度。你的项目需要:

  • 在合适的时机判断当前模型是否支持讲房
  • 监听 SDK 事件以更新进度与播放状态;
  • 调用 SDK 方法实现开始、暂停、切换段落;
  • 提供讲房入口、控制条、进度展示等 UI(样式可完全自定义)。

1.2 展示示例

讲房入口 小区内部 周边配套 户型解析

1.3 前置条件

  • 已接入众趣 zqsdk,并在 zqsdk.initcomplete 回调中拿到 window.housePlay(或等价实例)。
  • 模型存在讲房数据;支持讲房的模型会在初始化时提供 vr_speak_data。 AI讲房接口对接流程见: AI讲房业务对接文档
  • 本项目采用的baidu地图进行的示例组件开发, 如果需要正常使用,需要您设置环境变量VITE_APP_BAIDU_MAP_AK到.env中

1.4 申请百度ak

百度地图ak, 用于本demo项目中地图相关信息的展示。百度地图应用申请地址:https://lbsyun.baidu.com/) 创建应用,应用类型请选择【浏览器端】 创建应用

ak在此处复制 百度地图ak

二、集成步骤概览

步骤 内容
1 init_UI 中根据 vr_speak_data 判断是否支持讲房,并初始化你的「讲房可用」状态与段落列表
2 在 housePlay 上注册讲房相关事件,将进度、段落索引、暂停/结束同步到你的状态
3 在用户操作时调用 startLecture / pauseLecture / goToLectureByName
4 实现讲房入口、控制条、进度条、讲房时的遮罩等 UI
5 监听 connect_ui_operation,在讲房过程中显隐地图、小区图、周边配套等

以下按步骤展开。


三、第一步:判断模型是否支持讲房

时机:在 housePlay.on('init_UI', ...) 回调中执行。

做法:从 housePlay.model.settings.vr_speak_data 判断是否存在且为非空对象。若是,则当前模型支持讲房。

housePlay.on('init_UI', function(model) {
  // model 即 housePlay.model,可从 model.settings 读取配置
  let settings = model.settings || {};
  let vrSpeakData = settings.vr_speak_data;
  let hasLecture = vrSpeakData && typeof vrSpeakData === 'object' && Object.keys(vrSpeakData).length > 0;

  if (hasLecture) {
    // 在你的应用里:显示「AI 讲房」入口,并初始化讲房段落列表(见下)
    yourApp.setLectureAvailable(true);
    yourApp.initLectureSegmentList(vrSpeakData);  // 可选:用 vrSpeakData 解析自定义段落,或使用固定 4 段
  }
});

段落列表建议:SDK 按「场景名」切换,固定为以下 4 段(顺序可自定),你只需维护「展示名 + 传给 SDK 的名称」的映射:

展示名(可自定义) 传给 SDK 的名称(必须一致)
小区内部 uptown_inside
周边配套 uptown_outside
户型解析 house_parse
房源信息 house_info

列表末尾可加一项「退出讲房」,仅用于关闭面板并暂停,不传 SDK。


四、第二步:准备讲房状态与段落数据

建议在你自己项目中维护至少以下状态(名称可自定):

状态 含义 建议更新方式
讲房是否可用 当前模型是否有讲房数据 仅在 init_UI 中根据 vr_speak_data 设置
讲房面板是否显示 是否打开讲房控制条/浮层 用户点击入口时设为 true,点击退出时设为 false
是否正在播放 与 SDK 实际播放状态一致 仅由 SDK 事件回调更新,避免与 SDK 不同步
当前段落索引 0~3 对应四段,或你的列表 index lectureUpdate 第一参数或用户点击段落时更新
当前段落进度 0–100,用于进度条 lectureUpdate 第二参数更新

段落数据结构示例(便于你渲染列表并调用 goToLectureByName):

const defaultSegments = [
  { name: '小区内部', lectureName: 'uptown_inside' },
  { name: '周边配套', lectureName: 'uptown_outside' },
  { name: '户型解析', lectureName: 'house_parse' },
  { name: '房源信息', lectureName: 'house_info' }
];
// 可再 push 一项 { name: '退出讲房', lectureName: null } 用于关闭面板

五、第三步:注册 SDK 事件并同步到你的状态

在 housePlay 可用后(例如在 zqsdk.initcompletehousePlay.start() 之后),注册以下讲房事件,并仅在这些回调中更新「是否正在播放」「当前段落索引」「当前段落进度」。

// 讲房进度更新:第一参数为当前段落 index 或 playStyle,第二参数为当前段落内进度百分比
housePlay.on('lectureUpdate', function(playStyleOrIndex, percent) {
  yourApp.setLectureProgress(typeof percent === 'number' ? percent : 0);
  if (typeof playStyleOrIndex === 'number' && playStyleOrIndex >= 0) {
    yourApp.setCurrentSegmentIndex(playStyleOrIndex);
  }
});

// 讲房场景切换:playStyle 为新段落名称(如 'uptown_outside')
housePlay.on('lectureNextPlayStyle', function(playStyle) {
  yourApp.setCurrentSegmentByPlayStyle(playStyle);
  yourApp.setLectureProgress(0);  // 切换段落时重置进度
});

// 讲房暂停
housePlay.on('lecturePause', function() {
  yourApp.setLecturePlaying(false);
});

// 讲房结束
housePlay.on('lectureEnd', function() {
  yourApp.setLecturePlaying(false);
  yourApp.hideLecturePanel();  // 可选:自动关闭面板
});

注意:不要在用户点击「播放」时仅本地把「是否正在播放」设为 true 而不调 SDK;应先调 SDK,再由上述事件回写状态,以保证与 SDK 一致。


六、第四步:在用户操作时调用 SDK 方法

SDK 提供三个与讲房相关的方法(均在 housePlay 上调用):

方法 说明 调用时机建议
housePlay.startLecture() 开始或继续讲房 用户点击「开始讲房」或控制条上的「播放」
housePlay.pauseLecture() 暂停讲房 用户点击「暂停」或「退出讲房」
housePlay.goToLectureByName(name) 跳转到指定段落 开始/继续播放前先跳段,再调用 startLecture()

推荐交互逻辑

  • 用户点击「AI 讲房」入口:显示讲房面板 → 将本地「当前选中段」设为 0(或保持上次)→ 调用 housePlay.startLecture()(若希望从第一段开始,可先 goToLectureByName('uptown_inside')startLecture())。
  • 用户点击控制条「播放」:若当前有选中段落且非「退出」,先 goToLectureByName(当前段的 lectureName),再 startLecture();否则仅 startLecture()
  • 用户点击「暂停」:仅调用 pauseLecture();播放状态由事件 lecturePause/lectureEnd 回写。
  • 用户点击某一段落
  • 若当前为暂停:只更新你本地的「当前选中段」和进度条置 0,不调 SDK
  • 若当前为播放:先 goToLectureByName(该段 lectureName),再 startLecture()
  • 用户点击「退出讲房」:调用 pauseLecture(),并关闭讲房面板、重置本地展示状态。

这样可避免「暂停时误触 SDK」导致状态错乱。


七、第五步:实现讲房 UI

UI 完全由你自定义,只需满足功能对应关系即可。

7.1 建议具备的 UI 元素

  1. 讲房入口
  2. 显示条件:SDK 已就绪且当前模型 vr_speak_data 存在(即「讲房可用」为 true),且讲房面板未打开。
  3. 点击:打开讲房面板,并调用 startLecture()(必要时先 goToLectureByNamestartLecture)。

  4. 讲房控制条/浮层

  5. 显示条件:讲房面板已打开。
  6. 内容建议:

    • 播放/暂停按钮 → 对应 startLecture / pauseLecture,且播放时若需从当前段开始则先 goToLectureByName
    • 段落列表(4 段 + 可选「退出讲房」)→ 点击段落时按「第四步」逻辑处理;
    • 当前段进度条 → 绑定「当前段落进度」状态(0–100)。
  7. 讲房播放时的画布遮罩(推荐)

  8. 当「讲房面板已打开且正在播放」时,在 3D/全景画布上盖一层透明遮罩(pointer-events: auto),避免用户误触旋转/漫游;讲房控制条置于遮罩之上并可点击。这样体验更稳定。

  9. 讲房模式下的其他 UI

  10. 可根据需要隐藏底部栏、右侧面板、导览入口等,仅保留讲房相关控件,避免与讲房控制条重叠。

7.2 可选:顶部房源标题

若你有房源名称,可在讲房面板打开时在顶部居中展示,提升识别度;数据来源由你业务决定(如从 vr_speak_data.settings 或房源接口获取)。


八、第六步:讲房过程中显示地图、小区图、周边配套

讲房播放到某些段落时,SDK 会通过事件 connect_ui_operation 下发「要显示/隐藏的 UI」,用于在讲房过程中自动弹出或关闭地图、小区图、房源详情等。

8.1 监听方式

housePlay.on('connect_ui_operation', function(action) {
  if (!action || action.type !== 'UI_OPERATION' || !action.data) return;
  if (action.data.type !== 'UI') return;

  let target = action.data.target;   // 要控制的 UI 目标
  let show = action.data.action;     // true 显示,false 隐藏(或其他数据)

  switch (target) {
    case 'houseInfo':   // 房源详情(可包含地图、小区图、房源信息等)
      yourApp.toggleHouseInfoPanel(show);
      break;
    case 'bigMap':      // 大地图
      yourApp.toggleBigMap(show);
      break;
    case 'imgSwiper':   // 小区图片轮播
      yourApp.toggleCommunityImageSwiper(show);
      break;
    case 'bigHangpai':  // 大航拍
      yourApp.toggleBigHangpaiPanel(show);
      break;
    case 'mapSearchItemIndex':  // 周边配套 POI 数据
      yourApp.setMapSearchData(show);
      break;
    // 其他 target 按需处理
  }
});

8.2 数据来源

讲房所需的地图、小区图等数据通常来自模型配置。可在 init_UI 或房源数据加载时,将 housePlay.model.settings.vr_speak_data.settings 中与 UI 相关的字段(如 communityImagesaddresspanoramaLink 等)合并到你自己的房源/详情数据中,再在收到 connect_ui_operation 时根据 target 显示对应组件。

8.3 周边配套 POI 数据(mapSearchItemIndex)

当讲房进入「周边配套」段落时,SDK 会通过 connect_ui_operation 下发 mapSearchItemIndex 数据,包含当前分类的 POI(兴趣点)列表,用于在地图上展示周边学校、地铁、公交、购物中心、医院、公园、菜市场等配套设施。

8.3.1 数据结构

// action.data 结构
{
  type: 'UI',
  target: 'mapSearchItemIndex',
  action: {
    name: '学校',  // 分类名称:学校 | 地铁 | 公交 | 购物中心 | 医院 | 公园 | 菜市场
    poi: [
      {
        name: 'XX小学',           // POI 名称
        title: 'XX小学',          // POI 标题(可能与 name 相同)
        lng: 116.404,             // 经度
        lat: 39.915,              // 纬度
        location: { lng, lat },   // 坐标(部分版本使用此字段)
        distance: 500,            // 距离房源的距离(米)
        address: 'XX路XX号'       // 地址(可选)
      },
      // ... 更多 POI
    ]
  }
}

注意:SDK 下发的分类名称可能是具体类型(如 "初中"、"小学"),需要映射到标准分类(如 "学校")。

8.3.2 分类名称映射

SDK 可能下发的分类名称与标准分类的映射关系:

SDK 下发名称 标准分类
幼儿园、小学、初中、高中、大学 学校
地铁、地铁站 地铁
公交、公交站 公交
购物、商场、超市 购物中心
医院、诊所、卫生院 医院
公园、绿地、广场 公园
菜市场、菜场、农贸市场、生鲜 菜市场
// 分类名称映射函数示例
function normalizeMapSearchCategory(name) {
  const schoolTypes = ['幼儿园', '小学', '初中', '高中', '大学', '学校'];
  if (schoolTypes.includes(name)) return '学校';
  if (name.includes('地铁')) return '地铁';
  if (name.includes('公交')) return '公交';
  if (name.includes('购物') || name.includes('商场') || name.includes('超市')) return '购物中心';
  if (name.includes('医院') || name.includes('诊所')) return '医院';
  if (name.includes('公园') || name.includes('绿地')) return '公园';
  if (name.includes('菜市场') || name.includes('农贸')) return '菜市场';
  return name;  // 已是标准分类则直接返回
}

8.3.3 监听与处理

housePlay.on('connect_ui_operation', function(action) {
  if (!action || action.type !== 'UI_OPERATION' || !action.data) return;
  if (action.data.type !== 'UI') return;

  let target = action.data.target;
  let data = action.data.action;

  switch (target) {
    // ... 其他 target 处理

    case 'mapSearchItemIndex':
      // 周边配套 POI 数据
      if (data && typeof data === 'object' && data.name && data.poi) {
        let rawCategoryName = data.name;
        let normalizedCategory = normalizeMapSearchCategory(rawCategoryName);

        yourApp.setMapSearchData({
          rawName: rawCategoryName,           // 原始名称(用于显示)
          normalizedName: normalizedCategory, // 标准分类(用于索引)
          poi: data.poi.map(function(item) {
            return {
              name: item.name || item.title,
              lng: item.lng || (item.location && item.location.lng),
              lat: item.lat || (item.location && item.location.lat),
              distance: item.distance || 0,
              address: item.address || ''
            };
          }).sort(function(a, b) {
            return (a.distance || 0) - (b.distance || 0);  // 按距离排序
          })
        });
      }
      break;
  }
});

8.3.4 状态管理建议

建议在你的应用中维护以下周边配套相关状态:

状态 含义 说明
mapSearchCurrentIndex 当前分类索引 0-6 对应:学校、地铁、公交、购物中心、医院、公园、菜市场
mapSearchCurrentName 当前分类名称 如 "学校"、"地铁" 等
mapSearchCurrentPoi 当前分类的 POI 列表 数组,包含 name、lng、lat、distance 等
mapSearchCategoriesData 所有分类的 POI 缓存 对象,key 为标准分类名,value 为 POI 数组
// 分类常量
const MAP_SEARCH_CATEGORIES = ['学校', '地铁', '公交', '购物中心', '医院', '公园', '菜市场'];

// 设置 POI 数据
function setMapSearchData(data) {
  let rawCategoryName = data.rawName;
  let normalizedCategory = data.normalizedName;
  let poiList = data.poi;

  // 使用标准分类名缓存数据
  mapSearchCategoriesData[normalizedCategory] = poiList;

  // 更新当前分类(显示原始名称便于用户理解)
  mapSearchCurrentName = rawCategoryName;
  mapSearchCurrentPoi = poiList;
  mapSearchCurrentIndex = MAP_SEARCH_CATEGORIES.indexOf(normalizedCategory);
}

// 切换分类(用户手动切换时)
function switchMapSearchCategory(index) {
  let categoryName = MAP_SEARCH_CATEGORIES[index];
  let cachedData = mapSearchCategoriesData[categoryName];

  mapSearchCurrentIndex = index;
  mapSearchCurrentName = categoryName;
  mapSearchCurrentPoi = cachedData || [];
}

8.3.5 地图 UI 实现建议

周边配套地图建议包含以下 UI 元素:

  1. 地图主体
  2. 显示房源位置(中心标记点)
  3. 显示当前分类的 POI 标记
  4. POI 数据更新时自动显示第一个 POI 的步行路径

  5. 分类切换栏(底部或顶部)

  6. 7 个分类按钮:学校、地铁、公交、购物中心、医院、公园、菜市场
  7. 高亮当前分类
  8. 点击切换分类,更新地图上的 POI 标记

  9. POI 列表侧边栏(可选)

  10. 显示当前分类的 POI 列表
  11. 每项显示名称和距离
  12. 点击可定位到该 POI 并显示步行路径

  13. 步行路径

  14. POI 数据更新时,自动显示第一个 POI 的步行路线
  15. 点击 POI 时,切换显示该 POI 的步行路线
  16. 使用百度地图 WalkingRoute API 实现
// 百度地图步行路径示例
let walkingRoute = new BMap.WalkingRoute(map, {
  renderOptions: { map: map, autoViewport: false }
});

// 显示步行路径
function showWalkingRoute(poi) {
  walkingRoute.clearResults();
  let start = new BMap.Point(houseLng, houseLat);  // 房源坐标
  let end = new BMap.Point(poi.lng, poi.lat);       // POI 坐标
  walkingRoute.search(start, end);
}

// POI 数据更新时自动显示第一个 POI 的路径
function renderPoiMarkers(poiList) {
  // ... 渲染 POI 标记 ...

  // 自动显示第一个 POI 的步行路径
  if (poiList.length > 0) {
    showWalkingRoute(poiList[0]);
  }
}

九、API 速查表

9.1 方法(housePlay)

方法 说明
housePlay.startLecture() 开始/继续讲房
housePlay.pauseLecture() 暂停讲房
housePlay.goToLectureByName(name) 跳转到指定段落;name 取值:uptown_inside | uptown_outside | house_parse | house_info

9.2 事件(housePlay.on)

事件 回调参数 说明
lectureUpdate (playStyleOrIndex, percent) 进度更新;第一参数为段落 index 或 playStyle 字符串,第二参数为 0–100 百分比
lectureNextPlayStyle (playStyle) 场景切换;playStyle 为新段落名称(如 uptown_outside
lecturePause 讲房暂停
lectureEnd 讲房结束
lecture (state) 旧版;state === 'stop' 表示停止
connect_ui_operation (action) 讲房过程中显隐 UI;action.data.targetaction.data.action 见下表

9.3 connect_ui_operation target 速查

target action 类型 说明
houseInfo boolean 房源详情面板显隐
bigMap boolean 大地图显隐
imgSwiper boolean 小区图片轮播显隐
bigHangpai boolean 大航拍面板显隐
houseInfoSwiper boolean 房源信息轮播自动播放
initHouseInfoSwiper number 房源信息轮播初始索引
mapSearchItemIndex { name, poi[] } 周边配套 POI 数据(见 8.3 节)

9.4 判断讲房是否可用

  • init_UI 中读取:housePlay.model.settings.vr_speak_data,存在且为非空对象即表示支持讲房。

十、常见问题与注意事项

  1. 务必在事件回调中更新「是否正在播放」
    不要只在点击播放时本地设为 true;否则 SDK 暂停或出错时,你的 UI 会与真实状态不一致。应以 lecturePauselectureEnd 为准。

  2. 暂停时切换段落不要调 SDK
    用户暂停后点击其他段落,只更新你本地的「当前选中段」和进度条;等用户再次点击「播放」时再 goToLectureByName + startLecture,避免多余调用。

  3. 先 goToLectureByName 再 startLecture
    若希望「从指定段开始播放」,请先调用 goToLectureByName(lectureName),再调用 startLecture()

  4. 方法是否存在
    部分环境或旧版 SDK 可能未暴露 startLecture / pauseLecture / goToLectureByName,调用前可用 housePlay.startLecture != null 等方式做兼容。

  5. 与导览的关系
    讲房与导览相互独立;同一模型可同时有导览和讲房,分别用 heroLocationsvr_speak_data 判断即可。

  6. 周边配套分类名称映射
    SDK 下发的分类名称可能是具体类型(如 "初中"),需要映射到标准分类(如 "学校")后再进行索引和缓存,确保分类切换栏正确高亮。

  7. 周边配套路径自动显示
    当 POI 数据更新时,建议自动显示第一个 POI 的步行路径,提升用户体验。


十一、本仓库实现参考

若需参考本仓库的完整实现(Vue 3 + Pinia + TypeScript),可参阅:

11.1 核心文件

文件 说明
src/stores/introduce.ts 讲房状态管理(段落、进度、POI、面板显隐等)
src/stores/vr.ts SDK 事件监听与分发(lectureUpdateconnect_ui_operation 等)
src/types/lecture.ts 讲房相关类型定义(MapSearchPoiItemMapSearchDatanormalizeMapSearchCategory 等)

11.2 UI 组件

文件 说明
src/modules/pc/introduce/NestIntroduceBtn.vue 讲房入口按钮
src/modules/pc/introduce/NestIntroducePanel.vue 讲房控制面板(段落列表、进度条、播放/暂停)
src/modules/pc/introduce/NestIntroduceOverlay.vue 讲房播放遮罩
src/modules/pc/introduce/NestIntroduceActionPanel.vue 讲房弹出 UI 容器

11.3 讲房弹出面板

文件 说明
src/modules/pc/introduce/NestLectureBigMap.vue 讲房大地图(含周边配套 POI)
src/modules/pc/introduce/NestLectureImgSwiper.vue 讲房图片轮播
src/modules/pc/introduce/NestLectureBigHangpai.vue 讲房航拍面板
src/modules/pc/introduce/NestLectureHouseInfo.vue 讲房房源信息

11.4 公共组件

文件 说明
src/components/common/pc/NestModalMap.vue 通用地图组件(支持 POI 标记、分类切换、步行路径)
src/components/common/pc/NestModalImgSwiper.vue 通用图片轮播组件
src/components/common/pc/NestHouseInfo.vue 通用房源信息组件

11.5 布局入口

文件 说明
src/layouts/pc/PcHouseLayout.vue PC 端布局(讲房组件挂载点)

十二、 离线数据包目录位置与资源替换

众趣推送的离线数据包中,会存在AI讲房相关资源内容,如讲房音频等资源。资源目录放置在 {{model_id}}/audio/目录下。

替换目录:

模型版本目录

  1. settings.txt (单个文件)
  2. settings_local.txt (单个文件)

模型id目录

  1. audio (整个目录)

截图示意

{{模型id}}/audio目录 AI讲房音频文件在离线数据包位置

{{模型版本}}/setting.txt 和 setting_local.txt AI讲房离线数据包setting替换示意