浏览代码

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

chenlei 11 月之前
父节点
当前提交
b27e6972cb
共有 100 个文件被更改,包括 52609 次插入6418 次删除
  1. 1 0
      .eslintignore
  2. 2 0
      .gitignore
  3. 1 0
      .prettierignore
  4. 2 1
      .prettierrc
  5. 99 3
      README.md
  6. 5 0
      auto-imports.d.ts
  7. 13 0
      components.d.ts
  8. 2 6
      config.js
  9. 二进制
      hotspot/assets/images/Volume-off.png
  10. 二进制
      hotspot/assets/images/Volume-on.png
  11. 二进制
      hotspot/assets/images/icon-image-1@2x.png
  12. 二进制
      hotspot/assets/images/icon-image@2x.png
  13. 二进制
      hotspot/assets/images/icon-left-min.png
  14. 二进制
      hotspot/assets/images/icon-model-1@2x.png
  15. 二进制
      hotspot/assets/images/icon-model@2x.png
  16. 二进制
      hotspot/assets/images/icon-next@2x-min.png
  17. 二进制
      hotspot/assets/images/icon-previous@2x-min.png
  18. 二进制
      hotspot/assets/images/icon-right-min.png
  19. 二进制
      hotspot/assets/images/icon-video-1@2x.png
  20. 二进制
      hotspot/assets/images/icon-video@2x.png
  21. 29 0
      hotspot/hotspot.html
  22. 6 0
      hotspot/main.ts
  23. 6 0
      hotspot/shims-vue.d.ts
  24. 264 0
      hotspot/views/hotspot/index.scss
  25. 266 0
      hotspot/views/hotspot/index.vue
  26. 3 0
      jest.config.js
  27. 1 1
      lint-staged.config.js
  28. 26 10
      package.json
  29. 9781 6397
      pnpm-lock.yaml
  30. 二进制
      public/favicon.ico
  31. 二进制
      public/favicon/favicon.ico
  32. 二进制
      public/fonts/SourceHanSansCN.ttf
  33. 二进制
      public/fonts/SourceHanSansCN.woff
  34. 二进制
      public/fonts/SourceHanSansCN.woff2
  35. 二进制
      public/fonts/open-sans-light/OpenSansLight.eot
  36. 21034 0
      public/fonts/open-sans-light/OpenSansLight.svg
  37. 二进制
      public/fonts/open-sans-light/OpenSansLight.ttf
  38. 二进制
      public/fonts/open-sans-light/OpenSansLight.woff
  39. 二进制
      public/fonts/open-sans-light/OpenSansLight.woff2
  40. 二进制
      public/fonts/open-sans-semibold/OpenSansSemibold.eot
  41. 21055 0
      public/fonts/open-sans-semibold/OpenSansSemibold.svg
  42. 二进制
      public/fonts/open-sans-semibold/OpenSansSemibold.ttf
  43. 二进制
      public/fonts/open-sans-semibold/OpenSansSemibold.woff
  44. 二进制
      public/fonts/open-sans-semibold/OpenSansSemibold.woff2
  45. 二进制
      public/fonts/open-sans/OpenSansLight.woff2
  46. 二进制
      public/fonts/open-sans/OpenSansRegular.woff2
  47. 二进制
      public/fonts/proxima-nova/2B71A2_0_0.woff
  48. 二进制
      public/fonts/proxima-nova/2B71A2_1_0.woff
  49. 二进制
      public/images/4dage-logo.png
  50. 二进制
      public/images/4dagePoint.png
  51. 二进制
      public/images/4dagePoint2.png
  52. 二进制
      public/images/End.png
  53. 二进制
      public/images/End_128.png
  54. 二进制
      public/images/End_unable_128.png
  55. 二进制
      public/images/Nav_Help_Arrow_keys.png
  56. 二进制
      public/images/Nav_Help_Close.png
  57. 二进制
      public/images/Nav_Help_Highlights.png
  58. 二进制
      public/images/Nav_Help_Icon.png
  59. 二进制
      public/images/New.png
  60. 二进制
      public/images/Notes.png
  61. 二进制
      public/images/Notes_hover.png
  62. 二进制
      public/images/Personal_Pic.png
  63. 二进制
      public/images/Start.png
  64. 二进制
      public/images/VR.png
  65. 二进制
      public/images/Volume btn_off.png
  66. 二进制
      public/images/Volume btn_on.png
  67. 二进制
      public/images/auto-suspend.png
  68. 二进制
      public/images/auto.png
  69. 二进制
      public/images/btm_logo.png
  70. 二进制
      public/images/circle_active.png
  71. 二进制
      public/images/circle_activeF.png
  72. 二进制
      public/images/circle_normal.png
  73. 二进制
      public/images/circle_wait.png
  74. 二进制
      public/images/close1.png
  75. 二进制
      public/images/coordinate.png
  76. 二进制
      public/images/coordinate2.png
  77. 二进制
      public/images/coordinateClose.png
  78. 二进制
      public/images/crosshair.cur
  79. 二进制
      public/images/cursor.png
  80. 二进制
      public/images/delete.png
  81. 二进制
      public/images/division.png
  82. 13 0
      public/images/doll_label_corner.svg
  83. 二进制
      public/images/dollhouse.png
  84. 二进制
      public/images/edit/End_128.png
  85. 二进制
      public/images/edit/End_unable_128.png
  86. 二进制
      public/images/edit/VR.png
  87. 二进制
      public/images/edit/autoTour.png
  88. 二进制
      public/images/edit/box_video.png
  89. 二进制
      public/images/edit/hotStyle_1.png
  90. 二进制
      public/images/edit/hotStyle_2.png
  91. 二进制
      public/images/edit/hotpoint.png
  92. 二进制
      public/images/edit/image.png
  93. 二进制
      public/images/edit/information.png
  94. 二进制
      public/images/edit/music.png
  95. 二进制
      public/images/edit/panoVisi.png
  96. 二进制
      public/images/edit/save.png
  97. 二进制
      public/images/edit/screen.png
  98. 二进制
      public/images/engraving-icon.png
  99. 二进制
      public/images/enlarge_on.png
  100. 0 0
      public/images/export.png

