فهرست منبع

Merge branch 'zgrs' into syjy

chenlei 8 ماه پیش
والد
کامیت
5ecdcfc252
77فایلهای تغییر یافته به همراه1837 افزوده شده و 241 حذف شده
  1. 2 0
      .eslintrc.js
  2. 2 1
      auto-imports.d.ts
  3. 8 0
      components.d.ts
  4. BIN
      hotspot/assets/images/Volume-off.png
  5. BIN
      hotspot/assets/images/Volume-on.png
  6. BIN
      hotspot/assets/images/icon-image-1@2x.png
  7. BIN
      hotspot/assets/images/icon-image@2x.png
  8. BIN
      hotspot/assets/images/icon-model-1@2x.png
  9. BIN
      hotspot/assets/images/icon-model@2x.png
  10. BIN
      hotspot/assets/images/icon-next@2x-min.png
  11. BIN
      hotspot/assets/images/icon-previous@2x-min.png
  12. BIN
      hotspot/assets/images/icon-video-1@2x.png
  13. BIN
      hotspot/assets/images/icon-video@2x.png
  14. 1 0
      hotspot/main.ts
  15. 78 163
      hotspot/views/hotspot/index.scss
  16. 56 44
      hotspot/views/hotspot/index.vue
  17. 5 0
      package.json
  18. 49 0
      pnpm-lock.yaml
  19. BIN
      public/favicon/favicon-zgrs.ico
  20. BIN
      public/images/zgrs/point.png
  21. BIN
      public/images/zgrs/point2.png
  22. 6 1
      public/js/main_2020_show.js
  23. 16 1
      src/api/home.ts
  24. 1 1
      src/app.scss
  25. BIN
      src/assets/images/zgrs/Subtract-min.png
  26. BIN
      src/assets/images/zgrs/Volume btn_off-m.png
  27. BIN
      src/assets/images/zgrs/Volume btn_off.png
  28. BIN
      src/assets/images/zgrs/Volume btn_on-m.png
  29. BIN
      src/assets/images/zgrs/Volume btn_on.png
  30. BIN
      src/assets/images/zgrs/active-menu-2.png
  31. BIN
      src/assets/images/zgrs/active-menu.png
  32. BIN
      src/assets/images/zgrs/auto-suspend.png
  33. BIN
      src/assets/images/zgrs/auto.png
  34. BIN
      src/assets/images/zgrs/back.png
  35. BIN
      src/assets/images/zgrs/close.png
  36. BIN
      src/assets/images/zgrs/dollhouse-active.png
  37. BIN
      src/assets/images/zgrs/dollhouse.png
  38. BIN
      src/assets/images/zgrs/floor-active.png
  39. BIN
      src/assets/images/zgrs/floor.png
  40. BIN
      src/assets/images/zgrs/good-active-m.png
  41. BIN
      src/assets/images/zgrs/good-active.png
  42. BIN
      src/assets/images/zgrs/good-m.png
  43. BIN
      src/assets/images/zgrs/good.png
  44. BIN
      src/assets/images/zgrs/helper-m.png
  45. BIN
      src/assets/images/zgrs/helper.png
  46. BIN
      src/assets/images/zgrs/hotlist-active.png
  47. BIN
      src/assets/images/zgrs/hotlist.png
  48. BIN
      src/assets/images/zgrs/pause.png
  49. BIN
      src/assets/images/zgrs/play.png
  50. BIN
      src/assets/images/zgrs/share-m.png
  51. BIN
      src/assets/images/zgrs/share.png
  52. BIN
      src/assets/images/zgrs/title-bg-m.png
  53. BIN
      src/assets/images/zgrs/title-bg.png
  54. BIN
      src/assets/images/zgrs/title-down.png
  55. BIN
      src/assets/images/zgrs/title-right.png
  56. BIN
      src/assets/images/zgrs/title-up.png
  57. 5 0
      src/auto-imports.d.ts
  58. 0 27
      src/el.d.ts
  59. 4 0
      src/global.d.ts
  60. 8 0
      src/shims-vue.d.ts
  61. 7 0
      src/types/home.ts
  62. 1 0
      src/utils/services.ts
  63. 215 0
      src/views/home/components/guide/index.zgrs.scss
  64. 26 0
      src/views/home/components/guide/index.zgrs.tsx
  65. 47 0
      src/views/home/components/hot-spot-list/index.zgrs.scss
  66. 13 0
      src/views/home/components/hot-spot-list/index.zgrs.vue
  67. 238 0
      src/views/home/components/menu/index.zgrs.scss
  68. 209 0
      src/views/home/components/menu/index.zgrs.vue
  69. 60 0
      src/views/home/components/other/index.scss
  70. 5 2
      src/views/home/components/other/index.tsx
  71. 36 0
      src/views/home/components/popup/index.zgrs.scss
  72. 14 0
      src/views/home/components/popup/index.zgrs.tsx
  73. 56 0
      src/views/home/components/title/index.zgrs.scss
  74. 457 0
      src/views/home/components/title/index.zgrs.vue
  75. 99 0
      src/views/home/index.zgrs.scss
  76. 105 0
      src/views/home/index.zgrs.tsx
  77. 8 1
      tsconfig.json

+ 2 - 0
.eslintrc.js

@@ -8,6 +8,7 @@ module.exports = {
     'eslint:recommended',
     '@vue/typescript/recommended',
     'plugin:prettier/recommended',
+    './auto-imports.d.ts',
   ],
   parserOptions: {
     ecmaVersion: 2020,
@@ -16,5 +17,6 @@ module.exports = {
     'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
     'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
     'vue/multi-word-component-names': 'off',
+    '@typescript-eslint/ban-ts-comment': 'off',
   },
 };

+ 2 - 1
auto-imports.d.ts

@@ -1,5 +1,6 @@
 // Generated by 'unplugin-auto-import'
 export {}
 declare global {
-
+  const ElMessage: typeof import('element-plus/es')['ElMessage']
+  const ElNotification: typeof import('element-plus/es')['ElNotification']
 }

+ 8 - 0
components.d.ts

@@ -7,6 +7,14 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
+    ElCollapse: typeof import('element-plus/es')['ElCollapse']
+    ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
+    ElDropdown: typeof import('element-plus/es')['ElDropdown']
+    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
+    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
+    ElImage: typeof import('element-plus/es')['ElImage']
+    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
+    ElTooltip: typeof import('element-plus/es')['ElTooltip']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
   }

BIN
hotspot/assets/images/Volume-off.png


BIN
hotspot/assets/images/Volume-on.png


BIN
hotspot/assets/images/icon-image-1@2x.png


BIN
hotspot/assets/images/icon-image@2x.png


BIN
hotspot/assets/images/icon-model-1@2x.png


BIN
hotspot/assets/images/icon-model@2x.png


BIN
hotspot/assets/images/icon-next@2x-min.png


BIN
hotspot/assets/images/icon-previous@2x-min.png


BIN
hotspot/assets/images/icon-video-1@2x.png


BIN
hotspot/assets/images/icon-video@2x.png


+ 1 - 0
hotspot/main.ts

@@ -1,5 +1,6 @@
 import { createApp } from 'vue';
 import App from './views/hotspot/index.vue';
+import '@/app.scss';
 
 export const app = createApp(App);
 

+ 78 - 163
hotspot/views/hotspot/index.scss

@@ -4,70 +4,66 @@
   left: 0;
   right: 0;
   bottom: 0;
-  background: rgba(255, 252, 247, 0.8);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 22px 0 71px;
+  background: rgba(0, 0, 0, 0.8);
   z-index: var(--z-index-popper);
 
   .audioIcon {
     position: absolute;
-    top: 40px;
-    right: 128px;
+    right: 20px;
+    bottom: 5px;
 
     img {
-      width: 52px;
-      height: 52px;
+      width: 57px;
+      height: 57px;
       cursor: pointer;
     }
   }
   &-info {
-    position: absolute;
-    top: 42px;
-    left: 40px;
-    max-width: 50%;
+    color: white;
+    max-width: 1320px;
+    width: calc(100vw - 30vw);
 
     h3 {
-      margin-bottom: 14px;
-      color: #c41800;
-      font-size: 24px;
-      line-height: 28px;
+      margin-bottom: 18px;
+      font-size: 16px;
+      font-weight: bold;
+      text-align: center;
     }
     p {
-      width: 214px;
-      height: 213px;
-      color: #333333;
-      font-size: 14px;
-      overflow-y: auto;
-
-      &::-webkit-scrollbar {
-        width: 6px;
-      }
-      &::-webkit-scrollbar-thumb {
-        border-radius: 10px;
-        background-color: #c41800;
-      }
-      &::-webkit-scrollbar-track {
-        border-radius: 10px;
-      }
+      margin: 0 auto;
+      width: 80%;
+      font-size: 12px;
     }
   }
 
