Forráskód Böngészése

Merge branch 'test' of http://192.168.0.115:3000/chenlei/vue3-scene-web into demo

chenlei 11 hónapja
szülő
commit
941d979c59

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+public

+ 1 - 0
.gitignore

@@ -2,6 +2,7 @@
 .temp
 node_modules
 /build
+/dist
 
 
 # local env files

+ 1 - 0
.prettierignore

@@ -1,4 +1,5 @@
 build/
+public/
 **/*.png
 **/*.svg
 **/*.jpg

+ 2 - 1
.prettierrc

@@ -5,5 +5,6 @@
   "trailingComma": "es5",
   "printWidth": 100,
   "proseWrap": "never",
-  "vueIndentScriptAndStyle": true
+  "vueIndentScriptAndStyle": true,
+  "endOfLine": "auto"
 }

+ 33 - 17
README.md

@@ -32,6 +32,8 @@ yarn serve
 
 ### 多场景模式
 
+⚠️ `/public` 下为公用文件,不要在场景分支里修改
+
 通过 `process.env.SCENE` 区分场景
 
 尽量避免使用 `.vue` 自定义后缀,ide 暂不能友好支持模糊匹配 `.vue`,需要明确使用 `demo.vue`,导致无法区分场景
@@ -70,30 +72,44 @@ yarn serve
 测试环境项目地址:https://scene.4dage.com/?m=1172
 
 ```bash
-mv .temp/* .
-
 scene=${SPUG_GIT_BRANCH%%/*}
-lastFileDir=$(ls -d -F /home/spug_backup/vue3-scene-web/* -t | grep '/$' | head -n 2 | tail -n 1)
 
 echo "当前场景值:$scene"
-echo "上一个部署目录:$lastFileDir"
 
-if [ -d "${lastFileDir}data/" ]
+if [ $SPUG_DEPLOY_TYPE == "2" ]
 then
-  echo "copy data file"
-  cp -r -n ${lastFileDir}data/ .
-fi
+  echo "开始回滚"
+  basename "$PWD"
+else
+  mv .temp/* .
 
-if [ -d "${lastFileDir}resources/web/" ]
-then
-  echo "copy resources file"
-  if [ $scene = "test" ]
+  fileCount=$(find /home/spug_backup/vue3-scene-web/* -maxdepth 0 -type d -printf '.' | wc -c)
+  echo "当前文件夹数量:$fileCount"
+
+  if [ ${fileCount} -gt 1 ]
   then
-    rsync -au --exclude=js --exclude=img --exclude=fonts --exclude=css ${lastFileDir}resources/web ./resources
-  else
-    rsync -au --exclude=$scene ${lastFileDir}resources/web ./resources
+    lastFileDir=$(ls -d -F /home/spug_backup/vue3-scene-web/* -t | grep '/$' | head -n 2 | tail -n 1)
+
+    echo "上一个部署目录:$lastFileDir"
+
+    if [ -d "${lastFileDir}data/" ]
+    then
+      echo "copy data file"
+      cp -rTn ${lastFileDir}data/ ./data
+    fi
+
+    if [ -d "${lastFileDir}resources/web/" ]
+    then
+      echo "copy resources file"
+      if [ $scene = "test" ]
+      then
+        rsync -rtvu --exclude=./js --exclude=./img --exclude=./fonts --exclude=./css ${lastFileDir}resources/web/ ./resources/web/
+      else
+        rsync -rtvu --exclude=$scene ${lastFileDir}resources/web/ ./resources/web/
+      fi
+    fi
   fi
-fi
 
-rm -r .temp
+  rm -r .temp
+fi
 ```

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-left-min.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-right-min.png


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


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


+ 29 - 0
hotspot/hotspot.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8" />
+    <meta
+      name="viewport"
+      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, minimal-ui"
+    />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+    <link
+      rel="icon"
+      href="<%= BASE_URL %>favicon/favicon<%= !!process.env.SCENE ? '-' + process.env.SCENE : '' %>.ico"
+    />
+    <title><%= htmlWebpackPlugin.options.title %></title>
+    <meta name="description" content="四维时代" />
+    <meta property="og:title" content="四维时代" />
+    <meta property="og:description" content="四维时代" />
+    <meta property="og:image:type" content="image/jpg" />
+  </head>
+  <body>
+    <noscript>
+      <strong
+        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without
+        JavaScript enabled. Please enable it to continue.</strong
+      >
+    </noscript>
+    <div id="app"></div>
+  </body>
+</html>

+ 6 - 0
hotspot/main.ts

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

+ 6 - 0
hotspot/shims-vue.d.ts

@@ -0,0 +1,6 @@
+/* eslint-disable */
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue';
+  const component: DefineComponent<{}, {}, any>;
+  export default component;
+}

