app.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <iframe class="external" :src="url" ref="iframeRef" v-if="url"></iframe>
  3. <div class="laser-layer" v-show="!url">
  4. <div class="scene-canvas" ref="fuseRef" :class="{ full: full === 'scene' }">
  5. <span class="taggle switch" v-if="full !== 'scene'" @click="full = 'scene'">
  6. <ui-icon type="f-l" ctrl />
  7. </span>
  8. <div id="direction"></div>
  9. </div>
  10. <div id="scene-map" :class="{ full: full === 'map' }">
  11. <span class="taggle switch" v-if="full === 'scene'" @click="full = 'map'">
  12. <ui-icon type="f-l" ctrl />
  13. </span>
  14. </div>
  15. </div>
  16. </template>
  17. <script lang="ts">
  18. import { defineComponent, ref, watchEffect, computed, watch, nextTick } from "vue";
  19. import { SceneType } from "@/store";
  20. import { params } from "@/env";
  21. import { fuseModel, modelProps } from "./index";
  22. import { modelSDKFactory } from "./platform";
  23. const typeChange = () => {
  24. const oldType = modelProps.type;
  25. let stopWatch = (null as unknown) as () => void;
  26. const typePromise = new Promise((_, reject) => {
  27. stopWatch = watchEffect(() => {
  28. if (modelProps.type !== oldType) {
  29. reject(new Error("当前模型未加载完已切换到下个"));
  30. stopWatch!();
  31. }
  32. });
  33. });
  34. return { typePromise, typeCleanup: stopWatch };
  35. };
  36. export const Model = defineComponent({
  37. name: "model",
  38. setup() {
  39. const scene = computed(() => modelProps.type !== fuseModel && modelProps.type);
  40. const url = ref("");
  41. const setUrl = (newURL: string) => {
  42. if (newURL !== url.value) {
  43. setTimeout(() => {
  44. const hook = (iframeRef.value?.contentWindow as any)?.beforeDestroy;
  45. if (hook) {
  46. try {
  47. hook();
  48. } catch (e) {
  49. console.error(e);
  50. }
  51. url.value = "";
  52. setTimeout(() => (url.value = newURL), 300);
  53. } else {
  54. url.value = newURL;
  55. }
  56. });
  57. }
  58. };
  59. watchEffect(() => {
  60. if (!scene.value) {
  61. return setUrl("");
  62. }
  63. const type = scene.value.type;
  64. const urls = !(window as any).offline
  65. ? {
  66. [SceneType.SWKK]: `/swkk/spg.html?m=${scene.value.num}`,
  67. [SceneType.SWKJ]: `/swkk/spg.html?m=${scene.value.num}`,
  68. [SceneType.SWSS]: `/swss/index.html?m=${scene.value.num}`,
  69. [SceneType.SWSSMX]: `/swkk/spg.html?m=${scene.value.num}`,
  70. [SceneType.SWMX]: `index.html?caseId=${params.caseId}&app=${params.app}&modelId=${scene.value.num}&share=1#sign-model`,
  71. [SceneType.SWYDSS]: `/swss/index.html?m=${scene.value.num}`,
  72. [SceneType.SWYDMX]: `/swkk/spg.html?m=${scene.value.num}`,
  73. }
  74. : {
  75. [SceneType.SWKK]: `/swkk/spg.html?m=${scene.value.num}`,
  76. [SceneType.SWKJ]: `/swkk/spg.html?m=${scene.value.num}`,
  77. [SceneType.SWSS]: `/swss/offline.html?m=${scene.value.num}`,
  78. [SceneType.SWSSMX]: `/swkk/spg.html?m=${scene.value.num}`,
  79. [SceneType.SWMX]: `offline.html?caseId=${params.caseId}&app=${params.app}&modelId=${scene.value.num}&share=1#sign-model`,
  80. [SceneType.SWYDSS]: `/swss/offline.html?m=${scene.value.num}`,
  81. [SceneType.SWYDMX]: `/swkk/spg.html?m=${scene.value.num}`,
  82. };
  83. setUrl(urls[type as SceneType]);
  84. });
  85. const fuseRef = ref<HTMLDivElement>();
  86. const iframeRef = ref<HTMLIFrameElement>();
  87. watch(
  88. () => [modelProps.type, url.value],
  89. async ([type, url], oldType, onCleanup) => {
  90. if (type !== fuseModel && !url) {
  91. return;
  92. }
  93. const callback = modelProps.callback;
  94. await nextTick();
  95. const { typePromise, typeCleanup } = typeChange();
  96. const modelPromise = modelSDKFactory(
  97. type as any,
  98. type === fuseModel ? fuseRef.value! : iframeRef.value!
  99. );
  100. let result: any = null,
  101. error = null;
  102. try {
  103. result = await Promise.race([typePromise, modelPromise]);
  104. } catch (err: any) {
  105. error = err;
  106. }
  107. typeCleanup();
  108. callback && callback(result, error);
  109. },
  110. { immediate: true, flush: "post" }
  111. );
  112. // 处理iframe 定制页面
  113. watch(
  114. () => [scene.value && scene.value.type, url.value],
  115. ([type], oldType, onCleanup) => {
  116. if (type === false) {
  117. // 手动渲染融合场景
  118. console.log("手动渲染!");
  119. setTimeout(() => {
  120. (window as any).viewer.setDisplay(true);
  121. }, 100);
  122. }
  123. const interval = setInterval(async () => {
  124. let doc: Document | undefined;
  125. try {
  126. doc = iframeRef.value?.contentWindow?.document!;
  127. } catch {
  128. clearInterval(interval);
  129. }
  130. if (!doc || !doc.querySelector("div")) return;
  131. console.error(doc, doc.head);
  132. const target = doc.head;
  133. clearInterval(interval);
  134. (window as any).iframeCreated && (window as any).iframeCreated(iframeRef.value);
  135. if (type === SceneType.SWSS) {
  136. const $style = document.createElement("style");
  137. $style.type = "text/css";
  138. var textNode = document.createTextNode(`
  139. .mode-tab > .model-mode-tab.strengthen {
  140. display: none !important;
  141. }
  142. `);
  143. $style.appendChild(textNode);
  144. target.appendChild($style);
  145. }
  146. }, 16);
  147. onCleanup(() => clearInterval(interval));
  148. },
  149. { flush: "post", immediate: true }
  150. );
  151. return {
  152. iframeRef,
  153. full: ref("scene"),
  154. fuseRef,
  155. url,
  156. };
  157. },
  158. });
  159. export default Model;
  160. </script>
  161. <style scoped lang="scss">
  162. .laser-layer {
  163. position: absolute;
  164. z-index: 1;
  165. left: 0;
  166. top: 0;
  167. width: 100%;
  168. height: 100%;
  169. }
  170. .external {
  171. position: absolute;
  172. z-index: 1;
  173. border: none;
  174. top: calc(var(--editor-head-height) + var(--header-top));
  175. left: 0;
  176. height: calc(100% - (var(--editor-head-height) + var(--header-top)));
  177. width: 100%;
  178. }
  179. #direction {
  180. right: calc(var(--editor-menu-right) + var(--editor-toolbox-width)) !important;
  181. top: calc(var(--header-top) + var(--editor-head-height)) !important;
  182. margin: 10px;
  183. transition: top 0.3s ease, right 0.3s ease;
  184. }
  185. .scene-canvas,
  186. #scene-map {
  187. &:not(.full) {
  188. position: absolute !important;
  189. right: calc(var(--editor-menu-right) + var(--editor-toolbox-width)) !important;
  190. bottom: 0;
  191. width: 320px;
  192. height: 200px;
  193. z-index: 99;
  194. #direction {
  195. top: 0 !important;
  196. right: 0 !important;
  197. }
  198. }
  199. &.full {
  200. width: 100%;
  201. height: 100%;
  202. background-color: #ccc;
  203. z-index: 90;
  204. }
  205. }
  206. #scene-map {
  207. display: none;
  208. }
  209. .taggle {
  210. position: absolute;
  211. font-size: 16px;
  212. color: #fff;
  213. background: rgba(0, 0, 0, 0.3);
  214. z-index: 9999999;
  215. width: var(--taggle-btn-width);
  216. height: var(--taggle-btn-width);
  217. display: flex;
  218. align-items: center;
  219. left: 10px;
  220. top: 10px;
  221. overflow: hidden;
  222. justify-content: center;
  223. border-radius: 3px;
  224. cursor: pointer;
  225. }
  226. </style>