-  &-main {
-    position: absolute;
-    top: 50%;
-    left: 50%;
+  &-container {
+    position: relative;
+    flex: 1;
+    flex-shrink: 1;
+    height: 0;
     display: flex;
+    flex-direction: column;
     align-items: center;
-    justify-content: center;
-    max-width: 1100px;
-    width: calc(100vw - 548px);
-    transform: translate(-50%, -50%);
+    padding: 9vh 20px;
+    max-width: 1320px;
+    min-width: 400px;
+    width: auto;
+    background: rgba(53, 53, 53, 1);
+    box-sizing: border-box;
   }
+
   &-swiper {
     &__left,
     &__right {
       position: absolute;
       top: 50%;
-      width: 50px;
-      height: 50px;
+      width: 25px;
+      height: 24px;
       cursor: pointer;
       transform: translateY(-50%);
       z-index: 1;
@@ -83,17 +79,21 @@
   }
   &-model {
     width: 100%;
-    height: 600px;
+    height: 100%;
 
     iframe {
       width: 100%;
       height: 100%;
     }
   }
+  .swiper-slide {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
   &-video {
-    width: 100%;
-    height: 561px;
-    object-fit: cover;
+    height: 100%;
+    max-height: 100%;
   }
   &-img {
     display: flex;
@@ -102,22 +102,19 @@
     height: inherit;
 
     &-swiper {
-      height: 567px;
-    }
-    img {
+      flex: 1;
       width: 100%;
-      height: 100%;
-      object-fit: contain;
+      height: 0;
     }
   }
 
   &-nav {
     position: absolute;
     left: 50%;
-    bottom: 104px;
+    bottom: 5px;
     display: flex;
     align-items: center;
-    gap: 30px;
+    gap: 10px;
     transform: translateX(-50%);
 
     &__item {
@@ -125,31 +122,16 @@
       align-items: center;
       justify-content: center;
       gap: 9px;
-      width: 120px;
-      height: 36px;
-      border-radius: 18px;
-      color: #666666;
-      background: #d0cec6;
-      border: 1px solid #666666;
+      width: 57px;
+      height: 57px;
       box-sizing: border-box;
       cursor: pointer;
 
       &.active {
-        color: #ddaf35;
-        border-width: 0;
-        background: linear-gradient(#d67729, #c41800);
-      }
-      .model-icon {
-        width: 19px;
-        height: 22px;
       }
-      .video-icon {
-        width: 21px;
-        height: 18px;
-      }
-      .img-icon {
-        width: 21px;
-        height: 18px;
+      img {
+        width: 100%;
+        height: 100%;
       }
     }
   }
@@ -157,108 +139,41 @@
 
 @media only screen and (max-width: 600px) {
   .hotspot-page {
-    .audioIcon {
-      top: 20px;
-      right: 60px;
-
-      img {
-        width: 35px;
-        height: 35px;
-      }
-    }
-  }
-  .hotspot-page-info {
-    top: unset;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    display: flex;
-    flex-direction: column;
-    padding: 20px 25px;
-    max-width: 100%;
-    height: calc(100vh - 50vh - 95px);
-    box-sizing: border-box;
-    border-top-left-radius: 17px;
-    border-top-right-radius: 17px;
-    background: linear-gradient(181deg, #d67729 0%, #c41800 100%);
-
-    h3 {
-      margin-bottom: 7px;
+    &-container {
       max-width: 100%;
       width: 100%;
-      color: white;
-      font-size: 18px;
-      font-weight: bold;
-      display: -webkit-box;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      -webkit-line-clamp: 1;
-      -webkit-box-orient: vertical;
-      word-break: break-all;
-      word-wrap: break-word;
+      background: none;
     }
-    p {
-      flex: 1;
-      padding-right: 2px;
-      width: 100%;
-      height: 0;
-      color: white;
-      font-size: 15px;
-
-      &::-webkit-scrollbar {
-        width: 2px;
+    &-swiper {
+      &__left,
+      &__right {
+        top: unset;
+        bottom: 12px;
+        transform: none;
+      }
+      &__left {
+        left: 14px;
       }
-      &::-webkit-scrollbar-thumb {
-        background-color: #ddaf35;
+      &__right {
+        right: 14px;
       }
     }
-  }
-  .hotspot-page-nav {
-    bottom: 194px;
-    gap: 13px;
+    &-nav {
+      bottom: 0;
 
-    &__item {
-      width: 70px;
-      height: 20px;
-      font-size: 10px;
-      gap: 4px;
-
-      .video-icon,
-      .img-icon {
-        width: 12px;
-        height: 10px;
-      }
-      .model-icon {
-        width: 10px;
-        height: 11px;
+      &__item {
+        width: 48px;
+        height: 48px;
       }
     }
-  }
-  .hotspot-page-swiper__left,
-  .hotspot-page-swiper__right {
-    width: 19px;
-    height: 38px;
-  }
-  .hotspot-page-swiper__left {
-    left: 0;
-    background-image: url('@hotspot/assets/images/icon-left-min.png');
-  }
-  .hotspot-page-swiper__right {
-    right: 0;
-    background-image: url('@hotspot/assets/images/icon-right-min.png');
-  }
-  .hotspot-page-main {
-    top: 75px;
-    width: 100%;
-    transform: translateX(-50%);
-  }
+    .audioIcon {
+      right: 35px;
+      bottom: 1px;
 
-  .hotspot-page-video {
-    width: 300px;
-    height: 168px;
-  }
-  .hotspot-page-img-swiper,
-  .hotspot-page-model {
-    height: 50vh;
+      img {
+        width: 48px;
+        height: 48px;
+      }
+    }
   }
 }

+ 56 - 44
hotspot/views/hotspot/index.vue

@@ -1,21 +1,6 @@
 <template>
   <div class="hotspot-page">
-    <div class="hotspot-page-info">
-      <h3>{{ myTitle }}</h3>
-      <p>{{ myTxt }}</p>
-    </div>
-
-    <!-- 音频图标 -->
-    <div
-      v-if="audio && !isOneAduio"
-      class="audioIcon"
-      :title="audioSta ? '关闭音频' : '打开音频'"
-      @click="audioSta = !audioSta"
-    >
-      <img :src="audioSta ? VolumeOff : VolumeOn" alt="" />
-    </div>
-
-    <div class="hotspot-page-main">
+    <div class="hotspot-page-container">
       <!-- 音频播放器 -->
       <audio
         id="myAudio"
@@ -39,13 +24,8 @@
       </Swiper>
 
       <!-- 视频页面 -->
-      <Swiper
-        v-if="myType === 'video'"
-        class="hotspot-page-swiper hotspot-page-video"
-        @swiper="initSwiper"
-        @slideChange="handleChange"
-      >
-        <SwiperSlide v-for="(item, index) in curList" :key="item.url">
+      <div v-if="myType === 'video'" class="hotspot-page-swiper hotspot-page-video">
+        <template v-for="(item, index) in curList" :key="item.url">
           <video
             v-if="index === myInd"
             id="videoID"
@@ -54,8 +34,8 @@
             :src="item.url"
             autoplay
           />
-        </SwiperSlide>
-      </Swiper>
+        </template>
+      </div>
 
       <!-- 图片页面 -->
       <Swiper
@@ -64,9 +44,16 @@
         @swiper="initSwiper"
         @slideChange="handleChange"
       >
-        <SwiperSlide v-for="item in curList" :key="item">
+        <SwiperSlide v-for="(item, idx) in curList" :key="item">
           <div class="hotspot-page-img">
-            <img :src="item" alt="" />
+            <el-image
+              :src="item"
+              fit="contain"
+              style="width: 100%; height: 100%"
+              preview-teleported
+              :preview-src-list="curList"
+              :initial-index="idx"
+            />
           </div>
         </SwiperSlide>
       </Swiper>
@@ -75,26 +62,43 @@
         <div class="hotspot-page-swiper__left" @click="handlePre" />
         <div class="hotspot-page-swiper__right" @click="handleNext" />
       </template>
-    </div>
 
-    <!-- 底部的tab -->
-    <div v-if="flooTab.length > 1" class="hotspot-page-nav">
+      <!-- 底部的tab -->
+      <div v-if="flooTab.length > 1" class="hotspot-page-nav">
+        <div
+          v-for="item in flooTab"
+          :key="item.id"
+          :class="[
+            'hotspot-page-nav__item',
+            {
+              active: myType === item.type,
+            },
+          ]"
+          @click="handleTab(item)"
+        >
+          <img :class="`${item.type}-icon`" :src="myType === item.type ? item.acIcon : item.icon" />
+          <!-- {{ item.name }}
+          {{ item.type === 'img' ? `${myInd + 1}/${data.img.length}` : '' }} -->
+        </div>
+      </div>
+
+      <!-- 音频图标 -->
       <div
-        v-for="item in flooTab"
-        :key="item.id"
-        :class="[
-          'hotspot-page-nav__item',
-          {
-            active: myType === item.type,
-          },
-        ]"
-        @click="handleTab(item)"
+        v-if="audio && !isOneAduio"
+        class="audioIcon"
+        :title="audioSta ? '关闭音频' : '打开音频'"
+        @click="audioSta = !audioSta"
       >
-        <img :class="`${item.type}-icon`" :src="myType === item.type ? item.acIcon : item.icon" />
-        {{ item.name }}
-        {{ item.type === 'img' ? `${myInd + 1}/${data.img.length}` : '' }}
+        <img :src="audioSta ? VolumeOff : VolumeOn" alt="" />
       </div>
     </div>
+
+    <el-scrollbar :height="150" style="margin-top: 20px; height: 150px; flex-shrink: 0">
+      <div class="hotspot-page-info">
+        <h3>{{ myTitle }}</h3>
+        <p>{{ myTxt }}</p>
+      </div>
+    </el-scrollbar>
   </div>
 </template>
 
@@ -252,10 +256,18 @@
         this.myInd = activeIndex;
       },
       handlePre() {
-        this.swiper?.slidePrev();
+        if (this.myType === 'video') {
+          this.myInd = this.myInd > 0 ? this.myInd - 1 : this.curList.length - 1;
+        } else {
+          this.swiper?.slidePrev();
+        }
       },
       handleNext() {
-        this.swiper?.slideNext();
+        if (this.myType === 'video') {
+          this.myInd = this.myInd < this.curList.length - 1 ? this.myInd + 1 : 0;
+        } else {
+          this.swiper?.slideNext();
+        }
       },
     },
   };

+ 5 - 0
package.json

@@ -6,11 +6,15 @@
     "serve": "cross-env TITLE=大理洱海科普馆 HOT_DOMAIN=/hotspot.html vue-cli-service serve",
     "build:test": "cross-env TITLE=大理洱海科普馆 vue-cli-service build",
     "push:test": "cross-env node ./scripts/publish.js",
+    "serve:zgrs": "cross-env SCENE=zgrs TITLE=光大汇晨养老 HOT_DOMAIN=/hotspot.html vue-cli-service serve",
+    "build:zgrs:test": "cross-env SCENE=zgrs TITLE=光大汇晨养老 HOT_DOMAIN=/zgrs-hotspot.html vue-cli-service build",
+    "push:zgrs": "cross-env SCENE=zgrs node ./scripts/publish.js",
     "lint": "vue-cli-service lint",
     "prepare": "husky install"
   },
   "dependencies": {
     "axios": "^1.4.0",
+    "clipboard": "^2.0.11",
     "core-js": "^3.8.3",
     "element-plus": "^2.2.12",
     "lodash": "^4.17.21",
@@ -24,6 +28,7 @@
   "devDependencies": {
     "@commitlint/config-conventional": "^17.1.0",
     "@types/jest": "^27.0.1",
+    "@types/jquery": "^3.5.30",
     "@types/lodash": "^4.14.185",
     "@types/webpack-env": "^1.18.0",
     "@typescript-eslint/eslint-plugin": "^5.4.0",

+ 49 - 0
pnpm-lock.yaml

@@ -11,6 +11,9 @@ importers:
       axios:
         specifier: ^1.4.0
         version: 1.7.5
+      clipboard:
+        specifier: ^2.0.11
+        version: 2.0.11
       core-js:
         specifier: ^3.8.3
         version: 3.38.1
@@ -45,6 +48,9 @@ importers:
       '@types/jest':
         specifier: ^27.0.1
         version: 27.5.2
+      '@types/jquery':
+        specifier: ^3.5.30
+        version: 3.5.30
       '@types/lodash':
         specifier: ^4.14.185
         version: 4.17.7
@@ -1163,6 +1169,9 @@ packages:
   '@types/jest@27.5.2':
     resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==}
 
+  '@types/jquery@3.5.30':
+    resolution: {integrity: sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==}
+
   '@types/json-schema@7.0.15':
     resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
 
@@ -1217,6 +1226,9 @@ packages:
   '@types/serve-static@1.15.7':
     resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==}
 
+  '@types/sizzle@2.3.8':
+    resolution: {integrity: sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==}
+
   '@types/sockjs@0.3.36':
     resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==}
 
@@ -2063,6 +2075,9 @@ packages:
     resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
     engines: {node: '>=8'}
 
+  clipboard@2.0.11:
+    resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==}
+
   clipboardy@2.3.0:
     resolution: {integrity: sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==}
     engines: {node: '>=8'}
@@ -2627,6 +2642,9 @@ packages:
     resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
     engines: {node: '>=0.4.0'}
 
+  delegate@3.2.0:
+    resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
+
   depd@1.1.2:
     resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
     engines: {node: '>= 0.6'}
@@ -3197,6 +3215,9 @@ packages:
     resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
     engines: {node: '>=10'}
 
+  good-listener@1.2.2:
+    resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==}
+
   gopd@1.0.1:
     resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
 
@@ -5027,6 +5048,9 @@ packages:
   select-hose@2.0.0:
     resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==}
 
+  select@1.1.2:
+    resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==}
+
   selfsigned@2.4.1:
     resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==}
     engines: {node: '>=10'}
