任一存 2 лет назад
Родитель
Сommit
334e797210

+ 1 - 0
.eslintrc.js

@@ -50,6 +50,7 @@ module.exports = {
     globalApi: true,
     globalUtils: true,
     globalMapState: true,
+    globalMapGetters: true,
     globalMapMutations: true,
     KanKan: true,
   }

BIN
src/assets/images/pano/arrow-blunt.png


BIN
src/assets/images/pano/close-simple.png


+ 208 - 0
src/components/PanoList.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="pano-list">
+    <menu>
+      <div class="first-line">
+        <div class="tab-wrap">
+          <button
+            v-for="(item, index) in firstLineBtnList"
+            :key="index"
+            :class="{active: firstLineActiveIdx === index}"
+            @click="onClickFirstLineBtn(index)"
+          >
+            {{ item.name }}
+          </button>
+        </div>
+        <button
+          class="close"
+          @click="onClickClose"
+        >
+          <img
+            src="@/assets/images/pano/close-simple.png"
+            alt=""
+            draggable="false"
+          >
+        </button>
+      </div>
+      <div
+        v-if="firstLineActiveIdx === 1"
+        class="second-line"
+        :class="isSecondLineExpanded ? 'expand' : 'shrink'"
+      >
+        <div class="tab-wrap">
+          <button
+            v-for="(catalogLevel1) in catalogTopology"
+            :key="catalogLevel1.id"
+          >
+            {{ catalogLevel1.name }}
+          </button>
+        </div>
+        <button
+          class="expand-shrink"
+          @click="isSecondLineExpanded = !isSecondLineExpanded"
+        >
+          <img
+            src="@/assets/images/pano/arrow-blunt.png"
+            alt=""
+            draggable="false"
+          >
+        </button>
+      </div>
+    </menu>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      firstLineBtnList: [
+        {
+          name: '航拍全景',
+        },
+        {
+          name: '地拍全景',
+        },
+      ],
+      firstLineActiveIdx: 0,
+      secondLineActiveIdx: 0,
+      isSecondLineExpanded: false,
+    }
+  },
+  computed: {
+    ...globalMapGetters([
+      'catalogTopology',
+    ]),
+    dipaiSceneGroupList() {
+      return []
+    }
+  },
+  methods: {
+    onClickFirstLineBtn(index) {
+      this.firstLineActiveIdx = index
+    },
+    onClickClose: globalUtils.throttle(function() {
+      this.$router.go(-1)
+    }, 1000)
+  },
+  mounted() {
+    console.log('sfksgklsdlf', this.catalogTopology)
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.pano-list {
+  position: absolute;
+  z-index: 3;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background: #FAF5EB;
+  > menu {
+    position: relative;
+    .first-line {
+      height: 7.08rem;
+      background-color: #930909;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 0 1.67rem;
+      .tab-wrap {
+        display: flex;
+        gap: 3.58rem;
+        align-items: center;
+        > button {
+          font-size: 1.83rem;
+          color: #FFFFFF;
+          &.active {
+            color: #D8B275;
+            position: relative;
+            &::after {
+              content: '';
+              position: absolute;
+              width: 100%;
+              height: 0.25rem;
+              left: 0;
+              top: calc(100% + 0.46rem);
+              background-color: #D8B275;
+            }
+          }
+        }
+      }
+      .close {
+        height: 3.33rem;
+        width: 3.33rem;
+        > img {
+          height: 100%;
+          width: 100%;
+        }
+      }
+    }
+    .second-line {
+      position: absolute;
+      left: 0;
+      top: 100%;
+      width: 100%;
+      background-color: #840707;
+      display: flex;
+      align-items: center;
+      padding: 1.67rem;
+      &.shrink {
+        height: 5.83rem;
+        gap: 1.67rem;
+        .tab-wrap {
+          flex: 1 1 1px;
+          overflow: hidden;
+          display: flex;
+          align-items: center;
+          gap: 2.92rem;
+          > button {
+            white-space: nowrap;
+            word-break: keep-all;
+            font-size: 1.83rem;
+            color: #FFFFFF;
+            line-height: 2.15rem;
+          }
+        }
+        .expand-shrink {
+          flex: 0 0 auto;
+          padding: 1rem;
+          > img {
+            width: 1.76rem;
+            height: 0.8rem;
+          }
+        }
+      }
+      &.expand {
+        gap: 1.67rem;
+        flex-direction: column;
+        flex: 0 0 auto;
+        .tab-wrap {
+          display: flex;
+          align-items: center;
+          flex-wrap: wrap;
+          gap: 1.67rem 2.92rem;
+          > button {
+            white-space: nowrap;
+            word-break: keep-all;
+            font-size: 1.83rem;
+            color: #FFFFFF;
+            line-height: 2.15rem;
+          }
+        }
+        .expand-shrink {
+          flex: 0 0 auto;
+          padding: 1rem;
+          > img {
+            width: 1.76rem;
+            height: 0.8rem;
+            transform: rotate(180deg);
+          }
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 4 - 0
src/config.js

@@ -8,6 +8,10 @@ export default {
       self: '6',
       children: {},
     },
+    pano: {
+      self: '1',
+      children: {},
+    },
     relicInfo: {
       self: '2',
       children: {},

+ 23 - 0
src/router/index.js

@@ -7,6 +7,7 @@ import SwkkFadeIn from "@/views/SwkkFadeIn.vue"
 import SwkkView from "@/views/SwkkView.vue"
 import ObliqueView from "@/views/ObliqueView.vue"
 import PanoView from "@/views/PanoView.vue"
+import PanoList from "@/components/PanoList.vue"
 
 Vue.use(VueRouter)
 
@@ -55,6 +56,17 @@ const routes = [
       isShowBottomBar: true,
       canFullScreen: true,
     },
+    children: [
+      {
+        path: './pano-list',
+        name: 'PanoList',
+        component: PanoList,
+        meta: {
+          isShowBottomBar: false,
+          canFullScreen: false,
+        }
+      },
+    ],
   },
   {
     path: '/pano-view',
@@ -64,6 +76,17 @@ const routes = [
       isShowBottomBar: true,
       canFullScreen: true,
     },
+    children: [
+      {
+        path: './pano-list',
+        name: 'PanoList',
+        component: PanoList,
+        meta: {
+          isShowBottomBar: false,
+          canFullScreen: false,
+        }
+      },
+    ],
   },
   {
     path: '/relics-appr',

+ 41 - 1
src/store/index.js

@@ -8,8 +8,44 @@ export default new Vuex.Store({
     _isMutedBackup: false,
     isMuted: false,
     isFullScreen: false,
+
+    panoData: null,
   },
   getters: {
+    catalogTopology: (state) => {
+      if (!state.panoData || !state.panoData.catalogRoot || !state.panoData.catalogs) {
+        return
+      }
+      //四层:root,level1(一级分类),level2(二级分类或直属于一级分类的场景), level3(场景)
+      let root = globalUtils.deepClone(state.panoData.catalogRoot)
+      // 对于每个一级分类
+      for (const itemLevel1 of root) {
+        // 指定每个一级分类的下级
+        itemLevel1.childrenTemp = []
+        for (const itemLevel2Id of itemLevel1.children) {
+          for (const catalogsItem of state.panoData.catalogs) {
+            if (itemLevel2Id === catalogsItem.id) {
+              const itemLevel2 = globalUtils.deepClone(catalogsItem)
+              itemLevel2.parentId = itemLevel1.id // 看起来,vuex getter中的数据如果存在循环引用,在通过mutation转而再存储到vuex中时,会导致调用栈溢出,原因难道是vuex mutation在深拷贝时没有考虑循环引用的情况?所以这里不进行循环引用,只记录parent的id。
+              itemLevel2.children = []
+              itemLevel1.childrenTemp.push(itemLevel2)
+              // 对于每个三级元素
+              for (const sceneItem of state.panoData.scenes) {
+                // 如果属于上述二级分类
+                if (itemLevel2.id === sceneItem.category /* 注意拼写!!! */) {
+                  const itemLevel3 = globalUtils.deepClone(sceneItem)
+                  itemLevel3.parentId = itemLevel2.id // 看起来,vuex getter中的数据如果存在循环引用,在通过mutation转而再存储到vuex中时,会导致调用栈溢出,原因难道是vuex mutation在深拷贝时没有考虑循环引用的情况?所以这里不进行循环引用,只记录parent的id。
+                  itemLevel2.children.push(itemLevel3)
+                }
+              }
+            }
+          }
+        }
+        itemLevel1.children = itemLevel1.childrenTemp
+        delete itemLevel1.childrenTemp
+      }
+      return root
+    },
   },
   mutations: {
     toggleMute(state) {
@@ -46,7 +82,11 @@ export default new Vuex.Store({
           document.msExitFullscreen()
         }
       }
-    }
+    },
+
+    setPanoData(state, value) {
+      state.panoData = value
+    },
   },
   actions: {
   },

+ 43 - 0
src/utils.js

@@ -1,3 +1,45 @@
+// 深拷贝,为了解决循环引用和共同引用的问题,引入了WeakMap,又因为引入WeakMap可能会导致被拷贝对象被挂上【作为WeakMap的探针的】匿名函数(是pollyfill的行为吧?),所以不会拷贝非根元素的匿名函数。
+function deepClone(target, hash = new WeakMap()) {
+  // 定义一个变量
+  let result = null
+  // 如果当前需要深拷贝的是一个对象的话
+  if (typeof target === 'object') {
+    if (hash.has(target)) { // 如果是循环引用
+      result = hash.get(target)
+    } else if (Array.isArray(target)) { // 如果是一个数组的话
+      result = [] // 将result赋值为一个数组,并且执行遍历
+      hash.set(target, result)
+      for (let i in target) {
+        if (!(typeof(target[i]) === 'function' && !target.name)) {
+          // 递归克隆数组中的每一项
+          result.push(deepClone(target[i], hash))
+        }
+      }
+      // 判断如果当前的值是null的话;直接赋值为null
+    } else if (target === null) {
+      result = null
+      // 判断如果当前的值是一个RegExp对象的话,直接赋值
+    } else if (target.constructor === RegExp) {
+      result = target
+    } else {
+      // 否则是普通对象,直接for in循环,递归赋值对象的所有值
+      result = {}
+      hash.set(target, result)
+      for (let i in target) {
+        if (!(typeof(target[i]) === 'function' && !target.name)) {
+          result[i] = deepClone(target[i], hash)
+        }
+      }
+    }
+  } else if (typeof target === 'function') {
+    result = target
+  } else { // 如果不是对象也不是函数,直接赋值
+    result = target
+  }
+  // 返回最终结果
+  return result
+}
+
 export default {
   throttle(fn, interval) {
     let lastRunTime = 0
@@ -138,6 +180,7 @@ export default {
     }
     return array
   },
+  deepClone,
 }
 
 /**

+ 110 - 4
src/views/ObliqueView.vue

@@ -4,7 +4,10 @@
       id="mars3dContainer"
       class="mars3d-container"
     />
-    <button class="switch-to-pano">
+    <button
+      class="switch-pano"
+      @click="onClickSwitchPano"
+    >
       <img
         src="@/assets/images/swkk/changjingdaolan.png"
         alt=""
@@ -12,11 +15,14 @@
       >
       <div>全景切换</div>
     </button>
+
+    <router-view />
   </div>
 </template>
 
 <script>
 import "mars3d/dist/mars3d.css"
+import axios from "axios"
 import * as mars3d from "mars3d"
 
 export default {
@@ -25,9 +31,17 @@ export default {
     return {
     }
   },
-  computed: {},
+  computed: {
+    ...globalMapState([
+      'panoData',
+    ])
+  },
   watch: {},
-  created() {},
+  async created() {
+    let panoData = await this.fetchPanoData()
+    this.fixPanoData(panoData)
+    this.setPanoData(panoData)
+  },
   mounted() {
     this.initMap().then(
       (map) => {
@@ -58,6 +72,91 @@ export default {
   destroyed() {}, //生命周期 - 销毁完成
   activated() {},
   methods: {
+    ...globalMapMutations([
+      'setPanoData',
+    ]),
+    async fetchPanoData() {
+      const res = await axios({
+        method: 'get',
+        url: `https://zzbbh.4dage.com/YHT/Qjkk/local/WK1578926366500417536/someData.json?_=${Math.random()}`,
+      })
+      console.log('fetch pano data: ', res.data)
+      return res.data
+    },
+    // todo: 干啥呢?有用吗?
+    fixPanoData(panoData) {
+      let tmp = []
+      panoData.scenes.forEach((item) => {
+        panoData.catalogs.forEach((sub) => {
+          if (item.category == sub.id) {
+            if (tmp.indexOf(sub) < 0) {
+              tmp.push(sub)
+            }
+          }
+        })
+      })
+      tmp = globalUtils.unique(tmp)
+
+      panoData.catalogs = tmp
+      let rootmp = []
+      tmp.forEach((item) => {
+        panoData.catalogRoot.forEach((sub) => {
+          sub.children = globalUtils.unique(sub.children)
+
+          if (sub.children.indexOf(item.id) > -1) {
+            rootmp.push(sub)
+          }
+        })
+      })
+
+      rootmp = globalUtils.unique(rootmp)
+
+      let sortArr = panoData.catalogRoot.map((item) => item.name)
+      rootmp.sort((a, b) => {
+        return sortArr.indexOf(a.name) - sortArr.indexOf(b.name)
+      })
+
+      panoData.catalogRoot = rootmp.map((item) => {
+        let temp = []
+        item.children = globalUtils.unique(item.children)
+        item.children.forEach((sub) => {
+          tmp.forEach((jj) => {
+            if (jj.id == sub) {
+              temp.push(sub)
+            }
+          })
+        })
+        return {
+          ...item,
+          children: temp,
+        }
+      })
+
+      panoData.catalogs = tmp
+
+      let cid = "c_" + globalUtils.randomWord(true, 8, 8)
+
+      if (panoData.catalogRoot.length <= 0) {
+        panoData.catalogRoot.push({
+          id: "r_" + globalUtils.randomWord(true, 8, 8),
+          name: "全部场景",
+          children: [cid],
+        })
+      }
+
+      if (panoData.catalogs.length <= 0) {
+        panoData.catalogs.push({
+          id: cid,
+          name: "默认二级分组",
+        })
+      }
+
+      if (panoData.firstScene) {
+        panoData.firstScene = panoData.scenes.find(
+          (item) => item.sceneCode == panoData.firstScene.sceneCode
+        )
+      }
+    },
     // 初始化地图
     async initMap() {
       // 读取 config.json 配置文件
@@ -131,6 +230,9 @@ export default {
     cutVr(code, index) {
       this.$emit("mapCutVr", code, index)
     },
+    onClickSwitchPano() {
+      this.$router.push({ name: 'PanoList' })
+    }
   }, //如果页面有keep-alive缓存功能,这个函数会触发
 }
 </script>
@@ -140,19 +242,22 @@ export default {
   position: relative;
   width: 100%;
   height: 100%;
+  z-index: 0;
   #mars3dContainer {
     width: 100%;
     height: 100%;
     position: absolute;
     top: 0px;
     left: 0px;
+    z-index: 1;
   }
-  > button.switch-to-pano {
+  > button.switch-pano {
     position: absolute;
     top: 2.08rem;
     right: 1.63rem;
     width: 5rem;
     height: 5rem;
+    z-index: 2;
     > img {
       width: 100%;
       height: 100%;
@@ -162,6 +267,7 @@ export default {
       text-shadow: 0 0 0.1rem rgba(0,0,0,0.3);
       color: #333333;
       font-size: 1.17rem;
+      font-weight: bold;
     }
   }
 }

+ 7 - 89
src/views/PanoView.vue

@@ -2,29 +2,29 @@
   <div class="pano-view">
     <div
       id="pano"
+      :style="{zIndex: $globalConfig.zIndex.pano.self}"
     />
   </div>
 </template>
 
 <script>
 import * as krfn from "@/libs/pano-core/index.js"
-import axios from "axios"
 
 let __krfn = krfn.default
 
 export default {
   data() {
     return {
-      panoData: null,
     }
   },
+  computed: {
+    ...globalMapState([
+      'panoData',
+    ])
+  },
   async mounted() {
     window.__krfn = __krfn
 
-    let panoData = await this.fetchPanoData()
-    this.fixPanoData(panoData)
-    this.panoData = panoData
-
     const testScene = this.panoData.scenes[0]
     $("#pano").empty()
     window.vrInitFn = () => {
@@ -53,7 +53,7 @@ export default {
     // eslint-disable-next-line no-undef
     embedpano({
       xml: `https://zzbbh.4dage.com/YHT/Qjkk/local/pano/${testScene.sceneCode}/vtour/tour.xml`,
-      swf: "%HTMLPATH%/static/template/tour.swf",
+      swf: "%HTMLPATH%/static/template/tour.swf", // todo: 有用吗?
       target: "pano",
       html5: "auto",
       mobilescale: 1,
@@ -62,88 +62,6 @@ export default {
     })
   },
   methods: {
-    async fetchPanoData() {
-      const res = await axios({
-        method: 'get',
-        url: `https://zzbbh.4dage.com/YHT/Qjkk/local/WK1578926366500417536/someData.json?_=${Math.random()}`,
-      })
-      console.log('fetch pano data: ', res.data)
-      return res.data
-    },
-    // todo: 干啥呢?似乎用不到?
-    fixPanoData(panoData) {
-      let tmp = []
-      panoData.scenes.forEach((item) => {
-        panoData.catalogs.forEach((sub) => {
-          if (item.category == sub.id) {
-            if (tmp.indexOf(sub) < 0) {
-              tmp.push(sub)
-            }
-          }
-        })
-      })
-      tmp = globalUtils.unique(tmp)
-
-      panoData.catalogs = tmp
-      let rootmp = []
-      tmp.forEach((item) => {
-        panoData.catalogRoot.forEach((sub) => {
-          sub.children = globalUtils.unique(sub.children)
-
-          if (sub.children.indexOf(item.id) > -1) {
-            rootmp.push(sub)
-          }
-        })
-      })
-
-      rootmp = globalUtils.unique(rootmp)
-
-      let sortArr = panoData.catalogRoot.map((item) => item.name)
-      rootmp.sort((a, b) => {
-        return sortArr.indexOf(a.name) - sortArr.indexOf(b.name)
-      })
-
-      panoData.catalogRoot = rootmp.map((item) => {
-        let temp = []
-        item.children = globalUtils.unique(item.children)
-        item.children.forEach((sub) => {
-          tmp.forEach((jj) => {
-            if (jj.id == sub) {
-              temp.push(sub)
-            }
-          })
-        })
-        return {
-          ...item,
-          children: temp,
-        }
-      })
-
-      panoData.catalogs = tmp
-
-      let cid = "c_" + globalUtils.randomWord(true, 8, 8)
-
-      if (panoData.catalogRoot.length <= 0) {
-        panoData.catalogRoot.push({
-          id: "r_" + globalUtils.randomWord(true, 8, 8),
-          name: "全部场景",
-          children: [cid],
-        })
-      }
-
-      if (panoData.catalogs.length <= 0) {
-        panoData.catalogs.push({
-          id: cid,
-          name: "默认二级分组",
-        })
-      }
-
-      if (panoData.firstScene) {
-        panoData.firstScene = panoData.scenes.find(
-          (item) => item.sceneCode == panoData.firstScene.sceneCode
-        )
-      }
-    },
   },
 }
 </script>

+ 2 - 1
vue.config.js

@@ -23,6 +23,7 @@ module.exports = defineConfig({
         // utils: ['/src/utils.js', 'default']
         globalMapState: ['vuex', 'mapState'],
         globalMapMutations: ['vuex', 'mapMutations'],
+        globalMapGetters: ['vuex', 'mapGetters'],
         globalConfig: ['/src/config.js', 'default'],
         globalApi: ['/src/api.js', 'default'],
         globalUtils: ['/src/utils.js', 'default'],
@@ -30,4 +31,4 @@ module.exports = defineConfig({
       new NodePolyfillPlugin()
     ],
   },
-})
+})