Pārlūkot izejas kodu

feat: 贺龙纪念馆

chenlei 11 mēneši atpakaļ
vecāks
revīzija
e62c4a58b2

+ 3 - 0
package.json

@@ -16,6 +16,9 @@
     "serve:ylct": "cross-env VITE_APP_SCENE=ylct VITE_APP_TITLE=家国天下-勇立潮头 VITE_APP_HOT_DOMAIN=./hotspot.html vite",
     "build:ylct:test": "cross-env VITE_APP_SCENE=ylct VITE_APP_TITLE=家国天下-勇立潮头 VITE_APP_HOT_DOMAIN=./hotspot.html run-p type-check \"build-only {@}\" --",
     "push:ylct": "cross-env VITE_APP_SCENE=ylct node ./scripts/publish.js",
+    "serve:hljng": "cross-env VITE_APP_SCENE=hljng VITE_APP_TITLE=贺龙故居 VITE_APP_HOT_DOMAIN=./hotspot.html vite",
+    "build:hljng:test": "cross-env VITE_APP_SCENE=hljng VITE_APP_TITLE=贺龙故居 VITE_APP_HOT_DOMAIN=./hotspot.html run-p type-check \"build-only {@}\" --",
+    "push:hljng": "cross-env VITE_APP_SCENE=hljng node ./scripts/publish.js",
     "preview": "vite preview",
     "build-only": "vite build",
     "type-check": "vue-tsc --build --force"

BIN
public/favicon/favicon-hljng.ico


BIN
src/index/assets/images/hljng/Volume btn_off.png


BIN
src/index/assets/images/hljng/Volume btn_on.png


BIN
src/index/assets/images/hljng/auto-suspend.png


BIN
src/index/assets/images/hljng/auto.png


BIN
src/index/assets/images/hljng/back.png


BIN
src/index/assets/images/hljng/dollhouse.png


BIN
src/index/assets/images/hljng/enlarge_on.png


BIN
src/index/assets/images/hljng/floor.png


BIN
src/index/assets/images/hljng/hotlist.png


BIN
src/index/assets/images/hljng/inside.png


BIN
src/index/assets/images/hljng/narrow_off.png


BIN
src/index/assets/images/hljng/pause.png


BIN
src/index/assets/images/hljng/play.png


BIN
src/index/assets/images/hljng/share.png


BIN
src/index/assets/images/hljng/view.png


+ 230 - 0
src/index/views/home/components/menu/index.hljng.scss