@@ -5424,6 +5448,9 @@ packages:
     resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
     engines: {node: '>=0.6.0'}
 
+  tiny-emitter@2.1.0:
+    resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
+
   tmpl@1.0.5:
     resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
 
@@ -7377,6 +7404,10 @@ snapshots:
       jest-matcher-utils: 27.5.1
       pretty-format: 27.5.1
 
+  '@types/jquery@3.5.30':
+    dependencies:
+      '@types/sizzle': 2.3.8
+
   '@types/json-schema@7.0.15': {}
 
   '@types/lodash-es@4.17.12':
@@ -7428,6 +7459,8 @@ snapshots:
       '@types/node': 22.5.1
       '@types/send': 0.17.4
 
+  '@types/sizzle@2.3.8': {}
+
   '@types/sockjs@0.3.36':
     dependencies:
       '@types/node': 22.5.1
@@ -8702,6 +8735,12 @@ snapshots:
       slice-ansi: 3.0.0
       string-width: 4.2.3
 
+  clipboard@2.0.11:
+    dependencies:
+      good-listener: 1.2.2
+      select: 1.1.2
+      tiny-emitter: 2.1.0
+
   clipboardy@2.3.0:
     dependencies:
       arch: 2.2.0
@@ -9137,6 +9176,8 @@ snapshots:
 
   delayed-stream@1.0.0: {}
 
+  delegate@3.2.0: {}
+
   depd@1.1.2: {}
 
   depd@2.0.0: {}
@@ -9797,6 +9838,10 @@ snapshots:
       merge2: 1.4.1
       slash: 3.0.0
 
+  good-listener@1.2.2:
+    dependencies:
+      delegate: 3.2.0
+
   gopd@1.0.1:
     dependencies:
       get-intrinsic: 1.2.4
@@ -11842,6 +11887,8 @@ snapshots:
 
   select-hose@2.0.0: {}
 
+  select@1.1.2: {}
+
   selfsigned@2.4.1:
     dependencies:
       '@types/node-forge': 1.3.11
@@ -12259,6 +12306,8 @@ snapshots:
     dependencies:
       setimmediate: 1.0.5
 
+  tiny-emitter@2.1.0: {}
+
   tmpl@1.0.5: {}
 
   to-fast-properties@2.0.0: {}

BIN
public/favicon/favicon-zgrs.ico


