App.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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.store.on('flooruser', ({ floors }: { floors: FloorsType[] }) => {
  169. if (floors?.length) {
  170. sceneStore.loadFloorData(floors);
  171. }
  172. });
  173. app.Camera.on('mode.beforeChange', ({ toMode, floorIndex, fromMode }) => {
  174. appStore.setMode(toMode);
  175. if (toMode != 'dollhouse') {
  176. appStore.setFloorId(floorIndex);
  177. }
  178. console.log('setFloorId-4', toMode, floorIndex);
  179. if (fromMode) {
  180. appStore.setFlying(true);
  181. }
  182. });
  183. app.Camera.on('mode.afterChange', () => {
  184. appStore.setFlying(false);
  185. });
  186. app.Camera.on('flying.started', () => {
  187. appStore.setFlying(true);
  188. });
  189. app.Camera.on('flying.ended', ({ targetPano }) => {
  190. appStore.setFlying(false);
  191. console.log('setFloorId-3', targetPano.floorIndex);
  192. if (unref(mode) !== 'dollhouse') {
  193. appStore.setFloorId(targetPano.floorIndex);
  194. }
  195. });
  196. app.Scene.on('error', (data) => {
  197. console.log('当前错误code', data.code);
  198. switch (data.code) {
  199. case 5033:
  200. // Dialog.alert('该场景正在计算中,请稍后再试');
  201. rtcStore.showBaseDialog(
  202. {
  203. title: t('base.tips'),
  204. desc: t('base.baseError1'),
  205. okTxt: t('base.confirm'),
  206. closeTxt: t('base.cancel'),
  207. },
  208. () => {},
  209. );
  210. break;
  211. case 5034:
  212. // Dialog.alert('服务端开小差,请稍后再试');
  213. rtcStore.showBaseDialog(
  214. {
  215. title: t('base.tips'),
  216. desc: t('base.baseError2'),
  217. okTxt: t('base.confirm'),
  218. closeTxt: t('base.cancel'),
  219. isSingle: true,
  220. },
  221. () => {},
  222. );
  223. break;
  224. case 5009:
  225. // Dialog.alert('服务端开小差,请稍后再试');
  226. rtcStore.showBaseDialog(
  227. {
  228. title: t('base.tips'),
  229. desc: t('base.baseError2'),
  230. okTxt: t('base.confirm'),
  231. closeTxt: t('base.cancel'),
  232. isSingle: true,
  233. },
  234. () => {},
  235. );
  236. break;
  237. case 5005:
  238. // Dialog.alert('服务端开小差,请稍后再试');
  239. rtcStore.showBaseDialog(
  240. {
  241. title: t('base.tips'),
  242. desc: t('base.baseError2'),
  243. okTxt: t('base.confirm'),
  244. closeTxt: t('base.cancel'),
  245. isSingle: true,
  246. },
  247. () => {},
  248. );
  249. break;
  250. }
  251. });
  252. app.render();
  253. // SDK初始化结束
  254. });
  255. // SDK global Event end
  256. onUnmounted(() => {
  257. appStore.unLoad();
  258. });
  259. // method
  260. const changeMode = (name: string) => {
  261. if (!unref(flying)) {
  262. appStore.setMode(name);
  263. } else {
  264. console.log('正在飞', unref(flying));
  265. // appStore.setMode(name);
  266. }
  267. };
  268. </script>
  269. <template>
  270. <div class="debug flex justify-between px-1" v-if="showDebug">
  271. <!-- <span>
  272. {{ t('base.debuginfo') }}:公告,当前测试最新: {{ test_version }}, 当前满员条件:
  273. {{ maxNumber }}
  274. </span> -->
  275. <span>
  276. {{ t('base.debuginfo') }}:公告,当前测试1.1.0正在开发测试中,如要1.0.0请联系开发!
  277. </span>
  278. <span class="close" @click.stop="showDebug = false">X</span>
  279. </div>
  280. <LoadingLogo :thumb="true" />
  281. <!-- 引导页 -->
  282. <Title v-if="isLoaded && !!unref(refMiniMap)" />
  283. <Guideline />
  284. <div class="ui-view-layout" :class="{ show: show }">
  285. <div class="scene" ref="scene$"></div>
  286. <template v-if="dataLoaded">
  287. <!-- 小地图 start -->
  288. <MiniMap
  289. :show="!!unref(refMiniMap) && player.showWidgets"
  290. :show-dollhouse="Boolean(controls.showDollhouse)"
  291. :to="refMiniMap"
  292. @change-mode="changeMode"
  293. />
  294. <!-- 小地图 end -->
  295. <!-- 平面图 start -->
  296. <FloorplanView :show="floorplanViewShow" :mode="mode" @change-mode="changeMode" />
  297. <!-- 平面图 end -->
  298. <!-- 聊天 start-->
  299. <ChatRoom v-if="!isTourMode" />
  300. <!-- 聊天 end -->
  301. <!-- 多楼层 start-->
  302. <FloorSwitch />
  303. <!-- 多楼层 end -->
  304. <!-- panel start-->
  305. <GuidePanel v-if="isTourMode" />
  306. <!-- panel end -->
  307. </template>
  308. </div>
  309. <BaseDialog />
  310. </template>
  311. <style lang="scss">
  312. @import './app.scss';
  313. .debug {
  314. color: white;
  315. background-color: rgba($color: #000000, $alpha: 0.3);
  316. position: fixed;
  317. top: 0;
  318. left: 0;
  319. z-index: 1000001;
  320. text-align: left;
  321. width: 100%;
  322. .close {
  323. // background-color: red;
  324. width: 30px;
  325. text-align: center;
  326. &:hover {
  327. cursor: pointer;
  328. }
  329. }
  330. }
  331. @media screen and (min-width: 768px) {
  332. }
  333. </style>