@@ -0,0 +1,230 @@
+.pinBottom-container {
+  position: absolute;
+  bottom: 10px;
+  width: 100%;
+  transition: all 0.5s;
+  z-index: 200;
+
+  .pinBottom.playing {
+    bottom: 20px;
+  }
+}
+
+.pinBottom.open.noScroll.playing {
+  bottom: 175px;
+}
+
+.pinBottom {
+  position: absolute;
+  bottom: 0;
+  line-height: 1;
+  transition: all 0.5s;
+
+  a,
+  span {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    margin-top: 2px;
+    color: #ffffff;
+    font-size: 16px;
+    font-family: 'Source Han Sans CN-Regular';
+  }
+  span {
+    display: block;
+    text-align: center;
+  }
+  &.left {
+    left: 0;
+  }
+  &.right {
+    right: 30px;
+  }
+  &.open {
+    bottom: 155px;
+
+    &.playing {
+      bottom: 175px;
+    }
+  }
+}
+
+.viewContainer,
+.rightViewContainer,
+#gui-modes-map {
+  display: flex;
+  align-items: center;
+  gap: 30px;
+  height: 100%;
+}
+
+.viewContainer {
+  padding-left: 25px;
+}
+
+#play,
+#pause {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+
+  img {
+    width: 42px;
+    height: 42px;
+  }
+}
+
+#pullTab,
+.good-btn,
+#hotList,
+#gui-modes-dollhouse,
+#gui-modes-floorplan,
+#gui-modes-inside,
+#gui-fullscreen,
+#gui-fullscreen-exit,
+#volume,
+#sharing {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+
+  .icon-slot {
+    width: 42px;
+    height: 42px;
+  }
+  &.active,
+  &.opened {
+    span {
+      color: #981c23;
+    }
+  }
+}
+
+#gui-fullscreen-exit {
+  .icon-slot {
+    background: url('@/assets/images/hljng/narrow_off.png') no-repeat center / contain;
+  }
+}
+
+#gui-fullscreen {
+  .icon-slot {
+    background: url('@/assets/images/hljng/enlarge_on.png') no-repeat center / contain;
+  }
+}
+
+#gui-modes-inside {
+  .icon-slot {
+    background: url('@/assets/images/hljng/inside.png') no-repeat center / contain;
+  }
+}
+
+#pullTab {
+  .icon-slot {
+    background: url('@/assets/images/hljng/auto-suspend.png') no-repeat center / contain;
+  }
+}
+
+#hotList {
+  .icon-slot {
+    background: url('@/assets/images/hljng/hotlist.png') no-repeat center / contain;
+  }
+}
+
+#gui-modes-dollhouse {
+  .icon-slot {
+    background: url('@/assets/images/hljng/dollhouse.png') no-repeat center / contain;
+  }
+}
+
+#gui-modes-floorplan {
+  .icon-slot {
+    background: url('@/assets/images/hljng/floor.png') no-repeat center / contain;
+  }
+}
+
+#volume {
+  .icon-slot {
+    background: url('@/assets/images/hljng/Volume btn_off.png') no-repeat center / contain;
+  }
+
+  &.active {
+    background-image: url('@/assets/images/hljng/Volume btn_on.png');
+  }
+}
+
+#sharing {
+  .icon-slot {
+    background: url('@/assets/images/hljng/share.png') no-repeat center / contain;
+  }
+}
+
+.terms2 {
+  display: none;
+}
+
+@media only screen and (max-width: 600px) {
+  $gap: 25px;
+
+  .pinBottom {
+    &-container {
+      bottom: 15px;
+    }
+    &.open {
+      bottom: 100px;
+
+      &.noScroll.playing {
+        bottom: 120px;
+      }
+    }
+    &.left {
+      .viewContainer {
+        flex-direction: column;
+        padding-left: 10px;
+        gap: $gap;
+      }
+    }
+    &.right {
+      right: 10px;
+
+      .rightViewContainer {
+        flex-direction: column;
+        gap: $gap;
+
+        #sharing,
+        #volume,
+        #viewer,
+        .good-btn {
+          .icon-slot {
+            width: 35px;
+            height: 35px;
+          }
+          span {
+            font-size: 12px;
+          }
+        }
+      }
+    }
+  }
+  #play,
+  #pause,
+  #gui-modes-map,
+  #gui-fullscreen,
+  .hotList {
+    .icon-slot,
+    img {
+      width: 35px;
+      height: 35px;
+    }
+    span {
+      font-size: 12px;
+    }
+  }
+  #gui-modes-map {
+    flex-direction: column;
+    gap: $gap;
+  }
+}