BIN
public/images/zgrs/point.png


BIN
public/images/zgrs/point2.png


+ 6 - 1
public/js/main_2020_show.js

@@ -16893,7 +16893,12 @@ window.Modernizr = (function (n, e, t) {
             }
 
             //czj 判断someData 有没有hoticon字段 修改热点的样式
-            if (window.DATA.hoticon) {
+            if (window.hoticon) {
+              g_HotImage = {
+                point: window.hoticon.default,
+                point2: window.hoticon.higt
+              }
+            } else if (window.DATA.hoticon) {
               g_HotImage = {
                 point:
                   window.DATA.hoticon.default || 'https://super.4dage.com/images/4dagePoint2.png',

+ 16 - 1
src/api/home.ts

@@ -1,10 +1,25 @@
-import { GetHotSpotListResponse } from '@/types/home';
+import { GetHotSpotListResponse, GetSceneDetailResponse } from '@/types/home';
 import service from '@/utils/services';
 
+const SCENE_BASE_URL = 'https://count.4dage.com/api';
+
 export const homeApi = {
   getHotSpotList() {
     return service.get<GetHotSpotListResponse>(
       `${window.g_Prefix}/data/${window.number}/hot/js/data.js`
     );
   },
+
+  getSceneDetail(sceneCode: string) {
+    return service.get<GetSceneDetailResponse | null>(
+      `${SCENE_BASE_URL}/count/detail/${sceneCode}`
+    );
+  },
+
+  /**
+   * 点赞
+   */
+  saveStar(sceneCode: string) {
+    return service.get(`${SCENE_BASE_URL}/count/saveStar/${sceneCode}`);
+  },
 };

+ 1 - 1
src/app.scss

@@ -2,7 +2,7 @@
   --z-index-normal: 1;
   --z-index-top: 1000;
   --z-index-popper: 2000;
-  --z-hot-popper: 2001;
+  --z-hot-popper: 3000;
 }
 
 body,

BIN
src/assets/images/zgrs/Subtract-min.png


BIN
src/assets/images/zgrs/Volume btn_off-m.png


BIN
src/assets/images/zgrs/Volume btn_off.png


BIN
src/assets/images/zgrs/Volume btn_on-m.png


BIN
src/assets/images/zgrs/Volume btn_on.png


BIN
src/assets/images/zgrs/active-menu-2.png


BIN
src/assets/images/zgrs/active-menu.png


BIN
src/assets/images/zgrs/auto-suspend.png


BIN
src/assets/images/zgrs/auto.png


BIN
src/assets/images/zgrs/back.png


BIN
src/assets/images/zgrs/close.png


BIN
src/assets/images/zgrs/dollhouse-active.png


BIN
src/assets/images/zgrs/dollhouse.png


BIN
src/assets/images/zgrs/floor-active.png


BIN
src/assets/images/zgrs/floor.png


BIN
src/assets/images/zgrs/good-active-m.png


BIN
src/assets/images/zgrs/good-active.png


BIN
src/assets/images/zgrs/good-m.png


BIN
src/assets/images/zgrs/good.png


BIN
src/assets/images/zgrs/helper-m.png


BIN
src/assets/images/zgrs/helper.png


BIN
src/assets/images/zgrs/hotlist-active.png


BIN
src/assets/images/zgrs/hotlist.png


BIN
src/assets/images/zgrs/pause.png


BIN
src/assets/images/zgrs/play.png


BIN
src/assets/images/zgrs/share-m.png


BIN
src/assets/images/zgrs/share.png


BIN
src/assets/images/zgrs/title-bg-m.png


BIN
src/assets/images/zgrs/title-bg.png


BIN
src/assets/images/zgrs/title-down.png


BIN
src/assets/images/zgrs/title-right.png


BIN
src/assets/images/zgrs/title-up.png


+ 5 - 0
src/auto-imports.d.ts

@@ -0,0 +1,5 @@
+// Generated by 'unplugin-auto-import'
+export {}
+declare global {
+
+}

+ 0 - 27
src/el.d.ts

@@ -1,27 +0,0 @@
-import type {
-  ElMessageBoxOptions,
-  MessageBoxData,
-  MessageHandler,
-  MessageParams,
-} from 'element-plus';
-import type { AppContext } from 'vue';
-
-declare global {
-  declare namespace ElMessageBox {
-    function confirm(
-      message: ElMessageBoxOptions['message'],
-      options?: ElMessageBoxOptions,
-      appContext?: AppContext | null
-    ): Promise<MessageBoxData>;
-    function confirm(
-      message: ElMessageBoxOptions['message'],
-      title: ElMessageBoxOptions['title'],
-      options?: ElMessageBoxOptions,
-      appContext?: AppContext | null
-    ): Promise<MessageBoxData>;
-  }
-  declare function ElMessage(
-    options?: MessageParams,
-    appContext?: null | AppContext
-  ): MessageHandler;
-}

+ 4 - 0
src/global.d.ts

@@ -7,4 +7,8 @@ interface Window {
   navigationStart: number;
   /** 获取热点 iframe 路径 */
   getHotIframePath: (str: string) => string;
+  $: JQuery;
+  browser: {
+    isMobile(): boolean;
+  };
 }

+ 8 - 0
src/shims-vue.d.ts

@@ -4,3 +4,11 @@ declare module '*.vue' {
   const component: DefineComponent<{}, {}, any>;
   export default component;
 }
+
+declare module '*.svg';
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.jpeg';
+declare module '*.gif';
+declare module '*.bmp';
+declare module '*.tiff';

+ 7 - 0
src/types/home.ts

@@ -4,3 +4,10 @@ export type GetHotSpotListResponse = Record<
     title: string;
   }
 >;
+
+export interface GetSceneDetailResponse {
+  id: number;
+  starSum: number;
+  shareSum: number;
+  visitSum: number;
+}

+ 1 - 0
src/utils/services.ts

@@ -50,6 +50,7 @@ const showMessage = (
     unknown
   > = 'error'
 ) => {
+  // @ts-ignore
   ElMessage({
     showClose: true,
     type,

+ 215 - 0
src/views/home/components/guide/index.zgrs.scss

@@ -0,0 +1,215 @@
+#drawer-container {
+  position: absolute;
+  left: 0;
+  bottom: 0px;
+  width: 100%;
+  height: 200px;
+  overflow: hidden;
+  pointer-events: none;
+  transition-property: bottom, opacity;
+  transition-duration: 0.5s;
+  z-index: var(--z-index-normal);
+
+  #drawer.playing {
+    bottom: 20px;
+  }
+}
+
+#drawer {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  color: #fff;
+  pointer-events: all;
+  transition-duration: 0.5s;
+  transition-property: height, bottom;
+
+  &.open {
+    height: 150px;
+  }
+}
+
+#drawer.open.fadeOut {
+  pointer-events: none;
+}
+
+.fullWidth .frame-container {
+  width: 100%;
+}
+.frame-container {
+  display: flex;
+  align-items: center;
+  width: calc(100% - 58px);
+  height: 100%;
+  background: rgba(27, 27, 28, 0.6);
+}
+
+.frame {
+  margin: 0 10px;
+  width: calc(100% - 20px);
+}
+
+.frame.noScroll {
+  margin: 17px 10px;
+}
+
+.frame .slidee {
+  display: flex;
+  height: 100%;
+  list-style: none;
+}
+
+.frame .slidee li {
+  position: relative;
+  flex-shrink: 0;
+  display: flex;
+  flex-direction: column-reverse;
+  align-items: center;
+  justify-content: center;
+  width: 186px;
+  height: 130px;
+  cursor: pointer;
+}
+
+.frame .slidee li .overlay {
+  margin-top: 5px;
+  color: #fff;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  font-size: 12px;
+  text-align: center;
+}
+
+.frame .slidee li .mark360View,
+.frame .slidee li .markInsideView {
+  position: absolute;
+  top: 2px;
+  left: 2px;
+  width: 50px;
+  max-height: 25px;
+  color: #fff;
+  background-color: rgba(0, 0, 0, 1);
+  z-index: 100;
+  transform: translate3d(0, 0, 0);
+}
+
+.frame .slidee li img {
+  width: 144px;
+  height: 83px;
+  object-fit: cover;
+  border-radius: 5px;
+  overflow: hidden;
+  border: 3px solid #c39f6b;
+}
+
+.frame .slidee li.thumbImg.active {
+  position: relative;
+  top: -5px;
+
+  > img {
+    width: 100%;
+    height: 100%;
+    border: 0;
+    mask: url('@/assets/images/zgrs/active-menu.png') no-repeat center / contain;
+  }
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: url('@/assets/images/zgrs/active-menu-2.png') no-repeat center / contain;
+    z-index: 1;
+  }
+  .overlay {
+    display: none;
+  }
+}
+
+#playHead {
+  display: table;
+  position: absolute;
+  bottom: -20px;
+  left: 0;
+  height: 20px;
+  display: flex;
+  align-items: center;
+  width: 100%;
+  transition-property: bottom;
+  transition-duration: 0.5s;
+  background-color: #000;
+
+  &.playing {
+    bottom: 0;
+  }
+}
+
+#status {
+  width: 65px;
+  color: #fff;
+  font-family: OpenSans, 'Helvetica Neue', Arial, sans-serif;
+  font-weight: 700;
+  font-size: 11px;
+  padding-left: 10px;
+}
+
+#progressBar {
+  padding: 0 10px;
+  flex: 1;
+  pointer-events: all;
+
+  .step {
+    height: 6px;
+    float: left;
+
+    &::before {
+      content: '';
+      display: block;
+      width: 100%;
+      height: 100%;
+      background-color: #575757;
+    }
+    &.active::before {
+      background-color: #00b4ed;
+    }
+  }
+}
+
+@media only screen and (max-width: 487px), (max-height: 487px) {
+  #drawer.open {
+    height: 115px;
+  }
+  .frame .slidee {
+    gap: 0;
+
+    li {
+      width: 135px;
+      height: 94px;
+
+      &.active {
+        top: -1px;
+      }
+      img {
+        width: 112px;
+        height: 64px;
+        border: none;
+      }
+      .overlay {
+        position: absolute;
+        left: 50%;
+        bottom: 15px;
+        padding: 0 2px;
+        width: 112px;
+        height: 20px;
+        line-height: 20px;
+        transform: translateX(-50%);
+        background: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.6));
+      }
+    }
+  }
+}

