瀏覽代碼

Merge branch 'new-dev-refactor' into dev

jinx 6 月之前
父節點
當前提交
b0e01ac48a
共有 89 個文件被更改,包括 42175 次插入3704 次删除
  1. 1 0
      package.json
  2. 2 0
      packages/qjkankan-editor/.env.testdev
  3. 33594 0
      packages/qjkankan-editor/package-lock.json
  4. 2 1
      packages/qjkankan-editor/package.json
  5. 18 0
      packages/qjkankan-editor/public/static/template/customTooltip.xml
  6. 47 58
      packages/qjkankan-editor/src/Store/index.js
  7. 339 0
      packages/qjkankan-editor/src/Store/modules/base.js
  8. 88 0
      packages/qjkankan-editor/src/Store/modules/explanation.js
  9. 294 0
      packages/qjkankan-editor/src/Store/modules/hotspot.js
  10. 12 0
      packages/qjkankan-editor/src/Store/modules/index.js
  11. 139 0
      packages/qjkankan-editor/src/Store/modules/mask.js
  12. 115 11
      packages/qjkankan-editor/src/Store/modules/navigation.js
  13. 37 32
      packages/qjkankan-editor/src/Store/modules/scene.js
  14. 133 0
      packages/qjkankan-editor/src/Store/modules/screen.js
  15. 1 1
      packages/qjkankan-editor/src/Store/modules/tags.js
  16. 136 128
      packages/qjkankan-editor/src/api/index.js
  17. 1064 0
      packages/qjkankan-editor/src/components/dragTree/index.vue
  18. 165 152
      packages/qjkankan-editor/src/components/insertPositionTipInEditor.vue
  19. 24 93
      packages/qjkankan-editor/src/components/materialSelectorFromWork.vue
  20. 56 112
      packages/qjkankan-editor/src/components/sceneGroupInEditor.vue
  21. 11 48
      packages/qjkankan-editor/src/components/sceneInGroupInEditor.vue
  22. 10 13
      packages/qjkankan-editor/src/core/hotspot.js
  23. 18 19
      packages/qjkankan-editor/src/core/utils.js
  24. 399 208
      packages/qjkankan-editor/src/framework/EditorHead.vue
  25. 24 46
      packages/qjkankan-editor/src/framework/MenuPC.vue
  26. 2 6
      packages/qjkankan-editor/src/framework/core/index.vue
  27. 3 0
      packages/qjkankan-editor/src/framework/play/pano/components/list.vue
  28. 528 0
      packages/qjkankan-editor/src/framework/play/pano/components/new-list.vue
  29. 87 101
      packages/qjkankan-editor/src/framework/play/pano/index.vue
  30. 1 1
      packages/qjkankan-editor/src/framework/show/index.vue
  31. 1 1
      packages/qjkankan-editor/src/framework/showMobile/index.vue
  32. 30 23
      packages/qjkankan-editor/src/pages/Edit.vue
  33. 31 24
      packages/qjkankan-editor/src/pages/edit.js
  34. 203 112
      packages/qjkankan-editor/src/utils/other.js
  35. 30 15
      packages/qjkankan-editor/src/utils/request.js
  36. 32 72
      packages/qjkankan-editor/src/views/base/Toolbar.vue
  37. 4 3
      packages/qjkankan-editor/src/views/base/autoCruiseSettings.vue
  38. 41 47
      packages/qjkankan-editor/src/views/base/backgroundMusicSettings.vue
  39. 113 320
      packages/qjkankan-editor/src/views/base/coverBase.vue
  40. 26 25
      packages/qjkankan-editor/src/views/base/customButtonSettings.vue
  41. 12 11
      packages/qjkankan-editor/src/views/base/customLogoSettings.vue
  42. 49 10
      packages/qjkankan-editor/src/views/base/index.vue
  43. 12 30
      packages/qjkankan-editor/src/views/base/openingAnimationSettings.vue
  44. 16 15
      packages/qjkankan-editor/src/views/base/openingTipSettings.vue
  45. 5 5
      packages/qjkankan-editor/src/views/base/passwordSettings.vue
  46. 55 49
      packages/qjkankan-editor/src/views/explanation/explanationSettings.vue
  47. 47 99
      packages/qjkankan-editor/src/views/hotspot/EditPanel.vue
  48. 147 124
      packages/qjkankan-editor/src/views/hotspot/HotSpotList.vue
  49. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotIconType/custom_image.vue
  50. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotIconType/personalized_tag.vue
  51. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotIconType/serial_frame.vue
  52. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotIconType/system_icon.vue
  53. 2 1
      packages/qjkankan-editor/src/views/hotspot/hotspotType/article.vue
  54. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotType/imageText.vue
  55. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotType/pdf.vue
  56. 1 1
      packages/qjkankan-editor/src/views/hotspot/hotspotType/phone.vue
  57. 65 108
      packages/qjkankan-editor/src/views/mask/setting.vue
  58. 30 41
      packages/qjkankan-editor/src/views/material/popup/share.vue
  59. 251 215
      packages/qjkankan-editor/src/views/material/works/list.vue
  60. 85 62
      packages/qjkankan-editor/src/views/navigation/groupSettings.vue
  61. 28 22
      packages/qjkankan-editor/src/views/navigation/initialSceneSettings.vue
  62. 69 38
      packages/qjkankan-editor/src/views/screen/Setting.vue
  63. 4 0
      packages/qjkankan-editor/vue.config.js
  64. 7 0
      packages/qjkankan-view/public/showviewer/lib/krpano/js/krpano.js
  65. 5 0
      packages/qjkankan-view/public/showviewer/lib/krpano/plugins/webvr1.21.js
  66. 2 1
      packages/qjkankan-view/public/showviewer/lib/krpano/tooltip.xml
  67. 13 24
      packages/qjkankan-view/src/apis/index.js
  68. 88 56
      packages/qjkankan-view/src/components/Pano/index.vue
  69. 49 51
      packages/qjkankan-view/src/components/UIGather/index.vue
  70. 506 0
      packages/qjkankan-view/src/components/UIGather/list copy 2.vue
  71. 589 0
      packages/qjkankan-view/src/components/UIGather/list copy.vue
  72. 412 460
      packages/qjkankan-view/src/components/UIGather/list.vue
  73. 2 2
      packages/qjkankan-view/src/components/UIGather/logo.vue
  74. 545 0
      packages/qjkankan-view/src/components/UIGather/mobile/list copy.vue
  75. 239 228
      packages/qjkankan-view/src/components/UIGather/mobile/list.vue
  76. 440 0
      packages/qjkankan-view/src/components/UIGather/new-list.vue
  77. 3 7
      packages/qjkankan-view/src/components/assembly/Password.vue
  78. 1 1
      packages/qjkankan-view/src/components/assembly/titieSlide.vue
  79. 11 13
      packages/qjkankan-view/src/hooks/useAudio.js
  80. 9 11
      packages/qjkankan-view/src/pages/show.js
  81. 188 128
      packages/qjkankan-view/src/pages/show.vue
  82. 148 143
      packages/qjkankan-view/src/pages/showMobile.vue
  83. 8 12
      packages/qjkankan-view/src/sdk/QJKanKan/index.js
  84. 5 5
      packages/qjkankan-view/src/sdk/QJKanKan/modules/Scene.js
  85. 25 32
      packages/qjkankan-view/src/sdk/QJKanKan/modules/Tags.js
  86. 5 4
      packages/qjkankan-view/src/sdk/QJKanKan/modules/hotspot.js
  87. 14 14
      packages/qjkankan-view/src/store/modules/scene.js
  88. 112 3
      packages/qjkankan-view/src/store/modules/tags.js
  89. 4 0
      packages/qjkankan-view/vue.config.js

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
     "view:serve": "pnpm --filter @qjkankan/view run serve",
     "view:build-testprod": "pnpm --filter @qjkankan/view run build-testprod",
     "kankan-view:serve": "pnpm --filter @qjkankan/kankan-view run serve",
+    "serve": "npm-run-all --parallel edit:test-serve view:serve",
     "kankan-view:build-testprod": "pnpm --filter @qjkankan/kankan-view run build-testprod"
   },
   "dependencies": {

+ 2 - 0
packages/qjkankan-editor/.env.testdev

@@ -4,6 +4,8 @@ VUE_APP_STATIC_DIR=static
 VUE_APP_CDN=https://ossxiaoan.4dage.com
 VUE_APP_PROXY_URL_ROOT='https://test.4dkankan.com'
 VUE_APP_PROXY_URL='https://test.4dkankan.com/qjkankan/'
+# VUE_APP_PROXY_URL='http://192.168.0.73:8002/qjkankan/'
+# VUE_APP_PROXY_URL='http://192.168.0.17:8002/qjkankan/'
 VUE_APP_ORIGIN=
 VUE_APP_URL_FILL=
 VUE_APP_DEBBUG_FLAG=0112-02

File diff suppressed because it is too large
+ 33594 - 0
packages/qjkankan-editor/package-lock.json


+ 2 - 1
packages/qjkankan-editor/package.json

@@ -45,6 +45,7 @@
     "vue-slider-component": "^3.2.24",
     "vue-toast-notification": "^3.1.1",
     "vue-toastification": "^1.7.14",
+    "vue-tree-list": "^1.5.0",
     "vuex": "^3.6.0"
   },
   "devDependencies": {
@@ -60,4 +61,4 @@
     "less-loader": "^5.0.0",
     "vue-template-compiler": "^2.6.12"
   }
-}
+}

+ 18 - 0
packages/qjkankan-editor/public/static/template/customTooltip.xml

@@ -2,6 +2,17 @@
     <!-- 1.20. 新热点模式 -->
     <action name="addJQHotspot">
         <!-- showlog(); -->
+        trace('pramas1::',%1);
+        trace('pramas2::',%2);
+        trace('pramas3::',%3);
+        trace('pramas4::',%4);
+        trace('pramas5::',%5);
+        trace('pramas6::',%6);
+        trace('pramas7::',%7);
+        trace('pramas8::',%8);
+        trace('pramas9::',%9);
+        trace('pramas10::',%10);
+     
         set(hsp_name,%1); 
         set(hsp_type,%2); 
         txtadd(iconUrl,'',%4);
@@ -392,9 +403,16 @@
             add(lastPy,dpy,5);
             trace('dpy::',dpy);
             mul(lastPy, -1);
+         
             set(layer[get(posName)].align,center);
             set(layer[get(posName)].y,get(lastPy));
             set(layer[get(posName)].x,0);
+            if(isNaN(dpy),
+              trace('lastPy::',get(lastPy));
+              set(layer[get(posName)].y,-40);
+            )
+
+        
         );
          if(
             pos == 'bottom',

+ 47 - 58
packages/qjkankan-editor/src/Store/index.js

@@ -15,7 +15,7 @@ const store = new Vuex.Store({
     info: "",
     infoSnapshotAtSave: "",
     showInfo: "",
-    hotspot: "", // 当前在新增或编辑的热点的信息
+    // hotspot: "", // 当前在新增或编辑的热点的信息
     backupHotSpot: "",
     initScene: "",
     tablist: [],
@@ -96,7 +96,7 @@ const store = new Vuex.Store({
       return !isSameObject(state.info, state.infoSnapshotAtSave);
     },
     showInfo: (state) => state.showInfo,
-    hotspot: (state) => state.hotspot,
+    // hotspot: (state) => state.hotspot,
     backupHotSpot: (state) => state.backupHotSpot,
     initScene: (state) => state.initScene,
     activeItem: (state) => state.activeItem,
@@ -114,40 +114,37 @@ const store = new Vuex.Store({
     editorNavDragInfo: (state) => state.editorNavDragInfo,
 
     // 1.3.0 新增层级后所有热点类型不同汇总显示的ICON
-    hotspotIcon: (state) => {
-      const category =
-        state.hotspot && state.hotspot.hotspotIconType
-          ? state.hotspot.hotspotIconType
-          : "";
-      switch (category) {
-        case "system_icon":
-          return {
-            img: state.hotspot.img,
-            type: "system_icon",
-          };
-        case "custom_image":
-          return {
-            ...state.hotspot.customIconInfo,
-            type: "custom_image",
-          };
-        case "serial_frame":
-          return {
-            ...state.hotspot.serialFrameInfo,
-            img: state.hotspot.serialFrameInfo.img,
-            type: "serial_frame",
-          };
-        case "personalized_tag":
-          return {
-            ...state.hotspot.personalizedTagInfo,
-            img: "",
-            type: "personalized_tag",
-          };
-        default:
-          return {
-            img: "",
-          };
-      }
-    },
+    // hotspotIcon: (state) => {
+    //   const category = state.hotspot && state.hotspot.hotspotIconType ? state.hotspot.hotspotIconType : "";
+    //   switch (category) {
+    //     case "system_icon":
+    //       return {
+    //         img: state.hotspot.img,
+    //         type: "system_icon",
+    //       };
+    //     case "custom_image":
+    //       return {
+    //         ...state.hotspot.customIconInfo,
+    //         type: "custom_image",
+    //       };
+    //     case "serial_frame":
+    //       return {
+    //         ...state.hotspot.serialFrameInfo,
+    //         img: state.hotspot.serialFrameInfo.img,
+    //         type: "serial_frame",
+    //       };
+    //     case "personalized_tag":
+    //       return {
+    //         ...state.hotspot.personalizedTagInfo,
+    //         img: "",
+    //         type: "personalized_tag",
+    //       };
+    //     default:
+    //       return {
+    //         img: "",
+    //       };
+    //   }
+    // },
   },
   mutations: {
     SetUserAvatar(state, avatar) {
@@ -195,10 +192,10 @@ const store = new Vuex.Store({
     TakeInfoSnapShotAtSave(state) {
       state.infoSnapshotAtSave = deepClone(state.info);
     },
-    SetHotspot(state, data) {
-      state.hotspot = data;
-      this.commit("BackupHotSpot", browser.CloneObject(data));
-    },
+    // SetHotspot(state, data) {
+    //   state.hotspot = data;
+    //   this.commit("BackupHotSpot", browser.CloneObject(data));
+    // },
     BackupHotSpot(state, data) {
       state.backupHotSpot = data;
     },
@@ -216,30 +213,22 @@ const store = new Vuex.Store({
       state.uploadStatusListVideo = data;
     },
     clearUploadStatusLists(state) {
-      state.uploadStatusListImage = state.uploadStatusListImage.filter(
-        (item) => {
-          return item.status !== "SUCCESS";
-        }
-      );
+      state.uploadStatusListImage = state.uploadStatusListImage.filter((item) => {
+        return item.status !== "SUCCESS";
+      });
       state.uploadStatusListPano = state.uploadStatusListPano.filter((item) => {
         return item.status !== "SUCCESS";
       });
-      state.uploadStatusListAudio = state.uploadStatusListAudio.filter(
-        (item) => {
-          return item.status !== "SUCCESS";
-        }
-      );
-      state.uploadStatusListVideo = state.uploadStatusListVideo.filter(
-        (item) => {
-          return item.status !== "SUCCESS";
-        }
-      );
+      state.uploadStatusListAudio = state.uploadStatusListAudio.filter((item) => {
+        return item.status !== "SUCCESS";
+      });
+      state.uploadStatusListVideo = state.uploadStatusListVideo.filter((item) => {
+        return item.status !== "SUCCESS";
+      });
     },
 
     setEditorNavDragType(state, data) {
-      if (
-        !["topologyGroupLevel1", "topologyGroupLevel2", "scene"].includes(data)
-      ) {
+      if (!["topologyGroupLevel1", "topologyGroupLevel2", "scene"].includes(data)) {
         throw "拖拽类型必须是'topologyGroupLevel1', 'topologyGroupLevel2', 'scene'之一!";
       }
       state.editorNavDragInfo.type = data;

+ 339 - 0
packages/qjkankan-editor/src/Store/modules/base.js

@@ -0,0 +1,339 @@
+import Vue from "vue";
+import { i18n } from "@/lang";
+import { saveBaseWorkInfo } from "@/api";
+import { deepClone, isSameObject } from "@/utils/other.js";
+import { $waiting } from "@/components/shared/loading";
+let vue = new Vue();
+export default {
+  namespaced: true,
+  state() {
+    return {
+      sceneList: [],
+      baseInfo: null,
+    };
+  },
+  getters: {
+    sceneList: (state) => {
+      return state.sceneList;
+    },
+    baseInfo: (state, getters, rootState, rootGetters) => {
+      if (state?.baseInfo?.navigationTrees) {
+        state.sceneList = [];
+        state.baseInfo.navigationTrees.forEach((one_tree) => {
+          if (one_tree.type == "group" && one_tree.children.length) {
+            one_tree.children.forEach((two_tree) => {
+              if (two_tree.type == "group" && two_tree.children.length) {
+                two_tree.children.forEach((scene) => {
+                  state.sceneList.push(scene);
+                });
+              } else if (two_tree.type == "pano" || two_tree.type == "4dkk") {
+                state.sceneList.push(two_tree);
+              }
+            });
+          }
+        });
+      }
+      // console.error("11111", state.sceneList);
+      // if (rootState.scene.currentScene) {
+      //   let currentScene = state.sceneList.find((item) => item.id == rootState.scene.currentScene.id || item.sid == rootState.scene.currentScene.id);
+      //   if (currentScene) {
+      //     this.commit("scene/setCurrentScene", currentScene);
+      //   }
+      // }
+      return state.baseInfo;
+    },
+  },
+  mutations: {
+    initDefaultData(state, payload) {
+      let forArr = ["workCustomMaskList", "workVisualAngleList", "workExplanationList"];
+      // state.baseInfo.workVisualAngleList.forEach((item, index) => {
+      //   if (!state.sceneList.some((j_item) => item.navigationId == j_item.id || item.navigationId == j_item.sid)) {
+      //     console.error(index, "删除了");
+      //     state.baseInfo.workVisualAngleList.splice(index, 1);
+      //   }
+      // });
+      // setTimeout(() => {
+      // console.error(state.sceneList);
+      this.commit("screen/setVisualAngleList", { sceneList: state.sceneList, workVisualAngleList: state.baseInfo.workVisualAngleList });
+      this.commit("mask/setCustomMaskList", { sceneList: state.sceneList, workCustomMaskList: state.baseInfo.workCustomMaskList, workId: state.baseInfo.workId });
+      this.commit("explanation/setExplanationList", { sceneList: state.sceneList, workExplanationList: state.baseInfo.workExplanationList });
+      // }, 0);
+      // if (state.sceneList.length) {
+      //   state.sceneList.forEach((item, index) => {
+      //     forArr.map((key) => {
+      //       let obj = {};
+      //       state.baseInfo[key].forEach((key_item, key_index) => {
+      //         if (key == "workVisualAngleList" && item.id == key_item.navigationId || item.sid == key_item.navigationId ) {
+      //           key_item.icon = item.icon;
+      //         }
+      //         obj[key_item.navigationId] = key_item;
+      //       });
+
+      //       if (obj[item.id]) {
+      //         console.error(key,1);
+      //       } else {
+      //         console.error(key,2);
+      //         switch (key) {
+      //           case "workCustomMaskList":
+      //             console.error("workCustomMaskList");
+      //             state.baseInfo[key].push({
+      //               data: {
+      //                 earth: {
+      //                   antidistorted: true,
+      //                   fodderId: null,
+      //                   icon: "",
+      //                   isDelete: 0,
+      //                   isShow: false,
+      //                   navigationId: item.sid ? item.sid : item.id,
+      //                   scale: 1,
+      //                   type: "earth",
+      //                   workId: state.baseInfo.workId,
+      //                 },
+      //                 sky: {
+      //                   antidistorted: true,
+      //                   fodderId: null,
+      //                   icon: "",
+      //                   isDelete: 0,
+      //                   isShow: true,
+      //                   navigationId: item.sid ? item.sid : item.id,
+      //                   scale: 1,
+      //                   type: "sky",
+      //                   workId: state.baseInfo.workId,
+      //                 },
+      //               },
+      //               navigationId: item.sid ? item.sid : item.id,
+      //             });
+      //             break;
+      //           case "workVisualAngleList":
+      //             state.baseInfo[key].push({
+      //               hlookat: 0,
+      //               icon: item.icon,
+      //               // id: 0,
+      //               vlookat: 0,
+      //               vlookatmax: 90,
+      //               vlookatmin: -90,
+      //               workId: state.baseInfo.workId,
+      //               navigationId: item.sid ? item.sid : item.id,
+      //             });
+      //             break;
+      //           case "workExplanationList":
+      //             console.error("workExplanationList");
+      //             state.baseInfo[key].push({
+      //               fodderId: null,
+      //               audioName: "",
+      //               audioUrl: "",
+      //               openByDefault: true,
+      //               navigationId: item.sid ? item.sid : item.id,
+      //               playRepeat: true,
+      //             });
+      //             break;
+      //         }
+      //       }
+
+      //       // if (key == "workCustomMaskList") {
+      //       //   let hasMask = false;
+      //       //   state.baseInfo[key].forEach((mask_item, mask_index) => {
+      //       //     if (mask_item.navigationId == item.id) {
+      //       //       hasMask = true;
+      //       //     }
+      //       //   });
+      //       //   if (!hasMask) {
+      //       //     state.baseInfo[key].push({
+      //       //       data: {
+      //       //         earth: {
+      //       //           antidistorted: true,
+      //       //           fodderId: null,
+      //       //           icon: "",
+      //       //           isDelete: 0,
+      //       //           isShow: false,
+      //       //           navigationId: item.id,
+      //       //           scale: 1,
+      //       //           type: "earth",
+      //       //           workId: state.baseInfo.workId,
+      //       //         },
+      //       //         sky: {
+      //       //           antidistorted: true,
+      //       //           fodderId: null,
+      //       //           icon: "",
+      //       //           id: 7,
+      //       //           isDelete: 0,
+      //       //           isShow: true,
+      //       //           navigationId: item.id,
+      //       //           scale: 1,
+      //       //           type: "sky",
+      //       //           workId: state.baseInfo.workId,
+      //       //         },
+      //       //       },
+      //       //       navigationId: item.id,
+      //       //     });
+      //       //   }
+      //       // }
+      //       // if (key == "workVisualAngleList") {
+      //       //   let hasVisual = false;
+      //       //   state.baseInfo[key].forEach((visual_item, visual_index) => {
+      //       //     if (visual_item.navigationId == item.id) {
+      //       //       hasVisual = true;
+      //       //       visual_item.icon = item.icon;
+      //       //     }
+      //       //   });
+      //       //   if (!hasVisual) {
+      //       //     state.baseInfo[key].push({
+      //       //       hlookat: 0,
+      //       //       icon: item.icon,
+      //       //       // id: 0,
+      //       //       vlookat: 0,
+      //       //       vlookatmax: 90,
+      //       //       vlookatmin: -90,
+      //       //       workId: state.baseInfo.workId,
+      //       //       navigationId: item.id,
+      //       //     });
+      //       //   }
+      //       // }
+      //       // if(key=='workExplanationList'){
+
+      //       // }
+      //     });
+      //   });
+      // }
+      // console.error(state.baseInfo["workVisualAngleList"]);
+    },
+    initbaseInfo(state, payload) {
+      // console.error('initbaseInfo')
+      let data = {
+        firstScene: null,
+        workVisualAngleList: [],
+        navigationTrees: [],
+        workCustomMaskList: [],
+        workId: "",
+
+        work: {
+          description: "",
+          icon: "",
+          id: "",
+          isAuto: 0,
+          name: "",
+          password: "",
+        },
+        workBackgroundMusic: null,
+        workCoverType: {
+          coverImageInWay: 0,
+          coverImageOrder: "",
+          coverImgBac: "",
+          coverMo: "",
+          coverMoLoc: "",
+          coverPc: "",
+          coverPcLoc: "",
+          coverSelect: "img",
+          coverVideoBac: "",
+          coverVideoControl: 0,
+          coverVideoInWay: 0,
+          cover_mo_id: 0,
+          cover_pc_id: 0,
+          id: 0,
+          imgColorSelec: "",
+          isShowCover: 0,
+          videoBacImg: "",
+          videoColorSelec: "",
+          videoMo: "",
+          videoMoIcon: "",
+          videoMoId: 0,
+          videoMoLoc: "",
+          videoPc: "",
+          videoPcIcon: "",
+          videoPcLoc: "",
+          video_pc_id: 0,
+          workId: "",
+        },
+        workLogo: {
+          id: 0,
+          isLogo: 0,
+          logo: "",
+          logoChange: false,
+          workId: "",
+        },
+        workOpeningAnimation: {
+          id: 0,
+          isShowOpeningAnimation: false,
+          openingAnimationType: 0,
+          workId: "",
+        },
+        workOpeningTip: {
+          appIcon: "",
+          appIconId: "",
+          id: 0,
+          isRemind: 0,
+          pcIcon: "",
+          pcIconId: "",
+          remindTime: 0,
+          workId: "",
+        },
+        workCustomButtonList: [
+          {
+            id: 0,
+            isShow: 0,
+            name: "",
+            openMethod: "",
+            type: "",
+            value: "",
+            workId: "",
+          },
+        ],
+        workExplanationList: [],
+        workHotList: [],
+      };
+      if (!state.baseInfo) {
+        state.baseInfo = data;
+        // state.baseInfo = {};
+      }
+      for (let key in state.baseInfo) {
+        if (state.baseInfo.hasOwnProperty(key)) {
+          if (payload[key] == null) {
+            state.baseInfo[key] = data[key];
+          } else {
+            state.baseInfo[key] = payload[key];
+          }
+        }
+      }
+    },
+    setData(state, payload) {
+      // this.commit("base/initbaseInfo");
+      for (let key in payload) {
+        if (state.baseInfo.hasOwnProperty(key)) {
+          state.baseInfo[key] = payload[key];
+        }
+      }
+    },
+    updateBaseInfo(state, payload) {
+      for (let key in payload) {
+        state.baseInfo[key] = payload[key];
+        // if (key == "navigationTrees") {
+        //   console.error(state.sceneList);
+        // }
+      }
+    },
+  },
+  actions: {
+    save({ commit, state, rootState }) {
+      // return new Promise((resolve,reject)=>{})
+
+      return new Promise((resolve, reject) => {
+        saveBaseWorkInfo(
+          state.baseInfo,
+          (res) => {
+            // vue.$msg.success(i18n.t("gather.save_done"));
+            // document.title = this.info.name;
+            document.title = state.baseInfo.work.name || i18n.t("gather.no_title");
+            $waiting.hide();
+            // this.getInfo();
+            // this.$store.commit("UpdateIsShowState", true);
+            // this.commit("TakeInfoSnapShotAtSave");
+            resolve(res);
+          },
+          (rej) => {
+            reject(rej);
+          }
+        );
+      });
+    },
+  },
+};

+ 88 - 0
packages/qjkankan-editor/src/Store/modules/explanation.js

@@ -0,0 +1,88 @@
+import Vue from "vue";
+import { i18n } from "@/lang";
+import { explanationSave } from "@/api";
+import { $waiting } from "@/components/shared/loading";
+import { forEach } from "lodash";
+let vue = new Vue();
+export default {
+  namespaced: true,
+  state() {
+    return {
+      workExplanationList: [],
+    };
+  },
+  getters: {
+    workExplanationList: (state) => state.workExplanationList,
+  },
+  mutations: {
+    setExplanationList(state, payload) {
+      let { sceneList, workExplanationList } = payload;
+      state.workExplanationList = workExplanationList;
+      // console.error("setExplanationList", sceneList, state.workExplanationList);
+
+      sceneList.forEach((s_item, s_index) => {
+        if (!state.workExplanationList.some((w_item) => s_item.id == w_item.navigationId || s_item.sid == w_item.navigationId)) {
+          // console.error(s_index, "不存在");
+
+          state.workExplanationList.push({
+            fodderId: null,
+            audioName: "",
+            audioUrl: "",
+            openByDefault: true,
+            navigationId: s_item.sid ? s_item.sid : s_item.id,
+            playRepeat: true,
+          });
+        } else {
+          // console.error(s_index, "存在");
+          let idx = state.workExplanationList.findIndex((idx_item) => s_item.id == idx_item.navigationId || s_item.sid == idx_item.navigationId);
+          // console.error("idx", idx, s_item.sid, s_item.id);
+          if (idx >= 0 && s_item.sid && typeof s_item.id == "number") {
+            // console.error(s_index, idx, "接口保存后的数据");
+            state.workExplanationList[idx].navigationId = s_item.id;
+          }
+        }
+      });
+
+      state.workExplanationList.forEach((item, index) => {
+        if (!sceneList.some((j_item) => item.navigationId == j_item.id || item.navigationId == j_item.sid)) {
+          // console.error(index, "删除了");
+          state.workExplanationList.splice(index, 1);
+        }
+      });
+
+      // console.error(state.workExplanationList);
+    },
+    setData(state, payload) {},
+  },
+  actions: {
+    save({ commit, state, rootState }) {
+      // console.error(rootState.base.baseInfo.workExplanationList);
+      // let list = [];
+      // if (rootState.base.sceneList.length != rootState.base.baseInfo.workExplanationList.length) {
+      //   list = rootState.base.baseInfo.workExplanationList.filter((item1) => rootState.base.sceneList.some((item2) => item2.id === item1.navigationId));
+      // } else {
+      //   list = rootState.base.baseInfo.workExplanationList;
+      // }
+      console.error(state.workExplanationList);
+      // $waiting.hide();
+      // return;
+
+      return new Promise((resolve, reject) => {
+        explanationSave(
+          {
+            list: state.workExplanationList,
+          },
+          (res) => {
+            // vue.$msg.success(i18n.t("gather.save_done"));
+            $waiting.hide();
+            this.commit("base/updateBaseInfo", { workExplanationList: state.workExplanationList });
+            resolve(res);
+          },
+          (rej) => {
+            reject(rej);
+          }
+        );
+      });
+    },
+  },
+};

+ 294 - 0
packages/qjkankan-editor/src/Store/modules/hotspot.js

@@ -0,0 +1,294 @@
+import Vue from "vue";
+import { i18n } from "@/lang";
+import { workHotSave } from "@/api";
+import { $waiting } from "@/components/shared/loading";
+
+import browser from "@/utils/browser";
+import { deepClone, isSameObject } from "@/utils/other.js";
+let vue = new Vue();
+export default {
+  namespaced: true,
+  state() {
+    return {
+      hotspotList: [],
+      currentHotsopts: [],
+      hotspot: null,
+      hotspotTypeList: {
+        scene: "scene",
+        image: "image",
+        video: "video",
+        audio: "audio",
+        link: "hyperlink",
+        textarea: "textarea",
+        tag: null,
+        imageText: "imageTextInfo",
+        article: "articleInfo",
+        pdf: "pdfInfo",
+        phone: "phoneInfo",
+      },
+      hotspotIconTypeList: {
+        system_icon: null,
+        custom_image: "customIconInfo",
+        serial_frame: "serialFrameInfo",
+        personalized_tag: "personalizedTagInfo",
+      },
+      dataType: {
+        customIconInfo: {
+          // 热点图标类型为自定义图标时,图标的数据
+          img: "",
+        },
+        serialFrameInfo: {
+          // 热点图标类型为序列帧时,序列帧的数据
+          img: "",
+          frameNumber: 0, // 总帧数
+          duration: 0, // 总播放时长(秒)
+        },
+        personalizedTagInfo: {
+          // 热点图标类型为个性标签时,个性标签的数据
+          isShowLine: true,
+          lineDirection: "left-top",
+          fillColor: "rgba(0, 0, 0, 0.5)",
+          borderColor: "rgba(255, 255, 255, 0.8)",
+          textColor: "rgba(255, 255, 255, 1)",
+          textDirection: "left-right",
+          isTextWrap: false,
+          textNumPerLine: 10,
+        },
+
+        scene: null,
+        hyperlink: "",
+        textarea: "",
+        image: [],
+        audio: "",
+        video: "",
+        imageTextInfo: {
+          // 热点类型为图文时,图文内容
+          imageList: [],
+          text: "",
+          isApplyToAll: true,
+          audio: {},
+        },
+        phoneInfo: {
+          // 热点类型为电话时,对应数据
+          phone: "",
+        },
+        pdfInfo: {
+          // 热点类型为pdf时,对应数据
+          name: "",
+          url: "",
+        },
+        articleInfo: {
+          html: "",
+        },
+      },
+    };
+  },
+  getters: {
+    hotspot: (state) => state.hotspot,
+    currentHotsopts: (state, getters, rootState, rootGetters) => {
+      state.currentHotsopts = state.hotspotList.filter((item) => item.navigationId == rootState.scene.currentScene.id || item.navigationId == rootState.scene.currentScene.sid);
+      return state.currentHotsopts;
+    },
+    hotspotList: (state, getters, rootState, rootGetters) => {
+      let hotspots = rootState.base.baseInfo.workHotList;
+      if (hotspots.length && !state.hotspotList.length) {
+        state.hotspotList = hotspots;
+      }
+      if (state.hotspotList.length) {
+        state.hotspotList.forEach((item, index) => {
+          if (item.content) {
+            let hotSpotType = item.hotspotType;
+            for (let key in state.hotspotTypeList) {
+              let value = state.hotspotTypeList[key];
+              if (key == hotSpotType && item.content[value]) {
+                item[value] = item.content[value];
+              } else if (value) {
+                item[value] = state.dataType[value];
+              }
+            }
+
+            let hotSpotIconType = item.hotspotIconType;
+            for (let key in state.hotspotIconTypeList) {
+              let value = state.hotspotIconTypeList[key];
+              if (key == hotSpotIconType && item.content[value]) {
+                item[value] = item.content[value];
+              } else if (value) {
+                item[value] = state.dataType[value];
+              }
+            }
+          }
+        });
+      }
+
+      return state.hotspotList;
+    },
+    // 1.3.0 新增层级后所有热点类型不同汇总显示的ICON
+    hotspotIcon: (state) => {
+      const category = state.hotspot && state.hotspot.hotspotIconType ? state.hotspot.hotspotIconType : "";
+      switch (category) {
+        case "system_icon":
+          return {
+            img: state.hotspot.img,
+            type: "system_icon",
+          };
+        case "custom_image":
+          return {
+            ...state.hotspot.customIconInfo,
+            type: "custom_image",
+          };
+        case "serial_frame":
+          return {
+            ...state.hotspot.serialFrameInfo,
+            img: state.hotspot.serialFrameInfo.img,
+            type: "serial_frame",
+          };
+        case "personalized_tag":
+          return {
+            ...state.hotspot.personalizedTagInfo,
+            img: "",
+            type: "personalized_tag",
+          };
+        default:
+          return {
+            img: "",
+          };
+      }
+    },
+    // currentScene: (state) => state.currentScene,
+  },
+  mutations: {
+    SetHotspot(state, data) {
+      state.hotspot = data;
+      this.commit("BackupHotSpot", browser.CloneObject(data));
+    },
+    updateHotspot(state, payload) {
+      if (payload.id) {
+        // 编辑
+        state.hotspotList.forEach((item, index) => {
+          if (item.id == payload.id) {
+            state.hotspotList[index] = payload;
+          }
+        });
+        state.currentHotsopts.forEach((item, index) => {
+          if (item.id == payload.id) {
+            state.currentHotsopts[index] = payload;
+          }
+        });
+      } else {
+        //新增
+        payload.id = "add_" + new Date().getTime();
+        state.hotspotList.push(payload);
+      }
+
+    },
+    processData(state, payload) {
+      // state.hotspot.content = {};
+      let list = state.hotspotList;
+      list.forEach((item) => {
+        if (!item.content) {
+          item.content = {};
+        }
+        let hotSpotType = item.hotspotType;
+
+        for (let key in state.hotspotTypeList) {
+          let value = state.hotspotTypeList[key];
+          if (key == hotSpotType) {
+            if (value) {
+              item.content[value] = item[value];
+            }
+          }
+          delete item[value];
+        }
+
+        let hotSpotIconType = item.hotspotIconType;
+        for (let key in state.hotspotIconTypeList) {
+          let value = state.hotspotIconTypeList[key];
+          if (key == hotSpotIconType) {
+            if (value) {
+              item.content[value] = item[value];
+            }
+          }
+          delete item[value];
+        }
+      });
+    },
+  },
+  actions: {
+    save({ commit, state, rootState }, payload) {
+      let list = deepClone(state.hotspotList);
+
+      list.forEach((item) => {
+        let current = rootState.base.sceneList.find((s_item) => s_item.sid && s_item.sid == item.navigationId);
+        if (current) {
+          item.navigationId = current.id;
+        }
+        if (item.hotspotType == "scene" && typeof item.scene.id != "number" && item.scene.id.indexOf("add") != -1) {
+          //场景类型 如果场景是新增,则scene的id也需要从服务端取值
+          let current = rootState.base.sceneList.find((s_item) => s_item.sid && s_item.sid == item.scene.sid);
+          if (current) {
+            item.scene.id = current.id;
+          }
+        }
+        if (typeof item.id != "number" && item.id.indexOf("add") != -1) {
+          delete item.id;
+        }
+        if (!item.content) {
+          item.content = {};
+        }
+        let hotSpotType = item.hotspotType;
+
+        for (let key in state.hotspotTypeList) {
+          let value = state.hotspotTypeList[key];
+          if (key == hotSpotType) {
+            if (value) {
+              item.content[value] = item[value];
+            }
+          }
+          delete item[value];
+        }
+        let hotSpotIconType = item.hotspotIconType;
+        for (let key in state.hotspotIconTypeList) {
+          let value = state.hotspotIconTypeList[key];
+          if (key == hotSpotIconType) {
+            if (value) {
+              item.content[value] = item[value];
+            }
+          }
+          delete item[value];
+        }
+      });
+      if (rootState.base.sceneList.length != list.length) {
+        //如果场景不存在,则对应的热点不保存
+        list = list.filter((item) => {
+          let hasTag = false;
+          rootState.base.sceneList.forEach((s_item) => {
+            if (s_item.id == item.navigationId) {
+              hasTag = true;
+            }
+          });
+          return hasTag;
+        });
+      }
+      // $waiting.hide();
+      // return;
+      let data = { list, workId: rootState.base.baseInfo.workId };
+
+      return new Promise((resolve, reject) => {
+        workHotSave(
+          data,
+          (res) => {
+            // vue.$msg.success(i18n.t("gather.save_done"));
+            $waiting.hide();
+            this.commit("base/updateBaseInfo", {
+              workHotList: state.hotspotList,
+            });
+            resolve(res);
+          },
+          (rej) => {
+            reject(rej);
+          }
+        );
+      });
+    },
+  },
+};

+ 12 - 0
packages/qjkankan-editor/src/Store/modules/index.js

@@ -1,8 +1,20 @@
 import scene from "./scene";
 import tags from "./tags";
 import notice from "./notice";
+import base from "./base";
+import screen from "./screen";
+import hotspot from "./hotspot";
+import navigation from "./navigation";
+import mask from "./mask";
+import explanation from "./explanation";
 export default {
   scene,
   tags,
   notice,
+  base,
+  screen,
+  hotspot,
+  navigation,
+  mask,
+  explanation,
 };

+ 139 - 0
packages/qjkankan-editor/src/Store/modules/mask.js

@@ -0,0 +1,139 @@
+import Vue from "vue";
+import { i18n } from "@/lang";
+import { maskSave } from "@/api";
+import { $waiting } from "@/components/shared/loading";
+import { forEach } from "lodash";
+let vue = new Vue();
+export default {
+  namespaced: true,
+  state() {
+    return {
+      workCustomMaskList: [],
+    };
+  },
+  getters: {
+    workCustomMaskList: (state, getters, rootState, rootGetters) => {
+      return state.workCustomMaskList;
+    },
+  },
+  mutations: {
+    setCustomMaskList(state, payload) {
+      let { sceneList, workCustomMaskList, workId } = payload;
+      state.workCustomMaskList = workCustomMaskList;
+      // console.error("setCustomMaskList", sceneList, state.workCustomMaskList);
+
+      sceneList.forEach((s_item, s_index) => {
+        if (!state.workCustomMaskList.some((w_item) => s_item.id == w_item.navigationId || s_item.sid == w_item.navigationId)) {
+          // console.error(s_index, "不存在");
+          state.workCustomMaskList.push({
+            data: {
+              earth: {
+                antidistorted: true,
+                fodderId: null,
+                icon: "",
+                isShow: false,
+                navigationId: s_item.sid ? s_item.sid : s_item.id,
+                scale: 1,
+                type: "earth",
+                workId,
+              },
+              sky: {
+                antidistorted: true,
+                fodderId: null,
+                icon: "",
+                isShow: true,
+                navigationId: s_item.sid ? s_item.sid : s_item.id,
+                scale: 1,
+                type: "sky",
+                workId,
+              },
+            },
+            navigationId: s_item.sid ? s_item.sid : s_item.id,
+          });
+        } else {
+          // console.error(s_index, "存在");
+          let idx = state.workCustomMaskList.findIndex((idx_item) => s_item.id == idx_item.navigationId || s_item.sid == idx_item.navigationId);
+          // console.error("idx", idx, s_item.sid, s_item.id);
+          if (idx >= 0 && s_item.sid && typeof s_item.id == "number") {
+            // console.error(s_index, idx, "接口保存后的数据");
+            state.workCustomMaskList[idx].navigationId = s_item.id;
+            state.workCustomMaskList[idx].data.sky.navigationId = s_item.id;
+            state.workCustomMaskList[idx].data.earth.navigationId = s_item.id;
+          }
+        }
+      });
+
+      state.workCustomMaskList.forEach((item, index) => {
+        if (!sceneList.some((j_item) => item.navigationId == j_item.id || item.navigationId == j_item.sid)) {
+          // console.error(index, "删除了");
+          state.workCustomMaskList.splice(index, 1);
+        }
+      });
+
+      // console.error(state.workCustomMaskList);
+    },
+
+    applycustomMaskToAll(state, payload) {
+      const { type, currentMask } = payload;
+      state.workCustomMaskList.forEach((item, index) => {
+        if (type === "sky") {
+          for (let key in currentMask.data.sky) {
+            if (key != "id" && key != "navigationId") {
+              item.data.sky[key] = currentMask.data.sky[key];
+            }
+          }
+        } else if (type === "earth") {
+          // console.log("当前场景-earth", earth);
+          for (let key in currentMask.data.earth) {
+            if (key != "id" && key != "navigationId") {
+              item.data.earth[key] = currentMask.data.earth[key];
+            }
+          }
+        }
+      });
+    },
+    setData(state, payload) {},
+  },
+  actions: {
+    save({ commit, state, rootState }) {
+      // console.error(rootState.base.baseInfo.workCustomMaskList);
+      // let workCustomMaskList = [];
+      // if (rootState.base.sceneList.length != rootState.base.baseInfo.workCustomMaskList.length) {
+      //   workCustomMaskList = rootState.base.baseInfo.workCustomMaskList.filter((item1) => rootState.base.sceneList.some((item2) => item2.id === item1.navigationId));
+      // } else {
+      //   workCustomMaskList = rootState.base.baseInfo.workCustomMaskList;
+      // }
+      // console.error(workCustomMaskList);
+
+      let list = [];
+
+      state.workCustomMaskList.forEach((item) => {
+        //组装服务端需要的数据
+        for (let key in item.data) {
+          list.push(item.data[key]);
+        }
+      });
+      // console.error(list);
+
+      // return
+
+      return new Promise((resolve, reject) => {
+        maskSave(
+          {
+            list,
+          },
+          (res) => {
+            // vue.$msg.success(i18n.t("gather.save_done"));
+            $waiting.hide();
+            // console.error(res);
+            this.commit("base/updateBaseInfo", { workCustomMaskList: state.workCustomMaskList });
+            resolve(res);
+          },
+          (rej) => {
+            reject(rej);
+          }
+        );
+      });
+    },
+  },
+};

+ 115 - 11
packages/qjkankan-editor/src/Store/modules/navigation.js

@@ -1,21 +1,125 @@
+import Vue from "vue";
+import { i18n } from "@/lang";
+import { navigationSave, initialSet } from "@/api";
+import { $waiting } from "@/components/shared/loading";
+let vue = new Vue();
 export default {
   namespaced: true,
   state() {
     return {
+      isSave: false,
       catalogTopology: [],
-
-      currentCatalogRoot: {},
-      //当前二级分组
-      currentSecondary: {},
-      //二级分组
-      secondaryList: {},
-      //当前场景分组
-      currentScenesList: {},
+      currentSecondId: null,
+      currentRootId: null,
+      saveFristScene: false, //是否可以保存初始场景
+      // currentCatalogRoot: {},
+      // //当前二级分组
+      // currentSecondary: {},
+      // //二级分组
+      // secondaryList: {},
+      // //当前场景分组
+      // currentScenesList: {},
+      navigationTrees: [],
     };
   },
-  getters: {},
-  mutations: {},
+  getters: {
+    isSave: (state) => state.isSave,
+    saveFristScene: (state) => state.saveFristScene,
+    currentRootId: (state) => state.currentRootId,
+    currentSecondId: (state) => state.currentSecondId,
+  },
+  mutations: {
+    setData(state, payload) {
+      for (let key in payload) {
+        state[key] = payload[key];
+      }
+    },
+  },
   actions: {
-    
+    save({ commit, state, rootState }) {
+      let list = JSON.parse(JSON.stringify(rootState.base.baseInfo.navigationTrees));
+      // console.error(rootState.base.baseInfo.navigationTrees);
+      list.forEach((item, index) => {
+        item.sort = index;
+        if (typeof item.id != "number" && item.id.indexOf("add_") != -1) {
+          delete item.id;
+        }
+
+        item = item.children.forEach((s_item, s_index) => {
+          s_item.sort = s_index;
+          if (typeof s_item.id != "number" && s_item.id.indexOf("add_") != -1) {
+            delete s_item.id;
+          }
+          // console.error(s_item);
+
+          s_item = s_item.children.forEach((t_item, t_index) => {
+            t_item.sort = t_index;
+            if (typeof t_item.id != "number" && t_item.id.indexOf("add_") != -1) {
+              delete t_item.id;
+            }
+          });
+        });
+      });
+      // console.error(rootState.base.baseInfo.navigationTrees, list);
+      // $waiting.hide();
+      // return;
+
+      return new Promise((resolve, reject) => {
+        navigationSave(
+          { list },
+          (res) => {
+            $waiting.hide();
+            if (res.code == 0) {
+              // vue.$msg.success(i18n.t("gather.save_done"));
+              // this.commit("TakeInfoSnapShotAtSave");
+              this.commit("base/updateBaseInfo", { navigationTrees: res.data });
+              if (rootState.scene.currentScene) {
+                setTimeout(() => {
+                  // console.error("有当前场景", rootState.scene.currentScene);
+                  let curreentScene = rootState.base.sceneList.find((item) => item.sid && item.sid == rootState.scene.currentScene.sid);
+                  if (curreentScene) {
+                    this.commit("scene/setCurrentScene", curreentScene);
+                  }
+                }, 0);
+                if (rootState.base.baseInfo.firstScene) {
+                  //如果初始场景是新增的,则需要更新id
+                  setTimeout(() => {
+                    let firstScene = rootState.base.sceneList.find((item) => item.sid && item.sid == rootState.base.baseInfo.firstScene.sid);
+                    if (firstScene) {
+                      rootState.base.baseInfo.firstScene.id = firstScene.id;
+                    }
+                  }, 0);
+                }
+              }
+              this.commit("base/initDefaultData");
+              state.isSave = true;
+
+              setTimeout(() => {
+                resolve();
+                if (state.saveFristScene) {
+                  let data = {};
+                  // console.error("有当前场景", rootState.base.baseInfo.firstScene);
+                  if (rootState.base.baseInfo.firstScene) {
+                    let firstScene = rootState.base.sceneList.find((item) => item.sceneCode == rootState.base.baseInfo.firstScene.sceneCode);
+                    if (firstScene) {
+                      data = { navigationId: firstScene.id, operType: "add" };
+                    }
+                  } else {
+                    data = { navigationId: null, operType: "delete" };
+                  }
+                  initialSet(data, (res) => {
+                    commit("setData", { saveFristScene: false });
+                  });
+                }
+              }, 0);
+            }
+          },
+          (err) => {
+            $waiting.hide();
+            reject();
+          }
+        );
+      });
+    },
   },
 };

+ 37 - 32
packages/qjkankan-editor/src/Store/modules/scene.js

@@ -30,10 +30,11 @@ export default {
       // 场景列表
       list: [],
       //当前场景
-      currentScene: {
-        customMask: initCustomMask,
-        initVisual: initVisual,
-      },
+      // currentScene: {
+      //   customMask: initCustomMask,
+      //   initVisual: initVisual,
+      // },
+      currentScene: null,
       //访问密码
       password: "",
       //场景数据
@@ -46,10 +47,12 @@ export default {
       secondaryList: {},
       //当前场景分组
       currentScenesList: {},
+      saveApiList: [],
     };
   },
   getters: {
     list: (state) => state.list,
+    saveApiList: (state) => state.saveApiList,
     secondaryList: (state) => state.secondaryList,
     currentCatalogRoot: (state) => state.currentCatalogRoot,
     currentScenesList: (state) => state.currentScenesList,
@@ -80,6 +83,19 @@ export default {
     setPassword(state, payload) {
       state.password = payload;
     },
+    //添加需要保存的API
+    setSaveApiList(state, payload) {
+      if (state.saveApiList.some((i) => i == payload)) {
+        return;
+      } else {
+        state.saveApiList.push(payload);
+      }
+      console.error("setSaveApiList", state.saveApiList);
+    },
+    removeSaveApi(state, payload) {
+      state.saveApiList = [];
+      console.error("removeSaveApi", state.saveApiList);
+    },
 
     // 设置当前场景
     setCurrentScene(state, payload) {
@@ -106,19 +122,18 @@ export default {
     // 设置当前一级分组
     setCurrentCatalogRoot(state, payload) {
       state.currentCatalogRoot = payload;
-      let temp = [];
-      payload.children &&
-        payload.children.forEach((item) => {
-          state.metadata.catalogs.forEach((sub) => {
-            if (item == sub.id) {
-              if (state.list.some((iii) => iii.category == sub.id)) {
-                temp.push(sub);
-              }
-            }
-          });
-        });
-
-      this.commit("scene/setSecondaryList", temp);
+      // let temp = [];
+      // payload.children &&
+      //   payload.children.forEach((item) => {
+      //     state.metadata.catalogs.forEach((sub) => {
+      //       if (item == sub.id) {
+      //         if (state.list.some((iii) => iii.category == sub.id)) {
+      //           temp.push(sub);
+      //         }
+      //       }
+      //     });
+      //   });
+      this.commit("scene/setSecondaryList", payload.children);
     },
 
     // 设置当前二级分组列表
@@ -140,9 +155,7 @@ export default {
   actions: {
     syncCurrentSceneToStore({ commit, state, rootState }) {
       const currentScene = state.currentScene;
-      const updateSceneIndex = Array.from(rootState.info.scenes).findIndex(
-        (item) => item.id === currentScene.id
-      );
+      const updateSceneIndex = Array.from(rootState.info.scenes).findIndex((item) => item.id === currentScene.id);
       if (updateSceneIndex > -1) {
         rootState.info.scenes[updateSceneIndex] = currentScene;
       }
@@ -176,23 +189,15 @@ export default {
       const currentScene = state.currentScene;
       Array.from(rootState.info.scenes).forEach((item, index) => {
         if (item.type === "pano") {
-          const isHasVlookat = () =>
-            rootState.info.scenes[index]["initVisual"].vlookat &&
-            !isNaN(rootState.info.scenes[index]["initVisual"].vlookat);
-          const isHasHlookat = () =>
-            rootState.info.scenes[index]["initVisual"].hlookat &&
-            !isNaN(rootState.info.scenes[index]["initVisual"].hlookat);
+          const isHasVlookat = () => rootState.info.scenes[index]["initVisual"].vlookat && !isNaN(rootState.info.scenes[index]["initVisual"].vlookat);
+          const isHasHlookat = () => rootState.info.scenes[index]["initVisual"].hlookat && !isNaN(rootState.info.scenes[index]["initVisual"].hlookat);
 
           console.log("isHasVlookat", isHasVlookat(), isHasHlookat());
 
           rootState.info.scenes[index]["initVisual"] = {
             ...currentScene.initVisual,
-            vlookat: isHasVlookat()
-              ? rootState.info.scenes[index]["initVisual"].vlookat
-              : vlookat || 0,
-            hlookat: isHasHlookat()
-              ? rootState.info.scenes[index]["initVisual"].hlookat
-              : hlookat || 0,
+            vlookat: isHasVlookat() ? rootState.info.scenes[index]["initVisual"].vlookat : vlookat || 0,
+            hlookat: isHasHlookat() ? rootState.info.scenes[index]["initVisual"].hlookat : hlookat || 0,
           };
         }
       });

+ 133 - 0
packages/qjkankan-editor/src/Store/modules/screen.js

@@ -0,0 +1,133 @@
+import Vue from "vue";
+import { i18n } from "@/lang";
+import { visualSave } from "@/api";
+import { $waiting } from "@/components/shared/loading";
+import { forEach } from "lodash";
+let vue = new Vue();
+export default {
+  namespaced: true,
+  state() {
+    return {
+      workVisualAngleList: [],
+    };
+  },
+  getters: {
+    workVisualAngleList: (state, getters, rootState, rootGetters) => {
+      // let list = rootState.base.baseInfo.workVisualAngleList;
+      // let sceneList = rootState.base.sceneList;
+      // console.error("******", list, sceneList);
+      // if (!state.workVisualAngleList.length) {
+      //   state.workVisualAngleList = list;
+      // }
+      return state.workVisualAngleList;
+    },
+  },
+  mutations: {
+    setVisualAngleList(state, payload) {
+      let { sceneList, workVisualAngleList } = payload;
+      state.workVisualAngleList = workVisualAngleList;
+      // console.error("setVisualAngleList", sceneList, state.workVisualAngleList);
+
+      sceneList.forEach((s_item, s_index) => {
+        if (!state.workVisualAngleList.some((w_item) => s_item.id == w_item.navigationId || s_item.sid == w_item.navigationId)) {
+          // console.error(s_index, "不存在");
+          state.workVisualAngleList.push({
+            hlookat: 0,
+            icon: s_item.icon,
+            vlookat: 0,
+            vlookatmax: 90,
+            vlookatmin: -90,
+            navigationId: s_item.sid ? s_item.sid : s_item.id,
+          });
+        } else {
+          // console.error(s_index, "存在");
+          let idx = state.workVisualAngleList.findIndex((idx_item) => s_item.id == idx_item.navigationId || s_item.sid == idx_item.navigationId);
+          // console.error("idx", idx, s_item.sid, s_item.id);
+          if (idx >= 0 && s_item.sid && typeof s_item.id == "number") {
+            // console.error(s_index, idx, "接口保存后的数据");
+            state.workVisualAngleList[idx].navigationId = s_item.id;
+          }
+        }
+
+        // if (!workVisualAngleList.some((w_item) => s_item.id == w_item.navigationId || s_item.sid == w_item.navigationId)) {
+        //   console.error("1111");
+        //   state.workVisualAngleList.push({
+        //     hlookat: 0,
+        //     icon: s_item.icon,
+        //     vlookat: 0,
+        //     vlookatmax: 90,
+        //     vlookatmin: -90,
+        //     navigationId: s_item.sid ? s_item.sid : s_item.id,
+        //   });
+        // } else {
+        //   let idx = state.workVisualAngleList.findIndex((idx_item) => s_item.id == idx_item.navigationId || s_item.sid == idx_item.navigationId);
+        //   console.error("2222", idx);
+        //   if (idx && s_item.sid && typeof s_item.id == "number") {
+        //     //接口保存后的数据
+        //     console.error("接口保存后的数据");
+        //     state.workVisualAngleList[idx].navigationId = s_item.id;
+        //   }
+        // }
+      });
+
+      state.workVisualAngleList.forEach((item, index) => {
+        if (!sceneList.some((j_item) => item.navigationId == j_item.id || item.navigationId == j_item.sid)) {
+          // console.error(index, "删除了");
+          state.workVisualAngleList.splice(index, 1);
+        }
+      });
+
+      // console.error(state.workVisualAngleList);
+    },
+    setData(state, payload) {
+      this.commit("base/initbaseInfo");
+      // state.baseInfo = payload;
+      for (let key in state.baseInfo) {
+        if (payload[key]) {
+          state.baseInfo[key] = payload[key];
+        }
+      }
+    },
+  },
+  actions: {
+    applyInitVisualToAll({ commit, state, rootState }, payload) {
+      state.workVisualAngleList.forEach((item, index) => {
+        item.vlookatmin = payload[0];
+        item.vlookatmax = payload[1];
+      });
+      // console.error(state.workVisualAngleList);
+    },
+    save({ commit, state, rootState }) {
+      // console.error(rootState.base.baseInfo.workVisualAngleList);
+      // console.error(rootState.base.sceneList);
+      // let workVisualAngleList = [];
+      // if (rootState.base.sceneList.length != rootState.base.baseInfo.workVisualAngleList.length) {
+      //   workVisualAngleList = rootState.base.baseInfo.workVisualAngleList.filter((item1) => rootState.base.sceneList.some((item2) => item2.id === item1.navigationId));
+      // } else {
+      //   workVisualAngleList = rootState.base.baseInfo.workVisualAngleList;
+      // }
+      // console.error("save", state.workVisualAngleList, rootState.base.sceneList);
+
+      // $waiting.hide();
+      // return;
+
+      return new Promise((resolve, reject) => {
+        visualSave(
+          {
+            list: state.workVisualAngleList,
+          },
+
+          (res) => {
+            // vue.$msg.success(i18n.t("gather.save_done"));
+            $waiting.hide();
+            this.commit("base/updateBaseInfo", { workVisualAngleList: res.data });
+            resolve(res);
+          },
+          (rej) => {
+            reject(rej);
+          }
+        );
+      });
+    },
+  },
+};

+ 1 - 1
packages/qjkankan-editor/src/Store/modules/tags.js

@@ -2,7 +2,7 @@ export default {
   namespaced: true,
   state() {
     return {
-      currentEditTag: '',
+      currentEditTag: "",
       // 是否在确认热点位置状态
       isConfirmingPosi: false,
     };

+ 136 - 128
packages/qjkankan-editor/src/api/index.js

@@ -33,16 +33,26 @@ export function getUserInfo(ok, no) {
  * @param {*} no
  */
 export function getPanoInfo(data, ok, no) {
-  return http.get(
-    `${ossUrl}/720yun_fd_manage/${
-      data || number()
-    }/someData.json?_=${Math.random()}`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`${ossUrl}/720yun_fd_manage/${data || number()}/someData.json?_=${Math.random()}`, {}, ok, no);
+}
+/**
+ * 获取全景列表
+ * @param {*} workId
+ * @param {*} ok
+ * @param {*} no
+ */
+export function getSceneList(workId, ok, no) {
+  return http.get(`${URL_FILL}/work/edit/navigation/scene/list?workId=${workId}`, {}, ok, no);
+}
+/**
+ * 获取作品自定义logo
+ * @param {*} workId
+ * @param {*} ok
+ * @param {*} no
+ */
+export function getWorkLogo(data, ok, no) {
+  return http.postJson(`${URL_FILL}/manage/logo/getLogo`, data, ok, no);
 }
-
 /**
  * 获取四维看看场景信息
  * @param {*} data
@@ -50,13 +60,7 @@ export function getPanoInfo(data, ok, no) {
  * @param {*} no
  */
 export function getSceneInfomation(data, ok, no) {
-  debugger;
-  return http.get(
-    `/api/scene/getInfo?num=${data.id}&_=${Math.random()}`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`/api/scene/getInfo?num=${data.id}&_=${Math.random()}`, {}, ok, no);
 }
 
 /**
@@ -66,12 +70,7 @@ export function getSceneInfomation(data, ok, no) {
  * @param {*} no
  */
 export function getTabList(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/manage/catalog/getCatalog/${number()}`,
-    data,
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/manage/catalog/getCatalog/${number()}`, data, ok, no);
 }
 
 /**
@@ -81,12 +80,7 @@ export function getTabList(data, ok, no) {
  * @param {*} no
  */
 export function getTabSceneList(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/manage/catalog/getScene/${data.catalogId}`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/manage/catalog/getScene/${data.catalogId}`, {}, ok, no);
 }
 
 /**
@@ -404,17 +398,8 @@ export function searchInAll3DScenes(data, ok) {
     // ),
   ]).then((res) => {
     console.log(res);
-    let total =
-      res[0].data.total +
-      res[1].data.total +
-      res[2].data.total +
-      res[3].data.total;
-    let list = [
-      ...res[0].data.list,
-      ...res[1].data.list,
-      ...res[2].data.list,
-      ...res[3].data.list,
-    ];
+    let total = res[0].data.total + res[1].data.total + res[2].data.total + res[3].data.total;
+    let list = [...res[0].data.list, ...res[1].data.list, ...res[2].data.list, ...res[3].data.list];
     ok({
       code: 0,
       data: {
@@ -432,12 +417,7 @@ export function searchInAll3DScenes(data, ok) {
  * @param {*} no
  */
 export function getIndex(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/manage/scene/getIndex/${number()}`,
-    data,
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/manage/scene/getIndex/${number()}`, data, ok, no);
 }
 
 /**
@@ -447,12 +427,7 @@ export function getIndex(data, ok, no) {
  * @param {*} no
  */
 export function saveIndexInfo(data, ok, no) {
-  return http.post(
-    `${URL_FILL}/manage/scene/setIndex/${data.id}/${number()}`,
-    "",
-    ok,
-    no
-  );
+  return http.post(`${URL_FILL}/manage/scene/setIndex/${data.id}/${number()}`, "", ok, no);
 }
 
 /**
@@ -532,12 +507,7 @@ export function saveUseHots(data, ok, no) {
  * @param {*} no
  */
 export function deleteIndexInfo(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/manage/scene/removeIndex/${number()}`,
-    data,
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/manage/scene/removeIndex/${number()}`, data, ok, no);
 }
 
 /**
@@ -557,12 +527,7 @@ export function getSceneInfo(data, ok, no) {
  * @param {*} no
  */
 export function getWebTabList(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/web/common/getCatalog/${number()}`,
-    data,
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/web/common/getCatalog/${number()}`, data, ok, no);
 }
 
 /**
@@ -573,21 +538,11 @@ export function getWebTabList(data, ok, no) {
  */
 export function checkWork(data, ok, no) {
   let visit = "";
-  if (
-    window.location.pathname.indexOf("showMobile.html") > -1 ||
-    window.location.pathname.indexOf("show.html") > -1
-  ) {
+  if (window.location.pathname.indexOf("showMobile.html") > -1 || window.location.pathname.indexOf("show.html") > -1) {
     visit = 1111;
   }
   console.log("checkWork", data);
-  return http.get(
-    `${URL_FILL}/web/common/checkWork/${number()}${
-      visit ? "?visit=" + visit : ""
-    }`,
-    data,
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/web/common/checkWork/${number()}${visit ? "?visit=" + visit : ""}`, data, ok, no);
 }
 
 /**
@@ -597,12 +552,7 @@ export function checkWork(data, ok, no) {
  * @param {*} no
  */
 export function getWebTabSceneList(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/web/common/getScene/${data.catalogId}`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/web/common/getScene/${data.catalogId}`, {}, ok, no);
 }
 
 /**
@@ -620,12 +570,7 @@ export function checkLogin() {
  * 检测素材状态
  */
 export function checkMStatus(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/manage/fodder/checkStatus/${data.ids.join(",")}`,
-    { islongpolling: data.islongpolling },
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/manage/fodder/checkStatus/${data.ids.join(",")}`, { islongpolling: data.islongpolling }, ok, no);
 }
 
 /**
@@ -666,12 +611,7 @@ export function getWebIndex(data, ok, no) {
  * @param {*} no
  */
 export function getAllVrList(data, ok, no) {
-  return http.get(
-    `${URL_FILL}/manage/scene/findByWork/${number()}`,
-    data,
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/manage/scene/findByWork/${number()}`, data, ok, no);
 }
 
 /**
@@ -794,22 +734,10 @@ export function getMaterialList(data, ok, no) {
  * @param {*} no
  */
 export function uploadMaterialOld(data, subdata, ok, no, onProgress) {
-  return http.uploadFile(
-    `${URL_FILL}/manage/fodder/upload/${subdata.type}/${subdata.uid}`,
-    data,
-    ok,
-    no,
-    onProgress
-  );
+  return http.uploadFile(`${URL_FILL}/manage/fodder/upload/${subdata.type}/${subdata.uid}`, data, ok, no, onProgress);
 }
 export function uploadMaterial(data, ok, no, onProgress) {
-  return http.uploadFile(
-    `${URL_FILL}/manage/fodder/uploadDir`,
-    data,
-    ok,
-    no,
-    onProgress
-  );
+  return http.uploadFile(`${URL_FILL}/manage/fodder/uploadDir`, data, ok, no, onProgress);
 }
 
 /**
@@ -853,20 +781,18 @@ function folderTreeSortRoutine(folderList) {
 
 // 获取目录结构
 export function getFolderTree(data) {
-  return http
-    .getJson(`${URL_FILL}/manage/dir/getTree/${data.type}`)
-    .then((res) => {
-      if (res.code === 0 && Array.isArray(res.data)) {
-        const temp = {
-          name: "根目录",
-          id: 1,
-          children: res.data,
-        };
-        res.data = temp;
-        postOrderTraversal(res.data, folderTreeSortRoutine);
-      }
-      return res;
-    });
+  return http.getJson(`${URL_FILL}/manage/dir/getTree/${data.type}`).then((res) => {
+    if (res.code === 0 && Array.isArray(res.data)) {
+      const temp = {
+        name: "根目录",
+        id: 1,
+        children: res.data,
+      };
+      res.data = temp;
+      postOrderTraversal(res.data, folderTreeSortRoutine);
+    }
+    return res;
+  });
 }
 
 // 素材库中新增文件夹
@@ -1036,10 +962,92 @@ export function getCamWorksList(data, ok, no) {
  * @param {*} no
  */
 export function exchangeId(data, ok, no) {
-  return http.postJson(
-    `${URL_FILL}/web/common/getIdInfo`,
-    data,
-    ok,
-    no
-  );
-}
+  return http.postJson(`${URL_FILL}/web/common/getIdInfo`, data, ok, no);
+}
+
+// 以下是重构新增的接口
+
+/**
+ * 新增作品
+ */
+
+export function addWork(data, ok, no) {
+  return http.postJson(`${URL_FILL}/manage/work/addWork`, data, ok, no);
+}
+
+/**
+ * 获取作品信息
+ */
+export function getWorkInfo(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.getJson(`${URL_FILL}/work/view`, data, ok, no);
+}
+/**
+ * 保存作品基本设置
+ */
+export function saveBaseWorkInfo(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/base/save`, data, ok, no);
+}
+/**
+ * 保存视角
+ */
+
+export function visualSave(data, ok, no) {
+  console.error(data);
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/visual/save`, data, ok, no);
+}
+/**
+ * 保存视角
+ */
+
+export function workHotSave(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/workHot/save`, data, ok, no);
+}
+/**
+ * 保存初始场景
+ */
+
+export function initialSet(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/navigation/scene/initial/set`, data, ok, no);
+}
+
+// /**
+//  * 导航新增分组
+//  */
+// export function group_add(data, ok, no) {
+//   !data.workId && (data.workId = number());
+//   return http.postJson(`${URL_FILL}/work/edit/navigation/group/add`, data, ok, no);
+// }
+/**
+ * 保存导航
+ */
+export function navigationSave(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/navigation/navigation/save`, data, ok, no);
+}
+/**
+ * 保存遮罩
+ */
+export function maskSave(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/mask/save`, data, ok, no);
+}
+/**
+ * 保存讲解
+ */
+export function explanationSave(data, ok, no) {
+  !data.workId && (data.workId = number());
+  return http.postJson(`${URL_FILL}/work/edit/explanation/save`, data, ok, no);
+}
+
+// /**
+//  * 导航新增分组
+//  */
+// export function navigation_delete(data, ok, no) {
+//   !data.workId && (data.workId = number());
+//   return http.delete(`${URL_FILL}/work/edit/navigation/navigation/delete`, data, ok, no);
+// }

File diff suppressed because it is too large
+ 1064 - 0
packages/qjkankan-editor/src/components/dragTree/index.vue


+ 165 - 152
packages/qjkankan-editor/src/components/insertPositionTipInEditor.vue

@@ -9,7 +9,7 @@
       marginLeft: marginLeft,
     }"
   >
-    {{positionDebug}}
+    {{ positionDebug }}
   </div>
 </template>
 
@@ -25,7 +25,7 @@ export default {
     },
     positionDebug: {
       type: String,
-      default: '',
+      default: "",
     },
     indentLevel: {
       type: Number,
@@ -42,136 +42,149 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: 'info',
-      dragInfo: 'editorNavDragInfo',
+      // info: 'info',
+      info: "base/baseInfo",
+      sceneList: "base/sceneList",
+      dragInfo: "editorNavDragInfo",
     }),
     marginLeft() {
-      return (this.indentLevel - 1) * 12 + 'px' 
+      return (this.indentLevel - 1) * 12 + "px";
     },
   },
   methods: {
     canDrop() {
+      console.error(this.dragInfo.type, this.topologyLevel, this.parentNode);
       switch (this.dragInfo.type) {
-        case 'scene': // 被拖拽的是场景
+        case "scene": // 被拖拽的是场景
           if (this.topologyLevel === 1) {
             // console.log('情况1:被拖拽的是场景,拖拽到一级分组列表');
-            return false
+            return false;
           } else if (this.topologyLevel === 2) {
             // console.log('情况2:被拖拽的是场景,拖拽到一级分组中');
-            if (this.parentNode.children.length === 0) { // 如果目标一级分组内容显示是空的,即其下只有一个(内容肯定是空的)默认二级分组
-              return true
-            } else { // 如果目标一级分组之下显示了内容,则肯定是显示了二级分组。
-              return false
+            if (this.parentNode.children.length === 0) {
+              // 如果目标一级分组内容显示是空的,即其下只有一个(内容肯定是空的)默认二级分组
+              return true;
+            } else {
+              // 如果目标一级分组之下显示了内容,则肯定是显示了二级分组。
+              // return false
+              return true;
             }
           } else if (this.topologyLevel === 3) {
             // console.log('情况3:被拖拽的是场景,拖拽到二级分组中');
-            return true
+            return true;
           } else {
-            console.error('情况4:不该出现');
-            return false
+            console.error("情况4:不该出现");
+            return false;
           }
-        case 'topologyGroupLevel2': // 被拖拽的是拓扑结构中二级分组
+        case "topologyGroupLevel2": // 被拖拽的是拓扑结构中二级分组
           if (this.topologyLevel === 1) {
             // console.log('情况5:被拖拽的是拓扑结构中二级分组,拖拽到一级分组列表');
-            return true
+            return true;
           } else if (this.topologyLevel === 2) {
             // console.log('情况6:被拖拽的是拓扑结构中二级分组,拖拽到一级分组中');
-            return true
+            return true;
           } else if (this.topologyLevel === 3) {
             if (this.indentLevel === 2) {
               // console.log('情况7:被拖拽的是拓扑结构中二级分组,拖拽到隐藏的默认二级分组中');
-              return true
+              return true;
             } else {
               // console.log('情况8:被拖拽的是拓扑结构中二级分组,拖拽到普通的二级分组中');
-              return false
+              return false;
             }
           } else {
-            console.error('情况9:不该出现');
-            return false
+            console.error("情况9:不该出现");
+            return false;
           }
-        case 'topologyGroupLevel1': // 被拖拽的是拓扑结构中一级分组
+        case "topologyGroupLevel1": // 被拖拽的是拓扑结构中一级分组
           if (this.topologyLevel === 1) {
             // console.log('情况10:被拖拽的是拓扑结构中一级分组,拖拽到一级分组列表');
-            return true
-          } else if (this.topologyLevel === 2) { // 拖拽到一级分组中
-            if (this.dragInfo.node.children.length === 1 && this.dragInfo.node.children[0].name === '默认二级分组') {
+            return true;
+          } else if (this.topologyLevel === 2) {
+            // 拖拽到一级分组中
+            if (this.dragInfo.node.children.length === 1 && this.dragInfo.node.children[0].name === "默认二级分组") {
               // console.log('情况11:被拖拽的一级分组只有一个隐藏的默认二级分组,拖拽到一级分组中');
-              return true
+              return true;
             } else {
               // console.log('情况12:被拖拽的一级分组并非只有一个隐藏的默认二级分组,拖拽到一级分组中');
-              return false
+              return false;
             }
-          } else if (this.topologyLevel === 3) { // 拖拽到二级分组中
-            if (this.indentLevel === 2) { // 拖拽到隐藏的默认二级分组中
-              if (this.dragInfo.node.children.length === 1 && this.dragInfo.node.children[0].name === '默认二级分组') {
+          } else if (this.topologyLevel === 3) {
+            // 拖拽到二级分组中
+            if (this.indentLevel === 2) {
+              // 拖拽到隐藏的默认二级分组中
+              if (this.dragInfo.node.children.length === 1 && this.dragInfo.node.children[0].name === "默认二级分组") {
                 if (this.dragInfo.node.children[0].id === this.parentNode.id) {
                   // console.log('情况13:被拖拽的一级分组只有一个隐藏的默认二级分组,拖拽到自身的默认二级分组中');
-                  return false
+                  return false;
                 } else {
                   // console.log('情况14:被拖拽的一级分组只有一个隐藏的默认二级分组,拖拽到另一个一级分组中隐藏的默认二级分组中');
-                  return true
+                  return true;
                 }
               } else {
                 // console.log('情况15:被拖拽的一级分组并非只有一个隐藏的默认二级分组,拖拽到隐藏的默认二级分组中');
-                return false
+                return false;
               }
             } else {
               // console.log('情况16:被拖拽的是拓扑结构中一级分组,拖拽到普通二级分组中')
-              return false
+              return false;
             }
           } else {
-            console.error('情况17:不该出现');
-            return false
+            console.error("情况17:不该出现");
+            return false;
           }
         default:
-          console.error('情况18:不该出现');
-          return false
+          console.error("情况18:不该出现");
+          return false;
       }
     },
     onDragEnter(e) {
       if (!this.canDrop()) {
-        return
+        return;
       } else {
-        e.preventDefault()
-        e.target.style.backgroundColor = '#0076f6'
-        e.dataTransfer.dropEffect = 'move'
+        e.preventDefault();
+        e.target.style.backgroundColor = "#0076f6";
+        e.dataTransfer.dropEffect = "move";
       }
     },
     onDragOver(e) {
       if (!this.canDrop()) {
-        return
+        return;
       } else {
-        e.preventDefault()
-        e.dataTransfer.dropEffect = 'move'
+        e.preventDefault();
+        e.dataTransfer.dropEffect = "move";
       }
     },
     onDragLeave(e) {
       // e.preventDefault()
-      e.target.style.backgroundColor = ''
+      e.target.style.backgroundColor = "";
     },
     onDrop(e) {
       // e.preventDefault()
-      e.target.style.backgroundColor = ''
+      e.target.style.backgroundColor = "";
 
       switch (this.dragInfo.type) {
-        case 'scene': // 被拖拽的是场景
+        case "scene": // 被拖拽的是场景
           {
             if (this.topologyLevel === 2) {
               // console.log('情况2:被拖拽的是场景,拖拽到一级分组中。只有一种可能:目标一级分组内容显示是空的,即其下只有一个(内容肯定是空的)默认二级分组。')
-              
+
               /**
                * 找到被拖拽的场景
                */
-              const draggedNode = this.info.scenes.find((item) => {
-                return item.id === this.dragInfo.node.id
-              })
+              // const draggedNode = this.info.scenes.find((item) => {
 
+              const draggedNode = this.sceneList.find((item) => {
+                // return item.id === this.dragInfo.node.id
+                return item.sceneCode === this.dragInfo.node.sceneCode;
+              });
+
+              console.log( this.parentNode);
               /**
                * 找到目标一级分组
                */
               const targetGroupLevel1 = this.info.catalogRoot.find((item) => {
-                return item.id === this.parentNode.id
-              })
+                return item.id === this.parentNode.id;
+              });
               console.log(targetGroupLevel1);
 
               /**
@@ -179,31 +192,31 @@ export default {
                */
               const targetGroupLevel2 = this.info.catalogs.find((item) => {
                 console.log(item.id);
-                return item.id === targetGroupLevel1.children[0]
-              })
+                return item.id === targetGroupLevel1.children[0];
+              });
 
               /**
                * 被拖拽的场景的所属二级分组记录修改
                */
-              draggedNode.category = targetGroupLevel2.id
+              draggedNode.category = targetGroupLevel2.id;
             } else if (this.topologyLevel === 3) {
               // console.log('情况3:被拖拽的是场景,插入到二级分组中')
 
               /**
                * 确定要插入的分组中各场景应有的weight(从1开始递增,小的先显示)
                */
-              const belongGroupCopy = deepClone(this.parentNode.children)
-              const draggedNodeCopy = deepClone(this.dragInfo.node)
-              draggedNodeCopy.isCopy = true
-              belongGroupCopy.splice(this.index, 0, draggedNodeCopy)
+              const belongGroupCopy = deepClone(this.parentNode.children);
+              const draggedNodeCopy = deepClone(this.dragInfo.node);
+              draggedNodeCopy.isCopy = true;
+              belongGroupCopy.splice(this.index, 0, draggedNodeCopy);
               const toDeleteIndex = belongGroupCopy.findIndex((item) => {
-                return (item.id === this.dragInfo.node.id && !item.isCopy)
-              })
+                return item.id === this.dragInfo.node.id && !item.isCopy;
+              });
               if (toDeleteIndex >= 0) {
-                belongGroupCopy.splice(toDeleteIndex, 1)
+                belongGroupCopy.splice(toDeleteIndex, 1);
               }
               for (let [index, elem] of belongGroupCopy.entries()) {
-                elem.weight = index + 1
+                elem.weight = index + 1;
               }
 
               /**
@@ -212,17 +225,17 @@ export default {
               for (const eachSceneCopy of belongGroupCopy) {
                 for (const eachScene of this.info.scenes) {
                   if (eachSceneCopy.id === eachScene.id) {
-                    this.$set( eachScene, 'weight', eachSceneCopy.weight )
+                    this.$set(eachScene, "weight", eachSceneCopy.weight);
                   }
                   if (this.dragInfo.node.id === eachScene.id) {
-                    eachScene.category = this.parentNode.id // 注意category拼写!
+                    eachScene.category = this.parentNode.id; // 注意category拼写!
                   }
                 }
               }
             }
           }
           break;
-        case 'topologyGroupLevel2': // 被拖拽的是拓扑结构中二级分组
+        case "topologyGroupLevel2": // 被拖拽的是拓扑结构中二级分组
           if (this.topologyLevel === 1) {
             // console.log('情况5:被拖拽的是拓扑结构中二级分组,拖拽到一级分组列表');
 
@@ -230,38 +243,38 @@ export default {
              * 在拖拽到的位置新增一个一级分组,name是原二级分组的name;唯一child的id赋值为被拖拽的二级分组的id
              */
             const newGroupLevel1 = {
-              id: 'r_' + this.$randomWord(true, 8, 8),
+              id: "r_" + this.$randomWord(true, 8, 8),
               name: this.dragInfo.node.name,
               children: [this.dragInfo.node.id],
-            }
-            this.info.catalogRoot.splice(this.index, 0, newGroupLevel1)
+            };
+            this.info.catalogRoot.splice(this.index, 0, newGroupLevel1);
 
             // 被拖拽的二级分组名称改为“默认二级分组”;
             const draggedGroup = this.info.catalogs.find((item) => {
-              return item.id === this.dragInfo.node.id
-            })
-            
-            draggedGroup.name = '默认二级分组'
+              return item.id === this.dragInfo.node.id;
+            });
+
+            draggedGroup.name = "默认二级分组";
 
             /**
              * 被拖拽的二级分组原属的一级分组的children中,删除【被拖拽的二级分组对应的元素】
              */
             const parentGroup = this.info.catalogRoot.find((item) => {
-              return item.id === this.dragInfo.node.parentId
-            })
+              return item.id === this.dragInfo.node.parentId;
+            });
             const idxToDelete = parentGroup.children.findIndex((id) => {
-              return id === this.dragInfo.node.id
-            })
-            parentGroup.children.splice(idxToDelete, 1)
-            
+              return id === this.dragInfo.node.id;
+            });
+            parentGroup.children.splice(idxToDelete, 1);
+
             // 如果被拖拽的二级分组原属的一级分组中没有任何二级分组了,则新增一个默认二级分组
             if (parentGroup.children.length === 0) {
-              let newGroupLevel2Id = 'c_' + this.$randomWord(true, 8, 8)
-              parentGroup.children.push(newGroupLevel2Id)
+              let newGroupLevel2Id = "c_" + this.$randomWord(true, 8, 8);
+              parentGroup.children.push(newGroupLevel2Id);
               this.info.catalogs.push({
                 id: newGroupLevel2Id,
-                name: '默认二级分组',
-              })
+                name: "默认二级分组",
+              });
             }
             break;
           } else if (this.topologyLevel === 2) {
@@ -269,152 +282,152 @@ export default {
 
             // 找到原属一级分组
             const originalParentGroup = this.info.catalogRoot.find((item) => {
-              return item.id === this.dragInfo.node.parentId
-            })
-            
+              return item.id === this.dragInfo.node.parentId;
+            });
+
             // 找到原属一级分组children中那个二级分组条目,并做上待删除记号
             const originalGroupIndex = originalParentGroup.children.findIndex((eachLevel2Id) => {
-              return eachLevel2Id === this.dragInfo.node.id
-            })
-            originalParentGroup.children[originalGroupIndex] += '__need__delete__'
-            
+              return eachLevel2Id === this.dragInfo.node.id;
+            });
+            originalParentGroup.children[originalGroupIndex] += "__need__delete__";
+
             // 找到要插入的一级分组
             const targetGroup = this.info.catalogRoot.find((item) => {
-              return item.id === this.parentNode.id
-            })
-            
+              return item.id === this.parentNode.id;
+            });
+
             // 把被拖拽的二级分组的id插进去
-            targetGroup.children.splice(this.index, 0, this.dragInfo.node.id)
+            targetGroup.children.splice(this.index, 0, this.dragInfo.node.id);
 
             // 把原来那个二级分组条目删除
             this.$nextTick(() => {
               const toDeleteIndex = originalParentGroup.children.findIndex((eachLevel2Id) => {
-                return eachLevel2Id.toString().endsWith('__need__delete__')
-              })
-              originalParentGroup.children.splice(toDeleteIndex, 1)
-            })
+                return eachLevel2Id.toString().endsWith("__need__delete__");
+              });
+              originalParentGroup.children.splice(toDeleteIndex, 1);
+            });
 
             // 如果被拖拽的二级分组原属的一级分组中没有任何二级分组了,则新增一个默认二级分组
             if (originalParentGroup.children.length === 0) {
-              let newGroupLevel2Id = 'c_' + this.$randomWord(true, 8, 8)
-              originalParentGroup.children.push(newGroupLevel2Id)
+              let newGroupLevel2Id = "c_" + this.$randomWord(true, 8, 8);
+              originalParentGroup.children.push(newGroupLevel2Id);
               this.info.catalogs.push({
                 id: newGroupLevel2Id,
-                name: '默认二级分组',
-              })
+                name: "默认二级分组",
+              });
             }
           } else if (this.topologyLevel === 3) {
             // console.log('情况7:被拖拽的是拓扑结构中二级分组,拖拽到隐藏的默认二级分组中');
 
             // 找到拖拽到的二级分组所属的一级分组
             const targetGroupLevel1 = this.info.catalogRoot.find((item) => {
-              return item.id === this.parentNode.parentId
-            })
+              return item.id === this.parentNode.parentId;
+            });
 
             // 新增一个child条目,对应被拖拽的二级分组
-            targetGroupLevel1.children.push(this.dragInfo.node.id)
-            
+            targetGroupLevel1.children.push(this.dragInfo.node.id);
+
             // 找到被拖拽的二级分组原属的一级分组
             const originalGroupLevel1 = this.info.catalogRoot.find((item) => {
-              return item.id === this.dragInfo.node.parentId
-            })
-            
+              return item.id === this.dragInfo.node.parentId;
+            });
+
             // 删除这个二级分组条目
             const idxToDelete = originalGroupLevel1.children.findIndex((id) => {
-              return id === this.dragInfo.node.id
-            })
-            originalGroupLevel1.children.splice(idxToDelete, 1)
+              return id === this.dragInfo.node.id;
+            });
+            originalGroupLevel1.children.splice(idxToDelete, 1);
 
             // 如果被拖拽的二级分组原属的一级分组中没有任何二级分组了,则新增一个默认二级分组
             if (originalGroupLevel1.children.length === 0) {
-              let newGroupLevel2Id = 'c_' + this.$randomWord(true, 8, 8)
-              originalGroupLevel1.children.push(newGroupLevel2Id)
+              let newGroupLevel2Id = "c_" + this.$randomWord(true, 8, 8);
+              originalGroupLevel1.children.push(newGroupLevel2Id);
               this.info.catalogs.push({
                 id: newGroupLevel2Id,
-                name: '默认二级分组',
-              })
+                name: "默认二级分组",
+              });
             }
           }
           break;
-        case 'topologyGroupLevel1': // 被拖拽的是拓扑结构中一级分组
+        case "topologyGroupLevel1": // 被拖拽的是拓扑结构中一级分组
           if (this.topologyLevel === 1) {
             // console.log('情况10:被拖拽的是拓扑结构中一级分组,拖拽到一级分组列表');
 
             // 在一级分组列表中找到这个一级分组条目
             let originalGroupIndex = this.info.catalogRoot.findIndex((item) => {
-              return item.id === this.dragInfo.node.id
-            })
-            
+              return item.id === this.dragInfo.node.id;
+            });
+
             // 复制这个一级分组条目
-            const groupCopy = deepClone(this.info.catalogRoot[originalGroupIndex])
+            const groupCopy = deepClone(this.info.catalogRoot[originalGroupIndex]);
 
             // 旧的一级分组条目加上待删除标记
-            this.info.catalogRoot[originalGroupIndex].__need__delete__ = true
+            this.info.catalogRoot[originalGroupIndex].__need__delete__ = true;
 
             // 插入新的一级分组条目
-            this.info.catalogRoot.splice(this.index, 0 , groupCopy)
+            this.info.catalogRoot.splice(this.index, 0, groupCopy);
 
             // 在一级分组列表中再次找到要删除的一级分组条目
             originalGroupIndex = this.info.catalogRoot.findIndex((item) => {
-              return item.__need__delete__
-            })
+              return item.__need__delete__;
+            });
 
             // 删除旧的一级分组条目
-            this.info.catalogRoot.splice(originalGroupIndex, 1)
+            this.info.catalogRoot.splice(originalGroupIndex, 1);
           } else if (this.topologyLevel === 2) {
             // console.log('情况11:被拖拽的一级分组只有一个隐藏的默认二级分组,拖拽到一级分组中');
 
             // 默认二级分组改名成原一级分组的名字
             const groupLevel2 = this.info.catalogs.find((item) => {
-              return item.id === this.dragInfo.node.children[0].id
-            })
-            groupLevel2.name = this.dragInfo.node.name
+              return item.id === this.dragInfo.node.children[0].id;
+            });
+            groupLevel2.name = this.dragInfo.node.name;
 
             // 拖拽到的一级分组中新增一个child,对应那个二级分组
             const targetGroupLevel1 = this.info.catalogRoot.find((item) => {
-              return item.id === this.parentNode.id
-            })
-            targetGroupLevel1.children.splice(this.index, 0, this.dragInfo.node.children[0].id)
-            
+              return item.id === this.parentNode.id;
+            });
+            targetGroupLevel1.children.splice(this.index, 0, this.dragInfo.node.children[0].id);
+
             // 删除原一级分组
             const originalGroupLevel1Idx = this.info.catalogRoot.findIndex((item) => {
-              return item.id === this.dragInfo.node.id
-            })
-            this.info.catalogRoot.splice(originalGroupLevel1Idx, 1)
+              return item.id === this.dragInfo.node.id;
+            });
+            this.info.catalogRoot.splice(originalGroupLevel1Idx, 1);
           } else if (this.topologyLevel === 3) {
             // console.log('情况14:被拖拽的一级分组只有一个隐藏的默认二级分组,拖拽到另一个一级分组中隐藏的默认二级分组中');
 
             // 找到被拖拽的一级分组索引
             const originalGroupLevel1Idx = this.info.catalogRoot.findIndex((item) => {
-              return item.id === this.dragInfo.node.id
-            })
+              return item.id === this.dragInfo.node.id;
+            });
 
             // 找到被拖拽的一级分组下辖的默认二级分组的完整数据条目
             const groupLevel2 = this.info.catalogs.find((item) => {
-              return item.id === this.info.catalogRoot[originalGroupLevel1Idx].children[0]
-            })
+              return item.id === this.info.catalogRoot[originalGroupLevel1Idx].children[0];
+            });
 
             // 被拖拽的一级分组下辖默认二级分组的名称改为与父亲同名
-            groupLevel2.name = this.info.catalogRoot[originalGroupLevel1Idx].name
+            groupLevel2.name = this.info.catalogRoot[originalGroupLevel1Idx].name;
 
             // 找到拖拽到的二级分组所属的一级分组
             const targetGroupLevel1 = this.info.catalogRoot.find((item) => {
-              return item.id === this.parentNode.parentId
-            })
+              return item.id === this.parentNode.parentId;
+            });
 
             // 新增一个child条目,对应被拖拽的一级分组下辖的默认二级分组
-            targetGroupLevel1.children.push(groupLevel2.id)
-            
+            targetGroupLevel1.children.push(groupLevel2.id);
+
             // 删除被拖拽的一级分组
-            this.info.catalogRoot.splice(originalGroupLevel1Idx, 1)
+            this.info.catalogRoot.splice(originalGroupLevel1Idx, 1);
           }
           break;
         default:
           break;
       }
-    }
-  }
-}
+    },
+  },
+};
 </script>
 
 <style lang="less" scoped>
@@ -431,4 +444,4 @@ export default {
   color: transparent;
   // background-color: red;
 }
-</style>
+</style>

+ 24 - 93
packages/qjkankan-editor/src/components/materialSelectorFromWork.vue

@@ -6,39 +6,20 @@
     </div>
 
     <div class="material-tab">
-      <a
-        class="material-tab-item"
-        @click.prevent="currentMaterialType = 'pano'"
-      >
-        <span
-          class="text"
-          :class="{ active: currentMaterialType === 'pano' }"
-          >{{ $i18n.t(`gather.panorama`) }}</span
-        >
+      <a class="material-tab-item" @click.prevent="currentMaterialType = 'pano'">
+        <span class="text" :class="{ active: currentMaterialType === 'pano' }">{{ $i18n.t(`gather.panorama`) }}</span>
         <div v-if="currentMaterialType === 'pano'" class="bottom-line"></div>
       </a>
       <a class="material-tab-item" @click.prevent="currentMaterialType = '3D'">
-        <span class="text" :class="{ active: currentMaterialType === '3D' }">{{
-          $i18n.t(`gather.scene`)
-        }}</span>
+        <span class="text" :class="{ active: currentMaterialType === '3D' }">{{ $i18n.t(`gather.scene`) }}</span>
         <div v-if="currentMaterialType === '3D'" class="bottom-line"></div>
       </a>
     </div>
 
     <div class="filter" :class="{ active: isSearchKeyInputActive }">
-      <input
-        type="text"
-        :placeholder="$i18n.t(`gather.keywords`)"
-        v-model="searchKey"
-        @focus="isSearchKeyInputActive = true"
-        @blur="isSearchKeyInputActive = false"
-      />
+      <input type="text" :placeholder="$i18n.t(`gather.keywords`)" v-model="searchKey" @focus="isSearchKeyInputActive = true" @blur="isSearchKeyInputActive = false" />
       <i v-if="!searchKey" class="iconfont icon-editor_search search-icon" />
-      <i
-        v-if="searchKey"
-        @click="searchKey = ''"
-        class="iconfont icon-toast_red clear-icon"
-      ></i>
+      <i v-if="searchKey" @click="searchKey = ''" class="iconfont icon-toast_red clear-icon"></i>
     </div>
 
     <div class="table table-pano" v-show="currentMaterialType === 'pano'">
@@ -51,20 +32,9 @@
             return t;
           })
         }} -->
-        <div
-          class="table-body-row"
-          v-for="(item) in panoList"
-          :key="`${item.id}`"
-          @click="onClickRow"
-        >
+        <div class="table-body-row" v-for="item in panoList" :key="`${item.id}`" @click="onClickRow">
           <span class="table-data">
-            <RadioOrCheckbox
-              class="checkbox"
-              :isLightTheme="false"
-              :isMultiSelection="false"
-              :isCheckedInitial="select.some((i) => i.id === item.id)"
-              @change="(v) => selectItem(item, v)"
-            />
+            <RadioOrCheckbox class="checkbox" :isLightTheme="false" :isMultiSelection="false" :isCheckedInitial="select.some((i) => i.id === item.id)" @change="(v) => selectItem(item, v)" />
           </span>
           <span class="table-data">
             <div class="list-img">
@@ -72,9 +42,7 @@
             </div>
           </span>
           <span class="table-data">
-            <span class="name ellipsis" v-title="item.sceneTitle">{{
-              item.sceneTitle
-            }}</span>
+            <span class="name ellipsis" v-title="item.name">{{ item.name }}</span>
           </span>
         </div>
         <!-- <div class="no-more-data">
@@ -84,17 +52,11 @@
       <!-- 无数据时的提示 -->
       <div v-show="!(panoList.length !== 0)" class="no-data">
         <div v-if="latestUsedSearchKey">
-          <img
-            :src="require('@/assets/images/default/empty_search_dark.png')"
-            alt=""
-          />
+          <img :src="require('@/assets/images/default/empty_search_dark.png')" alt="" />
           <span>{{ $i18n.t(`gather.no_search_result`) }}</span>
         </div>
         <div v-if="!latestUsedSearchKey">
-          <img
-            :src="require('@/assets/images/default/empty_dark.png')"
-            alt=""
-          />
+          <img :src="require('@/assets/images/default/empty_dark.png')" alt="" />
           <span>{{ $i18n.t(`gather.no_material_result`) }}</span>
         </div>
       </div>
@@ -102,20 +64,9 @@
 
     <div class="table table-3D" v-show="currentMaterialType === '3D'">
       <div v-show="scene3DList.length !== 0" class="table-body">
-        <div
-          class="table-body-row"
-          v-for="item in scene3DList"
-          :key="`${item.id}`"
-          @click="onClickRow"
-        >
+        <div class="table-body-row" v-for="item in scene3DList" :key="`${item.id}`" @click="onClickRow">
           <span class="table-data">
-            <RadioOrCheckbox
-              class="checkbox"
-              :isLightTheme="false"
-              :isMultiSelection="false"
-              :isCheckedInitial="select.some((i) => i.id === item.id)"
-              @change="(v) => selectItem(item, v)"
-            />
+            <RadioOrCheckbox class="checkbox" :isLightTheme="false" :isMultiSelection="false" :isCheckedInitial="select.some((i) => i.id === item.id)" @change="(v) => selectItem(item, v)" />
           </span>
           <span class="table-data">
             <div class="list-img">
@@ -123,9 +74,7 @@
             </div>
           </span>
           <span class="table-data">
-            <span class="name ellipsis" v-title="item.sceneTitle">{{
-              item.sceneTitle
-            }}</span>
+            <span class="name ellipsis" v-title="item.name">{{ item.name }}</span>
           </span>
         </div>
         <!-- <div class="no-more-data">
@@ -135,17 +84,11 @@
       <!-- 无数据时的提示 -->
       <div v-show="!(scene3DList.length !== 0)" class="no-data">
         <div v-if="latestUsedSearchKey">
-          <img
-            :src="require('@/assets/images/default/empty_search_dark.png')"
-            alt=""
-          />
+          <img :src="require('@/assets/images/default/empty_search_dark.png')" alt="" />
           <span>{{ $i18n.t(`gather.no_search_result`) }}</span>
         </div>
         <div v-if="!latestUsedSearchKey">
-          <img
-            :src="require('@/assets/images/default/empty_dark.png')"
-            alt=""
-          />
+          <img :src="require('@/assets/images/default/empty_dark.png')" alt="" />
           <span>{{ $i18n.t(`gather.no_material_result`) }}</span>
         </div>
       </div>
@@ -156,11 +99,7 @@
         <button class="ui-button deepcancel" @click="$emit('cancel')">
           {{ $i18n.t(`gather.cancel`) }}
         </button>
-        <button
-          class="ui-button submit"
-          :class="{ disable: !select.length }"
-          @click="onClickComfirm"
-        >
+        <button class="ui-button submit" :class="{ disable: !select.length }" @click="onClickComfirm">
           {{ $i18n.t(`gather.comfirm`) }}
         </button>
       </div>
@@ -187,28 +126,28 @@ export default {
     RadioOrCheckbox,
   },
   computed: {
-    ...mapGetters(["info"]),
+    ...mapGetters({ info: "base/baseInfo", sceneList: "base/sceneList" }),
     panoList() {
-      return this.info.scenes
+      return this.sceneList
         .filter((item) => {
           return item.type === "pano";
         })
         .filter((item) => {
           if (this.searchKey) {
-            return item.sceneTitle.includes(this.searchKey);
+            return item.name.includes(this.searchKey);
           } else {
             return item;
           }
         });
     },
     scene3DList() {
-      return this.info.scenes
+      return this.sceneList
         .filter((item) => {
           return item.type === "4dkk";
         })
         .filter((item) => {
           if (this.searchKey) {
-            return item.sceneTitle.includes(this.searchKey);
+            return item.name.includes(this.searchKey);
           } else {
             return item;
           }
@@ -258,9 +197,7 @@ export default {
       }
     },
     onClickRow(e) {
-      const checkboxNodeList = e.currentTarget.getElementsByClassName(
-        "selection-click-target"
-      );
+      const checkboxNodeList = e.currentTarget.getElementsByClassName("selection-click-target");
       if (checkboxNodeList && checkboxNodeList[0]) {
         checkboxNodeList[0].click();
       }
@@ -394,10 +331,7 @@ export default {
   width: 100%;
   height: @table-height;
   > .table-body {
-    height: calc(
-      @table-height - @table-head-row-height - @table-border-size -
-        @table-border-size
-    );
+    height: calc(@table-height - @table-head-row-height - @table-border-size - @table-border-size);
     overflow: auto;
     display: inline-block;
     width: 100%;
@@ -451,10 +385,7 @@ export default {
     }
   }
   > .no-data {
-    height: calc(
-      @table-height - @table-head-row-height - @table-border-size -
-        @table-border-size
-    );
+    height: calc(@table-height - @table-head-row-height - @table-border-size - @table-border-size);
     width: 100%;
     position: relative;
     > div {

+ 56 - 112
packages/qjkankan-editor/src/components/sceneGroupInEditor.vue

@@ -16,18 +16,9 @@
       <div class="drag-image" ref="drag-image">
         <i class="iconfont icon-editor_folder_off"></i>
       </div>
-      <i
-        class="iconfont icon-edit_input_arrow icon-expand"
-        :class="isExpanded ? '' : 'collapsed'"
-      ></i>
-      <i
-        v-show="isExpanded"
-        class="iconfont icon-editor_folder_on folder_expanded"
-      ></i>
-      <i
-        v-show="!isExpanded"
-        class="iconfont icon-editor_folder_off folder_collapsed"
-      ></i>
+      <i class="iconfont icon-edit_input_arrow icon-expand" :class="isExpanded ? '' : 'collapsed'"></i>
+      <i v-show="isExpanded" class="iconfont icon-editor_folder_on folder_expanded"></i>
+      <i v-show="!isExpanded" class="iconfont icon-editor_folder_off folder_collapsed"></i>
       <template v-if="!isRenaming">
         <span class="group-name" v-title="transTitle(groupNode.name)">
           <!-- {{
@@ -38,47 +29,30 @@
           {{ transTitle(groupNode.name) }}
           <!-- {{ groupLv1name(groupNode.name) }} -->
         </span>
-        <i
-          v-show="level === 1"
-          class="iconfont icon-editor_list_add icon-add"
-          v-tooltip="$i18n.t('navigation.add_two_group')"
-          @click.stop="onRequestForAddGroup"
-        >
-        </i>
-        <i
+        <i v-show="level === 1" class="iconfont icon-editor_list_add icon-add" v-tooltip="$i18n.t('navigation.add_two_group')" @click.stop="onRequestForAddGroup"> </i>
+        <!-- <i
           class="iconfont icon-editor_list_image icon-image"
           v-tooltip="$i18n.t('navigation.add_pano_or_scene')"
           @click.stop="onRequestForAddScene"
           v-show="
-            level === 2 ||
-            (level === 1 &&
+            level === 1 ||
+            (level === 0 &&
               (groupNode.children.length === 0 ||
-                (groupNode.children.length === 1 &&
-                  (groupNode.children[0].name === '默认二级分组' ||
-                    groupNode.children[0].name ===
-                      $i18n.t('navigation.default_group_two')))))
+                (groupNode.children.length === 1 && (groupNode.children[0].name === '默认二级分组' || groupNode.children[0].name === $i18n.t('navigation.default_group_two')))))
           "
         >
-        </i>
-        <i
-          class="iconfont icon-editor_list_edit icon-edit"
-          v-tooltip="$i18n.t('navigation.rename')"
-          @click.stop="onClickForRename"
-        >
-        </i>
+        </i> -->
         <i
-          class="iconfont icon-editor_list_delete icon-delete"
-          v-tooltip="$i18n.t('navigation.delete')"
-          @click.stop="onRequestForDelete"
+          class="iconfont icon-editor_list_image icon-image"
+          v-tooltip="$i18n.t('navigation.add_pano_or_scene')"
+          @click.stop="onRequestForAddScene(groupNode)"
+          v-show="groupNode.type == 'group' && groupNode.children[0].type != 'group'"
         >
         </i>
+        <i class="iconfont icon-editor_list_edit icon-edit" v-tooltip="$i18n.t('navigation.rename')" @click.stop="onClickForRename"> </i>
+        <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="$i18n.t('navigation.delete')" @click.stop="onRequestForDelete"> </i>
         <div class="deletion-confirm-wrap">
-          <div
-            class="deletion-confirm"
-            :class="isConfirmingDeletion ? 'show' : 'hide'"
-            v-clickoutside="onRequestForCancelDelete"
-            @click.stop="onConfirmDelete"
-          >
+          <div class="deletion-confirm" :class="isConfirmingDeletion ? 'show' : 'hide'" v-clickoutside="onRequestForCancelDelete" @click.stop="onConfirmDelete">
             {{ $i18n.t("navigation.delete") }}
           </div>
         </div>
@@ -97,23 +71,8 @@
     </div>
 
     <div class="group-content" v-if="isExpanded">
-      <template
-        v-if="
-          !(
-            groupNode.children.length === 1 &&
-            (groupNode.children[0].name === '默认二级分组' ||
-              groupNode.children[0].name ===
-                $i18n.t('navigation.default_group_two'))
-          )
-        "
-      >
-        <InsertPositionTip
-          position-debug="1"
-          :indentLevel="level + 1"
-          :topologyLevel="level + 1"
-          :parentNode="groupNode"
-          :index="0"
-        ></InsertPositionTip>
+      <template v-if="!(groupNode.children.length === 1 && (groupNode.children[0].name === '默认二级分组' || groupNode.children[0].name === $i18n.t('navigation.default_group_two')))">
+        <InsertPositionTip position-debug="1" :indentLevel="level + 1" :topologyLevel="level + 1" :parentNode="groupNode" :index="0"></InsertPositionTip>
         <div v-for="(item, index) of groupNode.children" :key="item.id">
           <component
             :is="'SceneGroup'"
@@ -135,28 +94,14 @@
             @rename="onRenameScene"
             @delete="onDeleteScene"
           />
-          <InsertPositionTip
-            position-debug="2"
-            :indentLevel="level + 1"
-            :topologyLevel="level + 1"
-            :parentNode="groupNode"
-            :index="index + 1"
-          ></InsertPositionTip>
+          
+          <InsertPositionTip position-debug="2" :indentLevel="level + 1" :topologyLevel="level + 1" :parentNode="groupNode" :index="index + 1"></InsertPositionTip>
         </div>
       </template>
       <template v-else>
         <!-- 自动生成的默认二级分组不显示,里边的内容显示成直属于一级分组的效果。 -->
-        <InsertPositionTip
-          position-debug="3"
-          :indentLevel="level + 1"
-          :topologyLevel="level + 2"
-          :parentNode="groupNode.children[0]"
-          :index="0"
-        ></InsertPositionTip>
-        <div
-          v-for="(item, index) of groupNode.children[0].children"
-          :key="item.id"
-        >
+        <InsertPositionTip position-debug="3" :indentLevel="level + 1" :topologyLevel="level + 2" :parentNode="groupNode.children[0]" :index="0"></InsertPositionTip>
+        <div v-for="(item, index) of groupNode.children[0].children" :key="item.id">
           <SceneInGroup
             :style="{
               paddingLeft: sceneItemPaddingLeft,
@@ -165,13 +110,7 @@
             @rename="onRenameScene"
             @delete="onDeleteScene"
           />
-          <InsertPositionTip
-            position-debug="4"
-            :indentLevel="level + 1"
-            :topologyLevel="level + 2"
-            :parentNode="groupNode.children[0]"
-            :index="index + 1"
-          ></InsertPositionTip>
+          <InsertPositionTip position-debug="4" :indentLevel="level + 1" :topologyLevel="level + 2" :parentNode="groupNode.children[0]" :index="index + 1"></InsertPositionTip>
         </div>
       </template>
     </div>
@@ -221,6 +160,7 @@ export default {
       isConfirmingDeletion: false,
       isShowSelectionWindow: false,
       dragEnterTimerId: null,
+      insertTag: null,
     };
   },
   computed: {
@@ -242,13 +182,13 @@ export default {
     },
     groupLv1name() {
       return (name) => {
-        return name && name.length
-          ? name
-          : this.$i18n.t("navigation.group_one");
+        return name && name.length ? name : this.$i18n.t("navigation.group_one");
       };
     },
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      info: "base/baseInfo",
+      sceneList: "base/sceneList",
       dragInfo: "editorNavDragInfo",
     }),
     topBarPaddingLeft() {
@@ -265,6 +205,7 @@ export default {
       clearDragInfo: "clearEditorNavDragInfo",
     }),
     onClickTopBar() {
+      console.error(1231232)
       if (this.isConfirmingDeletion) {
         return;
       }
@@ -281,7 +222,9 @@ export default {
     onRequestForAddGroup() {
       this.$emit("addGroup", this.groupNode.id);
     },
-    onRequestForAddScene() {
+    onRequestForAddScene(item) {
+      this.insertTag = item;
+
       this.isShowSelectionWindow = true;
     },
     onClickForRename() {
@@ -342,50 +285,53 @@ export default {
       this.$emit("deleteGroup", ...params);
     },
     onSubmitFromMaterialSelector(selected) {
+      console.error(this.insertTag.level);
+      let changeListLength = this.insertTag.children.length || 0;
       let newScenes = [];
       for (const item of selected) {
         if (item.materialType === "pano") {
           newScenes.push({
+            fodderId: item.id,
             icon: item.icon,
             sceneCode: item.sceneCode,
-            sceneTitle: item.name,
-            category:
-              this.level === 1
-                ? this.groupNode.children[0].id
-                : this.groupNode.id,
+            name: item.name,
+            // category: this.level === 1 ? this.groupNode.children[0].id : this.groupNode.id,
             type: "pano",
-            id: "s_" + this.$randomWord(true, 8, 8),
+            sort: changeListLength,
+            parentId: null,
+            level: this.insertTag.level + 1,
+            // id: "s_" + this.$randomWord(true, 8, 8),
           });
         } else if (item.materialType === "3D") {
           console.log("item.num", item.num);
           newScenes.push({
             icon: item.thumb,
             sceneCode: item.num,
-            sceneTitle: item.sceneName,
-            category:
-              this.level === 1
-                ? this.groupNode.children[0].id
-                : this.groupNode.id,
+            name: item.sceneName,
+            sort: changeListLength,
+            // category: this.level === 1 ? this.groupNode.children[0].id : this.groupNode.id,
             type: "4dkk",
+            level: this.insertTag.level + 1,
             version: isUpgradeAdapter(item.isUpgrade), // 'V3' OR 'V4'. 全景看看v1.3新增
-            id: "s_" + this.$randomWord(true, 8, 8),
+            // id: "s_" + this.$randomWord(true, 8, 8),
+            // fodderId: item.id,
           });
         }
+        changeListLength++;
       }
+      console.error(newScenes);
+      console.error(this.sceneList);
 
       let allSuccess = true;
       newScenes.forEach((item, i) => {
-        let temp = this.info.scenes.find((eachScene) => {
+        // let temp = this.info.scenes.find((eachScene) => {
+        let temp = this.sceneList.find((eachScene) => {
           return eachScene.sceneCode === item.sceneCode;
         });
         if (temp) {
           setTimeout(() => {
             this.$msg.message(
-              `${
-                item.type == "4dkk"
-                  ? this.$i18n.t("navigation.scene_name")
-                  : this.$i18n.t("navigation.pano")
-              }${this.$i18n.t("navigation.already_exists", {
+              `${item.type == "4dkk" ? this.$i18n.t("navigation.scene_name") : this.$i18n.t("navigation.pano")}${this.$i18n.t("navigation.already_exists", {
                 msg: item.sceneTitle,
               })}`
             );
@@ -393,7 +339,8 @@ export default {
           allSuccess = false;
           return;
         }
-        this.info.scenes.push(item);
+        // this.info.scenes.push(item);
+        this.insertTag.children.push(item);
       });
 
       this.isShowSelectionWindow = false;
@@ -410,10 +357,7 @@ export default {
       e.dataTransfer.setDragImage(this.$refs["drag-image"], -10, -18);
     },
     onDragEnter(e) {
-      if (
-        e.target.contains(e.relatedTarget) ||
-        (this.level === 2 && this.dragInfo.type.includes("Group"))
-      ) {
+      if (e.target.contains(e.relatedTarget) || (this.level === 2 && this.dragInfo.type.includes("Group"))) {
         return;
       }
       this.dragEnterTimerId = setTimeout(() => {

+ 11 - 48
packages/qjkankan-editor/src/components/sceneInGroupInEditor.vue

@@ -1,32 +1,11 @@
 <template>
-  <div
-    class="scene-item"
-    :class="isConfirmingDeletion ? '' : 'not-confirming-deletion'"
-    @dragstart="onDragStart"
-    @dragend="clearDragInfo"
-    draggable="true"
-  >
+  <div class="scene-item" :class="isConfirmingDeletion ? '' : 'not-confirming-deletion'" @dragstart="onDragStart" @dragend="clearDragInfo" draggable="true">
     <div class="drag-image" ref="drag-image">
-      <img
-        :src="sceneInfo.icon + ossImagePreviewUrlSuffix()"
-        alt=""
-        class="scene-image"
-        draggable="false"
-      />
+      <img :src="sceneInfo.icon + ossImagePreviewUrlSuffix()" alt="" class="scene-image" draggable="false" />
     </div>
-    <img
-      :src="sceneInfo.icon + ossImagePreviewUrlSuffix()"
-      alt=""
-      class="scene-image"
-      draggable="false"
-    />
+    <img :src="sceneInfo.icon + ossImagePreviewUrlSuffix()" alt="" class="scene-image" draggable="false" />
     <div class="right">
-      <span
-        v-if="!isRenaming"
-        class="scene-title"
-        v-title="sceneInfo.sceneTitle"
-        >{{ sceneInfo.sceneTitle }}</span
-      >
+      <span v-if="!isRenaming" class="scene-title" v-title="sceneInfo.name">{{ sceneInfo.name }}</span>
       <input
         v-if="isRenaming"
         class="scene-title-input"
@@ -40,28 +19,13 @@
       <div class="right-bottom">
         <span class="scene-type">{{ translateSceneType(sceneInfo.type) }}</span>
         <div class="icons">
-          <i
-            class="iconfont icon-editor_list_edit icon-edit"
-            v-tooltip="$i18n.t('navigation.rename')"
-            @click="onRequestForRename"
-          >
-          </i>
-          <i
-            class="iconfont icon-editor_list_delete icon-delete"
-            v-tooltip="$i18n.t('navigation.delete')"
-            @click="onRequestForDelete"
-          >
-          </i>
+          <i class="iconfont icon-editor_list_edit icon-edit" v-tooltip="$i18n.t('navigation.rename')" @click="onRequestForRename"> </i>
+          <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="$i18n.t('navigation.delete')" @click="onRequestForDelete"> </i>
         </div>
       </div>
     </div>
     <div class="deletion-confirm-wrap">
-      <div
-        class="deletion-confirm"
-        :class="isConfirmingDeletion ? 'show' : 'hide'"
-        v-clickoutside="onRequestForCancelDelete"
-        @click="onConfirmDelete"
-      >
+      <div class="deletion-confirm" :class="isConfirmingDeletion ? 'show' : 'hide'" v-clickoutside="onRequestForCancelDelete" @click="onConfirmDelete">
         {{ $i18n.t("navigation.delete") }}
       </div>
     </div>
@@ -97,6 +61,8 @@ export default {
       clearDragInfo: "clearEditorNavDragInfo",
     }),
     onDragStart(e) {
+      console.error(this.sceneInfo);
+
       this.recordDragType("scene");
       this.recordDragNode(this.sceneInfo);
       e.dataTransfer.setDragImage(this.$refs["drag-image"], -10, -18);
@@ -111,17 +77,14 @@ export default {
     },
     onRequestForRename() {
       this.isRenaming = true;
-      this.newName = this.sceneInfo.sceneTitle;
+      this.newName = this.sceneInfo.name;
       this.$nextTick(() => {
         this.$refs["input-for-rename"].focus();
       });
     },
     onInputNewNameComplete() {
       this.isRenaming = false;
-      if (
-        String(this.newName).trim().length === 0 ||
-        String(this.newName).trim().length > 50
-      ) {
+      if (String(this.newName).trim().length === 0 || String(this.newName).trim().length > 50) {
         this.$msg.warning(this.$i18n.t("hotspot.title_placeholder"));
         return;
       }

+ 10 - 13
packages/qjkankan-editor/src/core/hotspot.js

@@ -44,14 +44,17 @@ const convertBaseStyle = (dest, origin) => {
 
     if ("visible" in origin && typeof origin.visible == "boolean") {
       dest.visible = origin.visible ? 0 : 1;
-      origin.titleDisplayMode = origin.visible ? "always" : "never";
+      // origin.titleDisplayMode = origin.visible ? "always" : "never";
+      origin.titleDisplayMode = origin.visible ? 1 : 0;
       delete origin.visible;
       dest.style.position = "top";
     }
-    if (origin && origin.titleDisplayMode == "always") {
+    // if (origin && origin.titleDisplayMode == "always") {
+    if (origin && origin.titleDisplayMode == 1) {
       dest.visible = 0;
     }
-    if (origin && origin.titleDisplayMode == "never") {
+    // if (origin && origin.titleDisplayMode == "never") {
+    if (origin && origin.titleDisplayMode == 0) {
       dest.visible = 1;
     }
     if (origin && origin.titleDisplayMode == "hover") {
@@ -96,9 +99,7 @@ const coverSystemIconPart = (origin) => {
 };
 
 const coverImageconPart = (origin) => {
-  const defaultImage = config.getStaticResource(
-    "/panoassets/images/hotspot/image_place_holder.png"
-  );
+  const defaultImage = config.getStaticResource("/panoassets/images/hotspot/image_place_holder.png");
   const duplicate = structuredClone(initState);
   duplicate.id = origin.name;
   duplicate.title = origin.hotspotTitle;
@@ -113,9 +114,7 @@ const coverImageconPart = (origin) => {
 };
 const coverSerialFrame = (origin) => {
   const duplicate = structuredClone(initState);
-  const defaultImage = config.getStaticResource(
-    "/panoassets/images/hotspot/image_place_holder.png"
-  );
+  const defaultImage = config.getStaticResource("/panoassets/images/hotspot/image_place_holder.png");
   // console.log('defaultImage', defaultImage);
   duplicate.id = origin.name;
   duplicate.title = origin.hotspotTitle;
@@ -123,10 +122,7 @@ const coverSerialFrame = (origin) => {
   duplicate.atv = origin.atv;
   duplicate.type = 2;
 
-  duplicate.icon =
-    origin.serialFrameInfo.img +
-      "?x-oss-process=image/resize,w_128,q_80" ||
-    defaultImage;
+  duplicate.icon = origin.serialFrameInfo.img + "?x-oss-process=image/resize,w_128,q_80" || defaultImage;
   duplicate.link = origin.link || "";
   duplicate.size = origin.size;
   convertBaseStyle(duplicate, origin);
@@ -148,6 +144,7 @@ const coverpersonalizedTag = (origin) => {
 
 export const convertJQHotspot = (origin) => {
   const type = origin.hotspotIconType;
+  
   switch (type) {
     case "system_icon":
       return coverSystemIconPart(origin);

+ 18 - 19
packages/qjkankan-editor/src/core/utils.js

@@ -98,9 +98,8 @@ export default class Utils {
     try {
       krpano.set("curscreen_x", krpano.get("stagewidth") / 2);
       krpano.set("curscreen_y", krpano.get("stageheight") / 2);
-      krpano.call(
-        "screentosphere(curscreen_x, curscreen_y, curscreen_ath, curscreen_atv);"
-      );
+      krpano.call("screentosphere(curscreen_x, curscreen_y, curscreen_ath, curscreen_atv);");
+
       const hotspot = convertJQHotspot(param);
       const hotspotStyle = Object.values(hotspot.style);
       const hotspotString = hotspotStyle.join("|");
@@ -110,17 +109,17 @@ export default class Utils {
       let icon = hotspot.icon.replace(/,/g, "|");
       let title = this.htmlEncode(hotspot.title);
       const callString = `addJQHotspot(
-        ${hotspot.id},
-        ${hotspot.type},
-        ${title},
-        "${icon}",
-        ${ath},
-        ${atv},
-        "${hotspot.link}",
-        ${hotspotSize},
-        ${hotspot.visible},
-        "${hotspotString}"
-        )`;
+          ${hotspot.id},
+          ${hotspot.type},
+          ${title},
+          "${icon}",
+          ${ath},
+          ${atv},
+          "${hotspot.link}",
+          ${hotspotSize},
+          ${hotspot.visible},
+          "${hotspotString}"
+          )`;
       krpano.call(callString);
     } catch (error) {
       console.error("error", error);
@@ -197,10 +196,7 @@ export default class Utils {
     } else if (window.location.pathname.indexOf("edit") > -1) {
       vue.$bus.emit("openHotspot", id);
     } else {
-      window.parent.postMessage(
-        { event: "hotspot", targetCode: sceneCode },
-        "*"
-      );
+      window.parent.postMessage({ event: "hotspot", targetCode: sceneCode }, "*");
     }
   }
 
@@ -217,7 +213,10 @@ export default class Utils {
       mysd = JSON.parse(someData);
     }
 
-    mysd.hotspots.forEach((item) => {
+    // mysd.hotspots.forEach((item) => {
+    //   this.addhotspot(krpano, item, type);
+    // });
+    mysd.forEach((item) => {
       this.addhotspot(krpano, item, type);
     });
   }

+ 399 - 208
packages/qjkankan-editor/src/framework/EditorHead.vue

@@ -1,42 +1,24 @@
 <template>
-  <header class="app-head" app-border dir-bottom>
-    <a
-      class="app-head-back"
-      :href="`./material.html?lang=${$lang}&from=${routerForm}#/works`"
-    >
+  <header class="app-head" app-border dir-bottom v-if="info">
+    <a class="app-head-back" :href="`./material.html?lang=${$lang}&from=${routerForm}#/works`">
       <i class="iconfont icon-editor_return"></i>
       {{ back_myworks }}
     </a>
-    <span class="app-head-title">{{ info.name }}</span>
-    <div
-      class="app-head-save ui-button deepcancel app-head-view"
-      @click="onView"
-      :class="{ disable: !canLoad || isEditing }"
-    >
+
+    <span class="app-head-title">{{ info.work.name }}</span>
+    <div class="app-head-save ui-button deepcancel app-head-view" @click="onView" :class="{ disable: !canLoad || isEditing }">
       <i class="iconfont icon-editor_play"></i>
       {{ preview }}
     </div>
-
-    <div
-      class="ui-button submit app-head-save"
-      @click="onSave"
-      :class="{ disable: !canLoad || isEditing }"
-    >
+    <div class="ui-button submit app-head-save" @click="onSave" :class="{ disable: !canLoad || isEditing }">
       <i class="iconfont icon-editor_save"></i>
       {{ savetips }}
     </div>
-    <preview
-      v-if="info"
-      :key="Math.random()"
-      :name="info.name"
-      :show="showPreview"
-      :ifr="`./show.html?id=${info.id}&lang=${$lang}&rnd=${Math.random()}`"
-      @close="showPreview = false"
-    />
+    <preview v-if="info" :key="Math.random()" :name="info.work.name" :show="showPreview" :ifr="`./show.html?id=${info.work.id}&lang=${$lang}&rnd=${Math.random()}`" @close="showPreview = false" />
   </header>
 </template>
 <script>
-import { saveWorks, getPanoInfo, checkLogin } from "@/api";
+import { saveWorks, getPanoInfo, checkLogin, getWorkInfo } from "@/api";
 import { mapGetters } from "vuex";
 // import config from '@/config'
 import preview from "@/components/preview";
@@ -56,32 +38,128 @@ export default {
 
       showPreview: false,
       canLoad: false,
+      hiddenSave: false,
     };
   },
 
   components: { preview },
-
+  watch: {
+    // "$route.name": {
+    //   immediate: true,
+    //   handler: function (newVal) {
+    //     if (newVal == "hotspot") {
+    //       // this.hiddenSave = true;
+    //     } else {
+    //       // this.hiddenSave = false;
+    //     }
+    //   },
+    // },
+    sceneList: {
+      handler: function (newVal, oldVal) {
+        if (newVal.length != oldVal.length) {
+          // console.error("sceneList change");
+          let currentScene = newVal.find((item) => item.id == this.currentScene.id);
+          if (!currentScene) {
+            //如果找不到当前选择的场景,则情况。重新遍历查找
+            this.$store.commit("scene/setCurrentScene", null);
+          }
+        }
+      },
+      deep: true,
+    },
+  },
   mounted() {
+    this.getSettingsInfo();
     this.$bus.on("canLoad", (data) => {
+      // console.error("canLoad", data);
       this.canLoad = data;
-      if (data) {
-        this.getInfo().then((res) => {
-          // getInfo里调用了后端接口,底层用了jquery的网络请求方法,为啥会导致promise嵌套没有展平,res拿到的不是promise 对象的resolve值而是promise对象本身????
-          res.then(() => {
-            this.$store.commit("TakeInfoSnapShotAtSave");
-          });
-        });
-      }
+      // if (data) {
+      // this.getInfo().then((res) => {
+      //   // getInfo里调用了后端接口,底层用了jquery的网络请求方法,为啥会导致promise嵌套没有展平,res拿到的不是promise 对象的resolve值而是promise对象本身????
+      //   res.then(() => {
+      //     // alert(1);
+      //     this.$store.commit("TakeInfoSnapShotAtSave");
+      //   });
+      // });
+      // }
     });
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      baseInfo: "base/baseInfo",
+      sceneList: "base/sceneList",
       isShow: "isShow",
       catalogTopology: "catalogTopology",
       currentScene: "scene/currentScene",
       isEditing: "isEditing",
+      saveApiList: "scene/saveApiList",
+      currentRootId: "navigation/currentRootId",
+      currentSecondId: "navigation/currentSecondId",
     }),
+
+    info() {
+      if (this.baseInfo) {
+        this.$store.commit("base/initDefaultData");
+        //初始化
+
+        if (!this.currentScene) {
+          console.error("初始化");
+          let firstScene = null;
+          if (this.baseInfo.firstScene) {
+            firstScene = this.baseInfo.firstScene;
+          } else {
+            // firstScene = this.baseInfo.navigationTrees[0].children[0];
+            firstScene = this.sceneList[0];
+          }
+          this.$store.commit("scene/setCurrentScene", firstScene);
+          let activeScene = null;
+          this.baseInfo.navigationTrees.forEach((item, index) => {
+            activeScene = item.children.find((pano) => pano.id == firstScene.id);
+
+            if (activeScene) {
+              this.$store.commit("scene/setCurrentScenesList", item.children);
+              this.$store.commit("navigation/setData", { currentSecondId: null, currentRootId: item.id });
+              // throw new Error("LoopTerminated");
+            }
+            item = item.children.forEach((s_item, s_index) => {
+              activeScene = s_item.children.find((pano) => pano.id == firstScene.id);
+
+              if (activeScene) {
+                this.$store.commit("scene/setCurrentScenesList", s_item.children);
+                this.$store.commit("navigation/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+                // throw new Error("LoopTerminated");
+              }
+              s_item = s_item.children.forEach((t_item, t_index) => {
+                activeScene = t_item.children.find((pano) => pano.id == firstScene.id);
+
+                if (activeScene) {
+                  this.$store.commit("scene/setCurrentScenesList", s_item.children);
+                  this.$store.commit("navigation/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+                  // throw new Error("LoopTerminated");
+                }
+              });
+            });
+          });
+        }
+        console.error(this.currentSecondId);
+        // if (data.firstScene) {
+        //   firstScene = data.scenes.find((item) => item.sceneCode == data.firstScene.sceneCode);
+        // }
+
+        // //判断列表里是否有全景图
+        // if (data.scenes.some((item) => item.type == "pano") && firstScene && firstScene.type == "4dkk") {
+        //   console.log(this.currentScene.sceneCode);
+        //   //如果当前有选中场景,则激活选中场景,无则显示第一个全景图
+        //   firstScene = this.currentScene.sceneCode ? this.currentScene : data.scenes.find((item) => item.type == "pano");
+        // }
+
+        // console.log(firstScene, "firstScene");
+        // this.$store.commit("scene/setCurrentScene", firstScene || data.scenes[0]);
+      }
+      return this.baseInfo;
+    },
+
     routerForm() {
       const from = browser.urlQueryValue("from");
       return from;
@@ -89,12 +167,8 @@ export default {
   },
   methods: {
     checkParams() {
-      if (
-        !this.info.name &&
-        !this.info.icon &&
-        !this.info.description &&
-        this.info.scenes.length <= 0
-      ) {
+      // if (!this.info.work.name && !this.info.work.icon && !this.info.work.description && this.info.scenes.length <= 0) {
+      if (!this.baseInfo.work.name && !this.baseInfo.work.icon && !this.baseInfo.work.description) {
         this.$alert({
           content: this.$i18n.t("gather.nothing_edit"),
           ok: () => {
@@ -111,113 +185,173 @@ export default {
       }
       const isFixData = this.fixData();
       if (isFixData) {
-        this.info.scenes = this.info.scenes.map((item) => {
-          if (typeof item.someData == "string") {
-            item.someData = item.someData.replace(hhhreg, "");
-          }
-          return item;
-        });
+        // this.info.scenes = this.info.scenes.map((item) => {
+        //   if (typeof item.someData == "string") {
+        //     item.someData = item.someData.replace(hhhreg, "");
+        //   }
+        //   return item;
+        // });
         $waiting.show();
 
-        saveWorks(
-          {
-            password: this.info.password,
-            someData: { ...this.info, status: 1 },
-          },
-          () => {
-            this.$msg.success(this.$i18n.t("gather.save_done"));
-            // document.title = this.info.name;
-            document.title = this.info.name || this.$i18n.t("gather.no_title");
-            // this.getInfo().then((res) => {
-            //   // getInfo里调用了后端接口,底层用了jquery的网络请求方法,为啥会导致promise嵌套没有展平,res拿到的不是promise 对象的resolve值而是promise对象本身????
-            //   res.then(() => {
-            //     this.$store.commit("UpdateIsShowState", true);
-            //     $waiting.hide()
-            //     setTimeout(() => {
-            //       if (this.info.scenes.length <= 0 && this.isShow) {
-            //         return this.$alert({
-            //           content: this.$i18n.t('gather.at_least_one_scene'),
-            //         });
-            //       }
-            //       this.showPreview = true;
-            //     }, 500);
-            //   })
-            // });
+        let promises = [];
+        if (this.saveApiList.length) {
+          let idx = this.saveApiList.find((item) => item == "navigation");
+          console.error(idx);
+          let length = 0;
+          if (idx) {
+            //优先保存导航树
+            this.$store.dispatch(`navigation/save`).then((res) => {
+              this.saveApiList.forEach((item) => {
+                if (item != "navigation") {
+                  promises.push(this.$store.dispatch(`${item}/save`));
+                }
+              });
+              let currentRouter = this.saveApiList.find((item) => item == this.$route.name);
+              if (!currentRouter) {
+                promises.push(this.$store.dispatch(`${this.$route.name}/save`));
+              }
+              Promise.all(promises)
+                .then((res) => {
+                  console.log("Promise.all success", res);
+                  this.$store.commit(`scene/removeSaveApi`);
+                  this.$msg.success(i18n.t("gather.save_done"));
+                  //成功后查看
+                  $waiting.hide();
+                  if (this.sceneList <= 0 && this.isShow) {
+                    return this.$alert({
+                      content: this.$i18n.t("gather.at_least_one_scene"),
+                    });
+                  }
+                  this.showPreview = true;
+                })
+                .catch((err) => {
+                  console.error("Promise.all failed", err);
+                });
+            });
+          } else {
+            this.saveApiList.forEach((item) => {
+              promises.push(this.$store.dispatch(`${item}/save`));
+            });
+            let currentRouter = this.saveApiList.find((item) => item == this.$route.name);
+            if (!currentRouter) {
+              promises.push(this.$store.dispatch(`${this.$route.name}/save`));
+            }
+            Promise.all(promises)
+              .then((res) => {
+                console.log("Promise.all success", res);
+                this.$store.commit(`scene/removeSaveApi`);
+                this.$msg.success(i18n.t("gather.save_done"));
+                //成功后查看
+                $waiting.hide();
+                if (this.sceneList <= 0 && this.isShow) {
+                  return this.$alert({
+                    content: this.$i18n.t("gather.at_least_one_scene"),
+                  });
+                }
+                this.showPreview = true;
+              })
+              .catch((err) => {
+                console.error("Promise.all failed", err);
+              });
+          }
+        } else {
+          this.$store.dispatch(`${this.$route.name}/save`).then((res) => {
+            this.$store.commit(`scene/removeSaveApi`);
+            this.$msg.success(i18n.t("gather.save_done"));
+            //成功后查看
             $waiting.hide();
-            if (this.info.scenes.length <= 0 && this.isShow) {
+            if (this.sceneList <= 0 && this.isShow) {
               return this.$alert({
                 content: this.$i18n.t("gather.at_least_one_scene"),
               });
             }
             this.showPreview = true;
-          }
-        );
+          });
+        }
+
+        // saveWorks(
+        //   {
+        //     password: this.info.work.password,
+        //     someData: { ...this.info, status: 1 },
+        //   },
+        //   () => {
+        //     this.$msg.success(this.$i18n.t("gather.save_done"));
+        //     // document.title = this.info.name;
+        //     document.title = this.info.work.name || this.$i18n.t("gather.no_title");
+        //     // this.getInfo().then((res) => {
+        //     //   // getInfo里调用了后端接口,底层用了jquery的网络请求方法,为啥会导致promise嵌套没有展平,res拿到的不是promise 对象的resolve值而是promise对象本身????
+        //     //   res.then(() => {
+        //     //     this.$store.commit("UpdateIsShowState", true);
+        //     //     $waiting.hide()
+        //     //     setTimeout(() => {
+        //     //       if (this.info.scenes.length <= 0 && this.isShow) {
+        //     //         return this.$alert({
+        //     //           content: this.$i18n.t('gather.at_least_one_scene'),
+        //     //         });
+        //     //       }
+        //     //       this.showPreview = true;
+        //     //     }, 500);
+        //     //   })
+        //     // });
+        //     $waiting.hide();
+        //     if (this.info.scenes.length <= 0 && this.isShow) {
+        //       return this.$alert({
+        //         content: this.$i18n.t("gather.at_least_one_scene"),
+        //       });
+        //     }
+        //     this.showPreview = true;
+        //   }
+        // );
       }
     },
 
     fixData() {
       // 如果没有设置作品封面,拿第一个一级分组内第一个二级分组内第一个场景作为作品封面。
-      if (!this.info.icon) {
-        this.info.icon = this.catalogTopology[0].children[0].children[0].icon;
+      if (!this.baseInfo.work.icon) {
+        // this.info.work.icon = this.catalogTopology[0].children[0].children[0].icon;
+        this.baseInfo.work.icon = this.baseInfo.navigationTrees[0].children[0].icon;
       }
 
-      if (this.info.firstScene) {
-        this.info.firstScene = this.info.scenes.find(
-          (item) => item.sceneCode == this.info.firstScene.sceneCode
-        );
+      if (this.baseInfo.firstScene) {
+        this.baseInfo.firstScene = this.sceneList.find((item) => item.sceneCode == this.baseInfo.firstScene.sceneCode);
       }
-      const scenes = this.info.scenes;
+      const scenes = this.sceneList;
       // debugger
-      if (scenes && scenes.length > 0) {
-        scenes.forEach((scene, index) => {
-          if (
-            scene.someData &&
-            scene.someData.hotspots &&
-            scene.someData.hotspots.length > 0
-          ) {
-            this.lockHotspotResourceTree(index, scene.someData.hotspots);
-          }
-        });
+      // if (scenes && scenes.length > 0) {
+      //   scenes.forEach((scene, index) => {
+      //     if (scene.someData && scene.someData.hotspots && scene.someData.hotspots.length > 0) {
+      //       this.lockHotspotResourceTree(index, scene.someData.hotspots);
+      //     }
+      //   });
+      // }
+      if (this.baseInfo && this.baseInfo.workHotList && this.baseInfo.workHotList.length > 0) {
+        this.lockHotspotResourceTree(this.baseInfo.workHotList);
       }
+
       //开场校验
-      if (this.info.coverInfo.isShowCover === 1) {
-        if (this.info.coverInfo.coverSelect === "videoAndImg") {
-          if (
-            !this.info.coverInfo.coverPc ||
-            !this.info.coverInfo.coverMo ||
-            !this.info.coverInfo.videoPc ||
-            !this.info.coverInfo.videoMo
-          ) {
+      if (this.baseInfo?.workCoverType?.isShowCover === 1) {
+        if (this.baseInfo.workCoverType.coverSelect === "videoAndImg") {
+          if (!this.baseInfo.workCoverType.coverPc || !this.baseInfo.workCoverType.coverMo || !this.baseInfo.workCoverType.videoPc || !this.baseInfo.workCoverType.videoMo) {
             this.$msg.warning(this.$i18n.t("gather.converinfo_no_valid"));
             return false;
           }
         }
-        if (this.info.coverInfo.coverSelect === "video") {
-          if (
-            !this.info.coverInfo.videoPc ||
-            !this.info.coverInfo.videoMo ||
-            this.info.coverInfo.videoPc == "" ||
-            this.info.coverInfo.videoMo == ""
-          ) {
+        if (this.baseInfo.workCoverType.coverSelect === "video") {
+          if (!this.baseInfo.workCoverType.videoPc || !this.baseInfo.workCoverType.videoMo || this.baseInfo.workCoverType.videoPc == "" || this.baseInfo.workCoverType.videoMo == "") {
             this.$msg.warning(this.$i18n.t("gather.converinfo_no_valid"));
             return false;
           }
         }
-        if (this.info.coverInfo.coverSelect === "img") {
-          if (
-            !this.info.coverInfo.coverPc ||
-            !this.info.coverInfo.coverMo ||
-            this.info.coverInfo.coverPc == "" ||
-            this.info.coverInfo.coverMo == ""
-          ) {
+        if (this.baseInfo.workCoverType.coverSelect === "img") {
+          if (!this.baseInfo.workCoverType.coverPc || !this.baseInfo.workCoverType.coverMo || this.baseInfo.workCoverType.coverPc == "" || this.baseInfo.workCoverType.coverMo == "") {
             this.$msg.warning(this.$i18n.t("gather.converinfo_no_valid"));
             return false;
           }
         }
 
         //新增不上传背景底图限制
-        if (this.info.coverInfo.coverImgBac === "imgTile") {
-          if (!this.info.coverInfo.coverBac) {
+        if (this.baseInfo.workCoverType.coverImgBac === "imgTile") {
+          if (!this.baseInfo.workCoverType.coverBac) {
             this.$msg.warning(this.$i18n.t("gather.converinfo_no_valid"));
             return false;
           }
@@ -230,34 +364,40 @@ export default {
     /**
      * 给热点的资源引用上锁
      */
-    lockHotspotResourceTree(sceneIndex, hotspots) {
+    // lockHotspotResourceTree(sceneIndex, hotspots) {
+    lockHotspotResourceTree(hotspots) {
       hotspots.forEach((item, index) => {
-        const target = this.info.scenes[sceneIndex].someData.hotspots[index];
+        // const target = this.info.scenes[sceneIndex].someData.hotspots[index];
         let fodderId = [];
-
         switch (item.hotspotType) {
           case "image":
-            fodderId = target.image
-              .reduce((p, c) => p.concat(c["id"]), [])
-              .map((i) => i);
-            target.fodderId = fodderId;
+            // fodderId = target.image.reduce((p, c) => p.concat(c["id"]), []).map((i) => i);
+            // target.fodderId = fodderId;
+            fodderId = item.image.reduce((p, c) => p.concat(c["id"]), []).map((i) => i);
+            item.fodderId = fodderId;
             break;
           case "audio":
-            target.fodderId = [target.audio.id].map((i) => i);
+            // target.fodderId = [target.audio.id].map((i) => i);
+            item.fodderId = [item.audio.id].map((i) => i);
             break;
           case "imageText":
-            fodderId = target.imageTextInfo.imageList
-              .reduce((p, c) => p.concat(c["id"]), [])
-              .map((i) => i);
-            target.imageTextInfo.audio.id &&
-              fodderId.push(target.imageTextInfo.audio.id);
-            target.fodderId = fodderId;
+            // fodderId = target.imageTextInfo.imageList.reduce((p, c) => p.concat(c["id"]), []).map((i) => i);
+            // target.imageTextInfo.audio.id && fodderId.push(target.imageTextInfo.audio.id);
+            // target.fodderId = fodderId;
+            fodderId = item.imageTextInfo.imageList.reduce((p, c) => p.concat(c["id"]), []).map((i) => i);
+            item.imageTextInfo.audio.id && fodderId.push(item.imageTextInfo.audio.id);
+            item.fodderId = fodderId;
             break;
           case "video":
-            target.fodderId = [target.video.id];
+            // target.fodderId = [target.video.id];
+            item.fodderId = [item.video.id];
+            break;
+          case "scene":
+            item.fodderId = [item.scene.fodderId].map((i) => i);
             break;
           default:
-            target.fodderId = [];
+            // target.fodderId = [];
+            item.fodderId = [];
             break;
         }
       });
@@ -269,30 +409,86 @@ export default {
       }
       const isFixData = this.fixData();
       if (isFixData) {
-        this.info.scenes = this.info.scenes.map((item) => {
-          if (typeof item.someData == "string") {
-            item.someData = item.someData.replace(hhhreg, "");
-          }
-          return item;
-        });
+        // this.sceneList = this.sceneList.map((item) => {
+        //   if (typeof item.someData == "string") {
+        //     item.someData = item.someData.replace(hhhreg, "");
+        //   }
+        //   return item;
+        // });
         $waiting.show();
 
-        saveWorks(
-          {
-            password: this.info.password,
-            someData: { ...this.info, status: 1 },
-          },
-          () => {
-            this.$msg.success(this.$i18n.t("gather.save_done"));
-            // document.title = this.info.name;
-            document.title = this.info.name || this.$i18n.t("gather.no_title");
-            $waiting.hide();
-            // this.getInfo();
-            // this.$store.commit("UpdateIsShowState", true);
-            this.$store.commit("TakeInfoSnapShotAtSave");
-          },
-          () => {}
-        );
+        // console.error(this.$router.name)
+        let promises = [];
+        if (this.saveApiList.length) {
+          let idx = this.saveApiList.find((item) => item == "navigation");
+          console.error(idx);
+          let length = 0;
+          if (idx) {
+            //优先保存导航树
+            this.$store.dispatch(`navigation/save`).then((res) => {
+              this.saveApiList.forEach((item) => {
+                if (item != "navigation") {
+                  promises.push(this.$store.dispatch(`${item}/save`));
+                  // this.$store.dispatch(`${item}/save`).then((res) => {});
+                }
+              });
+              let currentRouter = this.saveApiList.find((item) => item == this.$route.name);
+              if (!currentRouter) {
+                promises.push(this.$store.dispatch(`${this.$route.name}/save`));
+              }
+              Promise.all(promises)
+                .then((res) => {
+                  console.log("Promise.all success", res);
+                  this.$store.commit(`scene/removeSaveApi`);
+                  this.$msg.success(i18n.t("gather.save_done"));
+                })
+                .catch((err) => {
+                  console.error("Promise.all failed", err);
+                });
+            });
+          } else {
+            this.saveApiList.forEach((item) => {
+              promises.push(this.$store.dispatch(`${item}/save`));
+              // this.$store.dispatch(`${item}/save`).then((res) => {});
+            });
+            let currentRouter = this.saveApiList.find((item) => item == this.$route.name);
+            if (!currentRouter) {
+              promises.push(this.$store.dispatch(`${this.$route.name}/save`));
+            }
+            Promise.all(promises)
+              .then((res) => {
+                console.log("Promise.all success", res);
+                this.$store.commit(`scene/removeSaveApi`);
+                this.$msg.success(i18n.t("gather.save_done"));
+              })
+              .catch((err) => {
+                console.error("Promise.all failed", err);
+              });
+          }
+        } else {
+          this.$store.dispatch(`${this.$route.name}/save`).then((res) => {
+            this.$store.commit(`scene/removeSaveApi`);
+            this.$msg.success(i18n.t("gather.save_done"));
+          });
+        }
+        // this.$store.commit(`scene/removeSaveApi`);
+        // this.$msg.success(i18n.t("gather.save_done"));
+        // saveWorks(
+        //   {
+        //     password: this.info.password,
+        //     someData: { ...this.info, status: 1 },
+        //   },
+        //   () => {
+        //     this.$msg.success(this.$i18n.t("gather.save_done"));
+        //     // document.title = this.info.name;
+        //     document.title = this.info.name || this.$i18n.t("gather.no_title");
+        //     $waiting.hide();
+        //     // this.getInfo();
+        //     // this.$store.commit("UpdateIsShowState", true);
+        //     this.$store.commit("TakeInfoSnapShotAtSave");
+        //   },
+        //   () => {}
+        // );
       }
     },
     getInfo() {
@@ -301,66 +497,61 @@ export default {
           $waiting.hide();
 
           if (res.code == 0) {
-            $waiting.show();
-            getPanoInfo(
-              "",
-              (data) => {
-                this.$store.commit("SetInfo", data);
-                this.$store.commit("scene/setScenes", data.scenes);
-                $waiting.hide();
-
-                let firstScene = "";
-
-                if (data.firstScene) {
-                  firstScene = data.scenes.find(
-                    (item) => item.sceneCode == data.firstScene.sceneCode
-                  );
-                }
-
-                //判断列表里是否有全景图
-                if (
-                  data.scenes.some((item) => item.type == "pano") &&
-                  firstScene &&
-                  firstScene.type == "4dkk"
-                ) {
-                  console.log(this.currentScene.sceneCode);
-                  //如果当前有选中场景,则激活选中场景,无则显示第一个全景图
-                  firstScene = this.currentScene.sceneCode
-                    ? this.currentScene
-                    : data.scenes.find((item) => item.type == "pano");
-                }
-
-                console.log(firstScene, "firstScene");
-                this.$store.commit(
-                  "scene/setCurrentScene",
-                  firstScene || data.scenes[0]
-                );
-
-                // 查询初始场景的所在1级分组
-                let catalog = data.catalogs.find(
-                  (item) => item.id == this.currentScene.category
-                );
-                data.catalogRoot.forEach((item) => {
-                  let temp =
-                    item.children &&
-                    item.children.find((sub) => sub == catalog.id);
-                  if (temp) {
-                    this.$store.commit("scene/setCurrentCatalogRoot", item);
-                    return;
-                  }
-                });
-                resolve();
-              },
-              (err) => {
-                reject(err);
-              }
-            );
+            // $waiting.show();
+            // getPanoInfo(
+            //   "",
+            //   (data) => {
+            //     this.$store.commit("SetInfo", data);
+            //     this.$store.commit("scene/setScenes", data.scenes);
+            //     $waiting.hide();
+            //     let firstScene = "";
+            //     if (data.firstScene) {
+            //       firstScene = data.scenes.find((item) => item.sceneCode == data.firstScene.sceneCode);
+            //     }
+            //     //判断列表里是否有全景图
+            //     if (data.scenes.some((item) => item.type == "pano") && firstScene && firstScene.type == "4dkk") {
+            //       console.log(this.currentScene.sceneCode);
+            //       //如果当前有选中场景,则激活选中场景,无则显示第一个全景图
+            //       firstScene = this.currentScene.sceneCode ? this.currentScene : data.scenes.find((item) => item.type == "pano");
+            //     }
+            //     console.log(firstScene, "firstScene");
+            //     this.$store.commit("scene/setCurrentScene", firstScene || data.scenes[0]);
+            //     // 查询初始场景的所在1级分组
+            //     let catalog = data.catalogs.find((item) => item.id == this.currentScene.category);
+            //     data.catalogRoot.forEach((item) => {
+            //       let temp = item.children && item.children.find((sub) => sub == catalog.id);
+            //       if (temp) {
+            //         this.$store.commit("scene/setCurrentCatalogRoot", item);
+            //         return;
+            //       }
+            //     });
+            //     resolve();
+            //   },
+            //   (err) => {
+            //     reject(err);
+            //   }
+            // );
           } else {
             reject();
           }
         });
       });
     },
+
+    getSettingsInfo() {
+      // this.$store.commit("base/setData", this.info);
+      getWorkInfo(
+        {},
+        (res) => {
+          let data = res.data;
+          this.$store.commit("base/initbaseInfo", data);
+
+          this.$store.commit("UpdateIsShowState", true);
+          // OnlineDetector.valid();
+        },
+        () => {}
+      );
+    },
   },
 };
 </script>

+ 24 - 46
packages/qjkankan-editor/src/framework/MenuPC.vue

@@ -1,49 +1,17 @@
 <template>
-  <div
-    class="pc-menu"
-    :style="`width:${$i18n.t('style_key.menu_width')}`"
-    app-border
-    dir-right
-  >
+  <div class="pc-menu" :style="`width:${$i18n.t('style_key.menu_width')}`" app-border dir-right>
     <ul class="pc-menu-container">
-      <li
-        v-for="(item, key) in menu"
-        v-show="!item.hidden"
-        :class="{ disable: (key != 0 && !isShow) || isEditing }"
-        :key="key"
-      >
+      <li v-for="(item, key) in menu" v-show="!item.hidden" :class="{ disable: (key != 0 && !isShow) || isEditing }" :key="key">
         <router-link :to="item.link" :exact="false">
-          <img
-            class="normal"
-            :src="require(`@/assets/images/icons/navs/${item.icon}_normal.svg`)"
-            alt=""
-          />
-          <img
-            class="active"
-            :src="
-              require(`@/assets/images/icons/navs/${item.icon}_selected.svg`)
-            "
-            alt=""
-          />
+          <img class="normal" :src="require(`@/assets/images/icons/navs/${item.icon}_normal.svg`)" alt="" />
+          <img class="active" :src="require(`@/assets/images/icons/navs/${item.icon}_selected.svg`)" alt="" />
           <span>{{ item.text }}</span>
         </router-link>
       </li>
     </ul>
 
-    <a
-      class="help"
-      :href="
-        $lang == 'en'
-          ? 'https://docs.4dkankan.com/#/product/4dpano/en-us/README'
-          : 'https://docs.4dkankan.com/#/product/4dpano/zh-cn/README'
-      "
-      target="_blank"
-    >
-      <img
-        v-tooltip="$i18n.t('edit_settings.help_center')"
-        :src="require(`@/assets/images/icons/help_tips.png`)"
-        alt=""
-      />
+    <a class="help" :href="$lang == 'en' ? 'https://docs.4dkankan.com/#/product/4dpano/en-us/README' : 'https://docs.4dkankan.com/#/product/4dpano/zh-cn/README'" target="_blank">
+      <img v-tooltip="$i18n.t('edit_settings.help_center')" :src="require(`@/assets/images/icons/help_tips.png`)" alt="" />
     </a>
   </div>
 </template>
@@ -60,19 +28,29 @@ export default {
   },
   computed: {
     ...mapGetters({
+      info: "base/baseInfo",
       isShow: "isShow",
       isEditing: "isEditing",
     }),
   },
   watch: {
-    "$route.name": {
-      immediate: true,
-      handler: function (newVal) {
-        if (newVal != "base" && !this.isShow) {
-          this.$router.push({ path: "/base", redirect: true });
-        }
-      },
-    },
+    // "$route.name": {
+    //   immediate: true,
+    //   handler: function (newVal) {
+    //     if (newVal != "base" && !this.isShow) {
+    //       this.$router.push({ path: "/base", redirect: true });
+    //     }
+    //   },
+    // },
+    // info: {
+    //   immediate: true,
+    //   handler: function (newVal) {
+    //     if (newVal) {
+    //       console.error('');
+    //       this.$router.push({ path: "/navigation" });
+    //     }
+    //   },
+    // },
   },
   mounted() {
     // 添加滚动条

+ 2 - 6
packages/qjkankan-editor/src/framework/core/index.vue

@@ -6,9 +6,7 @@
     <div v-show="activeItem" id="pano"></div>
     <template v-if="showSnapshot && activeItem">
       <snapshot :showFlash="showFlash"></snapshot>
-      <button class="ui-button submit" @click="onClick">
-        将当前视角设为初始画面
-      </button>
+      <button class="ui-button submit" @click="onClick">将当前视角设为初始画面</button>
     </template>
   </div>
 </template>
@@ -45,9 +43,7 @@ export default {
   },
   methods: {
     updateInfo() {
-      let iidx = this.info.scenes.findIndex(
-        (item) => this.activeItem.sceneCode == item.sceneCode
-      );
+      let iidx = this.info.scenes.findIndex((item) => this.activeItem.sceneCode == item.sceneCode);
       if (iidx > -1) {
         this.info.scenes[iidx] = {
           ...this.activeItem,

+ 3 - 0
packages/qjkankan-editor/src/framework/play/pano/components/list.vue

@@ -201,6 +201,7 @@ export default {
       }
     },
     currentScenesList(val) {
+      console.error(val)
       // this.loadList();
       if (val.length > 0) {
         // console.warn("initScenesSwiper", val);
@@ -215,6 +216,7 @@ export default {
     metadata: {
       deep: true,
       handler: function (newVal) {
+        console.error(newVal)
         if (newVal.scenes) {
           this.$store.commit("scene/setScenes", newVal.scenes);
           if (!this.show) {
@@ -232,6 +234,7 @@ export default {
       isLockV4Scene: "isLockV4Scene",
     }),
     ...mapGetters({
+      info:'base/baseInfo',
       metadata: "scene/metadata",
       scenes: "scene/list",
       currentScene: "scene/currentScene",

+ 528 - 0
packages/qjkankan-editor/src/framework/play/pano/components/new-list.vue

@@ -0,0 +1,528 @@
+<template>
+  <div class="bar-list" v-if="info" :class="{ disable: isEditing }">
+    <!-- {{secondGroups}} -->
+    <div class="top-con" v-show="currentScenesList.length">
+      <div v-if="currentScenesList.length" class="scene-list swiper-container" ref="scene-swiper" :style="`width:${scenesListW > 1150 ? '100%' : scenesListW + 'px'}`">
+        <!-- <div class="scene-list swiper-container" ref="scene-swiper"> -->
+        <div class="swiper-wrapper scene-wrapper">
+          <!-- <div
+            class="swiper-slide scene-slide"
+            :class="{
+              active: currentScene.id == item.id ||currentScene.id == item.sid  ,
+              disabled: isLockV4Scene,
+              // loopspan:
+              //   item.sceneTitle.length > spanlength &&
+              //   currentScene.id == item.id,
+            }"
+            v-for="(item, index) in currentScenesList"
+          > -->
+          <div
+            class="swiper-slide scene-slide"
+            :class="{
+              active: currentScene.id == item.id || (currentScene.sid && currentScene.sid == item.sid),
+              disabled: isLockV4Scene,
+              // loopspan:
+              //   item.sceneTitle.length > spanlength &&
+              //   currentScene.id == item.id,
+            }"
+            v-for="(item, index) in currentScenesList"
+          >
+            <div @click="tabCurrentScene(item, index)" class="scene-content" :style="`background-image:url(${item.icon});`">
+              <!-- <img :src="i.icon" alt="" /> -->
+
+              <i class="iconfont" :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
+              <div class="marquee">
+                <marquee-text :repeat="1" :duration="Math.ceil(item.name.length / 10) * 5" :key="item.id" v-if="item.name.length > spanlength && currentScene.id == item.id">
+                  {{ item.name }}
+                </marquee-text>
+                <span v-else>
+                  {{ item.name }}
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="second-group-box">
+        <!-- <div v-if="info.navigationTrees[rootTabIndex]?.children[0]?.type == 'group' && showSecondTab" class="second-group-list swiper-container" ref="second-group-swiper"> -->
+        <div v-if="secondGroups.length > 1" class="second-group-list swiper-container" ref="second-group-swiper">
+          <div class="swiper-wrapper second-group-wrapper">
+            <div
+              class="swiper-slide second-group-slide"
+              @click="tabSecond(item, index)"
+              v-if="item.children.length"
+              v-for="(item, index) in secondGroups"
+              :class="{ active: currentSecondId == item.id, disabled: isLockV4Scene, loopspan: fixTitle(item.name).length > spanlength && currentSecondId == item.id }"
+            >
+              <div class="second-group-content">
+                <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentSecondId == item.id">
+                  {{ fixTitle(item.name) }}
+                </marquee-text>
+                <span v-else>
+                  {{ fixTitle(item.name) }}
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="bottom-com" :style="`width:${catalogRootW}px;`">
+      <div v-if="info.navigationTrees.length > 1" :style="`width:${catalogRootW}px;`" class="root-group-list swiper-container" ref="root-group">
+        <div class="swiper-wrapper root-group-wrapper">
+          <div
+            class="swiper-slide root-group-slide"
+            :class="{
+              active: currentRootId == item.id,
+              disabled: isLockV4Scene,
+              loopspan: fixTitle(item.name).length > spanlength && currentRootId.id == item.id,
+            }"
+            v-for="(item, index) in info.navigationTrees"
+            @click="tabRoot(item, index)"
+          >
+            <div class="root-group-content">
+              <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentRootId == item.id">
+                {{ fixTitle(item.name) }}
+              </marquee-text>
+              <span v-else>
+                {{ fixTitle(item.name) }}
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapGetters, mapState } from "vuex";
+import { debounce } from "lodash";
+import MarqueeText from "vue-marquee-text-component";
+export default {
+  components: { MarqueeText },
+  data() {
+    return {
+      spanlength: 6,
+      swidth: {
+        swcatalogRoot: 104,
+        swSecondary: 84,
+        swScenes: 72,
+      },
+      SceneSwiper: null,
+      SecondGroupSwiper: null,
+      rootGroupSwiper: null,
+      swiperOptions: {
+        slidesPerView: "auto",
+        centeredSlides: true,
+        slideToClickedSlide: true,
+        centerInsufficientSlides: true,
+        centeredSlidesBounds: true,
+        freeMode: {
+          enabled: true,
+          sticky: false,
+        },
+      },
+    };
+  },
+
+  watch: {
+    currentScenesList(val) {
+      if (!val.length && this.info.navigationTrees[this.rootTabIndex]?.children.length > 1 && this.info.navigationTrees[this.rootTabIndex]?.children[0].type == "group") {
+        let index =
+          this.info.navigationTrees[this.rootTabIndex]?.children.findIndex((item) => item.children.length) == -1
+            ? 0
+            : this.info.navigationTrees[this.rootTabIndex]?.children.findIndex((item) => item.children.length);
+        let id = this.info.navigationTrees[this.rootTabIndex]?.children[index]?.id;
+        this.$store.commit("navigation/setData", { currentSecondId: id });
+        this.tabSecond(this.info.navigationTrees[this.rootTabIndex]?.children[index]);
+      }
+
+      this.$nextTick(() => {
+        this.initSceneSwiper();
+        if (this.info.navigationTrees.length > 1) {
+          this.initRootGroupSwiper();
+        }
+        if (this.info.navigationTrees[this.rootTabIndex]?.children[this.secondTabIndex]?.type == "group" && this.secondGroups.length > 1) {
+          this.initSecondGroupSwiper();
+        }
+      });
+    },
+    "info.navigationTrees": {
+      // immediate: true,
+      handler: function (newVal, oldVal) {
+        if (newVal) {
+          // //监听数变化更新当前列表
+          // this.$nextTick(() => {
+          //   if (this.currentRootId || this.currentSecondId) {
+          //     if (this.currentRootId && this.currentSecondId) {
+          //       this.$store.commit("scene/setCurrentScenesList", this.info.navigationTrees[this.rootTabIndex].children[this.secondTabIndex].children);
+          //     } else if (this.currentRootId && !this.currentSecondId) {
+          //       this.$store.commit("scene/setCurrentScenesList", this.info.navigationTrees[this.rootTabIndex].children);
+          //     }
+          //   }
+          // });
+        }
+      },
+      deep: true,
+    },
+  },
+
+  computed: {
+    ...mapState({
+      isLockV4Scene: "isLockV4Scene",
+    }),
+    ...mapGetters({
+      info: "base/baseInfo",
+      currentScene: "scene/currentScene",
+      sceneList: "base/sceneList",
+      currentScenesList: "scene/currentScenesList",
+      currentSecondId: "navigation/currentSecondId",
+      currentRootId: "navigation/currentRootId",
+      isEditing: "isEditing",
+    }),
+    scenesListW() {
+      return this?.currentScenesList?.length * (this.swidth["swScenes"] + 10) || 0;
+    },
+    secondaryW() {
+      return this.info.navigationTrees[0].length * (this.swidth["swSecondary"] + 10);
+    },
+    catalogRootW() {
+      // return this.info.navigationTrees.length * (this.swidth["swcatalogRoot"] + 10);
+
+      return this.info.navigationTrees.length * (this.swidth["swcatalogRoot"] + 10);
+    },
+    rootTabIndex() {
+      return this.info.navigationTrees.findIndex((item) => item.id == this.currentRootId) == -1 ? 0 : this.info.navigationTrees.findIndex((item) => item.id == this.currentRootId);
+    },
+    secondTabIndex() {
+      return this.info.navigationTrees[this.rootTabIndex].children.findIndex((item) => item.id == this.currentSecondId) == -1
+        ? 0
+        : this.info.navigationTrees[this.rootTabIndex].children.findIndex((item) => item.id == this.currentSecondId);
+    },
+    showSecondTab() {
+      return this.info.navigationTrees[this.rootTabIndex].children.some((item) => item.id != this.currentSecondId);
+    },
+    secondGroups() {
+      if (this.rootTabIndex >= 0) {
+        return this.info.navigationTrees[this.rootTabIndex].children.filter((item) => item.children.length);
+      } else {
+        return [];
+      }
+    },
+  },
+  methods: {
+    fixTitle(name) {
+      if (name == "默认二级分组") {
+        name = this.$i18n.t("navigation.default_group_two");
+      } else if (name == "一级分组") {
+        name = this.$i18n.t("navigation.group_one");
+      } else {
+        // eslint-disable-next-line no-self-assign
+        name = name;
+      }
+      return name;
+    },
+
+    tabRoot(item, index) {
+      this.$store.commit("navigation/setData", { currentRootId: item.id, currentSecondId: null });
+      // this.handlerCurrent();
+      this.changeSceneList();
+      this.$nextTick(() => {
+        // this.rootGroupSwiper.slideTo(index);
+      });
+    },
+    tabSecond(item, index) {
+      this.$store.commit("navigation/setData", { currentSecondId: item.id });
+      let sceneList = this.info.navigationTrees[this.rootTabIndex].children.length ? this.info.navigationTrees[this.rootTabIndex].children[this.secondTabIndex].children : [];
+      this.$store.commit("scene/setCurrentScenesList", sceneList);
+      this.$nextTick(() => {
+        // this.rootGroupSwiper.slideTo(index);
+      });
+    },
+    changeSceneList() {
+      let currentList = null;
+
+      if (this.info.navigationTrees[this.rootTabIndex].children.length && this.info.navigationTrees[this.rootTabIndex].children[0].type == "group") {
+        this.$store.commit("navigation/setData", { currentSecondId: this.info.navigationTrees[this.rootTabIndex].children[0].id });
+
+        //如果有当前视图则选择二级目录
+        this.info.navigationTrees[this.rootTabIndex].children.forEach((item, index) => {
+          if (item.children.length) {
+            item.children.forEach((t_item, t_index) => {
+              if (t_item.id == this.currentScene.id || (t_item.sid && this.currentScene.sid && t_item.sid == this.currentScene.sid)) {
+                this.$store.commit("navigation/setData", { currentSecondId: item.id });
+              }
+            });
+          }
+        });
+
+        currentList = this.info.navigationTrees[this.rootTabIndex].children[this.secondTabIndex].children;
+        this.$store.commit("scene/setCurrentScenesList", currentList);
+      }
+
+      // if (this.secondTabIndex == -1) {
+      if (!this.currentSecondId) {
+        console.error("没有二级目录");
+        let rootList = this.info.navigationTrees.find((item) => item.id == this.currentRootId);
+        this.$store.commit("scene/setCurrentScenesList", rootList.children);
+      }
+      this.$nextTick(() => {
+        this.initSceneSwiper();
+
+        this.initRootGroupSwiper();
+
+        if (this.info.navigationTrees[this.currentRootId]?.children[this.currentSecondId]?.type == "group") {
+          this.initSecondGroupSwiper();
+        }
+      });
+    },
+    handlerCurrent() {
+      this.$nextTick(() => {
+        this.SecondGroupSwiper.slideTo(this.secondTabIndex);
+        let sceneIndex = this.currentScenesList.findIndex((item) => {
+          return item.id == this.currentScene.id || (item.sid && this.currentScene.sid && item.sid == this.currentScene.sid);
+        });
+        if (sceneIndex >= 0) {
+          this.SceneSwiper.slideTo(this.sceneIndex);
+        }
+      });
+    },
+    initSceneSwiper() {
+      if (this.SceneSwiper) {
+        this.SceneSwiper.destroy();
+        this.SceneSwiper = null;
+      }
+      if (!this.currentScenesList.length) {
+        return;
+      }
+      this.SceneSwiper = new Swiper(".scene-list", this.swiperOptions);
+      this.$nextTick(() => {
+        let sceneIndex = this.currentScenesList.findIndex((item) => {
+          return item.id == this.currentScene.id || (item.sid && this.currentScene.sid && item.sid == this.currentScene.sid);
+        });
+        if (sceneIndex >= 0) {
+          this.SceneSwiper.slideTo(sceneIndex);
+        }
+      });
+    },
+
+    tabCurrentScene(data, index) {
+      this.$store.commit("scene/setCurrentScene", data);
+
+      this.SceneSwiper.slideTo(index);
+    },
+    initSecondGroupSwiper() {
+      if (this.SecondGroupSwiper) {
+        this.SecondGroupSwiper.destroy();
+        this.SecondGroupSwiper = null;
+      }
+      this.SecondGroupSwiper = new Swiper(".second-group-list", this.swiperOptions);
+      this.$nextTick(() => {
+        if (this.secondTabIndex >= 0) {
+          this.SecondGroupSwiper.slideTo(this.secondTabIndex);
+        }
+      });
+    },
+
+    tabCurrentSecondGroup(data, index) {
+      // this.$store.commit("scene/setCurrentScene", data);
+
+      this.SecondGroupSwiper.slideTo(index);
+    },
+
+    initRootGroupSwiper() {
+      if (this.info.navigationTrees.length <= 1) {
+        return;
+      }
+      if (this.rootGroupSwiper) {
+        this.rootGroupSwiper.destroy();
+        this.rootGroupSwiper = null;
+      }
+      this.rootGroupSwiper = new Swiper(".root-group-list", this.swiperOptions);
+      this.$nextTick(() => {
+        if (this.rootTabIndex >= 0) {
+          this.rootGroupSwiper.slideTo(this.rootTabIndex);
+        }
+      });
+    },
+
+    tabCurrentRootGroup(data, index) {
+      // this.$store.commit("scene/setCurrentScene", data);
+
+      this.SecondGroupSwiper.slideTo(index);
+    },
+  },
+
+  mounted() {},
+};
+</script>
+
+<style lang="less" scoped>
+@width: 1150px;
+.swiper-slide {
+  cursor: pointer;
+  &.loopspan {
+    > span,
+    > div > span {
+      animation: 5s wordsLoop linear infinite normal;
+    }
+  }
+}
+
+.bar-list {
+  position: absolute;
+  bottom: 28px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  max-width: @width;
+  overflow: hidden;
+  transition: 0.3s all ease;
+  .top-con {
+    display: inline-block;
+    margin: 0 auto 10px;
+    padding: 10px 6px;
+    overflow: hidden;
+    background: linear-gradient(268deg, transparent, rgba(0, 0, 0, 0.4) 25%, rgba(0, 0, 0, 0.4) 75%, transparent);
+    .scene-list {
+      max-width: @width;
+      margin: 0 auto;
+      // display: inline-block;
+
+      .scene-wrapper {
+        .scene-slide {
+          margin: 0 5px;
+          // white-space: nowrap;
+          cursor: pointer;
+          width: 72px;
+          height: 72px;
+          border-radius: 6px;
+          border: 1px solid #fff;
+          background-size: cover;
+          position: relative;
+          overflow: hidden;
+          &.active {
+            border: 1px solid #0076f6;
+            .marquee {
+              color: #fff !important;
+            }
+          }
+          .scene-content {
+            width: 100%;
+            height: 100%;
+            background-size: cover;
+            position: relative;
+
+            img {
+              width: 100%;
+              height: 100%;
+              pointer-events: none;
+              object-fit: cover;
+            }
+            > .iconfont {
+              position: absolute;
+              left: 4px;
+              top: 4px;
+              z-index: 99;
+              text-shadow: 0px 0px 4px rgb(0, 0, 0, 0.3);
+            }
+            .marquee {
+              position: absolute;
+              bottom: 0;
+              left: 0;
+              height: 20px;
+              background: rgba(0, 0, 0, 0.5);
+              width: 100%;
+              overflow: hidden;
+              color: rgba(255, 255, 255, 0.6);
+              > span {
+                width: 100%;
+                line-height: 20px;
+                word-break: keep-all;
+                display: inline-block;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .second-group-box {
+      .second-group-list {
+        max-width: @width;
+        margin: 0 auto;
+        display: inline-block;
+
+        .second-group-wrapper {
+          .second-group-slide {
+            width: 84px;
+            box-sizing: border-box;
+            overflow: hidden;
+            padding-bottom: 6px;
+            cursor: pointer;
+            margin: 0 5px;
+            white-space: nowrap;
+            color: hsla(0, 0%, 100%, 0.6);
+            margin: 20px auto 10px;
+            &.active {
+              color: #fff;
+
+              &::before {
+                content: "";
+                display: inline-block;
+                position: absolute;
+                bottom: 0;
+                width: 20px;
+                height: 2px;
+                z-index: 9999;
+                left: 50%;
+                transform: translateX(-50%);
+                background: #0076f6;
+              }
+            }
+            .second-group-content {
+              // width: 100%;
+              // height: 100%;
+              // background-size: cover;
+              // position: relative;
+            }
+          }
+        }
+      }
+    }
+  }
+  .bottom-com {
+    margin: 0px auto;
+    .root-group-list {
+      max-width: @width;
+      margin: 0 auto;
+      .swiper-wrapper {
+      }
+      .root-group-wrapper {
+        .root-group-slide {
+          width: 104px;
+          background: rgba(0, 0, 0, 0.5);
+          border-radius: 4px;
+          padding: 4px 10px;
+          border: 1px solid hsla(0, 0%, 100%, 0.5);
+          box-sizing: border-box;
+          overflow: hidden;
+          margin: 0 5px;
+          white-space: nowrap;
+          color: hsla(0, 0%, 100%, 0.6);
+
+          &.active {
+            border: 1px solid #fff;
+            color: #fff;
+          }
+          .root-group-content {
+            width: 100%;
+            word-break: keep-all;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 87 - 101
packages/qjkankan-editor/src/framework/play/pano/index.vue

@@ -1,43 +1,25 @@
 <template>
   <div class="panocon" :class="[routerName]">
-    <div v-show="currentScene.type !== '4dkk'" id="pano"></div>
+    <div v-show="currentScene?.type !== '4dkk'" id="pano"></div>
     <!-- 调试信息: {{ currentScene }} -->
     <iframe
       id="iframe-4dkk"
       :class="currentSceneVersion"
       width=""
-      v-if="currentScene.type === '4dkk'"
-      :src="`${locationOrigin}/sp${
-        currentSceneVersion === 'V3'
-          ? 'c'
-          : currentSceneVersion === 'V4'
-          ? 'g'
-          : 'g'
-      }.html?m=${currentScene.sceneCode}&lang=${lang}`"
+      v-if="currentScene?.type === '4dkk'"
+      :src="`${locationOrigin}/sp${currentSceneVersion === 'V3' ? 'c' : currentSceneVersion === 'V4' ? 'g' : 'g'}.html?m=${currentScene?.sceneCode}&lang=${lang}`"
       frameborder="0"
     />
 
-    <div
-      class="showexplanation"
-      v-if="
-        showExplanation &&
-        currentScene.explanation &&
-        currentScene.explanation.audioId
-      "
-    >
+    <div class="showexplanation" v-if="showExplanation && currentScene?.explanation && currentScene?.explanation.audioId">
       <img :src="require(`@/assets/images/commentary@2x.png`)" alt="" />
     </div>
-    <list></list>
+    <!-- <list></list> -->
+    <newList></newList>
 
-    <template
-      v-if="showSnapshot && currentScene && currentScene.type !== '4dkk'"
-    >
+    <template v-if="showSnapshot && currentScene && currentScene?.type !== '4dkk'">
       <snapshot :showFlash="showFlash"></snapshot>
-      <button
-        class="ui-button submit set-initial-view"
-        :class="{ disable: currentScene.type === '4dkk' }"
-        @click="onClick"
-      >
+      <button class="ui-button submit set-initial-view" :class="{ disable: currentScene?.type === '4dkk' }" @click="onClick">
         {{ $i18n.t("screen.setting_screen") }}
       </button>
     </template>
@@ -46,6 +28,7 @@
 
 <script>
 import list from "./components/list";
+import newList from "./components/new-list.vue";
 import { mapGetters } from "vuex";
 import * as krfn from "@/core/index.js";
 import { $waiting } from "@/components/shared/loading";
@@ -53,11 +36,12 @@ import Snapshot from "@/components/Snapshot";
 import { uploadCover, searchInAll3DScenes } from "@/api";
 import config from "@/config";
 import { isUpgradeAdapter } from "@/utils/fixVersion";
+import hotspot from "@/Store/modules/hotspot";
 
 let __krfn = krfn.default;
 
 export default {
-  components: { list, Snapshot },
+  components: { list, Snapshot, newList },
   data() {
     return {
       showFlash: false,
@@ -73,8 +57,12 @@ export default {
     },
     ...mapGetters({
       currentScene: "scene/currentScene",
-      info: "info",
+      baseInfo: "base/baseInfo",
       isConfirmingPosi: "tags/isConfirmingPosi",
+      sceneList: "base/sceneList",
+      hotspotList: "hotspot/hotspotList",
+      workVisualAngleList: "screen/workVisualAngleList",
+      workCustomMaskList: "mask/workCustomMaskList",
     }),
 
     showSnapshot() {
@@ -89,7 +77,8 @@ export default {
       __krfn.utils.toggleHotspot(this.$getKrpano(), newVal == "hotspot");
       this.handleRouterCoverForCap();
     },
-    currentScene(newVal) {
+    currentScene(newVal, oldVal) {
+      console.error(newVal);
       if (newVal) {
         this.$nextTick(() => {
           this.$bus.emit("initView", newVal.icon);
@@ -117,23 +106,31 @@ export default {
         }
         this.handleIFrameCss();
       } else {
+        // if (newVal.sid && newVal.sid == oldVal?.sid) {
+        //   //如果是保存则禁止刷新
+        //   return false;
+        // }
         $("#pano").empty();
         window.vrInitFn = () => {
           $waiting.hide();
-          __krfn.utils.initHotspot(
-            this.$getKrpano(),
-            newVal && newVal.someData,
-            true
-          );
-          __krfn.utils.toggleHotspot(
-            this.$getKrpano(),
-            this.$route.name == "hotspot"
-          );
+          let hotspots = this.hotspotList.filter((item) => item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid);
+          __krfn.utils.initHotspot(this.$getKrpano(), hotspots, true);
+          __krfn.utils.toggleHotspot(this.$getKrpano(), this.$route.name == "hotspot");
         };
         window.vrViewFn = () => {
           try {
-            let visual = newVal.initVisual;
-            const { sky, earth } = newVal.customMask;
+            // let visual = newVal.initVisual;
+            // const { sky, earth } = newVal.customMask;
+
+            let visual = this.workVisualAngleList.find((item) => item.navigationId == newVal.id || item.navigationId == newVal.sid);
+
+            let sky, earth;
+            this.workCustomMaskList.forEach((item, index) => {
+              if (this.currentScene.id == item.navigationId || this.currentScene.sid == item.navigationId) {
+                sky = item.data.sky;
+                earth = item.data.earth;
+              }
+            });
             this.handleVisual(visual);
             this.handleSkyCover(sky);
             this.handleEarthCover(earth);
@@ -149,7 +146,6 @@ export default {
         if (newVal) {
           removepano("#pano");
           $waiting.show();
-
           embedpano({
             // http://oss-xiaoan.oss-cn-shenzhen.aliyuncs.com/720yun_fd_manage/fd720_Va0LrkXW3/vtour/tour.xml
             // xml: "%HTMLPATH%/static/template/tour.xml",
@@ -180,32 +176,45 @@ export default {
   },
   methods: {
     updateInfo() {
-      let iidx = this.info.scenes.findIndex(
-        (item) => this.currentScene.sceneCode == item.sceneCode
-      );
+      // let iidx = this.baseInfo.scenes.findIndex((item) => this.currentScene.sceneCode == item.sceneCode);
+      let iidx = this.sceneList.findIndex((item) => this.currentScene.sceneCode == item.sceneCode);
       if (iidx > -1) {
-        this.info.scenes[iidx] = {
+        this.sceneList[iidx] = {
           ...this.currentScene,
         };
       }
-      this.$store.commit("SetInfo", this.info);
+      // this.$store.commit("SetInfo", this.baseInfo);
     },
     onClick() {
       this.$bus.emit("toggleFlash", true);
       let canvas = $("#krpanoSWFObject canvas")[0];
       let data = __krfn.utils.setInitView(this.$getKrpano(), canvas);
+      console.error(this.currentScene, data);
       uploadCover({ file: data.url, filename: "initCover.jpg" }, (res) => {
         if (res.code == 0) {
-          this.currentScene.icon = res.data;
-          this.currentScene.initVisual = {
-            hlookat: data.hlookat,
-            vlookat: data.vlookat,
-          };
+          let c_scene = JSON.parse(JSON.stringify(this.currentScene));
+          c_scene.icon = res.data;
+          this.$store.commit("scene/setCurrentScene", c_scene);
+          // this.currentScene.icon = res.data;
+          // this.currentScene.initVisual = {
+          //   hlookat: data.hlookat,
+          //   vlookat: data.vlookat,
+          // };
+          this.workVisualAngleList.map((item) => {
+            if (item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid) {
+              item.hlookat = data.hlookat;
+              item.vlookat = data.vlookat;
+              item.icon = res.data;
+            }
+          });
+
+          // this.$store.commit("base/setData", { workVisualAngleList: this.baseInfo.workVisualAngleList });
+
           this.$bus.emit("toggleFlash", false);
           this.$bus.emit("initView", res.data);
-          this.updateInfo();
+          // this.updateInfo();
           this.$msg.success(this.$i18n.t("gather.setting_success"));
-          this.$store.commit("SetInfo", this.info);
+          // this.$store.commit("SetInfo", this.baseInfo);
         }
       });
     },
@@ -215,23 +224,11 @@ export default {
     },
 
     handleVisual(visual) {
-      this.$getKrpano().set(
-        "view.vlookat",
-        "vlookat" in visual ? visual.vlookat : ""
-      );
-      this.$getKrpano().set(
-        "view.hlookat",
-        "hlookat" in visual ? visual.hlookat : ""
-      );
+      this.$getKrpano().set("view.vlookat", "vlookat" in visual ? visual.vlookat : "");
+      this.$getKrpano().set("view.hlookat", "hlookat" in visual ? visual.hlookat : "");
       this.$getKrpano().set("view.limitview", "lookat");
-      this.$getKrpano().set(
-        "view.vlookatmin",
-        "vlookatmin" in visual ? visual.vlookatmin : "-90"
-      );
-      this.$getKrpano().set(
-        "view.vlookatmax",
-        "vlookatmax" in visual ? visual.vlookatmax : "90"
-      );
+      this.$getKrpano().set("view.vlookatmin", "vlookatmin" in visual ? visual.vlookatmin : "-90");
+      this.$getKrpano().set("view.vlookatmax", "vlookatmax" in visual ? visual.vlookatmax : "90");
     },
     /**
      * 1.6 处理遮罩层
@@ -241,28 +238,19 @@ export default {
       // console.log("handleSkyCover", skyMask);
       if (skyMask) {
         if ("isShow" in skyMask) {
-          this.$getKrpano().set(
-            `hotspot[peaklogo].visible`,
-            Boolean(skyMask.isShow)
-          );
+          this.$getKrpano().set(`hotspot[peaklogo].visible`, Boolean(skyMask.isShow));
         }
 
         if (skyMask.icon) {
           this.$getKrpano().set(`hotspot[peaklogo].url`, skyMask.icon);
-        } 
+        }
         if ("scale" in skyMask) {
           this.$getKrpano().set(`hotspot[peaklogo].scale`, skyMask.scale);
         }
         if ("antidistorted" in skyMask) {
-          this.$getKrpano().set(
-            `hotspot[peaklogo].distorted`,
-            skyMask.antidistorted
-          );
+          this.$getKrpano().set(`hotspot[peaklogo].distorted`, skyMask.antidistorted);
           if (!skyMask.antidistorted) {
-            this.$getKrpano().set(
-              `hotspot[peaklogo].scale`,
-              skyMask.scale * 0.5
-            );
+            this.$getKrpano().set(`hotspot[peaklogo].scale`, skyMask.scale * 0.5);
           }
         }
       }
@@ -274,10 +262,7 @@ export default {
     handleEarthCover(earthMask) {
       if (earthMask) {
         if ("isShow" in earthMask) {
-          this.$getKrpano().set(
-            `hotspot[nadirlogo].visible`,
-            Boolean(earthMask.isShow)
-          );
+          this.$getKrpano().set(`hotspot[nadirlogo].visible`, Boolean(earthMask.isShow));
         }
         if (earthMask.icon) {
           this.$getKrpano().set(`hotspot[nadirlogo].url`, earthMask.icon);
@@ -286,27 +271,27 @@ export default {
           this.$getKrpano().set(`hotspot[nadirlogo].scale`, earthMask.scale);
         }
         if ("antidistorted" in earthMask) {
-          this.$getKrpano().set(
-            `hotspot[nadirlogo].distorted`,
-            earthMask.antidistorted
-          );
+          this.$getKrpano().set(`hotspot[nadirlogo].distorted`, earthMask.antidistorted);
           if (!earthMask.antidistorted) {
-            this.$getKrpano().set(
-              `hotspot[nadirlogo].scale`,
-              earthMask.scale * 0.5
-            );
+            this.$getKrpano().set(`hotspot[nadirlogo].scale`, earthMask.scale * 0.5);
           }
         }
       }
     },
     handleRouterCoverForCap() {
-      const { sky, earth } = this.currentScene.customMask;
-      sky.isShow &&
-        this.$getKrpano() &&
-        this.$getKrpano().set("hotspot[peaklogo].visible", true);
-      earth.isShow &&
-        this.$getKrpano() &&
-        this.$getKrpano().set("hotspot[nadirlogo].visible", true);
+      if (!this.baseInfo) {
+        return;
+      }
+      let sky, earth;
+      this.workCustomMaskList.forEach((item, index) => {
+        if (this.currentScene.id == item.navigationId || this.currentScene.sid == item.navigationId) {
+          sky = item.data.sky;
+          earth = item.data.earth;
+        }
+      });
+      // const { sky, earth } = this.currentScene.customMask;
+      sky.isShow && this.$getKrpano() && this.$getKrpano().set("hotspot[peaklogo].visible", true);
+      earth.isShow && this.$getKrpano() && this.$getKrpano().set("hotspot[nadirlogo].visible", true);
     },
     handleIFrameCss() {
       setTimeout(() => {
@@ -344,6 +329,7 @@ export default {
     window.__krfn = __krfn;
 
     this.$bus.on("addhotspot", (data) => {
+      console.error("data", data);
       this.addhotspot(data);
     });
 

+ 1 - 1
packages/qjkankan-editor/src/framework/show/index.vue

@@ -293,7 +293,7 @@ export default {
             return;
           }
           if (newVal.hotspotType == "scene") {
-            this.activeItem = newVal.secne;
+            this.activeItem = newVal.scene;
             return;
           }
           this.showPreview = true;

+ 1 - 1
packages/qjkankan-editor/src/framework/showMobile/index.vue

@@ -378,7 +378,7 @@ export default {
             return;
           }
           if (newVal.hotspotType == "scene") {
-            this.activeItem = newVal.secne;
+            this.activeItem = newVal.scene;
             return;
           }
           if (newVal.hotspotType == "image") {

+ 30 - 23
packages/qjkankan-editor/src/pages/Edit.vue

@@ -14,7 +14,12 @@ export default {
     AppLayout,
   },
   computed: {
-    ...mapGetters(["isInfoChangedSinceSave"]),
+    // ...mapGetters(["isInfoChangedSinceSave"]),
+    ...mapGetters({
+      isInfoChangedSinceSave: "isInfoChangedSinceSave",
+      saveApiList: "scene/saveApiList",
+      saveFristScene: "navigation/saveFristScene",
+    }),
   },
   watch: {
     $route() {
@@ -25,34 +30,33 @@ export default {
     },
   },
   async beforeMount() {
-    const id = browser.urlQueryValue('id');
-    const lang = browser.urlQueryValue("lang") || 'zh'
-    const token = browser.urlQueryValue('token');
+    const id = browser.urlQueryValue("id");
+    const lang = browser.urlQueryValue("lang") || "zh";
+    const token = browser.urlQueryValue("token");
     const res = await exchangeId({
-      id: id
-    })
+      id: id,
+    });
 
     if (token) {
-      localStorage.setItem('token', token)
+      localStorage.setItem("token", token);
     }
 
-
-    const { id: resID, num, calcStatus } = res.data
-    const isCam = String(num).length > 0
-    let urlRes = `./edit.html?id=${resID}&lang=${lang}#/base`
+    const { id: resID, num, calcStatus } = res.data;
+    const isCam = String(num).length > 0;
+    let urlRes = `./edit.html?id=${resID}&lang=${lang}#/base`;
     if (isCam) {
-      urlRes = `./edit.html?id=${resID}&lang=${lang}&from=cam#/base`
+      urlRes = `./edit.html?id=${resID}&lang=${lang}&from=cam#/base`;
     }
     if (calcStatus === 0) {
       this.$alert({
-        content: this.$i18n.t('material.works.calcing'),
+        content: this.$i18n.t("material.works.calcing"),
       });
-      return
+      return;
     }
-    console.log('res', urlRes)
+    console.log("res", urlRes);
     location.replace(urlRes);
-  //检验是不是该用户作品
-  checkWork({
+    //检验是不是该用户作品
+    checkWork({
       val: 1,
     }).then((res) => {
       if (res.data) {
@@ -69,23 +73,26 @@ export default {
       } else {
         return this.$alert({ content: "该作品已被删除" });
       }
-    });
-
-    getPanoInfo().then(() => {
-      this.$store.commit("UpdateIsShowState", true);
       OnlineDetector.valid();
     });
 
+    // getPanoInfo().then(() => {
+    //   this.$store.commit("UpdateIsShowState", true);
+    //   OnlineDetector.valid();
+    // });
   },
   mounted() {
     document.title = this.$i18n.t("gather.editpage_name");
     window.store = this.$store;
 
-  
     window.addEventListener(
       "beforeunload",
       (e) => {
-        if (this.isInfoChangedSinceSave) {
+        // if (this.isInfoChangedSinceSave) {
+        //   e.preventDefault();
+        //   e.returnValue = "您有未保存的修改,仍要离开?"; // 如今的浏览器不会显示这条信息了。
+        // }
+        if (this.saveApiList.length || this.saveFristScene) {
           e.preventDefault();
           e.returnValue = "您有未保存的修改,仍要离开?"; // 如今的浏览器不会显示这条信息了。
         }

+ 31 - 24
packages/qjkankan-editor/src/pages/edit.js

@@ -1,22 +1,30 @@
-import Vue from 'vue'
-import '../mixins'
-import App from './Edit.vue'
-import router from '../router/editorRouter'
-import store from '../Store'
-import 'viewerjs/dist/viewer.css'
-import Viewer from 'v-viewer'
-import '@/directives/vTitleInEditor.js'
-import '@/directives/vTooltipInEditor.js'
-import { i18n } from "@/lang"
-import { debuggerHelper } from '../mixins/debuggerHelper'
+import Vue from "vue";
+import "../mixins";
+import App from "./Edit.vue";
+import router from "../router/editorRouter";
+import store from "../Store";
+import "viewerjs/dist/viewer.css";
+import Viewer from "v-viewer";
+import "@/directives/vTitleInEditor.js";
+import "@/directives/vTooltipInEditor.js";
+import { i18n } from "@/lang";
+import { debuggerHelper } from "../mixins/debuggerHelper";
 import Toast from "vue-toastification";
+import browser from "../utils/browser";
 import "vue-toastification/dist/index.css";
+// import VueTreeList from "vue-tree-list";
 
+import { Tree } from "element-ui";
+Vue.use(Tree);
+//调试模式写入token
+if (browser.urlQueryValue("token") && process.env.NODE_ENV == "development" && !localStorage.getItem("token")) {
+  localStorage.setItem("token", browser.urlQueryValue("token"));
+}
 const options = {
-  transition: 'Vue-Toastification__bounce',
+  transition: "Vue-Toastification__bounce",
   maxToasts: 3,
   newestOnTop: true,
-  position: 'top-right',
+  position: "top-right",
   timeout: 2000,
   closeOnClick: true,
   pauseOnFocusLoss: true,
@@ -25,9 +33,9 @@ const options = {
   draggablePercent: 0.7,
   showCloseButtonOnHover: false,
   hideProgressBar: true,
-  closeButton: 'button',
+  closeButton: "button",
   icon: true,
-  rtl: false
+  rtl: false,
 };
 
 Vue.use(Toast, options);
@@ -35,22 +43,21 @@ debuggerHelper(true);
 
 // console.log(`version: ${process.env.VUE_APP_VERSION}`)
 // 热点图标默认大小
-window.g_hotspotCurrentScale = 1
+window.g_hotspotCurrentScale = 1;
 
+// Vue.use(VueTreeList);
 Vue.use(Viewer, {
   defaultOptions: {
     toolbar: 0,
     title: 0,
-    navbar: false
-  }
-})
+    navbar: false,
+  },
+});
 
-Vue.config.productionTip = false
+Vue.config.productionTip = false;
 new Vue({
   router,
   store,
   i18n,
-  render: h => h(App)
-}).$mount('#app')
-
-
+  render: (h) => h(App),
+}).$mount("#app");

+ 203 - 112
packages/qjkankan-editor/src/utils/other.js

@@ -1,27 +1,27 @@
 /**
  * 返回一个自带节流效果的函数,用res表示。
- * 
+ *
  * fn:需要被节流的函数
  * interval:最短多长时间允许执行一次fn
- * 
+ *
  * 功能要点:
  * 1.fn代码里如有this,res被执行时,this会指向res的调用者;
  * 2.res被执行时的实参会映射到fn的形参;
  * 3.第一次调用res时,会立即执行fn。
  */
 export function throttle(fn, interval) {
-  let lastRunTime = 0
+  let lastRunTime = 0;
 
   return function (...args) {
-    let elapsedTime = Date.now() - lastRunTime
+    let elapsedTime = Date.now() - lastRunTime;
     if (elapsedTime < interval) {
-      return
+      return;
     }
 
-    let context = this
-    lastRunTime = Date.now()
-    fn.apply(context, args)
-  }
+    let context = this;
+    lastRunTime = Date.now();
+    fn.apply(context, args);
+  };
 }
 
 /**
@@ -31,78 +31,81 @@ export function throttle(fn, interval) {
  * delay: 消抖时长
  * isImmediateCall: 是否在一组操作中的第一次调用时立即执行fn
  * isRememberLastCall:是否在一组中最后一次调用后等delay时长再执行fn
- * 
+ *
  * 如果isRememberLastCall为false,意味着fn不会被延迟执行,所以fnDebounced执行时,要么在内部调用fn,同步返回fn返回值;要么内部决定本次不调用fn,同步返回null。
  * 如果isRememberLastCall为true,意味着fn可能被延迟执行,所以fnDebounced会返回一个Promise,在fn被调用时用其返回值resolve该Promise,或者在fn的延时调用计划被取消时用'canceled'resolve该Promise。(不宜reject,否则又没有人去catch,会导致浏览器报错。)
  */
 export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLastCall = true) {
-  console.assert(isImmediateCall || isRememberLastCall, 'isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!')
-  let timer = null
-  let retPromiseLastTimeResolver = null
+  console.assert(isImmediateCall || isRememberLastCall, "isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!");
+  let timer = null;
+  let retPromiseLastTimeResolver = null;
   // 上次调用的时刻
-  let lastCallTime = 0
+  let lastCallTime = 0;
 
   if (isImmediateCall && !isRememberLastCall) {
     return function (...args) {
-      let ret = null
-      const currentTime = Date.now()
+      let ret = null;
+      const currentTime = Date.now();
       if (currentTime - lastCallTime >= delay) {
-        ret = fn.apply(this, args)
+        ret = fn.apply(this, args);
       }
-      lastCallTime = currentTime
-      return ret
-    }
+      lastCallTime = currentTime;
+      return ret;
+    };
   } else if (!isImmediateCall && isRememberLastCall) {
     return function (...args) {
       if (timer) {
-        clearTimeout(timer)
-        timer = null
+        clearTimeout(timer);
+        timer = null;
       }
       if (retPromiseLastTimeResolver) {
-        retPromiseLastTimeResolver('canceled')
-        retPromiseLastTimeResolver = null
+        retPromiseLastTimeResolver("canceled");
+        retPromiseLastTimeResolver = null;
       }
       const ret = new Promise((resolve, reject) => {
-        retPromiseLastTimeResolver = resolve
+        retPromiseLastTimeResolver = resolve;
         timer = setTimeout(() => {
-          timer = null
-          retPromiseLastTimeResolver = null
-          resolve(fn.apply(this, args))
-        }, delay)
-      })
-      return ret
-    }
+          timer = null;
+          retPromiseLastTimeResolver = null;
+          resolve(fn.apply(this, args));
+        }, delay);
+      });
+      return ret;
+    };
   } else if (isImmediateCall && isRememberLastCall) {
     return function (...args) {
-      const currentTime = Date.now()
-      if (currentTime - lastCallTime >= delay) { // 一组操作中的第一次
-        const res = fn.apply(this, args)
-        lastCallTime = currentTime
-        return Promise.resolve(res)
-      } else { // 一组中的后续调用
-        if (timer) { // 在此之前存在中间调用
-          lastCallTime = currentTime
-          clearTimeout(timer)
-          timer = null
+      const currentTime = Date.now();
+      if (currentTime - lastCallTime >= delay) {
+        // 一组操作中的第一次
+        const res = fn.apply(this, args);
+        lastCallTime = currentTime;
+        return Promise.resolve(res);
+      } else {
+        // 一组中的后续调用
+        if (timer) {
+          // 在此之前存在中间调用
+          lastCallTime = currentTime;
+          clearTimeout(timer);
+          timer = null;
         }
         if (retPromiseLastTimeResolver) {
-          retPromiseLastTimeResolver('canceled')
-          retPromiseLastTimeResolver = null
+          retPromiseLastTimeResolver("canceled");
+          retPromiseLastTimeResolver = null;
         }
         const ret = new Promise((resolve, reject) => {
-          retPromiseLastTimeResolver = resolve
+          retPromiseLastTimeResolver = resolve;
           timer = setTimeout(() => {
-            lastCallTime = 0
-            timer = null
-            retPromiseLastTimeResolver = null
-            resolve(fn.apply(this, args))
-          }, delay)
-        })
-        return ret
+            lastCallTime = 0;
+            timer = null;
+            retPromiseLastTimeResolver = null;
+            resolve(fn.apply(this, args));
+          }, delay);
+        });
+        return ret;
       }
-    }
+    };
   } else {
-    console.error('不应该执行到这里!')
+    console.error("不应该执行到这里!");
   }
 }
 
@@ -123,116 +126,116 @@ export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLas
 ④12345678
  */
 export function isValidPhoneNumber(value) {
-  const reg = /^1\d{10}$|^400[0-9]{7}|^(0\d{2,3}-?|\(0\d{2,3}\))?[1-9]\d{4,7}(-\d{1,8})?$/
+  const reg = /^1\d{10}$|^400[0-9]{7}|^(0\d{2,3}-?|\(0\d{2,3}\))?[1-9]\d{4,7}(-\d{1,8})?$/;
   // const reg = /^400[0-9]{7}|^1[34578]\d{9}$|^0[0-9]{2,3}-[0-9]{8}/
 
-  return reg.test(value)
+  return reg.test(value);
 }
 
 // 深拷贝,为了解决循环引用和共同引用的问题,引入了WeakMap,又因为引入WeakMap可能会导致被拷贝对象被挂上【作为WeakMap的探针的】匿名函数(是pollyfill的行为吧?),所以不会拷贝非根元素的匿名函数。
 export function deepClone(target, hash = new WeakMap()) {
   // 定义一个变量
-  let result = null
+  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)
+  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)) {
+        if (!(typeof target[i] === "function" && !target.name)) {
           // 递归克隆数组中的每一项
-          result.push(deepClone(target[i], hash))
+          result.push(deepClone(target[i], hash));
         }
       }
       // 判断如果当前的值是null的话;直接赋值为null
     } else if (target === null) {
-      result = null
+      result = null;
       // 判断如果当前的值是一个RegExp对象的话,直接赋值
     } else if (target.constructor === RegExp) {
-      result = target
+      result = target;
     } else {
       // 否则是普通对象,直接for in循环,递归赋值对象的所有值
-      result = {}
-      hash.set(target, result)
+      result = {};
+      hash.set(target, result);
       for (let i in target) {
-        if (!(typeof (target[i]) === 'function' && !target.name)) {
-          result[i] = deepClone(target[i], hash)
+        if (!(typeof target[i] === "function" && !target.name)) {
+          result[i] = deepClone(target[i], hash);
         }
       }
     }
-  } else if (typeof target === 'function') {
-    result = target
-  } else { // 如果不是对象也不是函数,直接赋值
-    result = target
+  } else if (typeof target === "function") {
+    result = target;
+  } else {
+    // 如果不是对象也不是函数,直接赋值
+    result = target;
   }
   // 返回最终结果
-  return result
+  return result;
 }
 
 export function isObjectBroad(p) {
-  return typeof (p) === 'object' || typeof (p) === 'function'
+  return typeof p === "object" || typeof p === "function";
 }
 
 // 判断两个对象内容是否相同。未考虑循环引用、共同引用的情况。
 export function isSameObject(object1, object2) {
-  const keys1 = Object.keys(object1)
-  const keys2 = Object.keys(object2)
+  const keys1 = Object.keys(object1);
+  const keys2 = Object.keys(object2);
 
   if (keys1.length !== keys2.length) {
-    return false
+    return false;
   }
 
   for (let index = 0; index < keys1.length; index++) {
-    const val1 = object1[keys1[index]]
-    const val2 = object2[keys2[index]]
-    const areObjects = isObjectBroad(val1) && isObjectBroad(val2)
-    if (
-      (areObjects && !isSameObject(val1, val2)) ||
-      (!areObjects && (val1 !== val2))
-    ) {
-      return false
+    const val1 = object1[keys1[index]];
+    const val2 = object2[keys2[index]];
+    const areObjects = isObjectBroad(val1) && isObjectBroad(val2);
+    if ((areObjects && !isSameObject(val1, val2)) || (!areObjects && val1 !== val2)) {
+      return false;
     }
   }
-  return true
+  return true;
 }
 
 export function ossImagePreviewUrlSuffix(downScaleRate = 10) {
-  return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`
+  return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`;
 }
 
 export function postOrderTraversal(root, routine) {
   if (root.children && Array.isArray(root.children)) {
     for (const child of root.children) {
-      postOrderTraversal(child, routine)
+      postOrderTraversal(child, routine);
     }
   }
-  routine(root)
+  routine(root);
 }
 
 export function preOrderTraversalSearch(root, checkNode, targetNodePath) {
-  console.assert(root && checkNode && targetNodePath, `param invalid: ${root} ${checkNode} ${targetNodePath}`)
-  targetNodePath.push(root)
-  let rootCheckRes = checkNode(root)
+  console.assert(root && checkNode && targetNodePath, `param invalid: ${root} ${checkNode} ${targetNodePath}`);
+  targetNodePath.push(root);
+  let rootCheckRes = checkNode(root);
   if (rootCheckRes) {
-    return true
+    return true;
   }
   if (root.children && Array.isArray(root.children)) {
     for (const child of root.children) {
-      let childCheckRes = preOrderTraversalSearch(child, checkNode, targetNodePath)
+      let childCheckRes = preOrderTraversalSearch(child, checkNode, targetNodePath);
       if (childCheckRes) {
-        return true
+        return true;
       }
     }
   }
-  targetNodePath.pop()
-  return false
+  targetNodePath.pop();
+  return false;
 }
 export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
-  nodeIdList = nodeIdList.filter(i => i);
+  nodeIdList = nodeIdList.filter((i) => i);
   if (nodeIdList.length === 0) {
-    return null
+    return null;
   }
 
   // console.log('nodeIdList', nodeIdList)
@@ -242,27 +245,115 @@ export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
     {
       id: nodeTree.id,
       name: nodeTree.name,
-    }
-  ]
+    },
+  ];
   if (nodeIdList[1] || nodeIdList[1] === 0) {
     // console.assert(nodeTree.children && nodeTree.children.length > 0, 'nodeIdList2nodeInfoListByNodeTree: 不可能的任务2!')
     const nextLevelRoot = nodeTree.children.find((item) => {
-      return item.id === nodeIdList[1]
-    })
+      return item.id === nodeIdList[1];
+    });
     // console.log('nextLevelRoot', nextLevelRoot)
     // console.assert(nextLevelRoot, 'nodeIdList2nodeInfoListByNodeTree: invalid param 2!')
-    ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot))
+    ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot));
   }
 
-  return ret
+  return ret;
 }
 
 export function capitalize(str) {
   if (!str) {
-    return
+    return;
   }
   if (str.length === 1) {
-    return str[0].toUpperCase()
+    return str[0].toUpperCase();
+  }
+  return str[0].toUpperCase() + str.slice(1, str.length);
+}
+
+export function getRandomSid() {
+  //5-7位随机字符串 + 6位时间    为热点准备
+  var pre = randomWord(true, 5, 7);
+  var post = new Date().getTime() + "";
+  var len = post.length;
+  post = post.substring(len - 8, len - 5) + post.substring(len - 3, len); //其实还是有可能重复的....
+  return pre + post;
+}
+function randomWord(randomFlag, min, max) {
+  //随机字符串
+  var str = "",
+    range = min,
+    arr = [
+      "0",
+      "1",
+      "2",
+      "3",
+      "4",
+      "5",
+      "6",
+      "7",
+      "8",
+      "9",
+      "a",
+      "b",
+      "c",
+      "d",
+      "e",
+      "f",
+      "g",
+      "h",
+      "i",
+      "j",
+      "k",
+      "l",
+      "m",
+      "n",
+      "o",
+      "p",
+      "q",
+      "r",
+      "s",
+      "t",
+      "u",
+      "v",
+      "w",
+      "x",
+      "y",
+      "z",
+      "A",
+      "B",
+      "C",
+      "D",
+      "E",
+      "F",
+      "G",
+      "H",
+      "I",
+      "J",
+      "K",
+      "L",
+      "M",
+      "N",
+      "O",
+      "P",
+      "Q",
+      "R",
+      "S",
+      "T",
+      "U",
+      "V",
+      "W",
+      "X",
+      "Y",
+      "Z",
+    ];
+
+  if (randomFlag) {
+    // 随机长度
+    range = Math.round(Math.random() * (max - min)) + min;
   }
-  return str[0].toUpperCase() + str.slice(1, str.length)
-}
+  for (var i = 0; i < range; i++) {
+    var pos = Math.round(Math.random() * (arr.length - 1));
+    str += arr[pos];
+  }
+  return str;
+}

+ 30 - 15
packages/qjkankan-editor/src/utils/request.js

@@ -70,6 +70,10 @@ const statusCode = {
 let __showNetworkError = false;
 
 export const showLoginTips = () => {
+  //移除登录状态token
+  if (localStorage.getItem("token")) {
+    localStorage.removeItem("token");
+  }
   // 防止多次请求弹出
   if (showLoginTips.__is_show) {
     return;
@@ -94,10 +98,7 @@ export const showLoginTips = () => {
           postQueue = [];
           LoginDetector.valid();
           location.reload();
-        } else if (
-          response.code === statusCode.FAILURE_CODE_5001 ||
-          response.code === statusCode.FAILURE_CODE_5002
-        ) {
+        } else if (response.code === statusCode.FAILURE_CODE_5001 || response.code === statusCode.FAILURE_CODE_5002) {
           showLoginTips();
         }
       });
@@ -179,10 +180,7 @@ export function statusCodesHandler(result, callback) {
     return;
   }
 
-  if (
-    result.code == statusCode.FAILURE_CODE_5001 ||
-    result.code === statusCode.FAILURE_CODE_5002
-  ) {
+  if (result.code == statusCode.FAILURE_CODE_5001 || result.code === statusCode.FAILURE_CODE_5002) {
     callback(result.code);
     showLoginTips();
     return false;
@@ -261,10 +259,7 @@ export const http = {
       if (typeof result.code !== "undefined") {
         const flag = statusCodesHandler(result, function (code) {
           // 需要登录的状态
-          if (
-            code == statusCode.FAILURE_CODE_5001 ||
-            code === statusCode.FAILURE_CODE_5002
-          ) {
+          if (code == statusCode.FAILURE_CODE_5001 || code === statusCode.FAILURE_CODE_5002) {
             postQueue.push(function () {
               http[method](url, data, done, fail);
             });
@@ -406,6 +401,17 @@ export const http = {
       fail
     );
   },
+
+  /**
+   * delete请求
+   * @param {String} url 请求地址
+   * @param {Object?} data 请求参数
+   * @param {Function?} done 成功回调
+   * @param {Function?} fail 失败回调
+   */
+  delete(url, data = {}, done, fail) {
+    return this.__request($.delete(url, data), "delete", url, data, done, fail);
+  },
   postJsonWithHeader(url, data = {}, headers, done, fail) {
     return this.__request(
       $.ajax({
@@ -496,9 +502,7 @@ export const http = {
     };
 
     img.onerror = function () {
-      retry > 0
-        ? setTimeout(() => load(), 1e3)
-        : def.reject(`[${url}]${i18n.t("tips_code.loading_fail")}`);
+      retry > 0 ? setTimeout(() => load(), 1e3) : def.reject(`[${url}]${i18n.t("tips_code.loading_fail")}`);
     };
 
     img.onload = function () {
@@ -551,4 +555,15 @@ export const http = {
     }
     return this.postForm(url, form, done, fail);
   },
+  postFormData(url, data = {}, done, fail) {
+    const form = new FormData();
+    for (let key in data) {
+      if (key === "file") {
+        form.append("file", base64ToBlob(data.file), data.filename);
+      } else if (key != "filename") {
+        form.append(key, data[key]);
+      }
+    }
+    return this.postForm(url, form, done, fail);
+  },
 };

+ 32 - 72
packages/qjkankan-editor/src/views/base/Toolbar.vue

@@ -9,24 +9,13 @@
         <div class="upload-con">
           <div class="uc-l">
             <div class="preview">
-              <img
-                :src="
-                  info.icon ||
-                  require('@/assets/images/default/img_cover_default_2.png')
-                "
-                alt=""
-              />
-              <button
-                class="ui-button submit setting-cover-btn"
-                @click="onClickSettingCover"
-              >
+              <img :src="info.work.icon || require('@/assets/images/default/img_cover_default_2.png')" alt="" />
+
+              <button class="ui-button submit setting-cover-btn" @click="onClickSettingCover">
                 {{ $i18n.t(`edit_settings.setting_cover`) }}
               </button>
             </div>
-            <div
-              class="ui-remark"
-              v-html="$i18n.t(`edit_settings.cover_size`)"
-            ></div>
+            <div class="ui-remark" v-html="$i18n.t(`edit_settings.cover_size`)"></div>
           </div>
           <div class="uc-r">
             <div class="ui-title">
@@ -34,7 +23,7 @@
             </div>
             <div class="title-input-wrapper">
               <input
-                v-model.trim="info.name"
+                v-model.trim="info.work.name"
                 @input="emojistr"
                 @keydown.enter="onTitleInputEnter"
                 type="textarea"
@@ -55,13 +44,7 @@
               type="text"
             /> -->
 
-              <editor
-                ref="editor"
-                :html="info.description"
-                :placeholder="$i18n.t(`edit_settings.intro_placeholder`)"
-                :maxlength="2000"
-                @change="onEditorChange"
-              ></editor>
+              <editor ref="editor" :html="info.work.description" :placeholder="$i18n.t(`edit_settings.intro_placeholder`)" :maxlength="2000" @change="onEditorChange"></editor>
 
               <span class="count">{{ introLength }}/2000</span>
             </div>
@@ -70,53 +53,29 @@
       </div>
       <div class="bottom-container">
         <menu>
-          <li
-            v-for="item in tabs"
-            :key="item"
-            :class="{ active: activeTab === item }"
-            @click="activeTab = item"
-          >
+          <li v-for="item in tabs" :key="item" :class="{ active: activeTab === item }" @click="activeTab = item">
             {{ $i18n.t(`baseSetting.${item}`) }}
           </li>
         </menu>
 
         <div class="settings-view-wrapper">
-          <OpeningTipSettings
-            v-show="activeTab === 'openTips'"
-          ></OpeningTipSettings>
-          <OpeningAnimationSettings
-            v-show="activeTab === 'openAnimate'"
-          ></OpeningAnimationSettings>
-          <PasswordSettings
-            v-show="activeTab === 'password'"
-          ></PasswordSettings>
-          <AutoCruiseSettings
-            v-show="activeTab === 'cruise'"
-          ></AutoCruiseSettings>
-          <BackgroundMusicSettings
-            v-show="activeTab === 'bgm'"
-          ></BackgroundMusicSettings>
-          <CustomLogoSettings
-            v-show="activeTab === 'logo'"
-          ></CustomLogoSettings>
+          <OpeningTipSettings v-show="activeTab === 'openTips'"></OpeningTipSettings>
+          <OpeningAnimationSettings v-show="activeTab === 'openAnimate'"></OpeningAnimationSettings>
+          <PasswordSettings v-show="activeTab === 'password'"></PasswordSettings>
+          <AutoCruiseSettings v-show="activeTab === 'cruise'"></AutoCruiseSettings>
+          <BackgroundMusicSettings v-show="activeTab === 'bgm'"></BackgroundMusicSettings>
+          <CustomLogoSettings v-show="activeTab === 'logo'"></CustomLogoSettings>
           <!-- <CustomMaskSettings
             v-show="activeTab === 'customCover'"
           ></CustomMaskSettings> -->
-          <CustomButtonSettings
-            v-show="activeTab === 'customButton'"
-          ></CustomButtonSettings>
+          <CustomButtonSettings v-show="activeTab === 'customButton'"></CustomButtonSettings>
           <CoverBase v-show="activeTab === 'openCover'"></CoverBase>
         </div>
       </div>
     </div>
 
     <div class="dialog" style="z-index: 2000" v-if="isShowSettingCoverWindow">
-      <MaterialSelector
-        :selectableType="['image', 'pano', '3D']"
-        :title="$i18n.t(`gather.select_material`)"
-        @cancel="isShowSettingCoverWindow = false"
-        @submit="onCoverSelected"
-      />
+      <MaterialSelector :selectableType="['image', 'pano', '3D']" :title="$i18n.t(`gather.select_material`)" @cancel="isShowSettingCoverWindow = false" @submit="onCoverSelected" />
     </div>
   </div>
 </template>
@@ -134,6 +93,7 @@ import CustomMaskSettings from "@/views/base/customMaskSettings.vue";
 import CustomButtonSettings from "@/views/base/customButtonSettings.vue";
 import Editor from "@/components/shared/Editor";
 import CoverBase from "@/views/base/coverBase.vue";
+import { deepClone, isSameObject } from "@/utils/other.js";
 
 export default {
   components: {
@@ -181,29 +141,29 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      info: "base/baseInfo",
     }),
     titleLength() {
-      return this.info.name.length || "0";
+      return this.info.work.name.length || "0";
     },
   },
   mounted() {},
-  watch: {},
+  watch: {
+  
+  },
   methods: {
     emojistr() {
-      this.info.name = this.info.name.replace(
-        /(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/g,
-        function (char) {
-          if (char.length === 2) {
-            return "";
-          } else {
-            return char;
-          }
+      this.info.work.name = this.info.work.name.replace(/(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/g, function (char) {
+        if (char.length === 2) {
+          return "";
+        } else {
+          return char;
         }
-      );
+      });
     },
     onEditorChange(content) {
-      this.info.description = content.html;
+      this.info.work.description = content.html;
       this.introLength = content.size;
     },
     onClickSettingCover() {
@@ -211,11 +171,11 @@ export default {
     },
     onCoverSelected(selected) {
       if (selected[0].materialType === "image") {
-        this.info.icon = selected[0].icon;
+        this.info.work.icon = selected[0].icon;
       } else if (selected[0].materialType === "pano") {
-        this.info.icon = selected[0].icon;
+        this.info.work.icon = selected[0].icon;
       } else if (selected[0].materialType === "3D") {
-        this.info.icon = selected[0].thumb;
+        this.info.work.icon = selected[0].thumb;
       }
       this.isShowSettingCoverWindow = false;
     },

+ 4 - 3
packages/qjkankan-editor/src/views/base/autoCruiseSettings.vue

@@ -4,7 +4,7 @@
     <br>
     <div class="switch-wrapper">
       <span class="label">{{enter_auto}}</span>
-      <Switcher :value="info.isAuto" @change="onSwitcherChange"></Switcher>
+      <Switcher :value="info.work.isAuto" @change="onSwitcherChange"></Switcher>
     </div>
   </div>
 </template>
@@ -30,12 +30,13 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info:'info'
+      // info:'info'
+      info:'base/baseInfo'
     })
   },
   methods: {
     onSwitcherChange(data) {
-      this.info.isAuto = data
+      this.info.work.isAuto = data
     }
   }
 }

+ 41 - 47
packages/qjkankan-editor/src/views/base/backgroundMusicSettings.vue

@@ -1,31 +1,25 @@
 <template>
   <div class="background-music-settings">
-    <span class="title">{{set_bgm}}</span>
-    <br>
-    <button v-if="!(info && info.backgroundMusic && info.backgroundMusic.name)" @click="onClickMusicSelectionBtn">
+    <span class="title">{{ set_bgm }}</span>
+    <br />
+    <button v-if="!(info && info.workBackgroundMusic && info.workBackgroundMusic.name)" @click="onClickMusicSelectionBtn">
       <i class="iconfont icon-editor_add"></i>
-      {{add_audio}}
+      {{ add_audio }}
     </button>
     <template v-else>
       <div class="music-display" @click.self="onClickCurrentMusic">
-        <Audio ref="my-audio" class="audio-control" :backgroundColor="'#1A1B1D'" :myAudioUrl="info.backgroundMusic.ossPath"></Audio>
-        <div class="name" v-title="info.backgroundMusic.name" @click="onClickCurrentMusic">{{info.backgroundMusic.name}}</div>
+        <Audio ref="my-audio" class="audio-control" :backgroundColor="'#1A1B1D'" :myAudioUrl="info.workBackgroundMusic.ossPath"></Audio>
+        <div class="name" v-title="info.workBackgroundMusic.name" @click="onClickCurrentMusic">{{ info.workBackgroundMusic.name }}</div>
         <i class="iconfont icon-editor_list_delete" @click.stop="onClickDeleteMusicBtn"></i>
       </div>
       <button @click="onClickMusicSelectionBtn">
         <i class="iconfont icon-editor_update"></i>
-        {{change_audio}}
+        {{ change_audio }}
       </button>
     </template>
 
     <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
-      <MaterialSelector
-        :title="select_material"
-        @cancel="isShowSelectionWindow = false"
-        @submit="handleSubmitFromMaterialSelector"
-        :selectableType="['audio']"
-        initialMaterialType="audio"
-      />
+      <MaterialSelector :title="select_material" @cancel="isShowSelectionWindow = false" @submit="handleSubmitFromMaterialSelector" :selectableType="['audio']" initialMaterialType="audio" />
     </div>
   </div>
 </template>
@@ -34,7 +28,7 @@
 import { mapGetters } from "vuex";
 import MaterialSelector from "@/components/materialSelector.vue";
 import Audio from "@/components/audio/audioButton.vue";
-import {i18n} from "@/lang"
+import { i18n } from "@/lang";
 
 export default {
   components: {
@@ -43,49 +37,49 @@ export default {
   },
   data() {
     return {
-      set_bgm:i18n.t("edit_settings.set_bgm"),
-      add_audio:i18n.t("edit_settings.add_audio"),
-      change_audio:i18n.t("edit_settings.change_audio"),
-      select_material:i18n.t("gather.select_material"),
+      set_bgm: i18n.t("edit_settings.set_bgm"),
+      add_audio: i18n.t("edit_settings.add_audio"),
+      change_audio: i18n.t("edit_settings.change_audio"),
+      select_material: i18n.t("gather.select_material"),
       isShowSelectionWindow: false,
-    }
+    };
   },
   computed: {
     ...mapGetters({
-      info:'info'
+      // info:'info'
+      info: "base/baseInfo",
     }),
   },
   methods: {
     onClickMusicSelectionBtn() {
-      this.isShowSelectionWindow = true
+      this.isShowSelectionWindow = true;
     },
     onClickDeleteMusicBtn() {
-      this.info.backgroundMusic.id = null
-      this.info.backgroundMusic.name = null
-      this.info.backgroundMusic.ossPath = null
+      this.info.workBackgroundMusic = null;
+      // this.info.workBackgroundMusic.id = null
+      // this.info.workBackgroundMusic.fodderId = null
+      // this.info.workBackgroundMusic.name = null
+      // this.info.workBackgroundMusic.ossPath = null
     },
     handleSubmitFromMaterialSelector(selected) {
-      this.isShowSelectionWindow = false
-      this.info.backgroundMusic.id = selected[0].id
-      this.info.backgroundMusic.name = selected[0].name
-      this.info.backgroundMusic.ossPath = selected[0].ossPath
+      this.isShowSelectionWindow = false;
+      this.info.workBackgroundMusic = {};
+      this.info.workBackgroundMusic.fodderId = selected[0].id;
+      this.info.workBackgroundMusic.name = selected[0].name;
+      this.info.workBackgroundMusic.ossPath = selected[0].ossPath;
     },
     onClickCurrentMusic() {
-      if (this.$refs['my-audio']) {
-        this.$refs['my-audio'].switchPlayPause()
+      if (this.$refs["my-audio"]) {
+        this.$refs["my-audio"].switchPlayPause();
       }
-    }
+    },
   },
   mounted() {
-    if (!this.info.backgroundMusic) {
-      this.info.backgroundMusic = {
-        id: '',
-        name: '',
-        ossPath: '',
-      }
+    if (!this.info.workBackgroundMusic) {
+      this.info.workBackgroundMusic = null;
     }
-  }
-}
+  },
+};
 </script>
 
 <style lang="less" scoped>
@@ -95,17 +89,17 @@ export default {
   height: 546px;
   > .title {
     font-size: 18px;
-    color: #FFFFFF;
+    color: #ffffff;
   }
   > button {
     margin-top: 16px;
     width: 234px;
     height: 40px;
-    background: #1A1B1D;
+    background: #1a1b1d;
     border-radius: 2px;
     border: 1px solid #404040;
     display: block;
-    color: #0076F6;
+    color: #0076f6;
     font-size: 14px;
     cursor: pointer;
     &:hover {
@@ -120,7 +114,7 @@ export default {
     margin-top: 16px;
     width: 234px;
     height: 40px;
-    background: #1A1B1D;
+    background: #1a1b1d;
     border-radius: 2px;
     border: 1px solid #404040;
     color: #fff;
@@ -128,7 +122,7 @@ export default {
     position: relative;
     text-align: center;
     &:hover {
-      color: #0076F6;
+      color: #0076f6;
       border-color: @color;
       > .audio-control {
         display: inline-block;
@@ -162,9 +156,9 @@ export default {
       transform: translateY(-50%);
       right: 18px;
       &:hover {
-        color: #FA5555;
+        color: #fa5555;
       }
     }
   }
 }
-</style>
+</style>

+ 113 - 320
packages/qjkankan-editor/src/views/base/coverBase.vue

@@ -65,84 +65,46 @@ pc端视频位置 videoPcLoc
 	before:开场图片前
 	later:开场图片后
  -->
-  <div class="coverBase" v-if="info.coverInfo">
+  <div class="coverBase" v-if="info.workCoverType">
     <!-- 顶上标题 -->
     <span class="title">{{ coverBase_button }}</span>
-    <i
-      class="iconfont icon-help_i tool-tip-for-editor"
-      v-tooltip="coverBase_button_tips"
-    >
-    </i>
+    <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="coverBase_button_tips"> </i>
     <br />
     <div class="switch-wrapper">
       <span class="label">{{ $i18n.t("edit_settings.cover_show") }}</span>
-      <Switcher
-        :value="info.coverInfo.isShowCover"
-        @change="onSwitcherChange"
-      ></Switcher>
+      <Switcher :value="info.workCoverType.isShowCover" @change="onSwitcherChange"></Switcher>
     </div>
     <div class="cover_pull">
       <!-- 封面类型选择下拉框 -->
       <div class="cover_pull_tit">{{ cover_pull_tit }}</div>
 
-      <Select
-        v-model="info.coverInfo.coverSelect"
-        class="base-select"
-        placement="bottom"
-      >
-        <Option
-          v-for="item in selectList"
-          :key="item.type"
-          :label="item.txt"
-          :value="item.type"
-        >
-        </Option>
+      <Select v-model="info.workCoverType.coverSelect" class="base-select" placement="bottom">
+        <Option v-for="item in selectList" :key="item.type" :label="item.txt" :value="item.type"> </Option>
       </Select>
-      <!-- {{ info.coverInfo.coverSelect }} -->
+      <!-- {{ info.workCoverType.coverSelect }} -->
       <!-- 关于图片 -->
-      <div class="mainImgBox" v-show="info.coverInfo.coverSelect !== 'video'">
+      <div class="mainImgBox" v-show="info.workCoverType.coverSelect !== 'video'">
         <div class="coverImgTit">{{ coverImgTit }}</div>
         <div class="coverImgBox">
           <!-- pc图片 -->
           <div class="coverImgBox_ll">
             <div class="tit">{{ $i18n.t(`edit_settings.pc`) }}</div>
             <div class="coverImgMain">
-              <SelectedImage
-                :imgSrc="info.coverInfo.coverPc"
-                :defaultImgSrc="require('@/assets/img/coverUpTit1.png')"
-                @cancel="onCancelPcTip"
-              ></SelectedImage>
+              <SelectedImage :imgSrc="info.workCoverType.coverPc" :defaultImgSrc="require('@/assets/img/coverUpTit1.png')" @cancel="onCancelPcTip"></SelectedImage>
               <div class="imgRight">
                 <!-- 上传图片按钮 -->
-                <button
-                  class="ui-button submit"
-                  @click="
-                    (isShowSelectionWindow = true),
-                      (selectingFor = 'pc'),
-                      (upType = 'image'),
-                      (upTypeSta = 'imgBac')
-                  "
-                >
+                <button class="ui-button submit" @click="(isShowSelectionWindow = true), (selectingFor = 'pc'), (upType = 'image'), (upTypeSta = 'imgBac')">
                   {{ $i18n.t(`edit_settings.select_image`) }}
                 </button>
                 <!-- 上传提示 -->
-                <div
-                  class="ui-remark"
-                  v-html="$i18n.t(`edit_settings.coverUpTit1`)"
-                ></div>
+                <div class="ui-remark" v-html="$i18n.t(`edit_settings.coverUpTit1`)"></div>
                 <!-- 选择居中和全屏 -->
                 <div class="coverImglocBox">
-                  <div
-                    :class="{ active: info.coverInfo.coverPcLoc === 'center' }"
-                    @click="info.coverInfo.coverPcLoc = 'center'"
-                  >
+                  <div :class="{ active: info.workCoverType.coverPcLoc === 'center' }" @click="info.workCoverType.coverPcLoc = 'center'">
                     <div class="inco"></div>
                     <div class="txt">{{ coverImgLoc1 }}</div>
                   </div>
-                  <div
-                    :class="{ active: info.coverInfo.coverPcLoc === 'full' }"
-                    @click="info.coverInfo.coverPcLoc = 'full'"
-                  >
+                  <div :class="{ active: info.workCoverType.coverPcLoc === 'full' }" @click="info.workCoverType.coverPcLoc = 'full'">
                     <div class="inco inco2"></div>
                     <div class="txt">{{ coverImgLoc2 }}</div>
                   </div>
@@ -154,42 +116,21 @@ pc端视频位置 videoPcLoc
             <div class="tit">{{ $i18n.t(`edit_settings.mobile`) }}</div>
             <!-- 移动端图片 -->
             <div class="coverImgMain">
-              <SelectedImage
-                :imgSrc="info.coverInfo.coverMo"
-                :defaultImgSrc="require('@/assets/img/coverUpTit1.png')"
-                @cancel="onCancelAppTip"
-              ></SelectedImage>
+              <SelectedImage :imgSrc="info.workCoverType.coverMo" :defaultImgSrc="require('@/assets/img/coverUpTit1.png')" @cancel="onCancelAppTip"></SelectedImage>
               <div class="imgRight">
                 <!-- 上传图片按钮 -->
-                <button
-                  class="ui-button submit"
-                  @click="
-                    (isShowSelectionWindow = true),
-                      (selectingFor = 'mobile'),
-                      (upType = 'image'),
-                      (upTypeSta = 'imgBac')
-                  "
-                >
+                <button class="ui-button submit" @click="(isShowSelectionWindow = true), (selectingFor = 'mobile'), (upType = 'image'), (upTypeSta = 'imgBac')">
                   {{ $i18n.t(`edit_settings.select_image`) }}
                 </button>
                 <!-- 上传提示 -->
-                <div
-                  class="ui-remark"
-                  v-html="$i18n.t(`edit_settings.coverUpTit2`)"
-                ></div>
+                <div class="ui-remark" v-html="$i18n.t(`edit_settings.coverUpTit2`)"></div>
                 <!-- 选择居中和全屏 -->
                 <div class="coverImglocBox">
-                  <div
-                    :class="{ active: info.coverInfo.coverMoLoc === 'center' }"
-                    @click="info.coverInfo.coverMoLoc = 'center'"
-                  >
+                  <div :class="{ active: info.workCoverType.coverMoLoc === 'center' }" @click="info.workCoverType.coverMoLoc = 'center'">
                     <div class="inco"></div>
                     <div class="txt">{{ coverImgLoc1 }}</div>
                   </div>
-                  <div
-                    :class="{ active: info.coverInfo.coverMoLoc === 'full' }"
-                    @click="info.coverInfo.coverMoLoc = 'full'"
-                  >
+                  <div :class="{ active: info.workCoverType.coverMoLoc === 'full' }" @click="info.workCoverType.coverMoLoc = 'full'">
                     <div class="inco inco2"></div>
                     <div class="txt">{{ coverImgLoc2 }}</div>
                   </div>
@@ -202,63 +143,24 @@ pc端视频位置 videoPcLoc
         <!-- 图片背景设置 -->
         <div class="coverImgBacBox">
           <div class="tit">{{ $i18n.t(`edit_settings.coverImgBacTit`) }}</div>
-          <Select
-            v-model="info.coverInfo.coverImgBac"
-            class="base-select"
-            placement="bottom"
-          >
-            <Option
-              v-for="item in coverImgBacList"
-              :key="item.type"
-              :label="item.txt"
-              :value="item.type"
-            >
-            </Option>
+          <Select v-model="info.workCoverType.coverImgBac" class="base-select" placement="bottom">
+            <Option v-for="item in coverImgBacList" :key="item.type" :label="item.txt" :value="item.type"> </Option>
           </Select>
           <!-- 选择颜色 -->
-          <div
-            class="imgColorSelec"
-            v-show="info.coverInfo.coverImgBac === 'colorFill'"
-          >
-            <div class="ll">{{ info.coverInfo.imgColorSelec }}</div>
-            <div
-              class="rr"
-              :style="`background-color: ${info.coverInfo.imgColorSelec};`"
-            ></div>
-            <ColorPicker
-              @change="imgColorSelecChange"
-              v-model="info.coverInfo.imgColorSelec"
-              :predefine="predefineColors"
-            >
-            </ColorPicker>
+          <div class="imgColorSelec" v-show="info.workCoverType.coverImgBac === 'colorFill'">
+            <div class="ll">{{ info.workCoverType.imgColorSelec }}</div>
+            <div class="rr" :style="`background-color: ${info.workCoverType.imgColorSelec};`"></div>
+            <ColorPicker @change="imgColorSelecChange" v-model="info.workCoverType.imgColorSelec" :predefine="predefineColors"> </ColorPicker>
           </div>
           <!-- 选择背景图片 -->
-          <div
-            class="imgClolrBacImg"
-            v-show="info.coverInfo.coverImgBac === 'imgTile'"
-          >
-            <SelectedImage
-              :imgSrc="info.coverInfo.coverBac"
-              :defaultImgSrc="require('@/assets/img/coverUpTit1.png')"
-              @cancel="onCancelBac"
-            ></SelectedImage>
+          <div class="imgClolrBacImg" v-show="info.workCoverType.coverImgBac === 'imgTile'">
+            <SelectedImage :imgSrc="info.workCoverType.coverBac" :defaultImgSrc="require('@/assets/img/coverUpTit1.png')" @cancel="onCancelBac"></SelectedImage>
             <!-- 上传图片按钮 -->
             <div class="imgRight">
-              <button
-                class="ui-button submit"
-                @click="
-                  (isShowSelectionWindow = true),
-                    (selectingFor = 'bac'),
-                    (upType = 'image'),
-                    (upTypeSta = 'imgBac')
-                "
-              >
+              <button class="ui-button submit" @click="(isShowSelectionWindow = true), (selectingFor = 'bac'), (upType = 'image'), (upTypeSta = 'imgBac')">
                 {{ $i18n.t(`edit_settings.select_image`) }}
               </button>
-              <div
-                class="ui-remark"
-                v-html="$i18n.t(`edit_settings.coverUpTit3`)"
-              ></div>
+              <div class="ui-remark" v-html="$i18n.t(`edit_settings.coverUpTit3`)"></div>
             </div>
           </div>
         </div>
@@ -272,16 +174,13 @@ pc端视频位置 videoPcLoc
             {{ $i18n.t(`edit_settings.coverImageInWayTit`) }}
           </div>
           <div class="VideoTitR">
-            <Switcher
-              :value="info.coverInfo.coverImageInWay"
-              @change="coverImageInWayChange"
-            ></Switcher>
+            <Switcher :value="info.workCoverType.coverImageInWay" @change="coverImageInWayChange"></Switcher>
           </div>
         </div>
       </div>
 
       <!-- 关于视频 -->
-      <div class="mainVideoBox" v-show="info.coverInfo.coverSelect !== 'img'">
+      <div class="mainVideoBox" v-show="info.workCoverType.coverSelect !== 'img'">
         <div class="coverImgTit">
           {{ $i18n.t(`edit_settings.coverVideoTit`) }}
         </div>
@@ -290,42 +189,21 @@ pc端视频位置 videoPcLoc
           <div class="coverImgBox_ll">
             <div class="tit">{{ $i18n.t(`edit_settings.pc`) }}</div>
             <div class="coverImgMain">
-              <SelectedImage
-                :imgSrc="info.coverInfo.videoPcIcon"
-                :defaultImgSrc="require('@/assets/img/coverUpTit2.png')"
-                @cancel="clearVideoPc"
-              ></SelectedImage>
+              <SelectedImage :imgSrc="info.workCoverType.videoPcIcon" :defaultImgSrc="require('@/assets/img/coverUpTit2.png')" @cancel="clearVideoPc"></SelectedImage>
               <div class="imgRight">
                 <!-- 上传视频按钮 -->
-                <button
-                  class="ui-button submit"
-                  @click="
-                    (isShowSelectionWindow = true),
-                      (selectingFor = 'pc'),
-                      (upType = 'video'),
-                      (upTypeSta = 'videoBac')
-                  "
-                >
+                <button class="ui-button submit" @click="(isShowSelectionWindow = true), (selectingFor = 'pc'), (upType = 'video'), (upTypeSta = 'videoBac')">
                   {{ $i18n.t(`edit_settings.select_video`) }}
                 </button>
                 <!-- 上传提示 -->
-                <div
-                  class="ui-remark"
-                  v-html="$i18n.t(`edit_settings.coverUpTit4`)"
-                ></div>
+                <div class="ui-remark" v-html="$i18n.t(`edit_settings.coverUpTit4`)"></div>
                 <!-- 选择居中和全屏 -->
                 <div class="coverImglocBox">
-                  <div
-                    :class="{ active: info.coverInfo.videoPcLoc === 'center' }"
-                    @click="info.coverInfo.videoPcLoc = 'center'"
-                  >
+                  <div :class="{ active: info.workCoverType.videoPcLoc === 'center' }" @click="info.workCoverType.videoPcLoc = 'center'">
                     <div class="inco"></div>
                     <div class="txt">{{ coverImgLoc1 }}</div>
                   </div>
-                  <div
-                    :class="{ active: info.coverInfo.videoPcLoc === 'full' }"
-                    @click="info.coverInfo.videoPcLoc = 'full'"
-                  >
+                  <div :class="{ active: info.workCoverType.videoPcLoc === 'full' }" @click="info.workCoverType.videoPcLoc = 'full'">
                     <div class="inco inco2"></div>
                     <div class="txt">{{ coverImgLoc2 }}</div>
                   </div>
@@ -337,42 +215,21 @@ pc端视频位置 videoPcLoc
             <div class="tit">{{ $i18n.t(`edit_settings.mobile`) }}</div>
             <!-- 移动端视频 -->
             <div class="coverImgMain">
-              <SelectedImage
-                :imgSrc="info.coverInfo.videoMoIcon"
-                :defaultImgSrc="require('@/assets/img/coverUpTit2.png')"
-                @cancel="clearVideoMo"
-              ></SelectedImage>
+              <SelectedImage :imgSrc="info.workCoverType.videoMoIcon" :defaultImgSrc="require('@/assets/img/coverUpTit2.png')" @cancel="clearVideoMo"></SelectedImage>
               <div class="imgRight">
                 <!-- 上传视频按钮 -->
-                <button
-                  class="ui-button submit"
-                  @click="
-                    (isShowSelectionWindow = true),
-                      (selectingFor = 'mobile'),
-                      (upType = 'video'),
-                      (upTypeSta = 'videoBac')
-                  "
-                >
+                <button class="ui-button submit" @click="(isShowSelectionWindow = true), (selectingFor = 'mobile'), (upType = 'video'), (upTypeSta = 'videoBac')">
                   {{ $i18n.t(`edit_settings.select_video`) }}
                 </button>
                 <!-- 上传提示 -->
-                <div
-                  class="ui-remark"
-                  v-html="$i18n.t(`edit_settings.coverUpTit5`)"
-                ></div>
+                <div class="ui-remark" v-html="$i18n.t(`edit_settings.coverUpTit5`)"></div>
                 <!-- 选择居中和全屏 -->
                 <div class="coverImglocBox">
-                  <div
-                    :class="{ active: info.coverInfo.videoMoLoc === 'center' }"
-                    @click="info.coverInfo.videoMoLoc = 'center'"
-                  >
+                  <div :class="{ active: info.workCoverType.videoMoLoc === 'center' }" @click="info.workCoverType.videoMoLoc = 'center'">
                     <div class="inco"></div>
                     <div class="txt">{{ coverImgLoc1 }}</div>
                   </div>
-                  <div
-                    :class="{ active: info.coverInfo.videoMoLoc === 'full' }"
-                    @click="info.coverInfo.videoMoLoc = 'full'"
-                  >
+                  <div :class="{ active: info.workCoverType.videoMoLoc === 'full' }" @click="info.workCoverType.videoMoLoc = 'full'">
                     <div class="inco inco2"></div>
                     <div class="txt">{{ coverImgLoc2 }}</div>
                   </div>
@@ -385,59 +242,24 @@ pc端视频位置 videoPcLoc
         <!-- 视频背景设置 -->
         <div class="coverImgBacBox">
           <div class="tit">{{ $i18n.t(`edit_settings.coverImgBacTit`) }}</div>
-          <Select v-model="info.coverInfo.coverVideoBac" class="base-select">
-            <Option
-              v-for="item in coverImgBacList"
-              :key="item.type"
-              :label="item.txt"
-              :value="item.type"
-            >
-            </Option>
+          <Select v-model="info.workCoverType.coverVideoBac" class="base-select">
+            <Option v-for="item in coverImgBacList" :key="item.type" :label="item.txt" :value="item.type"> </Option>
           </Select>
           <!-- 选择颜色 -->
-          <div
-            class="imgColorSelec"
-            v-show="info.coverInfo.coverVideoBac === 'colorFill'"
-          >
-            <div class="ll">{{ info.coverInfo.videoColorSelec }}</div>
-            <div
-              class="rr"
-              :style="`background-color: ${info.coverInfo.videoColorSelec};`"
-            ></div>
-            <ColorPicker
-              @change="videoColorSelecChange"
-              v-model="info.coverInfo.videoColorSelec"
-              :predefine="predefineColors"
-            >
-            </ColorPicker>
+          <div class="imgColorSelec" v-show="info.workCoverType.coverVideoBac === 'colorFill'">
+            <div class="ll">{{ info.workCoverType.videoColorSelec }}</div>
+            <div class="rr" :style="`background-color: ${info.workCoverType.videoColorSelec};`"></div>
+            <ColorPicker @change="videoColorSelecChange" v-model="info.workCoverType.videoColorSelec" :predefine="predefineColors"> </ColorPicker>
           </div>
           <!-- 选择背景图片 -->
-          <div
-            class="imgClolrBacImg"
-            v-show="info.coverInfo.coverVideoBac === 'imgTile'"
-          >
-            <SelectedImage
-              :imgSrc="info.coverInfo.videoBacImg"
-              :defaultImgSrc="require('@/assets/img/coverUpTit1.png')"
-              @cancel="onCancelBac2"
-            ></SelectedImage>
+          <div class="imgClolrBacImg" v-show="info.workCoverType.coverVideoBac === 'imgTile'">
+            <SelectedImage :imgSrc="info.workCoverType.videoBacImg" :defaultImgSrc="require('@/assets/img/coverUpTit1.png')" @cancel="onCancelBac2"></SelectedImage>
             <!-- 上传图片按钮 -->
             <div class="imgRight">
-              <button
-                class="ui-button submit"
-                @click="
-                  (isShowSelectionWindow = true),
-                    (selectingFor = 'bac'),
-                    (upType = 'image'),
-                    (upTypeSta = 'videoBac')
-                "
-              >
+              <button class="ui-button submit" @click="(isShowSelectionWindow = true), (selectingFor = 'bac'), (upType = 'image'), (upTypeSta = 'videoBac')">
                 {{ $i18n.t(`edit_settings.select_image`) }}
               </button>
-              <div
-                class="ui-remark"
-                v-html="$i18n.t(`edit_settings.coverUpTit3`)"
-              ></div>
+              <div class="ui-remark" v-html="$i18n.t(`edit_settings.coverUpTit3`)"></div>
             </div>
           </div>
         </div>
@@ -451,10 +273,7 @@ pc端视频位置 videoPcLoc
             {{ $i18n.t(`edit_settings.coverVideoInWayTit`) }}
           </div>
           <div class="VideoTitR">
-            <Switcher
-              :value="info.coverInfo.coverVideoInWay"
-              @change="coverVideoInWayChange"
-            ></Switcher>
+            <Switcher :value="info.workCoverType.coverVideoInWay" @change="coverVideoInWayChange"></Switcher>
           </div>
         </div>
         <!-- 视频控件 -->
@@ -466,33 +285,16 @@ pc端视频位置 videoPcLoc
             {{ $i18n.t(`edit_settings.coverVideoControlTit`) }}
           </div>
           <div class="VideoTitR">
-            <Switcher
-              :value="info.coverInfo.coverVideoControl"
-              @change="coverVideoControlChange"
-            ></Switcher>
+            <Switcher :value="info.workCoverType.coverVideoControl" @change="coverVideoControlChange"></Switcher>
           </div>
         </div>
         <!-- 出现顺序 -->
 
-        <div
-          class="coverImgTit"
-          v-show="info.coverInfo.coverSelect === 'videoAndImg'"
-        >
+        <div class="coverImgTit" v-show="info.workCoverType.coverSelect === 'videoAndImg'">
           {{ $i18n.t(`edit_settings.coverImageOrder`) }}
         </div>
-        <Select
-          placement="bottom"
-          class="base-select"
-          v-model="info.coverInfo.coverImageOrder"
-          v-show="info.coverInfo.coverSelect === 'videoAndImg'"
-        >
-          <Option
-            v-for="item in coverImageOrderList"
-            :key="item.type"
-            :label="item.txt"
-            :value="item.type"
-          >
-          </Option>
+        <Select placement="bottom" class="base-select" v-model="info.workCoverType.coverImageOrder" v-show="info.workCoverType.coverSelect === 'videoAndImg'">
+          <Option v-for="item in coverImageOrderList" :key="item.type" :label="item.txt" :value="item.type"> </Option>
         </Select>
       </div>
       <br />
@@ -552,7 +354,7 @@ export default {
         },
       ],
 
-      coverInfo: {
+      workCoverType: {
         // 封面类型下拉框数据
         coverSelect: "img",
         // 图片pc端位置
@@ -599,16 +401,7 @@ export default {
         { txt: i18n.t("edit_settings.coverImgBacSelec2"), type: "imgTile" },
       ],
 
-      predefineColors: [
-        "#000000",
-        "#ff4500",
-        "#ff8c00",
-        "#ffd700",
-        "#90ee90",
-        "#00ced1",
-        "#1e90ff",
-        "#c71585",
-      ],
+      predefineColors: ["#000000", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"],
 
       isShowSelectionWindow: false,
       selectingFor: "", // 'pc', 'mobile'
@@ -621,24 +414,19 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      oldInfo: "info",
+      info: "base/baseInfo",
     }),
   },
   watch: {
-    "info.coverInfo": {
+    "info.workCoverType": {
       handler(val) {
         if (val) {
-          const folderIds = [
-            val.coverPcId,
-            val.coverMoId,
-            val.videoPcId,
-            val.videoMoId,
-            val.coverImgBacId,
-            val.videoBacImgId,
-          ].filter((i) => i);
+          const folderIds = [val.coverPcId, val.coverMoId, val.videoPcId, val.videoMoId, val.coverImgBacId, val.videoBacImgId].filter((i) => i);
           // console.log("folderIds", folderIds);
           this.folderIds = folderIds;
-          // this.info.coverInfo.fodderId = folderIds;
+          // this.info.workCoverType.fodderId = folderIds;
         }
       },
       immediate: true,
@@ -648,55 +436,55 @@ export default {
   methods: {
     //是否显示
     onSwitcherChange(data) {
-      this.info.coverInfo.isShowCover = data;
+      this.info.workCoverType.isShowCover = data;
     },
 
     // 视频的进入方式
     coverVideoInWayChange(val) {
-      this.info.coverInfo.coverVideoInWay = val;
+      this.info.workCoverType.coverVideoInWay = val;
     },
     // 图片的进入方式
     coverImageInWayChange(val) {
       console.log(val, "coverImageInWayChange");
-      this.info.coverInfo.coverImageInWay = val;
+      this.info.workCoverType.coverImageInWay = val;
     },
     coverVideoControlChange(val) {
-      this.info.coverInfo.coverVideoControl = val;
+      this.info.workCoverType.coverVideoControl = val;
     },
     // 图片的颜色选择器改变事件
     imgColorSelecChange(val) {
-      this.info.coverInfo.imgColorSelec = val;
+      this.info.workCoverType.imgColorSelec = val;
     },
     videoColorSelecChange(val) {
-      this.info.coverInfo.videoColorSelec = val;
+      this.info.workCoverType.videoColorSelec = val;
     },
     handleSubmitFromMaterialSelector(selected) {
       // 点击确定的时候是选择的图片上传
       if (this.upTypeSta === "imgBac") {
         if (this.selectingFor === "pc") {
-          this.info.coverInfo.coverPc = selected[0].icon;
-          this.info.coverInfo.coverPcId = selected[0].id;
+          this.info.workCoverType.coverPc = selected[0].icon;
+          this.info.workCoverType.coverPcId = selected[0].id;
         } else if (this.selectingFor === "mobile") {
-          this.info.coverInfo.coverMo = selected[0].icon;
-          this.info.coverInfo.coverMoId = selected[0].id;
+          this.info.workCoverType.coverMo = selected[0].icon;
+          this.info.workCoverType.coverMoId = selected[0].id;
         } else {
-          this.info.coverInfo.coverBac = selected[0].icon;
-          this.info.coverInfo.coverBacId = selected[0].id;
+          this.info.workCoverType.coverBac = selected[0].icon;
+          this.info.workCoverType.coverBacId = selected[0].id;
         }
       } else {
         // 视频上传
         if (this.selectingFor === "pc") {
           console.log(selected[0], "selected[0]");
-          this.info.coverInfo.videoPc = selected[0].ossPath;
-          this.info.coverInfo.videoPcIcon = selected[0].icon;
-          this.info.coverInfo.videoPcId = selected[0].id;
+          this.info.workCoverType.videoPc = selected[0].ossPath;
+          this.info.workCoverType.videoPcIcon = selected[0].icon;
+          this.info.workCoverType.videoPcId = selected[0].id;
         } else if (this.selectingFor === "mobile") {
-          this.info.coverInfo.videoMo = selected[0].ossPath;
-          this.info.coverInfo.videoMoId = selected[0].id;
-          this.info.coverInfo.videoMoIcon = selected[0].icon;
+          this.info.workCoverType.videoMo = selected[0].ossPath;
+          this.info.workCoverType.videoMoId = selected[0].id;
+          this.info.workCoverType.videoMoIcon = selected[0].icon;
         } else {
-          this.info.coverInfo.videoBacImg = selected[0].icon;
-          this.info.coverInfo.videoBacImgId = selected[0].id;
+          this.info.workCoverType.videoBacImg = selected[0].icon;
+          this.info.workCoverType.videoBacImgId = selected[0].id;
         }
       }
       this.isShowSelectionWindow = false;
@@ -705,72 +493,77 @@ export default {
     },
     onCancelPcTip() {
       this.$nextTick(() => {
-        this.info.coverInfo.coverPc = "";
-        this.info.coverInfo.coverPcId = null;
+        this.info.workCoverType.coverPc = "";
+        this.info.workCoverType.coverPcId = null;
         this.updateFolderIds();
-        console.log("onCancelPcTip-1", this.info.coverInfo);
+        console.log("onCancelPcTip-1", this.info.workCoverType);
       });
     },
     onCancelAppTip() {
       this.$nextTick(() => {
-        this.info.coverInfo.coverMo = "";
-        this.info.coverInfo.coverMoId = null;
+        this.info.workCoverType.coverMo = "";
+        this.info.workCoverType.coverMoId = null;
         this.updateFolderIds();
       });
     },
     onCancelBac() {
       this.$nextTick(() => {
-        this.info.coverInfo.coverBac = "";
-        this.info.coverInfo.coverBacId = null;
+        this.info.workCoverType.coverBac = "";
+        this.info.workCoverType.coverBacId = null;
         this.updateFolderIds();
       });
     },
     // 关于视频
     clearVideoPc() {
-      this.info.coverInfo.videoPc = "";
-      this.info.coverInfo.videoPcId = null;
-      this.info.coverInfo.videoPcIcon = "";
+      this.info.workCoverType.videoPc = "";
+      this.info.workCoverType.videoPcId = null;
+      this.info.workCoverType.videoPcIcon = "";
       this.updateFolderIds();
     },
     clearVideoMo() {
-      this.info.coverInfo.videoMo = "";
-      this.info.coverInfo.videoMoId = null;
-      this.info.coverInfo.videoMoIcon = "";
+      this.info.workCoverType.videoMo = "";
+      this.info.workCoverType.videoMoId = null;
+      this.info.workCoverType.videoMoIcon = "";
       this.updateFolderIds();
     },
     onCancelBac2() {
-      this.info.coverInfo.videoBacImg = "";
-      this.info.coverInfo.videoBacImgId = null;
+      this.info.workCoverType.videoBacImg = "";
+      this.info.workCoverType.videoBacImgId = null;
       this.updateFolderIds();
     },
     updateFolderIds() {
       //fodderId 以前的字段有误在先
       setTimeout(() => {
         console.log("updateFolderIds:", this.folderIds);
-        this.info.coverInfo.fodderId = this.folderIds;
+        this.info.workCoverType.fodderId = this.folderIds;
       }, 300);
     },
   },
   created() {
-    if (!this.info.coverInfo) {
-      this.$store.commit("SetInfo", {
-        coverInfo: { ...this.coverInfo },
-        ...this.info,
+    if (!this.info.workCoverType) {
+     
+
+      // this.$store.commit("SetInfo", {
+      //   workCoverType: { ...this.workCoverType },
+      //   ...this.info,
+      // });
+      this.$store.commit("base/initbaseInfo", {
+        workCoverType: this.workCoverType,
       });
     } else {
-      Object.keys(this.coverInfo).forEach((i) => {
-        if (!(i in this.info.coverInfo)) {
-          this.info.coverInfo[i] = this.coverInfo[i];
+      Object.keys(this.workCoverType).forEach((i) => {
+        if (!(i in this.info.workCoverType)) {
+          this.info.workCoverType[i] = this.workCoverType[i];
         }
       });
-
       this.$store.commit("SetInfo", {
-        ...this.info,
+        // ...this.info,
+        ...this.oldInfo,
       });
     }
   },
   mounted() {
-    // console.log("this.info.coverInfo", this.info.coverInfo);
+    // console.log("this.info.workCoverType", this.info.workCoverType);
   },
   beforeCreate() {}, //生命周期 - 创建之前
   beforeMount() {}, //生命周期 - 挂载之前

+ 26 - 25
packages/qjkankan-editor/src/views/base/customButtonSettings.vue

@@ -10,7 +10,7 @@
     <br />
 
     <div
-      v-for="(item, index) of info.customButton"
+      v-for="(item, index) of info.workCustomButtonList"
       :key="index"
       class="button-setting-item"
       :class="{ expand: expandStatus[index] }"
@@ -223,7 +223,8 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      info: "base/baseInfo",
     }),
     translateName() {
       return (name) => {
@@ -239,9 +240,9 @@ export default {
     },
     //1.3.1后用这个过滤中文Key
     // eslint-disable-next-line vue/return-in-computed-property
-    customButton() {
-      if (this.info.customButton && this.info.customButton.length > 0) {
-        return Array.from(this.info.customButton).map((item) => {
+    workCustomButtonList() {
+      if (this.info.workCustomButtonList && this.info.workCustomButtonList.length > 0) {
+        return Array.from(this.info.workCustomButtonList).map((item) => {
           if (item.type == "电话") {
             item.type = "phone";
           }
@@ -278,8 +279,8 @@ export default {
       }
     },
     buttonValueTips() {
-      if (this.customButton) {
-        return this.customButton.map((item) => {
+      if (this.workCustomButtonList) {
+        return this.workCustomButtonList.map((item) => {
           if (item.type === "phone") {
             return i18n.t("edit_settings.phone");
           } else if (item.type === "link") {
@@ -307,7 +308,7 @@ export default {
       handler(newValue) {
         console.log("editingInfo.type", newValue);
         const item = this.buttonTypeList.find((i) => i.id === newValue);
-        const current = this.info.customButton[this.editingButtonIdx];
+        const current = this.info.workCustomButtonList[this.editingButtonIdx];
         console.log("current.name", current.name);
         if (current.name.length === 0) {
           this.editingInfo.name = item.label;
@@ -319,9 +320,9 @@ export default {
   },
 
   beforeMount() {
-    if (!this.info.customButton) {
+    if (!this.info.workCustomButtonList) {
       // 这是在v1.2版之前创建的作品,还没设置过自定义按钮,所以还没有customButton字段
-      this.info.customButton = [
+      this.info.workCustomButtonList = [
         {
           type: "phone",
           name: i18n.t("customButton.phone"),
@@ -339,10 +340,10 @@ export default {
       ];
     }
 
-    if (!this.info.customButton[0].openMethod) {
+    if (!this.info.workCustomButtonList[0].openMethod) {
       // 这是在v1.3版之前创建的作品,还没设置过自定义按钮的打开方式。
-      this.info.customButton[0].openMethod = "_self";
-      this.info.customButton[1].openMethod = "_self";
+      this.info.workCustomButtonList[0].openMethod = "_self";
+      this.info.workCustomButtonList[1].openMethod = "_self";
     }
   },
   mounted() {
@@ -368,10 +369,10 @@ export default {
     },
     onRequestForEdit(index) {
       this.editingButtonIdx = index;
-      this.editingInfo.type = this.customButton[index].type;
-      this.editingInfo.name = this.customButton[index].name;
-      this.editingInfo.value = this.customButton[index].value;
-      this.editingInfo.openMethod = this.customButton[index].openMethod;
+      this.editingInfo.type = this.workCustomButtonList[index].type;
+      this.editingInfo.name = this.workCustomButtonList[index].name;
+      this.editingInfo.value = this.workCustomButtonList[index].value;
+      this.editingInfo.openMethod = this.workCustomButtonList[index].openMethod;
       this.isEditing = true;
     },
     checkButtonName(name) {
@@ -404,32 +405,32 @@ export default {
       ) {
         return;
       }
-      this.info.customButton[this.editingButtonIdx].type =
+      this.info.workCustomButtonList[this.editingButtonIdx].type =
         this.editingInfo.type;
-      this.info.customButton[this.editingButtonIdx].name =
+      this.info.workCustomButtonList[this.editingButtonIdx].name =
         this.editingInfo.name;
-      this.info.customButton[this.editingButtonIdx].value =
+      this.info.workCustomButtonList[this.editingButtonIdx].value =
         this.editingInfo.value;
       if (this.editingInfo.type === "link") {
-        this.info.customButton[this.editingButtonIdx].openMethod =
+        this.info.workCustomButtonList[this.editingButtonIdx].openMethod =
           this.editingInfo.openMethod;
       }
       this.$msg.success(i18n.t("gather.success"));
       this.isEditing = false;
     },
     onRequestForShow(index) {
-      if (!this.checkButtonName(this.info.customButton[index].name)) {
+      if (!this.checkButtonName(this.info.workCustomButtonList[index].name)) {
         return;
       }
       if (
         !this.checkButtonValue(
-          this.info.customButton[index].value,
-          this.info.customButton[index].type
+          this.info.workCustomButtonList[index].value,
+          this.info.workCustomButtonList[index].type
         )
       ) {
         return;
       }
-      this.info.customButton[index].isShow = true;
+      this.info.workCustomButtonList[index].isShow = true;
     },
   },
 };

+ 12 - 11
packages/qjkankan-editor/src/views/base/customLogoSettings.vue

@@ -4,11 +4,11 @@
     <br />
     <div class="switch-wrapper">
       <span class="label">{{ $i18n.t(`edit_settings.show_logo`) }}</span>
-      <Switcher :value="info.isLogo" @change="onSwitcherChange"></Switcher>
+      <Switcher :value="info.workLogo.isLogo" @change="onSwitcherChange"></Switcher>
     </div>
-    <div class="bottom" :class="{ disabled: !info.isLogo }">
+    <div class="bottom" :class="{ disabled: !info.workLogo.isLogo }">
       <SelectedImage
-        :imgSrc="info.logo"
+        :imgSrc="info.workLogo.logo"
         :defaultImgSrc="
           require(`@/assets/images/default/logo_white_${$lang}.svg`)
         "
@@ -64,12 +64,13 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      info:'base/baseInfo'
     }),
   },
   methods: {
     onSwitcherChange(data) {
-      this.info.isLogo = data;
+      this.info.workLogo.isLogo = data;
     },
     onClickSelectingPicBtn() {
       this.isShowSelectionWindow = true;
@@ -87,15 +88,15 @@ export default {
       // })
     },
     handleSubmitFromMaterialSelector(selected) {
-      this.info.logo = selected[0].icon;
-      this.info.logoId = selected[0].id;
-      this.info.logoChange = true;
+      this.info.workLogo.logo = selected[0].icon;
+      this.info.workLogo.logoId = selected[0].id;
+      this.info.workLogo.logoChange = true;
       this.isShowSelectionWindow = false;
     },
     onClickCancelCustomLogo() {
-      this.info.logo = "";
-      this.info.logoId = "";
-      this.info.logoChange = false;
+      this.info.workLogo.logo = "";
+      this.info.workLogo.logoId = "";
+      this.info.workLogo.logoChange = false;
     },
   },
   mounted() {},

+ 49 - 10
packages/qjkankan-editor/src/views/base/index.vue

@@ -8,24 +8,63 @@
 <script>
 import Toolbar from "./Toolbar";
 import { mapGetters } from "vuex";
-
+import { deepClone, isSameObject } from "@/utils/other.js";
 export default {
   name: "EditorBase",
   components: {
-    Toolbar
+    Toolbar,
+  },
+  data() {
+    return {
+      isInit: true,
+    };
   },
   computed: {
     ...mapGetters({
-      info: 'info'
+      // info: "info",
+      info: "base/baseInfo",
     }),
-  }
+    newInfo() {
+      let info = {
+        work: this.info?.work,
+        workBackgroundMusic: this.info?.workBackgroundMusic,
+        workCoverType: this.info?.workCoverType,
+        workCustomButton: this.info?.workCustomButton,
+        workId: this.info?.workId,
+        workLogo: this.info?.workLogo,
+        workOpeningAnimation: this.info?.workOpeningAnimation,
+        workOpeningTip: this.info?.workOpeningTip,
+      };
+      return JSON.parse(JSON.stringify(info));
+      // return info;
+    },
+  },
+  watch: {
+    newInfo: {
+      handler(newVal, oldVal) {
+        if (newVal && this.$route.name == "base") {
+          // if (JSON.stringify(newVal).length != JSON.stringify(oldVal).length && this.$route.name == "base") {
+          if (this.isInit) {
+            //防止第一次获取数据的时候触发
+            this.isInit = false;
+          } else {
+            this.$store.commit("scene/setSaveApiList", "base");
+          }
+        }
+      },
+      deep: true,
+    },
+  },
+
+  methods: {},
+  mounted() {},
 };
 </script>
 
 <style lang="less">
-    input::placeholder,
-    textarea::placeholder {
-      font-size: 14px;
-      color: #505050 !important;
-    }
-</style>
+input::placeholder,
+textarea::placeholder {
+  font-size: 14px;
+  color: #505050 !important;
+}
+</style>

+ 12 - 30
packages/qjkankan-editor/src/views/base/openingAnimationSettings.vue

@@ -3,13 +3,8 @@
     <span class="title">{{ $i18n.t(`edit_settings.opening_setting`) }}</span>
     <br />
     <div class="switch-wrapper">
-      <span class="label">{{
-        $i18n.t("edit_settings.opening_animation_show")
-      }}</span>
-      <Switcher
-        :value="info.isShowOpeningAnimation"
-        @change="onSwitcherChange"
-      ></Switcher>
+      <span class="label">{{ $i18n.t("edit_settings.opening_animation_show") }}</span>
+      <Switcher :value="info.workOpeningAnimation.isShowOpeningAnimation" @change="onSwitcherChange"></Switcher>
     </div>
     <div class="btns-and-video">
       <div class="btn-wrapper">
@@ -17,28 +12,15 @@
           v-for="item of openingTypeList"
           :key="item"
           class="opening-selection-btn"
-          :class="{ 'active-opening-type': info.openingAnimationType === item }"
-          @click="info.openingAnimationType = item"
+          :class="{ 'active-opening-type': info.workOpeningAnimation.openingAnimationType === item }"
+          @click="info.workOpeningAnimation.openingAnimationType = item"
         >
           {{ $i18n.t(`baseSetting.opa${item}`) }}
         </button>
       </div>
       <div class="video-wrapper">
-        <video
-          ref="opvideo"
-          :src="
-            require(`@/assets/videos/opa${fallbackOpeningAnimationType(
-              info.openingAnimationType
-            )}_x264.mp4`)
-          "
-          autoplay
-          loop
-        ></video>
-        <i
-          v-if="playing"
-          @click="bofang"
-          class="iconfont iconshow_playback"
-        ></i>
+        <video ref="opvideo" :src="require(`@/assets/videos/opa${fallbackOpeningAnimationType(info.workOpeningAnimation.openingAnimationType)}_x264.mp4`)" autoplay loop></video>
+        <i v-if="playing" @click="bofang" class="iconfont iconshow_playback"></i>
       </div>
     </div>
   </div>
@@ -68,7 +50,8 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      info: "base/baseInfo",
     }),
     fallbackOpeningAnimationType() {
       return (val) => {
@@ -93,17 +76,16 @@ export default {
       this.$refs.opvideo.play();
     },
     onSwitcherChange(data) {
-      this.info.isShowOpeningAnimation = data;
+      this.info.workOpeningAnimation.isShowOpeningAnimation = data;
     },
   },
   mounted() {
-    if (this.info.isShowOpeningAnimation === undefined) {
+    if (this.info.workOpeningAnimation.isShowOpeningAnimation === undefined) {
       this.$set(this.info, "isShowOpeningAnimation", false);
     }
     if (
-      !this.info.openingAnimationType ||
-      (typeof this.info.openingAnimationType === "string" &&
-        this.info.openingAnimationType.match(/[\u3400-\u9FBF]/))
+      !this.info.workOpeningAnimation.openingAnimationType ||
+      (typeof this.info.workOpeningAnimation.openingAnimationType === "string" && this.info.workOpeningAnimation.openingAnimationType.match(/[\u3400-\u9FBF]/))
     ) {
       this.$set(this.info, "openingAnimationType", this.openingTypeList[0]);
     }

+ 16 - 15
packages/qjkankan-editor/src/views/base/openingTipSettings.vue

@@ -8,7 +8,7 @@
       <div class="title">{{$i18n.t(`edit_settings.pc`)}}</div>
       <div class="bottom">
         <SelectedImage
-          :imgSrc="info.pcIcon"
+          :imgSrc="info.workOpeningTip.pcIcon"
           :defaultImgSrc="require('@/assets/images/default/img_tipspc_default.png')"
           @cancel="onCancelPcTip"
         ></SelectedImage>
@@ -22,7 +22,7 @@
       <div class="title">{{$i18n.t(`edit_settings.mobile`)}}</div>
       <div class="bottom">
         <SelectedImage
-          :imgSrc="info.appIcon"
+          :imgSrc="info.workOpeningTip.appIcon"
           :defaultImgSrc="require('@/assets/images/default/img_tipsmb_default.png')"
           @cancel="onCancelAppTip"
         ></SelectedImage>
@@ -36,7 +36,7 @@
     <div class="title">{{$i18n.t(`edit_settings.show_setting`)}}</div>
     <div class="switch-wrapper">
         <span class="label">{{$i18n.t(`edit_settings.first_notice`)}}</span>
-        <switcher :value="info.isRemind" @change="onSwitcherChange"></switcher>
+        <switcher :value="info.workOpeningTip.isRemind" @change="onSwitcherChange"></switcher>
     </div>
     <div class="range-wrapper">
       <RangeItem :value="rang" @input="onRangeChange" />
@@ -85,40 +85,41 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info:'info'
+      // info:'info'
+      info:'base/baseInfo'
     })
   },
 
   mounted() {
     this.$nextTick(()=>{
-      this.rang.value = this.info.remindTime
+      this.rang.value = this.info.workOpeningTip.remindTime
     })
   },
   methods: {
     handleSubmitFromMaterialSelector(selected) {
       if (this.selectingFor === 'pc') {
-        this.info.pcIcon = selected[0].icon
-        this.info.pcIconId = selected[0].id
+        this.info.workOpeningTip.pcIcon = selected[0].icon
+        this.info.workOpeningTip.pcIconId = selected[0].id
       } else if (this.selectingFor === 'mobile') {
-        this.info.appIcon = selected[0].icon
-        this.info.appIconId = selected[0].id
+        this.info.workOpeningTip.appIcon = selected[0].icon
+        this.info.workOpeningTip.appIconId = selected[0].id
       }
       this.isShowSelectionWindow = false
     },
     onCancelPcTip() {
-      this.info.pcIcon = ''
-      this.info.pcIconId = null
+      this.info.workOpeningTip.pcIcon = ''
+      this.info.workOpeningTip.pcIconId = null
     },
     onCancelAppTip() {
-      this.info.appIcon = ''
-      this.info.appIconId = null
+      this.info.workOpeningTip.appIcon = ''
+      this.info.workOpeningTip.appIconId = null
     },
     onSwitcherChange(data){
-      this.info.isRemind = data
+      this.info.workOpeningTip.isRemind = data
     },
     onRangeChange(data) {
       console.log(data.value);
-      this.info.remindTime = parseInt(data.value)
+      this.info.workOpeningTip.remindTime = parseInt(data.value)
     },
   }
 }

+ 5 - 5
packages/qjkankan-editor/src/views/base/passwordSettings.vue

@@ -7,7 +7,7 @@
         :type="canSee ? 'text' : 'password'"
         :placeholder="$i18n.t(`edit_settings.password_placeholder`)"
         :maxlength="20"
-        v-model="info.password"
+        v-model="info.work.password"
         autocomplete="new-password"
         @keydown.enter="onInputEnter"
       >
@@ -30,14 +30,14 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info:'info'
+      // info:'info'
+      info:'base/baseInfo'
     })
   },
   watch: {
-    'info.password': {
+    'info.work.password': {
       handler(v) {
-        console.log(123);
-        this.info.password = v.replace(/[^\w]/g, '').replace(/_/g, '')
+        this.info.work.password = v.replace(/[^\w]/g, '').replace(/_/g, '')
       }
     }
   },

+ 55 - 49
packages/qjkankan-editor/src/views/explanation/explanationSettings.vue

@@ -1,46 +1,40 @@
 <template>
-  <div class="explanation-settings"  app-border dir-left>
-    <template v-if="currentScene.type !== '4dkk'">
+  <div v-if="currentScene" class="explanation-settings" app-border dir-left>
+    <template v-if="currentScene.type !== '4dkk' && currentExplanation">
       <div class="title">
-        {{$i18n.t("explanation.explanation_settings")}}
-        <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('explanation.explanation_tips')"/>
+        {{ $i18n.t("explanation.explanation_settings") }}
+        <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('explanation.explanation_tips')" />
       </div>
       <button
-        v-if="!currentScene.explanation.audioId"
+        v-if="!currentExplanation?.fodderId"
         class="ui-button submit"
         :class="{
-          disable: !currentScene || currentScene.type ==='4dkk',
+          disable: !currentScene || currentScene.type === '4dkk',
         }"
         @click="isShowSelectionWindow = true"
       >
         <i class="iconfont icon-editor_add"></i>
-        {{$i18n.t("explanation.add_audio")}}
+        {{ $i18n.t("explanation.add_audio") }}
       </button>
       <template v-else>
         <div class="music-display" @click.self="onClickCurrentMusic">
-          <Audio ref="my-audio" class="audio-control" :backgroundColor="'#1A1B1D'" :myAudioUrl="currentScene.explanation.audioUrl"></Audio>
-          <div class="name" :key="currentScene.explanation.audioName" v-title="currentScene.explanation.audioName" @click="onClickCurrentMusic">{{currentScene.explanation.audioName}}</div>
+          <Audio ref="my-audio" class="audio-control" :backgroundColor="'#1A1B1D'" :myAudioUrl="currentExplanation.audioUrl"></Audio>
+          <div class="name" :key="currentExplanation.audioName" v-title="currentExplanation.audioName" @click="onClickCurrentMusic">{{ currentExplanation.audioName }}</div>
           <i class="iconfont icon-editor_list_delete" @click.stop="onClickDeleteMusicBtn"></i>
         </div>
         <button class="ui-button" @click="isShowSelectionWindow = true">
           <i class="iconfont icon-editor_update"></i>
-          {{$i18n.t("explanation.change_audio")}}
+          {{ $i18n.t("explanation.change_audio") }}
         </button>
       </template>
 
       <div class="switch-wrapper">
-        <span class="label">{{$i18n.t("explanation.default_open")}}</span>
-        <Switcher
-          :disable="!currentScene || currentScene.type ==='4dkk'"
-          :value="currentScene.explanation.openByDefault" @change="currentScene.explanation.openByDefault = !currentScene.explanation.openByDefault"
-        />
+        <span class="label">{{ $i18n.t("explanation.default_open") }}</span>
+        <Switcher :disable="!currentScene || currentScene.type === '4dkk'" :value="currentExplanation.openByDefault" @change="currentExplanation.openByDefault = !currentExplanation.openByDefault" />
       </div>
       <div class="switch-wrapper">
-        <span class="label">{{$i18n.t("explanation.loop")}}</span>
-        <Switcher
-          :disable="!currentScene || currentScene.type ==='4dkk'"
-          :value="currentScene.explanation.repeat" @change="currentScene.explanation.repeat = !currentScene.explanation.repeat"
-        />
+        <span class="label">{{ $i18n.t("explanation.loop") }}</span>
+        <Switcher :disable="!currentScene || currentScene.type === '4dkk'" :value="currentExplanation.playRepeat" @change="currentExplanation.playRepeat = !currentExplanation.playRepeat" />
       </div>
 
       <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
@@ -73,51 +67,63 @@ export default {
   },
   computed: {
     ...mapGetters({
-        info: "info",
-        currentScene: "scene/currentScene",
+      baseInfo: "base/baseInfo",
+      currentScene: "scene/currentScene",
+      workExplanationList: "explanation/workExplanationList",
     }),
+    currentExplanation() {
+      return this.workExplanationList.find((item) => item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid);
+    },
   },
   data() {
     return {
       isShowSelectionWindow: false,
-    }
+    };
   },
   methods: {
     onClickCurrentMusic() {
-      if (this.$refs['my-audio']) {
-        this.$refs['my-audio'].switchPlayPause()
+      if (this.$refs["my-audio"]) {
+        this.$refs["my-audio"].switchPlayPause();
       }
     },
     onClickDeleteMusicBtn() {
-      this.currentScene.explanation.audioId = ''
-      this.currentScene.explanation.audioUrl = ''
-      this.currentScene.explanation.audioName = ''
+      this.currentExplanation.fodderId = null;
+      this.currentExplanation.audioUrl = "";
+      this.currentExplanation.audioName = "";
     },
     handleSubmitFromMaterialSelector(selected) {
-      this.currentScene.explanation.audioId = selected[0].id
-      this.currentScene.explanation.audioName = selected[0].name
-      this.currentScene.explanation.audioUrl = selected[0].ossPath
-      this.isShowSelectionWindow = false
-
+      this.currentExplanation.fodderId = selected[0].id;
+      this.currentExplanation.audioName = selected[0].name;
+      this.currentExplanation.audioUrl = selected[0].ossPath;
+      this.isShowSelectionWindow = false;
     },
   },
   watch: {
-    currentScene: {
-      immediate: true,
-      handler: function (newVal) {
-        if (!newVal.explanation) {
-          this.$set(newVal, 'explanation', {
-            audioId: '',
-            audioName: '',
-            audioUrl: '',
-            openByDefault: true,
-            repeat: true,
-          })
+    baseInfo: {
+      handler(newVal, oldVal) {
+        if (newVal && this.$route.name == "explanation") {
+          console.error("explanation change");
+          this.$store.commit("scene/setSaveApiList", "explanation");
         }
       },
+      deep: true,
     },
+    // currentScene: {
+    //   immediate: true,
+    //   handler: function (newVal) {
+    //     if (!newVal.explanation) {
+    //       this.$set(newVal, "explanation", {
+    //         fodderId: "",
+    //         audioName: "",
+    //         audioUrl: "",
+    //         openByDefault: true,
+    //         playRepeat: true,
+    //       });
+    //     }
+    //   },
+    // },
   },
-}
+};
 </script>
 
 <style lang="less" scoped>
@@ -147,7 +153,7 @@ export default {
     margin-top: 16px;
     width: 234px;
     height: 36px;
-    background: #1A1B1D;
+    background: #1a1b1d;
     border-radius: 2px;
     border: 1px solid #404040;
     color: #fff;
@@ -155,7 +161,7 @@ export default {
     text-align: center;
     position: relative;
     &:hover {
-      color: #0076F6;
+      color: #0076f6;
       border-color: @color;
       > .audio-control {
         display: inline-block;
@@ -189,7 +195,7 @@ export default {
       transform: translateY(-50%);
       right: 18px;
       &:hover {
-        color: #FA5555;
+        color: #fa5555;
       }
     }
   }
@@ -211,4 +217,4 @@ export default {
     font-size: 14px;
   }
 }
-</style>
+</style>

+ 47 - 99
packages/qjkankan-editor/src/views/hotspot/EditPanel.vue

@@ -1,10 +1,5 @@
 <template>
-  <transition
-    appear
-    name="custom-classes-transition"
-    enter-active-class="animated slideInRight speed"
-    leave-active-class="animated slideOutRight speed"
-  >
+  <transition appear name="custom-classes-transition" enter-active-class="animated slideInRight speed" leave-active-class="animated slideOutRight speed">
     <div class="hots-panel" v-show="show">
       <div class="ui-between header">
         <span>{{ editTitle }}{{ $i18n.t("hotspot.hotspot_name") }}</span>
@@ -14,39 +9,19 @@
       <div class="content" ref="content">
         <div class="type-setting">
           <div class="remark">{{ $i18n.t("hotspot.hotspot_type") }}</div>
-          <combox
-            class="combox"
-            :data="hotspotTypeList"
-            :selected-id="hotspot.hotspotType"
-            @change="onHotSpotTypeChange"
-          />
+          <combox class="combox" :data="hotspotTypeList" :selected-id="hotspot.hotspotType" @change="onHotSpotTypeChange" />
         </div>
 
         <div class="icon-setting">
           <div class="remark">{{ $i18n.t("hotspot.hotspot_icon") }}</div>
-          <combox
-            class="combox"
-            :data="hotspotIconTypeList"
-            :selected-id="hotspot.hotspotIconType"
-            @change="onHotspotIconTypeChange"
-          />
-          <component
-            class="icon-setting-component"
-            :is="iconTypeComponent"
-            ref="icon-setting-component"
-          />
-          <div
-            class="bars"
-            v-if="hotspot.hotspotIconType !== 'personalized_tag'"
-          >
+          <combox class="combox" :data="hotspotIconTypeList" :selected-id="hotspot.hotspotIconType" @change="onHotspotIconTypeChange" />
+          <component class="icon-setting-component" :is="iconTypeComponent" ref="icon-setting-component" />
+          <div class="bars" v-if="hotspot.hotspotIconType !== 'personalized_tag'">
             <RangeItem :value="rang" @input="onRangeChange" />
           </div>
         </div>
 
-        <div
-          class="title-setting"
-          v-if="hotspot.hotspotIconType !== 'personalized_tag'"
-        >
+        <div class="title-setting" v-if="hotspot.hotspotIconType !== 'personalized_tag'">
           <div class="remark-highlight">
             {{ $i18n.t("hotspot.hotspot_title") }}
           </div>
@@ -55,10 +30,7 @@
             <div class="remark">
               {{ $i18n.t("hotspot.show_hotspot_title") }}
             </div>
-            <Switcher
-              :value="tempHotpotLabelShow"
-              @change="onSwitcherChange"
-            ></Switcher>
+            <Switcher :value="tempHotpotLabelShow" @change="onSwitcherChange"></Switcher>
           </div>
 
           <!-- <div class="title-input-wrapper">
@@ -128,19 +100,8 @@
 
         <div class="effect-setting">
           <div class="title-line">
-            <div
-              class="remark-highlight"
-              v-if="
-                $i18n.t(
-                  `hotspot.hotspot_type_specific_settings_title.${hotspot.hotspotType}`
-                )
-              "
-            >
-              {{
-                $i18n.t(
-                  `hotspot.hotspot_type_specific_settings_title.${hotspot.hotspotType}`
-                )
-              }}
+            <div class="remark-highlight" v-if="$i18n.t(`hotspot.hotspot_type_specific_settings_title.${hotspot.hotspotType}`)">
+              {{ $i18n.t(`hotspot.hotspot_type_specific_settings_title.${hotspot.hotspotType}`) }}
             </div>
             <div class="tip" v-if="currentHotspotTypeConfigData.tip">
               {{ currentHotspotTypeConfigData.tip }}
@@ -149,7 +110,7 @@
           <component
             class="effect-setting-component"
             @sceneSelect="handleSceneSelect"
-            :scene="hotspot.secne"
+            :scene="hotspot.scene"
             @imageChange="
               (data) => {
                 hotspot.image = data;
@@ -191,19 +152,12 @@
         </div>
       </div>
       <div class="ui-between footer" app-border dir-top>
-        <button
-          class="ui-button deepcancel"
-          :class="{ disable: false }"
-          @click="realCancel"
-        >
+        <button class="ui-button deepcancel" :class="{ disable: false }" @click="realCancel">
           {{ $i18n.t("hotspot.cancel") }}
         </button>
-        <button
-          class="ui-button submit"
-          :class="{ disable: !canSubmit }"
-          @click="save"
-        >
-          {{ $i18n.t("hotspot.finish") }}
+        <button class="ui-button submit" :class="{ disable: !canSubmit }" @click="save">
+          <!-- {{ $i18n.t("hotspot.finish") }} -->
+          {{ $i18n.t("edit_page.save") }}
         </button>
       </div>
     </div>
@@ -268,12 +222,14 @@ export default {
     "hotspot.titleDisplayMode": {
       handler(val) {
         console.log("titleDisplayMode", val);
-        if (val === "always") {
-          this.tempHotpotLabelShow = 1;
-        }
-        if (val === "never") {
-          this.tempHotpotLabelShow = 0;
-        }
+        // if (val === "always") {
+        //   this.tempHotpotLabelShow = 1;
+        // }
+        // if (val === "never") {
+        //   this.tempHotpotLabelShow = 0;
+        // }
+
+        this.tempHotpotLabelShow = val;
       },
       immediate: true,
     },
@@ -358,9 +314,9 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+      hotspot: "hotspot/hotspot",
       backupHotSpot: "backupHotSpot",
-      hotspotIcon: "hotspotIcon",
+      hotspotIcon: "hotspot/hotspotIcon",
     }),
     iconTypeComponent() {
       let tmp = this.hotspot.hotspotIconType;
@@ -402,7 +358,7 @@ export default {
       // 热点类型相关设置项
       switch (this.hotspot.hotspotType) {
         case "scene":
-          if (!(this.hotspot.secne && this.hotspot.secne.id)) {
+          if (!(this.hotspot.scene && this.hotspot.scene.id)) {
             return false;
           }
           break;
@@ -434,10 +390,7 @@ export default {
         case "tag":
           break;
         case "imageText":
-          if (
-            this.hotspot.imageTextInfo.imageList.length === 0 ||
-            this.hotspot.imageTextInfo.text.length === 0
-          ) {
+          if (this.hotspot.imageTextInfo.imageList.length === 0 || this.hotspot.imageTextInfo.text.length === 0) {
             return false;
           }
 
@@ -471,18 +424,12 @@ export default {
           }
           break;
         case "serial_frame":
-          if (
-            !this.hotspot.serialFrameInfo.img ||
-            this.hotspot.serialFrameInfo.img.length === 0
-          ) {
+          if (!this.hotspot.serialFrameInfo.img || this.hotspot.serialFrameInfo.img.length === 0) {
             return false;
           }
           break;
         case "personalized_tag":
-          if (
-            this.hotspot.personalizedTagInfo.isTextWrap &&
-            this.hotspot.personalizedTagInfo.textNumPerLine <= 0
-          ) {
+          if (this.hotspot.personalizedTagInfo.isTextWrap && this.hotspot.personalizedTagInfo.textNumPerLine <= 0) {
             return false;
           }
           break;
@@ -515,10 +462,7 @@ export default {
     this.$bus.on("edithotspot", this.handleEditHotspot);
 
     setTimeout(() => {
-      if (
-        this.editTitle != "编辑" &&
-        this.editTitle != this.$i18n.t("hotspot.edit")
-      ) {
+      if (this.editTitle != "编辑" && this.editTitle != this.$i18n.t("hotspot.edit")) {
         this.rang.value = window.g_hotspotCurrentScale;
         this.onRangeChange({ value: window.g_hotspotCurrentScale });
       }
@@ -527,14 +471,17 @@ export default {
 
   methods: {
     handleEditHotspot(data) {
+      console.error("handleEditHotspot",data);
       const krpano = document.getElementById("krpanoSWFObject");
       __krfn.utils.edithotspot(krpano, data);
     },
     handleSceneSelect(data) {
-      this.hotspot.secne = {
+      this.hotspot.scene = {
         ...data,
-        someData: {},
+        // someData: {},
       };
+      // this.hotspot.content.scene = data;
+      console.error(this.hotspot);
     },
     onHotSpotTypeChange(data) {
       this.hotspot.hotspotType = data.id;
@@ -619,10 +566,7 @@ export default {
       }
     },
     listerFnReset() {
-      if (
-        this.hotspot.hotspotTitle == "单击确定热点位置" ||
-        this.hotspot.hotspotTitle == this.$i18n.t("hotspot.click_to_comfirm")
-      ) {
+      if (this.hotspot.hotspotTitle == "单击确定热点位置" || this.hotspot.hotspotTitle == this.$i18n.t("hotspot.click_to_comfirm")) {
         this.hotspot.hotspotTitle = "";
       }
     },
@@ -640,11 +584,9 @@ export default {
       // }
     },
     realCancel() {
-      this.$store.commit("SetHotspot", this.backupHotSpot);
+      this.$store.commit("hotspot/SetHotspot", this.backupHotSpot);
       console.log("cancel", this.backupHotSpot);
-      this.$getKrpano().call(
-        "cancelJQHotspot(" + this.backupHotSpot.name + ");"
-      );
+      this.$getKrpano().call("cancelJQHotspot(" + this.backupHotSpot.name + ");");
       this.$emit("close", {
         type: this.editTitle == this.$i18n.t("hotspot.edit") ? "edit" : "add",
         data: this.backupHotSpot,
@@ -656,18 +598,24 @@ export default {
       // this.$getKrpano().set(`hotspot[${data.name}].url`, data.img);
     },
     save() {
-      this.$store.commit("SetHotspot", this.hotspot);
+      // this.$store.commit("SetHotspot", this.hotspot);
+      // this.$emit("close");
+      // this.$emit("save", this.hotspot);
+      // this.$store.dispatch("hotspot/save", this.hotspot);
+
+      this.$store.commit("hotspot/updateHotspot", this.hotspot);
+      this.$store.commit("scene/setSaveApiList", "hotspot");
       this.$emit("close");
-      this.$emit("save", this.hotspot);
     },
     onSwitcherChange(val) {
       console.log("onSwitcherChange", val);
       this.tempHotpotLabelShow = val;
       if (val === 0) {
-        this.hotspot.titleDisplayMode = "never";
+        // this.hotspot.titleDisplayMode = "never";
+        this.hotspot.titleDisplayMode = 0;
       }
       if (val === 1) {
-        this.hotspot.titleDisplayMode = "always";
+        this.hotspot.titleDisplayMode = 1;
       }
     },
   },

+ 147 - 124
packages/qjkankan-editor/src/views/hotspot/HotSpotList.vue

@@ -2,10 +2,7 @@
   <div class="hot-spot-list" app-border dir-left>
     <div class="title">
       {{ $i18n.t("hotspot.add_hotspot") }}
-      <i
-        class="iconfont icon-help_i tool-tip-for-editor"
-        v-tooltip="$i18n.t('hotspot.hotspot_tips')"
-      />
+      <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('hotspot.hotspot_tips')" />
     </div>
 
     <template v-if="currentScene.type !== '4dkk'">
@@ -24,43 +21,23 @@
         >
           <img class="icon" :src="item.icon" alt="" draggable="false" />
           <div class="type-name">{{ item.name }}</div>
-          <img
-            v-if="item.isExperience"
-            class="exp-tag"
-            :src="experience_icon"
-            alt=""
-            draggable="false"
-          />
+          <img v-if="item.isExperience" class="exp-tag" :src="experience_icon" alt="" draggable="false" />
         </li>
       </ul>
 
       <div class="total-count">
         {{ $i18n.t("hotspot.current_hotspots") }}
-        <span class="number">({{ someData.hotspots.length }})</span>
+        <span class="number">({{ currentHotsopts.length }})</span>
       </div>
       <div class="hots">
-        <ul v-if="someData.hotspots.length > 0">
-          <li
-            v-for="(item, key) in someData.hotspots"
-            :key="key"
-            @click="open(item)"
-          >
+        <!-- <ul v-if="someData.hotspots.length > 0"> -->
+        <ul v-if="currentHotsopts.length > 0">
+          <li v-for="(item, key) in currentHotsopts" :key="key" @click="open(item)">
             <img class="hot-spot-thumb" :src="item.img" alt="" />
-            <span class="hot-spot-title" v-title="item.hotspotTitle">{{
-              item.hotspotTitle
-            }}</span>
-            <i
-              class="iconfont icon-editor_list_delete icon-delete"
-              v-tooltip="$i18n.t('hotspot.delete')"
-              @click.stop="deleIndex = key"
-            />
+            <span class="hot-spot-title" v-title="item.hotspotTitle">{{ item.hotspotTitle }}</span>
+            <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="$i18n.t('hotspot.delete')" @click.stop="deleIndex = key" />
             <div class="deletion-confirm-wrap">
-              <div
-                class="deletion-confirm"
-                :class="deleIndex == key ? 'show' : 'hide'"
-                v-clickoutside="clickoutside"
-                @click.stop="deleteHot(item)"
-              >
+              <div class="deletion-confirm" :class="deleIndex == key ? 'show' : 'hide'" v-clickoutside="clickoutside" @click.stop="deleteHot(item)">
                 {{ $i18n.t("hotspot.delete") }}
               </div>
             </div>
@@ -75,12 +52,7 @@
 
     <div class="goto-4dkk-tip" v-if="currentScene.type === '4dkk'">
       <div class="img-wrap">
-        <img
-          class=""
-          src="@/assets/images/default/goto-4dage.png"
-          alt=""
-          draggable="false"
-        />
+        <img class="" src="@/assets/images/default/goto-4dage.png" alt="" draggable="false" />
         <div class="tip-text">
           {{ $i18n.t("screen.goto_4dkk_edit_tips") }}
         </div>
@@ -90,14 +62,7 @@
       </button>
     </div>
 
-    <EditPanel
-      class="adding-hotspot-panel"
-      v-if="showPanel"
-      :editTitle="editTitle"
-      :show="showPanel"
-      @save="save"
-      @close="close"
-    />
+    <EditPanel class="adding-hotspot-panel" v-if="showPanel" :editTitle="editTitle" :show="showPanel" @save="save" @close="close" />
   </div>
 </template>
 
@@ -131,8 +96,12 @@ export default {
   computed: {
     ...mapGetters({
       currentScene: "scene/currentScene",
-      hotspot: "hotspot",
-      info: "info",
+      hotspot: "hotspot/hotspot",
+      // info: "info",
+      baseInfo: "base/baseInfo",
+      hotspotList: "hotspot/hotspotList",
+      isConfirmingPosi: "tags/isConfirmingPosi",
+      currentHotsopts: "hotspot/currentHotsopts",
     }),
     experience_icon() {
       const lang = browser.urlQueryValue("lang");
@@ -142,65 +111,85 @@ export default {
         return require("@/assets/img/experience_zh.png");
       }
     },
+    // currentHotsopts() {
+    //   console.error('currentHotsopts', this.hotspotList)
+    //   return this.hotspotList.filter((item) => item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid);
+    // },
   },
   watch: {
     "$route.name": function () {
       this.showPanel = false;
     },
+
+    hotspotList: {
+      handler(newVal, oldVal) {
+        if (newVal && this.$route.name == "hotspot") {
+          this.$store.commit("scene/setSaveApiList", "hotspot");
+        }
+      },
+      deep: true,
+    },
     currentScene: {
       immediate: true,
       handler: function (newVal) {
-        this.someData = newVal.someData || "";
-        if (this.someData) {
-          if (typeof this.someData == "string") {
-            try {
-              this.someData = JSON.parse(this.someData);
-            } catch (e) {
-              console.error(e);
-              return false;
-            }
-          }
-          if (!this.someData.hotspots) {
-            this.someData.hotspots = [];
-          }
-        } else {
-          this.someData = { hotspots: [] };
-        }
+        // this.someData = newVal.someData || "";
+        // if (this.someData) {
+        //   if (typeof this.someData == "string") {
+        //     try {
+        //       this.someData = JSON.parse(this.someData);
+        //     } catch (e) {
+        //       console.error(e);
+        //       return false;
+        //     }
+        //   }
+        //   if (!this.someData.hotspots) {
+        //     this.someData.hotspots = [];
+        //   }
+        // } else {
+        //   this.someData = { hotspots: [] };
+        // }
       },
     },
     showPanel(newVal) {
       this.$store.commit("UpdateIsEditingState", newVal);
-      this.$store.commit("tags/setIsConfirmingPosi", false);
+      if (!newVal) {
+        this.$store.commit("tags/setIsConfirmingPosi", false);
+      }
     },
   },
   mounted() {
     this.$bus.on("updateHotSpotHV", (data) => {
-      let hptarget = this.someData.hotspots.find(
-        (item) => item.name.toLowerCase() == data.hpname.toLowerCase()
-      );
-      if (hptarget) {
-        console.log("hptarget", hptarget);
-        hptarget.ath = data.ath;
-        hptarget.atv = data.atv;
+      console.error("updateHotSpotHV", data);
+      // let hptarget = this.someData.hotspots.find((item) => item.name.toLowerCase() == data.hpname.toLowerCase());
+
+      let hptarget = this.hotspotList.findIndex((item) => item.name.toLowerCase() == data.hpname.toLowerCase());
+      if (this.hotspot && data.hpname.toLowerCase() == this.hotspot.name.toLowerCase()) {
+        //兼容新增
+        this.hotspot.ath = data.ath;
+        this.hotspot.atv = data.atv;
+      }
+      if (hptarget >= 0) {
+        this.hotspotList[hptarget].ath = data.ath;
+        this.hotspotList[hptarget].atv = data.atv;
+        // console.log("hptarget", this.hotspotList[hptarget]);
+        this.$store.commit("scene/setSaveApiList", "hotspot");
       }
     });
 
     this.$bus.on("openHotspot", (data) => {
-      let idx = this.someData.hotspots.findIndex(
-        (item) => item.name.toLowerCase() == data.toLowerCase()
-      );
+      console.error("openHotspot", data);
+      // let idx = this.someData.hotspots.findIndex((item) => item.name.toLowerCase() == data.toLowerCase());
+      // console.error(this.hotspotList[2].name.toLowerCase(), data.toLowerCase());
+      let idx = this.hotspotList.findIndex((item) => item.name.toLowerCase() == data.toLowerCase());
       // console.log(data);
-      if (data == this.hotspot.name) {
+      if (data == this.hotspotList[idx].name) {
         // window.__krfn.utils.looktohotspot(this.$getKrpano(), this.hotspot.name);
         if (!this.showPanel) {
-          this.open(this.someData.hotspots[idx]);
+          this.open(this.hotspotList[idx]);
         }
         return;
       }
-      if (
-        this.editTitle == "新增" ||
-        this.editTitle == this.$i18n.t("hotspot.add")
-      ) {
+      if (this.editTitle == "新增" || this.editTitle == this.$i18n.t("hotspot.add")) {
         if (this.showPanel) {
           return this.$confirm({
             content: this.$i18n.t("hotspot.close_dialog"),
@@ -212,7 +201,8 @@ export default {
         }
       }
 
-      this.open(this.someData.hotspots[idx]);
+      // this.open(this.someData.hotspots[idx]);
+      this.open(this.hotspotList[idx]);
     });
   },
   methods: {
@@ -227,10 +217,8 @@ export default {
         if (data.type == "edit") {
           this.deleteKRHotspot(data.data);
           this.$bus.emit("addhotspot", data.data);
-          let idx = this.someData.hotspots.findIndex(
-            (item) => item.name == data.data.name
-          );
-          this.someData.hotspots[idx] = data.data;
+          let idx = this.hotspotList.findIndex((item) => item.name == data.data.name);
+          this.hotspotList[idx] = data.data;
         } else {
           this.deleteKRHotspot(data.data);
         }
@@ -239,9 +227,7 @@ export default {
     },
 
     updateInfo() {
-      let iidx = this.info.scenes.findIndex(
-        (item) => this.currentScene.sceneCode == item.sceneCode
-      );
+      let iidx = this.info.scenes.findIndex((item) => this.currentScene.sceneCode == item.sceneCode);
       if (iidx > -1) {
         this.info.scenes[iidx] = {
           ...this.currentScene,
@@ -253,13 +239,11 @@ export default {
       let HV = window.__krfn.utils.getHotspotHV(this.$getKrpano(), data.name);
       data.ath = HV.ath;
       data.atv = HV.atv;
-      let idx = this.someData.hotspots.findIndex(
-        (item) => item.name === data.name
-      );
+      let idx = this.hotspotList.findIndex((item) => item.name === data.name);
       if (idx <= -1) {
-        this.someData.hotspots.push(data);
+        this.hotspotList.push(data);
       } else {
-        this.someData.hotspots[idx] = data;
+        this.hotspotList[idx] = data;
       }
 
       this.currentScene.someData = this.someData;
@@ -267,12 +251,9 @@ export default {
 
       // window.g_hotspotCurrentScale = mapFontSize[data.fontSize] || 1;
 
-      window.g_hotspotCurrentScale =
-        Math.ceil((data.fontSize * 10) / 12) / 10 || 1;
+      window.g_hotspotCurrentScale = Math.ceil((data.fontSize * 10) / 12) / 10 || 1;
       console.log("g_hotspotCurrentScale", g_hotspotCurrentScale);
-      let iidx = this.info.scenes.findIndex(
-        (item) => this.currentScene.sceneCode == item.sceneCode
-      );
+      let iidx = this.info.scenes.findIndex((item) => this.currentScene.sceneCode == item.sceneCode);
       if (iidx > -1) {
         this.info.scenes[iidx] = {
           ...this.currentScene,
@@ -282,32 +263,73 @@ export default {
       this.updateInfo();
     },
     deleteHot(data) {
-      this.someData.hotspots.splice(
-        this.someData.hotspots.findIndex((item) => item.name === data.name),
+      this.hotspotList.splice(
+        this.hotspotList.findIndex((item) => item.name === data.name),
         1
       );
       this.deleteKRHotspot(data);
       this.currentScene.someData = this.someData;
-      this.updateInfo();
-      this.$msg.success(
-        this.$i18n.t("hotspot.delete") + this.$i18n.t("hotspot.success")
-      );
+      // this.updateInfo();
+      this.$msg.success(this.$i18n.t("hotspot.delete") + this.$i18n.t("hotspot.success"));
     },
     open(data) {
       let hotspotData = null;
       if (data.isAdd) {
         this.editTitle = this.$i18n.t("hotspot.add");
+        let customIconInfo = {
+          // 热点图标类型为自定义图标时,图标的数据
+          img: "",
+        };
+        let serialFrameInfo = {
+          // 热点图标类型为序列帧时,序列帧的数据
+          img: "",
+          frameNumber: 0, // 总帧数
+          duration: 0, // 总播放时长(秒)
+        };
+        let personalizedTagInfo = {
+          // 热点图标类型为个性标签时,个性标签的数据
+          isShowLine: true,
+          lineDirection: "left-top",
+          fillColor: "rgba(0, 0, 0, 0.5)",
+          borderColor: "rgba(255, 255, 255, 0.8)",
+          textColor: "rgba(255, 255, 255, 1)",
+          textDirection: "left-right",
+          isTextWrap: false,
+          textNumPerLine: 10,
+        };
+
+        let scene = null;
+        let hyperlink = "";
+        let textarea = "";
+        let image = [];
+        let audio = "";
+        let video = "";
+        let imageTextInfo = {
+          // 热点类型为图文时,图文内容
+          imageList: [],
+          text: "",
+          isApplyToAll: true,
+          audio: {},
+        };
+        let phoneInfo = {
+          // 热点类型为电话时,对应数据
+          phone: "",
+        };
+        let pdfInfo = {
+          // 热点类型为pdf时,对应数据
+          name: "",
+          url: "",
+        };
+        let articleInfo = {
+          html: "",
+        };
         hotspotData = {
           hotspotType: data.hotspotType, // 热点类型,切换场景、图片、视频、音频、链接、文本等等
-
+          navigationId: this.currentScene.sid ? this.currentScene.sid : this.currentScene.id,
           hotspotIconType: "system_icon", // 热点图标的类型,系统图标(system_icon)、自定义图片(custom_image)、序列帧(serial_frame)、个性标签(personalized_tag)
-          img:
-            this.$config.getStaticResource("/panoassets/images/hotspot/icon/") +
-            `img_doticon_${String(data.idxInSystemIconList).padStart(
-              2,
-              "0"
-            )}.svg`, // 热点图标类型为系统图标时,图标在展时段使用的url
+          img: this.$config.getStaticResource("/panoassets/images/hotspot/icon/") + `img_doticon_${String(data.idxInSystemIconList).padStart(2, "0")}.svg`, // 热点图标类型为系统图标时,图标在展时段使用的url
           icontype: "icon" + data.idxInSystemIconList, // 热点图标类型为系统图标时,图标的id
+          fodderId: [],
           customIconInfo: {
             // 热点图标类型为自定义图标时,图标的数据
             img: "",
@@ -332,19 +354,20 @@ export default {
           name: "_" + this.$randomWord(true, 8, 8),
           hotspotTitle: this.$i18n.t("hotspot.click_to_comfirm"),
           fontSize: 12,
-          type: "",
-          link: "",
-          titleDisplayMode: "always", // 'always' | 'never' | 'hover' 标题显示方式
+          // type: "",
+          // link: "",
+          titleDisplayMode: 1, // 'always' | 'never' | 'hover' 标题显示方式
           titlePosition: "top", // 'top' | 'bottom' | 'left' | 'right' | 'custom' 标题相对图标位置
           ath: "",
           atv: "",
           size: 1,
-          secne: null,
+          workId: this.baseInfo.workId,
+          scene: null,
           hyperlink: "",
           textarea: "",
           image: [], // 热点类型为图片时,图片列表
-          audio: "",
-          video: "",
+          video: null,
+          audio: null,
           imageTextInfo: {
             // 热点类型为图文时,图文内容
             imageList: [],
@@ -367,10 +390,8 @@ export default {
         };
 
         this.$bus.emit("addhotspot", hotspotData);
-        this.$getKrpano().set(
-          "layer[tooltip_" + hotspotData.name + "].visible",
-          true
-        );
+
+        this.$getKrpano().set("layer[tooltip_" + hotspotData.name + "].visible", true);
         // debugger;
         setTimeout(() => {
           this.$store.commit("tags/setIsConfirmingPosi", hotspotData.name);
@@ -447,8 +468,10 @@ export default {
          * end of v1.3新增
          */
       }
-      this.$store.commit("SetHotspot", hotspotData);
-      this.showPanel = true;
+      this.$store.commit("hotspot/SetHotspot", hotspotData);
+      setTimeout(() => {
+        this.showPanel = true;
+      }, 100);
 
       if (!data.isAdd) {
         this.editTitle = this.$i18n.t("hotspot.edit");

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotIconType/custom_image.vue

@@ -51,7 +51,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: 'hotspot',
+      hotspot: "hotspot/hotspot",
     }),
   },
   methods: {

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotIconType/personalized_tag.vue

@@ -278,7 +278,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+       hotspot: "hotspot/hotspot",
     }),
     currentTextDirectionIdx() {
       switch (this.hotspot.personalizedTagInfo.textDirection) {

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotIconType/serial_frame.vue

@@ -103,7 +103,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+       hotspot: "hotspot/hotspot",
     }),
   },
   watch: {

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotIconType/system_icon.vue

@@ -46,7 +46,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+       hotspot: "hotspot/hotspot",
     }),
   },
   methods: {

+ 2 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotType/article.vue

@@ -31,7 +31,8 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: 'hotspot',
+      // hotspot: 'hotspot',
+      hotspot: "hotspot/hotspot",
     }),
   },
   methods: {

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotType/imageText.vue

@@ -144,7 +144,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+       hotspot: "hotspot/hotspot",
     }),
   },
   methods: {

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotType/pdf.vue

@@ -36,7 +36,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+       hotspot: "hotspot/hotspot",
       currentScene: "scene/currentScene",
     }),
   },

+ 1 - 1
packages/qjkankan-editor/src/views/hotspot/hotspotType/phone.vue

@@ -25,7 +25,7 @@ export default {
   },
   computed: {
     ...mapGetters({
-      hotspot: "hotspot",
+       hotspot: "hotspot/hotspot",
     }),
   },
 

+ 65 - 108
packages/qjkankan-editor/src/views/mask/setting.vue

@@ -1,71 +1,39 @@
 <template>
-  <div class="cover-panel">
-    <template v-if="currentScene.type !== '4dkk'">
+  <div v-if="currentScene" class="cover-panel">
+    <template v-if="currentScene?.type !== '4dkk' && currentMask">
       <div class="title">
         {{ $i18n.t("edit_settings.mask_setting") }}
-        <i
-          class="iconfont icon-help_i tool-tip-for-editor"
-          v-tooltip="$i18n.t('mask.cover_tips')"
-        />
+        <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('mask.cover_tips')" />
       </div>
       <!-- sky start -->
       <div class="swi-col">
         <span> {{ $i18n.t("edit_settings.sky_mask") }} </span>
-        <switcher
-          :value="sky.isShow"
-          @change="(value) => (sky.isShow = Boolean(value))"
-        ></switcher>
+        <switcher :value="currentMask.data.sky.isShow" @change="(value) => (currentMask.data.sky.isShow = Boolean(value))"></switcher>
       </div>
-      <template v-if="sky.isShow">
+      <template v-if="currentMask.data.sky.isShow">
         <div class="swi-col update-col">
-          <SelectedImage
-            :imgSrc="sky.icon"
-            :defaultImgSrc="
-              require(`@/assets/images/default/mask_bg_${$lang}.png`)
-            "
-            @cancel="onClickCancelSkyLogo"
-          ></SelectedImage>
+          <SelectedImage :imgSrc="currentMask.data.sky.icon" :defaultImgSrc="require(`@/assets/images/default/mask_bg_${$lang}.png`)" @cancel="onClickCancelSkyLogo"></SelectedImage>
           <div class="action">
-            <button
-              @click="onSelectPic('sky')"
-              class="ui-button submit"
-              :class="[$lang]"
-            >
+            <button @click="onSelectPic('sky')" class="ui-button submit" :class="[$lang]">
               <!-- 选择图片 -->
               {{ $i18n.t(`mask.select_image`) }}
             </button>
-            <div
-              class="ui-remark"
-              v-html="$i18n.t(`edit_settings.mask_size`)"
-            ></div>
+            <div class="ui-remark" v-html="$i18n.t(`edit_settings.mask_size`)"></div>
           </div>
         </div>
 
         <div class="up-col">
           <span> {{ $i18n.t(`mask.scaling_ratio`) }} </span>
 
-          <slider
-            v-model="sky.scale"
-            show-stops
-            :marks="scaleMarks"
-            :step="0.1"
-            :min="0.5"
-            :max="2"
-          />
+          <slider v-model="currentMask.data.sky.scale" show-stops :marks="scaleMarks" :step="0.1" :min="0.5" :max="2" />
         </div>
         <div class="swi-col">
           <span> {{ $i18n.t("mask.follow_the_scene") }} </span>
-          <switcher
-            :value="sky.antidistorted"
-            @change="(sa) => (sky.antidistorted = Boolean(sa))"
-          ></switcher>
+          <switcher :value="currentMask.data.sky.antidistorted" @change="(sa) => (currentMask.data.sky.antidistorted = Boolean(sa))"></switcher>
         </div>
         <div class="swi-col">
           <span> {{ $i18n.t("mask.apply_to_all_pano") }} </span>
-          <switcher
-            :value="isApplySkyToAll"
-            @change="(sat) => (isApplySkyToAll = Boolean(sat))"
-          ></switcher>
+          <switcher :value="isApplySkyToAll" @change="(sat) => (isApplySkyToAll = Boolean(sat))"></switcher>
         </div>
       </template>
       <!-- sky end -->
@@ -73,68 +41,38 @@
       <!-- earth start -->
       <div class="swi-col">
         <span> {{ $i18n.t(`edit_settings.bottom_mask`) }} </span>
-        <switcher
-          :value="earth.isShow"
-          @change="(value) => (earth.isShow = Boolean(value))"
-        ></switcher>
+        <switcher :value="currentMask.data.earth.isShow" @change="(value) => (currentMask.data.earth.isShow = Boolean(value))"></switcher>
       </div>
-      <template v-if="earth.isShow">
+      <template v-if="currentMask.data.earth.isShow">
         <div class="swi-col update-col">
-          <SelectedImage
-            :imgSrc="earth.icon"
-            :defaultImgSrc="
-              require(`@/assets/images/default/mask_bg_${$lang}.png`)
-            "
-            @cancel="onClickCancelEarthLogo"
-          ></SelectedImage>
+          <SelectedImage :imgSrc="currentMask.data.earth.icon" :defaultImgSrc="require(`@/assets/images/default/mask_bg_${$lang}.png`)" @cancel="onClickCancelEarthLogo"></SelectedImage>
           <div class="action">
             <button @click="onSelectPic('earth')" class="ui-button submit">
               {{ $i18n.t(`mask.select_image`) }}
             </button>
-            <div
-              class="ui-remark"
-              v-html="$i18n.t(`edit_settings.mask_size`)"
-            ></div>
+            <div class="ui-remark" v-html="$i18n.t(`edit_settings.mask_size`)"></div>
           </div>
         </div>
 
         <div class="up-col">
           <span> {{ $i18n.t(`mask.scaling_ratio`) }} </span>
-          <slider
-            v-model="earth.scale"
-            show-stops
-            :marks="scaleMarks"
-            :step="0.1"
-            :min="0.5"
-            :max="2"
-          />
+          <slider v-model="currentMask.data.earth.scale" show-stops :marks="scaleMarks" :step="0.1" :min="0.5" :max="2" />
         </div>
         <div class="swi-col">
           <span> {{ $i18n.t("mask.follow_the_scene") }} </span>
-          <switcher
-            :value="earth.antidistorted"
-            @change="(ve) => (earth.antidistorted = Boolean(ve))"
-          ></switcher>
+          <switcher :value="currentMask.data.earth.antidistorted" @change="(ve) => (currentMask.data.earth.antidistorted = Boolean(ve))"></switcher>
         </div>
         <div class="swi-col">
           <span> {{ $i18n.t("mask.apply_to_all_pano") }} </span>
-          <switcher
-            :value="isApplyEarthToAll"
-            @change="(vet) => (isApplyEarthToAll = Boolean(vet))"
-          ></switcher>
+          <switcher :value="isApplyEarthToAll" @change="(vet) => (isApplyEarthToAll = Boolean(vet))"></switcher>
         </div>
       </template>
       <!-- earth end -->
     </template>
 
-    <div class="goto-4dkk-tip" v-if="currentScene.type === '4dkk'">
+    <div class="goto-4dkk-tip" v-if="currentScene?.type === '4dkk'">
       <div class="img-wrap">
-        <img
-          class=""
-          src="@/assets/images/default/goto-4dage.png"
-          alt=""
-          draggable="false"
-        />
+        <img class="" src="@/assets/images/default/goto-4dage.png" alt="" draggable="false" />
         <div class="tip-text">
           {{ $i18n.t("screen.goto_4dkk_edit_tips") }}
         </div>
@@ -145,12 +83,7 @@
     </div>
 
     <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
-      <MaterialSelector
-        :title="$i18n.t(`gather.select_material`)"
-        @cancel="isShowSelectionWindow = false"
-        @submit="handleSubmitFromMaterialSelector"
-        :selectableType="['image']"
-      />
+      <MaterialSelector :title="$i18n.t(`gather.select_material`)" @cancel="isShowSelectionWindow = false" @submit="handleSubmitFromMaterialSelector" :selectableType="['image']" />
     </div>
   </div>
 </template>
@@ -171,7 +104,14 @@ export default {
   computed: {
     ...mapGetters({
       currentScene: "scene/currentScene",
+      baseInfo: "base/baseInfo",
+      workCustomMaskList: "mask/workCustomMaskList",
     }),
+
+    currentMask() {
+      let item = this.workCustomMaskList.find((item) => item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid);
+      return item;
+    },
   },
   data() {
     return {
@@ -201,11 +141,19 @@ export default {
     };
   },
   watch: {
-    sky: {
+    currentMask: {
+      handler(newVal, oldVal) {
+        if (newVal && oldVal && this.$route.name == "mask" && newVal.navigationId == oldVal.navigationId) {
+          this.$store.commit("scene/setSaveApiList", "mask");
+        }
+      },
+      deep: true,
+    },
+    "currentMask.data.sky": {
       handler: function (val) {
         if (this.currentScene.type === "pano" && val) {
-          this.currentScene.customMask.sky = val;
-          this.updateCurrentScene();
+          // this.currentScene.customMask.sky = val;
+          // this.updateCurrentScene();
           this.handlePeakStatus(val.isShow);
           this.handlePeakScale(val.scale);
           this.handlePeakURL(val.icon);
@@ -214,11 +162,11 @@ export default {
       },
       deep: true,
     },
-    earth: {
+    "currentMask.data.earth": {
       handler: function (val) {
         if (this.currentScene.type === "pano" && val) {
-          this.currentScene.customMask.earth = val;
-          this.updateCurrentScene();
+          // this.currentScene.customMask.earth = val;
+          // this.updateCurrentScene();
           this.handleNadirStatus(val.isShow);
           this.handleNadirScale(val.scale);
           this.handleNadirURL(val.icon);
@@ -233,9 +181,10 @@ export default {
           this.$confirm({
             content: this.$i18n.t("mask.is_apply_to_all_pano"),
             ok: () => {
-              this.$store.dispatch("scene/applycustomMaskToAll", "sky");
+              this.$store.commit("mask/applycustomMaskToAll", { type: "sky", currentMask: this.currentMask });
               this.$msg.success(this.$i18n.t("gather.edit_success"));
               setTimeout(() => (this.isApplySkyToAll = false), 1000);
+              this.$store.commit("scene/setSaveApiList", "mask");
             },
             no: () => {
               this.isApplySkyToAll = false;
@@ -251,9 +200,10 @@ export default {
           this.$confirm({
             content: this.$i18n.t("mask.is_apply_to_all_pano"),
             ok: () => {
-              this.$store.dispatch("scene/applycustomMaskToAll", "earth");
+              this.$store.commit("mask/applycustomMaskToAll", { type: "earth", currentMask: this.currentMask });
               this.$msg.success(this.$i18n.t("gather.edit_success"));
               setTimeout(() => (this.isApplyEarthToAll = false), 1000);
+              this.$store.commit("scene/setSaveApiList", "mask");
             },
             no: () => {
               this.isApplyEarthToAll = false;
@@ -263,14 +213,15 @@ export default {
       },
       immediate: true,
     },
-    "currentScene.customMask": {
+    currentScene: {
       handler: function (newVal, oldVal) {
         if (newVal && newVal !== oldVal) {
-          const { sky, earth } = newVal;
+          // let item = this.workCustomMaskList.find((item) => item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid);
+          // const { sky, earth } = item.data;
           // this.isApplyEarthToAll = false;
           // this.isApplySkyToAll = false;
-          this.sky = { ...sky };
-          this.earth = { ...earth };
+          // this.sky = { ...sky };
+          // this.earth = { ...earth };
         }
       },
       deep: true,
@@ -288,26 +239,32 @@ export default {
   },
   mounted() {},
   methods: {
+    updataMaskList(type, item) {
+      // let item = this.baseInfo.workCustomMaskList.find((item) => item.navigationId == this.currentScene.id);
+      // console.error();
+    },
     onClickGo4dkk() {
       window.open("/#/scene");
     },
     onClickCancelSkyLogo() {
       this.handlePeakURL("static/template/skin/masking.png");
-      this.sky.fodderId = "";
-      this.sky.icon = "";
+      this.currentMask.data.sky.fodderId = "";
+      this.currentMask.data.sky.icon = "";
     },
     onClickCancelEarthLogo() {
       this.handleNadirURL("static/template/skin/masking.png");
-      this.earth.fodderId = "";
-      this.earth.icon = "";
+      this.currentMask.data.earth.fodderId = "";
+      this.currentMask.data.earth.icon = "";
     },
     onSelectPic(type) {
       this.isSelect = type;
       this.isShowSelectionWindow = true;
     },
     handleSubmitFromMaterialSelector(selected) {
-      this[this.isSelect].icon = selected[0].icon;
-      this[this.isSelect].fodderId = selected[0].id;
+      this.currentMask.data[this.isSelect].icon = selected[0].icon;
+      this.currentMask.data[this.isSelect].fodderId = selected[0].id;
+      // this[this.isSelect].icon = selected[0].icon;
+      // this[this.isSelect].fodderId = selected[0].id;
       this.isShowSelectionWindow = false;
     },
     handlePeakStatus(status) {
@@ -334,7 +291,7 @@ export default {
       if (kr) {
         kr.set("hotspot[peaklogo].distorted", anti);
         if (!anti) {
-          kr.set("hotspot[peaklogo].scale", this.sky.scale * 0.5);
+          kr.set("hotspot[peaklogo].scale", this.currentMask.data.sky.scale * 0.5);
         }
       }
     },
@@ -362,7 +319,7 @@ export default {
       if (kr) {
         kr.set("hotspot[nadirlogo].distorted", anti);
         if (!anti) {
-          kr.set("hotspot[nadirlogo].scale", this.earth.scale * 0.5);
+          kr.set("hotspot[nadirlogo].scale", this.currentMask.data.earth.scale * 0.5);
         }
       }
     },

+ 30 - 41
packages/qjkankan-editor/src/views/material/popup/share.vue

@@ -1,9 +1,6 @@
 <template>
   <popup v-if="show">
-    <div
-      class="ui-message ui-message-confirm message-material"
-      style="width: 500px"
-    >
+    <div class="ui-message ui-message-confirm message-material" style="width: 500px">
       <div class="ui-message-header header-material">
         <span>{{ share }}</span>
         <span @click="$emit('close')">
@@ -14,14 +11,7 @@
         <ul>
           <li>
             <span>{{ work_link }}</span>
-            <input
-              :title="item.share + `&vr=${defaultscenesCode}&lang=${$lang}`"
-              class="ui-input"
-              disabled
-              type="text"
-              maxlength="15"
-              :value="item.share + `&vr=${defaultscenesCode}&lang=${$lang}`"
-            />
+            <input :title="path + `?id=${item.id}&lang=${$lang}`" class="ui-input" disabled type="text" maxlength="15" :value="path + `?id=${item.id}&lang=${$lang}`" />
           </li>
           <li>
             <span>{{ work_qrCode }} </span>
@@ -29,9 +19,7 @@
             <div
               class="qrcode"
               :style="{
-                backgroundImage: `url(${
-                  currentQRcode || require('@/assets/svg/loading.svg')
-                })`,
+                backgroundImage: `url(${currentQRcode || require('@/assets/svg/loading.svg')})`,
               }"
             />
           </li>
@@ -39,17 +27,10 @@
       </div>
 
       <div class="ui-message-footer footer-material">
-        <button
-          @click="downloadImg(item)"
-          class="ui-button"
-          :class="currentQRcode.length === 0 ? 'disable' : ''"
-        >
+        <button @click="downloadImg(item)" class="ui-button" :class="currentQRcode.length === 0 ? 'disable' : ''">
           {{ download_qrCode }}
         </button>
-        <button
-          @click="copy(item.share + `&vr=${defaultscenesCode}&lang=${$lang}`)"
-          class="ui-button submit"
-        >
+        <button @click="copy(path + `?id=${item.id}&lang=${$lang}`)" class="ui-button submit">
           {{ copy_link }}
         </button>
       </div>
@@ -59,7 +40,7 @@
 
 <script>
 import Popup from "@/components/shared/popup";
-import { getQrCode } from "@/api";
+import { getQrCode, getWorkLogo } from "@/api";
 import { i18n } from "@/lang";
 
 export default {
@@ -76,28 +57,38 @@ export default {
       copy_link: i18n.t("material.works.copy_link"),
       key: "",
       currentQRcode: "",
+      path: process.env.VUE_APP_PROXY_URL_ROOT + "/panorama/showMobile.html",
     };
   },
   computed: {
-    defaultscenesCode: function () {
-      if (this.item.firstScene) {
-        return this.item.firstScene.sceneCode || "";
-      } else {
-        return Array.from(this.item.scenes).length > 0
-          ? Array.from(this.item.scenes)[0].sceneCode
-          : "";
-      }
-    },
+    // defaultscenesCode: function () {
+    //   if (this.item.firstScene) {
+    //     return this.item.firstScene.sceneCode || "";
+    //   } else {
+    //     return Array.from(this.item.scenes).length > 0
+    //       ? Array.from(this.item.scenes)[0].sceneCode
+    //       : "";
+    //   }
+    // },
   },
   watch: {
     item: {
-      handler: function (val) {
+      handler: async function (val) {
         this.currentQRcode = "";
-        if (val.share) {
+        // if (val.share) {
+        if (val.id) {
           const shareLink =
-            val.share + `&vr=${this.defaultscenesCode}&lang=${this.$lang}`;
+            // val.share + `&vr=${this.defaultscenesCode}&lang=${this.$lang}`;
+            this.path + `?id=${val.id}&lang=${this.$lang}`;
           console.log("当前分享", shareLink);
-          const llogo = val.logo || "";
+          let llogo = "";
+          await getWorkLogo({ workId: val.id }).then((res) => {
+            if (res.data && res.data.logo) {
+              llogo = res.data.logo;
+            } else {
+              llogo = "";
+            }
+          });
           getQrCode({ logo: llogo, text: shareLink }).then((res) => {
             if (res.code === 0) {
               this.currentQRcode = res.data.img;
@@ -138,9 +129,7 @@ export default {
       textArea.select();
 
       try {
-        document.execCommand("copy")
-          ? this.$msg.success(this.$i18n.t("gather.scene_link_copy_tips"))
-          : this.$msg.error(this.$i18n.t("gather.scene_link_copy_failed"));
+        document.execCommand("copy") ? this.$msg.success(this.$i18n.t("gather.scene_link_copy_tips")) : this.$msg.error(this.$i18n.t("gather.scene_link_copy_failed"));
       } catch (err) {
         this.$msg.error(this.$i18n.t("gather.scene_link_copy_failed"));
       }

+ 251 - 215
packages/qjkankan-editor/src/views/material/works/list.vue

@@ -1,22 +1,11 @@
 <template>
-  <div
-    class="scroll-container"
-    ref="w-list-ref"
-    @scroll.self="onWorkListScroll"
-  >
+  <div class="scroll-container" ref="w-list-ref" @scroll.self="onWorkListScroll">
     <div class="back-top" @click="onClickBackTop" v-show="isShowBackTopBtn">
       <i class="iconfont icon-top"></i>
     </div>
     <div class="mask" v-show="isShowMask"></div>
     <!-- 断网时,输入关键字触发网络请求后,骨架图的隐藏会触发v-infinite-scroll,原因未知。进而导致循环触发,因此list为空时要禁用v-infinite-scroll -->
-    <ul
-      class="w-list"
-      v-if="!(list.length === 0 && !hasMoreData)"
-      v-infinite-scroll="requestMoreData"
-      :infinite-scroll-disabled="
-        !hasMoreData || isRequestingMoreData || list.length === 0
-      "
-    >
+    <ul class="w-list" v-if="!(list.length === 0 && !hasMoreData)" v-infinite-scroll="requestMoreData" :infinite-scroll-disabled="!hasMoreData || isRequestingMoreData || list.length === 0">
       <li class="add-work" @click="add">
         <div class="wrapper">
           <div class="add-con">
@@ -35,26 +24,14 @@
           </div>
         </li>
       </template>
-      <li
-        v-for="(item, i) in list"
-        :key="i"
-        :class="{ 'has-more-data': hasMoreData }"
-      >
+      <li v-for="(item, i) in list" :key="i" :class="{ 'has-more-data': hasMoreData }">
         <div class="wrapper">
           <div class="li-hover">
-            <span class="lipreview" @click="handlePreview(item)">{{
-              preview
-            }}</span>
+            <span class="lipreview" @click="handlePreview(item)">{{ preview }}</span>
             <ul class="oper">
-              <li class="comfirmhover" @click="edit(item)">
-                <i class="iconfont icon-works_editor"></i>{{ edittips }}
-              </li>
-              <li class="comfirmhover" @click="openShare(item)">
-                <i class="iconfont icon-works_share"></i>{{ share }}
-              </li>
-              <li class="cancelhover" @click="del(item, i)">
-                <i class="iconfont icon-works_delete"></i>{{ deltips }}
-              </li>
+              <li class="comfirmhover" @click="edit(item)"><i class="iconfont icon-works_editor"></i>{{ edittips }}</li>
+              <li class="comfirmhover" @click="openShare(item)"><i class="iconfont icon-works_share"></i>{{ share }}</li>
+              <li class="cancelhover" @click="del(item, i)"><i class="iconfont icon-works_delete"></i>{{ deltips }}</li>
             </ul>
           </div>
           <div class="img" @click="handlePreview(item)">
@@ -62,60 +39,32 @@
           </div>
           <div class="li-info">
             <div>
-              <span class="shenglve tttttt" :title="item.name || no_title">{{
-                item.name || no_title
-              }}</span>
+              <span class="shenglve tttttt" :title="item.name || no_title">{{ item.name || no_title }}</span>
             </div>
             <div>
               <span>{{ item.createTime.split(" ")[0] }}</span>
-              <div :title="item.visit">
-                <i class="iconfont icon-works_look"></i
-                >{{ item.visit > 10000 ? "1w+" : item.visit }}
-              </div>
+              <div :title="item.visit"><i class="iconfont icon-works_look"></i>{{ item.visit > 10000 ? "1w+" : item.visit }}</div>
             </div>
           </div>
         </div>
       </li>
-      <div
-        class="work-list-loading-wrapper"
-        v-show="isRequestingMoreData && list.length !== 0"
-      >
-        <img
-          class="work-list-loading"
-          :src="require('@/assets/images/icons/work-list-loading.gif')"
-        />
+      <div class="work-list-loading-wrapper" v-show="isRequestingMoreData && list.length !== 0">
+        <img class="work-list-loading" :src="require('@/assets/images/icons/work-list-loading.gif')" />
       </div>
     </ul>
-    <div
-      class="nodata"
-      v-if="list.length == 0 && !hasMoreData && lastestUsedSearchKey"
-    >
+    <div class="nodata" v-if="list.length == 0 && !hasMoreData && lastestUsedSearchKey">
       <img :src="$noresult" alt="" />
       <span>{{ no_search_result }}~</span>
     </div>
-    <div
-      class="nodata"
-      v-if="list.length == 0 && !hasMoreData && !lastestUsedSearchKey"
-    >
+    <div class="nodata" v-if="list.length == 0 && !hasMoreData && !lastestUsedSearchKey">
       <img :src="config.empty" alt="" />
       <span>{{ no_works }}</span>
       <button @click="add" class="upload-btn-in-table">{{ create }}</button>
     </div>
 
-    <share
-      :show="showShare"
-      :item="shareItem"
-      @close="showShare = false"
-    ></share>
-
-    <preview
-      v-if="showItem"
-      :name="showItem.name"
-      :show="showPreview"
-      :ifr="`./show.html?id=${showItem.id}&lang=${$lang}`"
-      :dark="false"
-      @close="showPreview = false"
-    />
+    <share :show="showShare" :item="shareItem" @close="showShare = false"></share>
+
+    <preview v-if="showItem" :name="showItem.name" :show="showPreview" :ifr="`./show.html?id=${showItem.id}&lang=${$lang}`" :dark="false" @close="showPreview = false" />
 
     <div class="dialog" style="z-index: 1000" v-if="isShowMaterialSelector">
       <MaterialSelector
@@ -141,14 +90,9 @@ import MaterialSelector from "@/components/materialSelector.vue";
 import { mapGetters } from "vuex";
 import { i18n } from "@/lang";
 import { $waiting } from "@/components/shared/loading";
-
-import {
-  addWorks,
-  getWorksList,
-  delWorks,
-  getPanoInfo,
-  saveWorks,
-} from "@/api";
+import { isUpgradeAdapter } from "@/utils/fixVersion";
+import { addWorks, getWorksList, delWorks, getPanoInfo, getSceneList, saveWorks, addWork } from "@/api";
+import { version } from "less";
 
 export default {
   components: {
@@ -221,7 +165,6 @@ export default {
     },
   },
   methods: {
-  
     refreshListDebounced: debounce(
       function () {
         this.list = [];
@@ -233,19 +176,29 @@ export default {
       false
     ),
     openShare(data) {
-      console.log(data);
-      getPanoInfo(data.id, (data) => {
-        if (data.scenes.length <= 0) {
-          return this.$msg.warning(this.$i18n.t("material.works.no_link"));
-        }
-        this.showShare = true;
-        this.shareItem = data;
-      });
+      this.showShare = true;
+      this.shareItem = { id: data.id };
+      // getSceneList(data.id, (res) => {
+      //   let data = res.data;
+      //   if ( data.panoList.length <= 0) {
+      //     return this.$msg.warning(this.$i18n.t("material.works.no_link"));
+      //   }
+      //   this.showShare = true;
+      //   this.shareItem = data;
+      // });
+      // getPanoInfo(data.id, (data) => {
+      //   if (data.scenes.length <= 0) {
+      //     return this.$msg.warning(this.$i18n.t("material.works.no_link"));
+      //   }
+      //   this.showShare = true;
+      //   this.shareItem = data;
+      // });
     },
 
     handlePreview(item) {
-      getPanoInfo(item.id, (data) => {
-        if (data.scenes.length <= 0) {
+      getSceneList(item.id, (res) => {
+        let data = res.data;
+        if (data.fdkkList.length <= 0 && data.panoList.length <= 0) {
           return this.$msg.warning(this.$i18n.t("material.works.no_link"));
         }
         this.showItem = {
@@ -254,138 +207,223 @@ export default {
         };
         this.showPreview = true;
       });
+      // getPanoInfo(item.id, (data) => {
+      //   if (data.scenes.length <= 0) {
+      //     return this.$msg.warning(this.$i18n.t("material.works.no_link"));
+      //   }
+      //   this.showItem = {
+      //     ...item,
+      //     ...data,
+      //   };
+      //   this.showPreview = true;
+      // });
     },
     add() {
       // 新建作品,弹窗让用户给作品选择素材。
-      $waiting.show();
-      addWorks({}, (res) => {
-        $waiting.hide();
-        this.newWorkId = res.data.id;
-        this.isShowMaterialSelector = true;
-      });
+      // $waiting.show();
+      // addWorks({}, (res) => {
+      //   $waiting.hide();
+      //   this.newWorkId = res.data.id;
+      //   this.isShowMaterialSelector = true;
+      // });
+
+      this.isShowMaterialSelector = true;
     },
     handleSubmitFromMaterialSelector(selected) {
       $waiting.show();
-      // 拿新作品的初始数据
-      getPanoInfo(
-        this.newWorkId,
-        // 拿到了。
-        (data) => {
-          // 往里边添加用户选中的素材。
-          this.$store.commit("SetInfo", data);
-          console.log("selected", selected);
-          for (const [key, item] of Object.entries(selected)) {
-            if (item.materialType === "pano") {
-              let newScene = {
-                icon: item.icon,
-                sceneCode: item.sceneCode,
-                sceneTitle: item.name,
-                category: this.info.catalogs[0].id,
-                type: "pano",
-                id: "s_" + this.$randomWord(true, 8, 8),
-              };
-
-              console.log("key", key);
-              if (Number(key) === 0) {
-                //新建时开天空mask
-                newScene = Object.assign(newScene, this.info.scenes[0]);
-                newScene.customMask.sky.isShow = true;
-                this.info.scenes[0] = newScene;
-              } else {
-                newScene = Object.assign(newScene, {
-                  customMask: this.info.scenes[0].customMask,
-                  initVisual: this.info.scenes[0].initVisual,
-                });
-                newScene.customMask.sky.isShow = true;
-                this.info.scenes.push(newScene);
-              }
-            } else if (item.materialType === "3D") {
-              let newScene = {
-                icon: item.thumb,
-                sceneCode: item.num,
-                sceneTitle: item.sceneName,
-                category: this.info.catalogs[0].id,
-                type: "4dkk",
-                id: "s_" + this.$randomWord(true, 8, 8),
-              };
-              if (Number(key) === 0) {
-                this.info.scenes[0] = null;
-                this.info.scenes[0] = newScene;
-              } else {
-                this.info.scenes.push(newScene);
-              }
-            }
-          }
-
-          // 保存新作品
-          saveWorks(
-            {
-              id: this.newWorkId,
-              password: "",
-              someData: {
-                ...this.info,
-                status: 1,
-                icon: this.info.scenes[0].icon,
-              },
-            },
-            // 保存成功
-            () => {
-              $waiting.hide();
-              // 隐藏素材选择弹窗
-              this.isShowMaterialSelector = false;
-
-              // 刷新作品列表
-              this.list = [];
-              this.isRequestingMoreData = false;
-              this.hasMoreData = true;
-              this.requestMoreData()
-                .then(() => {
-                  // 刷新成功
-
-                  // 弹出提示窗口
-                  this.$confirm({
-                    title: this.$i18n.t("tips_code.tips"),
-                    content: this.$i18n.t("material.works.had_created"),
-                    okText: this.$i18n.t("material.works.goto_preview"),
-                    ok: () => {
-                      this.handlePreview(this.list[0]);
-                      this.newWorkId = "";
-                      this.$store.commit("SetInfo", {});
-                    },
-                    ok2Text: this.$i18n.t("material.works.continue_edit"),
-                    ok2: () => {
-                      window.open(
-                        `./edit.html?id=${this.newWorkId}&lang=${this.$lang}`
-                      );
-                      this.newWorkId = "";
-                      this.$store.commit("SetInfo", {});
-                    },
-                  });
-                })
-                .catch(() => {
-                  this.$msg.message(
-                    this.$i18n.t("material.works.had_created_but_no_link")
-                  );
-                  console.error("已成功新建作品,但刷新作品列表失败。");
-                });
-            },
-            // 保存失败,删除新建的作品。
-            (error) => {
-              $waiting.hide();
-              console.error("保存失败:", error);
-              delWorks(this.newWorkId);
-              this.newWorkId = "";
-              this.$store.commit("SetInfo", {});
-            }
-          );
-        },
-        // 没拿到,删除新建的作品。
-        (error) => {
-          console.error("没拿到新建的作品数据:", error);
-          delWorks(this.newWorkId);
-          this.newWorkId = "";
+      console.error(selected);
+      let list = [];
+      for (const [index, item] of Object.entries(selected)) {
+        let newScene = {};
+        if (item.materialType === "pano") {
+          newScene = {
+            icon: item.icon,
+            sceneCode: item.sceneCode,
+            name: item.name,
+            // category: this.info.catalogs[0].id,
+            type: "pano",
+            fodderId: item.id,
+          };
+          list.push(newScene);
+        } else if (item.materialType === "3D") {
+          newScene = {
+            icon: item.thumb,
+            sceneCode: item.num,
+            name: item.sceneName,
+            type: "4dkk",
+            version: isUpgradeAdapter(item.isUpgrade),
+          };
+          // if (Number(index) === 0) {
+          //   this.info.scenes[0] = null;
+          //   this.info.scenes[0] = newScene;
+          // } else {
+          //   this.info.scenes.push(newScene);
+          // }
+          list.push(newScene);
         }
-      );
+        console.error(list);
+      }
+      addWork({ sceneDtoList: list }, (ok) => {
+        console.error("ok", ok);
+        this.newWorkId = ok.data.id;
+        addWorks({ workId: this.newWorkId }, (res) => {
+          $waiting.hide();
+          // 隐藏素材选择弹窗
+          this.isShowMaterialSelector = false;
+
+          // 刷新作品列表
+          this.list = [];
+          this.isRequestingMoreData = false;
+          this.hasMoreData = true;
+          this.requestMoreData()
+            .then(() => {
+              // 刷新成功
+
+              // 弹出提示窗口
+              this.$confirm({
+                title: this.$i18n.t("tips_code.tips"),
+                content: this.$i18n.t("material.works.had_created"),
+                okText: this.$i18n.t("material.works.goto_preview"),
+                ok: () => {
+                  this.handlePreview(this.list[0]);
+                  this.newWorkId = "";
+                  this.$store.commit("SetInfo", {});
+                },
+                ok2Text: this.$i18n.t("material.works.continue_edit"),
+                ok2: () => {
+                  window.open(`./edit.html?id=${this.newWorkId}&lang=${this.$lang}`);
+                  this.newWorkId = "";
+                  this.$store.commit("SetInfo", {});
+                },
+              });
+            })
+            .catch(() => {
+              this.$msg.message(this.$i18n.t("material.works.had_created_but_no_link"));
+              console.error("已成功新建作品,但刷新作品列表失败。");
+            });
+        });
+      });
+
+      // 拿新作品的初始数据
+      // getPanoInfo(
+      //   this.newWorkId,
+      //   // 拿到了。
+      //   (data) => {
+      //     // 往里边添加用户选中的素材。
+      //     this.$store.commit("SetInfo", data);
+      //     console.log("selected", selected);
+      //     for (const [key, item] of Object.entries(selected)) {
+      //       if (item.materialType === "pano") {
+      //         let newScene = {
+      //           icon: item.icon,
+      //           sceneCode: item.sceneCode,
+      //           sceneTitle: item.name,
+      //           category: this.info.catalogs[0].id,
+      //           type: "pano",
+      //           id: "s_" + this.$randomWord(true, 8, 8),
+      //         };
+
+      //         console.log("key", key);
+      //         if (Number(key) === 0) {
+      //           //新建时开天空mask
+      //           newScene = Object.assign(newScene, this.info.scenes[0]);
+      //           newScene.customMask.sky.isShow = true;
+      //           this.info.scenes[0] = newScene;
+      //         } else {
+      //           newScene = Object.assign(newScene, {
+      //             customMask: this.info.scenes[0].customMask,
+      //             initVisual: this.info.scenes[0].initVisual,
+      //           });
+      //           newScene.customMask.sky.isShow = true;
+      //           this.info.scenes.push(newScene);
+      //         }
+      //       } else if (item.materialType === "3D") {
+      //         let newScene = {
+      //           icon: item.thumb,
+      //           sceneCode: item.num,
+      //           sceneTitle: item.sceneName,
+      //           category: this.info.catalogs[0].id,
+      //           type: "4dkk",
+      //           id: "s_" + this.$randomWord(true, 8, 8),
+      //         };
+      //         if (Number(key) === 0) {
+      //           this.info.scenes[0] = null;
+      //           this.info.scenes[0] = newScene;
+      //         } else {
+      //           this.info.scenes.push(newScene);
+      //         }
+      //       }
+      //     }
+
+      //     // 保存新作品
+      //     saveWorks(
+      //       {
+      //         id: this.newWorkId,
+      //         password: "",
+      //         someData: {
+      //           ...this.info,
+      //           status: 1,
+      //           icon: this.info.scenes[0].icon,
+      //         },
+      //       },
+      //       // 保存成功
+      //       () => {
+      //         $waiting.hide();
+      //         // 隐藏素材选择弹窗
+      //         this.isShowMaterialSelector = false;
+
+      //         // 刷新作品列表
+      //         this.list = [];
+      //         this.isRequestingMoreData = false;
+      //         this.hasMoreData = true;
+      //         this.requestMoreData()
+      //           .then(() => {
+      //             // 刷新成功
+
+      //             // 弹出提示窗口
+      //             this.$confirm({
+      //               title: this.$i18n.t("tips_code.tips"),
+      //               content: this.$i18n.t("material.works.had_created"),
+      //               okText: this.$i18n.t("material.works.goto_preview"),
+      //               ok: () => {
+      //                 this.handlePreview(this.list[0]);
+      //                 this.newWorkId = "";
+      //                 this.$store.commit("SetInfo", {});
+      //               },
+      //               ok2Text: this.$i18n.t("material.works.continue_edit"),
+      //               ok2: () => {
+      //                 window.open(
+      //                   `./edit.html?id=${this.newWorkId}&lang=${this.$lang}`
+      //                 );
+      //                 this.newWorkId = "";
+      //                 this.$store.commit("SetInfo", {});
+      //               },
+      //             });
+      //           })
+      //           .catch(() => {
+      //             this.$msg.message(
+      //               this.$i18n.t("material.works.had_created_but_no_link")
+      //             );
+      //             console.error("已成功新建作品,但刷新作品列表失败。");
+      //           });
+      //       },
+      //       // 保存失败,删除新建的作品。
+      //       (error) => {
+      //         $waiting.hide();
+      //         console.error("保存失败:", error);
+      //         delWorks(this.newWorkId);
+      //         this.newWorkId = "";
+      //         this.$store.commit("SetInfo", {});
+      //       }
+      //     );
+      //   },
+      //   // 没拿到,删除新建的作品。
+      //   (error) => {
+      //     console.error("没拿到新建的作品数据:", error);
+      //     delWorks(this.newWorkId);
+      //     this.newWorkId = "";
+      //   }
+      // );
     },
 
     edit(item) {
@@ -488,10 +526,8 @@ export default {
         }
 
         const nowTime = Date.now();
-        const assumeScrollTop =
-          totalScroll - ((nowTime - startTime) * totalScroll) / 500;
-        this.$refs["w-list-ref"].scrollTop =
-          assumeScrollTop > 0 ? assumeScrollTop : 0;
+        const assumeScrollTop = totalScroll - ((nowTime - startTime) * totalScroll) / 500;
+        this.$refs["w-list-ref"].scrollTop = assumeScrollTop > 0 ? assumeScrollTop : 0;
         requestAnimationFrame(fn);
       };
       requestAnimationFrame(fn);

+ 85 - 62
packages/qjkankan-editor/src/views/navigation/groupSettings.vue

@@ -2,23 +2,17 @@
   <div class="group-settings" app-border dir-right>
     <div class="ui-title-big">
       {{ $i18n.t("navigation.scene_navigation") }}
-      <i
-        class="iconfont icon-help_i tool-tip-for-editor"
-        v-tooltip="$i18n.t('navigation.scene_tips')"
-      >
-      </i>
+      <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('navigation.scene_tips')"> </i>
     </div>
-    <button
-      class="ui-button create-group-btn"
-      @click="onRequestForAddLevel1Group"
-    >
+    <!-- <button class="ui-button create-group-btn" @click="onRequestForAddLevel1Group"> -->
+    <button class="ui-button create-group-btn" @click="addNode(null)">
       <i class="iconfont icon-editor_add"></i>
       {{ $i18n.t("navigation.add_group") }}
     </button>
 
     <div class="scene-group-wrap">
-      <InsertPositionTip position-debug="-1" :index="0"></InsertPositionTip>
-      <div v-for="(item, index) of catalogTopology" :key="item.id">
+      <!-- <InsertPositionTip position-debug="-1" :index="0"></InsertPositionTip>
+      <div v-for="(item, index) of info.navigationTrees" :key="item.id">
         <SceneGroupInEditor
           ref="scene-group"
           :groupNode="item"
@@ -29,11 +23,10 @@
           @renameGroup="onRenameGroup"
           @deleteGroup="onDeleteGroup"
         />
-        <InsertPositionTip
-          position-debug="0"
-          :index="index + 1"
-        ></InsertPositionTip>
-      </div>
+        <InsertPositionTip position-debug="0" :index="index + 1"></InsertPositionTip>
+      </div> -->
+
+      <dragTree ref="childTree" />
     </div>
   </div>
 </template>
@@ -43,23 +36,76 @@ import SceneGroupInEditor from "@/components/sceneGroupInEditor.vue";
 import { mapGetters } from "vuex";
 import { deepClone } from "@/utils/other.js";
 import InsertPositionTip from "@/components/insertPositionTipInEditor.vue";
-
+import dragTree from "@/components/dragTree/index.vue";
 export default {
   components: {
     SceneGroupInEditor,
     InsertPositionTip,
+    dragTree,
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      info: "base/baseInfo",
+      isSave: "navigation/isSave",
       catalogTopology: "catalogTopology",
     }),
   },
   data() {
     return {};
   },
-  watch: {},
+  watch: {
+    "info.navigationTrees": {
+      // immediate: true,
+      handler: function (newVal, oldVal) {
+        if (newVal && oldVal && this.$route.name == "navigation") {
+          // console.error("navigationTrees change", this.isSave);
+          this.$store.commit("scene/setSaveApiList", "navigation");
+          // if (!this.isSave && oldVal) {
+          //   console.error("navigationTrees change", this.isSave);
+          //   this.$store.commit("scene/setSaveApiList", "navigation");
+          // } else {
+          //   this.$store.commit("navigation/setData", { isSave: false });
+          // }
+        }
+      },
+      deep: true,
+    },
+  },
   methods: {
+    addNode(data) {
+      this.$refs.childTree.addNode(null);
+      // let roundId = new Date().getTime();
+      // let group = {
+      //   id: "add_" + roundId,
+      //   sort: 0,
+      //   name: data ? this.$i18n.t("navigation.group_two") : this.$i18n.t("navigation.group_one"),
+      //   type: "group",
+      //   level: data ? 1 : 0,
+      //   children: [],
+      // };
+      // if (data) {
+      //   if (data.children.length && data.children[0].type != "group") {
+      //     //新增子目录的时候,如果目录里面没有子目录,新增一个默认目录包含旧数据
+      //     let list = JSON.parse(JSON.stringify(data.children));
+      //     data.children = [];
+      //     let defaultDir = {
+      //       id: "add_" + (roundId + 1),
+      //       sort: 0,
+      //       name: this.$i18n.t("navigation.default_group_two"),
+      //       type: "group",
+      //       children: list,
+      //     };
+      //     console.error(defaultDir);
+      //     data.children.push(defaultDir);
+      //   }
+      //   data.children.push(group);
+
+      //   this.$refs.sceneTree.store.nodesMap[data.id].expanded = true;
+      // } else {
+      //   this.info.navigationTrees.push(group);
+      // }
+    },
     onRequestForAddLevel1Group() {
       const newGroupLevel1Id = "r_" + this.$randomWord(true, 8, 8);
       const newGroupLevel2Id = "c_" + this.$randomWord(true, 8, 8);
@@ -100,11 +146,9 @@ export default {
         });
         parentGroupComp.isExpanded = true;
         this.$nextTick(() => {
-          const newGroupComp = parentGroupComp.$refs["scene-group"].find(
-            (item) => {
-              return item.groupNode.id === id;
-            }
-          );
+          const newGroupComp = parentGroupComp.$refs["scene-group"].find((item) => {
+            return item.groupNode.id === id;
+          });
           newGroupComp.onClickForRename();
         });
       });
@@ -149,10 +193,7 @@ export default {
     },
     onDeleteGroup(groupId, groupLevel) {
       try {
-        if (
-          (groupLevel === 1 && this.info.catalogRoot.length === 1) ||
-          (groupLevel === 2 && this.info.catalogs.length === 1)
-        ) {
+        if ((groupLevel === 1 && this.info.catalogRoot.length === 1) || (groupLevel === 2 && this.info.catalogs.length === 1)) {
           this.$alert({
             content: this.$i18n.t("navigation.keep_one_group"),
           });
@@ -160,16 +201,13 @@ export default {
         }
 
         if (groupLevel === 1 && this.info.catalogRoot.length !== 1) {
-          const targetGroupIdx = this.info.catalogRoot.findIndex(
-            (groupLevel1) => {
-              return groupLevel1.id === groupId;
-            }
-          );
+          const targetGroupIdx = this.info.catalogRoot.findIndex((groupLevel1) => {
+            return groupLevel1.id === groupId;
+          });
           if (targetGroupIdx < 0) {
             throw "没有找到要删除的一级分组!";
           }
-          const groupLevel2List =
-            this.info.catalogRoot[targetGroupIdx].children;
+          const groupLevel2List = this.info.catalogRoot[targetGroupIdx].children;
           // 检查是否所有场景都(间接)属于该一级分组
           if (
             this.info.scenes.every((scene) => {
@@ -199,7 +237,6 @@ export default {
           }
         }
       } catch (e) {
-        console.error(e);
         this.$msg.error(this.$i18n.t("gather.delete_fail"));
         return;
       }
@@ -210,14 +247,8 @@ export default {
         // 要删除的二级分组所属的一级分组在catalogRoot中的索引
         let belongGroupIdxLevel1 = null;
         // 确定上边两个变量的取值
-        for (const [
-          groupIdxLevel1,
-          groupLevel1,
-        ] of this.info.catalogRoot.entries()) {
-          for (const [
-            groupIdxLevel2,
-            childId,
-          ] of groupLevel1.children.entries()) {
+        for (const [groupIdxLevel1, groupLevel1] of this.info.catalogRoot.entries()) {
+          for (const [groupIdxLevel2, childId] of groupLevel1.children.entries()) {
             if (childId === groupId) {
               targetGroupIdxLevel2 = groupIdxLevel2;
               break;
@@ -233,10 +264,7 @@ export default {
         }
 
         // 删除catalogRoot[x].children中那个二级分组条目
-        this.info.catalogRoot[belongGroupIdxLevel1].children.splice(
-          targetGroupIdxLevel2,
-          1
-        );
+        this.info.catalogRoot[belongGroupIdxLevel1].children.splice(targetGroupIdxLevel2, 1);
 
         // 删除catalogs中那个二级分组条目
         const targetIdx = this.info.catalogs.findIndex((item) => {
@@ -255,9 +283,7 @@ export default {
         // 如果所属一级分组中没有任何二级分组了,则新增一个默认二级分组
         if (this.info.catalogRoot[belongGroupIdxLevel1].children.length === 0) {
           let newGroupLevel2Id = "c_" + this.$randomWord(true, 8, 8);
-          this.info.catalogRoot[belongGroupIdxLevel1].children.push(
-            newGroupLevel2Id
-          );
+          this.info.catalogRoot[belongGroupIdxLevel1].children.push(newGroupLevel2Id);
           this.info.catalogs.push({
             id: newGroupLevel2Id,
             name: this.$i18n.t("navigation.default_group_two"),
@@ -270,17 +296,14 @@ export default {
       const backup = deepClone(this.info);
       try {
         if (groupLevel === 1) {
-          const targetGroupIdx = this.info.catalogRoot.findIndex(
-            (groupLevel1) => {
-              return groupLevel1.id === groupId;
-            }
-          );
+          const targetGroupIdx = this.info.catalogRoot.findIndex((groupLevel1) => {
+            return groupLevel1.id === groupId;
+          });
           if (targetGroupIdx < 0) {
             throw "没有找到要删除的一级分组!";
           }
           // 删除该一级分组下的所有二级分组及属于这些二级分组的场景
-          for (const childId of this.info.catalogRoot[targetGroupIdx]
-            .children) {
+          for (const childId of this.info.catalogRoot[targetGroupIdx].children) {
             deleteGroupLevel2(childId);
           }
           // 删除该一级分组
@@ -299,8 +322,6 @@ export default {
           this.$store.commit("scene/setCurrentCatalogRoot", firstTab);
         }, 100);
       } catch (e) {
-        console.error(e);
-        this.$msg.error("删除失败");
         this.$msg.error(this.$i18n.t("gather.delete_fail"));
         this.$store.commit("SetInfo", backup);
       }
@@ -360,8 +381,10 @@ export default {
     height: 1px;
     overflow: auto;
     margin-top: 24px;
-    padding-left: 20px;
-    padding-right: 20px;
+    // padding-left: 20px;
+    // padding-right: 20px;
+    padding-left: 10px;
+    padding-right: 10px;
   }
 
   .ui-message {

+ 28 - 22
packages/qjkankan-editor/src/views/navigation/initialSceneSettings.vue

@@ -2,26 +2,12 @@
   <div class="initial-scene-settings" app-border dir-left>
     <div class="initial-scene-settings__title">
       {{ $i18n.t("navigation.init_scene") }}
-      <i
-        class="iconfont icon-help_i tool-tip-for-editor"
-        v-tooltip="$i18n.t('navigation.init_scene_tips')"
-      />
+      <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('navigation.init_scene_tips')" />
     </div>
+    <img class="preview" v-if="info?.firstScene" :src="info?.firstScene.icon" alt="" />
+    <img class="placeholder" v-else src="@/assets/images/pano-image-placeholder.png" alt="" />
 
-    <img
-      class="preview"
-      v-if="info.firstScene"
-      :src="info.firstScene.icon"
-      alt=""
-    />
-    <img
-      class="placeholder"
-      v-else
-      src="@/assets/images/pano-image-placeholder.png"
-      alt=""
-    />
-
-    <div class="change-init" v-if="info.firstScene">
+    <div class="change-init" v-if="info?.firstScene">
       <button class="ui-button deepcancel" @click="deleteIndexInfo">
         {{ $i18n.t("navigation.delete_init_scene") }}
       </button>
@@ -59,30 +45,50 @@ export default {
   components: {
     Selector,
   },
+  watch: {
+    "info.firstScene.id": {
+      // immediate: true,
+      handler: function (newVal, oldVal) {
+        if (JSON.stringify(newVal) != JSON.stringify(oldVal) && JSON.stringify(oldVal) != "undefined") {
+          this.$store.commit("navigation/setData", { saveFristScene: true });
+        }
+      },
+      deep: true,
+    },
+  },
   data() {
     return {
       showInitScene: false,
     };
   },
+
   methods: {
     deleteIndexInfo() {
       this.$confirm({
         content: this.$i18n.t("tips.delete"),
         ok: () => {
-          this.info.firstScene = "";
-          this.$store.commit("SetInfo", this.info);
+          // this.info.firstScene = "";
+          // this.$store.commit("SetInfo", this.info);
+
+          this.$store.commit("base/setData", { firstScene: null });
+
+          // this.$store.commit("base/setData", this.info);
           this.$msg.success(this.$i18n.t("tips.delete_done"));
+          // this.$forceUpdate();
         },
       });
     },
     handleSelect(data) {
-      this.info.firstScene = data[0]; // 注意此处是浅拷贝
+      // this.info.firstScene = data[0]; // 注意此处是浅拷贝
+      this.$store.commit("base/setData", { firstScene: data[0] });
+
       this.showInitScene = false;
+      // this.$forceUpdate();
     },
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      info: "base/baseInfo",
     }),
   },
   mounted() {},

+ 69 - 38
packages/qjkankan-editor/src/views/screen/Setting.vue

@@ -1,25 +1,12 @@
 <template>
-  <div class="view-setting" app-border dir-left>
+  <div v-if="currentScene" class="view-setting" app-border dir-left>
     <div class="title">
       {{ $i18n.t("screen.init_screen") }}
-      <i
-        class="iconfont icon-help_i tool-tip-for-editor"
-        v-tooltip="$i18n.t('screen.screen_tips')"
-      />
+      <i class="iconfont icon-help_i tool-tip-for-editor" v-tooltip="$i18n.t('screen.screen_tips')" />
     </div>
     <template v-if="currentScene.type !== '4dkk'">
-      <img
-        class="preview"
-        v-if="initImg"
-        :src="`${initImg}?${Math.random()}`"
-        alt=""
-      />
-      <img
-        class="placeholder"
-        v-else
-        src="@/assets/images/pano-image-placeholder.png"
-        alt=""
-      />
+      <img class="preview" v-if="initImg" :src="`${initImg}?${Math.random()}`" alt="" />
+      <img class="placeholder" v-else src="@/assets/images/pano-image-placeholder.png" alt="" />
       <div class="item">
         <div class="">
           {{ $i18n.t("modules.screen.v_angle_re") }}
@@ -34,6 +21,7 @@
           :marks="marks"
           direction="rtl"
           tooltip="active"
+          @change="handleChange"
           :tooltip-formatter="formatterMarks"
           :processStyle="{
             backgroundColor: '#409eff',
@@ -61,12 +49,7 @@
 
     <div class="goto-4dkk-tip" v-if="currentScene.type === '4dkk'">
       <div class="img-wrap">
-        <img
-          class=""
-          src="@/assets/images/default/goto-4dage.png"
-          alt=""
-          draggable="false"
-        />
+        <img class="" src="@/assets/images/default/goto-4dage.png" alt="" draggable="false" />
         <div class="tip-text">
           {{ $i18n.t("screen.goto_4dkk_edit_tips") }}
         </div>
@@ -93,6 +76,9 @@ export default {
   computed: {
     ...mapGetters({
       currentScene: "scene/currentScene",
+      workVisualAngleList: "screen/workVisualAngleList",
+      baseInfo: "base/baseInfo",
+      sceneList: "base/sceneList",
     }),
   },
   data() {
@@ -105,6 +91,7 @@ export default {
       initImg: "",
       vlookat: [-90, 90],
       applyToAll: false,
+      changeScene: false,
     };
   },
   watch: {
@@ -124,13 +111,42 @@ export default {
       immediate: true,
     },
 
-    "currentScene.initVisual": {
+    // "currentScene.initVisual": {
+    //   handler(val) {
+    //     console.error(val)
+    //     if (val) {
+    //       const { vlookatmin, vlookatmax } = val;
+    //       if (vlookatmin && vlookatmax) {
+    //         this.vlookat = [vlookatmin, vlookatmax];
+    //       }
+    //     }
+    //   },
+    //   deep: true,
+    //   immediate: true,
+    // },
+    currentScene: {
       handler(val) {
         if (val) {
-          const { vlookatmin, vlookatmax } = val;
-          if (vlookatmin && vlookatmax) {
-            this.vlookat = [vlookatmin, vlookatmax];
+          // console.error("currentScene change", val);
+          // let vlookatmin, vlookatmax;
+          let item = this.workVisualAngleList.find((item) => item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid);
+
+          if (item) {
+            this.changeScene = true;
+            const { vlookatmin, vlookatmax } = item;
+            if (vlookatmin && vlookatmax) {
+              this.vlookat = [vlookatmin, vlookatmax];
+            }
+          }
+
+          let scene = this.sceneList.find((scene) => scene.id == val.id);
+          if (scene) {
+            scene.icon = val.icon;
           }
+          // const { vlookatmin, vlookatmax } = this.baseInfo.workVisualAngleList;
+          // if (vlookatmin && vlookatmax) {
+          //   this.vlookat = [vlookatmin, vlookatmax];
+          // }
         }
       },
       deep: true,
@@ -139,6 +155,10 @@ export default {
     vlookat: {
       handler: function (val) {
         this.handleVlootAt(val);
+        if (val && this.$route.name == "screen" && this.changeScene == false) {
+          this.$store.commit("scene/setSaveApiList", "screen");
+        }
+        this.changeScene = false;
       },
     },
     applyToAll: {
@@ -151,13 +171,15 @@ export default {
           this.$confirm({
             content: this.$i18n.t("mask.is_apply_to_all_pano"),
             ok: () => {
-              this.$store.dispatch("scene/applyInitVisualToAll", {
-                vlookat,
-                hlookat,
-              });
-              this.updateCurrentScene();
+              this.$store.dispatch("screen/applyInitVisualToAll", this.vlookat);
+              // this.$store.dispatch("scene/applyInitVisualToAll", {
+              //   vlookat,
+              //   hlookat,
+              // });
+              // this.updateCurrentScene();
               this.$msg.success(this.$i18n.t("gather.edit_success"));
               setTimeout(() => (this.applyToAll = false), 1000);
+              this.$store.commit("scene/setSaveApiList", "screen");
             },
             no: () => {
               this.applyToAll = false;
@@ -169,6 +191,15 @@ export default {
     },
   },
   methods: {
+    handleChange(value, index) {
+      this.workVisualAngleList.forEach((item, index) => {
+        if (item.navigationId == this.currentScene.id || item.navigationId == this.currentScene.sid) {
+          item.vlookatmin = this.vlookat[0];
+          item.vlookatmax = this.vlookat[1];
+        }
+      });
+      // 拖动结束时的处理逻辑
+    },
     onClickGo4dkk() {
       window.open("/#/scene");
     },
@@ -179,11 +210,11 @@ export default {
       if (val) {
         const min = Math.min(...val);
         const max = Math.max(...val);
-        if (this.currentScene.initVisual) {
-          this.currentScene.initVisual.vlookatmin = min;
-          this.currentScene.initVisual.vlookatmax = max;
-          this.handleKrAction(min, max);
-        }
+        // if (this.currentScene.initVisual) {
+        // this.currentScene.initVisual.vlookatmin = min;
+        // this.currentScene.initVisual.vlookatmax = max;
+        this.handleKrAction(min, max);
+        // }
       }
     },
     handleKrAction(min, max) {
@@ -218,7 +249,7 @@ export default {
     });
 
     if (!this.initImg) {
-      this.initImg = this.currentScene.icon;
+      this.initImg = this.currentScene?.icon || "";
     }
   },
 };

+ 4 - 0
packages/qjkankan-editor/vue.config.js

@@ -36,6 +36,10 @@ module.exports = {
         target: proxy_url,
         changeOrigin: true,
       },
+      "/work": {
+        target: proxy_url,
+        changeOrigin: true,
+      },
       "/ucenter": {
         target: process.env.VUE_APP_PROXY_URL_ROOT,
         changeOrigin: true,

File diff suppressed because it is too large
+ 7 - 0
packages/qjkankan-view/public/showviewer/lib/krpano/js/krpano.js


File diff suppressed because it is too large
+ 5 - 0
packages/qjkankan-view/public/showviewer/lib/krpano/plugins/webvr1.21.js


+ 2 - 1
packages/qjkankan-view/public/showviewer/lib/krpano/tooltip.xml

@@ -197,6 +197,7 @@
             set_label_pos(get(tooltipname),get(pos));
          ); -->
           if(hoverstatus == 0,
+          trace('测试:',get(hoverstatus));
             delayedcall(0.5,
             txtadd(tooltipname, 'tooltip_', get(name)); 
             set_label_pos(get(tooltipname),get(pos));
@@ -207,7 +208,7 @@
          );
           <!-- 2 hover  -->
         if(hoverstatus == 2,
-            <!-- trace('测试'); -->
+            trace('测试');
            set(layer[get(tooltipname)].visible,false);
             delayedcall(0.5,
              txtadd(tooltipname, 'tooltip_', get(name)); 

+ 13 - 24
packages/qjkankan-view/src/apis/index.js

@@ -24,12 +24,7 @@ const URL_FILL = config.urlFill;
  * @param {*} no
  */
 export function getPanoInfo(ok, no) {
-  return http.get(
-    `${ossUrl}/720yun_fd_manage/${number()}/someData.json?_=${Math.random()}`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`${ossUrl}/720yun_fd_manage/${number()}/someData.json?_=${Math.random()}`, {}, ok, no);
 }
 
 /**
@@ -39,12 +34,7 @@ export function getPanoInfo(ok, no) {
  * @param {*} no
  */
 export function getFdkkInfo(data, ok, no) {
-  return http.get(
-    `${fdkkURL}api/scene/getInfo?num=${data.num}&_=${Math.random()}`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`${fdkkURL}api/scene/getInfo?num=${data.num}&_=${Math.random()}`, {}, ok, no);
 }
 
 /**
@@ -54,12 +44,7 @@ export function getFdkkInfo(data, ok, no) {
  * @param {*} no
  */
 export function checkWork(ok, no) {
-  return http.get(
-    `${URL_FILL}/web/common/checkWork/${number()}?visit=1111&val=0`,
-    {},
-    ok,
-    no
-  );
+  return http.get(`${URL_FILL}/web/common/checkWork/${number()}?visit=1111&val=0`, {}, ok, no);
 }
 
 /**
@@ -69,10 +54,14 @@ export function checkWork(ok, no) {
  * @param {*} no
  */
 export function exchangeId(data, ok, no) {
-  return http.post(
-    `${URL_FILL}/web/common/getIdInfo`,
-    data,
-    ok,
-    no
-  );
+  return http.post(`${URL_FILL}/web/common/getIdInfo`, data, ok, no);
+}
+/**
+ * 检查作品是否可用
+ * @param {*} data
+ * @param {*} ok
+ * @param {*} no
+ */
+export function getWorkInfo(ok, no) {
+  return http.get(`${URL_FILL}/work/view?workId=${number()}`, {}, ok, no);
 }

+ 88 - 56
packages/qjkankan-view/src/components/Pano/index.vue

@@ -4,15 +4,7 @@
 </template>
 
 <script setup>
-import {
-  ref,
-  onMounted,
-  computed,
-  watch,
-  nextTick,
-  unref,
-  watchEffect,
-} from "vue";
+import { ref, onMounted, computed, watch, nextTick, unref, watchEffect } from "vue";
 import { useStore } from "vuex";
 import { useApp, getApp } from "@/app";
 import Fdkk from "../Fdkk";
@@ -26,13 +18,13 @@ const store = useStore();
 const isMobile = computed(() => browser.isMobile());
 const lang = computed(() => config.lang);
 
+const hotspots = computed(() => store.getters["tags/hotspots"]);
 const currentScene = computed(() => store.getters["scene/currentScene"]);
+const vrStatus = computed(() => store.getters["functions/vrStatus"]);
 const isHasNormalBGM = computed(() => store.getters["audio/isHasNormalBGM"]);
 const scenesList = computed(() => store.getters["scene/list"]);
 const metadata = computed(() => store.getters["scene/metadata"]);
-const currentPlaying = computed(
-  () => store.getters["functions/currentPlaying"]
-);
+const currentPlaying = computed(() => store.getters["functions/currentPlaying"]);
 
 const hadGetInfo = ref(false);
 
@@ -40,28 +32,85 @@ const loadScene = async (currentScene) => {
   let app = await getApp();
   // await new Promise((r) => setTimeout(r, 10000));
   console.error("loadScene", unref(currentScene).id);
+
   if (app.krpanoDom) {
     let { sceneCode, initVisual, someData } = currentScene;
-    app.krpanoDom.call(
-      `skin_loadscene('scene_${sceneCode}',${
-        initVisual ? initVisual.vlookat : "0"
-      },${initVisual ? initVisual.hlookat : "0"})`
-    );
 
-    if (someData && someData.hotspots && someData.hotspots.length > 0) {
-      app.Tags.initHotspot(someData.hotspots, false);
+    let currnetVisual = metadata.value.workVisualAngleList.forEach((item) => {
+      item.navigationId == currentScene.id;
+    });
+    app.krpanoDom.call(`skin_loadscene('scene_${sceneCode}',${currnetVisual ? currnetVisual.vlookat : "0"},${currnetVisual ? currnetVisual.hlookat : "0"})`);
+
+    if (hotspots.value.length) {
+      let currentHotspots = hotspots.value.filter((item) => item.navigationId == currentScene.id);
+
+      if (currentHotspots.length) {
+        // hotspots.forEach((item, index) => {
+        //   if (item.content) {
+        //     let hotSpotType = item.hotspotType;
+        //     for (let key in hotspotTypeList.value) {
+        //       let data = hotspotTypeList.value[key];
+        //       if (key == hotSpotType && item.content[data]) {
+        //         item[data] = item.content[data];
+        //       } else if (data) {
+        //         item[data] = dataType.value[data];
+        //       }
+        //     }
+
+        //     let hotSpotIconType = item.hotspotIconType;
+        //     for (let key in hotspotIconTypeList.value) {
+        //       let data = hotspotIconTypeList.value[key];
+        //       if (key == hotSpotIconType && item.content[data]) {
+        //         item[data] = item.content[data];
+        //       } else if (data) {
+        //         item[data] = dataType.value[data];
+        //       }
+        //     }
+        //   }
+        // });
+        app.Tags.initHotspot(currentHotspots, false);
+      }
     }
 
-    const { vlookat, hlookat, vlookatmax, vlookatmin } =
-      currentScene.initVisual;
+    // const { vlookat, hlookat, vlookatmax, vlookatmin } =
+    //   currentScene.initVisual;
     app.krpanoDom.set(`view.limitview`, "lookat");
-    app.krpanoDom.set(`view.vlookat`, vlookat || 0);
-    app.krpanoDom.set(`view.hlookat`, hlookat || 0);
-    app.krpanoDom.set(`view.vlookatmin`, vlookatmin);
-    app.krpanoDom.set(`view.vlookatmax`, vlookatmax);
+    app.krpanoDom.set(`view.vlookat`, currnetVisual ? currnetVisual.vlookat : 0);
+    app.krpanoDom.set(`view.hlookat`, currnetVisual ? currnetVisual.hlookat : 0);
+    app.krpanoDom.set(`view.vlookatmin`, currnetVisual ? currnetVisual.vlookatmin : -90);
+    app.krpanoDom.set(`view.vlookatmax`, currnetVisual ? currnetVisual.vlookatmax : 90);
+
+    // console.log("customMasks", currentScene.customMask);
+    // const { earth, sky } = currentScene.customMask;
+    let customMask = metadata.value.workCustomMaskList.find((item) => item.navigationId == currentScene.id);
 
-    console.log("customMasks", currentScene.customMask);
-    const { earth, sky } = currentScene.customMask;
+    if (!customMask) {
+      customMask = {
+        data: {
+          earth: {
+            antidistorted: true,
+            fodderId: null,
+            icon: "",
+            isShow: false,
+            navigationId: currentScene.id,
+            scale: 1,
+            type: "earth",
+          },
+          sky: {
+            antidistorted: true,
+            fodderId: null,
+            icon: "",
+            isShow: true,
+            navigationId: currentScene.id,
+            scale: 1,
+            type: "sky",
+          },
+        },
+        navigationId: currentScene.id,
+      };
+    }
+    // const { sky, earth } = currentScene.value.customMask;
+    const { sky, earth } = customMask.data;
     handleMasksUpdate(sky, earth, app);
   }
 };
@@ -76,9 +125,7 @@ watch(
       null,
       ""
         .concat(window.location.pathname, "?")
-        .concat(
-          `id=${metadata.value.id}&vr=${newVal.sceneCode}&lang=${lang.value}`
-        )
+        .concat(`id=${metadata.value.workId}&vr=${newVal.sceneCode}&lang=${lang.value}`)
         .concat(`${vlog ? "&vlog=" + vlog : ""} `)
     );
 
@@ -109,11 +156,7 @@ watch(
         store.commit("scene/setFdkkCurrentVersion", isVersion);
         // v3
         if (isVersion === "V3") {
-          let flag =
-            data.data.bgMusic &&
-            data.data.bgMusic != "0" &&
-            data.data.bgMusic != "Cheerful" &&
-            data.data.bgMusic != "noMusic";
+          let flag = data.data.bgMusic && data.data.bgMusic != "0" && data.data.bgMusic != "Cheerful" && data.data.bgMusic != "noMusic";
 
           console.error("是否有V3--BGM", flag);
           store.commit("fdkk/setV3FdkkBGM", flag);
@@ -137,9 +180,7 @@ watch(
 );
 
 const updateListPosi = () => {
-  let catalog = metadata.value.catalogs.find(
-    (item) => item.id == currentScene.value.category
-  );
+  let catalog = metadata.value.catalogs.find((item) => item.id == currentScene.value.category);
 
   // 查询初始场景的所在1级分组
   metadata.value.catalogRoot.forEach((item) => {
@@ -157,25 +198,26 @@ const updateListPosi = () => {
 useApp().then((app) => {
   app.Tags.on("clickHotspot", (data) => {
     let { id } = data;
-    let hotspot = unref(currentScene).someData.hotspots.find(
-      (item) => item.name.toLowerCase() === id.toLowerCase()
-    );
-
+    // let hotspot = unref(currentScene).someData.hotspots.find((item) => item.name.toLowerCase() === id.toLowerCase());
+    let hotspot = hotspots.value.find((item) => item.name.toLowerCase() === id.toLowerCase());
     if (hotspot) {
       const isNotclickType = ["tag"];
       if (!isNotclickType.includes(hotspot.hotspotType)) {
         console.log("click", hotspot);
         // store.commit("functions/setPauseFrom", "");
         // store.dispatch("audio/pauseBGM");
+
         if (hotspot.hotspotType == "phone" && unref(isMobile)) {
           window.open(`tel:${hotspot.phoneInfo.phone}`, "_self");
           return;
         }
         if (hotspot.hotspotType == "scene") {
+          console.error(scenesList.value);
           store.commit(
             "scene/setCurrentScene",
-            scenesList.value.find((item) => item.id == hotspot.secne.id)
+            scenesList.value.find((item) => item.id == hotspot.scene.id)
           );
+          // store.commit("scene/setCurrentScenesList", sceneList);
           updateListPosi();
         } else if (hotspot.hotspotType == "link") {
           if (hotspot.linkOpenType == "newTab") {
@@ -185,11 +227,7 @@ useApp().then((app) => {
           }
         } else {
           store.commit("tags/setCurrentTag", hotspot);
-          if (
-            hotspot.hotspotType == "audio" ||
-            hotspot.hotspotType == "imageText" ||
-            hotspot.hotspotType == "video"
-          ) {
+          if (hotspot.hotspotType == "audio" || hotspot.hotspotType == "imageText" || hotspot.hotspotType == "video") {
             store.dispatch("audio/pauseBGM");
             // store.commit("functions/setPauseFrom", currentPlaying.value);
           }
@@ -232,10 +270,7 @@ const handleMasksUpdate = (skyMask, earthMask, app) => {
   }
   if (earthMask) {
     if ("isShow" in earthMask) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].visible`,
-        Boolean(earthMask.isShow)
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].visible`, Boolean(earthMask.isShow));
     }
     if (earthMask.icon) {
       app.krpanoDom.set(`hotspot[nadirlogo].url`, earthMask.icon);
@@ -244,10 +279,7 @@ const handleMasksUpdate = (skyMask, earthMask, app) => {
       app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale);
     }
     if ("antidistorted" in earthMask) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].distorted`,
-        earthMask.antidistorted
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].distorted`, earthMask.antidistorted);
       if (!earthMask.antidistorted) {
         app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale * 0.9);
       }

+ 49 - 51
packages/qjkankan-view/src/components/UIGather/index.vue

@@ -11,11 +11,11 @@
                         alt="" />
             </div>
 
-            <sceneList />
-            <div class="btnmask"></div>
-      </div>
+    <sceneList />
+    <div class="btnmask"></div>
+  </div>
 </template>
-  
+
 <script setup>
 import Logo from "./logo";
 import Tips from "./tips";
@@ -25,6 +25,7 @@ import Menu from "./menu";
 import { ref, onMounted, computed, watch, nextTick } from "vue";
 import { useStore } from "vuex";
 import { useApp, getApp } from "@/app";
+import browser from "@/utils/browser";
 
 const store = useStore();
 
@@ -43,62 +44,59 @@ const showControls = computed(() => {
 
 
 const onIsShowList = (data) => {
-      store.commit("functions/setShowScenesList", !isShowScenesList.value);
+  store.commit("functions/setShowScenesList", !isShowScenesList.value);
 };
 
-
 useApp().then((app) => {
-      app.Scene.on("ready", () => {
-            if (currentScene && currentScene.value.type == '4dkk' && !showUI.value) {
-                  showUI.value = true
-            }
-      })
-      app.Scene.on("sceneReady", () => {
-            showUI.value = true
-      })
-})
-
-
+  app.Scene.on("ready", () => {
+    if (currentScene && currentScene.value.type == "4dkk" && !showUI.value) {
+      showUI.value = true;
+    }
+  });
+  app.Scene.on("sceneReady", () => {
+    showUI.value = true;
+  });
+});
 </script>
-  
+
 <style lang="scss" scoped>
 .v3daolan {
-      background: rgba(0, 0, 0, .3);
-      border: 1px solid hsla(0, 0%, 100%, .2);
-      position: fixed;
-      bottom: 20px;
-      left: 50%;
-      transform: translateX(-50%);
-      border-radius: 50%;
-      pointer-events: auto;
-      width: 36px;
-      height: 36px;
-      line-height: 36px;
-      color: #fff;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
+  background: rgba(0, 0, 0, 0.3);
+  border: 1px solid hsla(0, 0%, 100%, 0.2);
+  position: fixed;
+  bottom: 20px;
+  left: 50%;
+  transform: translateX(-50%);
+  border-radius: 50%;
+  pointer-events: auto;
+  width: 36px;
+  height: 36px;
+  line-height: 36px;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
 
-      &.active {
-            background: rgba(0, 0, 0, 0.5);
-      }
+  &.active {
+    background: rgba(0, 0, 0, 0.5);
+  }
 
-      >img {
-            width: 26px;
-            height: 26px;
-      }
+  > img {
+    width: 26px;
+    height: 26px;
+  }
 }
 
 .btnmask {
-      width: 100%;
-      position: fixed;
-      bottom: 0;
-      left: 0;
-      right: 0;
-      pointer-events: none;
-      opacity: 0.3;
-      height: 50px;
-      background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 52%, #000000 100%);
+  width: 100%;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  pointer-events: none;
+  opacity: 0.3;
+  height: 50px;
+  background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 52%, #000000 100%);
 }
-</style>
+</style>

+ 506 - 0
packages/qjkankan-view/src/components/UIGather/list copy 2.vue

@@ -0,0 +1,506 @@
+<template>
+  <div
+    class="bar-list"
+    v-if="show && !(metadata.navigationTrees && metadata.navigationTrees.length == 1 && scenes.length == 1 && metadata.navigationTrees[rootTabIndex].length == 1)"
+    :class="{ barshow: isShowScenesList }"
+  >
+    <div class="top-con">
+      <div class="swiper-container swiper1" :style="`width:calc(100% - 20px)`" id="swScenes" v-if="currentScenesList.length > 0">
+        <ul class="swiper-wrapper">
+          <li
+            @click="tabCurrentScene(item)"
+            class="swiper-slide"
+            :class="{
+              active: currentScene.sceneCode == item.sceneCode,
+              // loopspan:
+              //   item.name.length > spanlength &&
+              //   currentScene.id == item.id,
+            }"
+            :style="{ backgroundImage: `url(${item.icon})` }"
+            v-for="(item, i) in currentScenesList"
+            :key="i"
+          >
+            <i class="iconfont" :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
+            <div class="marquee">
+              <marquee-text :repeat="1" :duration="Math.ceil(item.name.length / 10) * 5" :key="item.id" v-if="item.name.length > spanlength && currentScene.id == item.id">
+                {{ item.name }}
+              </marquee-text>
+              <span v-else>
+                {{ item.name }}
+              </span>
+            </div>
+          </li>
+        </ul>
+      </div>
+      <div class="swiper-container swiper2" id="swSecondary" :style="`width:${clamp(secondaryW, 0, 1150)}px`" v-if="metadata.navigationTrees[rootTabIndex]?.children.length > 1">
+        <!-- {{ clamp(secondaryW, 0, 1150) }} -->
+        <ul class="swiper-wrapper">
+          <li
+            class="swiper-slide"
+            @click="tabSecondary(item, i)"
+            :class="{
+              active: currentSecondary.id == item.id,
+              // loopspan:
+              //   fixTitle(item.name).length > spanlength &&
+              //   currentSecondary.id == item.id,
+            }"
+            v-for="(item, i) in metadata.navigationTrees[rootTabIndex]?.children"
+            :key="i"
+          >
+            <!-- {{ Math.ceil(fixTitle(item.name).length / 10) }} -->
+
+            <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentSecondary.id == item.id">
+              {{ fixTitle(item.name) }}
+            </marquee-text>
+            <span v-else>
+              {{ fixTitle(item.name) }}
+            </span>
+            <!-- <span v-if="currentSecondary.id == item.id">{{
+              fixTitle(item.name)
+            }}</span>
+            <span v-else>{{
+              fixTitle(item.name).length > spanlength
+                ? fixTitle(item.name).slice(0, spanlength)
+                : fixTitle(item.name)
+            }}</span> -->
+          </li>
+        </ul>
+      </div>
+    </div>
+
+    <div class="swiper-container" id="swcatalogRoot" :style="`width:${catalogRootW > innerW ? '100%' : catalogRootW + 'px'}`" v-if="metadata.navigationTrees.length">
+      <ul class="swiper-wrapper" v-if="metadata.navigationTrees.length > 1">
+        <li
+          class="swiper-slide"
+          :class="{
+            active: currentCatalogRoot.id == item.id,
+            // loopspan:
+            //   fixTitle(item.name).length > spanlength &&
+            //   currentCatalogRoot.id == item.id,
+          }"
+          @click="tabRoot(item)"
+          v-for="(item, i) in metadata.navigationTrees"
+          :key="i"
+        >
+          <!-- <span v-if="currentCatalogRoot.id == item.id">{{
+            fixTitle(item.name)
+          }}</span>
+          <span v-else>{{
+            fixTitle(item.name).length > spanlength
+              ? fixTitle(item.name).slice(0, spanlength)
+              : fixTitle(item.name)
+          }}</span> -->
+
+          <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentCatalogRoot.id == item.id">
+            {{ fixTitle(item.name) }}
+          </marquee-text>
+          <span v-else>
+            {{ fixTitle(item.name) }}
+          </span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch, computed, onMounted, nextTick, unref, watchEffect } from "vue";
+import { useStore } from "vuex";
+import { useApp } from "@/app";
+import MarqueeText from "vue-marquee-text-component";
+import { useI18n, getLocale } from "@/i18n";
+const { t } = useI18n({ useScope: "global" });
+
+const store = useStore();
+
+const spanlength = ref(5);
+
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+const currentSecondId = computed(() => store.getters["scene/currentSecondId"]);
+const currentRootId = computed(() => store.getters["scene/currentRootId"]);
+
+const clamp = computed(() => (num, min, max) => Math.min(Math.max(num, min), max));
+
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
+const currentSecondary = computed(() => store.getters["scene/currentSecondary"]);
+
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+
+const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
+
+const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
+
+const show = ref(false);
+
+const swidth = ref({
+  swcatalogRoot: 104,
+  swSecondary: 84,
+  swScenes: 72,
+});
+const rootTabIndex = computed(() => {
+  console.error(currentRootId.value);
+  return metadata.value && metadata.value?.navigationTrees ? metadata.value?.navigationTrees.findIndex((item) => item.id == currentRootId.value) : 0;
+});
+
+const secondTabIndex = computed(() => {
+  return metadata.value && metadata.value?.navigationTrees ? metadata.value.navigationTrees[rootTabIndex.value]?.children.findIndex((item) => item.id == currentSecondId.value) : 0;
+});
+const scenesListW = computed(() => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10);
+const secondaryW = computed(() => metadata.value.navigationTrees[secondTabIndex.value].length * (swidth.value["swSecondary"] + 10) - 10);
+const catalogRootW = computed(() => metadata.value.navigationTrees.length * (swidth.value["swcatalogRoot"] + 10) - 10);
+
+const innerW = computed(() => 1150);
+
+const tabCurrentScene = (data) => {
+  console.log("tabCurrentScene", data.id, currentScene.value.id);
+  if (data.id !== currentScene.value.id) {
+    store.commit("scene/setCurrentScene", data);
+    setTimeout(() => {
+      scenesSwiperFocus();
+    }, 300);
+  } else {
+    console.log("重复点击当前导航");
+    // window.alert("alert-test-->重复点击当前导航");
+  }
+};
+
+const tabSecondary = (data, index) => {
+  store.commit("scene/setCurrentSecondary", data);
+};
+
+const tabRoot = (data) => {
+  store.commit("scene/setCurrentCatalogRoot", data);
+};
+
+const fixTitle = (name) => {
+  if (name == "默认二级分组") {
+    name = t("navigation.default_group_two");
+  } else if (name == "一级分组") {
+    name = t("navigation.group_one");
+  } else {
+    name = name;
+  }
+  return name;
+};
+const swiperOptions = {
+  slidesPerView: "auto",
+  centeredSlides: true,
+  spaceBetween: 10,
+  centerInsufficientSlides: true,
+  centeredSlidesBounds: true,
+  freeMode: {
+    enabled: true,
+    sticky: false,
+    momentumBounce: false,
+    // momentumVelocityRatio: 0.5,
+  },
+};
+
+// const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+
+const initMainSwiper = () => {
+  nextTick(() => {
+    if (window.mainNatSwiper) {
+      // window.mainNatSwiper = null;
+      window.mainNatSwiper.update();
+      window.sencordNatSwiper.slideReset();
+    }
+    window.mainNatSwiper = new Swiper("#swcatalogRoot", swiperOptions);
+  });
+};
+const initsencordNatSwiper = () => {
+  nextTick(() => {
+    console.warn("initsencordNatSwiper");
+    if (window.sencordNatSwiper) {
+      window.sencordNatSwiper.update();
+      window.sencordNatSwiper.slideReset();
+    }
+    window.sencordNatSwiper = new Swiper("#swSecondary", swiperOptions);
+  });
+};
+const initScenesSwiper = () => {
+  // console.warn("initScenesSwiper");
+  nextTick(() => {
+    if (window.scenesNatSwiper) {
+      try {
+        window.scenesNatSwiper.slides.length > 0 && window.scenesNatSwiper.update();
+        window.scenesNatSwiper.slideReset();
+      } catch (error) {}
+
+      // window.scenesNatSwiper = null;
+    } else {
+      window.scenesNatSwiper = new Swiper("#swScenes", {
+        ...swiperOptions,
+      });
+    }
+    scenesSwiperFocus();
+  });
+};
+
+const scenesSwiperFocus = () => {
+  const sceneIndex = Array.from(currentScenesList.value).findIndex((item) => item.id === currentScene.value.id);
+  if (window.scenesNatSwiper && window.scenesNatSwiper.slides.length > 0) {
+    const index = sceneIndex < 0 ? 0 : sceneIndex;
+    const fIndex = index < 5 ? 0 : index;
+    console.warn("scenesSwiperFocus", fIndex);
+    window.scenesNatSwiper.slideTo(fIndex);
+  }
+};
+
+const sencordNatSwiperFocus = () => {
+  nextTick(() => {
+    const current = Array.from(secondaryList.value).findIndex((item) => item.id === currentSecondary.value.id);
+    if (window.sencordNatSwiper) {
+      const index = current < 0 ? 0 : current;
+      console.error("sencordNatSwiperFocus", index);
+      // window.sencordNatSwiper.slideTo(current);
+      window.sencordNatSwiper.slideTo(current);
+    }
+  });
+};
+
+onMounted(() => {
+  useApp().then(async (app) => {
+    show.value = true;
+    console.error("app", show.value);
+  });
+  watchEffect(() => {
+    if (metadata.value.navigationTrees && unref(metadata.value.navigationTrees).length > 0) {
+      initMainSwiper();
+    }
+  });
+
+  watch(secondTabIndex.value, () => {
+    if (unref(metadata.value.navigationTrees[rootTabIndex.value].children).length > 1) {
+      initsencordNatSwiper();
+      sencordNatSwiperFocus();
+    } else {
+      if (window.sencordNatSwiper) {
+        console.warn("destroy-sencordNatSwiper");
+        window.sencordNatSwiper.update();
+        window.sencordNatSwiper.slideReset();
+      }
+    }
+  });
+
+  watch(currentScenesList, () => {
+    initScenesSwiper();
+  });
+  watch(currentCatalogRoot, (val) => {
+    if (Array.from(unref(val).children).includes(currentScene.value.category)) {
+      //当前场景在一类的children
+      const activeSecond = Array.from(unref(secondaryList)).find((i) => i.id === currentScene.value.category);
+      // console.log("activeSecond", activeSecond);
+      store.commit("scene/setCurrentSecondary", activeSecond);
+      if (window.sencordNatSwiper) {
+        window.sencordNatSwiper.update();
+      }
+    }
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+$width: 1150px;
+
+.bar-list {
+  position: absolute;
+  bottom: 68px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  max-width: $width;
+  overflow: hidden;
+  max-height: 0;
+  transition: 0.3s all ease;
+  z-index: 9;
+
+  .swiper-container {
+    width: 100%;
+    position: relative;
+    margin: 0 auto;
+
+    > ul {
+      > li {
+        white-space: nowrap;
+
+        > span,
+        > div > span {
+          cursor: pointer;
+          display: inline-block;
+          color: rgba(255, 255, 255, 0.6);
+        }
+
+        &.loopspan {
+          > span,
+          > div > span {
+            animation: 10s wordsLoop linear infinite normal;
+          }
+        }
+
+        &.active {
+          > span,
+          > div > span {
+            color: rgba(255, 255, 255, 1);
+          }
+        }
+      }
+    }
+  }
+
+  .top-con {
+    margin: 0 auto 10px;
+    padding: 10px 0;
+    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.4) 25%, rgba(0, 0, 0, 0.4) 75%, rgba(0, 0, 0, 0) 100%);
+  }
+
+  #swcatalogRoot {
+    > ul {
+      > li {
+        width: 104px;
+        background: rgba(0, 0, 0, 0.5);
+        border-radius: 4px;
+        padding: 4px 10px;
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        box-sizing: border-box;
+        overflow: hidden;
+
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          border: 1px solid rgba(255, 255, 255, 1);
+        }
+      }
+    }
+  }
+
+  #swSecondary {
+    margin: 20px auto 10px;
+
+    > ul {
+      > li {
+        width: 84px;
+        box-sizing: border-box;
+        overflow: hidden;
+        padding-bottom: 6px;
+
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          position: relative;
+
+          &::before {
+            content: "";
+            display: inline-block;
+            position: absolute;
+            bottom: 0;
+            width: 20px;
+            height: 2px;
+            z-index: 9999;
+            left: 50%;
+            transform: translateX(-50%);
+            background: var(--colors-primary-base);
+          }
+        }
+      }
+    }
+  }
+
+  #swScenes {
+    > ul {
+      > li {
+        cursor: pointer;
+        width: 72px;
+        height: 72px;
+        border-radius: 6px;
+        border: 1px solid #ffffff;
+        background-size: cover;
+        position: relative;
+        overflow: hidden;
+
+        .iconfont {
+          position: absolute;
+          left: 4px;
+          top: 4px;
+          z-index: 99;
+
+          &::after {
+            background: rgba(0, 0, 0, 0.3);
+            content: "";
+            width: 14px;
+            height: 14px;
+            display: inline-block;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            z-index: -1;
+            filter: blur(4px);
+          }
+        }
+
+        > div {
+          position: absolute;
+          bottom: 0;
+          left: 0;
+          height: 20px;
+          background: rgba(0, 0, 0, 0.5);
+          width: 100%;
+          overflow: hidden;
+
+          > span,
+          div {
+            // width: 100%;
+            line-height: 20px;
+            word-break: keep-all;
+            white-space: normal;
+          }
+        }
+
+        &.active {
+          border: 1px solid var(--colors-primary-base);
+
+          > div {
+            > span {
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.barshow {
+  max-height: 190px;
+}
+
+@keyframes wordsLoop {
+  0% {
+    transform: translateX(100%);
+    -webkit-transform: translateX(100%);
+  }
+  100% {
+    transform: translateX(-180%);
+    -webkit-transform: translateX(-180%);
+  }
+}
+.marquee {
+  .marquee-text-wrap {
+    height: 20px;
+    line-height: 20px;
+  }
+}
+</style>
+<style>
+.marquee-text-text {
+  padding: 0 5px;
+}
+</style>

+ 589 - 0
packages/qjkankan-view/src/components/UIGather/list copy.vue

@@ -0,0 +1,589 @@
+<template>
+  <div
+    class="bar-list"
+    v-if="
+      show &&
+      !(
+        metadata.catalogRoot &&
+        metadata.catalogRoot.length == 1 &&
+        scenes.length == 1 &&
+        secondaryList.length == 1
+      )
+    "
+    :class="{ barshow: isShowScenesList }"
+  >
+    <div class="top-con">
+      <div
+        class="swiper-container swiper1"
+        :style="`width:calc(100% - 20px)`"
+        id="swScenes"
+        v-if="currentScenesList.length > 0"
+      >
+        <ul class="swiper-wrapper">
+          <li
+            @click="tabCurrentScene(item)"
+            class="swiper-slide"
+            :class="{
+              active: currentScene.sceneCode == item.sceneCode,
+              // loopspan:
+              //   item.sceneTitle.length > spanlength &&
+              //   currentScene.id == item.id,
+            }"
+            :style="{ backgroundImage: `url(${item.icon})` }"
+            v-for="(item, i) in currentScenesList"
+            :key="i"
+          >
+            <i
+              class="iconfont"
+              :class="
+                item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'
+              "
+            ></i>
+            <div class="marquee">
+              <marquee-text
+                :repeat="1"
+                :duration="Math.ceil(item.sceneTitle.length / 10) * 5"
+                :key="item.id"
+                v-if="
+                  item.sceneTitle.length > spanlength &&
+                  currentScene.id == item.id
+                "
+              >
+                {{ item.sceneTitle }}
+              </marquee-text>
+              <span v-else>
+                {{ item.sceneTitle }}
+              </span>
+            </div>
+          </li>
+        </ul>
+      </div>
+
+      <div
+        class="swiper-container swiper2"
+        id="swSecondary"
+        :style="`width:${clamp(secondaryW, 0, 1150)}px`"
+        v-if="secondaryList.length > 1"
+      >
+        <!-- {{ clamp(secondaryW, 0, 1150) }} -->
+        <ul class="swiper-wrapper">
+          <li
+            class="swiper-slide"
+            @click="tabSecondary(item, i)"
+            :class="{
+              active: currentSecondary.id == item.id,
+              // loopspan:
+              //   fixTitle(item.name).length > spanlength &&
+              //   currentSecondary.id == item.id,
+            }"
+            v-for="(item, i) in secondaryList"
+            :key="i"
+          >
+            <!-- {{ Math.ceil(fixTitle(item.name).length / 10) }} -->
+
+            <marquee-text
+              :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
+              :key="item.id"
+              :repeat="1"
+              v-if="
+                fixTitle(item.name).length > spanlength &&
+                currentSecondary.id == item.id
+              "
+            >
+              {{ fixTitle(item.name) }}
+            </marquee-text>
+            <span v-else>
+              {{ fixTitle(item.name) }}
+            </span>
+            <!-- <span v-if="currentSecondary.id == item.id">{{
+              fixTitle(item.name)
+            }}</span>
+            <span v-else>{{
+              fixTitle(item.name).length > spanlength
+                ? fixTitle(item.name).slice(0, spanlength)
+                : fixTitle(item.name)
+            }}</span> -->
+          </li>
+        </ul>
+      </div>
+    </div>
+
+    <div
+      class="swiper-container"
+      id="swcatalogRoot"
+      :style="`width:${catalogRootW > innerW ? '100%' : catalogRootW + 'px'}`"
+      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1"
+    >
+      <ul class="swiper-wrapper" v-if="metadata.catalogRoot.length > 1">
+        <li
+          class="swiper-slide"
+          :class="{
+            active: currentCatalogRoot.id == item.id,
+            // loopspan:
+            //   fixTitle(item.name).length > spanlength &&
+            //   currentCatalogRoot.id == item.id,
+          }"
+          @click="tabRoot(item)"
+          v-for="(item, i) in metadata.catalogRoot"
+          :key="i"
+        >
+          <!-- <span v-if="currentCatalogRoot.id == item.id">{{
+            fixTitle(item.name)
+          }}</span>
+          <span v-else>{{
+            fixTitle(item.name).length > spanlength
+              ? fixTitle(item.name).slice(0, spanlength)
+              : fixTitle(item.name)
+          }}</span> -->
+
+          <marquee-text
+            :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
+            :key="item.id"
+            :repeat="1"
+            v-if="
+              fixTitle(item.name).length > spanlength &&
+              currentCatalogRoot.id == item.id
+            "
+          >
+            {{ fixTitle(item.name) }}
+          </marquee-text>
+          <span v-else>
+            {{ fixTitle(item.name) }}
+          </span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  computed,
+  onMounted,
+  nextTick,
+  unref,
+  watchEffect,
+} from "vue";
+import { useStore } from "vuex";
+import { useApp } from "@/app";
+import MarqueeText from "vue-marquee-text-component";
+import { useI18n, getLocale } from "@/i18n";
+const { t } = useI18n({ useScope: "global" });
+
+const store = useStore();
+
+const spanlength = ref(5);
+
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
+const clamp = computed(
+  () => (num, min, max) => Math.min(Math.max(num, min), max)
+);
+
+const currentCatalogRoot = computed(
+  () => store.getters["scene/currentCatalogRoot"]
+);
+const currentSecondary = computed(
+  () => store.getters["scene/currentSecondary"]
+);
+
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+
+const isShowScenesList = computed(
+  () => store.getters["functions/isShowScenesList"]
+);
+
+const currentScenesList = computed(
+  () => store.getters["scene/currentScenesList"]
+);
+
+const show = ref(false);
+
+const swidth = ref({
+  swcatalogRoot: 104,
+  swSecondary: 84,
+  swScenes: 72,
+});
+
+const scenesListW = computed(
+  () => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10
+);
+const secondaryW = computed(
+  () => secondaryList.value.length * (swidth.value["swSecondary"] + 10) - 10
+);
+const catalogRootW = computed(
+  () =>
+    metadata.value.catalogRoot.length * (swidth.value["swcatalogRoot"] + 10) -
+    10
+);
+const innerW = computed(() => 1150);
+
+const tabCurrentScene = (data) => {
+  console.log("tabCurrentScene", data.id, currentScene.value.id);
+  if (data.id !== currentScene.value.id) {
+    store.commit("scene/setCurrentScene", data);
+    setTimeout(() => {
+      scenesSwiperFocus();
+    }, 300);
+  } else {
+    console.log("重复点击当前导航");
+    // window.alert("alert-test-->重复点击当前导航");
+  }
+};
+
+const tabSecondary = (data, index) => {
+  store.commit("scene/setCurrentSecondary", data);
+};
+
+const tabRoot = (data) => {
+  store.commit("scene/setCurrentCatalogRoot", data);
+};
+
+const fixTitle = (name) => {
+  if (name == "默认二级分组") {
+    name = t("navigation.default_group_two");
+  } else if (name == "一级分组") {
+    name = t("navigation.group_one");
+  } else {
+    name = name;
+  }
+  return name;
+};
+const swiperOptions = {
+  slidesPerView: "auto",
+  centeredSlides: true,
+  spaceBetween: 10,
+  centerInsufficientSlides: true,
+  centeredSlidesBounds: true,
+  freeMode: {
+    enabled: true,
+    sticky: false,
+    momentumBounce: false,
+    // momentumVelocityRatio: 0.5,
+  },
+};
+
+// const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+
+const initMainSwiper = () => {
+  nextTick(() => {
+    if (window.mainNatSwiper) {
+      // window.mainNatSwiper = null;
+      window.mainNatSwiper.update();
+      window.sencordNatSwiper.slideReset();
+    }
+    window.mainNatSwiper = new Swiper("#swcatalogRoot", swiperOptions);
+  });
+};
+const initsencordNatSwiper = () => {
+  nextTick(() => {
+    console.warn("initsencordNatSwiper");
+    if (window.sencordNatSwiper) {
+      window.sencordNatSwiper.update();
+      window.sencordNatSwiper.slideReset();
+    }
+    window.sencordNatSwiper = new Swiper("#swSecondary", swiperOptions);
+  });
+};
+const initScenesSwiper = () => {
+  // console.warn("initScenesSwiper");
+  nextTick(() => {
+    if (window.scenesNatSwiper) {
+      try {
+        window.scenesNatSwiper.slides.length > 0 &&  window.scenesNatSwiper.update();
+        window.scenesNatSwiper.slideReset();
+      } catch (error) {}
+
+      // window.scenesNatSwiper = null;
+    } else {
+      window.scenesNatSwiper = new Swiper("#swScenes", {
+        ...swiperOptions,
+      });
+    }
+    scenesSwiperFocus();
+  });
+};
+
+const scenesSwiperFocus = () => {
+  const sceneIndex = Array.from(currentScenesList.value).findIndex(
+    (item) => item.id === currentScene.value.id
+  );
+  if (window.scenesNatSwiper && window.scenesNatSwiper.slides.length > 0) {
+    const index = sceneIndex < 0 ? 0 : sceneIndex;
+    const fIndex = index < 5 ? 0 : index;
+    console.warn("scenesSwiperFocus", fIndex);
+    window.scenesNatSwiper.slideTo(fIndex);
+  }
+};
+
+const sencordNatSwiperFocus = () => {
+  nextTick(() => {
+    const current = Array.from(secondaryList.value).findIndex(
+      (item) => item.id === currentSecondary.value.id
+    );
+    if (window.sencordNatSwiper) {
+      const index = current < 0 ? 0 : current;
+      console.warn("sencordNatSwiperFocus", index);
+      // window.sencordNatSwiper.slideTo(current);
+      window.sencordNatSwiper.slideTo(current);
+    }
+  });
+};
+
+onMounted(() => {
+  useApp().then(async (app) => {
+    show.value = true;
+  });
+  watchEffect(() => {
+    if (
+      metadata.value.catalogRoot &&
+      unref(metadata.value.catalogRoot).length > 0
+    ) {
+      initMainSwiper();
+    }
+  });
+
+  watch([currentSecondary, secondaryList], () => {
+    if (unref(secondaryList).length > 1) {
+      initsencordNatSwiper();
+      sencordNatSwiperFocus();
+    } else {
+      if (window.sencordNatSwiper) {
+        console.warn("destroy-sencordNatSwiper");
+        window.sencordNatSwiper.update();
+        window.sencordNatSwiper.slideReset();
+      }
+    }
+  });
+
+  watch(currentScenesList, () => {
+    initScenesSwiper();
+  });
+  watch(currentCatalogRoot, (val) => {
+    if (Array.from(unref(val).children).includes(currentScene.value.category)) {
+      //当前场景在一类的children
+      const activeSecond = Array.from(unref(secondaryList)).find(
+        (i) => i.id === currentScene.value.category
+      );
+      // console.log("activeSecond", activeSecond);
+      store.commit("scene/setCurrentSecondary", activeSecond);
+      if (window.sencordNatSwiper) {
+        window.sencordNatSwiper.update();
+      }
+    }
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+$width: 1150px;
+
+.bar-list {
+  position: absolute;
+  bottom: 68px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  max-width: $width;
+  overflow: hidden;
+  max-height: 0;
+  transition: 0.3s all ease;
+  z-index: 9;
+
+  .swiper-container {
+    width: 100%;
+    position: relative;
+    margin: 0 auto;
+
+    > ul {
+      > li {
+        white-space: nowrap;
+
+        > span,
+        > div > span {
+          cursor: pointer;
+          display: inline-block;
+          color: rgba(255, 255, 255, 0.6);
+        }
+
+        &.loopspan {
+          > span,
+          > div > span {
+            animation: 10s wordsLoop linear infinite normal;
+          }
+        }
+
+        &.active {
+          > span,
+          > div > span {
+            color: rgba(255, 255, 255, 1);
+          }
+        }
+      }
+    }
+  }
+
+  .top-con {
+    margin: 0 auto 10px;
+    padding: 10px 0;
+    background: linear-gradient(
+      268deg,
+      rgba(0, 0, 0, 0) 0%,
+      rgba(0, 0, 0, 0.4) 25%,
+      rgba(0, 0, 0, 0.4) 75%,
+      rgba(0, 0, 0, 0) 100%
+    );
+  }
+
+  #swcatalogRoot {
+    > ul {
+      > li {
+        width: 104px;
+        background: rgba(0, 0, 0, 0.5);
+        border-radius: 4px;
+        padding: 4px 10px;
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        box-sizing: border-box;
+        overflow: hidden;
+
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          border: 1px solid rgba(255, 255, 255, 1);
+        }
+      }
+    }
+  }
+
+  #swSecondary {
+    margin: 20px auto 10px;
+
+    > ul {
+      > li {
+        width: 84px;
+        box-sizing: border-box;
+        overflow: hidden;
+        padding-bottom: 6px;
+
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          position: relative;
+
+          &::before {
+            content: "";
+            display: inline-block;
+            position: absolute;
+            bottom: 0;
+            width: 20px;
+            height: 2px;
+            z-index: 9999;
+            left: 50%;
+            transform: translateX(-50%);
+            background: var(--colors-primary-base);
+          }
+        }
+      }
+    }
+  }
+
+  #swScenes {
+    > ul {
+      > li {
+        cursor: pointer;
+        width: 72px;
+        height: 72px;
+        border-radius: 6px;
+        border: 1px solid #ffffff;
+        background-size: cover;
+        position: relative;
+        overflow: hidden;
+
+        .iconfont {
+          position: absolute;
+          left: 4px;
+          top: 4px;
+          z-index: 99;
+
+          &::after {
+            background: rgba(0, 0, 0, 0.3);
+            content: "";
+            width: 14px;
+            height: 14px;
+            display: inline-block;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            z-index: -1;
+            filter: blur(4px);
+          }
+        }
+
+        > div {
+          position: absolute;
+          bottom: 0;
+          left: 0;
+          height: 20px;
+          background: rgba(0, 0, 0, 0.5);
+          width: 100%;
+          overflow: hidden;
+
+          > span,
+          div {
+            // width: 100%;
+            line-height: 20px;
+            word-break: keep-all;
+            white-space: normal;
+          }
+        }
+
+        &.active {
+          border: 1px solid var(--colors-primary-base);
+
+          > div {
+            > span {
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.barshow {
+  max-height: 190px;
+}
+
+@keyframes wordsLoop {
+  0% {
+    transform: translateX(100%);
+    -webkit-transform: translateX(100%);
+  }
+  100% {
+    transform: translateX(-180%);
+    -webkit-transform: translateX(-180%);
+  }
+}
+.marquee {
+  .marquee-text-wrap {
+    height: 20px;
+    line-height: 20px;
+  }
+}
+</style>
+<style>
+.marquee-text-text {
+  padding: 0 5px;
+}
+</style>

+ 412 - 460
packages/qjkankan-view/src/components/UIGather/list.vue

@@ -1,171 +1,104 @@
 <template>
-  <div
-    class="bar-list"
-    v-if="
-      show &&
-      !(
-        metadata.catalogRoot &&
-        metadata.catalogRoot.length == 1 &&
-        scenes.length == 1 &&
-        secondaryList.length == 1
-      )
-    "
-    :class="{ barshow: isShowScenesList }"
-  >
-    <div class="top-con">
-      <div
-        class="swiper-container swiper1"
-        :style="`width:calc(100% - 20px)`"
-        id="swScenes"
-        v-if="currentScenesList.length > 0"
-      >
-        <ul class="swiper-wrapper">
-          <li
-            @click="tabCurrentScene(item)"
-            class="swiper-slide"
+  <div class="bar-list" v-if="metadata?.navigationTrees?.length" :class="{ barshow: isShowScenesList }">
+    <div class="top-con" v-show="currentScenesList.length >= 1">
+      <div v-if="currentScenesList.length >= 1" class="scene-list swiper-container" ref="scene-swiper" :style="`width:${scenesListW > 1150 ? '100%' : scenesListW + 'px'}`">
+        <!-- <div class="scene-list swiper-container" ref="scene-swiper"> -->
+        <div class="swiper-wrapper scene-wrapper">
+          <!-- <div
+            class="swiper-slide scene-slide"
             :class="{
-              active: currentScene.sceneCode == item.sceneCode,
+              active: currentScene.id == item.id ||currentScene.id == item.sid  ,
+              disabled: isLockV4Scene,
               // loopspan:
               //   item.sceneTitle.length > spanlength &&
               //   currentScene.id == item.id,
             }"
-            :style="{ backgroundImage: `url(${item.icon})` }"
-            v-for="(item, i) in currentScenesList"
-            :key="i"
-          >
-            <i
-              class="iconfont"
-              :class="
-                item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'
-              "
-            ></i>
-            <div class="marquee">
-              <marquee-text
-                :repeat="1"
-                :duration="Math.ceil(item.sceneTitle.length / 10) * 5"
-                :key="item.id"
-                v-if="
-                  item.sceneTitle.length > spanlength &&
-                  currentScene.id == item.id
-                "
-              >
-                {{ item.sceneTitle }}
-              </marquee-text>
-              <span v-else>
-                {{ item.sceneTitle }}
-              </span>
-            </div>
-          </li>
-        </ul>
-      </div>
-
-      <div
-        class="swiper-container swiper2"
-        id="swSecondary"
-        :style="`width:${clamp(secondaryW, 0, 1150)}px`"
-        v-if="secondaryList.length > 1"
-      >
-        <!-- {{ clamp(secondaryW, 0, 1150) }} -->
-        <ul class="swiper-wrapper">
-          <li
-            class="swiper-slide"
-            @click="tabSecondary(item, i)"
+            v-for="(item, index) in currentScenesList"
+          > -->
+          <div
+            class="swiper-slide scene-slide"
             :class="{
-              active: currentSecondary.id == item.id,
+              active: currentScene.id == item.id || (currentScene.sid && currentScene.sid == item.sid),
+
               // loopspan:
-              //   fixTitle(item.name).length > spanlength &&
-              //   currentSecondary.id == item.id,
+              //   item.sceneTitle.length > spanlength &&
+              //   currentScene.id == item.id,
             }"
-            v-for="(item, i) in secondaryList"
-            :key="i"
+            v-for="(item, index) in currentScenesList"
           >
-            <!-- {{ Math.ceil(fixTitle(item.name).length / 10) }} -->
-
-            <marquee-text
-              :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
-              :key="item.id"
-              :repeat="1"
-              v-if="
-                fixTitle(item.name).length > spanlength &&
-                currentSecondary.id == item.id
-              "
-            >
-              {{ fixTitle(item.name) }}
-            </marquee-text>
-            <span v-else>
-              {{ fixTitle(item.name) }}
-            </span>
-            <!-- <span v-if="currentSecondary.id == item.id">{{
-              fixTitle(item.name)
-            }}</span>
-            <span v-else>{{
-              fixTitle(item.name).length > spanlength
-                ? fixTitle(item.name).slice(0, spanlength)
-                : fixTitle(item.name)
-            }}</span> -->
-          </li>
-        </ul>
+            <div @click="tabCurrentScene(item, index)" class="scene-content" :style="`background-image:url(${item.icon});`">
+              <!-- <img :src="i.icon" alt="" /> -->
+
+              <i class="iconfont" :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
+              <div class="marquee">
+                <marquee-text :repeat="1" :duration="Math.ceil(item.name.length / 10) * 5" :key="item.id" v-if="item.name.length > spanlength && currentScene.id == item.id">
+                  {{ item.name }}
+                </marquee-text>
+                <span v-else>
+                  {{ item.name }}
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="second-group-box" v-if="metadata?.navigationTrees[rootTabIndex]">
+        <!-- <div v-if="metadata?.navigationTrees[rootTabIndex]?.children[0]?.type == 'group' && showSecondTab" class="second-group-list swiper-container" ref="second-group-swiper"> -->
+        <div v-if="secondGroups.length > 1" class="second-group-list swiper-container" ref="second-group-swiper">
+          <div class="swiper-wrapper second-group-wrapper">
+            <template v-for="(item, index) in metadata?.navigationTrees[rootTabIndex]?.children">
+              <div
+                class="swiper-slide second-group-slide"
+                @click="tabSecond(item, index)"
+                v-if="item.children.length"
+                :class="{ active: currentSecondId == item.id, loopspan: fixTitle(item.name).length > spanlength && currentSecondId == item.id }"
+              >
+                <div class="second-group-content">
+                  <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentSecondId == item.id">
+                    {{ fixTitle(item.name) }}
+                  </marquee-text>
+                  <span v-else>
+                    {{ fixTitle(item.name) }}
+                  </span>
+                </div>
+              </div>
+            </template>
+          </div>
+        </div>
       </div>
     </div>
-
-    <div
-      class="swiper-container"
-      id="swcatalogRoot"
-      :style="`width:${catalogRootW > innerW ? '100%' : catalogRootW + 'px'}`"
-      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1"
-    >
-      <ul class="swiper-wrapper" v-if="metadata.catalogRoot.length > 1">
-        <li
-          class="swiper-slide"
-          :class="{
-            active: currentCatalogRoot.id == item.id,
-            // loopspan:
-            //   fixTitle(item.name).length > spanlength &&
-            //   currentCatalogRoot.id == item.id,
-          }"
-          @click="tabRoot(item)"
-          v-for="(item, i) in metadata.catalogRoot"
-          :key="i"
-        >
-          <!-- <span v-if="currentCatalogRoot.id == item.id">{{
-            fixTitle(item.name)
-          }}</span>
-          <span v-else>{{
-            fixTitle(item.name).length > spanlength
-              ? fixTitle(item.name).slice(0, spanlength)
-              : fixTitle(item.name)
-          }}</span> -->
-
-          <marquee-text
-            :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
-            :key="item.id"
-            :repeat="1"
-            v-if="
-              fixTitle(item.name).length > spanlength &&
-              currentCatalogRoot.id == item.id
-            "
-          >
-            {{ fixTitle(item.name) }}
-          </marquee-text>
-          <span v-else>
-            {{ fixTitle(item.name) }}
-          </span>
-        </li>
-      </ul>
+    <div class="bottom-com">
+      <div v-if="metadata?.navigationTrees?.length > 1" :style="`width:${catalogRootW}px;`" class="root-group-list swiper-container" ref="root-group">
+        <div class="swiper-wrapper root-group-wrapper">
+          <template v-for="(item, index) in metadata?.navigationTrees">
+            <div
+              v-if="item.children.length"
+              class="swiper-slide root-group-slide"
+              :class="{
+                active: currentRootId == item.id,
+
+                loopspan: fixTitle(item.name).length > spanlength && currentRootId.id == item.id,
+              }"
+              @click="tabRoot(item, index)"
+            >
+              <div class="root-group-content">
+                <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentRootId == item.id">
+                  {{ fixTitle(item.name) }}
+                </marquee-text>
+                <span v-else>
+                  {{ fixTitle(item.name) }}
+                </span>
+              </div>
+            </div>
+          </template>
+        </div>
+      </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import {
-  ref,
-  watch,
-  computed,
-  onMounted,
-  nextTick,
-  unref,
-  watchEffect,
-} from "vue";
+import { ref, watch, computed, onMounted, nextTick, unref, watchEffect } from "vue";
 import { useStore } from "vuex";
 import { useApp } from "@/app";
 import MarqueeText from "vue-marquee-text-component";
@@ -179,69 +112,121 @@ const spanlength = ref(5);
 const metadata = computed(() => store.getters["scene/metadata"]);
 const scenes = computed(() => store.getters["scene/list"]);
 const currentScene = computed(() => store.getters["scene/currentScene"]);
+const currentSecondId = computed(() => store.getters["scene/currentSecondId"]);
+const currentRootId = computed(() => store.getters["scene/currentRootId"]);
 
-const clamp = computed(
-  () => (num, min, max) => Math.min(Math.max(num, min), max)
-);
+const clamp = computed(() => (num, min, max) => Math.min(Math.max(num, min), max));
 
-const currentCatalogRoot = computed(
-  () => store.getters["scene/currentCatalogRoot"]
-);
-const currentSecondary = computed(
-  () => store.getters["scene/currentSecondary"]
-);
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
+const currentSecondary = computed(() => store.getters["scene/currentSecondary"]);
 
 const secondaryList = computed(() => store.getters["scene/secondaryList"]);
 
-const isShowScenesList = computed(
-  () => store.getters["functions/isShowScenesList"]
-);
+const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
 
-const currentScenesList = computed(
-  () => store.getters["scene/currentScenesList"]
-);
+const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
 
 const show = ref(false);
-
 const swidth = ref({
   swcatalogRoot: 104,
   swSecondary: 84,
   swScenes: 72,
 });
 
-const scenesListW = computed(
-  () => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10
-);
-const secondaryW = computed(
-  () => secondaryList.value.length * (swidth.value["swSecondary"] + 10) - 10
-);
-const catalogRootW = computed(
-  () =>
-    metadata.value.catalogRoot.length * (swidth.value["swcatalogRoot"] + 10) -
-    10
-);
-const innerW = computed(() => 1150);
+const SceneSwiper = ref(null);
+const SecondGroupSwiper = ref(null);
+const rootGroupSwiper = ref(null);
 
-const tabCurrentScene = (data) => {
-  console.log("tabCurrentScene", data.id, currentScene.value.id);
-  if (data.id !== currentScene.value.id) {
-    store.commit("scene/setCurrentScene", data);
-    setTimeout(() => {
-      scenesSwiperFocus();
-    }, 300);
-  } else {
-    console.log("重复点击当前导航");
-    // window.alert("alert-test-->重复点击当前导航");
+const rootTabIndex = computed(() => {
+  let idx = 0;
+  if (metadata.value && metadata.value?.navigationTrees) {
+    idx = metadata.value?.navigationTrees.findIndex((item) => item.id == currentRootId.value);
   }
-};
+  return idx;
+});
 
-const tabSecondary = (data, index) => {
-  store.commit("scene/setCurrentSecondary", data);
-};
+const secondTabIndex = computed(() => {
+  let idx = 0;
+  if (metadata.value && metadata.value?.navigationTrees) {
+    idx = metadata.value.navigationTrees[rootTabIndex.value]?.children.findIndex((item) => item.id == currentSecondId.value);
+  }
+  return idx;
+});
 
-const tabRoot = (data) => {
-  store.commit("scene/setCurrentCatalogRoot", data);
-};
+const showSecondTab = computed(() => {
+  return metadata.value && metadata.value?.navigationTrees ? metadata.value?.navigationTrees[rootTabIndex.value].children.some((item) => item.id != currentSecondId.value) : false;
+});
+const secondGroups = computed(() => {
+  return metadata.value?.navigationTrees[rootTabIndex.value].children.filter((item) => item.children.length);
+});
+
+const scenesListW = computed(() => currentScenesList.value.length * (swidth.value["swScenes"] + 10));
+
+const secondaryW = computed(() => {
+  let list = metadata.value?.navigationTrees[rootTabIndex.value].children.filter((item) => item.children.length);
+  return list.length * (swidth.value["swSecondary"] + 10) - 10;
+});
+const catalogRootW = computed(() => {
+  let list = metadata.value.navigationTrees.filter((item) => item.children.length);
+  return list.length * (swidth.value["swcatalogRoot"] + 10);
+});
+
+const innerW = computed(() => 1150);
+watch(
+  () => currentScenesList.value,
+  () => {
+    nextTick(() => {
+      initSceneSwiper();
+      initRootGroupSwiper();
+      // if (metadata.value.navigationTrees[rootTabIndex.value]?.children[secondTabIndex.value]?.type == "group") {
+      //   initSecondGroupSwiper();
+      // }
+    });
+  }
+);
+watch(
+  () => currentScene.value,
+  () => {
+    nextTick(() => {
+      let currentSceneData = currentScene.value;
+      metadata.value.navigationTrees.forEach((item, index) => {
+        let activeScene = item.children.find((pano) => pano.id == currentSceneData.id);
+
+        if (activeScene) {
+          store.commit("scene/setCurrentScenesList", item.children);
+          store.commit("scene/setData", { currentSecondId: null, currentRootId: item.id });
+        }
+        item = item.children.forEach((s_item, s_index) => {
+          activeScene = s_item.children.find((pano) => pano.id == currentSceneData.id);
+
+          if (activeScene) {
+            store.commit("scene/setCurrentScenesList", s_item.children);
+            store.commit("scene/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+          }
+          s_item = s_item.children.forEach((t_item, t_index) => {
+            activeScene = t_item.children.find((pano) => pano.id == currentSceneData.id);
+
+            if (activeScene) {
+              store.commit("scene/setCurrentScenesList", s_item.children);
+              store.commit("scene/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+            }
+          });
+        });
+      });
+    });
+  }
+);
+
+watch(
+  () => currentRootId.value,
+  (val) => {
+    nextTick(() => {
+      if (secondGroups.value.length > 1) {
+        initSecondGroupSwiper();
+      }
+    });
+  }
+);
 
 const fixTitle = (name) => {
   if (name == "默认二级分组") {
@@ -256,6 +241,7 @@ const fixTitle = (name) => {
 const swiperOptions = {
   slidesPerView: "auto",
   centeredSlides: true,
+  slideToClickedSlide: true,
   spaceBetween: 10,
   centerInsufficientSlides: true,
   centeredSlidesBounds: true,
@@ -267,71 +253,111 @@ const swiperOptions = {
   },
 };
 
-// const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+const tabRoot = (item, index) => {
+  store.commit("scene/setData", { currentRootId: item.id, currentSecondId: null });
+  setTimeout(() => {
+    changeSceneList();
+  }, 0);
+};
+const tabSecond = (item, index) => {
+  store.commit("scene/setData", { currentSecondId: item.id });
+  let sceneList = metadata.value.navigationTrees[rootTabIndex.value].children[secondTabIndex.value].children;
+  store.commit("scene/setCurrentScenesList", sceneList);
+};
+const changeSceneList = () => {
+  let currentList = null;
+
+  if (metadata.value.navigationTrees[rootTabIndex.value].children.length && metadata.value.navigationTrees[rootTabIndex.value].children[0].type == "group") {
+    store.commit("scene/setData", { currentSecondId: metadata.value.navigationTrees[rootTabIndex.value].children[0].id });
+
+    //如果有当前视图则选择二级目录
+    metadata.value.navigationTrees[rootTabIndex.value].children.forEach((item, index) => {
+      if (item.children.length) {
+        item.children.forEach((t_item, t_index) => {
+          if (t_item.id == currentScene.value.id || (t_item.sid && currentScene.value.sid && t_item.sid == currentScene.value.sid)) {
+            store.commit("scene/setData", { currentSecondId: item.id });
+          }
+        });
+      }
+    });
+
+    currentList = metadata.value.navigationTrees[rootTabIndex.value].children[secondTabIndex.value].children;
+    store.commit("scene/setCurrentScenesList", currentList);
+  }
 
-const initMainSwiper = () => {
+  if (secondTabIndex.value == -1) {
+    console.error("没有二级目录");
+    let rootList = metadata.value.navigationTrees.find((item) => item.id == currentRootId.value);
+    store.commit("scene/setCurrentScenesList", rootList.children);
+  }
   nextTick(() => {
-    if (window.mainNatSwiper) {
-      // window.mainNatSwiper = null;
-      window.mainNatSwiper.update();
-      window.sencordNatSwiper.slideReset();
+    // initSceneSwiper();
+    // initRootGroupSwiper();
+    if (metadata.value.navigationTrees[currentRootId.value]?.children[currentSecondId.value]?.type == "group") {
+      // initSecondGroupSwiper();
     }
-    window.mainNatSwiper = new Swiper("#swcatalogRoot", swiperOptions);
   });
 };
-const initsencordNatSwiper = () => {
+const initSceneSwiper = () => {
+  if (currentScenesList.value.length <= 1) {
+    return;
+  }
+  if (SceneSwiper.value) {
+    SceneSwiper.value.destroy();
+    SceneSwiper.value = null;
+  }
+  if (!currentScenesList.value.length) {
+    return;
+  }
+
   nextTick(() => {
-    console.warn("initsencordNatSwiper");
-    if (window.sencordNatSwiper) {
-      window.sencordNatSwiper.update();
-      window.sencordNatSwiper.slideReset();
+    SceneSwiper.value = new Swiper(".scene-list", swiperOptions);
+    let index = currentScenesList.value.findIndex((item) => item.id == currentScene.value.id);
+
+    if (index >= 0) {
+      SceneSwiper.value.slideTo(index);
     }
-    window.sencordNatSwiper = new Swiper("#swSecondary", swiperOptions);
   });
 };
-const initScenesSwiper = () => {
-  // console.warn("initScenesSwiper");
+const tabCurrentScene = (data, index) => {
+  store.commit("scene/setCurrentScene", data);
+
+  // SceneSwiper.value.slideTo(index);
+};
+const initSecondGroupSwiper = () => {
+  if (SecondGroupSwiper.value) {
+    SecondGroupSwiper.value.destroy(false);
+    SecondGroupSwiper.value = null;
+  }
+
   nextTick(() => {
-    if (window.scenesNatSwiper) {
-      try {
-        window.scenesNatSwiper.slides.length > 0 &&  window.scenesNatSwiper.update();
-        window.scenesNatSwiper.slideReset();
-      } catch (error) {}
-
-      // window.scenesNatSwiper = null;
-    } else {
-      window.scenesNatSwiper = new Swiper("#swScenes", {
-        ...swiperOptions,
-      });
+    SecondGroupSwiper.value = new Swiper(".second-group-list", swiperOptions);
+    if (secondTabIndex.value >= 0) {
+      SecondGroupSwiper.value.slideTo(secondTabIndex.value);
     }
-    scenesSwiperFocus();
   });
 };
 
-const scenesSwiperFocus = () => {
-  const sceneIndex = Array.from(currentScenesList.value).findIndex(
-    (item) => item.id === currentScene.value.id
-  );
-  if (window.scenesNatSwiper && window.scenesNatSwiper.slides.length > 0) {
-    const index = sceneIndex < 0 ? 0 : sceneIndex;
-    const fIndex = index < 5 ? 0 : index;
-    console.warn("scenesSwiperFocus", fIndex);
-    window.scenesNatSwiper.slideTo(fIndex);
+const initRootGroupSwiper = () => {
+  // if (rootGroupSwiper.value) {
+  //   rootGroupSwiper.value.destroy();
+  //   rootGroupSwiper.value = null;
+  // }
+  // nextTick(() => {
+  //   rootGroupSwiper.value = new Swiper(".root-group-list", swiperOptions);
+  // });
+  if (metadata.value.navigationTrees.length <= 1) {
+    return;
   }
-};
-
-const sencordNatSwiperFocus = () => {
-  nextTick(() => {
-    const current = Array.from(secondaryList.value).findIndex(
-      (item) => item.id === currentSecondary.value.id
-    );
-    if (window.sencordNatSwiper) {
-      const index = current < 0 ? 0 : current;
-      console.warn("sencordNatSwiperFocus", index);
-      // window.sencordNatSwiper.slideTo(current);
-      window.sencordNatSwiper.slideTo(current);
+  if (!rootGroupSwiper.value) {
+    rootGroupSwiper.value = new Swiper(".root-group-list", swiperOptions);
+    if (rootTabIndex.value >= 0) {
+      rootGroupSwiper.value.slideTo(rootTabIndex.value);
     }
-  });
+    if (secondGroups.value.length > 1) {
+      initSecondGroupSwiper();
+    }
+  }
 };
 
 onMounted(() => {
@@ -340,253 +366,179 @@ onMounted(() => {
       store.commit("functions/setShowScenesList", false);
     }
     show.value = true;
-  });
-  watchEffect(() => {
-    if (
-      metadata.value.catalogRoot &&
-      unref(metadata.value.catalogRoot).length > 0
-    ) {
-      initMainSwiper();
-    }
-  });
-
-  watch([currentSecondary, secondaryList], () => {
-    if (unref(secondaryList).length > 1) {
-      initsencordNatSwiper();
-      sencordNatSwiperFocus();
-    } else {
-      if (window.sencordNatSwiper) {
-        console.warn("destroy-sencordNatSwiper");
-        window.sencordNatSwiper.update();
-        window.sencordNatSwiper.slideReset();
-      }
-    }
-  });
-
-  watch(currentScenesList, () => {
-    initScenesSwiper();
-  });
-  watch(currentCatalogRoot, (val) => {
-    if (Array.from(unref(val).children).includes(currentScene.value.category)) {
-      //当前场景在一类的children
-      const activeSecond = Array.from(unref(secondaryList)).find(
-        (i) => i.id === currentScene.value.category
-      );
-      // console.log("activeSecond", activeSecond);
-      store.commit("scene/setCurrentSecondary", activeSecond);
-      if (window.sencordNatSwiper) {
-        window.sencordNatSwiper.update();
-      }
-    }
+    console.error("app", show.value);
   });
 });
 </script>
 
-<style lang="scss" scoped>
-$width: 1150px;
+<style lang="less" scoped>
+@width: 1150px;
+.swiper-slide {
+  cursor: pointer;
+  &.loopspan {
+    > span,
+    > div > span {
+      animation: 5s wordsLoop linear infinite normal;
+    }
+  }
+}
 
 .bar-list {
   position: absolute;
-  bottom: 68px;
+  bottom: 28px;
   left: 50%;
   transform: translateX(-50%);
   text-align: center;
-  max-width: $width;
+  max-width: @width;
   overflow: hidden;
-  max-height: 0;
   transition: 0.3s all ease;
-  z-index: 9;
-
-  .swiper-container {
-    width: 100%;
-    position: relative;
-    margin: 0 auto;
-
-    > ul {
-      > li {
-        white-space: nowrap;
-
-        > span,
-        > div > span {
-          cursor: pointer;
-          display: inline-block;
-          color: rgba(255, 255, 255, 0.6);
-        }
-
-        &.loopspan {
-          > span,
-          > div > span {
-            animation: 10s wordsLoop linear infinite normal;
-          }
-        }
-
-        &.active {
-          > span,
-          > div > span {
-            color: rgba(255, 255, 255, 1);
-          }
-        }
-      }
-    }
+  max-height: 0px;
+  &.barshow {
+    max-height: 190px;
   }
-
   .top-con {
+    display: inline-block;
     margin: 0 auto 10px;
-    padding: 10px 0;
-    background: linear-gradient(
-      268deg,
-      rgba(0, 0, 0, 0) 0%,
-      rgba(0, 0, 0, 0.4) 25%,
-      rgba(0, 0, 0, 0.4) 75%,
-      rgba(0, 0, 0, 0) 100%
-    );
-  }
-
-  #swcatalogRoot {
-    > ul {
-      > li {
-        width: 104px;
-        background: rgba(0, 0, 0, 0.5);
-        border-radius: 4px;
-        padding: 4px 10px;
-        border: 1px solid rgba(255, 255, 255, 0.5);
-        box-sizing: border-box;
-        overflow: hidden;
-
-        > span {
-          width: 100%;
-          word-break: keep-all;
-        }
-
-        &.active {
-          border: 1px solid rgba(255, 255, 255, 1);
+    padding: 10px 6px;
+    overflow: hidden;
+    background: linear-gradient(268deg, transparent, rgba(0, 0, 0, 0.4) 25%, rgba(0, 0, 0, 0.4) 75%, transparent);
+    .scene-list {
+      max-width: @width;
+      margin: 0 auto;
+      // display: inline-block;
+
+      .scene-wrapper {
+        .scene-slide {
+          margin: 0 5px;
+          // white-space: nowrap;
+          cursor: pointer;
+          width: 72px;
+          height: 72px;
+          border-radius: 6px;
+          border: 1px solid #fff;
+          background-size: cover;
+          position: relative;
+          overflow: hidden;
+          &.active {
+            border: 1px solid #0076f6;
+            .marquee {
+              color: #fff !important;
+            }
+          }
+          .scene-content {
+            width: 100%;
+            height: 100%;
+            background-size: cover;
+            position: relative;
+
+            img {
+              width: 100%;
+              height: 100%;
+              pointer-events: none;
+              object-fit: cover;
+            }
+            > .iconfont {
+              position: absolute;
+              left: 4px;
+              top: 4px;
+              z-index: 99;
+              text-shadow: 0px 0px 4px rgb(0, 0, 0, 0.3);
+            }
+            .marquee {
+              position: absolute;
+              bottom: 0;
+              left: 0;
+              height: 20px;
+              background: rgba(0, 0, 0, 0.5);
+              width: 100%;
+              overflow: hidden;
+              color: rgba(255, 255, 255, 0.6);
+              > span {
+                width: 100%;
+                line-height: 20px;
+                word-break: keep-all;
+                display: inline-block;
+              }
+            }
+          }
         }
       }
     }
-  }
-
-  #swSecondary {
-    margin: 20px auto 10px;
-
-    > ul {
-      > li {
-        width: 84px;
-        box-sizing: border-box;
-        overflow: hidden;
-        padding-bottom: 6px;
 
-        > span {
-          width: 100%;
-          word-break: keep-all;
-        }
-
-        &.active {
-          position: relative;
-
-          &::before {
-            content: "";
-            display: inline-block;
-            position: absolute;
-            bottom: 0;
-            width: 20px;
-            height: 2px;
-            z-index: 9999;
-            left: 50%;
-            transform: translateX(-50%);
-            background: var(--colors-primary-base);
+    .second-group-box {
+      .second-group-list {
+        max-width: @width;
+        margin: 0 auto;
+        display: inline-block;
+
+        .second-group-wrapper {
+          .second-group-slide {
+            width: 84px;
+            box-sizing: border-box;
+            overflow: hidden;
+            padding-bottom: 6px;
+            cursor: pointer;
+            margin: 0 5px;
+            white-space: nowrap;
+            color: hsla(0, 0%, 100%, 0.6);
+            margin: 20px auto 10px;
+            &.active {
+              color: #fff;
+
+              &::before {
+                content: "";
+                display: inline-block;
+                position: absolute;
+                bottom: 0;
+                width: 20px;
+                height: 2px;
+                z-index: 9999;
+                left: 50%;
+                transform: translateX(-50%);
+                background: #0076f6;
+              }
+            }
+            .second-group-content {
+              // width: 100%;
+              // height: 100%;
+              // background-size: cover;
+              // position: relative;
+            }
           }
         }
       }
     }
   }
-
-  #swScenes {
-    > ul {
-      > li {
-        cursor: pointer;
-        width: 72px;
-        height: 72px;
-        border-radius: 6px;
-        border: 1px solid #ffffff;
-        background-size: cover;
-        position: relative;
-        overflow: hidden;
-
-        .iconfont {
-          position: absolute;
-          left: 4px;
-          top: 4px;
-          z-index: 99;
-
-          &::after {
-            background: rgba(0, 0, 0, 0.3);
-            content: "";
-            width: 14px;
-            height: 14px;
-            display: inline-block;
-            position: absolute;
-            top: 50%;
-            left: 50%;
-            transform: translate(-50%, -50%);
-            z-index: -1;
-            filter: blur(4px);
-          }
-        }
-
-        > div {
-          position: absolute;
-          bottom: 0;
-          left: 0;
-          height: 20px;
+  .bottom-com {
+    .root-group-list {
+      max-width: @width;
+      margin: 0 auto;
+      .swiper-wrapper {
+      }
+      .root-group-wrapper {
+        .root-group-slide {
+          width: 104px;
           background: rgba(0, 0, 0, 0.5);
-          width: 100%;
+          border-radius: 4px;
+          padding: 4px 10px;
+          border: 1px solid hsla(0, 0%, 100%, 0.5);
+          box-sizing: border-box;
           overflow: hidden;
+          margin: 0 5px;
+          white-space: nowrap;
+          color: hsla(0, 0%, 100%, 0.6);
 
-          > span,
-          div {
-            // width: 100%;
-            line-height: 20px;
-            word-break: keep-all;
-            white-space: normal;
+          &.active {
+            border: 1px solid #fff;
+            color: #fff;
           }
-        }
-
-        &.active {
-          border: 1px solid var(--colors-primary-base);
-
-          > div {
-            > span {
-            }
+          .root-group-content {
+            width: 100%;
+            word-break: keep-all;
           }
         }
       }
     }
   }
 }
-
-.barshow {
-  max-height: 190px;
-}
-
-@keyframes wordsLoop {
-  0% {
-    transform: translateX(100%);
-    -webkit-transform: translateX(100%);
-  }
-  100% {
-    transform: translateX(-180%);
-    -webkit-transform: translateX(-180%);
-  }
-}
-.marquee {
-  .marquee-text-wrap {
-    height: 20px;
-    line-height: 20px;
-  }
-}
-</style>
-<style>
-.marquee-text-text {
-  padding: 0 5px;
-}
 </style>

+ 2 - 2
packages/qjkankan-view/src/components/UIGather/logo.vue

@@ -1,8 +1,8 @@
 <template>
-  <div class="logo" v-if="metadata.isLogo && fdkkCurrentVersion != 'V3'">
+  <div class="logo" v-if="metadata?.workLogo?.isLogo && fdkkCurrentVersion != 'V3'">
     <img
       :src="
-        metadata.logo ||
+        metadata?.workLogo?.logo ||
         require(`@/assets/images/default/logo_white_${local}.svg`)
       "
       alt=""

+ 545 - 0
packages/qjkankan-view/src/components/UIGather/mobile/list copy.vue

@@ -0,0 +1,545 @@
+<template>
+  <div
+    class="bar-list"
+    v-if="
+      show &&
+      !(
+        metadata.catalogRoot &&
+        metadata.catalogRoot.length == 1 &&
+        scenes.length == 1 &&
+        secondaryList.length == 1
+      )
+    "
+    :class="{ barshow: isShowScenesList }"
+  >
+    <div class="top-con">
+      <div
+        class="swiper-container"
+        id="swScenes"
+        :style="`width:${Math.min(scenesListW, innerW)}px;
+      padding:${scenesListW > innerW ? '0 15px' : '0'}`"
+        v-if="currentScenesList.length > 0"
+      >
+        <ul class="swiper-wrapper">
+          <li
+            @click="tabCurrentScene(item)"
+            class="swiper-slide"
+            :class="{
+              active: currentScene.sceneCode == item.sceneCode,
+              loopspan:
+                item.sceneTitle.length > spanlength &&
+                currentScene.id == item.id,
+            }"
+            :style="{ backgroundImage: `url(${item.icon})` }"
+            v-for="(item, i) in currentScenesList"
+            :key="i"
+          >
+            <i
+              class="iconfont"
+              :class="
+                item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'
+              "
+            ></i>
+            <div class="marquee">
+              <marquee-text
+                :repeat="1"
+                :duration="Math.ceil(item.sceneTitle.length / 10) * 5"
+                :key="item.id"
+                v-if="
+                  item.sceneTitle.length > spanlength &&
+                  currentScene.id == item.id
+                "
+              >
+                {{ item.sceneTitle }}
+              </marquee-text>
+              <span v-else>
+                {{ item.sceneTitle }}
+              </span>
+            </div>
+          </li>
+        </ul>
+      </div>
+
+      <div
+        class="swiper-container"
+        id="swSecondary"
+        :style="`width:${Math.min(secondaryW, innerW)}px;
+      padding:${secondaryW > innerW ? '0 15px' : '0'}`"
+        v-if="secondaryList.length > 1"
+      >
+        <ul class="swiper-wrapper">
+          <li
+            class="swiper-slide"
+            @click="tabSecondary(item)"
+            :class="{
+              active: currentSecondary.id == item.id,
+              loopspan:
+                fixTitle(item.name).length > spanlength &&
+                currentSecondary.id == item.id,
+            }"
+            v-for="(item, i) in secondaryList"
+            :key="i"
+          >
+            <marquee-text
+              :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
+              :key="item.id"
+              :repeat="1"
+              v-if="
+                fixTitle(item.name).length > spanlength &&
+                currentSecondary.id == item.id
+              "
+            >
+              {{ fixTitle(item.name) }}
+            </marquee-text>
+            <span v-else>
+              {{ fixTitle(item.name) }}
+            </span>
+          </li>
+        </ul>
+      </div>
+    </div>
+
+    <div
+      class="swiper-container"
+      id="swcatalogRoot"
+      :style="`width:${Math.min(catalogRootW, innerW)}px;
+    padding:${catalogRootW > innerW ? '0 15px' : '0'}`"
+      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1"
+    >
+      <ul class="swiper-wrapper" v-show="metadata.catalogRoot.length > 1">
+        <li
+          class="swiper-slide"
+          :class="{
+            active: currentCatalogRoot.id == item.id,
+            loopspan:
+              fixTitle(item.name).length > spanlength &&
+              currentCatalogRoot.id == item.id,
+          }"
+          @click="tabRoot(item)"
+          v-for="(item, i) in metadata.catalogRoot"
+          :key="i"
+        >
+          <marquee-text
+            :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
+            :key="item.id"
+            :repeat="1"
+            v-if="
+              fixTitle(item.name).length > spanlength &&
+              currentCatalogRoot.id == item.id
+            "
+          >
+            {{ fixTitle(item.name) }}
+          </marquee-text>
+          <span v-else>
+            {{ fixTitle(item.name) }}
+          </span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  unref,
+  watch,
+  watchEffect,
+  computed,
+  onMounted,
+  nextTick,
+} from "vue";
+import { useStore } from "vuex";
+import { useApp } from "@/app";
+import { useI18n, getLocale } from "@/i18n";
+const { t } = useI18n({ useScope: "global" });
+import MarqueeText from "vue-marquee-text-component";
+
+const store = useStore();
+
+const spanlength = ref(5);
+
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
+const currentCatalogRoot = computed(
+  () => store.getters["scene/currentCatalogRoot"]
+);
+const currentSecondary = computed(
+  () => store.getters["scene/currentSecondary"]
+);
+
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+
+const isShowScenesList = computed(
+  () => store.getters["functions/isShowScenesList"]
+);
+
+const currentScenesList = computed(
+  () => store.getters["scene/currentScenesList"]
+);
+
+const swidth = ref({
+  swcatalogRoot: 104,
+  swSecondary: 84,
+  swScenes: 72,
+});
+
+const innerW = computed(() => window.innerWidth);
+
+const scenesListW = computed(
+  () => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10
+);
+const secondaryW = computed(
+  () => secondaryList.value.length * (swidth.value["swSecondary"] + 10) - 10
+);
+const catalogRootW = computed(
+  () =>
+    metadata.value.catalogRoot.length * (swidth.value["swcatalogRoot"] + 10) -
+    10
+);
+
+const show = ref(false);
+
+const tabCurrentScene = (data) => {
+  console.log("tabCurrentScene", data.id, currentScene.value.id);
+  if (data.id !== currentScene.value.id) {
+    store.commit("scene/setCurrentScene", data);
+    setTimeout(() => {
+      scenesSwiperFocus();
+    }, 300);
+  } else {
+    console.log("重复点击当前导航");
+    // window.alert("alert-test-->重复点击当前导航");
+  }
+};
+
+const tabSecondary = (data) => {
+  store.commit("scene/setCurrentSecondary", data);
+};
+
+const tabRoot = (data) => {
+  store.commit("scene/setCurrentCatalogRoot", data);
+};
+
+const fixTitle = (name) => {
+  if (name == "默认二级分组") {
+    name = t("navigation.default_group_two");
+  } else if (name == "一级分组") {
+    name = t("navigation.group_one");
+  } else {
+    name = name;
+  }
+  return name;
+};
+
+const swiperOptions = {
+  slidesPerView: "auto",
+  centeredSlides: true,
+  spaceBetween: 10,
+  centerInsufficientSlides: true,
+  centeredSlidesBounds: true,
+  freeMode: {
+    enabled: true,
+    sticky: false,
+  },
+};
+
+// const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+
+const initMainSwiper = () => {
+  nextTick(() => {
+    if (window.mainNatSwiper) {
+      window.mainNatSwiper.update();
+      window.sencordNatSwiper.slideReset();
+    }
+    window.mainNatSwiper = new Swiper("#swcatalogRoot", swiperOptions);
+  });
+};
+const initsencordNatSwiper = () => {
+  nextTick(() => {
+    if (window.sencordNatSwiper) {
+      window.sencordNatSwiper.update();
+      window.sencordNatSwiper.slideReset();
+    }
+    window.sencordNatSwiper = new Swiper("#swSecondary", swiperOptions);
+  });
+};
+const initScenesSwiper = () => {
+  // console.warn("initScenesSwiper");
+  nextTick(() => {
+    if (window.scenesNatSwiper) {
+      window.scenesNatSwiper.slides.length > 0 &&
+        window.scenesNatSwiper.update();
+      window.scenesNatSwiper.slideReset();
+      // window.scenesNatSwiper = null;
+    } else {
+      window.scenesNatSwiper = new Swiper("#swScenes", {
+        ...swiperOptions,
+      });
+    }
+    scenesSwiperFocus();
+  });
+};
+
+const scenesSwiperFocus = () => {
+  const sceneIndex = Array.from(currentScenesList.value).findIndex(
+    (item) => item.id === currentScene.value.id
+  );
+  if (window.scenesNatSwiper && window.scenesNatSwiper.slides.length > 0) {
+    const index = sceneIndex < 0 ? 0 : sceneIndex;
+    const fIndex = index < 5 ? 0 : index;
+    console.warn("scenesSwiperFocus", fIndex);
+    window.scenesNatSwiper.slideTo(fIndex);
+  }
+};
+
+const sencordNatSwiperFocus = () => {
+  nextTick(() => {
+    const current = Array.from(secondaryList.value).findIndex(
+      (item) => item.id === currentSecondary.value.id
+    );
+    if (window.sencordNatSwiper && window.sencordNatSwiper.slides.length > 0) {
+      console.warn("current-sencordNatSwiper-index", current);
+      window.sencordNatSwiper.slideTo(current);
+    }
+  });
+};
+
+onMounted(() => {
+  useApp().then(async (app) => {
+    show.value = true;
+  });
+  watchEffect(() => {
+    if (
+      metadata.value.catalogRoot &&
+      unref(metadata.value.catalogRoot).length > 0
+    ) {
+      initMainSwiper();
+    }
+  });
+
+  watch(currentSecondary, () => {
+    if (unref(secondaryList).length > 1) {
+      initsencordNatSwiper();
+      sencordNatSwiperFocus();
+    } else {
+      if (window.sencordNatSwiper) {
+        console.warn("destroy-sencordNatSwiper");
+        window.sencordNatSwiper.update();
+        window.sencordNatSwiper.slideReset();
+      }
+    }
+  });
+
+  watch(currentScenesList, () => {
+    initScenesSwiper();
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+.bar-list {
+  position: absolute;
+  bottom: 88px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  overflow: hidden;
+  max-height: 0;
+  transition: 0.3s all ease;
+  z-index: 9;
+  width: 100%;
+
+  .swiper-container {
+    width: 100%;
+    position: relative;
+    margin: 0 auto;
+
+    > ul {
+      > li {
+        white-space: nowrap;
+
+        > span,
+        > div > span {
+          cursor: pointer;
+          display: inline-block;
+          color: rgba(255, 255, 255, 0.6);
+        }
+
+        &.loopspan {
+          > span,
+          > div > span {
+            animation: 5s wordsLoop linear infinite normal;
+          }
+        }
+
+        &.active {
+          > span,
+          > div > span {
+            color: rgba(255, 255, 255, 1);
+          }
+        }
+      }
+    }
+  }
+
+  .top-con {
+    margin-bottom: 10px;
+    padding: 10px 0;
+    width: 100%;
+    background: rgba(0, 0, 0, 0.5);
+  }
+
+  #swcatalogRoot {
+    padding: 0 15px;
+
+    > ul {
+      > li {
+        width: 104px;
+        background: rgba(0, 0, 0, 0.5);
+        border-radius: 4px;
+        padding: 4px 10px;
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        box-sizing: border-box;
+        overflow: hidden;
+
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          border: 1px solid rgba(255, 255, 255, 1);
+        }
+      }
+    }
+  }
+
+  #swSecondary {
+    margin: 20px auto 10px;
+    padding: 0 15px;
+
+    > ul {
+      > li {
+        width: 84px;
+        box-sizing: border-box;
+        overflow: hidden;
+        padding-bottom: 6px;
+
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          position: relative;
+
+          &::before {
+            content: "";
+            display: inline-block;
+            position: absolute;
+            bottom: 0;
+            width: 20px;
+            height: 2px;
+            z-index: 9999;
+            left: 50%;
+            transform: translateX(-50%);
+            background: var(--colors-primary-base);
+          }
+        }
+      }
+    }
+  }
+
+  #swScenes {
+    // padding: 0 15px;
+    > ul {
+      > li {
+        cursor: pointer;
+        width: 72px;
+        height: 72px;
+        border-radius: 6px;
+        border: 1px solid #ffffff;
+        background-size: cover;
+        position: relative;
+        overflow: hidden;
+
+        .iconfont {
+          position: absolute;
+          left: 4px;
+          top: 4px;
+          z-index: 99;
+
+          &::after {
+            background: rgba(0, 0, 0, 0.3);
+            content: "";
+            width: 14px;
+            height: 14px;
+            display: inline-block;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            z-index: -1;
+            filter: blur(4px);
+          }
+        }
+
+        > div {
+          position: absolute;
+          bottom: 0;
+          left: 0;
+          height: 20px;
+          background: rgba(0, 0, 0, 0.5);
+          width: 100%;
+          overflow: hidden;
+
+          > span,
+          div {
+            width: 100%;
+            line-height: 20px;
+            word-break: keep-all;
+          }
+        }
+
+        &.active {
+          border: 1px solid var(--colors-primary-base);
+
+          > div {
+            > span {
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.barshow {
+  max-height: 190px;
+}
+
+@keyframes wordsLoop {
+  0% {
+    transform: translateX(100%);
+    -webkit-transform: translateX(100%);
+  }
+  100% {
+    transform: translateX(-180%);
+    -webkit-transform: translateX(-180%);
+  }
+}
+.marquee {
+  .marquee-text-wrap {
+    height: 20px;
+    line-height: 20px;
+  }
+}
+</style>
+<style>
+.marquee-text-text {
+  padding: 0 5px;
+}
+</style>

+ 239 - 228
packages/qjkankan-view/src/components/UIGather/mobile/list.vue

@@ -1,159 +1,110 @@
 <template>
-  <div
-    class="bar-list"
-    v-if="
-      show &&
-      !(
-        metadata.catalogRoot &&
-        metadata.catalogRoot.length == 1 &&
-        scenes.length == 1 &&
-        secondaryList.length == 1
-      )
-    "
-    :class="{ barshow: isShowScenesList }"
-  >
-    <div class="top-con">
+  <div class="bar-list" v-if="metadata?.navigationTrees?.length" :class="{ barshow: isShowScenesList }">
+    <div class="top-con" v-show="currentScenesList.length">
       <div
-        class="swiper-container"
+        ref="scene-swiper"
+        class="scene-list swiper-container"
         id="swScenes"
         :style="`width:${Math.min(scenesListW, innerW)}px;
       padding:${scenesListW > innerW ? '0 15px' : '0'}`"
         v-if="currentScenesList.length > 0"
       >
         <ul class="swiper-wrapper">
-          <li
-            @click="tabCurrentScene(item)"
-            class="swiper-slide"
-            :class="{
-              active: currentScene.sceneCode == item.sceneCode,
-              loopspan:
-                item.sceneTitle.length > spanlength &&
-                currentScene.id == item.id,
-            }"
-            :style="{ backgroundImage: `url(${item.icon})` }"
-            v-for="(item, i) in currentScenesList"
-            :key="i"
-          >
-            <i
-              class="iconfont"
-              :class="
-                item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'
-              "
-            ></i>
-            <div class="marquee">
-              <marquee-text
-                :repeat="1"
-                :duration="Math.ceil(item.sceneTitle.length / 10) * 5"
-                :key="item.id"
-                v-if="
-                  item.sceneTitle.length > spanlength &&
-                  currentScene.id == item.id
-                "
-              >
-                {{ item.sceneTitle }}
-              </marquee-text>
-              <span v-else>
-                {{ item.sceneTitle }}
-              </span>
-            </div>
-          </li>
+          <template v-for="(item, i) in currentScenesList" :key="item.id">
+            <li
+              @click="tabCurrentScene(item)"
+              class="swiper-slide"
+              :style="`background-image:url(${item.icon});`"
+              :class="{
+                active: currentScene.sceneCode == item.sceneCode,
+                loopspan: item.name.length > spanlength && currentScene.id == item.id,
+              }"
+            >
+              <i class="iconfont" :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
+              <div class="marquee">
+                <marquee-text :repeat="1" :duration="Math.ceil(item.name.length / 10) * 5" :key="item.id" v-if="item.name.length > spanlength && currentScene.id == item.id">
+                  {{ item.name }}
+                </marquee-text>
+                <span v-else>
+                  {{ item.name }}
+                </span>
+              </div>
+            </li>
+          </template>
         </ul>
       </div>
 
       <div
-        class="swiper-container"
+        class="swiper-container second-group-list"
+        ref="second-group-swiper"
         id="swSecondary"
         :style="`width:${Math.min(secondaryW, innerW)}px;
       padding:${secondaryW > innerW ? '0 15px' : '0'}`"
-        v-if="secondaryList.length > 1"
+        v-if="metadata?.navigationTrees[rootTabIndex] && metadata?.navigationTrees[rootTabIndex]?.children[0]?.type == 'group' && showSecondTab"
       >
         <ul class="swiper-wrapper">
-          <li
-            class="swiper-slide"
-            @click="tabSecondary(item)"
-            :class="{
-              active: currentSecondary.id == item.id,
-              loopspan:
-                fixTitle(item.name).length > spanlength &&
-                currentSecondary.id == item.id,
-            }"
-            v-for="(item, i) in secondaryList"
-            :key="i"
-          >
-            <marquee-text
-              :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
+          <template v-for="(item, i) in metadata?.navigationTrees[rootTabIndex]?.children">
+            <li
+              class="swiper-slide"
+              @click="tabSecond(item, i)"
+              :class="{
+                active: currentSecondId == item.id,
+                loopspan: fixTitle(item.name).length > spanlength && currentSecondId == item.id,
+              }"
               :key="item.id"
-              :repeat="1"
-              v-if="
-                fixTitle(item.name).length > spanlength &&
-                currentSecondary.id == item.id
-              "
+              v-if="item.children.length"
             >
-              {{ fixTitle(item.name) }}
-            </marquee-text>
-            <span v-else>
-              {{ fixTitle(item.name) }}
-            </span>
-          </li>
+              <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentSecondary.id == item.id">
+                {{ fixTitle(item.name) }}
+              </marquee-text>
+              <span v-else>
+                {{ fixTitle(item.name) }}
+              </span>
+            </li>
+          </template>
         </ul>
       </div>
     </div>
 
     <div
-      class="swiper-container"
+      class="swiper-container root-group-list"
+      ref="root-group"
       id="swcatalogRoot"
       :style="`width:${Math.min(catalogRootW, innerW)}px;
     padding:${catalogRootW > innerW ? '0 15px' : '0'}`"
-      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1"
+      v-if="metadata?.navigationTrees?.length > 1"
     >
-      <ul class="swiper-wrapper" v-show="metadata.catalogRoot.length > 1">
-        <li
-          class="swiper-slide"
-          :class="{
-            active: currentCatalogRoot.id == item.id,
-            loopspan:
-              fixTitle(item.name).length > spanlength &&
-              currentCatalogRoot.id == item.id,
-          }"
-          @click="tabRoot(item)"
-          v-for="(item, i) in metadata.catalogRoot"
-          :key="i"
-        >
-          <marquee-text
-            :duration="Math.ceil(fixTitle(item.name).length / 10) * 5"
-            :key="item.id"
-            :repeat="1"
-            v-if="
-              fixTitle(item.name).length > spanlength &&
-              currentCatalogRoot.id == item.id
-            "
+      <ul class="swiper-wrapper">
+        <template v-for="(item, i) in metadata?.navigationTrees" :key="item.id">
+          <li
+            v-if="item.children.length"
+            class="swiper-slide"
+            :class="{
+              active: currentRootId == item.id,
+              loopspan: fixTitle(item.name).length > spanlength && currentRootId == item.id,
+            }"
+            @click="tabRoot(item, i)"
           >
-            {{ fixTitle(item.name) }}
-          </marquee-text>
-          <span v-else>
-            {{ fixTitle(item.name) }}
-          </span>
-        </li>
+            <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentRootId == item.id">
+              {{ fixTitle(item.name) }}
+            </marquee-text>
+            <span v-else>
+              {{ fixTitle(item.name) }}
+            </span>
+          </li>
+        </template>
       </ul>
     </div>
   </div>
 </template>
 
 <script setup>
-import {
-  ref,
-  unref,
-  watch,
-  watchEffect,
-  computed,
-  onMounted,
-  nextTick,
-} from "vue";
+import { ref, watch, computed, onMounted, nextTick, unref, watchEffect } from "vue";
 import { useStore } from "vuex";
 import { useApp } from "@/app";
+import MarqueeText from "vue-marquee-text-component";
 import { useI18n, getLocale } from "@/i18n";
 const { t } = useI18n({ useScope: "global" });
-import MarqueeText from "vue-marquee-text-component";
 
 const store = useStore();
 
@@ -162,66 +113,88 @@ const spanlength = ref(5);
 const metadata = computed(() => store.getters["scene/metadata"]);
 const scenes = computed(() => store.getters["scene/list"]);
 const currentScene = computed(() => store.getters["scene/currentScene"]);
+const currentSecondId = computed(() => store.getters["scene/currentSecondId"]);
+const currentRootId = computed(() => store.getters["scene/currentRootId"]);
 
-const currentCatalogRoot = computed(
-  () => store.getters["scene/currentCatalogRoot"]
-);
-const currentSecondary = computed(
-  () => store.getters["scene/currentSecondary"]
-);
+const clamp = computed(() => (num, min, max) => Math.min(Math.max(num, min), max));
+
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
+const currentSecondary = computed(() => store.getters["scene/currentSecondary"]);
 
 const secondaryList = computed(() => store.getters["scene/secondaryList"]);
 
-const isShowScenesList = computed(
-  () => store.getters["functions/isShowScenesList"]
-);
+const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
 
-const currentScenesList = computed(
-  () => store.getters["scene/currentScenesList"]
-);
+const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
 
+const show = ref(false);
 const swidth = ref({
   swcatalogRoot: 104,
   swSecondary: 84,
   swScenes: 72,
 });
 
-const innerW = computed(() => window.innerWidth);
-
-const scenesListW = computed(
-  () => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10
-);
-const secondaryW = computed(
-  () => secondaryList.value.length * (swidth.value["swSecondary"] + 10) - 10
-);
-const catalogRootW = computed(
-  () =>
-    metadata.value.catalogRoot.length * (swidth.value["swcatalogRoot"] + 10) -
-    10
-);
+const SceneSwiper = ref(null);
+const SecondGroupSwiper = ref(null);
+const rootGroupSwiper = ref(null);
 
-const show = ref(false);
+const rootTabIndex = computed(() => {
+  let idx = 0;
+  if (metadata.value && metadata.value?.navigationTrees) {
+    idx = metadata.value?.navigationTrees.findIndex((item) => item.id == currentRootId.value);
+  }
+  return idx;
+});
 
-const tabCurrentScene = (data) => {
-  console.log("tabCurrentScene", data.id, currentScene.value.id);
-  if (data.id !== currentScene.value.id) {
-    store.commit("scene/setCurrentScene", data);
-    setTimeout(() => {
-      scenesSwiperFocus();
-    }, 300);
-  } else {
-    console.log("重复点击当前导航");
-    // window.alert("alert-test-->重复点击当前导航");
+const secondTabIndex = computed(() => {
+  let idx = 0;
+  if (metadata.value && metadata.value?.navigationTrees) {
+    idx = metadata.value.navigationTrees[rootTabIndex.value]?.children.findIndex((item) => item.id == currentSecondId.value);
   }
-};
+  return idx;
+});
 
-const tabSecondary = (data) => {
-  store.commit("scene/setCurrentSecondary", data);
-};
+const showSecondTab = computed(() => {
+  return metadata.value && metadata.value?.navigationTrees ? metadata.value?.navigationTrees[rootTabIndex.value].children.some((item) => item.id != currentSecondId.value) : false;
+});
 
-const tabRoot = (data) => {
-  store.commit("scene/setCurrentCatalogRoot", data);
-};
+const scenesListW = computed(() => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10);
+const secondaryW = computed(() => {
+  let list = metadata.value?.navigationTrees[rootTabIndex.value].children.filter((item) => item.children.length);
+  return list.length * (swidth.value["swSecondary"] + 10) - 10;
+});
+
+const catalogRootW = computed(() => {
+  let list = metadata.value.navigationTrees.filter((item) => item.children.length);
+  return list.length * (swidth.value["swcatalogRoot"] + 10);
+});
+
+const innerW = computed(() => window.innerWidth);
+
+watch(
+  () => currentScenesList.value,
+  () => {
+    nextTick(() => {
+      initSceneSwiper();
+      initRootGroupSwiper();
+
+      // if (metadata.value.navigationTrees[rootTabIndex.value]?.children[secondTabIndex.value]?.type == "group") {
+      //   initSecondGroupSwiper();
+      // }
+    });
+  }
+);
+watch(
+  () => rootTabIndex.value,
+  (val) => {
+    console.error(val);
+    nextTick(() => {
+      if (metadata.value.navigationTrees[rootTabIndex.value]?.children[secondTabIndex.value]?.type == "group") {
+        initSecondGroupSwiper();
+      }
+    });
+  }
+);
 
 const fixTitle = (name) => {
   if (name == "默认二级分组") {
@@ -233,10 +206,10 @@ const fixTitle = (name) => {
   }
   return name;
 };
-
 const swiperOptions = {
   slidesPerView: "auto",
   centeredSlides: true,
+  slideToClickedSlide: true,
   spaceBetween: 10,
   centerInsufficientSlides: true,
   centeredSlidesBounds: true,
@@ -245,66 +218,128 @@ const swiperOptions = {
     sticky: false,
   },
 };
+const tabRoot = (item, index) => {
+  store.commit("scene/setData", { currentRootId: item.id, currentSecondId: null });
+  setTimeout(() => {
+    changeSceneList();
+  }, 0);
+
+  // setTimeout(() => {
+  //   rootGroupSwiper.value.slideTo(index);
+  //   SecondGroupSwiper.value.slideTo(rootTabIndex.value);
+  // }, 0);
+};
+const tabSecond = (item, index) => {
+  store.commit("scene/setData", { currentSecondId: item.id });
+  let sceneList = metadata.value.navigationTrees[rootTabIndex.value].children[secondTabIndex.value].children;
+  store.commit("scene/setCurrentScenesList", sceneList);
+
+  //
+
+  // setTimeout(() => {
+  //   SecondGroupSwiper.value.slideTo(index);
+  // }, 0);
+
+  // changeSceneList();
+};
+const changeSceneList = () => {
+  let currentList = null;
+
+  if (metadata.value.navigationTrees[rootTabIndex.value].children.length && metadata.value.navigationTrees[rootTabIndex.value].children[0].type == "group") {
+    store.commit("scene/setData", { currentSecondId: metadata.value.navigationTrees[rootTabIndex.value].children[0].id });
+    //如果有当前视图则选择二级目录
+    metadata.value.navigationTrees[rootTabIndex.value].children.forEach((item, index) => {
+      if (item.children.length) {
+        item.children.forEach((t_item, t_index) => {
+          if (t_item.id == currentScene.value.id || (t_item.sid && currentScene.value.sid && t_item.sid == currentScene.value.sid)) {
+            store.commit("scene/setData", { currentSecondId: item.id });
+          }
+        });
+      }
+    });
 
-// const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+    currentList = metadata.value.navigationTrees[rootTabIndex.value].children[secondTabIndex.value].children;
+    store.commit("scene/setCurrentScenesList", currentList);
+  }
 
-const initMainSwiper = () => {
+  if (secondTabIndex.value == -1) {
+    console.error("没有二级目录");
+    let rootList = metadata.value.navigationTrees.find((item) => item.id == currentRootId.value);
+    store.commit("scene/setCurrentScenesList", rootList.children);
+  }
   nextTick(() => {
-    if (window.mainNatSwiper) {
-      window.mainNatSwiper.update();
-      window.sencordNatSwiper.slideReset();
+    initSceneSwiper();
+    initRootGroupSwiper();
+    if (metadata.value.navigationTrees[currentRootId.value]?.children[currentSecondId.value]?.type == "group") {
+      initSecondGroupSwiper();
     }
-    window.mainNatSwiper = new Swiper("#swcatalogRoot", swiperOptions);
   });
 };
-const initsencordNatSwiper = () => {
+const initSceneSwiper = () => {
+  if (SceneSwiper.value) {
+    SceneSwiper.value.destroy();
+    SceneSwiper.value = null;
+  }
+  if (!currentScenesList.value.length) {
+    return;
+  }
+
   nextTick(() => {
-    if (window.sencordNatSwiper) {
-      window.sencordNatSwiper.update();
-      window.sencordNatSwiper.slideReset();
+    SceneSwiper.value = new Swiper(".scene-list", swiperOptions);
+    let index = currentScenesList.value.findIndex((item) => item.id == currentScene.value.id);
+    if (index >= 0) {
+      SceneSwiper.value.slideTo(index);
     }
-    window.sencordNatSwiper = new Swiper("#swSecondary", swiperOptions);
   });
 };
-const initScenesSwiper = () => {
-  // console.warn("initScenesSwiper");
+const tabCurrentScene = (data, index) => {
+  store.commit("scene/setCurrentScene", data);
+
+  // SceneSwiper.value.slideTo(index);
+};
+const initSecondGroupSwiper = () => {
+  console.error("initSecondGroupSwiper");
+  if (SecondGroupSwiper.value) {
+    SecondGroupSwiper.value.destroy();
+    SecondGroupSwiper.value = null;
+  }
+
   nextTick(() => {
-    if (window.scenesNatSwiper) {
-      window.scenesNatSwiper.slides.length > 0 &&
-        window.scenesNatSwiper.update();
-      window.scenesNatSwiper.slideReset();
-      // window.scenesNatSwiper = null;
-    } else {
-      window.scenesNatSwiper = new Swiper("#swScenes", {
-        ...swiperOptions,
-      });
+    SecondGroupSwiper.value = new Swiper(".second-group-list", swiperOptions);
+    if (secondTabIndex.value >= 0) {
+      SecondGroupSwiper.value.slideTo(secondTabIndex.value);
     }
-    scenesSwiperFocus();
   });
 };
 
-const scenesSwiperFocus = () => {
-  const sceneIndex = Array.from(currentScenesList.value).findIndex(
-    (item) => item.id === currentScene.value.id
-  );
-  if (window.scenesNatSwiper && window.scenesNatSwiper.slides.length > 0) {
-    const index = sceneIndex < 0 ? 0 : sceneIndex;
-    const fIndex = index < 5 ? 0 : index;
-    console.warn("scenesSwiperFocus", fIndex);
-    window.scenesNatSwiper.slideTo(fIndex);
-  }
+const tabCurrentSecondGroup = (data, index) => {
+  // store.commit("scene/setCurrentScene", data);
+
+  SecondGroupSwiper.value.slideTo(index);
 };
 
-const sencordNatSwiperFocus = () => {
-  nextTick(() => {
-    const current = Array.from(secondaryList.value).findIndex(
-      (item) => item.id === currentSecondary.value.id
-    );
-    if (window.sencordNatSwiper && window.sencordNatSwiper.slides.length > 0) {
-      console.warn("current-sencordNatSwiper-index", current);
-      window.sencordNatSwiper.slideTo(current);
+const initRootGroupSwiper = () => {
+  // if (rootGroupSwiper.value) {
+  //   rootGroupSwiper.value.destroy();
+  //   rootGroupSwiper.value = null;
+  // }
+  // nextTick(() => {
+  //   rootGroupSwiper.value = new Swiper(".root-group-list", swiperOptions);
+  // });
+
+  if (!rootGroupSwiper.value) {
+    rootGroupSwiper.value = new Swiper(".root-group-list", swiperOptions);
+    if (rootGroupSwiper.value >= 0) {
+      rootGroupSwiper.value.slideTo(rootTabIndex.value);
     }
-  });
+    initSecondGroupSwiper();
+  }
+};
+
+const tabCurrentRootGroup = (data, index) => {
+  // store.commit("scene/setCurrentScene", data);
+
+  SecondGroupSwiper.value.slideTo(index);
 };
 
 onMounted(() => {
@@ -313,31 +348,7 @@ onMounted(() => {
       store.commit("functions/setShowScenesList", false);
     }
     show.value = true;
-  });
-  watchEffect(() => {
-    if (
-      metadata.value.catalogRoot &&
-      unref(metadata.value.catalogRoot).length > 0
-    ) {
-      initMainSwiper();
-    }
-  });
-
-  watch(currentSecondary, () => {
-    if (unref(secondaryList).length > 1) {
-      initsencordNatSwiper();
-      sencordNatSwiperFocus();
-    } else {
-      if (window.sencordNatSwiper) {
-        console.warn("destroy-sencordNatSwiper");
-        window.sencordNatSwiper.update();
-        window.sencordNatSwiper.slideReset();
-      }
-    }
-  });
-
-  watch(currentScenesList, () => {
-    initScenesSwiper();
+    console.error("app", show.value);
   });
 });
 </script>

+ 440 - 0
packages/qjkankan-view/src/components/UIGather/new-list.vue

@@ -0,0 +1,440 @@
+<template>
+  <div class="bar-list" v-if="info" :class="{ disable: isEditing }">
+    <div class="top-con" v-show="currentScenesList.length">
+      <div v-if="currentScenesList.length" class="scene-list swiper-container" ref="scene-swiper" :style="`width:${scenesListW > 1150 ? '100%' : scenesListW + 'px'}`">
+        <!-- <div class="scene-list swiper-container" ref="scene-swiper"> -->
+        <div class="swiper-wrapper scene-wrapper">
+          <!-- <div
+            class="swiper-slide scene-slide"
+            :class="{
+              active: currentScene.id == item.id ||currentScene.id == item.sid  ,
+              disabled: isLockV4Scene,
+              // loopspan:
+              //   item.sceneTitle.length > spanlength &&
+              //   currentScene.id == item.id,
+            }"
+            v-for="(item, index) in currentScenesList"
+          > -->
+          <div
+            class="swiper-slide scene-slide"
+            :class="{
+              active: currentScene.id == item.id || (currentScene.sid && currentScene.sid == item.sid),
+              disabled: isLockV4Scene,
+              // loopspan:
+              //   item.sceneTitle.length > spanlength &&
+              //   currentScene.id == item.id,
+            }"
+            v-for="(item, index) in currentScenesList"
+          >
+            <div @click="tabCurrentScene(item, index)" class="scene-content" :style="`background-image:url(${item.icon});`">
+              <!-- <img :src="i.icon" alt="" /> -->
+
+              <i class="iconfont" :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
+              <div class="marquee">
+                <marquee-text :repeat="1" :duration="Math.ceil(item.name.length / 10) * 5" :key="item.id" v-if="item.name.length > spanlength && currentScene.id == item.id">
+                  {{ item.name }}
+                </marquee-text>
+                <span v-else>
+                  {{ item.name }}
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="second-group-box">
+        <div v-if="info.navigationTrees[rootTabIndex]?.children[0]?.type == 'group' && showSecondTab" class="second-group-list swiper-container" ref="second-group-swiper">
+          <div class="swiper-wrapper second-group-wrapper">
+            <div
+              class="swiper-slide second-group-slide"
+              @click="tabSecond(item)"
+              v-if="item.children.length"
+              v-for="(item, index) in info.navigationTrees[rootTabIndex].children"
+              :class="{ active: currentSecondId == item.id, disabled: isLockV4Scene, loopspan: fixTitle(item.name).length > spanlength && currentSecondId == item.id }"
+            >
+              <div class="second-group-content">
+                <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentSecondId == item.id">
+                  {{ fixTitle(item.name) }}
+                </marquee-text>
+                <span v-else>
+                  {{ fixTitle(item.name) }}
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="bottom-com">
+      <div v-if="info.navigationTrees.length > 1" :style="`width:${catalogRootW}px;`" class="root-group-list swiper-container" ref="root-group">
+        <div class="swiper-wrapper root-group-wrapper">
+          <div
+            class="swiper-slide root-group-slide"
+            :class="{
+              active: currentRootId == item.id,
+              disabled: isLockV4Scene,
+              loopspan: fixTitle(item.name).length > spanlength && currentRootId.id == item.id,
+            }"
+            v-for="(item, index) in info.navigationTrees"
+            @click="tabRoot(item)"
+          >
+            <div class="root-group-content">
+              <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentRootId == item.id">
+                {{ fixTitle(item.name) }}
+              </marquee-text>
+              <span v-else>
+                {{ fixTitle(item.name) }}
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapGetters, mapState } from "vuex";
+import MarqueeText from "vue-marquee-text-component";
+export default {
+  components: { MarqueeText },
+  data() {
+    return {
+      spanlength: 6,
+      swidth: {
+        swcatalogRoot: 104,
+        swSecondary: 84,
+        swScenes: 72,
+      },
+      SceneSwiper: null,
+      SecondGroupSwiper: null,
+      rootGroupSwiper: null,
+      swiperOptions: {
+        slidesPerView: "auto",
+        centeredSlides: true,
+        centerInsufficientSlides: true,
+        centeredSlidesBounds: true,
+        freeMode: {
+          enabled: true,
+          sticky: false,
+        },
+      },
+    };
+  },
+
+  watch: {
+    currentScenesList(val) {
+      this.$nextTick(() => {
+        this.initSceneSwiper();
+        this.initRootGroupSwiper();
+        if (this.info.navigationTrees[this.currentRootId]?.children[this.currentSecondId]?.type == "group") {
+          this.initSecondGroupSwiper();
+        }
+      });
+    },
+    "info.navigationTrees": {
+      // immediate: true,
+      handler: function (newVal, oldVal) {
+        if (newVal) {
+          this.$nextTick(() => {
+            // this.changeSceneList();
+          });
+        }
+      },
+      deep: true,
+    },
+  },
+
+  computed: {
+    ...mapState({
+      isLockV4Scene: "isLockV4Scene",
+    }),
+    ...mapGetters({
+      info: "base/baseInfo",
+      currentScene: "scene/currentScene",
+      currentScenesList: "scene/currentScenesList",
+      currentSecondId: "navigation/currentSecondId",
+      currentRootId: "navigation/currentRootId",
+      isEditing: "isEditing",
+    }),
+    scenesListW() {
+      return this?.currentScenesList?.length * (this.swidth["swScenes"] + 10) || 0;
+    },
+    secondaryW() {
+      return this.info.navigationTrees[0].length * (this.swidth["swSecondary"] + 10);
+    },
+    catalogRootW() {
+      // return this.info.navigationTrees.length * (this.swidth["swcatalogRoot"] + 10);
+
+      return this.info.navigationTrees.length * (this.swidth["swcatalogRoot"] + 10);
+    },
+    rootTabIndex() {
+      return this.info.navigationTrees.findIndex((item) => item.id == this.currentRootId);
+    },
+    secondTabIndex() {
+      return this.info.navigationTrees[this.rootTabIndex].children.findIndex((item) => item.id == this.currentSecondId);
+    },
+    showSecondTab() {
+      return this.info.navigationTrees[this.rootTabIndex].children.some((item) => item.id != this.currentSecondId);
+    },
+  },
+  methods: {
+    fixTitle(name) {
+      if (name == "默认二级分组") {
+        name = this.$i18n.t("navigation.default_group_two");
+      } else if (name == "一级分组") {
+        name = this.$i18n.t("navigation.group_one");
+      } else {
+        // eslint-disable-next-line no-self-assign
+        name = name;
+      }
+      return name;
+    },
+    tabRoot(item) {
+      this.$store.commit("navigation/setData", { currentRootId: item.id });
+      this.changeSceneList();
+    },
+    tabSecond(item) {
+      this.$store.commit("navigation/setData", { currentSecondId: item.id });
+      let sceneList = this.info.navigationTrees[this.rootTabIndex].children[this.secondTabIndex].children;
+      this.$store.commit("scene/setCurrentScenesList", sceneList);
+      // this.changeSceneList();
+    },
+    changeSceneList() {
+      let currentList = null;
+
+      if (this.info.navigationTrees[this.rootTabIndex].children.length && this.info.navigationTrees[this.rootTabIndex].children[0].type == "group") {
+        this.$store.commit("navigation/setData", { currentSecondId: this.info.navigationTrees[this.rootTabIndex].children[0].id });
+        currentList = this.info.navigationTrees[this.rootTabIndex].children[this.secondTabIndex].children;
+        this.$store.commit("scene/setCurrentScenesList", currentList);
+      }
+
+      if (this.secondTabIndex == -1) {
+        console.error("没有二级目录");
+        let rootList = this.info.navigationTrees.find((item) => item.id == this.currentRootId);
+        this.$store.commit("scene/setCurrentScenesList", rootList.children);
+      }
+      this.$nextTick(() => {
+        this.initSceneSwiper();
+        this.initRootGroupSwiper();
+        if (this.info.navigationTrees[this.currentRootId]?.children[this.currentSecondId]?.type == "group") {
+          this.initSecondGroupSwiper();
+        }
+      });
+    },
+    initSceneSwiper() {
+      if (this.SceneSwiper) {
+        this.SceneSwiper.destroy();
+        this.SceneSwiper = null;
+      }
+      if (!this.currentScenesList.length) {
+        return;
+      }
+      this.SceneSwiper = new Swiper(".scene-list", this.swiperOptions);
+    },
+
+    tabCurrentScene(data, index) {
+      this.$store.commit("scene/setCurrentScene", data);
+
+      this.SceneSwiper.slideTo(index);
+    },
+    initSecondGroupSwiper() {
+      if (this.SecondGroupSwiper) {
+        this.SecondGroupSwiper.destroy();
+        this.SecondGroupSwiper = null;
+      }
+      this.SecondGroupSwiper = new Swiper(".second-group-list", this.swiperOptions);
+    },
+
+    tabCurrentSecondGroup(data, index) {
+      // this.$store.commit("scene/setCurrentScene", data);
+
+      this.SecondGroupSwiper.slideTo(index);
+    },
+
+    initRootGroupSwiper() {
+      if (this.rootGroupSwiper) {
+        this.rootGroupSwiper.destroy();
+        this.rootGroupSwiper = null;
+      }
+      this.rootGroupSwiper = new Swiper(".root-group-list", this.swiperOptions);
+    },
+
+    tabCurrentRootGroup(data, index) {
+      // this.$store.commit("scene/setCurrentScene", data);
+
+      this.SecondGroupSwiper.slideTo(index);
+    },
+  },
+
+  mounted() {},
+};
+</script>
+
+<style lang="less" scoped>
+@width: 1150px;
+.swiper-slide {
+  cursor: pointer;
+  &.loopspan {
+    > span,
+    > div > span {
+      animation: 5s wordsLoop linear infinite normal;
+    }
+  }
+}
+
+.bar-list {
+  position: absolute;
+  bottom: 28px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  max-width: @width;
+  overflow: hidden;
+  transition: 0.3s all ease;
+  .top-con {
+    display: inline-block;
+    margin: 0 auto 10px;
+    padding: 10px 6px;
+    overflow: hidden;
+    background: linear-gradient(268deg, transparent, rgba(0, 0, 0, 0.4) 25%, rgba(0, 0, 0, 0.4) 75%, transparent);
+    .scene-list {
+      max-width: @width;
+      margin: 0 auto;
+      // display: inline-block;
+
+      .scene-wrapper {
+        .scene-slide {
+          margin: 0 5px;
+          // white-space: nowrap;
+          cursor: pointer;
+          width: 72px;
+          height: 72px;
+          border-radius: 6px;
+          border: 1px solid #fff;
+          background-size: cover;
+          position: relative;
+          overflow: hidden;
+          &.active {
+            border: 1px solid #0076f6;
+            .marquee {
+              color: #fff !important;
+            }
+          }
+          .scene-content {
+            width: 100%;
+            height: 100%;
+            background-size: cover;
+            position: relative;
+
+            img {
+              width: 100%;
+              height: 100%;
+              pointer-events: none;
+              object-fit: cover;
+            }
+            > .iconfont {
+              position: absolute;
+              left: 4px;
+              top: 4px;
+              z-index: 99;
+              text-shadow: 0px 0px 4px rgb(0, 0, 0, 0.3);
+            }
+            .marquee {
+              position: absolute;
+              bottom: 0;
+              left: 0;
+              height: 20px;
+              background: rgba(0, 0, 0, 0.5);
+              width: 100%;
+              overflow: hidden;
+              color: rgba(255, 255, 255, 0.6);
+              > span {
+                width: 100%;
+                line-height: 20px;
+                word-break: keep-all;
+                display: inline-block;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .second-group-box {
+      .second-group-list {
+        max-width: @width;
+        margin: 0 auto;
+        display: inline-block;
+
+        .second-group-wrapper {
+          .second-group-slide {
+            width: 84px;
+            box-sizing: border-box;
+            overflow: hidden;
+            padding-bottom: 6px;
+            cursor: pointer;
+            margin: 0 5px;
+            white-space: nowrap;
+            color: hsla(0, 0%, 100%, 0.6);
+            margin: 20px auto 10px;
+            &.active {
+              color: #fff;
+
+              &::before {
+                content: "";
+                display: inline-block;
+                position: absolute;
+                bottom: 0;
+                width: 20px;
+                height: 2px;
+                z-index: 9999;
+                left: 50%;
+                transform: translateX(-50%);
+                background: #0076f6;
+              }
+            }
+            .second-group-content {
+              // width: 100%;
+              // height: 100%;
+              // background-size: cover;
+              // position: relative;
+            }
+          }
+        }
+      }
+    }
+  }
+  .bottom-com {
+    .root-group-list {
+      max-width: @width;
+      margin: 0 auto;
+      .swiper-wrapper {
+      }
+      .root-group-wrapper {
+        .root-group-slide {
+          width: 104px;
+          background: rgba(0, 0, 0, 0.5);
+          border-radius: 4px;
+          padding: 4px 10px;
+          border: 1px solid hsla(0, 0%, 100%, 0.5);
+          box-sizing: border-box;
+          overflow: hidden;
+          margin: 0 5px;
+          white-space: nowrap;
+          color: hsla(0, 0%, 100%, 0.6);
+
+          &.active {
+            border: 1px solid #fff;
+            color: #fff;
+          }
+          .root-group-content {
+            width: 100%;
+            word-break: keep-all;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 3 - 7
packages/qjkankan-view/src/components/assembly/Password.vue

@@ -3,13 +3,7 @@
     <!-- {{ currentScene }} -->
     <img :src="currentScene.icon" alt="" />
 
-    <ui-window
-      @ok="onOk"
-      :okText="$t('common.confirm')"
-      :title="$t('common.tips')"
-      :showCloseIcon="false"
-      :showCancelButton="false"
-    >
+    <ui-window @ok="onOk" :okText="$t('common.confirm')" :title="$t('common.tips')" :showCloseIcon="false" :showCancelButton="false">
       <template v-slot:content>
         <div class="wrapper">
           <ui-input
@@ -64,9 +58,11 @@ const onPasswordChange = (e) => {
 };
 
 watch(passwordkey, () => {
+  console.error("passwordkey.value", passwordkey.value);
   if (passwordkey.value) {
     show.value = true;
   } else {
+    console.error(getApp());
     getApp().Scene.unlock();
   }
 });

+ 1 - 1
packages/qjkankan-view/src/components/assembly/titieSlide.vue

@@ -2,7 +2,7 @@
   <div v-if="currentScene.type == 'pano'">
     <transition name="fade">
       <div v-if="titleshow" class="titieSlide">
-        {{ currentScene.sceneTitle }}
+        {{ currentScene.name }}
       </div>
     </transition>
   </div>

+ 11 - 13
packages/qjkankan-view/src/hooks/useAudio.js

@@ -13,12 +13,10 @@ const currentAudio = computed(() => store.getters["audio/currentAudio"]);
 const currentAudioTemp = ref("");
 const isDoneforCover = computed(() => store.getters["scene/isDoneforCover"]);
 const currentScene = computed(() => store.getters["scene/currentScene"]);
+const metadata = computed(() => store.getters["scene/metadata"]);
 const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
 const isShowCover = computed(() =>
-  store.getters["scene/metadata"].coverInfo &&
-  "isShowCover" in store.getters["scene/metadata"].coverInfo
-    ? store.getters["scene/metadata"].coverInfo.isShowCover === 1
-    : false
+  store.getters["scene/metadata"].coverInfo && "isShowCover" in store.getters["scene/metadata"].coverInfo ? store.getters["scene/metadata"].coverInfo.isShowCover === 1 : false
 );
 
 async function initDefaultAudio() {
@@ -97,11 +95,7 @@ function initAudioPlayer() {
   if (!unref(isInit)) {
     isInit.value = true;
     console.log("initAudioPlayer");
-    const player = createAudioPlayer(
-      unref(currentAudio).url,
-      unref(currentAudio).isAuto,
-      unref(currentAudio).repeat
-    );
+    const player = createAudioPlayer(unref(currentAudio).url, unref(currentAudio).isAuto, unref(currentAudio).repeat);
     currentPlayer.value = player;
     player.on("play", () => {
       console.log("play--22", player.isPlaying);
@@ -120,11 +114,15 @@ function initAudioPlayer() {
 }
 
 function watchUpdateCurrentScenEexplanation(data) {
-  if ("explanation" in data) {
+  // console.error("watchUpdateCurrentScenEexplanation", metadata.value, data);
+  let currentExplanation = metadata.value.workExplanationList.find((item) => item.navigationId == data.id);
+  console.error("currentExplanation", currentExplanation);
+  // if ("explanation" in data) {
+  if (currentExplanation) {
     store.dispatch("audio/initExplanationBGM", {
-      url: data.explanation.audioUrl,
-      repeat: data.explanation.repeat,
-      isAuto: data.explanation.openByDefault,
+      url: currentExplanation.audioUrl,
+      repeat: currentExplanation.repeat,
+      isAuto: currentExplanation.openByDefault,
     });
   } else {
     console.log("not initExplanationBGM");

+ 9 - 11
packages/qjkankan-view/src/pages/show.js

@@ -4,22 +4,23 @@ import i18n, { getLocale, setI18nLanguage, loadLocaleMessages } from "../i18n";
 import Components from "@/global_components";
 import Show from "./show.vue";
 import { createApp } from "vue";
-import Checkbrowser from '@/components/assembly/Checkbrowser.vue'
+import Checkbrowser from "@/components/assembly/Checkbrowser.vue";
 import ClickOutSide from "../utils/fns/ClickOutSide";
 import ToolTip from "../utils/fns/ToolTip";
-import VueLazyLoad from 'vue3-lazyload'
+import VueLazyLoad from "vue3-lazyload";
 import browser from "../utils/browser";
 import Deferred from "@/utils/Deferred";
-import store from '../store'
+import store from "../store";
 import Toast from "vue-toastification";
 // Import the CSS or use your own!
 import "vue-toastification/dist/index.css";
 
+
 const options = {
-  transition: 'Vue-Toastification__bounce',
+  transition: "Vue-Toastification__bounce",
   maxToasts: 3,
   newestOnTop: true,
-  position: 'top-right',
+  position: "top-right",
   timeout: 2000,
   closeOnClick: true,
   pauseOnFocusLoss: true,
@@ -28,13 +29,11 @@ const options = {
   draggablePercent: 0.7,
   showCloseButtonOnHover: false,
   hideProgressBar: true,
-  closeButton: 'button',
+  closeButton: "button",
   icon: true,
-  rtl: false
+  rtl: false,
 };
 
-
-
 let App;
 if (
   browser.detectChrome() ||
@@ -51,12 +50,11 @@ if (
 ) {
   App = Show;
 } else {
-  App = Checkbrowser
+  App = Checkbrowser;
 }
 
 const local = getLocale();
 
-
 loadLocaleMessages(i18n, local).then(() => {
   setI18nLanguage(i18n, local);
 

+ 188 - 128
packages/qjkankan-view/src/pages/show.vue

@@ -16,6 +16,16 @@
 </template>
 
 <script setup>
+import { debounce } from "lodash-es";
+
+const _ = window.ResizeObserver;
+window.ResizeObserver = class ResizeObserver extends _ {
+  constructor(callback) {
+    callback = debounce(callback, 100);
+    super(callback);
+  }
+};
+
 import Pano from "@/components/Pano";
 import Tags from "@/components/assembly/Tags";
 import Password from "@/components/assembly/Password";
@@ -31,7 +41,7 @@ import { createApp } from "@/app";
 import { Dialog } from "@/global_components";
 
 import { ref, onMounted, computed, watch, nextTick, unref } from "vue";
-import { getPanoInfo, checkWork, exchangeId } from "@/apis";
+import { getPanoInfo, checkWork, exchangeId, getWorkInfo } from "@/apis";
 import { useStore } from "vuex";
 import config from "@/utils/config";
 import browser from "@/utils/browser";
@@ -48,39 +58,38 @@ const hasPasswordLock = ref(false);
 const lang = getLocale();
 
 const currentScene = computed(() => store.getters["scene/currentScene"]);
-const currentCatalogRoot = computed(
-  () => store.getters["scene/currentCatalogRoot"]
-);
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
 
 const isAutoRotate = computed(() => store.getters["functions/isAutoRotate"]);
 
 const earthMask = computed(() => store.getters["scene/earthMask"]);
 const skyMask = computed(() => store.getters["scene/skyMask"]);
+const hotspotTypeList = computed(() => store.getters["tags/hotspotTypeList"]);
+const hotspotIconTypeList = computed(() => store.getters["tags/hotspotIconTypeList"]);
+const dataType = computed(() => store.getters["tags/dataType"]);
+const hotspots = computed(() => store.getters["tags/hotspots"]);
+
 const isShowOpeningAnimation = ref(0);
 
 onMounted(async () => {
   if (browser.isMobile()) {
-    window.location.href = window.location.href.replace(
-      "show.html",
-      "showMobile.html"
-    );
+    window.location.href = window.location.href.replace("show.html", "showMobile.html");
     return;
   }
 
   const idRes = await exchangeId({
-    id: config.projectNum
-  })
-  const { id, num, calcStatus } = idRes.data
-  config.projectNum = id
+    id: config.projectNum,
+  });
+  const { id, num, calcStatus } = idRes.data;
+  config.projectNum = id;
 
   if (calcStatus === 0) {
-
     Dialog.alert({
       title: t("common.tips"),
-      content: t('common.calcing'),
+      content: t("common.calcing"),
       okText: t("common.confirm"),
     });
-    return
+    return;
   }
 
   let res = await checkWork();
@@ -89,98 +98,137 @@ onMounted(async () => {
     workEnable.value = res.data;
     return;
   }
-  getPanoInfo().then(async (data) => {
-    isShowOpeningAnimation.value = data.isShowOpeningAnimation
-      ? Number(data.isShowOpeningAnimation)
-      : 0;
+  // getWorkInfo().then((res) => {
+  //   console.error(res);
+  // });
+  // getPanoInfo().then(async (data) => {
+  getWorkInfo().then(async (res) => {
+    let data = res.data;
+    let { workOpeningAnimation, work, navigationTrees, workVisualAngleList, workCustomMaskList, workHotList } = data;
+
+    store.commit("tags/setData", { hotspots: workHotList });
+    isShowOpeningAnimation.value = workOpeningAnimation && workOpeningAnimation?.isShowOpeningAnimation ? Number(workOpeningAnimation?.isShowOpeningAnimation) : 0;
     //TODO 兼容1.2.0或以下数据
-    if (
-      !("isShowOpeningAnimation" in data) &&
-      "openingAnimationType" in data &&
-      data.openingAnimationType.length > 0
-    ) {
+    if (workOpeningAnimation && !("isShowOpeningAnimation" in workOpeningAnimation) && "openingAnimationType" in workOpeningAnimation && workOpeningAnimation.openingAnimationType.length > 0) {
       console.log("小行星没有开关,但有openingAnimationType强制开启");
       isShowOpeningAnimation.value = 1;
     }
-    store.commit("scene/setScenes", data.scenes);
-    store.commit(
-      "scene/setPassword",
-      data.password === "" ? false : data.password
-    );
-    if (data.password.length > 0) {
+    let scenes = [];
+    navigationTrees.forEach((item) => {
+      item.children.forEach((s_item) => {
+        if (s_item.type != "group") {
+          scenes.push(s_item);
+        }
+        s_item.children.forEach((t_item) => {
+          if (t_item.type != "group") {
+            scenes.push(t_item);
+          }
+        });
+      });
+    });
+    store.commit("scene/setScenes", scenes);
+    store.commit("scene/setPassword", work.password === "" ? false : work.password);
+    if (work.password.length > 0) {
       hasPasswordLock.value = true;
     }
     store.commit("scene/setMetaData", data);
     // document.title = data.name || t("common.no_title");
 
     let firstScene = "";
-
     if (config.sceneNum) {
-      firstScene = data.scenes.find(
-        (item) => item.sceneCode == config.sceneNum
-      );
+      firstScene = scenes.find((item) => item.sceneCode == config.sceneNum);
     } else if (data.firstScene) {
-      firstScene = data.scenes.find(
-        (item) => item.sceneCode == data.firstScene.sceneCode
-      );
+      firstScene = scenes.find((item) => item.sceneCode == data.firstScene.sceneCode);
     }
     // 所有audio入口
 
-    const currentSceneData = firstScene || data.scenes[0];
+    const currentSceneData = firstScene || scenes[0];
 
-    store.dispatch(
-      "audio/initNormalBGM",
-      data.backgroundMusic ? data.backgroundMusic.ossPath : ""
-    );
+    store.dispatch("audio/initNormalBGM", data.workBackgroundMusic ? data.workBackgroundMusic.ossPath : "");
     store.commit("scene/setCurrentScene", currentSceneData);
-
-    // 过滤空分组
-    let ttt = data.catalogRoot.filter((item) => {
-      let flag = "";
-
-      if (item.children) {
-        item.children.some((sub) => {
-          flag = data.scenes.some((son) => {
-            // console.log(String(son.category).toLowerCase(), String(sub).toLowerCase());
-            return (
-              String(son.category).toLowerCase() == String(sub).toLowerCase()
-            );
-          });
-          return flag;
-        });
-      }
-      return flag;
-    });
-
-    data.catalogRoot = ttt;
-
-    let catalog = data.catalogs.find(
-      (item) => item.id == currentScene.value.category
-    );
-
-    // 查询初始场景的所在1级分组
-    data.catalogRoot.forEach((item) => {
-      let temp =
-        item.children && item.children.find((sub) => sub == catalog.id);
-      if (temp) {
-        store.commit("scene/setCurrentCatalogRoot", item);
-        return;
+    console.error(currentSceneData);
+    let activeScene = null;
+    navigationTrees.forEach((item, index) => {
+      activeScene = item.children.find((pano) => pano.id == currentSceneData.id);
+
+      if (activeScene) {
+        store.commit("scene/setCurrentScenesList", item.children);
+        store.commit("scene/setData", { currentSecondId: null, currentRootId: item.id });
+        // throw new Error("LoopTerminated");
       }
+      item = item.children.forEach((s_item, s_index) => {
+        activeScene = s_item.children.find((pano) => pano.id == currentSceneData.id);
+
+        if (activeScene) {
+          store.commit("scene/setCurrentScenesList", s_item.children);
+          store.commit("scene/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+          // throw new Error("LoopTerminated");
+        }
+        s_item = s_item.children.forEach((t_item, t_index) => {
+          activeScene = t_item.children.find((pano) => pano.id == currentSceneData.id);
+
+          if (activeScene) {
+            store.commit("scene/setCurrentScenesList", s_item.children);
+            store.commit("scene/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+            // throw new Error("LoopTerminated");
+          }
+        });
+      });
     });
 
-    // 查询初始场景的所在2级分组
-    store.commit("scene/setCurrentSecondary", catalog);
-
-    store.commit("functions/setAutoRotate", !!data.isAuto);
+    // 过滤空分组
+    // let ttt = data.catalogRoot.filter((item) => {
+    //   let flag = "";
+
+    //   if (item.children) {
+    //     item.children.some((sub) => {
+    //       flag = data.scenes.some((son) => {
+    //         // console.log(String(son.category).toLowerCase(), String(sub).toLowerCase());
+    //         return String(son.category).toLowerCase() == String(sub).toLowerCase();
+    //       });
+    //       return flag;
+    //     });
+    //   }
+    //   return flag;
+    // });
+
+    // data.catalogRoot = ttt;
+
+    // let catalog = data.catalogs.find((item) => item.id == currentScene.value.category);
+
+    // // 查询初始场景的所在1级分组
+    // data.catalogRoot.forEach((item) => {
+    //   let temp = item.children && item.children.find((sub) => sub == catalog.id);
+    //   if (temp) {
+    //     store.commit("scene/setCurrentCatalogRoot", item);
+    //     return;
+    //   }
+    // });
+
+    // // 查询初始场景的所在2级分组
+    // store.commit("scene/setCurrentSecondary", catalog);
+
+    store.commit("functions/setAutoRotate", !!work.isAuto);
 
     show.value = true;
 
-    let isHavePano = data.scenes.some((item) => item.type == "pano");
-
+    let isHavePano = scenes.some((item) => item.type == "pano");
+
+    let currnetVisual = workVisualAngleList.find((item) => item.navigationId == currentScene.value.id);
+    // if (!currnetVisual) {
+    //   currnetVisual = {
+    //     hlookat: 0,
+    //     icon: currentScene.value.icon,
+    //     vlookat: 0,
+    //     vlookatmax: 90,
+    //     vlookatmin: -90,
+    //     navigationId: currentScene.value.id,
+    //   };
+    // }
+    // store.commit("scene/setCurrnetVisual", currnetVisual);
     const app = createApp({
       // xml: "%HTMLPATH%/static/template/tour.xml",
-      xml: `${process.env.VUE_APP_CDN}/720yun_fd_manage/${config.projectNum
-        }/tour.xml?rnd=${Math.random()}`,
+      xml: `${process.env.VUE_APP_CDN}/720yun_fd_manage/${config.projectNum}/tour.xml?rnd=${Math.random()}`,
       swf: "%HTMLPATH%/showviewer/lib/krpano/tour.swf",
       target: "pano",
       html5: "auto",
@@ -188,30 +236,18 @@ onMounted(async () => {
       isHavePano,
       vars: {
         startscene: "scene_" + currentScene.value.sceneCode,
-        "view.org_vlookat": currentScene.value.initVisual
-          ? currentScene.value.initVisual.vlookat
-          : 0,
-        "view.org_hlookat": currentScene.value.initVisual
-          ? currentScene.value.initVisual.hlookat
-          : 0,
-        "view.vlookat": currentScene.value.initVisual
-          ? currentScene.value.initVisual.vlookat
-          : 0,
-        "view.hlookat": currentScene.value.initVisual
-          ? currentScene.value.initVisual.hlookat
-          : 0,
-        "autorotate.enabled": !!data.isAuto,
-        "skin_settings.littleplanetintro":
-          typeof data.openingAnimationType === "number"
-            ? data.openingAnimationType
-            : 1,
+        "view.org_vlookat": currnetVisual ? currnetVisual.vlookat : 0,
+        "view.org_hlookat": currnetVisual ? currnetVisual.hlookat : 0,
+        "view.vlookat": currnetVisual ? currnetVisual.vlookat : 0,
+        "view.hlookat": currnetVisual ? currnetVisual.hlookat : 0,
+        "autorotate.enabled": !!work.isAuto,
+        "skin_settings.littleplanetintro": typeof workOpeningAnimation && workOpeningAnimation?.openingAnimationType === "number" ? workOpeningAnimation?.openingAnimationType : 1,
         "skin_settings.lptswitch": unref(isShowOpeningAnimation),
       },
       passQueryParameters: true,
     });
-
     if (app) {
-      coverInfo.value = data.coverInfo || {};
+      coverInfo.value = data.workCoverType || {};
       app.Scene.lock();
       //如果不需要开场封面就直接渲染
       if (!coverInfo.value?.isShowCover) {
@@ -219,24 +255,51 @@ onMounted(async () => {
       }
 
       if (isHavePano) {
+        console.error("isHavePano", hotspots.value);
         app.Scene.on("sceneReady", () => {
           if (app.krpanoDom) {
-            const { sky, earth } = currentScene.value.customMask;
+            let customMask = workCustomMaskList.find((item) => item.navigationId == currentScene.value.id);
+            // const { sky, earth } = currentScene.value.customMask;
+            const { sky, earth } = customMask.data;
             handleMasksUpdate(sky, earth, app);
-            app.krpanoDom.set(
-              `layer[webvr_exitbutton].html`,
-              t("common.exit_vr")
-            );
+            app.krpanoDom.set(`layer[webvr_exitbutton].html`, t("common.exit_vr"));
           }
-          let hotspots = [];
-          if (currentScene.value.someData) {
-            hotspots =
-              typeof currentScene.value.someData == "string"
-                ? JSON.parse(currentScene.value.someData).hotspots
-                : currentScene.value.someData.hotspots;
+          // let hotspots = [];
+          // if (currentScene.value.someData) {
+          //   hotspots = typeof currentScene.value.someData == "string" ? JSON.parse(currentScene.value.someData).hotspots : currentScene.value.someData.hotspots;
+          // }
+          console.error("sceneReady", hotspots.value);
+          let currentHotspots = hotspots.value.filter((item) => item.navigationId == currentScene.value.id);
+
+          if (currentHotspots.length) {
+            // hotspots.forEach((item, index) => {
+            //   if (item.content) {
+            //     let hotSpotType = item.hotspotType;
+            //     for (let key in hotspotTypeList.value) {
+            //       let data = hotspotTypeList.value[key];
+            //       if (key == hotSpotType && item.content[data]) {
+            //         item[data] = item.content[data];
+            //       } else if (data) {
+            //         item[data] = dataType.value[data];
+            //       }
+            //     }
+
+            //     let hotSpotIconType = item.hotspotIconType;
+            //     for (let key in hotspotIconTypeList.value) {
+            //       let data = hotspotIconTypeList.value[key];
+            //       if (key == hotSpotIconType && item.content[data]) {
+            //         item[data] = item.content[data];
+            //       } else if (data) {
+            //         item[data] = dataType.value[data];
+            //       }
+            //     }
+            //   }
+            // });
+
+            app.Tags.initHotspot(currentHotspots, false);
           }
-          app.Tags.initHotspot(hotspots, false);
-          handleVisualLimit(app, currentScene.value);
+          // app.Tags.initHotspot(hotspots, false);
+          handleVisualLimit(app, currentScene.value, workVisualAngleList);
         });
       }
 
@@ -248,12 +311,15 @@ onMounted(async () => {
 const handlePass = () => {
   hasPasswordLock.value = false;
 };
-const handleVisualLimit = (app, currentScene) => {
-  const { vlookatmax, vlookatmin } = currentScene.initVisual;
+const handleVisualLimit = (app, currentScene, workVisualAngleList) => {
+  let currnetVisual = workVisualAngleList.find((item) => item.navigationId == currentScene.id);
+  // const { vlookatmax, vlookatmin } = currentScene.initVisual;
   // console.log('initVisual',currentScene.initVisual)
+  console.error("视角", currnetVisual.vlookatmin, currnetVisual.vlookatmax);
+
   app.krpanoDom.set(`view.limitview`, "lookat");
-  app.krpanoDom.set(`view.vlookatmin`, vlookatmin);
-  app.krpanoDom.set(`view.vlookatmax`, vlookatmax);
+  app.krpanoDom.set(`view.vlookatmin`, currnetVisual.vlookatmin || -90);
+  app.krpanoDom.set(`view.vlookatmax`, currnetVisual.vlookatmax || 90);
 };
 
 const handleMasksUpdate = (skyMask, earthMask, app) => {
@@ -276,16 +342,13 @@ const handleMasksUpdate = (skyMask, earthMask, app) => {
     if ("antidistorted" in skyMask) {
       app.krpanoDom.set(`hotspot[peaklogo].distorted`, skyMask.antidistorted);
       if (!skyMask.antidistorted) {
-        app.krpanoDom.set(`hotspot[peaklogo].scale`, skyMask.scale * 0.50);
+        app.krpanoDom.set(`hotspot[peaklogo].scale`, skyMask.scale * 0.5);
       }
     }
   }
   if (earthMask) {
     if ("isShow" in earthMask) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].visible`,
-        Boolean(earthMask.isShow)
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].visible`, Boolean(earthMask.isShow));
     }
     if (earthMask.icon) {
       app.krpanoDom.set(`hotspot[nadirlogo].url`, earthMask.icon);
@@ -294,12 +357,9 @@ const handleMasksUpdate = (skyMask, earthMask, app) => {
       app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale);
     }
     if ("antidistorted" in earthMask) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].distorted`,
-        earthMask.antidistorted
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].distorted`, earthMask.antidistorted);
       if (!earthMask.antidistorted) {
-        app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale * 0.50);
+        app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale * 0.5);
       }
     }
   }

+ 148 - 143
packages/qjkankan-view/src/pages/showMobile.vue

@@ -1,10 +1,7 @@
 <template>
   <LoadingLogo v-if="workEnable" />
   <template v-if="workEnable">
-    <Opening
-      :coverData="coverInfo"
-      v-if="coverInfo.isShowCover && !hasPasswordLock"
-    />
+    <Opening :coverData="coverInfo" v-if="coverInfo.isShowCover && !hasPasswordLock" />
     <Password @pass="handlePass" />
     <Share />
     <div class="ui-view-layout" :class="{ show: show }">
@@ -30,16 +27,8 @@ import Opening from "@/components/assembly/OpeningMobile";
 import LoadingLogo from "@/components/assembly/Loading";
 import { createApp } from "@/app";
 
-import {
-  ref,
-  onMounted,
-  computed,
-  unref,
-  onBeforeMount,
-  watch,
-  nextTick,
-} from "vue";
-import { getPanoInfo, checkWork, exchangeId } from "@/apis";
+import { ref, onMounted, computed, unref, onBeforeMount, watch, nextTick } from "vue";
+import { getPanoInfo, checkWork, exchangeId, getWorkInfo } from "@/apis";
 import { useStore } from "vuex";
 import config from "@/utils/config";
 import browser from "@/utils/browser";
@@ -48,6 +37,17 @@ import { useWxShared } from "@/hooks/useWxshared";
 import { useAudio } from "@/hooks/useAudio";
 import { useI18n, getLocale } from "@/i18n";
 import { Dialog } from "@/global_components";
+
+import { debounce } from "lodash-es";
+
+const _ = window.ResizeObserver;
+window.ResizeObserver = class ResizeObserver extends _ {
+  constructor(callback) {
+    callback = debounce(callback, 100);
+    super(callback);
+  }
+};
+
 const { t } = useI18n({ useScope: "global" });
 
 const { initWxShare } = useWxShared();
@@ -61,44 +61,39 @@ const lang = getLocale();
 const hasPasswordLock = ref(false);
 
 const currentScene = computed(() => store.getters["scene/currentScene"]);
-const currentCatalogRoot = computed(
-  () => store.getters["scene/currentCatalogRoot"]
-);
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
 
 const isAutoRotate = computed(() => store.getters["functions/isAutoRotate"]);
-
+const hotspots = computed(() => store.getters["tags/hotspots"]);
 const earthMask = computed(() => store.getters["scene/earthMask"]);
 const skyMask = computed(() => store.getters["scene/skyMask"]);
 const isShowOpeningAnimation = ref(0);
 
 onBeforeMount(() => {
   const vlog = browser.getURLParam("vlog");
-  if (vlog) {
-    var vConsole = new window.VConsole();
-  }
+  // if (vlog) {
+  var vConsole = new window.VConsole();
+  // }
 });
 
 onMounted(async () => {
   if (!browser.isMobile()) {
-    window.location.href = window.location.href.replace(
-      "showMobile.html",
-      "show.html"
-    );
+    window.location.href = window.location.href.replace("showMobile.html", "show.html");
     return;
   }
   const idRes = await exchangeId({
-    id: config.projectNum
-  })
-  const { id, num, calcStatus } = idRes.data
-  config.projectNum = id
+    id: config.projectNum,
+  });
+  const { id, num, calcStatus } = idRes.data;
+  config.projectNum = id;
 
   if (calcStatus === 0) {
     Dialog.alert({
       title: t("common.tips"),
-      content: t('common.calcing'),
+      content: t("common.calcing"),
       okText: t("common.confirm"),
     });
-    return
+    return;
   }
 
   let res = await checkWork();
@@ -106,25 +101,36 @@ onMounted(async () => {
     workEnable.value = res.data;
     return;
   }
-  getPanoInfo().then(async (data) => {
-    isShowOpeningAnimation.value = data.isShowOpeningAnimation
-      ? Number(data.isShowOpeningAnimation)
-      : 0;
+  // getPanoInfo().then(async (data) => {
+  getWorkInfo().then(async (res) => {
+    let data = res.data;
+
+    let { workOpeningAnimation, work, navigationTrees, workVisualAngleList, workCustomMaskList, workHotList } = data;
+    store.commit("tags/setData", { hotspots: workHotList });
+    isShowOpeningAnimation.value = workOpeningAnimation.isShowOpeningAnimation ? Number(workOpeningAnimation.isShowOpeningAnimation) : 0;
     //TODO 兼容1.2.0或以下数据
-    if (
-      !("isShowOpeningAnimation" in data) &&
-      "openingAnimationType" in data &&
-      data.openingAnimationType.length > 0
-    ) {
+    if (!("isShowOpeningAnimation" in workOpeningAnimation) && "openingAnimationType" in workOpeningAnimation && workOpeningAnimation.openingAnimationType.length > 0) {
       console.log("小行星没有开关,但有openingAnimationType强制开启");
       isShowOpeningAnimation.value = 1;
     }
-    store.commit("scene/setScenes", data.scenes);
-    store.commit(
-      "scene/setPassword",
-      data.password === "" ? false : data.password
-    );
-    if (data.password.length > 0) {
+
+    let scenes = [];
+    navigationTrees.forEach((item) => {
+      item.children.forEach((s_item) => {
+        if (s_item.type != "group") {
+          scenes.push(s_item);
+        }
+        s_item.children.forEach((t_item) => {
+          if (t_item.type != "group") {
+            scenes.push(t_item);
+          }
+        });
+      });
+    });
+
+    store.commit("scene/setScenes", scenes);
+    store.commit("scene/setPassword", work.password === "" ? false : work.password);
+    if (work.password.length > 0) {
       hasPasswordLock.value = true;
     }
     store.commit("scene/setMetaData", data);
@@ -132,77 +138,90 @@ onMounted(async () => {
     // document.title = data.name || t("common.no_title");
 
     let firstScene = "";
-
     if (config.sceneNum) {
-      console.log(config.sceneNum);
-      firstScene = data.scenes.find(
-        (item) => item.sceneCode == config.sceneNum
-      );
+      firstScene = scenes.find((item) => item.sceneCode == config.sceneNum);
     } else if (data.firstScene) {
-      firstScene = data.scenes.find(
-        (item) => item.sceneCode == data.firstScene.sceneCode
-      );
+      firstScene = scenes.find((item) => item.sceneCode == data.firstScene.sceneCode);
     }
 
     // 所有audio入口
 
-    const currentSceneData = firstScene || data.scenes[0];
+    const currentSceneData = firstScene || scenes[0];
     // debugger;
-    store.dispatch(
-      "audio/initNormalBGM",
-      data.backgroundMusic ? data.backgroundMusic.ossPath : ""
-    );
+    store.dispatch("audio/initNormalBGM", data.workBackgroundMusic ? data.workBackgroundMusic.ossPath : "");
     store.commit("scene/setCurrentScene", currentSceneData);
-
-    // 过滤空分组
-    let ttt = data.catalogRoot.filter((item) => {
-      let flag = "";
-
-      if (item.children) {
-        item.children.some((sub) => {
-          flag = data.scenes.some((son) => {
-            // console.log(String(son.category).toLowerCase(), String(sub).toLowerCase());
-            return (
-              String(son.category).toLowerCase() == String(sub).toLowerCase()
-            );
-          });
-          return flag;
-        });
-      }
-      return flag;
-    });
-
-    data.catalogRoot = ttt;
-
-    let catalog = data.catalogs.find(
-      (item) => item.id == currentScene.value.category
-    );
-
-    // 查询初始场景的所在1级分组
-    data.catalogRoot.forEach((item) => {
-      let temp =
-        item.children && item.children.find((sub) => sub == catalog.id);
-      if (temp) {
-        store.commit("scene/setCurrentCatalogRoot", item);
-        return;
+    let activeScene = null;
+    navigationTrees.forEach((item, index) => {
+      activeScene = item.children.find((pano) => pano.id == currentSceneData.id);
+
+      if (activeScene) {
+        store.commit("scene/setCurrentScenesList", item.children);
+        store.commit("scene/setData", { currentSecondId: null, currentRootId: item.id });
+        // throw new Error("LoopTerminated");
       }
+      item = item.children.forEach((s_item, s_index) => {
+        activeScene = s_item.children.find((pano) => pano.id == currentSceneData.id);
+
+        if (activeScene) {
+          store.commit("scene/setCurrentScenesList", s_item.children);
+          store.commit("scene/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+          // throw new Error("LoopTerminated");
+        }
+        s_item = s_item.children.forEach((t_item, t_index) => {
+          activeScene = t_item.children.find((pano) => pano.id == currentSceneData.id);
+
+          if (activeScene) {
+            store.commit("scene/setCurrentScenesList", s_item.children);
+            store.commit("scene/setData", { currentSecondId: s_item.id, currentRootId: item.id });
+            // throw new Error("LoopTerminated");
+          }
+        });
+      });
     });
-
-    // 查询初始场景的所在2级分组
-    store.commit("scene/setCurrentSecondary", catalog);
-
-    store.commit("functions/setAutoRotate", !!data.isAuto);
+    // // 过滤空分组
+    // let ttt = data.catalogRoot.filter((item) => {
+    //   let flag = "";
+
+    //   if (item.children) {
+    //     item.children.some((sub) => {
+    //       flag = data.scenes.some((son) => {
+    //         // console.log(String(son.category).toLowerCase(), String(sub).toLowerCase());
+    //         return String(son.category).toLowerCase() == String(sub).toLowerCase();
+    //       });
+    //       return flag;
+    //     });
+    //   }
+    //   return flag;
+    // });
+
+    // data.catalogRoot = ttt;
+
+    // let catalog = data.catalogs.find((item) => item.id == currentScene.value.category);
+
+    // // 查询初始场景的所在1级分组
+    // data.catalogRoot.forEach((item) => {
+    //   let temp = item.children && item.children.find((sub) => sub == catalog.id);
+    //   if (temp) {
+    //     store.commit("scene/setCurrentCatalogRoot", item);
+    //     return;
+    //   }
+    // });
+
+    // // 查询初始场景的所在2级分组
+    // store.commit("scene/setCurrentSecondary", catalog);
+
+    store.commit("functions/setAutoRotate", !!work.isAuto);
 
     show.value = true;
 
-    let isHavePano = data.scenes.some((item) => item.type == "pano");
+    let isHavePano = scenes.some((item) => item.type == "pano");
+    let currnetVisual = workVisualAngleList.find((item) => item.navigationId == currentScene.value.id);
+
     document.body.setAttribute("is-mobile", true);
 
     const app = createApp({
       // xml: "%HTMLPATH%/static/template/tour.xml",
-      xml: `${process.env.VUE_APP_CDN}/720yun_fd_manage/${
-        config.projectNum
-      }/tour.xml?rnd=${Math.random()}`,
+      xml: `${process.env.VUE_APP_CDN}/720yun_fd_manage/${config.projectNum}/tour.xml?rnd=${Math.random()}`,
       swf: "%HTMLPATH%/showviewer/lib/krpano/tour.swf",
       target: "pano",
       html5: "auto",
@@ -210,23 +229,17 @@ onMounted(async () => {
       isHavePano,
       vars: {
         startscene: "scene_" + currentScene.value.sceneCode,
-        "view.org_vlookat": currentScene.value.initVisual
-          ? currentScene.value.initVisual.vlookat
-          : 0,
-        "view.org_hlookat": currentScene.value.initVisual
-          ? currentScene.value.initVisual.hlookat
-          : 0,
-        "autorotate.enabled": !!data.isAuto,
-        "skin_settings.littleplanetintro":
-          typeof data.openingAnimationType === "number"
-            ? data.openingAnimationType
-            : 1,
+        "view.org_vlookat": currnetVisual ? currnetVisual.vlookat : 0,
+        "view.org_hlookat": currnetVisual ? currnetVisual.hlookat : 0,
+        "autorotate.enabled": !!work.isAuto,
+        "skin_settings.littleplanetintro": typeof workOpeningAnimation.openingAnimationType === "number" ? workOpeningAnimation.openingAnimationType : 1,
         "skin_settings.lptswitch": unref(isShowOpeningAnimation),
       },
       passQueryParameters: true,
     });
+
     if (app) {
-      coverInfo.value = data.coverInfo || {};
+      coverInfo.value = data.workCoverType || {};
       app.Scene.lock();
       //如果不需要开场封面就直接渲染
       if (!coverInfo.value?.isShowCover) {
@@ -236,22 +249,25 @@ onMounted(async () => {
       if (isHavePano) {
         app.Scene.on("sceneReady", () => {
           if (app.krpanoDom) {
-            const { sky, earth } = currentScene.value.customMask;
+            // const { sky, earth } = currentScene.value.customMask;
+            let customMask = workCustomMaskList.find((item) => item.navigationId == currentScene.value.id);
+            const { sky, earth } = customMask.data;
             handleMasksUpdate(sky, earth, app);
-            app.krpanoDom.set(
-              `layer[webvr_exitbutton].html`,
-              t("common.exit_vr")
-            );
+            app.krpanoDom.set(`layer[webvr_exitbutton].html`, t("common.exit_vr"));
           }
+          console.error("sceneReady");
+
+          // let hotspots = [];
+          // if (currentScene.value.someData) {
+          //   hotspots = typeof currentScene.value.someData == "string" ? JSON.parse(currentScene.value.someData).hotspots : currentScene.value.someData.hotspots;
+          // }
 
-          let hotspots = [];
-          if (currentScene.value.someData) {
-            hotspots =
-              typeof currentScene.value.someData == "string"
-                ? JSON.parse(currentScene.value.someData).hotspots
-                : currentScene.value.someData.hotspots;
+          let currentHotspots = hotspots.value.filter((item) => item.navigationId == currentScene.value.id);
+          console.error("currentHotspots", currentHotspots);
+          if (currentHotspots.length) {
+            app.Tags.initHotspot(currentHotspots, false);
           }
-          app.Tags.initHotspot(hotspots, false);
+
           handleVisualLimit(app, currentScene.value);
         });
       }
@@ -267,11 +283,12 @@ const handlePass = () => {
   hasPasswordLock.value = false;
 };
 const handleVisualLimit = (app, currentScene) => {
-  const { vlookatmax, vlookatmin } = currentScene.initVisual;
+  // const { vlookatmax, vlookatmin } = currentScene.initVisual;
   // console.log('initVisual',currentScene.initVisual)
+  let currnetVisual = workVisualAngleList.forEach((item) => item.navigationId == currentScene.value.id);
   app.krpanoDom.set(`view.limitview`, "lookat");
-  app.krpanoDom.set(`view.vlookatmin`, vlookatmin);
-  app.krpanoDom.set(`view.vlookatmax`, vlookatmax);
+  app.krpanoDom.set(`view.vlookatmin`, currnetVisual.vlookatmin || -90);
+  app.krpanoDom.set(`view.vlookatmax`, currnetVisual.vlookatmax || 90);
 };
 const handleMasksUpdate = (skyMask, earthMask, app) => {
   const lang = getLocale();
@@ -282,10 +299,7 @@ const handleMasksUpdate = (skyMask, earthMask, app) => {
     }
 
     if (skyMask.icon) {
-      app.krpanoDom.set(
-        `hotspot[peaklogo].url`,
-        skyMask.icon + "?x-oss-process=image/resize,w_1000"
-      );
+      app.krpanoDom.set(`hotspot[peaklogo].url`, skyMask.icon + "?x-oss-process=image/resize,w_1000");
     } else {
       app.krpanoDom.set(`hotspot[peaklogo].url`, defaultMask);
     }
@@ -301,25 +315,16 @@ const handleMasksUpdate = (skyMask, earthMask, app) => {
   }
   if (earthMask) {
     if ("isShow" in earthMask) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].visible`,
-        Boolean(earthMask.isShow)
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].visible`, Boolean(earthMask.isShow));
     }
     if (earthMask.icon) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].url`,
-        earthMask.icon + "?x-oss-process=image/resize,w_1000"
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].url`, earthMask.icon + "?x-oss-process=image/resize,w_1000");
     }
     if ("scale" in earthMask) {
       app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale);
     }
     if ("antidistorted" in earthMask) {
-      app.krpanoDom.set(
-        `hotspot[nadirlogo].distorted`,
-        earthMask.antidistorted
-      );
+      app.krpanoDom.set(`hotspot[nadirlogo].distorted`, earthMask.antidistorted);
       if (!earthMask.antidistorted) {
         app.krpanoDom.set(`hotspot[nadirlogo].scale`, earthMask.scale * 1.5);
       }

+ 8 - 12
packages/qjkankan-view/src/sdk/QJKanKan/index.js

@@ -1,26 +1,22 @@
 import { setup as setupSDK } from "@/sdk/QJKanKan/modules";
 import Deferred from "@/sdk/utils/Deferred";
-import Emiter from '@/sdk/utils/Emiter'
+import Emiter from "@/sdk/utils/Emiter";
 
-export default class QJKanKan  extends Emiter {
-  static MITT = { Emiter }
+export default class QJKanKan extends Emiter {
+  static MITT = { Emiter };
   static Deferred = Deferred;
 
   constructor(options) {
-    super()
-    setupSDK(this)
+    super();
+    setupSDK(this);
     this.options = {
       ...options,
-    }
+    };
     this.krpanoDom = null;
   }
-  
 
   render() {
-    this.Scene.start()
+    console.error('render')
+    this.Scene.start();
   }
-
-  
-
-
 }

+ 5 - 5
packages/qjkankan-view/src/sdk/QJKanKan/modules/Scene.js

@@ -32,24 +32,21 @@ export default class Scene extends Emiter {
 
   async start() {
     window.onPanoReady = () => {
+      console.error("onPanoReady");
       if (!this.app.krpanoDom) {
         this.app.krpanoDom = document.getElementById("krpanoSWFObject");
       }
       this.emit("ready");
     };
-
     window.onPanoViewChanged = () => {
       this.emit("viewChanged");
     };
-
     window.onNewPano = () => {
       this.emit("onNewPano");
     };
-
     window.onExitVrStatus = () => {
       this.emit("onExitVr");
     };
-
     let settings = {
       "events[skin_events].onloadcomplete": "js(window.onPanoReady());",
       "events[skin_events].onviewchanged": "js(window.onPanoViewChanged());",
@@ -59,7 +56,7 @@ export default class Scene extends Emiter {
     if (this.locked) {
       await this.locked;
     }
-
+    console.error(settings);
     if (this.app.options.isHavePano) {
       // debugger;
       embedpano({
@@ -71,6 +68,9 @@ export default class Scene extends Emiter {
 
         onready: (pano) => {
           // console.log("pano", pano);
+          // if (!this.app.krpanoDom) {
+          //   this.app.krpanoDom = document.getElementById("krpanoSWFObject");
+          // }
           this.emit("ready");
         },
         onerror: function (error) {

+ 25 - 32
packages/qjkankan-view/src/sdk/QJKanKan/modules/Tags.js

@@ -1,5 +1,5 @@
 import Emiter from "@/sdk/utils/Emiter";
-import { convertJQHotspot } from './hotspot'
+import { convertJQHotspot } from "./hotspot";
 import browser from "@/utils/browser";
 export default class Tags extends Emiter {
   constructor(app) {
@@ -7,16 +7,14 @@ export default class Tags extends Emiter {
     this.app = app;
   }
 
-
   /**
- * 打开热点链接
- */
+   * 打开热点链接
+   */
 
   linkopen(sceneCode, id, hid) {
-    this.emit('clickHotspot', { sceneCode, id })
+    this.emit("clickHotspot", { sceneCode, id });
   }
 
-
   /**
    * 编码转换
    */
@@ -36,17 +34,17 @@ export default class Tags extends Emiter {
     let atv = this.app.krpanoDom.get("curscreen_atv");
 
     this.app.krpanoDom.call(`addImgTextHotSpot(
-        ${param.img.replace('static/', 'showviewer/').replace('.svg', '.png').replace('@2x', '')},
+        ${param.img.replace("static/", "showviewer/").replace(".svg", ".png").replace("@2x", "")},
         ${param.name},
         ${this.htmlEncode(param.hotspotTitle)},
-        ${param.ath != '' ? param.ath : ath},
-        ${param.atv != '' ? param.atv : atv},
-        ${param.secne ? param.secne.sceneCode : 'notjump'},
+        ${param.ath != "" ? param.ath : ath},
+        ${param.atv != "" ? param.atv : atv},
+        ${param.scene ? param.scene.sceneCode : "notjump"},
         ${type},
         ${param.link},
         ${true},
         ${param.visible},
-        ${(param.size * 50) || 1},
+        ${param.size * 50 || 1},
         ${Number(param.fontSize)})`);
   }
   addhotspot(param, type) {
@@ -62,9 +60,9 @@ export default class Tags extends Emiter {
       const hotspot = convertJQHotspot(param);
       // debugger;
       const hotspotStyle = Object.values(hotspot.style);
-      const hotspotString = hotspotStyle.join('|');
-     
-      let hotspotSize
+      const hotspotString = hotspotStyle.join("|");
+      console.error(param)
+      let hotspotSize;
       if (browser.isMobile()) {
         hotspotSize = (Number(hotspot.size) || 1) * 30;
         // console.log(`当前${hotspot.id}基础热点大小`, 30);
@@ -73,27 +71,27 @@ export default class Tags extends Emiter {
         // console.log(`当前${hotspot.id}基础热点大小`, 40);
       }
 
-      const ath = hotspot.ath != '' ? hotspot.ath : this.app.krpanoDom.get("curscreen_ath");
-      const atv = hotspot.atv != '' ? hotspot.atv : this.app.krpanoDom.get("curscreen_atv");
-      let icon = hotspot.icon.replace(/,/g, '|');
+      const ath = hotspot.ath != "" ? hotspot.ath : this.app.krpanoDom.get("curscreen_ath");
+      const atv = hotspot.atv != "" ? hotspot.atv : this.app.krpanoDom.get("curscreen_atv");
+      let icon = hotspot.icon.replace(/,/g, "|");
       const title = this.htmlEncode(hotspot.title);
       const callString = `addJQHotspot(
         ${hotspot.id},
         ${hotspot.type},
         ${title},
-        "${icon.replace('static/', 'showviewer/').replace('.svg', '.png').replace('@2x', '')}",
+        "${icon.replace("static/", "showviewer/").replace(".svg", ".png").replace("@2x", "")}",
         ${ath},
         ${atv},
         "${hotspot.link}",
         ${hotspotSize},
         ${hotspot.visible},
         "${hotspotString}",
-        "${param.secne ? param.secne.sceneCode : 'notjump'}",
+        "${param.scene ? param.scene.sceneCode : "notjump"}",
         )`;
-      // console.log('callString',callString); 
+      // console.log('callString',callString);
       this.app.krpanoDom.call(callString);
     } catch (error) {
-      console.error('error', error);
+      console.error("error", error);
     }
 
     // 设置热点图片默认的偏移值
@@ -105,23 +103,19 @@ export default class Tags extends Emiter {
     //   offset = '-100%'
     // }
     // krpano.set('layer[tooltip_' + param.name + '].y', `${offset}`)
-
   }
 
-
-
-
   /**
-  * 初始化
-  */
+   * 初始化
+   */
 
   initHotspot(hotspots, type) {
-    hotspots.forEach(item => {
-      this.addhotspot(item, type)
+    console.error("hotspots", hotspots);
+    hotspots.forEach((item) => {
+      this.addhotspot(item, type);
     });
   }
 
-
   /**
    * 显示隐藏热点
    */
@@ -130,8 +124,7 @@ export default class Tags extends Emiter {
     try {
       this.app.krpanoDom.call(`set_hotspot_visible(${toggle})`);
     } catch (e) {
-      e
+      e;
     }
   }
-
 }

+ 5 - 4
packages/qjkankan-view/src/sdk/QJKanKan/modules/hotspot.js

@@ -41,10 +41,12 @@ const convertBaseStyle = (dest, origin) => {
     dest.style.fontSize = origin.fontSize;
     dest.style.position = origin.titlePosition;
 
-    if (origin && origin.titleDisplayMode == "always") {
+    // if (origin && origin.titleDisplayMode == "always") {
+    if (origin && origin.titleDisplayMode == 1) {
       dest.visible = 0;
     }
-    if (origin && origin.titleDisplayMode == "never") {
+    // if (origin && origin.titleDisplayMode == "never") {
+    if (origin && origin.titleDisplayMode == 0) {
       dest.visible = 1;
     }
     if (origin && origin.titleDisplayMode == "hover") {
@@ -114,8 +116,7 @@ const coverSerialFrame = (origin) => {
   duplicate.ath = origin.ath;
   duplicate.atv = origin.atv;
   duplicate.type = 2;
-  duplicate.icon =
-    origin.serialFrameInfo.img + "?x-oss-process=image/resize,w_128,q_80" || "";
+  duplicate.icon = origin.serialFrameInfo.img + "?x-oss-process=image/resize,w_128,q_80" || "";
 
   duplicate.link = origin.link || "";
   duplicate.size = origin.size;

+ 14 - 14
packages/qjkankan-view/src/store/modules/scene.js

@@ -38,11 +38,14 @@ export default {
       secondaryList: {},
       //当前场景分组
       currentScenesList: {},
+
       //当时场景的版本
       fdkkCurrentVersion: "v4",
       // 存在开场动作并动作完成的flag
 
       isDoneforCover: false,
+      currentSecondId: null,
+      currentRootId: null,
     };
   },
   getters: {
@@ -50,6 +53,8 @@ export default {
     secondaryList: (state) => state.secondaryList,
     currentCatalogRoot: (state) => state.currentCatalogRoot,
     currentScenesList: (state) => state.currentScenesList,
+    currentSecondId: (state) => state.currentSecondId,
+    currentRootId: (state) => state.currentRootId,
     currentSecondary: (state) => state.currentSecondary,
     currentScene: (state) => state.currentScene,
     fdkkCurrentVersion: (state) => state.fdkkCurrentVersion,
@@ -98,6 +103,11 @@ export default {
     },
   },
   mutations: {
+    setData(state, payload) {
+      for (let key in payload) {
+        state[key] = payload[key];
+      }
+    },
     setScenes(state, payload) {
       state.list = payload;
     },
@@ -113,10 +123,7 @@ export default {
       if (payload.someData) {
         try {
           // someData旧数据有可能是字符串,要parse一下
-          payload.someData =
-            typeof payload.someData == "string"
-              ? JSON.parse(payload.someData)
-              : payload.someData;
+          payload.someData = typeof payload.someData == "string" ? JSON.parse(payload.someData) : payload.someData;
         } catch (error) {}
       }
       state.currentScene = payload;
@@ -167,16 +174,9 @@ export default {
     },
     setMetaData(state, payload) {
       state.metadata = payload;
-      document.title = payload.name;
-      document
-        .querySelector('meta[name="description"]')
-        .setAttribute(
-          "content",
-          payload.description.replace(/<\/?[^>]+(>|$)/g, "")
-        );
-      document
-        .querySelector('meta[name="cover"]')
-        .setAttribute("content", payload.icon);
+      document.title = payload.work.name;
+      document.querySelector('meta[name="description"]').setAttribute("content", payload.work.description.replace(/<\/?[^>]+(>|$)/g, ""));
+      document.querySelector('meta[name="cover"]').setAttribute("content", payload.work.icon);
     },
 
     setDoneforCover(state, payload) {

+ 112 - 3
packages/qjkankan-view/src/store/modules/tags.js

@@ -1,5 +1,4 @@
-
-import { useApp } from '@/app'
+import { useApp } from "@/app";
 
 export default {
   namespaced: true,
@@ -7,13 +6,123 @@ export default {
     return {
       // 当前热点
       currentTag: {},
+      hotspotTypeList: {
+        scene: "scene",
+        image: "image",
+        video: "video",
+        audio: "audio",
+        link: "hyperlink",
+        textarea: "textarea",
+        tag: null,
+        imageText: "imageTextInfo",
+        article: "articleInfo",
+        pdf: "pdfInfo",
+        phone: "phoneInfo",
+      },
+      hotspotIconTypeList: {
+        system_icon: null,
+        custom_image: "customIconInfo",
+        serial_frame: "serialFrameInfo",
+        personalized_tag: "personalizedTagInfo",
+      },
+      dataType: {
+        customIconInfo: {
+          // 热点图标类型为自定义图标时,图标的数据
+          img: "",
+        },
+        serialFrameInfo: {
+          // 热点图标类型为序列帧时,序列帧的数据
+          img: "",
+          frameNumber: 0, // 总帧数
+          duration: 0, // 总播放时长(秒)
+        },
+        personalizedTagInfo: {
+          // 热点图标类型为个性标签时,个性标签的数据
+          isShowLine: true,
+          lineDirection: "left-top",
+          fillColor: "rgba(0, 0, 0, 0.5)",
+          borderColor: "rgba(255, 255, 255, 0.8)",
+          textColor: "rgba(255, 255, 255, 1)",
+          textDirection: "left-right",
+          isTextWrap: false,
+          textNumPerLine: 10,
+        },
+
+        scene: null,
+        hyperlink: "",
+        textarea: "",
+        image: [],
+        audio: "",
+        video: "",
+        imageTextInfo: {
+          // 热点类型为图文时,图文内容
+          imageList: [],
+          text: "",
+          isApplyToAll: true,
+          audio: {},
+        },
+        phoneInfo: {
+          // 热点类型为电话时,对应数据
+          phone: "",
+        },
+        pdfInfo: {
+          // 热点类型为pdf时,对应数据
+          name: "",
+          url: "",
+        },
+        articleInfo: {
+          html: "",
+        },
+      },
+      hotspots: [],
     };
   },
   getters: {
     currentTag: (state) => state.currentTag,
-  
+    hotspotTypeList: (state) => state.hotspotTypeList,
+    hotspotIconTypeList: (state) => state.hotspotIconTypeList,
+    dataType: (state) => state.dataType,
+    hotspots: (state, getters, rootState, rootGetters) => {
+      console.error(rootState.scene?.matedata?.workHotList)
+      let hotspots = rootState.scene?.matedata?.workHotList || [];
+      if (hotspots.length && !state.hotspots.length) {
+        state.hotspots = hotspots;
+        console.error(state.hotspots);
+      }
+      if (state.hotspots.length) {
+        state.hotspots.forEach((item, index) => {
+          if (item.content) {
+            let hotSpotType = item.hotspotType;
+            for (let key in state.hotspotTypeList) {
+              let value = state.hotspotTypeList[key];
+              if (key == hotSpotType && item.content[value]) {
+                item[value] = item.content[value];
+              } else if (value) {
+                item[value] = state.dataType[value];
+              }
+            }
+
+            let hotSpotIconType = item.hotspotIconType;
+            for (let key in state.hotspotIconTypeList) {
+              let value = state.hotspotIconTypeList[key];
+              if (key == hotSpotIconType && item.content[value]) {
+                item[value] = item.content[value];
+              } else if (value) {
+                item[value] = state.dataType[value];
+              }
+            }
+          }
+        });
+      }
+      return state.hotspots;
+    },
   },
   mutations: {
+    setData(state, payload) {
+      for (let key in payload) {
+        state[key] = payload[key];
+      }
+    },
     setCurrentTag(state, payload) {
       state.currentTag = payload;
     },

+ 4 - 0
packages/qjkankan-view/vue.config.js

@@ -36,6 +36,10 @@ module.exports = defineConfig({
         target: process.env.VUE_APP_PROXY_URL_ROOT,
         changeOrigin: true,
       },
+      "/work": {
+        target: proxy_url,
+        changeOrigin: true,
+      },
     },
     client: {
       overlay: {