+ 157 - 0
src/index/views/home/components/menu/index.hljng.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="pinBottom-container">
+    <div class="pinBottom left">
+      <div class="viewContainer">
+        <div id="previous" class="previous desktop-only ui-icon" style="display: none">
+          <a>
+            <img :src="PauseIcon" width="24" height="24" data-original-title="播放" />
+          </a>
+        </div>
+        <div id="play" class="ui-icon" data-original-title="播放">
+          <img :src="PauseIcon" width="24" height="24" title="播放" />
+          <span>开始漫游</span>
+        </div>
+        <div id="pause" class="ui-icon" style="display: none">
+          <img title="暂停" :src="PlayIcon" width="24" height="24" />
+          <span>暂停漫游</span>
+        </div>
+        <div id="next" class="next desktop-only ui-icon wide" style="display: none">
+          <a>
+            <i title="" class="icon icon-dpad-right" data-original-title="下一个"></i>
+          </a>
+        </div>
+        <div id="gui-modes-map" class="ui-icon double active">
+          <div data-original-title="导览" id="pullTab" title="导览">
+            <div class="icon-slot" />
+            <span>导览列表</span>
+          </div>
+
+          <div id="hotList" title="热点列表" data-original-title="热点列表" style="display: none">
+            <div class="icon-slot" />
+            <span>热点列表</span>
+          </div>
+          <div data-original-title="全景漫游" id="gui-modes-inside" title="" class="">
+            <div class="icon-slot" />
+            <span>全景漫游</span>
+          </div>
+          <div data-original-title="迷你模型" id="gui-modes-dollhouse" title="迷你模型" class="">
+            <div class="icon-slot" />
+            <span>迷你模型</span>
+          </div>
+          <div data-original-title="俯视图" id="gui-modes-floorplan" title="俯视图">
+            <div class="icon-slot" />
+            <span>顶部视图</span>
+          </div>
+          <div data-original-title="VR" id="vr" title="" style="display: none"></div>
+          <div
+            data-original-title="消除外壳"
+            id="gui-remove-face"
+            title=""
+            style="display: none; float: left"
+          >
+            <img class="icon icon-inside" src="/images/face.jpg" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="pinBottom right hideTarget">
+      <div class="rightViewContainer clearfix">
+        <div id="sharing" class="ui-icon wide" title="分享" @click="shareVisible = true">
+          <div class="icon-slot" />
+          <span>分享</span>
+        </div>
+        <div id="volume" class="ui-icon wide" style="display: none">
+          <div class="icon-slot" />
+          <span>音乐</span>
+        </div>
+        <div id="vr" class="ui-icon wide hidden" style="display: none">
+          <a>
+            <i title="{[{ VIEW_IN_VR }]}" class="icon icon-webvr"></i>
+          </a>
+        </div>
+        <div
+          id="gui-fullscreen"
+          class="ui-icon wide"
+          data-placement="top"
+          title="{[{ VIEW_FULLSCREEN }]}"
+        >
+          <a>
+            <div class="icon-slot" />
+            <span>全屏</span>
+          </a>
+        </div>
+        <div
+          id="gui-fullscreen-exit"
+          class="ui-icon wide"
+          data-placement="top"
+          title="{[{ EXIT_FULLSCREEN }]}"
+          style="display: none"
+        >
+          <a>
+            <div class="icon-slot" />
+            <span>退出全屏</span>
+          </a>
+        </div>
+        <div class="pull-right terms terms2"></div>
+      </div>
+    </div>
+  </div>
+
+  <share-popup v-model:visible="shareVisible" />
+</template>
+
+<script setup lang="ts">
+  import { onMounted, onUnmounted, ref } from 'vue';
+  import PauseIcon from '@/assets/images/hljng/pause.png';
+  import PlayIcon from '@/assets/images/hljng/play.png';
+  import { homeApi } from '@/api';
+  import SharePopup from '../share-popup/index.vue';
+
+  let helperVisible = false;
+  const starSum = ref(0);
+  const shareVisible = ref(false);
+
+  const closeHelper = () => {
+    window.$('#interaction-modal').removeClass('fadeIn');
+    helperVisible = false;
+  };
+
+  const handleKeydown = () => {
+    helperVisible && closeHelper();
+  };
+  const handleClick = (e: MouseEvent | TouchEvent) => {
+    const clickedElement = e.target;
+    // @ts-ignore
+    const modalElement = clickedElement?.closest('#interaction-modal');
+    // @ts-ignore
+    const btnElement = clickedElement?.closest('.helper-btn');
+    if (!modalElement && !btnElement && helperVisible) {
+      closeHelper();
+    }
+  };
+
+  const getDetail = async () => {
+    const { data } = await homeApi.getSceneDetail(window.number);
+
+    if (!data) return;
+    starSum.value = data.starSum;
+  };
+
+  onMounted(() => {
+    getDetail();
+
+    window.addEventListener('keydown', handleKeydown);
+    window.addEventListener('click', handleClick);
+    window.addEventListener('touchmove', handleClick);
+  });
+
+  onUnmounted(() => {
+    window.removeEventListener('keydown', handleKeydown);
+    window.removeEventListener('click', handleClick);
+    window.removeEventListener('touchmove', handleClick);
+  });
+</script>
+
+<style lang="scss" scoped>
+  @use './index.hljng.scss';
+</style>

+ 1 - 1
src/index/views/home/components/share-popup/index.vue