+ 26 - 0
src/views/home/components/guide/index.zgrs.tsx

@@ -0,0 +1,26 @@
+import { defineComponent } from 'vue';
+import './index.zgrs.scss';
+
+export default defineComponent({
+  name: 'HomeGuide',
+  render() {
+    return (
+      <div id="drawer-container">
+        <div id="drawer" class="fullWidth">
+          <div class="frame-container">
+            <div id="scrollFrame" class="frame">
+              <ul id="thumb-container" class="slidee"></ul>
+            </div>
+          </div>
+        </div>
+        <div id="playHead">
+          <div id="status">
+            <span class="curIdx">1</span>
+            of <span class="totalSteps"></span>
+          </div>
+          <div id="progressBar"></div>
+        </div>
+      </div>
+    );
+  },
+});

+ 47 - 0
src/views/home/components/hot-spot-list/index.zgrs.scss

@@ -0,0 +1,47 @@
+#hotListContent {
+  ul {
+    width: 200px;
+  }
+  li {
+    position: relative;
+    height: 44px;
+    color: white;
+    align-items: center;
+    cursor: pointer;
+    display: flex;
+    font-size: var(--el-font-size-base);
+    line-height: 22px;
+    list-style: none;
+    outline: none;
+    padding: 5px 35px 5px 16px;
+
+    span {
+      display: inline-block;
+      width: 100%;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    &.active {
+      font-weight: bold;
+      background: rgba(203, 25, 29, 0.5);
+    }
+    &:not(.is-disabled):hover,
+    &:not(.is-disabled):focus {
+      font-weight: bold;
+      background: rgba(203, 25, 29, 0.5);
+
+      &::after {
+        content: '';
+        position: absolute;
+        top: 50%;
+        right: 0;
+        width: 35px;
+        height: 35px;
+        background: url('@/assets/images/zgrs/title-right.png') no-repeat center / contain;
+        transform: translateY(-50%);
+      }
+    }
+  }
+}

+ 13 - 0
src/views/home/components/hot-spot-list/index.zgrs.vue

@@ -0,0 +1,13 @@
+<template>
+  <el-scrollbar max-height="400px">
+    <div id="hotListWrap">
+      <div id="hotListContent">
+        <ul></ul>
+      </div>
+    </div>
+  </el-scrollbar>
+</template>
+
+<style lang="scss">
+  @import './index.zgrs.scss';
+</style>

+ 238 - 0
src/views/home/components/menu/index.zgrs.scss

@@ -0,0 +1,238 @@
+.pinBottom-container {
+  position: absolute;
+  bottom: 0px;
+  width: 100%;
+  transition: all 0.5s;
+  z-index: var(--z-index-top);
+
+  .pinBottom.playing {
+    bottom: 20px;
+  }
+}
+
+.pinBottom.open.noScroll.playing {
+  bottom: 175px;
+}
+
+.pinBottom {
+  position: absolute;
+  bottom: 0;
+  line-height: 1;
+  transition: all 0.5s;
+
+  &.left {
+    left: 0;
+    width: 450px;
+    height: 67px;
+    background: url('@/assets/images/zgrs/Subtract-min.png') no-repeat center right / contain;
+  }
+  &.right {
+    right: 40px;
+    bottom: 10px;
+  }
+  &.open {
+    bottom: 155px;
+
+    &.playing {
+      bottom: 165px;
+    }
+  }
+}
+
+.viewContainer,
+.rightViewContainer,
+#gui-modes-map {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  height: 100%;
+}
+
+.viewContainer {
+  padding-left: 35px;
+}
+
+#play,
+#pause {
+  cursor: pointer;
+
+  img {
+    width: 57px;
+    height: 57px;
+  }
+}
+
+#pullTab,
+.helper-btn,
+.good-btn,
+.hotList,
+#gui-modes-dollhouse,
+#gui-modes-floorplan,
+#volume,
+#sharing {
+  display: block;
+  width: 57px;
+  height: 57px;
+  cursor: pointer;
+}
+
+#pullTab {
+  background: url('@/assets/images/zgrs/auto-suspend.png') no-repeat center / contain;
+
+  &.opened {
+    background-image: url('@/assets/images/zgrs/auto.png');
+  }
+}
+
+.hotList {
+  background: url('@/assets/images/zgrs/hotlist.png') no-repeat center / contain;
+
+  &.active {
+    background-image: url('@/assets/images/zgrs/hotlist-active.png');
+  }
+}
+
+#gui-modes-dollhouse {
+  background: url('@/assets/images/zgrs/dollhouse.png') no-repeat center / contain;
+
+  &.active {
+    background-image: url('@/assets/images/zgrs/dollhouse-active.png');
+  }
+}
+
+#gui-modes-floorplan {
+  background: url('@/assets/images/zgrs/floor.png') no-repeat center / contain;
+
+  &.active {
+    background-image: url('@/assets/images/zgrs/floor-active.png');
+  }
+}
+
+#volume {
+  background: url('@/assets/images/zgrs/Volume btn_off.png') no-repeat center / contain;
+
+  &.active {
+    background-image: url('@/assets/images/zgrs/Volume btn_on.png');
+  }
+}
+
+#sharing {
+  background: url('@/assets/images/zgrs/share.png') no-repeat center / contain;
+}
+
+.helper-btn {
+  background: url('@/assets/images/zgrs/helper.png') no-repeat center / contain;
+}
+
+.good-btn {
+  position: relative;
+
+  &.active div {
+    background-image: url('@/assets/images/zgrs/good-active.png');
+  }
+  div {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 58px;
+    height: 67px;
+    transition: background-image 0.3s ease-in-out;
+    background: url('@/assets/images/zgrs/good.png') no-repeat center / contain;
+  }
+  span {
+    position: absolute;
+    top: -9px;
+    left: 50%;
+    font-size: 12px;
+    color: white;
+    transform: translateX(-50%);
+  }
+}
+
+.terms2 {
+  display: none;
+}
+
+@media only screen and (max-width: 600px) {
+  .pinBottom {
+    &.open {
+      bottom: 122px;
+
+      &.noScroll.playing {
+        bottom: 142px;
+      }
+    }
+    &.open.right {
+      bottom: 152px;
+
+      &.noScroll.playing {
+        bottom: 172px;
+      }
+    }
+    &.left {
+      width: 100%;
+      height: 55px;
+
+      .viewContainer {
+        padding-left: 15px;
+      }
+    }
+    &.right {
+      right: 13px;
+      bottom: 30px;
+
+      &.playing {
+        bottom: 50px;
+      }
+      .rightViewContainer {
+        flex-direction: column;
+
+        .helper-btn,
+        #sharing,
+        #volume,
+        .good-btn {
+          width: 47px;
+          height: 47px;
+        }
+        .good-btn {
+          &.active {
+            div {
+              background-image: url('@/assets/images/zgrs/good-active-m.png');
+            }
+          }
+          div {
+            width: 47px;
+            height: 54px;
+            background-image: url('@/assets/images/zgrs/good-m.png');
+          }
+          span {
+            top: -7px;
+          }
+        }
+        .helper-btn {
+          background-image: url('@/assets/images/zgrs/helper-m.png');
+        }
+        #sharing {
+          background-image: url('@/assets/images/zgrs/share-m.png');
+        }
+        #volume {
+          background-image: url('@/assets/images/zgrs/Volume btn_off-m.png');
+
+          &.active {
+            background-image: url('@/assets/images/zgrs/Volume btn_on-m.png');
+          }
+        }
+      }
+    }
+  }
+  #play img,
+  #pause img,
+  #gui-modes-map > div,
+  .hotList {
+    width: 47px;
+    height: 47px;
+  }
+  #gui-modes-map {
+    gap: 5px;
+  }
+}