+ 264 - 0
hotspot/views/hotspot/index.scss

@@ -0,0 +1,264 @@
+.hotspot-page {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(255, 252, 247, 0.8);
+  z-index: var(--z-index-popper);
+
+  .audioIcon {
+    position: absolute;
+    top: 40px;
+    right: 128px;
+
+    img {
+      width: 52px;
+      height: 52px;
+      cursor: pointer;
+    }
+  }
+  &-info {
+    position: absolute;
+    top: 42px;
+    left: 40px;
+    max-width: 50%;
+
+    h3 {
+      margin-bottom: 14px;
+      color: #c41800;
+      font-size: 24px;
+      line-height: 28px;
+    }
+    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;
+      }
+    }
+  }
+
+  &-main {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    max-width: 1100px;
+    width: calc(100vw - 548px);
+    transform: translate(-50%, -50%);
+  }
+  &-swiper {
+    &__left,
+    &__right {
+      position: absolute;
+      top: 50%;
+      width: 50px;
+      height: 50px;
+      cursor: pointer;
+      transform: translateY(-50%);
+      z-index: 1;
+    }
+    &__left {
+      left: -70px;
+      background: url('@hotspot/assets/images/icon-previous@2x-min.png') no-repeat center / contain;
+    }
+    &__right {
+      right: -70px;
+      background: url('@hotspot/assets/images/icon-next@2x-min.png') no-repeat center / contain;
+    }
+  }
+  &-model {
+    width: 100%;
+    height: 600px;
+
+    iframe {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  &-video {
+    width: 100%;
+    height: 561px;
+    object-fit: cover;
+  }
+  &-img {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: inherit;
+
+    &-swiper {
+      height: 567px;
+    }
+    img {
+      width: 100%;
+      height: 100%;
+      object-fit: contain;
+    }
+  }
+
+  &-nav {
+    position: absolute;
+    left: 50%;
+    bottom: 104px;
+    display: flex;
+    align-items: center;
+    gap: 30px;
+    transform: translateX(-50%);
+
+    &__item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 9px;
+      width: 120px;
+      height: 36px;
+      border-radius: 18px;
+      color: #666666;
+      background: #d0cec6;
+      border: 1px solid #666666;
+      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;
+      }
+    }
+  }
+}
+
+@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;
+      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;
+    }
+    p {
+      flex: 1;
+      padding-right: 2px;
+      width: 100%;
+      height: 0;
+      color: white;
+      font-size: 15px;
+
+      &::-webkit-scrollbar {
+        width: 2px;
+      }
+      &::-webkit-scrollbar-thumb {
+        background-color: #ddaf35;
+      }
+    }
+  }
+  .hotspot-page-nav {
+    bottom: 194px;
+    gap: 13px;
+
+    &__item {
+      width: 70px;
+      height: 20px;
+      font-size: 10px;
+      gap: 4px;
+
+      .video-icon,
+      .img-icon {
+        width: 12px;
+        height: 10px;
+      }
+      .model-icon {
+        width: 10px;
+        height: 11px;
+      }
+    }
+  }
+  .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%);
+  }
+
+  .hotspot-page-video {
+    width: 300px;
+    height: 168px;
+  }
+  .hotspot-page-img-swiper,
+  .hotspot-page-model {
+    height: 50vh;
+  }
+}

+ 266 - 0
hotspot/views/hotspot/index.vue