@@ -138,7 +138,7 @@
       height: 433px;
 
       .el-dialog__header {
-        padding: 30px;
+        padding: 10px 30px;
       }
       .el-dialog__body {
         padding: 0 30px;

+ 190 - 0
src/index/views/home/components/title/index.hljng.scss

@@ -0,0 +1,190 @@
+.pinTop {
+  display: none;
+  position: fixed;
+  top: 16px;
+  left: 50%;
+  align-items: center;
+  flex-direction: column;
+  transition: all 0.5s;
+  line-height: 1;
+  transform: translateX(-50%);
+  z-index: var(--z-index-normal);
+}
+
+#model-title {
+  position: relative;
+  padding: 0 10px;
+  min-width: 180px;
+  height: 38px;
+  font-weight: 100;
+  transition: all 0.3s;
+  pointer-events: all;
+  border-radius: 10px;
+  word-wrap: break-word;
+  background: rgba(0, 0, 0, 0.2);
+
+  &:hover,
+  &.expand {
+    background: rgba(0, 0, 0, 0.5);
+  }
+}
+
+.title-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  white-space: nowrap;
+}
+
+#title-toggle {
+  display: none;
+}
+
+#title-container-wrapper {
+  width: 100%;
+  overflow: hidden;
+}
+
+.title-container {
+  position: relative;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  right: 0;
+  margin-left: 1px;
+  height: 38px;
+  width: 100%;
+  white-space: nowrap;
+  transition: all 0.4s ease-in-out;
+
+  &.expand {
+    #more-hint {
+      display: none;
+    }
+  }
+  &.meta-toggle {
+    cursor: pointer;
+  }
+  #more-hint,
+  &.expand #less-hint {
+    display: flex;
+    align-items: center;
+    padding-left: 12px;
+    height: 100%;
+    font-weight: 400;
+    font-size: 10px;
+  }
+  #less-hint {
+    display: none;
+  }
+  .icon-dpad-down {
+    position: relative;
+    display: block;
+    width: 0;
+    height: 0;
+    border-width: 8px 6px 0;
+    border-style: solid;
+    border-color: #fff transparent transparent;
+    margin: 40px 0;
+    transition: all 0.2s;
+  }
+  .icon-dpad-up {
+    display: block;
+    width: 0;
+    height: 0;
+    border-width: 0px 6px 8px;
+    border-style: solid;
+    border-color: transparent transparent #fff;
+    margin: 40px 0;
+    transition: all 0.2s;
+    position: relative;
+  }
+}
+
+#title-logo {
+  margin-right: 6px;
+
+  i {
+    display: block;
+    width: 18px;
+    height: 18px;
+    background: url('/images/4dage-logo.png') left top no-repeat;
+    background-size: 100%;
+  }
+}
+
+#gui-name {
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  height: 100%;
+  line-height: 18px;
+  font-size: 18px;
+}
+
+#meta-info-wrapper {
+  position: relative;
+  top: 14px;
+  width: 360px;
+  overflow: hidden;
+
+  &.expand {
+    pointer-events: auto;
+  }
+}
+
+#meta-info {
+  position: relative;
+  bottom: 100%;
+  width: 100%;
+  padding: 10px;
+  white-space: normal;
+  font-size: 14px;
+  font-weight: 400;
+  line-height: 19px;
+  color: #fff;
+  border-radius: 10px;
+  transition: all 0.4s ease-in-out;
+
+  * {
+    user-select: text;
+  }
+  &.expand {
+    bottom: 0;
+  }
+  #meta-description {
+    margin-bottom: 10px;
+    word-wrap: break-word;
+    text-align: justify;
+  }
+  .contact-info {
+    margin-bottom: 12px;
+  }
+  .address {
+    width: 100%;
+    white-space: nowrap;
+    overflow: hidden;
+  }
+  .menu-toggles {
+    font-size: 14px;
+    padding-top: 10px;
+  }
+}
+
+@media only screen and (max-width: 370px) {
+  #gui-name {
+    font-size: 16px;
+  }
+  #meta-info-wrapper {
+    width: 100vw;
+  }
+  #meta-info {
+    font-size: 12px;
+  }
+}
+
+@media only screen and (max-width: 330px) {
+  #gui-name {
+    font-size: 14px;
+  }
+}

+ 84 - 0
src/index/views/home/components/title/index.hljng.tsx

