App.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <script setup lang="ts">
  2. import { onMounted, ref, computed, unref, watchEffect, onUnmounted } from 'vue';
  3. import { createApp } from '/@/hooks/userApp';
  4. // import tagView from '/@/components/custom/tagView.vue';
  5. // import tag from '/@/components/custom/tag.vue';
  6. import LoadingLogo from '/@/components/basic/loading.vue';
  7. import MiniMap from '/@/components/basic/miniMap.vue';
  8. import FloorplanView from '/@/components/basic/floorplan.vue';
  9. import { useSceneStore } from '/@/store/modules/scene';
  10. import type { FloorsType } from '/@/store/modules/scene';
  11. import { useAppStore } from './store/modules/app';
  12. import Guideline from '/@/components/basic/guide.vue';
  13. import ChatRoom from '/@/components/chatRoom/index.vue';
  14. import Title from '/@/components/basic/title.vue';
  15. import FloorSwitch from '/@/components/basic/FloorSwitch.vue';
  16. import GuidePanel from '/@/components/custom/panel.vue';
  17. import browser from '/@/utils/browser';
  18. // import { useRtcStore } from './store/modules/rtc';
  19. import { useTourPlayer } from './hooks/userTourPlayer';
  20. import { KankanMetaDataType } from '/#/sdk';
  21. // import Dialog from '/@/components/basic/dialog';
  22. import { useRtcStore } from './store/modules/rtc';
  23. import { useLocale } from '/@/locales/useLocale';
  24. import { LocaleType } from '/#/config';
  25. import { useI18n } from '/@/hooks/useI18n';
  26. import BaseDialog from '/@/components/chatRoom/dialog/base.vue';
  27. const { createTourPlayer } = useTourPlayer();
  28. const showDebug = ref(Number(import.meta.env.VITE_SHOW_DEBUGPANEL) === 1);
  29. const { t } = useI18n();
  30. const { changeLocale } = useLocale();
  31. // 设置语言
  32. const defaultLang = ['zh', 'en'];
  33. const lang = browser.getURLParam('lang');
  34. if (lang && defaultLang.includes(browser.getURLParam('lang'))) {
  35. changeLocale(lang as LocaleType);
  36. } else {
  37. changeLocale('zh');
  38. }
  39. const maxNumber = import.meta.env.VITE_ROOM_MEMBER || 10;
  40. const test_version = computed(() => import.meta.env.VITE_TEST_VERSION || '');
  41. const sceneStore = useSceneStore();
  42. const appStore = useAppStore();
  43. const rtcStore = useRtcStore();
  44. // const rtcStore = useRtcStore();
  45. const dataLoaded = ref(false);
  46. const scene$ = ref<Nullable<HTMLElement>>(null);
  47. const refMiniMap = ref<Nullable<string>>(null);
  48. const flying = computed(() => appStore.flying);
  49. const player = computed(() => appStore.player);
  50. const metadata = computed(() => sceneStore.metadata);
  51. const mode = computed(() => appStore.mode);
  52. // const isJoined = computed(() => rtcStore.isJoined);
  53. const isLoaded = computed(() => appStore.isLoaded);
  54. const controls = computed(() => {
  55. return metadata.value.controls;
  56. });
  57. const floorplanViewShow = computed(() => {
  58. return (
  59. Boolean(controls.value.showDollhouse) &&
  60. Boolean(controls.value.showFloorplan) &&
  61. mode.value !== 'panorama'
  62. );
  63. });
  64. const show = ref(false);
  65. const isTourMode = computed(() => appStore.isTourMode);
  66. watchEffect(() => {
  67. console.warn('mode', mode);
  68. });
  69. // 强制开https
  70. console.log('当前protocol', location.protocol);
  71. if (location.protocol !== 'https:') {
  72. location.protocol = 'https:';
  73. console.log('强制开https');
  74. }
  75. onMounted(async () => {
  76. const app = await createApp({
  77. dom: scene$.value as HTMLElement,
  78. num: browser.getURLParam('m'),
  79. mobile: true,
  80. });
  81. // SDK初始化
  82. app.use('MinMap', { theme: { camera_fillStyle: '#ED5D18' } }).then(() => {
  83. // refMiniMap.value = '[xui_min_map]';
  84. console.log('小地图SDK');
  85. });
  86. app.use('Tag');
  87. createTourPlayer();
  88. app
  89. .use('TagView', {
  90. render(data) {
  91. // console.log('tagView', data);
  92. if (data.media['image'] && data.media['image'].length) {
  93. // console.log('tagView-1', data);
  94. // return h(tagView, {
  95. // sid: data.sid,
  96. // url: app.resource.getUserResourceURL(data.media['image'][0].src),
  97. // content: data.content,
  98. // title: data.title,
  99. // });
  100. }
  101. // console.log('render', render);
  102. // return `<span class="tag-icon animate" style="background-image:url({{icon}})"></span>`;
  103. // return
  104. return `<span class="tag-icon animate" style="display:none"></span>`;
  105. },
  106. })
  107. .then(() => {
  108. // const openTags = (tag) => {
  109. // let item = tags.value.find((item) => item.sid == el.data.sid);
  110. // guideclicktag(item);
  111. // store.commit('tag/setTagClickType', {
  112. // type: 'goodlist',
  113. // data: item,
  114. // });
  115. // };
  116. // view.on('rendered', (_) => {
  117. // view.on('click', async (e) => {
  118. // var tag = e.data;
  119. // 聚焦當前點擊的熱點
  120. // debugger;
  121. // await view.focus(tag.sid);
  122. // });
  123. // view.on('focus', (e) => {
  124. // if (!e.data.media['image'] || !e.data.media['image'].length) {
  125. // return;
  126. // }
  127. // document.querySelectorAll('[xui_tags_view] >div').forEach((el) => {
  128. // el.querySelector('.tag-body').classList.remove('show');
  129. // el.style.zIndex = 'auto';
  130. // });
  131. // e.target.style.zIndex = '999';
  132. // e.target.querySelector('.tag-body').classList.add('show');
  133. // });
  134. // }); //dom渲染完成
  135. });
  136. // 暂时isTours url frag 做为 1自由观看模式与带看模式0
  137. if (Number(browser.getURLParam('isTour')) === 1) {
  138. appStore.setIsTourMode(true);
  139. } else {
  140. appStore.setIsTourMode(false);
  141. }
  142. // SDK global Event start
  143. app.Scene.on('ready', () => {
  144. show.value = true;
  145. });
  146. app.Scene.on('loaded', (pano) => {
  147. appStore.setFloorId(pano.floorIndex);
  148. refMiniMap.value = '[xui_min_map]';
  149. // store.commit("setFloorId", pano.floorIndex);
  150. // store.commit("rtc/setShowdaogou", true);
  151. // if (browser.getURLParam("roomId") && browser.getURLParam("vruserId") && browser.getURLParam("name")) {
  152. // store.commit("showShoppingguide", true);
  153. // }
  154. });
  155. app.store.on('metadata', (metadata: KankanMetaDataType) => {
  156. sceneStore.load(metadata);
  157. if (!metadata.controls.showMap) {
  158. app.MinMap.hide(true);
  159. }
  160. dataLoaded.value = true;
  161. appStore.isLoad();
  162. });
  163. app.store.on('floorcad', ({ floors }: { floors: FloorsType[] }) => {
  164. if (floors?.length) {
  165. sceneStore.loadFloorData(floors);
  166. }
  167. });
  168. app.Camera.on('mode.beforeChange', ({ toMode, floorIndex, fromMode }) => {
  169. appStore.setMode(toMode);
  170. if (toMode != 'dollhouse') {
  171. appStore.setFloorId(floorIndex);
  172. }
  173. console.log('setFloorId-4', toMode, floorIndex);
  174. if (fromMode) {
  175. appStore.setFlying(true);
  176. }
  177. });
  178. app.Camera.on('mode.afterChange', () => {
  179. appStore.setFlying(false);
  180. });
  181. app.Camera.on('flying.started', () => {
  182. appStore.setFlying(true);
  183. });
  184. app.Camera.on('flying.ended', ({ targetPano }) => {
  185. appStore.setFlying(false);
  186. console.log('setFloorId-3', targetPano.floorIndex);
  187. if (unref(mode) !== 'dollhouse') {
  188. appStore.setFloorId(targetPano.floorIndex);
  189. }
  190. });
  191. app.Scene.on('error', (data) => {
  192. console.log('当前错误code', data.code);
  193. switch (data.code) {
  194. case 5033:
  195. // Dialog.alert('该场景正在计算中,请稍后再试');
  196. rtcStore.showBaseDialog(
  197. {
  198. title: t('base.tips'),
  199. desc: t('base.baseError1'),
  200. okTxt: t('base.confirm'),
  201. closeTxt: t('base.cancel'),
  202. },
  203. () => {},
  204. );
  205. break;
  206. case 5034:
  207. // Dialog.alert('服务端开小差,请稍后再试');
  208. rtcStore.showBaseDialog(
  209. {
  210. title: t('base.tips'),
  211. desc: t('base.baseError2'),
  212. okTxt: t('base.confirm'),
  213. closeTxt: t('base.cancel'),
  214. isSingle: true,
  215. },
  216. () => {},
  217. );
  218. break;
  219. case 5009:
  220. // Dialog.alert('服务端开小差,请稍后再试');
  221. rtcStore.showBaseDialog(
  222. {
  223. title: t('base.tips'),
  224. desc: t('base.baseError2'),
  225. okTxt: t('base.confirm'),
  226. closeTxt: t('base.cancel'),
  227. isSingle: true,
  228. },
  229. () => {},
  230. );
  231. break;
  232. case 5005:
  233. // Dialog.alert('服务端开小差,请稍后再试');
  234. rtcStore.showBaseDialog(
  235. {
  236. title: t('base.tips'),
  237. desc: t('base.baseError2'),
  238. okTxt: t('base.confirm'),
  239. closeTxt: t('base.cancel'),
  240. isSingle: true,
  241. },
  242. () => {},
  243. );
  244. break;
  245. }
  246. });
  247. app.render();
  248. // SDK初始化结束
  249. });
  250. // SDK global Event end
  251. onUnmounted(() => {
  252. appStore.unLoad();
  253. });
  254. // method
  255. const changeMode = (name: string) => {
  256. if (!unref(flying)) {
  257. appStore.setMode(name);
  258. } else {
  259. console.log('正在飞', unref(flying));
  260. // appStore.setMode(name);
  261. }
  262. };
  263. </script>
  264. <template>
  265. <div class="debug flex justify-between px-1" v-if="showDebug">
  266. <span>
  267. {{ t('base.debuginfo') }}:公告,当前测试最新: {{ test_version }}, 当前满员条件:
  268. {{ maxNumber }}
  269. </span>
  270. <span class="close" @click.stop="showDebug = false">X</span>
  271. </div>
  272. <LoadingLogo :thumb="true" />
  273. <!-- 引导页 -->
  274. <Title v-if="isLoaded && !!unref(refMiniMap)" />
  275. <Guideline />
  276. <div class="ui-view-layout" :class="{ show: show }">
  277. <div class="scene" ref="scene$"></div>
  278. <template v-if="dataLoaded">
  279. <!-- 小地图 start -->
  280. <MiniMap
  281. :show="!!unref(refMiniMap) && player.showWidgets"
  282. :show-dollhouse="Boolean(controls.showDollhouse)"
  283. :to="refMiniMap"
  284. @change-mode="changeMode"
  285. />
  286. <!-- 小地图 end -->
  287. <!-- 平面图 start -->
  288. <FloorplanView :show="floorplanViewShow" :mode="mode" @change-mode="changeMode" />
  289. <!-- 平面图 end -->
  290. <!-- 聊天 start-->
  291. <ChatRoom v-if="!isTourMode" />
  292. <!-- 聊天 end -->
  293. <!-- 多楼层 start-->
  294. <FloorSwitch />
  295. <!-- 多楼层 end -->
  296. <!-- panel start-->
  297. <GuidePanel v-if="isTourMode" />
  298. <!-- panel end -->
  299. </template>
  300. </div>
  301. <BaseDialog />
  302. </template>
  303. <style lang="scss">
  304. @import './app.scss';
  305. .debug {
  306. color: white;
  307. background-color: rgba($color: #000000, $alpha: 0.3);
  308. position: fixed;
  309. top: 0;
  310. left: 0;
  311. z-index: 1000001;
  312. text-align: left;
  313. width: 100%;
  314. .close {
  315. // background-color: red;
  316. width: 30px;
  317. text-align: center;
  318. &:hover {
  319. cursor: pointer;
  320. }
  321. }
  322. }
  323. @media screen and (min-width: 768px) {
  324. }
  325. </style>