+ 1 - 0
.eslintignore

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

+ 2 - 0
.gitignore

@@ -1,6 +1,8 @@
 .DS_Store
+.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"
 }

+ 99 - 3
README.md

@@ -1,19 +1,115 @@
 ### 初始化
 
 ```bash
-pnpm install
+yarn
+
+yarn serve
+```
+
+<br>
+
+### 目录结构
+
+```
+|-build
+|-public // 静态资源
+|-src
+  |- api // 接口文件
+  |- types // 类型声明
+  |- assets // 公用代码资源
+  |- components // 公用组件
+  |- views // 项目页面入口
+  |- utils // 公用工具函数
+  |- router // 路由配置
+  |- store // 全局状态管理
+  |
+  |- env.d.ts // 环境变量声明
+  |- global.d.ts // 全局变量声明
+  |- el.d.ts // element 声明
 ```
 
+<br>
+
 ### 多场景模式
 
+⚠️ `/public` 下为公用文件,不要在场景分支里修改
+
 通过 `process.env.SCENE` 区分场景
 
-尽量使用 `.tsx` 后缀,因为 `.vue` 为自定义后缀,webpack 暂不能友好支持 `.vue` 后缀,需要明确使用 `demo.vue`,导致无法区分场景
+尽量避免使用 `.vue` 自定义后缀,ide 暂不能友好支持模糊匹配 `.vue`,需要明确使用 `demo.vue`,导致无法区分场景
+
+<br>
+
+### 关于分支
+
+`release` 稳定版本,线上代码稳定一段时间后同步 `release-buffer`
+
+`release-buffer` 发布版本,场景线分支发布生产环境需要合回此分支,使用 `--no-ff` 记录合并操作
+
+<br>
 
 ### 🚀 关于代码格式化
 
-在 ide 扩展中下载 `prettier`
+推荐使用 ide 插件,在 ide 扩展中下载 `prettier`
 
 以 vscode 为例:
 
 > 在 `file -> preferences -> setting` 中搜索 `defaultFormatter`<br> 选择 `Prettier - Code formatter`<br> 接着搜索 `format`<br> 将 `editor: format on save` 勾选
+
+<br>
+
+### 🤖 关于自动化部署
+
+发布地址:http://face3d.4dage.com:29394/deploy/app
+
+新建的分支如果需要自动化部署,需要在 `package.json` 下增加指令
+
+    举例:新增一个demo大场景
+    1. 在 release-buffer 下新增 demo 分支
+    2. 在 scripts 中新增 push:demo 指令,注意 push: 后的参数需要与分支名相同
+    3. 注意:不要在 release* 下发布版本
+
+测试环境项目地址:https://scene.4dage.com/?m=1172
+
+```bash
+scene=${SPUG_GIT_BRANCH%%/*}
+
+echo "当前场景值:$scene"
+
+if [ $SPUG_DEPLOY_TYPE == "2" ]
+then
+  echo "开始回滚"
+  basename "$PWD"
+else
+  mv .temp/* .
+
+  fileCount=$(find /home/spug_backup/vue3-scene-web/* -maxdepth 0 -type d -printf '.' | wc -c)
+  echo "当前文件夹数量:$fileCount"
+
+  if [ ${fileCount} -gt 1 ]
+  then
+    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
+
+  rm -r .temp
+fi
+```

+ 5 - 0
auto-imports.d.ts

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