@@ -0,0 +1,84 @@
+import { defineComponent } from 'vue';
+import './index.hljng.scss';
+
+export default defineComponent({
+  name: 'HomeTitle',
+  render() {
+    return (
+      <div class="pinTop">
+        <div id="model-title">
+          <div class="title-row">
+            <div id="title-toggle">
+              <a>
+                <i class="icon icon-dpad-left"></i>
+              </a>
+            </div>
+            <div id="title-container-wrapper" data-placement="bottom" data-html="true">
+              <div class="title-container  meta-toggle">
+                <div class="co-brand">
+                  {'{[{ PRESENTED_BY }]}'}
+                  <span class="title" id="cobrandTitle"></span>
+                </div>
+                <div id="title-logo">
+                  <i></i>
+                </div>
+                <div id="gui-name" class="titleText"></div>
+                <a id="more-hint">
+                  <i class="icon icon-dpad-down"></i>
+                </a>
+                <a id="less-hint">
+                  <i class="icon icon-dpad-up"></i>
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div id="meta-info-wrapper">
+          <div id="meta-info" class="darkGlass">
+            <div id="meta-description"></div>
+            <div class="contact-info">
+              <i class="icon icon-user pull-left"></i>
+              &#xA0;<div id="contact-data"></div>
+            </div>
+            <div class="address">
+              <i class="icon icon-pin"></i>
+              <span id="addressTxt"></span>
+            </div>
+            <div id="tag-toggles" class="menu-toggles hidden">
+              <span>{'{[{ MATTERTAG_CONTENT }]}'}</span>
+              <div id="tag-inputs" class="menu-radios">
+                <div id="show-tag" class="menu-radio-show">
+                  <input id="radio-tag-show" type="radio" name="tags" value="show" />
+                  <label for="radio-tag-show">{'{[{ SHOW }]}'}</label>
+                </div>
+                <div id="hide-tag" class="menu-radio-hide">
+                  <input id="radio-tag-hide" type="radio" name="tags" value="hide" />
+                  <label for="radio-tag-hide">{'{[{ HIDE }]}'}</label>
+                </div>
+              </div>
+            </div>
+            <div id="labels-toggles" class="menu-toggles hidden">
+              <span>Labels</span>
+              <div id="labels-inputs" class="menu-radios">
+                <div id="show-label" class="menu-radio-show">
+                  <input id="radio-labels-show" type="radio" name="labels" value="show" />
+                  <label for="radio-labels-show">Show</label>
+                </div>
+                <div id="hide-label" class="menu-radio-hide">
+                  <input id="radio-labels-hide" type="radio" name="labels" value="hide" />
+                  <label for="radio-labels-hide">Hide</label>
+                </div>
+              </div>
+            </div>
+            <div id="share-origin" class="hidden">
+              <div>
+                <i class="icon icon-ext-link"></i>
+                <div id="share-link-wrapper"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    );
+  },
+});

+ 138 - 0
src/index/views/home/index.hljng.scss

@@ -0,0 +1,138 @@
+.home {
+  width: 100%;
+  height: 100%;
+
+  &__back {
+    position: fixed;
+    top: 30px;
+    left: 30px;
+    cursor: pointer;
+    z-index: var(--z-index-top);
+  }
+  &_logo {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    left: 50%;
+    bottom: 20px;
+    width: 300px;
+    text-align: center;
+    font-size: 14px;
+    transform: translateX(-50%);
+    color: rgba(255, 255, 255, 0.8);
+
+    img {
+      width: 50%;
+    }
+    span {
+      font-size: 16px;
+      padding: 5px 0;
+      color: rgba(255, 255, 255, 0.8);
+      border-bottom: 1px solid rgba(255, 255, 255, 0.8);
+    }
+  }
+  &__point {
+    position: absolute;
+    top: 30px;
+    right: 30px;
+    width: 50px;
+    height: 50px;
+    cursor: pointer;
+    z-index: 1;
+  }
+}
+
+#player {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+
+  canvas {
+    position: relative;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+  }
+}
+
+#hot {
+  position: absolute;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+
+  > div[pos='right'] {
+    transform: translate(20px, -50%);
+  }
+  > div[pos='top'] {
+    transform: translate(-50%, calc(-100% - 20px));
+  }
+  > div[pos='middle'] {
+    transform: translate(-50%, -50%);
+  }
+  > div[pos='bottom'] {
+    transform: translate(-50%, 20px);
+  }
+  > div[pos='left'] {
+    transform: translate(calc(-100% - 20px), -50%);
+  }
+  > div {
+    position: absolute;
+    color: #fff;
+    user-select: none;
+    border-radius: 5px;
+    background-color: rgba(34, 34, 34, 0.3);
+    padding: 10px;
+    max-width: 400px;
+    letter-spacing: 1px;
+    line-height: 20px;
+    z-index: var(--z-index-top);
+  }
+}
+
+div.cad {
+  top: 44px;
+  right: 30px;
+  width: 150px;
+  height: 150px;
+  border: 1px solid #c39c6b;
+  background: rgba(111, 15, 13, 0.6);
+
+  path {
+    stroke-width: 1;
+    stroke: #c39c6b;
+    fill: RGBA(157, 34, 45, 0.7);
+  }
+  circle {
+    fill: #9d222d;
+  }
+}
+
+@media only screen and (max-width: 600px) {
+  .home {
+    &__back {
+      top: 12px;
+      left: 12px;
+      width: 35px;
+      height: 35px;
+      z-index: 1001;
+    }
+    &__point {
+      top: 100px;
+      right: 7px;
+      width: 37px;
+      height: 37px;
+    }
+  }
+  div.cad {
+    top: 20px;
+    right: 8px;
+    width: 90px;
+    height: 90px;
+  }
+}