+ 209 - 0
src/views/home/components/menu/index.zgrs.vue

@@ -0,0 +1,209 @@
+<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="播放">
+          <a>
+            <img :src="PauseIcon" width="24" height="24" title="播放" />
+          </a>
+        </div>
+        <div id="pause" class="ui-icon" style="display: none">
+          <a>
+            <img title="暂停" :src="PlayIcon" width="24" height="24" />
+          </a>
+        </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 id="hotList" class="hidden" />
+          <el-dropdown
+            trigger="click"
+            placement="top"
+            popper-class="scene-title-popper"
+            @visible-change="(v: boolean) => (hotspotActive = v)"
+          >
+            <div
+              data-original-title="热点列表"
+              class="hotList"
+              title="热点列表"
+              :class="{ active: hotspotActive }"
+            ></div>
+
+            <template #dropdown>
+              <hot-spot-list />
+            </template>
+          </el-dropdown>
+          <div
+            data-original-title="迷你模型"
+            id="gui-modes-dollhouse"
+            title="迷你模型"
+            class=""
+          ></div>
+          <div data-original-title="俯视图" id="gui-modes-floorplan" title="俯视图"></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 class="helper-btn" title="指引" @click="openHelper"></div>
+        <div class="good-btn" title="点赞" :class="{ active: staring }" @click="handleStar">
+          <div />
+          <span v-if="staring">{{ staringString }}</span>
+        </div>
+        <div id="sharing" class="ui-icon wide" title="分享" @click="copyUrl"></div>
+        <div id="volume" class="ui-icon wide" style="display: none"></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 }]}"
+          style="display: none"
+        >
+          <a>
+            <i class="icon icon-fullscreen"></i>
+          </a>
+        </div>
+        <div
+          id="gui-fullscreen-exit"
+          class="ui-icon wide"
+          data-placement="top"
+          title="{[{ EXIT_FULLSCREEN }]}"
+          style="display: none"
+        >
+          <a>
+            <i class="icon icon-fullscreen-exit"></i>
+          </a>
+        </div>
+        <div class="pull-right terms terms2"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, onMounted, onUnmounted, ref } from 'vue';
+  import clipboard from 'clipboard';
+  import PlayIcon from '@/assets/images/zgrs/play.png';
+  import PauseIcon from '@/assets/images/zgrs/pause.png';
+  import { homeApi } from '@/api';
+  import HotSpotList from '../hot-spot-list';
+
+  let helperVisible = false;
+  const hotspotActive = ref(false);
+  const starSum = ref(0);
+  const staring = ref(false);
+  const staringString = computed(() => {
+    const value = starSum.value;
+    if (value >= 10000) {
+      return `${Math.floor(value / 10000)}w+`;
+    } else if (value >= 1000) {
+      return `${Math.floor(value / 1000)}k+`;
+    } else {
+      return value.toString();
+    }
+  });
+
+  const copyUrl = () => {
+    clipboard.copy(window.location.href);
+
+    ElNotification({
+      title: '提示',
+      type: 'success',
+      message: '链接已复制',
+      position: 'bottom-right',
+    });
+  };
+
+  const openHelper = () => {
+    window
+      .$('#interaction-modal')
+      // @ts-ignore
+      .addClass(`fadeIn ${window.browser.isMobile() ? 'mobile' : 'desktop'}`);
+    helperVisible = true;
+  };
+
+  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;
+  };
+
+  const handleStar = async () => {
+    if (staring.value) return;
+
+    staring.value = true;
+    starSum.value++;
+
+    try {
+      await homeApi.saveStar(window.number);
+
+      setTimeout(() => {
+        staring.value = false;
+      }, 1000);
+    } catch (err) {
+      starSum.value--;
+    }
+  };
+
+  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>
+  @import './index.zgrs.scss';
+</style>

+ 60 - 0
src/views/home/components/other/index.scss

@@ -0,0 +1,60 @@
+#call-to-action #interaction-modal.desktop {
+  height: 350px;
+  width: 550px;
+  border-radius: 10px;
+}
+
+#interaction-modal.desktop hr,
+#interaction-modal.desktop img {
+  width: 100%;
+  height: 100%;
+}
+
+#call-to-action #interaction-modal.mobile {
+  height: auto;
+  width: 95%;
+  border-radius: 5px;
+}
+
+#interaction-modal.mobile img {
+  width: 100%;
+}
+
+#call-to-action #interaction-modal.fadeIn,
+#call-to-action #pause-icon.fadeIn {
+  opacity: 1;
+  pointer-events: auto;
+}
+
+#call-to-action #interaction-modal {
+  position: fixed;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  opacity: 0;
+  -webkit-transition: all 0.5s;
+  transition: all 0.5s;
+  z-index: 201;
+  pointer-events: none;
+}
+
+.next-button {
+  right: 15px;
+  transform: translate(0, -50%) rotate(45deg);
+}
+
+.prev-button {
+  left: 15px;
+  transform: translate(0, -50%) rotate(-135deg);
+}
+
+.nav-help-page {
+  position: absolute;
+  top: 50%;
+  z-index: 10;
+  cursor: pointer;
+  width: 20px;
+  height: 20px;
+  border-top: 3px solid #ffffff;
+  border-right: 3px solid #ffffff;
+}

+ 5 - 2
src/views/home/components/other/index.tsx

@@ -1,11 +1,12 @@
 import { defineComponent } from 'vue';