+ 13 - 0
components.d.ts

@@ -0,0 +1,13 @@
+// generated by unplugin-vue-components
+// We suggest you to commit this file into source control
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+  }
+}

+ 2 - 6
config.js

@@ -5,7 +5,7 @@ const ASSETS_DIR = 'resources/web/';
 /**
  * 主域名
  */
-const DOMAIN = '192.168.20.11';
+const DOMAIN = '4dage.com';
 
 /**
  * 前端版本号
@@ -24,10 +24,6 @@ const BACKEND_DOMAIN = process.env.DOMAIN || `www.${DOMAIN}`;
 
 module.exports = {
   /**
-   * 静态资源部署位置
-   */
-  publicPath: process.env.MUSEUM ? `/${process.env.MUSEUM}/` : `/`,
-  /**
    * 静态资源放置的子目录
    */
   assetsDir: ASSETS_DIR,
@@ -40,7 +36,7 @@ module.exports = {
     PROTOCOL,
     DOMAIN,
     BACKEND_DOMAIN,
-    ASSETS_URL: `/${ASSETS_DIR}`,
+    ASSETS_URL: `//${ASSETS_DIR}`,
     BACKEND_URL: `${PROTOCOL}://${BACKEND_DOMAIN}`,
   },
 };

二进制
hotspot/assets/images/Volume-off.png


二进制
hotspot/assets/images/Volume-on.png


二进制
hotspot/assets/images/icon-image-1@2x.png


二进制
hotspot/assets/images/icon-image@2x.png


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


二进制
hotspot/assets/images/icon-model-1@2x.png


二进制
hotspot/assets/images/icon-model@2x.png


二进制
hotspot/assets/images/icon-next@2x-min.png


二进制
hotspot/assets/images/icon-previous@2x-min.png


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


二进制
hotspot/assets/images/icon-video-1@2x.png


二进制
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>

+ 3 - 0
jest.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
+};

+ 1 - 1
lint-staged.config.js

@@ -1,3 +1,3 @@
 module.exports = {
-  "*.{js,jsx,vue,ts,tsx}": "vue-cli-service lint",
+  '*.{js,jsx,vue,ts,tsx}': 'vue-cli-service lint',
 };

+ 26 - 10
package.json

@@ -1,46 +1,62 @@
 {
-  "name": "4dage-web",
+  "name": "vue3-scene-web",
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "serve:hn": "cross-env SCENE=hn vue-cli-service serve",
-    "build:hn": "cross-env SCENE=hn vue-cli-service build",
+    "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",
     "lint": "vue-cli-service lint",
     "prepare": "husky install"
   },
   "dependencies": {
+    "axios": "^1.4.0",
     "core-js": "^3.8.3",
+    "element-plus": "^2.2.12",
     "lodash": "^4.17.21",
+    "pinia": "^2.0.24",
+    "query-string": "^8.1.0",
+    "swiper": "^11.1.10",
     "vue": "^3.2.13",
     "vue-class-component": "^8.0.0-0",
     "vue-router": "^4.0.3"
   },
   "devDependencies": {
-    "@commitlint/config-conventional": "^17.6.5",
-    "@types/lodash": "^4.14.195",
+    "@commitlint/config-conventional": "^17.1.0",
+    "@types/jest": "^27.0.1",
+    "@types/lodash": "^4.14.185",
+    "@types/webpack-env": "^1.18.0",
     "@typescript-eslint/eslint-plugin": "^5.4.0",
     "@typescript-eslint/parser": "^5.4.0",
     "@vue/cli-plugin-babel": "~5.0.0",
     "@vue/cli-plugin-eslint": "~5.0.0",
     "@vue/cli-plugin-router": "~5.0.0",
     "@vue/cli-plugin-typescript": "~5.0.0",
+    "@vue/cli-plugin-unit-jest": "~5.0.0",
+    "@vue/cli-plugin-vuex": "~5.0.0",
     "@vue/cli-service": "~5.0.0",
     "@vue/eslint-config-typescript": "^9.1.0",
+    "@vue/test-utils": "^2.0.0-0",
+    "@vue/vue3-jest": "^27.0.0-alpha.1",
+    "babel-jest": "^27.0.6",
     "babel-plugin-lodash": "^3.3.4",
-    "commitlint": "^17.6.5",
+    "commitlint": "^17.1.2",
     "cross-env": "^7.0.3",
     "eslint": "^7.32.0",
     "eslint-config-prettier": "^8.3.0",
     "eslint-plugin-prettier": "^4.0.0",
     "eslint-plugin-vue": "^8.0.3",
-    "husky": "^8.0.3",
+    "husky": "^8.0.2",
+    "jest": "^27.0.5",
     "json-loader": "^0.5.7",
+    "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",
-    "svg-sprite-loader": "^6.0.11",
-    "typescript": "~4.5.5"
+    "ts-jest": "^27.0.4",
+    "typescript": "^5.5.4",
+    "unplugin-auto-import": "^0.11.2",
+    "unplugin-vue-components": "^0.22.7"
   }
 }