+ 120 - 0
src/index/views/home/index.hljng.tsx

@@ -0,0 +1,120 @@
+import { defineComponent, ref } from 'vue';
+import JsScript from '@/components/js-script';
+import Title from './components/title';
+import WebVr from './components/web-vr';
+import Other from './components/other';
+import Guide from './components/guide';
+import Vrcon from './components/vrcon';
+import Menu from './components/menu';
+import GuiLoading from './components/gui-loading';
+import Popup from './components/popup';
+import HotSpotList from './components/hot-spot-list';
+import useBaseStore from '@/store/module/base';
+import BackIcon from '@/assets/images/hljng/back.png';
+import './index.hljng.scss';
+
+export default defineComponent({
+  name: 'home',
+  components: {
+    Title,
+    WebVr,
+    Other,
+    Vrcon,
+    GuiLoading,
+    JsScript,
+    Popup,
+  },
+  setup() {
+    const baseStore = useBaseStore();
+    const manageJsLoaded = ref(false);
+    const hotJsLoaded = ref(false);
+
+    return {
+      manageJsLoaded,
+      hotJsLoaded,
+      baseStore,
+    };
+  },
+  render() {
+    return (
+      <div class="home">
+        <img
+          class="home__back"
+          src={BackIcon}
+          onClick={() => {
+            window.location.href = `https://houseoss.4dkankan.com/project/helong-memorial-hall/${
+              window.browser.isMobile() ? 'mobile' : 'pc'
+            }/index.html`;
+          }}
+        />
+
+        {/* 进度条加载 */}
+        <GuiLoading />
+
+        {/* 加载初始页面 */}
+        <div id="gui-thumb" />
+
+        {/* 热点弹出框 */}
+        <Popup />
+
+        {/* 热点列表 */}
+        <HotSpotList />
+
+        {/* 场景canvs主容器 */}
+        <div id="player" />
+
+        {/* 底部菜单 */}
+        <div id="gui-parent">
+          {/* 热点气泡 */}
+          <div id="hot" />
+
+          <div id="gui" style="display: none;">
+            {/* 标题 */}
+            {/* @ts-ignore */}
+            <Title isInit={this.manageJsLoaded} />
+
+            {/* 底部菜单 */}
+            <Menu />
+
+            {/* 导览 */}
+            <Guide />
+          </div>
+
+          <WebVr />
+          <Vrcon />
+          <Other />
+        </div>
+
+        {/* TODO: 没有控制权,耦合严重;放在此处为了防止元素未渲染导致报错 */}
+        <JsScript
+          src="./js/manage.js"
+          onLoad={() => {
+            this.manageJsLoaded = true;
+            this.baseStore.handleJSLoaded('manageJsLoaded');
+          }}
+        />
+        {this.manageJsLoaded && (
+          <div>
+            <JsScript
+              src="./js/Hot.js"
+              onLoad={() => {
+                this.hotJsLoaded = true;
+                this.baseStore.handleJSLoaded('hotJsLoaded');
+              }}
+            />
+            {this.hotJsLoaded && (
+              <div>
+                <JsScript src="./js/main_2020_show.js" />
+                {/* 延迟加载 */}
+                <JsScript src="./js/lib/player-0.0.12.min.js" />
+                <JsScript src="./js/lib/Tween.js" />
+                <JsScript src="./js/SpecialScene.js" />
+                <JsScript src="./js/loadCAD.js" />
+              </div>
+            )}
+          </div>
+        )}
+      </div>
+    );
+  },
+});