@@ -0,0 +1,266 @@
+<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">
+      <!-- 音频播放器 -->
+      <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>
+
+      <!-- 视频页面 -->
+      <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">
+          <video
+            v-if="index === myInd"
+            id="videoID"
+            class="hotspot-page-video"
+            controls
+            :src="item.url"
+            autoplay
+          />
+        </SwiperSlide>
+      </Swiper>
+
+      <!-- 图片页面 -->
+      <Swiper
+        v-if="myType === 'img'"
+        class="hotspot-page-swiper hotspot-page-img-swiper"
+        @swiper="initSwiper"
+        @slideChange="handleChange"
+      >
+        <SwiperSlide v-for="item in curList" :key="item">
+          <div class="hotspot-page-img">
+            <img :src="item" alt="" />
+          </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>
+    </div>
+
+    <!-- 底部的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>
+</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() {
+        this.swiper?.slidePrev();
+      },
+      handleNext() {
+        this.swiper?.slideNext();
+      },
+    },
+  };
+</script>
+
+<style lang="scss">
+  @import './index.scss';
+</style>

+ 2 - 3
package.json

@@ -3,8 +3,8 @@
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "serve": "cross-env TITLE=大理洱海科普馆 vue-cli-service serve",
-    "build:test": "cross-env TITLE=大理洱海科普馆 vue-cli-service build",
+    "serve": "cross-env TITLE=大理洱海科普馆 HOT_DOMAIN=/hotspot.html vue-cli-service serve",
+    "build:test": "cross-env TITLE=大理洱海科普馆 HOT_DOMAIN=/hotspot.html vue-cli-service build",
     "push:test": "cross-env node ./scripts/publish.js",
 
     "serve:demo": "cross-env SCENE=demo TITLE=demo vue-cli-service serve",
@@ -56,7 +56,6 @@
     "lint-staged": "^11.1.2",
     "lodash-webpack-plugin": "^0.11.6",
     "node-polyfill-webpack-plugin": "^2.0.1",
-    "prettier": "^2.4.1",
     "sass": "^1.32.7",
     "sass-loader": "^12.0.0",
     "ts-jest": "^27.0.4",

+ 86 - 69
public/js/loadCAD.js