文件差异内容过多而无法显示
+ 9781 - 6397
pnpm-lock.yaml


二进制
public/favicon.ico


二进制
public/favicon/favicon.ico


二进制
public/fonts/SourceHanSansCN.ttf


二进制
public/fonts/SourceHanSansCN.woff


二进制
public/fonts/SourceHanSansCN.woff2


二进制
public/fonts/open-sans-light/OpenSansLight.eot


文件差异内容过多而无法显示
+ 21034 - 0
public/fonts/open-sans-light/OpenSansLight.svg


二进制
public/fonts/open-sans-light/OpenSansLight.ttf


二进制
public/fonts/open-sans-light/OpenSansLight.woff


二进制
public/fonts/open-sans-light/OpenSansLight.woff2


二进制
public/fonts/open-sans-semibold/OpenSansSemibold.eot


文件差异内容过多而无法显示
+ 21055 - 0
public/fonts/open-sans-semibold/OpenSansSemibold.svg


二进制
public/fonts/open-sans-semibold/OpenSansSemibold.ttf


二进制
public/fonts/open-sans-semibold/OpenSansSemibold.woff


二进制
public/fonts/open-sans-semibold/OpenSansSemibold.woff2


二进制
public/fonts/open-sans/OpenSansLight.woff2


二进制
public/fonts/open-sans/OpenSansRegular.woff2


二进制
public/fonts/proxima-nova/2B71A2_0_0.woff


二进制
public/fonts/proxima-nova/2B71A2_1_0.woff


二进制
public/images/4dage-logo.png


二进制
public/images/4dagePoint.png


二进制
public/images/4dagePoint2.png


二进制
public/images/End.png


二进制
public/images/End_128.png


二进制
public/images/End_unable_128.png


二进制
public/images/Nav_Help_Arrow_keys.png


二进制
public/images/Nav_Help_Close.png


二进制
public/images/Nav_Help_Highlights.png


二进制
public/images/Nav_Help_Icon.png


二进制
public/images/New.png


二进制
public/images/Notes.png


二进制
public/images/Notes_hover.png


二进制
public/images/Personal_Pic.png


二进制
public/images/Start.png


二进制
public/images/VR.png


二进制
public/images/Volume btn_off.png


二进制
public/images/Volume btn_on.png


二进制
public/images/auto-suspend.png


二进制
public/images/auto.png


二进制
public/images/btm_logo.png


二进制
public/images/circle_active.png


二进制
public/images/circle_activeF.png


二进制
public/images/circle_normal.png


二进制
public/images/circle_wait.png


二进制
public/images/close1.png


二进制
public/images/coordinate.png


二进制
public/images/coordinate2.png


二进制
public/images/coordinateClose.png


二进制
public/images/crosshair.cur


二进制
public/images/cursor.png


二进制
public/images/delete.png


二进制
public/images/division.png


+ 13 - 0
public/images/doll_label_corner.svg

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="6px" height="6px" viewBox="0 0 6 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>map_corner  </title>
+    <g id="页面-1" stroke="none" stroke-width="3" fill="none" fill-rule="evenodd">
+        <g id="展示界面-三维" transform="translate(-56.000000, -270.000000)" fill="#FFFFFF" fill-rule="nonzero">
+            <g id="编组-15" transform="translate(31.000000, 270.000000)">
+                <g id="形状结合-+-形状结合-蒙版" transform="translate(25.000000, 0.000000)">
+                    <path d="M6,6 L5,6 L5,2.705 L3.301,1 L0,1 L0,0 L3.71522641,0 L6,2.29344795 L6,6 Z" id="map_corner--" transform="translate(3.000000, 3.000000) scale(-1, 1) translate(-3.000000, -3.000000) "></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

二进制
public/images/dollhouse.png


二进制
public/images/edit/End_128.png


二进制
public/images/edit/End_unable_128.png


二进制
public/images/edit/VR.png


二进制
public/images/edit/autoTour.png


二进制
public/images/edit/box_video.png


二进制
public/images/edit/hotStyle_1.png


二进制
public/images/edit/hotStyle_2.png


二进制
public/images/edit/hotpoint.png


二进制
public/images/edit/image.png


二进制
public/images/edit/information.png


二进制
public/images/edit/music.png


二进制
public/images/edit/panoVisi.png


二进制
public/images/edit/save.png


二进制
public/images/edit/screen.png


二进制
public/images/engraving-icon.png


二进制
public/images/enlarge_on.png


+ 0 - 0
public/images/export.png


部分文件因为文件数量过多而无法显示