chenlei 8 月之前
父节点
当前提交
92b392f74f

+ 1 - 1
components.d.ts

@@ -7,7 +7,7 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
-    ElDropdown: typeof import('element-plus/es')['ElDropdown']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
     ElImage: typeof import('element-plus/es')['ElImage']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     RouterLink: typeof import('vue-router')['RouterLink']

二进制
hotspot/assets/images/syjy/icon-next-min.png


二进制
hotspot/assets/images/syjy/icon-previous-min.png


+ 7 - 0
hotspot/syjy-main.ts

@@ -0,0 +1,7 @@
+import { createApp } from 'vue';
+import App from './views/hotspot/index.syjy.vue';
+import '@/app.scss';
+
+export const app = createApp(App);
+
+app.mount('#app');

+ 214 - 0
hotspot/views/hotspot/index.syjy.scss

@@ -0,0 +1,214 @@
+.hotspot-page {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 22px 0 71px;
+  z-index: var(--z-index-popper);
+
+  .audioIcon {
+    position: absolute;
+    right: 20px;
+    bottom: 5px;
+
+    img {
+      width: 57px;
+      height: 57px;
+      cursor: pointer;
+    }
+  }
+  &-info {
+    color: #333333;
+    max-width: 1320px;
+    width: calc(100vw - 30vw);
+
+    h3 {
+      margin-bottom: 18px;
+      font-size: 18px;
+      font-weight: 700;
+      text-align: center;
+      color: #9d222d;
+    }
+    div {
+      margin: 0 auto;
+      width: 80%;
+      font-size: 18px;
+      line-height: 32px;
+      text-indent: 2em;
+    }
+  }
+
+  &-container {
+    position: relative;
+    flex: 1;
+    flex-shrink: 1;
+    height: 0;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 9vh 20px;
+    max-width: 1320px;
+    min-width: 400px;
+    width: auto;
+    box-sizing: border-box;
+  }
+
+  &-swiper {
+    &__left,
+    &__right {
+      position: absolute;
+      top: 50%;
+      width: 45px;
+      height: 50px;
+      cursor: pointer;
+      transform: translateY(-50%);
+      z-index: 1;
+    }
+    &__left {
+      left: -70px;
+      background: url('@hotspot/assets/images/syjy/icon-previous-min.png') no-repeat center /
+        contain;
+    }
+    &__right {
+      right: -70px;
+      background: url('@hotspot/assets/images/syjy/icon-next-min.png') no-repeat center / contain;
+    }
+  }
+  &-model {
+    width: 100%;
+    height: 100%;
+
+    iframe {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  .swiper-slide {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  &-video {
+    height: 100%;
+    max-height: 100%;
+  }
+  &-img {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: inherit;
+
+    &-swiper {
+      flex: 1;
+      width: 100%;
+      height: 0;
+    }
+  }
+
+  &-nav {
+    position: absolute;
+    left: 50%;
+    bottom: 5px;
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    transform: translateX(-50%);
+
+    &__item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 9px;
+      width: 57px;
+      height: 57px;
+      box-sizing: border-box;
+      cursor: pointer;
+
+      &.active {
+      }
+      img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+}
+
+span.el-image-viewer__close {
+  top: 44px;
+  right: 100px;
+}
+
+@media only screen and (max-width: 600px) {
+  .hotspot-page {
+    padding-bottom: 0;
+
+    .el-scrollbar {
+      flex: 1;
+      width: 100%;
+      height: 0 !important;
+      background: #9d222d;
+      border-radius: 17px 17px 0px 0px;
+    }
+    .el-scrollbar__wrap {
+      height: 100% !important;
+    }
+    &-info {
+      padding: 0 28px;
+      text-align: left;
+      width: 100%;
+      color: white;
+
+      h3 {
+        margin: 13px 0 8px;
+        color: inherit;
+        text-align: left;
+      }
+      div {
+        width: 100%;
+        font-size: 14px;
+      }
+    }
+    &-container {
+      max-width: 100%;
+      width: 100%;
+      background: none;
+    }
+    &-swiper {
+      &__left,
+      &__right {
+        top: unset;
+        bottom: 12px;
+        transform: none;
+      }
+      &__left {
+        left: 14px;
+      }
+      &__right {
+        right: 14px;
+      }
+    }
+    &-nav {
+      bottom: 0;
+
+      &__item {
+        width: 48px;
+        height: 48px;
+      }
+    }
+    .audioIcon {
+      right: 35px;
+      bottom: 1px;
+
+      img {
+        width: 48px;
+        height: 48px;
+      }
+    }
+  }
+}

+ 278 - 0
hotspot/views/hotspot/index.syjy.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="hotspot-page">
+    <div class="hotspot-page-container">
+      <!-- 音频播放器 -->
+      <audio
+        id="myAudio"
+        v-if="audio"
+        ref="volumeRef"
+        v-show="isOneAduio"
+        :src="audio"
+        controls
+      ></audio>
+
+      <!-- 模型页面 -->
+      <Swiper
+        v-if="myType === 'model'"
+        class="hotspot-page-swiper hotspot-page-model"
+        @swiper="initSwiper"
+        @slideChange="handleChange"
+      >
+        <SwiperSlide v-for="(item, index) in curList" :key="item.url">
+          <iframe v-if="index === myInd" :src="item" frameborder="0" />
+        </SwiperSlide>
+      </Swiper>
+
+      <!-- 视频页面 -->
+      <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"
+            class="hotspot-page-video"
+            controls
+            :src="item.url"
+            autoplay
+          />
+        </template>
+      </div>
+
+      <!-- 图片页面 -->
+      <Swiper
+        v-if="myType === 'img'"
+        class="hotspot-page-swiper hotspot-page-img-swiper"
+        @swiper="initSwiper"
+        @slideChange="handleChange"
+      >
+        <SwiperSlide v-for="(item, idx) in curList" :key="item">
+          <div class="hotspot-page-img">
+            <el-image
+              :src="item"
+              fit="contain"
+              style="width: 100%; height: 100%"
+              preview-teleported
+              :preview-src-list="curList"
+              :initial-index="idx"
+            />
+          </div>
+        </SwiperSlide>
+      </Swiper>
+
+      <template v-if="curList.length > 1">
+        <div class="hotspot-page-swiper__left" @click="handlePre" />
+        <div class="hotspot-page-swiper__right" @click="handleNext" />
+      </template>
+
+      <!-- 底部的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-if="audio && !isOneAduio"
+        class="audioIcon"
+        :title="audioSta ? '关闭音频' : '打开音频'"
+        @click="audioSta = !audioSta"
+      >
+        <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>
+        <div v-html="myTxt" />
+      </div>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script>
+  import { Swiper, SwiperSlide } from 'swiper/vue';
+  import 'swiper/css';
+  import { parseUrlParams } from '@/utils';
+
+  import ModelIcon from '@hotspot/assets/images/icon-model@2x.png';
+  import AcModelIcon from '@hotspot/assets/images/icon-model-1@2x.png';
+  import ImageIcon from '@hotspot/assets/images/icon-image@2x.png';
+  import AcImageIcon from '@hotspot/assets/images/icon-image-1@2x.png';
+  import VideoIcon from '@hotspot/assets/images/icon-video@2x.png';
+  import AcVideoIcon from '@hotspot/assets/images/icon-video-1@2x.png';
+  import VolumeOn from '@hotspot/assets/images/Volume-on.png';
+  import VolumeOff from '@hotspot/assets/images/Volume-off.png';
+
+  const urlParams = parseUrlParams(window.location.href);
+
+  export default {
+    name: 'hotspot',
+    components: {
+      Swiper,
+      SwiperSlide,
+    },
+    data() {
+      return {
+        VolumeOn,
+        VolumeOff,
+        m: urlParams.m,
+        id: urlParams.id,
+        // 音频地址
+        audio: '',
+        // 如果只有单独的音频
+        isOneAduio: false,
+        // 音频状态
+        audioSta: false,
+
+        data: {
+          // 模型数组
+          model: [],
+          // 视频数组
+          video: [],
+          // 图片数组
+          img: [],
+        },
+        // 当前 type
+        myType: '',
+
+        // 当前索引
+        myInd: 0,
+
+        // 底部的tab
+        flooTab: [],
+
+        // 标题
+        myTitle: '',
+        // 内容
+        myTxt: '',
+        // 视频内容
+        videoTxt: [],
+        imgTxt: [],
+
+        // 只有标题和文字(没有视频,没有模型,没有图片)
+        oneTxt: false,
+      };
+    },
+    computed: {
+      curList() {
+        return this.data[this.myType] || [];
+      },
+    },
+    watch: {
+      audioSta(val) {
+        if (val) {
+          this.$refs.volumeRef.play();
+          this.$refs.volumeRef.onended = () => {
+            // console.log("----音频播放完毕");
+            this.audioSta = false;
+          };
+        } else this.$refs.volumeRef.pause();
+      },
+    },
+    mounted() {
+      this.getData();
+    },
+    methods: {
+      async getData() {
+        // https://www.4dmodel.com/
+        let url = `https://super.4dage.com/data/${this.id}/hot/js/data.js?time=${Math.random()}`;
+        let result = await fetch(url).then((response) => response.json());
+        const resData = result[this.m];
+        console.log('----', resData);
+        if (resData) {
+          this.audio = resData.backgroundMusic;
+          // 只有单独的音频上传
+          if (resData.backgroundMusic && !resData.model && !resData.video && !resData.images) {
+            this.isOneAduio = true;
+          }
+          // 底部的tab
+          const arr = [];
+          const obj = {};
+          if (resData.model) {
+            obj.model = resData.model;
+            arr.push({ id: 1, type: 'model', name: '模型', icon: ModelIcon, acIcon: AcModelIcon });
+          }
+          if (resData.video) {
+            obj.video = resData.video;
+            arr.push({ id: 2, type: 'video', name: '视频', icon: VideoIcon, acIcon: AcVideoIcon });
+          } else {
+            this.$nextTick(() => {
+              if (
+                !window.navigator.userAgent.match(
+                  /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
+                )
+              ) {
+                this.audioSta = true;
+                this.$refs.volumeRef.play();
+              }
+            });
+          }
+          if (resData.images) {
+            obj.img = resData.images;
+            arr.push({ id: 3, type: 'img', name: '图片', icon: ImageIcon, acIcon: AcImageIcon });
+          }
+          this.flooTab = arr;
+          this.data = obj;
+
+          // 当前type的值 应该为
+          if (resData.model) this.myType = 'model';
+          else if (resData.video) this.myType = 'video';
+          else if (resData.images) this.myType = 'img';
+
+          this.myTitle = resData.title || '';
+          this.myTxt = resData.content || '';
+          this.videoTxt = resData.videosDesc || [];
+          this.imgTxt = resData.imagesDesc || [];
+
+          // 只有 标题和 文字介绍(没有视频,没有模型,没有图片)
+          if (!obj.model && !obj.video && !obj.img && !resData.backgroundMusic) {
+            this.oneTxt = true;
+          }
+        }
+      },
+
+      handleTab(item) {
+        this.myInd = 0;
+        this.myType = item.type;
+      },
+
+      initSwiper(swiper) {
+        this.swiper = swiper;
+      },
+      handleChange({ activeIndex }) {
+        this.myInd = activeIndex;
+      },
+      handlePre() {
+        if (this.myType === 'video') {
+          this.myInd = this.myInd > 0 ? this.myInd - 1 : this.curList.length - 1;
+        } else {
+          this.swiper?.slidePrev();
+        }
+      },
+      handleNext() {
+        if (this.myType === 'video') {
+          this.myInd = this.myInd < this.curList.length - 1 ? this.myInd + 1 : 0;
+        } else {
+          this.swiper?.slideNext();
+        }
+      },
+    },
+  };
+</script>
+
+<style lang="scss">
+  @import './index.syjy.scss';
+</style>

+ 1 - 0
package.json

@@ -25,6 +25,7 @@
     "swiper": "^11.1.10",
     "vue": "^3.2.13",
     "vue-class-component": "^8.0.0-0",
+    "vue-qrcode": "^2.2.2",
     "vue-router": "^4.0.3"
   },
   "devDependencies": {

+ 82 - 20
pnpm-lock.yaml

@@ -35,6 +35,9 @@ dependencies:
   vue-class-component:
     specifier: ^8.0.0-0
     version: 8.0.0-alpha.1(vue@3.2.13)
+  vue-qrcode:
+    specifier: ^2.2.2
+    version: 2.2.2(qrcode@1.5.4)(vue@3.2.13)
   vue-router:
     specifier: ^4.0.3
     version: 4.0.3(vue@3.2.13)
@@ -3747,7 +3750,6 @@ packages:
   /ansi-regex@5.0.1:
     resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
     engines: {node: '>=8'}
-    dev: true
 
   /ansi-regex@6.1.0:
     resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
@@ -3766,7 +3768,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       color-convert: 2.0.1
-    dev: true
 
   /ansi-styles@5.2.0:
     resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
@@ -4319,7 +4320,6 @@ packages:
   /camelcase@5.3.1:
     resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
     engines: {node: '>=6'}
-    dev: true
 
   /camelcase@6.3.0:
     resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
@@ -4489,6 +4489,14 @@ packages:
       is-wsl: 2.2.0
     dev: true
 
+  /cliui@6.0.0:
+    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 6.2.0
+    dev: false
+
   /cliui@7.0.4:
     resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
     dependencies:
@@ -4540,7 +4548,6 @@ packages:
     engines: {node: '>=7.0.0'}
     dependencies:
       color-name: 1.1.4
-    dev: true
 
   /color-name@1.1.3:
     resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
@@ -4548,7 +4555,6 @@ packages:
 
   /color-name@1.1.4:
     resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-    dev: true
 
   /colord@2.9.3:
     resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
@@ -5308,7 +5314,6 @@ packages:
   /decamelize@1.2.0:
     resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /decimal.js@10.4.3:
     resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
@@ -5435,6 +5440,10 @@ packages:
       randombytes: 2.1.0
     dev: true
 
+  /dijkstrajs@1.0.3:
+    resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
+    dev: false
+
   /dir-glob@3.0.1:
     resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
     engines: {node: '>=8'}
@@ -5606,7 +5615,6 @@ packages:
 
   /emoji-regex@8.0.0:
     resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-    dev: true
 
   /emoji-regex@9.2.2:
     resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
@@ -6192,7 +6200,6 @@ packages:
     dependencies:
       locate-path: 5.0.0
       path-exists: 4.0.0
-    dev: true
 
   /find-up@5.0.0:
     resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
@@ -6360,7 +6367,6 @@ packages:
   /get-caller-file@2.0.5:
     resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
     engines: {node: 6.* || 8.* || >= 10.*}
-    dev: true
 
   /get-intrinsic@1.2.4:
     resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
@@ -6938,7 +6944,6 @@ packages:
   /is-fullwidth-code-point@3.0.0:
     resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
     engines: {node: '>=8'}
-    dev: true
 
   /is-generator-fn@2.1.0:
     resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
@@ -7968,7 +7973,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       p-locate: 4.1.0
-    dev: true
 
   /locate-path@6.0.0:
     resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
@@ -8721,7 +8725,6 @@ packages:
     engines: {node: '>=6'}
     dependencies:
       p-try: 2.2.0
-    dev: true
 
   /p-limit@3.1.0:
     resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
@@ -8735,7 +8738,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       p-limit: 2.3.0
-    dev: true
 
   /p-locate@5.0.0:
     resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
@@ -8762,7 +8764,6 @@ packages:
   /p-try@2.2.0:
     resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
     engines: {node: '>=6'}
-    dev: true
 
   /package-json-from-dist@1.0.1:
     resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
@@ -8841,7 +8842,6 @@ packages:
   /path-exists@4.0.0:
     resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
     engines: {node: '>=8'}
-    dev: true
 
   /path-is-absolute@1.0.1:
     resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
@@ -8954,6 +8954,11 @@ packages:
       semver-compare: 1.0.0
     dev: true
 
+  /pngjs@5.0.0:
+    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+    engines: {node: '>=10.13.0'}
+    dev: false
+
   /portfinder@1.0.32:
     resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==}
     engines: {node: '>= 0.12.0'}
@@ -9500,6 +9505,16 @@ packages:
       (For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
     dev: true
 
+  /qrcode@1.5.4:
+    resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dependencies:
+      dijkstrajs: 1.0.3
+      pngjs: 5.0.0
+      yargs: 15.4.1
+    dev: false
+
   /qs@6.13.0:
     resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
     engines: {node: '>=0.6'}
@@ -9710,13 +9725,16 @@ packages:
   /require-directory@2.1.1:
     resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /require-from-string@2.0.2:
     resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /require-main-filename@2.0.0:
+    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+    dev: false
+
   /require-package-name@2.0.1:
     resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==}
     dev: true
@@ -10013,6 +10031,10 @@ packages:
       - supports-color
     dev: true
 
+  /set-blocking@2.0.0:
+    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+    dev: false
+
   /set-function-length@1.2.2:
     resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
     engines: {node: '>= 0.4'}
@@ -10341,7 +10363,6 @@ packages:
       emoji-regex: 8.0.0
       is-fullwidth-code-point: 3.0.0
       strip-ansi: 6.0.1
-    dev: true
 
   /string-width@5.1.2:
     resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
@@ -10385,7 +10406,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       ansi-regex: 5.0.1
-    dev: true
 
   /strip-ansi@7.1.0:
     resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
@@ -10796,7 +10816,6 @@ packages:
 
   /tslib@2.8.1:
     resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
-    dev: true
 
   /tsutils@3.21.0(typescript@5.5.4):
     resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
@@ -11232,6 +11251,17 @@ packages:
       webpack: 5.96.1
     dev: true
 
+  /vue-qrcode@2.2.2(qrcode@1.5.4)(vue@3.2.13):
+    resolution: {integrity: sha512-SbrXq/mSb1g2tbDyXPe9gy9KiMYsvxWKRErlpij1BqiFoHwQckheZV63CTw6yRLLUVG2RXAVlX+APkpdCK7SQQ==}
+    peerDependencies:
+      qrcode: ^1.0.0
+      vue: ^2.7.0 || ^3.0.0
+    dependencies:
+      qrcode: 1.5.4
+      tslib: 2.8.1
+      vue: 3.2.13
+    dev: false
+
   /vue-router@4.0.3(vue@3.2.13):
     resolution: {integrity: sha512-AD1OjtVPyQHTSpoRsEGfPpxRQwhAhxcacOYO3zJ3KNkYP/r09mileSp6kdMQKhZWP2cFsPR3E2M3PZguSN5/ww==}
     peerDependencies:
@@ -11511,6 +11541,10 @@ packages:
       webidl-conversions: 6.1.0
     dev: true
 
+  /which-module@2.0.1:
+    resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
+    dev: false
+
   /which-typed-array@1.1.15:
     resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
     engines: {node: '>= 0.4'}
@@ -11561,7 +11595,6 @@ packages:
       ansi-styles: 4.3.0
       string-width: 4.2.3
       strip-ansi: 6.0.1
-    dev: true
 
   /wrap-ansi@7.0.0:
     resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
@@ -11633,6 +11666,10 @@ packages:
     engines: {node: '>=0.4'}
     dev: true
 
+  /y18n@4.0.3:
+    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+    dev: false
+
   /y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
@@ -11655,6 +11692,14 @@ packages:
     engines: {node: '>= 6'}
     dev: true
 
+  /yargs-parser@18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+    dev: false
+
   /yargs-parser@20.2.9:
     resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
     engines: {node: '>=10'}
@@ -11665,6 +11710,23 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /yargs@15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cliui: 6.0.0
+      decamelize: 1.2.0
+      find-up: 4.1.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 4.2.3
+      which-module: 2.0.1
+      y18n: 4.0.3
+      yargs-parser: 18.1.3
+    dev: false
+
   /yargs@16.2.0:
     resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
     engines: {node: '>=10'}

二进制
src/assets/images/syjy/icon-close1.png


二进制
src/assets/images/syjy/icon-close2-min.png


+ 9 - 2
src/store/module/base.ts

@@ -2,8 +2,15 @@ import { defineStore } from 'pinia';
 import type { BaseStateType } from '../types';
 
 const useBaseStore = defineStore('base', {
-  state: (): BaseStateType => ({}),
-  actions: {},
+  state: (): BaseStateType => ({
+    manageJsLoaded: false,
+    hotJsLoaded: false,
+  }),
+  actions: {
+    handleJSLoaded(key: string) {
+      this[key] = true;
+    },
+  },
 });
 
 export default useBaseStore;

+ 4 - 1
src/store/types.ts

@@ -1 +1,4 @@
-export interface BaseStateType {}
+export interface BaseStateType {
+  manageJsLoaded: boolean;
+  hotJsLoaded: boolean;
+}

+ 33 - 52
src/views/home/components/guide/index.syjy.scss

@@ -44,12 +44,12 @@
   align-items: center;
   width: calc(100% - 58px);
   height: 100%;
-  background: rgba(27, 27, 28, 0.6);
+  background: rgba(157, 34, 45, 0.9);
 }
 
 .frame {
-  margin: 0 10px;
-  width: calc(100% - 20px);
+  margin: 0 20px;
+  width: calc(100% - 40px);
 }
 
 .frame.noScroll {
@@ -69,9 +69,21 @@
   flex-direction: column-reverse;
   align-items: center;
   justify-content: center;
-  width: 186px;
-  height: 130px;
+  margin: 0 10px;
+  width: 180px;
+  height: 110px;
   cursor: pointer;
+  box-sizing: border-box;
+
+  img {
+    border-radius: 10px;
+    overflow: hidden;
+  }
+  &.active {
+    img {
+      border: 2px solid #f5dd8c;
+    }
+  }
 }
 
 .frame .slidee li .overlay {
@@ -98,37 +110,9 @@
 }
 
 .frame .slidee li img {
-  width: 144px;
-  height: 83px;
+  width: 100%;
+  height: 100%;
   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 {
@@ -182,33 +166,30 @@
 
 @media only screen and (max-width: 487px), (max-height: 487px) {
   #drawer.open {
-    height: 115px;
+    height: 100px;
+  }
+  .frame {
+    margin: 0 7px;
+    width: calc(100% - 14px);
   }
   .frame .slidee {
     gap: 0;
 
     li {
-      width: 135px;
-      height: 94px;
+      margin: 0 4px;
+      width: auto;
+      height: auto;
+      gap: 3px;
 
-      &.active {
-        top: -1px;
-      }
       img {
-        width: 112px;
-        height: 64px;
+        width: 100px !important;
+        height: 60px !important;
         border: none;
+        border-radius: 7px;
       }
       .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));
+        margin-top: unset;
+        font-size: 12px;
       }
     }
   }

+ 1 - 1
src/views/home/components/guide/index.syjy.tsx

@@ -1,5 +1,5 @@
 import { defineComponent } from 'vue';
-import './index.zgrs.scss';
+import './index.syjy.scss';
 
 export default defineComponent({
   name: 'HomeGuide',

+ 117 - 0
src/views/home/components/hot-spot-list/index.syjy.scss

@@ -0,0 +1,117 @@
+#hotListWrap {
+  display: flex;
+  flex-direction: column;
+  position: absolute;
+  top: 0;
+  left: -400px;
+  padding-bottom: 30px;
+  width: 356px;
+  height: 100%;
+  box-sizing: border-box;
+  background: rgba(157, 34, 45, 0.9);
+  transition: left 0.4s, width 0.5s;
+  z-index: var(--z-index-popper);
+}
+
+.hotListActive {
+  left: 0 !important;
+}
+
+#hotListTitle {
+  margin: 0 40px;
+  padding: 64px 0 28px;
+  color: #f5dd8c;
+  font-size: 24px;
+  font-weight: 700;
+  border-bottom: 1px solid white;
+}
+
+#hotListContent {
+  width: 100%;
+  /* opacity: 0.65; */
+  flex-grow: 1;
+  height: 100%;
+  overflow-y: scroll;
+  overflow-x: hidden;
+}
+
+#hotListClose {
+  position: absolute;
+  width: 26px;
+  right: 9px;
+  top: 70px;
+  cursor: pointer;
+}
+
+#hotListContent ul {
+  padding: 0 40px;
+  font-size: 16px;
+}
+
+#hotListContent ul li {
+  height: 50px;
+  line-height: 50px;
+  color: white;
+  padding: 0 10px;
+  transition: color 0.3s, background 0.6s;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  border-bottom: 1px solid white;
+  cursor: pointer;
+}
+
+#hotListContent ul li:hover {
+  color: #f5dd8c;
+}
+
+#hotListIcon {
+  width: 34px;
+  margin-left: 48px;
+}
+
+#hotListContent::-webkit-scrollbar {
+  width: 4px;
+}
+
+#hotListContent::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  background-color: #f5dd8c;
+}
+
+#hotListContent::-webkit-scrollbar-track {
+  border-radius: 10px;
+}
+
+@media only screen and (max-width: 600px) {
+  #hotListWrap {
+    padding: 0 8px;
+    width: 250px;
+    box-sizing: border-box;
+  }
+
+  #hotListTitle {
+    margin: 0 5px;
+    padding: 25px 0 7px;
+    font-size: 18px;
+    border: none;
+  }
+  #hotListContent {
+    ul {
+      padding: 0 2px;
+      font-size: 13px;
+
+      li {
+        padding: 0;
+        height: 40px;
+        line-height: 40px;
+        border: none;
+      }
+    }
+  }
+  #hotListClose {
+    width: 23px;
+    right: 9px;
+    top: 22px;
+  }
+}