@@ -1,44 +1,43 @@
 window.grendCAD = (function grendCAD() {
-  let initFloor
-  let initScript
-  let initDOM
-  let point, dire
+  let initFloor;
+  let initScript;
+  let initDOM;
+  let point, dire;
 
   window.cad = {
-    setSign: function(p, d) {
-      point = p
-      dire = d
-    }
-  }
+    setSign: function (p, d) {
+      point = p;
+      dire = d;
+    },
+  };
 
   function loadScript(cb) {
-    if (initScript) return cb()
+    if (initScript) return cb();
 
-    let $script = document.createElement('script')
-    $script.src = '//www.4dmodel.com/CAD/bundle.js'
-    $script.onload = function() {
-      initScript = true
-      cb()
-    }
+    let $script = document.createElement('script');
+    $script.src = '//www.4dmodel.com/CAD/bundle.js';
+    $script.onload = function () {
+      initScript = true;
+      cb();
+    };
 
-    document.documentElement.appendChild($script)
+    document.documentElement.appendChild($script);
   }
 
   function loadDOM($parent) {
     if (initDOM) return initDOM;
-    let $layer = document.createElement('div')
-    let $cad = document.createElement('div')
-
-    $layer.className = 'cad'
-    $cad.id = 'cad'
-    $layer.appendChild($cad)
+    let $layer = document.createElement('div');
+    let $cad = document.createElement('div');
 
+    $layer.className = 'cad';
+    $cad.id = 'cad';
+    $layer.appendChild($cad);
 
-    let style = document.createElement('style')
+    let style = document.createElement('style');
     style.innerHTML = `
       .cad {
         position: absolute;
-        right: 80px;
+        right: 16px;
         top: 16px;
         width: 200px;
         height: 200px;
@@ -54,7 +53,6 @@ window.grendCAD = (function grendCAD() {
       @media only screen and (max-width: 600px) { 
         .cad {
             position: absolute;
-            left: 16px;
             top: 65px;
             width: 100px;
             height: 100px;
@@ -62,71 +60,90 @@ window.grendCAD = (function grendCAD() {
             border-radius: 5px;
         }
       }
-    `
+    `;
 
-    document.documentElement.appendChild(style)
-    document.documentElement.appendChild($layer)
-    $parent.appendChild(style)
-    $parent.appendChild($layer)
-    return $layer
+    document.documentElement.appendChild(style);
+    document.documentElement.appendChild($layer);
+    $parent.appendChild(style);
+    $parent.appendChild($layer);
+    return $layer;
   }
 
-
   function setStyle(signColor, borderColor, borderWidth) {
     cad.setDefaultPointStyle({
-      fillColor: "rgba(0,0,0,0)",
-      storkeColor: "rgba(0,0,0,0)"
+      fillColor: 'rgba(0,0,0,0)',
+      storkeColor: 'rgba(0,0,0,0)',
     });
 
-    console.log(borderWidth)
+    console.log(borderWidth);
     cad.setDefaultLineStyle({
       width: borderWidth,
-      color: borderColor
+      color: borderColor,
     });
 
     cad.setDefaultSignStyle({
-      color: signColor
-    })
+      color: signColor,
+    });
   }
 
-
-  return function(floor, $parent, signColor, borderColor, borderWidth) {
+  return function (floor, $parent, signColor, borderColor, borderWidth) {
     if (initFloor) {
-      console.log('cache')
-      return setStyle(signColor, borderColor, borderWidth)
+      console.log('cache');
+      return setStyle(signColor, borderColor, borderWidth);
     }
-    console.log('load')
-    initFloor = floor
+    console.log('load');
+    initFloor = floor;
 
-    loadScript(function() {
-      let $layer = loadDOM($parent)
+    loadScript(function () {
+      let $layer = loadDOM($parent);
 
-      $layer.style.visibility = 'hidden'
+      $layer.style.visibility = 'hidden';
       window.cad = structureCAD({
         data: {
-            block: [],
-            column: [],
-            door: [],
-            hole: [],
-            segment: [],
-            "vertex-xy": [],
-            "vertex-z": [],
-        }, 
+          block: [],
+          column: [],
+          door: [],
+          hole: [],
+          segment: [],
+          'vertex-xy': [],
+          'vertex-z': [],
+        },
         layer: $layer.querySelector('#cad'),
-        edit: false
+        edit: false,
       });
-      
-      setStyle(signColor, borderColor, borderWidth)
-      cad.hideDire()
-      cad.hideGauge()
-  
-      console.log('loadData')
+
+      setStyle(signColor, borderColor, borderWidth);
+      cad.hideDire();
+      cad.hideGauge();
+
+      console.log('loadData');
       cad.loadData(initFloor);
       if (point && dire) {
-        window.cad.setSign(point, dire)
+        window.cad.setSign(point, dire);
       }
-      $layer.style.visibility = 'visible'
-    })
-
-  }
-})();
+      $layer.style.visibility = 'visible';
+    });
+  };
+})();
+
+$.ajax({
+  url: g_Prefix + 'data/' + window.number + '/someData.json' + '?' + Date.now(),
+  method: 'GET',
+  success(data) {
+    if (!data.showCad) return;
+
+    $.ajax({
+      url: '//super.4dage.com/data/' + window.number + '/floor.json',
+      method: 'GET',
+      success(res) {
+        grendCAD(
+          res,
+          document.querySelector('body'),
+          data.cadSignColor,
+          data.cadBorderColor,
+          data.cadBorderWidth
+        );
+      },
+    });
+  },
+});

+ 29 - 29
public/js/main_2020_show.js

@@ -5191,7 +5191,7 @@ window.Modernizr = (function (n, e, t) {
                 ((this.currentItem = null),
                 (this.destinationItem = null),
                 (this.tourIsPlaying = !1),
-                //this.bgmReplay(),//xzw
+                // this.bgmReplay(),//xzw
                 (this.transitionStage = y.None),
                 (this.nextFunc = null),
                 (this.onTheBus = !1),
@@ -5730,11 +5730,11 @@ window.Modernizr = (function (n, e, t) {
                     this.emit('update.controls'),
                     this.emit(p.TourStart),
                     this.player.enablePreRendering(),
-                    /* this.walkingSectionPaused ? (this.clearWalkingSectionPaused(),
-                    this.goToDestination()) : this.goNext())) */
+                    this.walkingSectionPaused ? (this.clearWalkingSectionPaused(),
+                    this.goToDestination()) : this.goNext())
 
-                    this.setDestinationItem(this.findNearestItem()),
-                    this.goToDestination())
+                    // this.setDestinationItem(this.findNearestItem()),
+                    // this.goToDestination())
                   );
             }
           }),
@@ -5903,9 +5903,9 @@ window.Modernizr = (function (n, e, t) {
 
               // // zeg改
               if (null === this.currentItem || void 0 === this.currentItem) {
-                // this.setDestinationItem(this.firstDestination())
+                this.setDestinationItem(this.firstDestination())
                 // this.setDestinationItem([currentPanoDestinations[0], 0])
-                this.setDestinationItem([this.currentItem[0], 0]);
+                // this.setDestinationItem([this.currentItem[0], 0]);
               }
               // else if(this.currentItem[1] == null) {
               //     this.setDestinationItem([this.currentItem[0], 0])
@@ -8709,28 +8709,28 @@ window.Modernizr = (function (n, e, t) {
                 this.classList.add('active');
               }
             }),
-              $('#hotListContent ul').on('mouseover', function (e) {
-                l(e);
-                var target = e.target;
-                var hotList = document.getElementById('hotListWrap');
-                if (target.tagName === 'SPAN') {
-                  var targetParent = target.parentElement.parentElement;
-                  if (target.offsetWidth + 116 >= targetParent.clientWidth) {
-                    hotList.style.width = target.offsetWidth + 116 + 'px';
-                  } else {
-                    hotList.style.width = '';
-                  }
-                } else if (target.tagName === 'LI') {
-                  var targetParent = target.parentElement;
-                  if (target.children[0].offsetWidth + 116 >= targetParent.clientWidth) {
-                    hotList.style.width = target.children[0].offsetWidth + 116 + 'px';
-                  } else {
-                    hotList.style.width = '';
-                  }
-                } else {
-                  hotList.style.width = '';
-                }
-              });
+              // $('#hotListContent ul').on('mouseover', function (e) {
+              //   l(e);
+              //   var target = e.target;
+              //   var hotList = document.getElementById('hotListWrap');
+              //   if (target.tagName === 'SPAN') {
+              //     var targetParent = target.parentElement.parentElement;
+              //     if (target.offsetWidth + 116 >= targetParent.clientWidth) {
+              //       hotList.style.width = target.offsetWidth + 116 + 'px';
+              //     } else {
+              //       hotList.style.width = '';
+              //     }
+              //   } else if (target.tagName === 'LI') {
+              //     var targetParent = target.parentElement;
+              //     if (target.children[0].offsetWidth + 116 >= targetParent.clientWidth) {
+              //       hotList.style.width = target.children[0].offsetWidth + 116 + 'px';
+              //     } else {
+              //       hotList.style.width = '';
+              //     }
+              //   } else {
+              //     hotList.style.width = '';
+              //   }
+              // });
             $('#hotListContent ul').on('click', function (e) {
               l(e);
               this.preElem && this.preElem.classList.remove('active');

+ 5 - 4
public/js/manage.js

@@ -1768,9 +1768,11 @@ Manage.prototype.loadAudio = function () {
       if (state) {
         $('#volume a img').attr('src', './images/Volume btn_off.png');
         $('#volume').attr('title', '关闭声音');
+        $('#volume').addClass('active');
       } else {
         $('#volume a img').attr('src', './images/Volume btn_on.png');
         $('#volume').attr('title', '打开声音');
+        $('#volume').removeClass('active');
       }
     },
   });
@@ -1793,11 +1795,10 @@ Manage.prototype.loadAudio = function () {
   });
 
   $('#volume')
-    .find('a')
-    .on('click', () => {
-      if ($('#volume img')[0].src.indexOf('btn_on.png') > -1) {
+    .on('click', (e) => {
+      if (!$('#volume').hasClass('active')) {
         this.switchBgmState(true);
-      } else if ($('#volume img')[0].src.indexOf('btn_off.png') > -1) {
+      } else {
         this.switchBgmState(false);
       }
     });

+ 2 - 2
scripts/publish.js

@@ -5,9 +5,9 @@ const SCENE = process.env.SCENE;
 fs.ensureDirSync('.temp');
 fs.emptyDirSync('.temp');
 
-const distDir = `build${!!SCENE ? '/' + SCENE : ''}`;
+const distDir = `build${SCENE ? '/' + SCENE : ''}`;
 
-ch.execSync(`npm run build${!!SCENE ? ':' + SCENE : ''}:test`, {
+ch.execSync(`npm run build${SCENE ? ':' + SCENE : ''}:test`, {
   stdio: ['ignore', 'inherit', 'inherit'],
 });
 

+ 139 - 138
src/app.scss

@@ -1,138 +1,139 @@
-:root {
-  --z-index-normal: 1;
-  --z-index-top: 1000;
-  --z-index-popper: 2000;
-}
-
-body,
-ol,
-ul,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-p,
-th,
-td,
-dl,
-dd,
-form,
-fieldset,
-legend,
-input,
-textarea,
-select {
-  margin: 0;
-  padding: 0;
-}
-* {
-  box-sizing: border-box;
-  user-select: none;
-}
-html,
-body,
-#app {
-  width: 100%;
-  height: 100%;
-  overflow: hidden;
-}
-body {
-  margin: 0px;
-  font-size: 14px;
-  color: rgba(255, 255, 255, 0.9);
-  font-family: OpenSans, sans-serif;
-  -webkit-tap-highlight-color: transparent;
-}
-a {
-  color: #fff;
-  cursor: pointer;
-  text-decoration: none;
-}
-em {
-  font-style: normal;
-}
-li {
-  list-style: none;
-}
-img {
-  border: 0;
-  vertical-align: middle;
-}
-table {
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-p {
-  word-wrap: break-word;
-}
-iframe {
-  border: none;
-}
-
-@font-face {
-  font-family: OpenSans;
-  src: url('/public/fonts/open-sans/OpenSansRegular.woff2') format('woff2');
-  font-weight: 400;
-}
-
-@font-face {
-  font-family: OpenSans;
-  src: url('/public/fonts/open-sans-light/OpenSansLight.eot');
-  src: url('/public/fonts/open-sans-light/OpenSansLight.eot') format('embedded-opentype'),
-    url('/public/fonts/open-sans-light/OpenSansLight.woff2') format('woff2'),
-    url('/public/fonts/open-sans-light/OpenSansLight.woff') format('woff'),
-    url('/public/fonts/open-sans-light/OpenSansLight.ttf') format('truetype'),
-    url('/public/fonts/open-sans-light/OpenSansLight.svg#OpenSansLight') format('svg');
-  font-weight: 100;
-}
-
-@font-face {
-  font-family: OpenSans;
-  src: url('/public/fonts/open-sans-semibold/OpenSansSemibold.eot');
-  src: url('/public/fonts/open-sans-semibold/OpenSansSemibold.eot') format('embedded-opentype'),
-    url('/public/fonts/open-sans-semibold/OpenSansSemibold.woff2') format('woff2'),
-    url('/public/fonts/open-sans-semibold/OpenSansSemibold.woff') format('woff'),
-    url('/public/fonts/open-sans-semibold/OpenSansSemibold.ttf') format('truetype'),
-    url('/public/fonts/open-sans-semibold/OpenSansSemibold.svg#OpenSansSemibold') format('svg');
-  font-weight: 700;
-}
-
-.limit-line {
-  display: -webkit-box;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  -webkit-line-clamp: 1;
-  -webkit-box-orient: vertical;
-  word-break: break-all;
-  word-wrap: break-word;
-}
-
-.line-2 {
-  -webkit-line-clamp: 2;
-}
-
-.line-3 {
-  -webkit-line-clamp: 3;
-}
-
-.hidden {
-  display: none !important;
-  visibility: hidden !important;
-}
-
-.darkGlass {
-  background-color: rgba(0, 0, 0, 0.5);
-}
-
-.message-outer {
-  position: absolute;
-  display: table;
-  height: 100%;
-  width: 100%;
-
-  * {
-    transition: all 0.3s;
-  }
-}
+:root {
+  --z-index-normal: 1;
+  --z-index-top: 1000;
+  --z-index-popper: 2000;
+  --z-hot-popper: 2001;
+}
+
+body,
+ol,
+ul,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+th,
+td,
+dl,
+dd,
+form,
+fieldset,
+legend,
+input,
+textarea,
+select {
+  margin: 0;
+  padding: 0;
+}
+* {
+  box-sizing: border-box;
+  user-select: none;
+}
+html,
+body,
+#app {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+body {
+  margin: 0px;
+  font-size: 14px;
+  color: rgba(255, 255, 255, 0.9);
+  font-family: OpenSans, sans-serif;
+  -webkit-tap-highlight-color: transparent;
+}
+a {
+  color: #fff;
+  cursor: pointer;
+  text-decoration: none;
+}
+em {
+  font-style: normal;
+}
+li {
+  list-style: none;
+}
+img {
+  border: 0;
+  vertical-align: middle;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+p {
+  word-wrap: break-word;
+}
+iframe {
+  border: none;
+}
+
+@font-face {
+  font-family: OpenSans;
+  src: url('/public/fonts/open-sans/OpenSansRegular.woff2') format('woff2');
+  font-weight: 400;
+}
+
+@font-face {
+  font-family: OpenSans;
+  src: url('/public/fonts/open-sans-light/OpenSansLight.eot');
+  src: url('/public/fonts/open-sans-light/OpenSansLight.eot') format('embedded-opentype'),
+    url('/public/fonts/open-sans-light/OpenSansLight.woff2') format('woff2'),
+    url('/public/fonts/open-sans-light/OpenSansLight.woff') format('woff'),
+    url('/public/fonts/open-sans-light/OpenSansLight.ttf') format('truetype'),
+    url('/public/fonts/open-sans-light/OpenSansLight.svg#OpenSansLight') format('svg');
+  font-weight: 100;
+}
+
+@font-face {
+  font-family: OpenSans;
+  src: url('/public/fonts/open-sans-semibold/OpenSansSemibold.eot');
+  src: url('/public/fonts/open-sans-semibold/OpenSansSemibold.eot') format('embedded-opentype'),
+    url('/public/fonts/open-sans-semibold/OpenSansSemibold.woff2') format('woff2'),
+    url('/public/fonts/open-sans-semibold/OpenSansSemibold.woff') format('woff'),
+    url('/public/fonts/open-sans-semibold/OpenSansSemibold.ttf') format('truetype'),
+    url('/public/fonts/open-sans-semibold/OpenSansSemibold.svg#OpenSansSemibold') format('svg');
+  font-weight: 700;
+}
+
+.limit-line {
+  display: -webkit-box;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+  word-break: break-all;
+  word-wrap: break-word;
+}
+
+.line-2 {
+  -webkit-line-clamp: 2;
+}
+
+.line-3 {
+  -webkit-line-clamp: 3;
+}
+
+.hidden {
+  display: none !important;
+  visibility: hidden !important;
+}
+
+.darkGlass {
+  background-color: rgba(0, 0, 0, 0.5);
+}
+
+.message-outer {
+  position: absolute;
+  display: table;
+  height: 100%;
+  width: 100%;
+
+  * {
+    transition: all 0.3s;
+  }
+}

+ 5 - 2
src/configure.ts

@@ -4,7 +4,7 @@ const urlParams = queryString.parse(location.search.slice(1));
 
 window.number = urlParams.m as string;
 
-if (!!performance) {
+if (performance) {
   window.navigationStart = performance.timing.navigationStart;
 } else {
   window.navigationStart = Date.now() + 300;
@@ -12,6 +12,9 @@ if (!!performance) {
 
 window.getHotIframePath = (src: string) => {
   return process.env.NODE_ENV === 'development' && process.env.HOT_DOMAIN
-    ? src.replace('https://www.4dmodel.com/SuperTwo/hot_online1', process.env.HOT_DOMAIN)
+    ? src.replace(
+        'https://www.4dmodel.com/SuperTwo/hot_online1/index.html#/',
+        process.env.HOT_DOMAIN
+      )
     : src;
 };

+ 13 - 3
src/utils/hot.js

@@ -784,7 +784,11 @@ window.initHot = function (model) {
 
         //addPoints(cornerPoint)
 
-        this.cornerPoints[player.currentPano.id] = { cornerPoint, diffLon, diffLat };
+        this.cornerPoints[player.currentPano.id] = {
+          cornerPoint,
+          diffLon,
+          diffLat,
+        };
 
         return this.cornerPoints[player.currentPano.id];
       }
@@ -917,14 +921,20 @@ window.initHot = function (model) {
         }
       } else if (state) {
         if (/* !isVideoPlayed(video) */ video.paused) {
-          console.log({ str: 'videoControl play ' + ', ' + this.sid, level: 1 });
+          console.log({
+            str: 'videoControl play ' + ', ' + this.sid,
+            level: 1,
+          });
           this.loadVideo(video);
           video.play();
           //video.currentTime = video.lastCurTime || 0
           this.changeOpaWhenPlay(video);
 
           if (isVideoPlayed(video))
-            console.log({ str: 'played ' + this.sid + video.duration, level: 1 });
+            console.log({
+              str: 'played ' + this.sid + video.duration,
+              level: 1,
+            });
         }
       }
     }

+ 21 - 0
src/utils/index.ts

@@ -0,0 +1,21 @@
+export function parseUrlParams(url: string): Record<string, string> {
+  const params: Record<string, string> = {};
+
+  const queryString = url.split('?')[1];
+
+  if (!queryString) {
+    return params; // 没有查询参数,返回空对象
+  }
+
+  const pairs = queryString.split('&');
+
+  for (const pair of pairs) {
+    const [key, value] = pair.split('=');
+
+    if (key) {
+      params[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
+    }
+  }
+
+  return params;
+}

+ 2 - 1
src/views/home/components/hot-spot-list/index.scss

@@ -7,7 +7,7 @@
   width: 356px;
   height: 100%;
   transition: right 0.4s, width 0.5s;
-  z-index: var(--el-index-popper);
+  z-index: var(--z-index-popper);
 }
 
 .hotListActive {
@@ -47,6 +47,7 @@
   width: 26px;
   right: 64px;
   bottom: 64px;
+  cursor: pointer;
 }
 
 #hotListContent ul {

+ 14 - 14
src/views/home/components/menu/index.scss

@@ -20,20 +20,6 @@
   line-height: 1;
   transition: all 0.5s;
 
-  &.open {
-    bottom: 140px;
-
-    &.noScroll {
-      bottom: 117px;
-
-      &.playing {
-        bottom: 135px;
-      }
-    }
-    &.playing {
-      bottom: 150px;
-    }
-  }
   &.left {
     background: rgba(0, 0, 0, 0.2);
     border-radius: 10px;
@@ -56,6 +42,20 @@
       border-radius: 10px;
     }
   }
+  &.open {
+    bottom: 140px;
+
+    &.noScroll {
+      bottom: 117px;
+
+      &.playing {
+        bottom: 135px;
+      }
+    }
+    &.playing {
+      bottom: 150px;
+    }
+  }
 }
 
 .viewContainer {

+ 1 - 1
src/views/home/components/popup/index.scss

@@ -6,7 +6,7 @@
   height: 100%;
   text-align: center;
   background: rgba(0, 0, 0, 0.6);
-  z-index: var(--z-index-popper);
+  z-index: var(--z-hot-popper);
 
   &.wait {
     opacity: 0.1;

+ 17 - 6
vue.config.js

@@ -21,7 +21,22 @@ const ENV = getEnv();
 module.exports = defineConfig({
   transpileDependencies: true,
   lintOnSave: false,
-  indexPath: `${SCENE || 'index'}.html`,
+
+  pages: {
+    scene: {
+      template: 'public/index.html',
+      entry: 'src/main.ts',
+      filename: `${SCENE || 'index'}.html`,
+      title: process.env.TITLE,
+    },
+    hotspot: {
+      template: 'hotspot/hotspot.html',
+      entry: 'hotspot/main.ts',
+      filename: `${SCENE ? SCENE + '-hotspot' : 'hotspot'}.html`,
+      title: process.env.TITLE,
+    },
+  },
+
   outputDir: IS_PRODUCTION && !!SCENE ? `build/${SCENE}` : 'build',
   // 根据场景隔离
   assetsDir: path.posix.join(config.assetsDir, SCENE || ''),
@@ -53,6 +68,7 @@ module.exports = defineConfig({
       symlinks: false,
       alias: {
         '@': path.join(__dirname, 'src'),
+        '@hotspot': path.join(__dirname, 'hotspot'),
       },
     },
     plugins: [
@@ -132,11 +148,6 @@ module.exports = defineConfig({
         },
       });
     }
-
-    webpackConfig.plugin('html').tap((args) => {
-      args[0].title = process.env.TITLE;
-      return args;
-    });
   },
 });
 

+ 1 - 1
yarn.lock

@@ -7882,7 +7882,7 @@ prettier-linter-helpers@^1.0.0:
   dependencies:
     fast-diff "^1.1.2"
 
-"prettier@^1.18.2 || ^2.0.0", prettier@^2.4.1:
+"prettier@^1.18.2 || ^2.0.0":
   version "2.7.1"
   resolved "https://registry.npmmirror.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
   integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==