+import './index.scss';
 
 export default defineComponent({
   name: 'HomeOther',
   render() {
     return (
       <div>
-        <div id="share-modal" style="display: none">
+        <div id="share-modal">
           <div id="share-outer">
             <div class="share-images">
               <a id="facebook-share">
@@ -59,9 +60,11 @@ export default defineComponent({
               </a>
             </div>
           </div>
-          <div id="interaction-modal" v-show="0">
+          <div id="interaction-modal">
             <div id="interaction-modal-inner">
               <div class="nav-icon">
+                <img src="images/pc_step1.png" class="icon" title="导览" data-page="1" />
+
                 <div class="nav-help-button">
                   <div class="next-button nav-help-page" data-id="plus"></div>
                   <div class="prev-button nav-help-page"></div>

+ 36 - 0
src/views/home/components/popup/index.zgrs.scss

@@ -0,0 +1,36 @@
+#popup {
+  display: none;
+  position: relative;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  background: rgba(0, 0, 0, 0.6);
+  z-index: var(--z-hot-popper);
+
+  &.wait {
+    opacity: 0.1;
+  }
+}
+#id1 {
+  width: 100%;
+  height: 100%;
+}
+.popup-content {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+#closepop {
+  position: absolute;
+  left: 50%;
+  bottom: 25px;
+  width: 37px;
+  height: 37px;
+  cursor: pointer;
+  text-indent: -999em;
+  background-size: 100% 100%;
+  transform: translateX(-50%);
+  background: url('@/assets/images/zgrs/close.png') no-repeat;
+}

+ 14 - 0
src/views/home/components/popup/index.zgrs.tsx

@@ -0,0 +1,14 @@
+import { defineComponent } from 'vue';
+import './index.zgrs.scss';
+
+export default defineComponent({
+  name: 'HomePopup',
+  render() {
+    return (
+      <div id="popup">
+        <div class="popup-content"></div>
+        <div id="closepop">close</div>
+      </div>
+    );
+  },
+});

+ 56 - 0
src/views/home/components/title/index.zgrs.scss

@@ -0,0 +1,56 @@
+.scene-title {
+  position: fixed;
+  top: 0;
+  left: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 720px;
+  height: 44px;
+  font-size: 14px;
+  font-weight: bold;
+  color: white;
+  transform: translateX(-50%);
+  cursor: pointer;
+  background: url('@/assets/images/zgrs/title-bg.png') no-repeat center / contain;
+  z-index: var(--z-index-top);
+
+  p {
+    position: relative;
+    width: 160px;
+    text-align: center;
+  }
+  &::after {
+    content: '';
+    position: absolute;
+    top: 0;
+    right: 244px;
+    width: 37px;
+    height: 37px;
+    background: url('@/assets/images/zgrs/title-down.png') no-repeat center / contain;
+  }
+}
+
+.scene-sublist {
+  --el-text-color-regular: white;
+  --el-bg-color-overlay: transparent;
+  --el-dropdown-menuItem-hover-fill: rgba(203, 25, 29, 0.5);
+  --el-dropdown-menuItem-hover-color: white;
+}
+
+@media only screen and (max-width: 600px) {
+  .scene-title {
+    width: 100%;
+    height: 36px;
+    background-image: url('@/assets/images/zgrs/title-bg-m.png');
+
+    p {
+      padding-left: 37px;
+    }
+    &::after {
+      position: relative;
+      top: -4px;
+      right: 0;
+    }
+  }
+}

+ 457 - 0
src/views/home/components/title/index.zgrs.vue

@@ -0,0 +1,457 @@
+<template>
+  <div class="pinTop" style="display: none">
+    <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>
+            <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>
+
+  <el-dropdown trigger="click" popper-class="scene-title-popper">
+    <div class="scene-title">
+      <p id="gui-name" class="limit-line titleText" />
+    </div>
+
+    <template #dropdown>
+      <el-scrollbar max-height="400px">
+        <el-dropdown-menu>
+          <!-- pc hover展示 -->
+          <template v-if="!isMobile">
+            <el-tooltip
+              v-for="item in LIST"
+              :key="item.label"
+              append-to="body"
+              effect="dark"
+              :placement="isMobile ? 'bottom' : 'right'"
+              popper-class="scene-list-subitem-popup"
+              :show-arrow="false"
+              :trigger="isMobile ? 'click' : 'hover'"
+              :disabled="!item.children.length"
+            >
+              <li
+                class="scene-list-item el-tooltip__trigger el-tooltip__trigger el-dropdown-menu__item"
+              >
+                {{ item.label }}
+              </li>
+
+              <template #content>
+                <el-scrollbar max-height="400px">
+                  <el-dropdown-menu class="scene-sublist">
+                    <el-dropdown-item
+                      v-for="subitem in item.children"
+                      :key="subitem.sceneCode"
+                      class="scene-sublist-item"
+                      @click="changeScene(subitem)"
+                    >
+                      {{ subitem.label }}
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </el-scrollbar>
+              </template>
+            </el-tooltip>
+          </template>
+
+          <!-- 移动端 手风琴展示 -->
+          <template v-else>
+            <el-collapse accordion class="scene-title-collapse">
+              <el-collapse-item v-for="item in LIST" :key="item.label" :name="item.label">
+                <template #title>
+                  <div class="scene-title-collapse-item">
+                    {{ item.label }}
+                  </div>
+                </template>
+
+                <el-dropdown-menu class="scene-sublist">
+                  <el-dropdown-item
+                    v-for="subitem in item.children"
+                    :key="subitem.sceneCode"
+                    class="scene-sublist-item"
+                    @click="changeScene(subitem)"
+                  >
+                    {{ subitem.label }}
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-collapse-item>
+            </el-collapse>
+          </template>
+        </el-dropdown-menu>
+      </el-scrollbar>
+    </template>
+  </el-dropdown>
+</template>
+
+<script setup lang="ts">
+  import { ref, watch } from 'vue';
+
+  const props = defineProps<{
+    isInit: boolean;
+  }>();
+
+  const LIST = [
+    {
+      label: '外景展示',
+      children: [
+        {
+          label: '光大汇晨-3号楼外景',
+          sceneCode: 'KJ-In9jsvCe86s',
+        },
+        {
+          label: '光大汇晨-4号、5号楼外景',
+          sceneCode: 'KJ-ZMFs8MxP2SB',
+        },
+        {
+          label: '光大汇晨-后花园',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:244,qua:0.039007937897505625,0.9142990295337833,0.09079670316256798,-0.3927997197919962',
+        },
+      ],
+    },
+    {
+      label: '房型展示',
+      children: [
+        {
+          label: '4-1号楼6层 双人居室',
+          sceneCode: 'KJ-f3lOsooRQEL',
+          params:
+            '&firstView=pano:32,qua:-0.051998504111525345,-0.014998504545882635,-0.0007810446053117947,0.9985342235607464',
+        },
+        {
+          label: '4-2号楼4层 双人自理间',
+          sceneCode: 'KJ-SfNcYJffCof',
+          href: 'https://houseoss.4dkankan.com/project/GDHCYL/scene/index.html?pose=pano:16,qua:-0.0382,-0.0274,-0.001,0.9989',
+        },
+      ],
+    },
+    {
+      label: '功能性房间展示',
+      children: [
+        {
+          label: '光大4号楼-前台大厅',
+          sceneCode: 'KJ-WklncxbyqUz',
+        },
+        {
+          label: '光大5号楼-前台大厅',
+          sceneCode: 'KJ-YNQbf4hmdhp',
+        },
+        {
+          label: '4-1号楼1层 桌球台',
+          sceneCode: 'KJ-8yXDk9BK7NQ',
+          params:
+            '&firstView=pano:36,qua:-0.13839545703172249,-0.008993664428416585,-0.0012568289632250125,0.9903354179400402',
+        },
+        {
+          label: '4-1号楼1层 健身区域',
+          sceneCode: 'KJ-8yXDk9BK7NQ',
+          params:
+            '&firstView=pano:42,qua:-0.20459884853874494,0.334008449059796,0.07451714944112271,0.9170740759282797',
+        },
+        {
+          label: '4-1号楼1层 乒乓球台',
+          sceneCode: 'KJ-8yXDk9BK7NQ',
+          params:
+            '&firstView=pano:24,qua:0.0046569716799469505,0.9813820813814765,0.19050458281639018,-0.023990332947140786',
+        },
+        {
+          label: '4-1号楼1层 射箭区',
+          sceneCode: 'KJ-8yXDk9BK7NQ',
+          params:
+            '&firstView=pano:20,qua:-0.10227912194755563,0.8139137901708958,0.15090610701275087,0.5516435960690454',
+        },
+        {
+          label: '4-1号楼 娱乐休息区',
+          sceneCode: 'KJ-8yXDk9BK7NQ',
+          params:
+            '&firstView=pano:172,qua:-0.026285491500693498,-0.5010156996060221,-0.01522646481097273,0.8649049092888258',
+        },
+        {
+          label: '4-2号楼 VIP洽谈室',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:20,qua:-0.030210656596747626,0.7273994644789756,0.0320900949269009,0.6847974608518024',
+        },
+        {
+          label: '4-2号楼 休息区',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:56,qua:-0.0008089044739337793,-0.5144104029525004,-0.0004852334801243485,0.8575436151578546',
+        },
+        {
+          label: '4-2号楼 护士站',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:44,qua:-0.07980318002624788,-0.00979912811085812,-0.0007845418098985508,0.9967621598909656',
+        },
+        {
+          label: '4-2号楼 医疗部',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:66,qua:-0.04209952871323408,-0.0035019789053245386,-0.00014756338577590622,0.9991072752855208',
+        },
+        {
+          label: '4-2号楼 房务部、超市',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:74,qua:-0.000042130492863588265,0.9999250741545762,0.01169955473490148,0.0036007640279056207',
+        },
+        {
+          label: '4-2号楼 餐厅',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:76,qua:0.0003101107253231401,0.9971451465048626,0.07539654778114177,-0.00410132025091101',
+        },
+        {
+          label: '4-2号楼 多功能厅',
+          sceneCode: 'KJ-WklncxbyqUz',
+          params:
+            '&firstView=pano:226,qua:-0.0010279439479733514,0.9982114010524721,0.05699884137526075,0.018002213102936385',
+        },
+      ],
+    },
+    {
+      label: '楼层展示',
+      children: [
+        {
+          label: '光大4-1号楼-1F',
+          sceneCode: 'KJ-8yXDk9BK7NQ',
+          params:
+            '&firstView=pano:0,qua:-0.051828815102206244,-0.7091317714942,-0.05241417246055152,0.7012122683525472',
+        },
+        {
+          label: '光大4-1号楼-3F',
+          sceneCode: 'KJ-InMhgveLjSN',
+          params:
+            '&firstView=pano:2,qua:-0.001494510580635004,-0.6510945728407702,-0.0012820492666803882,0.7589940539227341',
+        },
+        {
+          label: '光大4-1号楼-4F',
+          sceneCode: 'KJ-Z3umjaDYj54',
+          params:
+            '&firstView=pano:0,qua:-0.012301715416710919,-0.6115701308322424,-0.009510753152011622,0.7910374155693033',
+        },
+        {
+          label: '光大4-1号楼-5F',
+          sceneCode: 'KJ-KWa3ofq5VtT',
+          params:
+            '&firstView=pano:0,qua:-0.03412505300824889,-0.6568498626985018,-0.02978109785591667,0.7526598396081043',
+        },
+        {
+          label: '光大4-1号楼-6F',
+          sceneCode: 'KJ-f3lOsooRQEL',
+          params:
+            '&firstView=pano:0,qua:-0.015325249200611228,-0.608161903992223,-0.01174456833630179,0.7935781673982758',
+        },
+        {
+          label: '光大4-2号楼-2F',
+          sceneCode: 'KJ-mK2fUzsBliv',
+          params:
+            '&firstView=pano:0,qua:-0.01749725260433772,-0.645792691843274,-0.014806159434117765,0.7631686765026857',
+        },
+        {
+          label: '光大4-2号楼-3F',
+          sceneCode: 'KJ-HuSSF9cJYpY',
+          params:
+            '&firstView=pano:0,qua:-0.045996339936217254,-0.6457122165537932,-0.039018126783055745,0.7611948828365402',
+        },
+        {
+          label: '光大4-2号楼-4F',
+          sceneCode: 'KJ-SfNcYJffCof',
+          href: 'https://houseoss.4dkankan.com/project/GDHCYL/scene/index.html',
+          params:
+            '&firstView=pano:136,qua:-0.04730401416249904,-0.6225940589150876,-0.03774820655556335,0.7802012747957571',
+        },
+        {
+          label: '光大4-2号楼-5F',
+          sceneCode: 'KJ-nOTU5Eg4Y0Y',
+          params:
+            '&firstView=pano:0,qua:-0.07287642059864806,-0.562579624926581,-0.049876072914877195,0.8220131275736767',
+        },
+        {
+          label: '光大4-2号楼-6F',
+          sceneCode: 'KJ-483Cb7j9OD0',
+          params:
+            '&firstView=pano:0,qua:-0.06459332775841535,-0.7106008781108282,-0.0658040120504373,0.6975270230111492',
+        },
+      ],
+    },
+  ];
+  const isMobile = ref(false);
+
+  watch(
+    () => props.isInit,
+    (v) => {
+      if (v) {
+        isMobile.value = window.browser.isMobile();
+      }
+    }
+  );
+
+  const changeScene = (item: Record<string, any>) => {
+    location.href = item.href
+      ? item.href
+      : `${location.origin}${location.pathname}?m=${item.sceneCode}${
+          item.params ? item.params : ''
+        }`;
+  };
+</script>
+
+<style lang="scss" scoped>
+  @import './index.zgrs.scss';
+</style>
+
+<style lang="scss">
+  .el-dropdown__popper.el-popper.scene-title-popper {
+    --el-bg-color-overlay: rgba(0, 0, 0, 0.4);
+    --el-border-color-light: transparent;
+
+    .scene-list-item {
+      position: relative;
+      padding-right: 35px;
+      height: 44px;
+      color: white;
+
+      &:not(.is-disabled):hover,
+      &:not(.is-disabled):focus {
+        --el-dropdown-menuItem-hover-color: white;
+        --el-dropdown-menuItem-hover-fill: rgba(203, 25, 29, 0.5);
+        font-weight: bold;
+
+        &::after {
+          content: '';
+          position: absolute;
+          top: 50%;
+          right: 0;
+          width: 35px;
+          height: 35px;
+          background: url('@/assets/images/zgrs/title-right.png') no-repeat center / contain;
+          transform: translateY(-50%);
+        }
+      }
+    }
+  }
+
+  .scene-list-subitem-popup {
+    padding: 0;
+  }
+
+  .scene-sublist-item {
+    height: 36px;
+  }
+
+  .scene-title-collapse {
+    --el-border-color-lighter: rgba(0, 0, 0, 0.2);
+    --el-collapse-header-height: 40px;
+    --el-collapse-header-bg-color: transparent;
+    --el-collapse-header-text-color: white;
+    --el-collapse-content-bg-color: transparent;
+    --el-collapse-content-font-size: 12px;
+    --el-collapse-content-text-color: white;
+
+    .el-collapse-item {
+      width: 205px;
+
+      &__header {
+        > .el-icon {
+          display: none;
+        }
+        &.is-active {
+          .scene-title-collapse-item {
+            background: rgba(203, 25, 29, 0.5);
+
+            &::after {
+              content: '';
+              position: absolute;
+              top: 50%;
+              right: 0;
+              width: 35px;
+              height: 35px;
+              background: url('@/assets/images/zgrs/title-right.png') no-repeat center / contain;
+              transform: translateY(-50%) rotate(-90deg);
+            }
+          }
+        }
+      }
+    }
+    &-item {
+      position: relative;
+      width: 100%;
+      color: inherit;
+      font-size: inherit;
+      font-weight: bold;
+      text-align: center;
+    }
+    .scene-sublist-item {
+      justify-content: center;
+    }
+    .el-collapse-item__content {
+      padding-bottom: 0;
+    }
+  }
+</style>

+ 99 - 0
src/views/home/index.zgrs.scss

@@ -0,0 +1,99 @@
+.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);
+    }
+  }
+}
+
+#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);
+  }
+}
+
+@media only screen and (max-width: 600px) {
+  .home {
+    &__back {
+      top: 12px;
+      left: 12px;
+      width: 35px;
+      height: 35px;
+      z-index: 1001;
+    }
+  }
+}