+ 23 - 0
src/views/home/components/hot-spot-list/index.syjy.tsx

@@ -0,0 +1,23 @@
+import { defineComponent } from 'vue';
+import CloseIcon from '@/assets/images/syjy/icon-close1.png';
+import './index.syjy.scss';
+
+export default defineComponent({
+  name: 'HomeHotSpotList',
+  render() {
+    return (
+      <div id="hotListWrap">
+        <div id="hotListTitle">
+          <div>
+            <span id="hotListText">热点列表</span>
+          </div>
+        </div>
+        <div id="hotListContent">
+          <ul></ul>
+        </div>
+
+        <img id="hotListClose" src={CloseIcon} alt="" />
+      </div>
+    );
+  },
+});

+ 30 - 17
src/views/home/components/menu/index.syjy.scss

@@ -27,10 +27,10 @@
     right: 30px;
   }
   &.open {
-    bottom: 155px;
+    bottom: 135px;
 
     &.playing {
-      bottom: 165px;
+      bottom: 155px;
     }
   }
 }
@@ -60,7 +60,8 @@
 
 #pullTab,
 .good-btn,
-.hotList,
+#hotList,
+.home-btn,
 #gui-modes-dollhouse,
 #gui-modes-floorplan,
 #volume,
@@ -71,6 +72,10 @@
   cursor: pointer;
 }
 
+.home-btn {
+  background: url('@/assets/images/syjy/icon-home-min.png') no-repeat center / contain;
+}
+
 #pullTab {
   background: url('@/assets/images/syjy/icon-daolan-min.png') no-repeat center / contain;
 
@@ -79,7 +84,7 @@
   }
 }
 
-.hotList {
+#hotList {
   background: url('@/assets/images/syjy/icon-redian-min.png') no-repeat center / contain;
 
   &.active {
@@ -121,11 +126,14 @@
 
 @media only screen and (max-width: 600px) {
   .pinBottom {
+    &-container {
+      bottom: 37px;
+    }
     &.open {
-      bottom: 122px;
+      bottom: 80px;
 
       &.noScroll.playing {
-        bottom: 142px;
+        bottom: 100px;
       }
     }
     &.open.right {
@@ -136,28 +144,32 @@
       }
     }
     &.left {
-      width: 100%;
-      height: 55px;
-
       .viewContainer {
-        padding-left: 15px;
+        flex-direction: column;
+        padding-left: 10px;
+        gap: 7px;
+        width: 47px;
       }
     }
     &.right {
-      right: 13px;
-      bottom: 30px;
+      position: fixed;
+      right: 8px;
+      top: 120px;
+      bottom: unset;
 
       &.playing {
         bottom: 50px;
       }
       .rightViewContainer {
         flex-direction: column;
+        gap: 7px;
 
         #sharing,
         #volume,
+        .home-btn,
         .good-btn {
-          width: 47px;
-          height: 47px;
+          width: 37px;
+          height: 37px;
         }
       }
     }
@@ -166,10 +178,11 @@
   #pause img,
   #gui-modes-map > div,
   .hotList {
-    width: 47px;
-    height: 47px;
+    width: 37px;
+    height: 37px;
   }
   #gui-modes-map {
-    gap: 5px;
+    flex-direction: column;
+    gap: 7px;
   }
 }

+ 11 - 33
src/views/home/components/menu/index.syjy.vue

@@ -25,24 +25,7 @@
         <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 id="hotList" title="热点列表" data-original-title="热点列表" style="display: none" />
           <div
             data-original-title="迷你模型"
             id="gui-modes-dollhouse"
@@ -64,7 +47,8 @@
     </div>
     <div class="pinBottom right hideTarget">
       <div class="rightViewContainer clearfix">
-        <div id="sharing" class="ui-icon wide" title="分享" @click="copyUrl"></div>
+        <div class="ui-icon wide home-btn" title="首页" @click="goToHome"></div>
+        <div id="sharing" class="ui-icon wide" title="分享" @click="shareVisible = true"></div>
         <div id="volume" class="ui-icon wide" style="display: none"></div>
         <div id="vr" class="ui-icon wide hidden" style="display: none">
           <a>
@@ -97,30 +81,20 @@
       </div>
     </div>
   </div>
+
+  <share-popup v-model:visible="shareVisible" />
 </template>
 
 <script setup lang="ts">
   import { onMounted, onUnmounted, ref } from 'vue';
-  import clipboard from 'clipboard';
   import PauseIcon from '@/assets/images/syjy/icon-manyou-1-min.png';
   import PlayIcon from '@/assets/images/syjy/icon-manyou-min.png';
   import { homeApi } from '@/api';
-  import HotSpotList from '../hot-spot-list';
+  import SharePopup from '../share-popup/index.vue';
 
   let helperVisible = false;
-  const hotspotActive = ref(false);
   const starSum = ref(0);
-
-  const copyUrl = () => {
-    clipboard.copy(window.location.href);
-
-    ElNotification({
-      title: '提示',
-      type: 'success',
-      message: '链接已复制',
-      position: 'bottom-right',
-    });
-  };
+  const shareVisible = ref(false);
 
   const closeHelper = () => {
     window.$('#interaction-modal').removeClass('fadeIn');
@@ -148,6 +122,10 @@
     starSum.value = data.starSum;
   };
 
+  const goToHome = () => {
+    location.href = 'https://houseoss.4dkankan.com/project/syjy/mobile/index.html';
+  };
+
   onMounted(() => {
     getDetail();
 

+ 45 - 0
src/views/home/components/popup/index.syjy.scss

@@ -0,0 +1,45 @@
+#popup {
+  display: none;
+  position: relative;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  backdrop-filter: blur(5px);
+  background: rgba(255, 252, 247, 0.8);
+  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;
+  top: 40px;
+  right: 30px;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  text-indent: -999em;
+  background-size: 100% 100%;
+  background: url('@/assets/images/syjy/icon-close2-min.png') no-repeat center / contain;
+}
+
+@media only screen and (max-width: 600px) {
+  #closepop {
+    top: 20px;
+    right: 20px;
+    width: 33px;
+    height: 33px;
+  }
+}

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

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

二进制
src/views/home/components/share-popup/images/bg-share-min.jpg


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

@@ -1 +1,161 @@
-<template></template>
+<template>
+  <el-dialog custom-class="share-popup" v-model="show" title="分享">
+    <p>请使用手机扫描二维码或 复制分享链接</p>
+
+    <vue-qrcode :value="url" :width="isMobile ? 150 : 180" class="share-popup__qrcode" />
+
+    <div class="share-popup-tools">
+      <div class="share-popup__btn" @click="copyUrl">复制分享链接</div>
+      <div v-if="isMobile" class="share-popup__btn" @click="saveQRCode">保存二维码</div>
+    </div>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { computed, ref, watch } from 'vue';
+  import VueQrcode from 'vue-qrcode';
+  import clipboard from 'clipboard';
+  import { storeToRefs } from 'pinia';
+  import useBaseStore from '@/store/module/base';
+
+  const baseStore = useBaseStore();
+  const { manageJsLoaded } = storeToRefs(baseStore);
+  const url = window.location.href;
+  const isMobile = ref(false);
+  const props = defineProps<{
+    visible: boolean;
+  }>();
+  const emits = defineEmits(['update:visible']);
+
+  const show = computed({
+    get() {
+      return props.visible;
+    },
+    set(v) {
+      emits('update:visible', v);
+    },
+  });
+
+  const copyUrl = () => {
+    clipboard.copy(url);
+
+    ElNotification({
+      title: '提示',
+      type: 'success',
+      message: '链接已复制',
+      position: 'bottom-right',
+    });
+  };
+
+  const saveQRCode = () => {
+    const img = document.getElementsByClassName('share-popup__qrcode');
+    if (img && img.length) {
+      const a = document.createElement('a');
+      // @ts-ignore
+      a.href = img[0].src;
+      a.download = '三亚家园.jpg';
+      a.click();
+    }
+  };
+
+  watch(manageJsLoaded, (v) => {
+    if (v) {
+      isMobile.value = window.browser.isMobile();
+    }
+  });
+</script>
+
+<style lang="scss">
+  .share-popup {
+    --el-dialog-width: 420px;
+    --el-dialog-border-radius: 0;
+    --el-dialog-title-font-size: 24px;
+    height: 580px;
+    background: url('./images/bg-share-min.jpg') no-repeat center / contain;
+
+    .el-dialog__header {
+      padding: 50px 40px;
+    }
+    .el-dialog__title {
+      color: #f5dd8c;
+      font-weight: 700;
+    }
+    .el-dialog__body {
+      padding: 0 60px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+
+      p {
+        width: 220px;
+        color: white;
+        font-size: 20px;
+        text-align: center;
+      }
+    }
+    .el-dialog__headerbtn {
+      --el-color-info: white;
+      top: 35px;
+      right: 20px;
+      font-size: 26px;
+    }
+    &__qrcode {
+      margin: 25px 0 40px;
+      overflow: hidden;
+      border-radius: 10px;
+    }
+    &__btn {
+      flex: 1;
+      height: 50px;
+      line-height: 50px;
+      text-align: center;
+      color: #9d222d;
+      font-size: 20px;
+      background: #f5dd8c;
+      border-radius: 100px;
+      cursor: pointer;
+    }
+    &-tools {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      width: 100%;
+    }
+  }
+
+  @media only screen and (max-width: 600px) {
+    .share-popup {
+      --el-dialog-width: 314px;
+      --el-dialog-border-radius: 0;
+      --el-dialog-title-font-size: 16px;
+      height: 433px;
+
+      .el-dialog__header {
+        padding: 30px;
+      }
+      .el-dialog__body {
+        padding: 0 30px;
+
+        p {
+          width: 160px;
+          font-size: 16px;
+        }
+      }
+      .el-dialog__headerbtn {
+        top: 15px;
+        right: 6px;
+        font-size: 23px;
+      }
+      &__qrcode {
+        margin: 18px 0 37px;
+        border-radius: 3px;
+      }
+      &__btn {
+        height: 43px;
+        line-height: 43px;
+        font-size: 16px;
+      }
+    }
+  }
+</style>

+ 190 - 0
src/views/home/components/title/index.syjy.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('/public/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/views/home/components/title/index.syjy.tsx

@@ -0,0 +1,84 @@
+import { defineComponent } from 'vue';
+import './index.syjy.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>
+    );
+  },
+});