+ 105 - 0
src/views/home/index.zgrs.tsx

@@ -0,0 +1,105 @@
+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 BackIcon from '@/assets/images/zgrs/back.png';
+import './index.zgrs.scss';
+
+// @ts-ignore
+window.hoticon = {
+  default: '/images/zgrs/point.png',
+  higt: '/images/zgrs/point2.png',
+};
+
+export default defineComponent({
+  name: 'home',
+  components: {
+    Title,
+    WebVr,
+    Other,
+    Vrcon,
+    GuiLoading,
+    JsScript,
+    Popup,
+  },
+  setup() {
+    const manageJsLoaded = ref(false);
+    const hotJsLoaded = ref(false);
+
+    return {
+      manageJsLoaded,
+      hotJsLoaded,
+    };
+  },
+  render() {
+    return (
+      <div class="home">
+        <img
+          class="home__back"
+          src={BackIcon}
+          onClick={() => {
+            window.location.href = 'https://houseoss.4dkankan.com/project/GDHCYL/home/index.html';
+          }}
+        />
+
+        {/* 进度条加载 */}
+        <GuiLoading />
+
+        {/* 加载初始页面 */}
+        <div id="gui-thumb" />
+
+        {/* 热点弹出框 */}
+        <Popup />
+
+        {/* 场景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.manageJsLoaded && (
+          <>
+            <JsScript src="/js/Hot.js" onLoad={() => (this.hotJsLoaded = true)} />
+            {this.hotJsLoaded && (
+              <>
+                <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>
+    );
+  },
+});

+ 8 - 1
tsconfig.json

@@ -19,6 +19,13 @@
     },
     "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
   },
-  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx"],
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "**/*.d.ts",
+    "tests/**/*.ts",
+    "tests/**/*.tsx"
+  ],
   "exclude": ["node_modules"]
 }