+ 24 - 0
src/views/home/index.syjy.scss

@@ -86,6 +86,24 @@
   }
 }
 
+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 {
@@ -96,4 +114,10 @@
       z-index: 1001;
     }
   }
+  div.cad {
+    top: 20px;
+    right: 8px;
+    width: 90px;
+    height: 90px;
+  }
 }

+ 21 - 2
src/views/home/index.syjy.tsx

@@ -8,7 +8,9 @@ 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 BackIcon from '@/assets/images/syjy/icon-back-min.png';
+import useBaseStore from '@/store/module/base';
 import './index.syjy.scss';
 
 // @ts-ignore
@@ -29,12 +31,14 @@ export default defineComponent({
     Popup,
   },
   setup() {
+    const baseStore = useBaseStore();
     const manageJsLoaded = ref(false);
     const hotJsLoaded = ref(false);
 
     return {
       manageJsLoaded,
       hotJsLoaded,
+      baseStore,
     };
   },
   render() {
@@ -61,6 +65,9 @@ export default defineComponent({
         {/* 热点弹出框 */}
         <Popup />
 
+        {/* 热点列表 */}
+        <HotSpotList />
+
         {/* 场景canvs主容器 */}
         <div id="player" />
 
@@ -87,10 +94,22 @@ export default defineComponent({
         </div>
 
         {/* TODO: 没有控制权,耦合严重;放在此处为了防止元素未渲染导致报错 */}
-        <JsScript src="/js/manage.js" onLoad={() => (this.manageJsLoaded = true)} />
+        <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)} />
+            <JsScript
+              src="/js/Hot.js"
+              onLoad={() => {
+                this.hotJsLoaded = true;
+                this.baseStore.handleJSLoaded('hotJsLoaded');
+              }}
+            />
             {this.hotJsLoaded && (
               <div>
                 <JsScript src="/js/main_2020_show.js" />

+ 2 - 2
vue.config.js

@@ -31,8 +31,8 @@ module.exports = defineConfig({
     },
     hotspot: {
       template: 'hotspot/hotspot.html',
-      entry: 'hotspot/main.ts',
-      filename: IS_PRODUCTION ? `${SCENE ? SCENE + '-hotspot' : 'hotspot'}.html` : 'hotspot.html',
+      entry: `hotspot/${SCENE ? SCENE + '-' : ''}main.ts`,
+      filename: IS_PRODUCTION ? `${SCENE ? SCENE + '-' : ''}hotspot.html` : 'hotspot.html',
       title: process.env.TITLE,
     },
   },