Browse Source

feat: 重构

jinx 11 months ago
parent
commit
68e81fb3a4
33 changed files with 36514 additions and 1246 deletions
  1. 2 1
      packages/qjkankan-editor/.env.testdev
  2. 33594 0
      packages/qjkankan-editor/package-lock.json
  3. 2 1
      packages/qjkankan-editor/package.json
  4. 265 84
      packages/qjkankan-editor/src/Store/modules/base.js
  5. 39 0
      packages/qjkankan-editor/src/Store/modules/explanation.js
  6. 2 2
      packages/qjkankan-editor/src/Store/modules/hotspot.js
  7. 6 0
      packages/qjkankan-editor/src/Store/modules/index.js
  8. 50 0
      packages/qjkankan-editor/src/Store/modules/mask.js
  9. 82 11
      packages/qjkankan-editor/src/Store/modules/navigation.js
  10. 31 32
      packages/qjkankan-editor/src/Store/modules/scene.js
  11. 24 17
      packages/qjkankan-editor/src/Store/modules/screen.js
  12. 59 6
      packages/qjkankan-editor/src/api/index.js
  13. 918 0
      packages/qjkankan-editor/src/components/dragTree/index.vue
  14. 165 152
      packages/qjkankan-editor/src/components/insertPositionTipInEditor.vue
  15. 24 93
      packages/qjkankan-editor/src/components/materialSelectorFromWork.vue
  16. 56 112
      packages/qjkankan-editor/src/components/sceneGroupInEditor.vue
  17. 11 48
      packages/qjkankan-editor/src/components/sceneInGroupInEditor.vue
  18. 150 76
      packages/qjkankan-editor/src/framework/EditorHead.vue
  19. 24 46
      packages/qjkankan-editor/src/framework/MenuPC.vue
  20. 3 0
      packages/qjkankan-editor/src/framework/play/pano/components/list.vue
  21. 428 0
      packages/qjkankan-editor/src/framework/play/pano/components/new-list.vue
  22. 76 99
      packages/qjkankan-editor/src/framework/play/pano/index.vue
  23. 5 0
      packages/qjkankan-editor/src/pages/edit.js
  24. 22 0
      packages/qjkankan-editor/src/utils/request.js
  25. 1 1
      packages/qjkankan-editor/src/views/base/coverBase.vue
  26. 2 2
      packages/qjkankan-editor/src/views/base/index.vue
  27. 49 52
      packages/qjkankan-editor/src/views/explanation/explanationSettings.vue
  28. 43 95
      packages/qjkankan-editor/src/views/mask/setting.vue
  29. 214 199
      packages/qjkankan-editor/src/views/material/works/list.vue
  30. 78 59
      packages/qjkankan-editor/src/views/navigation/groupSettings.vue
  31. 28 21
      packages/qjkankan-editor/src/views/navigation/initialSceneSettings.vue
  32. 60 36
      packages/qjkankan-editor/src/views/screen/Setting.vue
  33. 1 1
      packages/qjkankan-editor/vue.config.js

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

@@ -3,7 +3,8 @@ VUE_APP_MAIN_COLOR=''
 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='https://test.4dkankan.com/qjkankan/'
+VUE_APP_PROXY_URL='http://192.168.0.41: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"
   }
-}
+}

+ 265 - 84
packages/qjkankan-editor/src/Store/modules/base.js

@@ -7,108 +7,289 @@ export default {
   namespaced: true,
   state() {
     return {
+      sceneList: [],
       baseInfo: null,
     };
   },
   getters: {
-    baseInfo: (state) => state.baseInfo,
+    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);
+              }
+            });
+          }
+        });
+      }
+      return state.baseInfo;
+    },
   },
   mutations: {
+    ininDefaultData(state, payload) {
+      let forArr = ["workCustomMaskList", "workVisualAngleList", "workExplanationList"];
+      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) {
+                key_item.icon = item.icon;
+              }
+              obj[key_item.navigationId] = key_item;
+            });
+
+            if (obj[item.id]) {
+            } else {
+              switch (key) {
+                case "workCustomMaskList":
+                  console.error("workCustomMaskList");
+                  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: "",
+                        isDelete: 0,
+                        isShow: true,
+                        navigationId: item.id,
+                        scale: 1,
+                        type: "sky",
+                        workId: state.baseInfo.workId,
+                      },
+                    },
+                    navigationId: 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.id,
+                  });
+                  break;
+                case "workExplanationList":
+                  console.error("workExplanationList");
+                  state.baseInfo[key].push({
+                    fodderId: null,
+                    audioName: "",
+                    audioUrl: "",
+                    openByDefault: true,
+                    navigationId: 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["workExplanationList"]);
+    },
     initbaseInfo(state, payload) {
-      if (!state.baseInfo) {
-        state.baseInfo = {
-          workId: "",
+      let data = {
+        firstScene: null,
+        workVisualAngleList: [],
+        navigationTrees: [],
+        workCustomMaskList: [],
+        workId: "",
 
-          work: {
-            description: "",
-            icon: "",
-            id: "",
-            isAuto: 0,
-            name: "",
-            password: "",
-          },
-          workBackgroundMusic: {
-            fodderId: "",
-            id: null,
-            name: "",
-            ossPath: "",
-            workId: "",
-          },
-          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: "",
+        work: {
+          description: "",
+          icon: "",
+          id: "",
+          isAuto: 0,
+          name: "",
+          password: "",
+        },
+        workBackgroundMusic: {
+          fodderId: "",
+          id: null,
+          name: "",
+          ossPath: "",
+          workId: "",
+        },
+        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,
-            isRemind: 0,
-            pcIcon: "",
-            pcIconId: "",
-            remindTime: 0,
+            isShow: 0,
+            name: "",
+            openMethod: "",
+            type: "",
+            value: "",
             workId: "",
           },
-          workCustomButtonList: [
-            {
-              id: 0,
-              isShow: 0,
-              name: "",
-              openMethod: "",
-              type: "",
-              value: "",
-              workId: "",
-            },
-          ],
-        };
+        ],
+        workExplanationList: [],
+        workHotList: [],
+      };
+      if (!state.baseInfo) {
+        state.baseInfo = data;
+      }
+      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");
-      // state.baseInfo = payload;
-      for (let key in state.baseInfo) {
-        if (payload[key]) {
+      // 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];
+      }
+    },
   },
   actions: {
     save({ commit, state, rootState }) {

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

@@ -0,0 +1,39 @@
+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 {};
+  },
+  getters: {},
+  mutations: {
+    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(list);
+
+      explanationSave(
+        {
+          list,
+        },
+        () => {
+          vue.$msg.success(i18n.t("gather.save_done"));
+          $waiting.hide();
+        },
+        () => {}
+      );
+    },
+  },
+};

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

@@ -1,6 +1,6 @@
 import Vue from "vue";
 import { i18n } from "@/lang";
-import { workHot_save } from "@/api";
+import { workHotSave } from "@/api";
 import { $waiting } from "@/components/shared/loading";
 let vue = new Vue();
 export default {
@@ -24,7 +24,7 @@ export default {
   },
   actions: {
     save({ commit, state, rootState }, payload) {
-      workHot_save(
+      workHotSave(
         payload,
         () => {},
         () => {}

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

@@ -4,6 +4,9 @@ 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,
@@ -11,4 +14,7 @@ export default {
   base,
   screen,
   hotspot,
+  navigation,
+  mask,
+  explanation,
 };

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

@@ -0,0 +1,50 @@
+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 {};
+  },
+  getters: {},
+  mutations: {
+    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 = [];
+
+      workCustomMaskList.forEach((item) => {
+        //组装服务端需要的数据
+        for (let key in item.data) {
+          console.error(key)
+          list.push(item.data[key]);
+        }
+      });
+      console.error(list)
+      // return
+      maskSave(
+        {
+          list,
+        },
+        () => {
+          vue.$msg.success(i18n.t("gather.save_done"));
+          $waiting.hide();
+        },
+        () => {}
+      );
+    },
+  },
+};

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

@@ -1,21 +1,92 @@
+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 {
       catalogTopology: [],
-
-      currentCatalogRoot: {},
-      //当前二级分组
-      currentSecondary: {},
-      //二级分组
-      secondaryList: {},
-      //当前场景分组
-      currentScenesList: {},
+      currentSecondId: null,
+      currentRootId: null,
+      saveFristScene: false, //是否可以保存初始场景
+      // currentCatalogRoot: {},
+      // //当前二级分组
+      // currentSecondary: {},
+      // //二级分组
+      // secondaryList: {},
+      // //当前场景分组
+      // currentScenesList: {},
+      navigationTrees: [],
     };
   },
-  getters: {},
-  mutations: {},
+  getters: { currentRootId: (state) => state.currentRootId, currentSecondId: (state) => state.currentSecondId },
+  mutations: {
+    setData(state, payload) {
+      for (let key in payload) {
+        state[key] = payload[key];
+      }
+      console.error(payload);
+    },
+  },
   actions: {
-    
+    save({ commit, state, rootState }) {
+      console.error(rootState.base.baseInfo.navigationTrees);
+      rootState.base.baseInfo.navigationTrees.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);
+      // $waiting.hide();
+      // return;
+      navigationSave(
+        { list: rootState.base.baseInfo.navigationTrees },
+        (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 });
+            setTimeout(() => {
+              if (state.saveFristScene) {
+                let data = {};
+                if (rootState.base.baseInfo.firstScene) {
+                  let firstScene = rootState.base.sceneList.find((item) => item.sceneCode == rootState.base.baseInfo.firstScene.sceneCode);
+                  if (firstScene) {
+                    data = { navigationId: rootState.base.baseInfo.firstScene.id, operType: "add" };
+                  }
+                } else {
+                  data = { navigationId: null, operType: "delete" };
+                }
+                initialSet(data, (res) => {
+                  commit("setData", { saveFristScene: false });
+                });
+              }
+            }, 0);
+          }
+        },
+        (err) => {
+          $waiting.hide();
+        }
+      );
+    },
   },
 };

+ 31 - 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,6 +47,7 @@ export default {
       secondaryList: {},
       //当前场景分组
       currentScenesList: {},
+      saveApiList: [],
     };
   },
   getters: {
@@ -80,6 +82,13 @@ export default {
     setPassword(state, payload) {
       state.password = payload;
     },
+    //添加需要保存的API
+    setSaveApiList(state, payload) {
+      if (!state.saveApiList.some((i) => i == payload)) {
+        state.saveApiList.push(payload);
+      }
+      console.error("saveApiList", state.saveApiList);
+    },
 
     // 设置当前场景
     setCurrentScene(state, payload) {
@@ -106,19 +115,19 @@ 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);
+      //         }
+      //       }
+      //     });
+      //   });
+      console.error(payload);
+      this.commit("scene/setSecondaryList", payload.children);
     },
 
     // 设置当前二级分组列表
@@ -140,9 +149,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 +183,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,
           };
         }
       });

+ 24 - 17
packages/qjkankan-editor/src/Store/modules/screen.js

@@ -1,21 +1,15 @@
 import Vue from "vue";
 import { i18n } from "@/lang";
-import { visual_save } from "@/api";
+import { visualSave } from "@/api";
 import { $waiting } from "@/components/shared/loading";
+import { forEach } from "lodash";
 let vue = new Vue();
 export default {
   namespaced: true,
   state() {
-    return {
-      currentScene: {
-        // customMask: initCustomMask,
-        // initVisual: initVisual,
-      },
-    };
-  },
-  getters: {
-    currentScene: (state) => state.currentScene,
+    return {};
   },
+  getters: {},
   mutations: {
     setData(state, payload) {
       this.commit("base/initbaseInfo");
@@ -28,17 +22,30 @@ export default {
     },
   },
   actions: {
+    applyInitVisualToAll({ commit, state, rootState }, payload) {
+      rootState.base.baseInfo.workVisualAngleList.forEach((item, index) => {
+        item.vlookatmin = payload[0];
+        item.vlookatmax = payload[1];
+      });
+      console.error(rootState.base.baseInfo.workVisualAngleList);
+    },
     save({ commit, state, rootState }) {
-      visual_save(
-        state.baseInfo,
+      console.error(rootState.base.baseInfo.workVisualAngleList);
+      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(workVisualAngleList);
+      visualSave(
+        {
+          list: workVisualAngleList,
+        },
+
         () => {
           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");
         },
         () => {}
       );

+ 59 - 6
packages/qjkankan-editor/src/api/index.js

@@ -948,13 +948,23 @@ export function getCamWorksList(data, ok, no) {
 export function exchangeId(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);
-  // return http.getJson(`http://192.168.0.41:8002/qjkankan/work/view`, data, ok, no);
 }
 /**
  * 保存作品基本设置
@@ -962,23 +972,66 @@ export function getWorkInfo(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);
-  // return http.postJson(`http://192.168.0.41:8002/qjkankan/work/edit/base/save`, data, ok, no);
 }
 /**
  * 保存视角
  */
 
-export function visual_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);
-  // return http.postJson(`http://192.168.0.41:8002/qjkankan/work/edit/base/save`, data, ok, no);
 }
 /**
  * 保存视角
  */
 
-export function workHot_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);
-  // return http.postJson(`http://192.168.0.41:8002/qjkankan/work/edit/base/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);
+// }

+ 918 - 0
packages/qjkankan-editor/src/components/dragTree/index.vue

@@ -0,0 +1,918 @@
+<template>
+  <div>
+    <!-- <div v-if="isRenaming">
+      <input ref="rename" type="text" @blur="onInputNewNameComplete" v-model="inputData.name" />
+    </div> -->
+    <!-- <button @click="addNode(null)">Add Node</button> -->
+    <el-tree
+      :highlight-current="true"
+      ref="sceneTree"
+      :data="this.info.navigationTrees"
+      :expand-on-click-node="false"
+      node-key="id"
+      class="custom-tree"
+      :default-expand-all="false"
+      @node-drag-start="handleDragStart"
+      @node-drag-enter="handleDragEnter"
+      @node-drag-leave="handleDragLeave"
+      @node-drag-over="handleDragOver"
+      @node-drag-end="handleDragEnd"
+      @node-drop="handleDrop"
+      @node-click="handlerGroup"
+      @node-expand="handleNodeExpand"
+      @node-collapse="handleNodeCollapse"
+      draggable
+      :allow-drop="allowDrop"
+      :allow-drag="allowDrag"
+    >
+      <template slot-scope="{ node, data }">
+        <div class="tree-msg" :class="data.type == 'group' ? 'group-item' : 'image-item'">
+          <!-- <div class="line"></div> -->
+
+          <div class="tree-info">
+            <div v-if="data.type == 'group'" class="group-info">
+              <i v-if="data.type == 'group' && !expandedNodes.includes(data.id)" class="iconfont icon-editor_folder_off folder_collapsed"></i>
+              <!-- 拖拽图标 -->
+              <i ref="drag-group" class="drag-icon iconfont icon-editor_folder_off folder_collapsed"></i>
+              <i v-if="data.type == 'group' && expandedNodes.includes(data.id)" class="iconfont icon-editor_folder_on folder_expanded"></i>
+              <span v-if="reNameId != data.id"> {{ data.name }}</span>
+              <input
+                ref="reNameInput"
+                v-model.trim="data.name"
+                @blur="onInputNewNameComplete(data)"
+                v-if="reNameId == data.id"
+                maxlength="50"
+                :placeholder="$i18n.t('navigation.enter_name')"
+                class="group-title-input"
+              />
+              <div v-if="reNameId != data.id && deleteId == null" class="controls-btn">
+                <i v-if="data.type == 'group' && data.level == 0" class="iconfont icon-editor_list_add icon-add" @click.stop="addNode(data)" v-tooltip="$i18n.t('navigation.add_two_group')"> </i>
+                <i
+                  v-if="(data.type == 'group' && data.children.length && data.children[0].type != 'group') || (data.type == 'group' && !data.children.length)"
+                  class="iconfont icon-editor_list_image icon-image"
+                  @click.stop="onRequestForAddScene(data)"
+                  v-tooltip="$i18n.t('navigation.add_pano_or_scene')"
+                >
+                </i>
+                <i @click="onClickForRename(data)" class="iconfont icon-editor_list_edit icon-edit" v-tooltip="$i18n.t('navigation.rename')"> </i>
+                <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="$i18n.t('navigation.delete')" @click.stop="onDel(node, data)"> </i>
+              </div>
+            </div>
+            <div class="image-info" v-else>
+              <div class="cover-image">
+                <img ref="drag-image" :src="data.icon + ossImagePreviewUrlSuffix()" alt="" />
+              </div>
+
+              <div class="image-name">
+                <div class="name-top">
+                  <span v-if="reNameId != data.id">{{ data.name }}</span>
+                  <input
+                    ref="reNameInput"
+                    v-if="reNameId == data.id"
+                    v-model.trim="inputData.name"
+                    @blur="onInputNewNameComplete(data)"
+                    maxlength="50"
+                    :placeholder="$i18n.t('navigation.enter_name')"
+                    class="group-title-input"
+                  />
+                </div>
+                <span class="name-bottom">
+                  {{ data.type === "pano" ? $i18n.t("navigation.pano") : $i18n.t("navigation.scene") }}
+                </span>
+              </div>
+
+              <div v-if="reNameId != data.id && deleteId == null" class="controls-btn">
+                <i @click="onClickForRename(data)" class="iconfont icon-editor_list_edit icon-edit" v-tooltip="$i18n.t('navigation.rename')"> </i>
+                <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="$i18n.t('navigation.delete')" @click.stop="onDel(node, data)"> </i>
+              </div>
+            </div>
+          </div>
+          <div class="deletion-confirm-wrap">
+            <div class="deletion-confirm" :class="deleteId == data.id ? 'show' : 'hide'" v-clickoutside="onRequestForCancelDelete" @click.stop="onConfirmDelete(node, data)">
+              {{ $i18n.t("navigation.delete") }}
+            </div>
+          </div>
+          <!-- <div class="controls-btn">
+            <i v-if="data.type == 'group' && data.level == 0" class="iconfont icon-editor_list_add icon-add" @click.stop="addNode(data)" v-tooltip="$i18n.t('navigation.add_two_group')"> </i>
+            <i
+              v-if="(data.type == 'group' && data.children.length && data.children[0].type != 'group') || (data.type == 'group' && !data.children.length)"
+              class="iconfont icon-editor_list_image icon-image"
+              @click.stop="onRequestForAddScene(data)"
+              v-tooltip="$i18n.t('navigation.add_pano_or_scene')"
+            >
+            </i>
+            <i @click="onClickForRename(data)" class="iconfont icon-editor_list_edit icon-edit" v-tooltip="$i18n.t('navigation.rename')"> </i>
+            <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="$i18n.t('navigation.delete')" @click.stop="() => remove(node, data)"> </i>
+          </div> -->
+        </div>
+      </template>
+    </el-tree>
+
+    <!-- 选择全景图 or 场景 组件 -->
+    <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
+      <MaterialSelector
+        :title="$i18n.t('gather.select_material')"
+        @cancel="isShowSelectionWindow = false"
+        @submit="onSubmitFromMaterialSelector"
+        :selectableType="['pano', '3D']"
+        :initialMaterialType="'pano'"
+        :isMultiSelection="true"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+// import { VueTreeList, Tree, TreeNode } from "vue-tree-list";
+import { mapGetters, mapMutations } from "vuex";
+import SceneInGroup from "@/components/sceneInGroupInEditor.vue";
+import MaterialSelector from "@/components/materialSelector.vue";
+
+import { isUpgradeAdapter } from "@/utils/fixVersion";
+import { ossImagePreviewUrlSuffix } from "@/utils/other.js";
+export default {
+  components: {
+    // VueTreeList,
+    MaterialSelector,
+  },
+  computed: {
+    ...mapGetters({
+      info: "base/baseInfo",
+      sceneList: "base/sceneList",
+      currentScene: "scene/currentScene",
+      currentSecondId: "navigation/currentSecondId",
+      currentRootId: "navigation/currentRootId",
+    }),
+  },
+  watch: {
+    "info.navigationTrees": {
+      // immediate: true,
+      handler: function (newVal, oldVal) {
+        if (newVal) {
+          //赋值新数据后保存展开状态
+          this.$nextTick(() => {
+            this.newNodesMap = [];
+            for (let key in this.$refs.sceneTree.store.nodesMap) {
+              this.newNodesMap.push(this.$refs.sceneTree.store.nodesMap[key]);
+            }
+
+            this.nodesMap.forEach((item, index) => {
+              this.newNodesMap.forEach((new_item, new_index) => {
+                if (index == new_index) {
+                  new_item.expanded = item.expanded;
+                }
+              });
+            });
+
+            this.nodesMap = this.newNodesMap;
+          });
+        }
+      },
+      deep: true,
+    },
+    sceneList: {
+      // immediate: true,
+      handler: function (newVal, oldVal) {
+        if (newVal) {
+          if (this.info.firstScene) {
+            if (!newVal.some((item) => item.id == this.info.firstScene.id)) {
+              this.$store.commit("base/setData", { firstScene: null });
+            }
+
+            if (!newVal.some((item) => item.id == this.currentScene.id)) {
+              this.$store.commit("scene/setCurrentScene", null);
+            }
+          }
+        }
+      },
+      deep: true,
+    },
+  },
+  data() {
+    return {
+      inputData: null,
+      insertTag: null,
+      isShowSelectionWindow: false,
+      expandedNodes: [], // 用于存储已展开节点的数组
+      nodesMap: [],
+      newNodesMap: [],
+      reNameId: null,
+      backUpName: "",
+      deleteId: null,
+      timer: null,
+    };
+  },
+  methods: {
+    ossImagePreviewUrlSuffix,
+
+    handleNodeExpand(data, node, instance) {
+      console.log("节点展开", data);
+      // 当节点展开时,将其id添加到数组中
+      this.expandedNodes.push(data.id);
+      console.error(this.expandedNodes);
+    },
+    handleNodeCollapse(data, node, instance) {
+      console.log("节点折叠", data);
+      // 当节点折叠时,从数组中移除其id
+      const index = this.expandedNodes.indexOf(data.id);
+      if (index !== -1) {
+        this.expandedNodes.splice(index, 1);
+      }
+      console.error(this.expandedNodes);
+    },
+    handleDragStart(node, ev) {
+      console.log("drag start", node);
+      if (node.data.type == "group") {
+        ev.dataTransfer.setDragImage(this.$refs["drag-group"], -10, -18);
+      } else {
+        ev.dataTransfer.setDragImage(this.$refs["drag-image"], -10, -18);
+      }
+    },
+    handleDragEnter(draggingNode, dropNode, ev) {
+      console.log("tree drag enter: ", dropNode);
+      if (!this.timer) {
+        if (dropNode.data.type == "group" && dropNode.data.children.length && !dropNode.expanded) {
+          this.timer = setTimeout(() => {
+            console.error("可以展开");
+            dropNode.expanded = true;
+            this.clearTimer();
+          }, 1000);
+        }
+      }
+    },
+    clearTimer() {
+      clearTimeout(this.timer);
+      this.timer = null;
+    },
+    handleDragLeave(draggingNode, dropNode, ev) {
+      this.clearTimer();
+      // if (dropNode.expanded) {
+      //   dropNode.expanded = false;
+      // }
+      // console.log("tree drag leave: ", dropNode.data.type);
+    },
+    handleDragOver(draggingNode, dropNode, ev) {
+      // console.log("tree drag over: ", dropNode.data.type);
+    },
+    handleDragEnd(draggingNode, dropNode, dropType, ev) {
+      // console.log("tree drag end: ", dropNode && dropNode.data.type, dropType);
+      this.clearTimer();
+      this.processTreeData("drag");
+    },
+    handleDrop(draggingNode, dropNode, dropType, ev) {
+      // console.log("tree drop: ", dropNode.data.type, dropType);
+      return false;
+    },
+    allowDrop(draggingNode, dropNode, type) {
+      if (draggingNode.data.type != "group") {
+        //拖拽场景
+        if (dropNode.level == 1) {
+          //不允许拖拽到第一层级
+          if (!dropNode.data.children.length || (dropNode.data.children.length && dropNode.data.children[0].type != "group")) {
+            return type == "inner";
+          } else {
+            return false;
+          }
+        } else if (dropNode.level == 2) {
+          //不允许拖拽到和目录同级
+          if (dropNode.data.type == "group") {
+            return type == "inner";
+          }
+        }
+        return type == "prev" || type == "next";
+      } else {
+        //拖拽组
+        if (draggingNode.level == 2 || draggingNode.level == 1) {
+          //当拖拽的是第一第二级目录的时候,只能拖拽到第二级目录的前后,不能拖拽到2级目录的里面
+          if (dropNode.level == 3) {
+            return false;
+          }
+
+          if (dropNode.level == 2) {
+            if (dropNode.data.type != "group") {
+              if (dropNode.parent.data.children.length && dropNode.parent.data.children[0].type != "group") {
+                return type == "prev" || type == "next";
+              } else {
+                return false;
+              }
+            } else {
+              return type == "prev" || type == "next";
+            }
+          } else if (dropNode.level == 1) {
+            return type == "prev" || type == "next" || type == "inner";
+          }
+        }
+      }
+    },
+    allowDrag(draggingNode) {
+      // console.error(draggingNode.data.name)
+      // return draggingNode.data.name.indexOf("三级 3-2-2") === -1;
+      return true;
+    },
+
+    handlerGroup(data, note, e) {
+      if (data.type == "group") {
+        // if (data.children.length) {
+        this.$refs.sceneTree.store.nodesMap[data.id].expanded = !this.$refs.sceneTree.store.nodesMap[data.id].expanded;
+        if (this.$refs.sceneTree.store.nodesMap[data.id].expanded) {
+          this.handleNodeExpand(data);
+        } else {
+          this.handleNodeCollapse(data);
+        }
+        // }
+      }
+    },
+    onRequestForAddScene(item) {
+      this.insertTag = item;
+      this.isShowSelectionWindow = true;
+    },
+    onSubmitFromMaterialSelector(selected) {
+      console.error(this.insertTag);
+      let changeListLength = this.insertTag.children.length || 0;
+      let newScenes = [];
+      let roundId = new Date().getTime();
+      for (const item of selected) {
+        roundId += 1;
+        if (item.materialType === "pano") {
+          newScenes.push({
+            children: [],
+            fodderId: item.id,
+            icon: item.icon,
+            sceneCode: item.sceneCode,
+            name: item.name,
+            // category: this.level === 1 ? this.groupNode.children[0].id : this.groupNode.id,
+            type: "pano",
+            sort: changeListLength,
+            parentId: null,
+            level: this.insertTag.level + 1,
+            id: "add_" + roundId,
+            // id: "s_" + this.$randomWord(true, 8, 8),
+          });
+        } else if (item.materialType === "3D") {
+          console.log("item.num", item.num);
+          newScenes.push({
+            children: [],
+            icon: item.thumb,
+            sceneCode: item.num,
+            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: "add_" + roundId,
+
+            // 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.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", {
+                msg: item.sceneTitle,
+              })}`
+            );
+          }, i * 100);
+          allSuccess = false;
+          return;
+        }
+        // this.info.scenes.push(item);
+        this.insertTag.children.push(item);
+      });
+
+      this.isShowSelectionWindow = false;
+      if (allSuccess) {
+        this.handlerGroup(this.insertTag);
+        this.$msg.success(this.$i18n.t("gather.success"));
+      }
+    },
+    onClickForRename(data) {
+      // this.isRenaming = true;
+      this.inputData = data;
+      this.backUpName = data.name;
+      this.reNameId = data.id;
+
+      if (this.inputData.name == "默认二级分组") {
+        this.inputData.name = this.$i18n.t("navigation.default_group_two");
+      } else if (this.inputData.name == "一级分组") {
+        this.inputData.name = this.$i18n.t("navigation.group_one");
+      } else {
+        // this.inputData .name = data.name;
+      }
+      this.$nextTick(() => {
+        this.$refs.reNameInput.focus();
+      });
+      // this.$nextTick(() => {
+      //   // this.$refs['input-for-rename'].focus()
+      //   this.$refs["input-for-rename"].select();
+      //   this.$refs["input-for-rename"].scrollIntoView({
+      //     behavior: "smooth",
+      //   });
+      // });
+    },
+    onInputNewNameComplete(data) {
+      console.error(data.name);
+      if (this.inputData.name.trim() == "") {
+        // data.name = this.backUpName;
+        // data.name = data.name;
+        this.reNameId = null;
+        return;
+      }
+      data.name = this.inputData.name;
+      this.reNameId = null;
+      // this.isRenaming = false;
+      // if (!this.inputData.name.trim()) {
+      //   return;
+      // }
+      // if (this.inputData.name !== this.groupNode.name) {
+      //   // this.$emit("renameGroup", this.groupNode.id, this.level, this.newName);
+      // }
+      // this.inputData = null;
+    },
+
+    onDel(node, data) {
+      this.deleteId = data.id;
+    },
+    onRequestForCancelDelete() {
+      this.deleteId = null;
+    },
+    onConfirmDelete(node, data) {
+      // this.$emit("delete", this.sceneInfo.id);
+      // this.isConfirmingDeletion = false;
+      this.onRequestForCancelDelete();
+      if (this.sceneList.length == 1 && data.type != "group") {
+        this.$alert({
+          title: this.$i18n.t("navigation.delete_init_scene"),
+          content: this.$i18n.t("navigation.keep_one_scene"),
+        });
+        return;
+      }
+
+      console.error(node, data);
+
+      if (this.info.navigationTrees.length == 1 && node.level == 1) {
+        this.$alert({
+          content: this.$i18n.t("navigation.keep_one_group"),
+        });
+        return;
+      }
+
+      if ((node.level == 1 || node.level == 2) && data.type == "group") {
+        let delScenes = [];
+        this.info.navigationTrees.forEach((item) => {
+          item.children.forEach((s_item) => {
+            if (s_item.type != "group" && (item.id == node.data.id || s_item.id == node.data.id)) {
+              delScenes.push(s_item);
+            }
+            s_item.children.forEach((t_item) => {
+              if (t_item.type != "group" && (item.id == node.data.id || s_item.id == node.data.id)) {
+                delScenes.push(t_item);
+              }
+            });
+          });
+        });
+        if (delScenes.length >= this.sceneList.length) {
+          //如果要删除的场景大于等于 当前场景数量,则弹出提示 至少保留一个
+          this.$alert({
+            title: this.$i18n.t("navigation.delete_init_scene"),
+            content: this.$i18n.t("navigation.keep_one_scene"),
+          });
+          return;
+        }
+      }
+
+      const parent = node.parent;
+      const children = parent.data.children || parent.data;
+      const index = children.findIndex((d) => d.id === data.id);
+      children.splice(index, 1);
+      this.processTreeData("del");
+      // if (parent.data.children.length == 1 && parent.data.children[0].type == "group") {
+      //   let newChild = children[0].children;
+      //   parent.data.children = newChild;
+      // }
+      // console.error(this.info.navigationTrees);
+    },
+
+    addNode(data) {
+      let roundId = new Date().getTime();
+      let group = {
+        id: "add_" + roundId,
+        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,
+        //   };
+        //   data.children.push(defaultDir);
+        // }
+        data.children.push(group);
+        this.$refs.sceneTree.store.nodesMap[data.id].expanded = true;
+      } else {
+        this.info.navigationTrees.push(group);
+      }
+      this.processTreeData("add");
+      this.$nextTick(() => {
+        this.onClickForRename(group);
+      });
+    },
+    getRoundId() {
+      return new Date().getTime();
+    },
+    processTreeData(type) {
+      this.info.navigationTrees.forEach((item) => {
+        //一级目录
+
+        let panoList = item.children.filter((pano) => pano.type != "group");
+        let groupList = item.children.filter((pano) => pano.type == "group");
+
+        switch (type) {
+          case "add":
+            if (panoList.length && groupList.length) {
+              //新增目录到二级目录。且二级目录只有场景的时候
+              console.error("add");
+              let list = [];
+              let defaultDir = {
+                id: "add_" + (this.getRoundId() + 1),
+                sort: 0,
+                name: this.$i18n.t("navigation.default_group_two"),
+                type: "group",
+                children: panoList,
+              };
+              list.push(defaultDir);
+              list = list.concat(groupList);
+              item.children = list;
+            }
+
+            if (item.children.length == 1 && item.type == "group" && !item.children[0].children.length) {
+              //新增二级目录的时候,如果新增完只有一个二级目录,且二级目录为空,创建多一个目录,且第一个为默认二级目录
+              let list = [];
+              let defaultDir = {
+                id: "add_" + (this.getRoundId() + 1),
+                sort: 0,
+                name: this.$i18n.t("navigation.default_group_two"),
+                type: "group",
+                children: [],
+              };
+              list.push(defaultDir);
+              list = list.concat(groupList);
+              item.children = list;
+            }
+
+            break;
+          case "del":
+            if (groupList.length == 1 && !panoList.length && groupList[0].children.length) {
+              //当只有一个二级目录,二级目录有场景
+              item.children = groupList[0].children;
+            }
+            if (groupList.length == 1 && !groupList[0].children.length) {
+              //当只有一个二级目录,二级目录没有场景
+
+              item.children = [];
+            }
+
+            break;
+          case "drag":
+            if (panoList.length && groupList.length) {
+              //新增目录到二级目录。且二级目录只有场景的时候
+              console.error("drag");
+              let list = [];
+              let defaultDir = {
+                id: "add_" + (this.getRoundId() + 1),
+                sort: 0,
+                name: this.$i18n.t("navigation.default_group_two"),
+                type: "group",
+                children: panoList,
+              };
+              list.push(defaultDir);
+              list = list.concat(groupList);
+              item.children = list;
+              console.error(item.children);
+              break;
+            }
+        }
+
+        // if (item.id == this.currentRootId) {
+        //   console.error(groupList);
+        //   if (groupList.length) {
+        //     let index = groupList.findIndex((pano) => pano.children.length);
+
+        //     this.$store.commit("navigation/setData", { currentSecondId: groupList[index].id });
+        //   } else {
+        //     this.$store.commit("navigation/setData", { currentSecondId: null });
+        //   }
+        //   // let currentSecondId = item.children.find((group) => group.id == this.currentSecondId);
+        //   // console.error("currentSecondId", currentSecondId);
+        // }
+      });
+    },
+  },
+  created() {},
+};
+</script>
+
+<style lang="less" rel="stylesheet/less" scoped>
+.vtl {
+  // .vtl-drag-disabled {
+  //   background-color: #d0cfcf;
+  //   &:hover {
+  //     background-color: #d0cfcf;
+  //   }
+  // }
+  // .vtl-disabled {
+  //   background-color: #d0cfcf;
+  // }
+}
+</style>
+
+<style lang="less" rel="stylesheet/less" scoped>
+.icon {
+  &:hover {
+    cursor: pointer;
+  }
+}
+.el-tree {
+  background: none !important;
+  color: hsla(0, 0%, 100%, 0.6);
+  // .folder_expanded {
+  //   display: none;
+  // }
+  // .is-expanded {
+  //   .folder_collapsed {
+  //     display: none;
+  //   }
+  //   .folder_expanded {
+  //     display: inline-block;
+  //   }
+  // }
+  .tree-msg {
+    width: calc(100% - 24px);
+    // min-height: 40px;
+    color: hsla(0, 0%, 100%, 0.6);
+    // display: flex;
+    // align-items: center;
+    // justify-content: space-between;
+    padding-right: 10px;
+    .tree-info {
+      // display: flex;
+      // align-items: center;
+      // justify-content: flex-start;
+      height: 100%;
+      // height: 40px;
+      // flex: 1;
+      .group-info {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+        flex: 1;
+        height: 100%;
+        position: relative;
+        .drag-icon {
+          position: absolute;
+          width: 40px;
+          height: 40px;
+          background: #313131;
+          box-shadow: 0 0 4px 0 rgb(0 0 0 / 50%);
+          border-radius: 4px;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          z-index: -1;
+        }
+        span {
+          margin-left: 6px;
+          display: inline-block;
+          text-overflow: ellipsis;
+          overflow: hidden;
+          white-space: nowrap;
+          flex: 1 1 auto;
+        }
+      }
+    }
+    .image-info {
+      display: flex;
+      align-items: center;
+      // padding: 10px 0;
+      height: 64px;
+      // flex: 1;
+      width: 100%;
+      .cover-image {
+        flex: 0 0 auto;
+        width: 64px;
+        height: 64px;
+        background: #b0b0b0;
+        border-radius: 2px;
+        background-size: cover;
+        img {
+          width: 100%;
+          height: 100%;
+          pointer-events: none;
+          object-fit: cover;
+        }
+      }
+      .image-name {
+        margin-left: 10px;
+        width: calc(100% - 74px);
+        flex: 1 1 auto;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        height: 100%;
+        flex: 1;
+        .name-top {
+          width: 100%;
+          span {
+            width: 100%;
+            word-break: break-all;
+            display: -webkit-box;
+            -webkit-box-orient: vertical;
+            -webkit-line-clamp: 2;
+            overflow: hidden;
+            font-size: 14px;
+            color: hsla(0, 0%, 100%, 0.6);
+
+            white-space: normal;
+          }
+        }
+        .name-bottom {
+          color: #0076f6;
+        }
+      }
+      .group-title-input {
+        width: 100%;
+        margin-left: 0;
+      }
+    }
+    .group-title-input {
+      margin-left: 6px;
+      flex: 1 1 auto;
+      width: 1px;
+      height: 30px;
+      background: #1a1b1d;
+      border-radius: 2px;
+      border: 1px solid #404040;
+      color: #fff;
+      font-size: 14px;
+      padding: 0 10px 0 16px;
+      &:focus {
+        border-color: #0076f6;
+      }
+    }
+    .deletion-confirm-wrap {
+      position: absolute;
+      top: 0;
+      bottom: 0;
+      right: 0;
+      width: 44px;
+      overflow: hidden;
+      pointer-events: none;
+      border-top-right-radius: 4px;
+      border-bottom-right-radius: 4px;
+      > .deletion-confirm {
+        position: absolute;
+        top: 0;
+        bottom: 0;
+        width: 100%;
+        background: #fa5555;
+        transition: right 0.3s;
+        cursor: pointer;
+        text-align: center;
+        font-size: 12px;
+        color: #fff;
+        pointer-events: auto;
+        &::after {
+          content: "";
+          height: 100%;
+          vertical-align: middle;
+          display: inline-block;
+        }
+        &.show {
+          right: 0;
+        }
+        &.hide {
+          right: -44px;
+        }
+      }
+    }
+    .controls-btn {
+      display: none;
+
+      > i {
+        margin-left: 12px;
+
+        &.iconfont {
+          &:hover {
+            color: #0076f6;
+          }
+        }
+      }
+    }
+    &.image-item {
+      .controls-btn {
+        position: absolute;
+        right: 10px;
+        bottom: 8px;
+      }
+      .tree-info {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+        width: 100%;
+      }
+    }
+  }
+}
+</style>
+<style lang="less">
+.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
+  background: none !important;
+}
+.el-tree-node:focus > .el-tree-node__content {
+  background: none !important;
+}
+.el-tree-node:focus > .el-tree-node__content:hover {
+  background: #313131 !important;
+}
+.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content:hover {
+  background: #313131 !important;
+}
+.el-tree-node__content:hover,
+.el-upload-list__item:hover {
+  background: #313131;
+}
+.el-tree-node > .el-tree-node__children {
+  overflow: inherit;
+}
+.el-tree-node__content {
+  // height: 60px;
+  height: auto;
+  // padding: 10px 0;
+  border-radius: 4px;
+}
+.el-tree-node__content {
+  // padding-left: 30px !important;
+  position: relative;
+  &::before {
+    content: "";
+    width: 24px;
+    height: 24px;
+    display: inline-block;
+  }
+  &:hover {
+    .controls-btn {
+      display: block !important;
+    }
+  }
+}
+
+.custom-tree .el-tree__drop-indicator {
+  // display: none
+  // margin-top: -25px; /* 修改间距为10px */
+  // margin-bottom: 10px; /* 修改间距为10px */
+}
+.el-tree-node__expand-icon {
+  // height: 44px;
+  display: flex;
+  align-items: center;
+  margin-right: 5px;
+}
+// .el-tree-node__expand-icon:hover {
+//   background: red;
+// }
+// .el-tree-node__content{
+//   background: red;
+// }
+.el-tree-node__content .el-tree-node__expand-icon {
+  position: absolute;
+  height: 100%;
+  // left: 6px;
+}
+.el-tree-node__content .el-tree-node__expand-icon:first-child + .group-item {
+  height: 44px;
+}
+.el-tree-node__content .el-tree-node__expand-icon:first-child + .image-item {
+  height: 80px;
+}
+</style>

+ 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;
       }

+ 150 - 76
packages/qjkankan-editor/src/framework/EditorHead.vue

@@ -1,24 +1,24 @@
 <template>
-  <header class="app-head" app-border dir-bottom>
+  <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>
+
+    <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 }">
       <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";
@@ -44,6 +44,7 @@ export default {
   components: { preview },
 
   mounted() {
+    this.getSettingsInfo();
     this.$bus.on("canLoad", (data) => {
       this.canLoad = data;
       if (data) {
@@ -58,12 +59,76 @@ export default {
   },
   computed: {
     ...mapGetters({
-      info: "info",
+      // info: "info",
+      baseInfo: "base/baseInfo",
+      sceneList: "base/sceneList",
       isShow: "isShow",
       catalogTopology: "catalogTopology",
       currentScene: "scene/currentScene",
       isEditing: "isEditing",
     }),
+
+    info() {
+      if (this.baseInfo) {
+        this.$store.commit("base/ininDefaultData");
+        //初始化
+        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/setCurrentScenesList", this.baseInfo.navigationTrees[0].children);
+        }
+
+        if (!this.currentScene) {
+          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");
+                }
+              });
+            });
+          });
+
+          this.$store.commit("scene/setCurrentScene", 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]);
+      }
+      return this.baseInfo;
+    },
+
     routerForm() {
       const from = browser.urlQueryValue("from");
       return from;
@@ -71,7 +136,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: () => {
@@ -98,13 +164,13 @@ export default {
 
         saveWorks(
           {
-            password: this.info.password,
+            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.name || this.$i18n.t("gather.no_title");
+            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(() => {
@@ -134,46 +200,47 @@ export default {
 
     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.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;
           }
@@ -220,16 +287,16 @@ 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();
 
         // console.error(this.$router.name)
-        this.$store.dispatch(`${this.$route.name}/save`)
+        this.$store.dispatch(`${this.$route.name}/save`);
         // saveWorks(
         //   {
         //     password: this.info.password,
@@ -254,51 +321,58 @@ 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);
+        },
+        () => {}
+      );
+    },
   },
 };
 </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() {
     // 添加滚动条

+ 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",

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

@@ -0,0 +1,428 @@
+<template>
+  <div class="bar-list" v-if="info">
+    <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,
+              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 :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 { 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,
+        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",
+    }),
+    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 && item.children.length);
+    },
+  },
+  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);
+    },
+    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>

+ 76 - 99
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";
@@ -57,7 +40,7 @@ import { isUpgradeAdapter } from "@/utils/fixVersion";
 let __krfn = krfn.default;
 
 export default {
-  components: { list, Snapshot },
+  components: { list, Snapshot, newList },
   data() {
     return {
       showFlash: false,
@@ -73,8 +56,9 @@ export default {
     },
     ...mapGetters({
       currentScene: "scene/currentScene",
-      info: "info",
+      baseInfo: "base/baseInfo",
       isConfirmingPosi: "tags/isConfirmingPosi",
+      sceneList: "base/sceneList",
     }),
 
     showSnapshot() {
@@ -120,20 +104,23 @@ export default {
         $("#pano").empty();
         window.vrInitFn = () => {
           $waiting.hide();
-          __krfn.utils.initHotspot(
-            this.$getKrpano(),
-            newVal && newVal.someData,
-            true
-          );
-          __krfn.utils.toggleHotspot(
-            this.$getKrpano(),
-            this.$route.name == "hotspot"
-          );
+          __krfn.utils.initHotspot(this.$getKrpano(), newVal && newVal.someData, 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.baseInfo?.workVisualAngleList.find((item) => item.navigationId == newVal.id);
+
+            let sky, earth;
+            this.baseInfo?.workCustomMaskList?.forEach((item, index) => {
+              if (this.currentScene.id == item.navigationId) {
+                sky = item.data.sky;
+                earth = item.data.earth;
+              }
+            });
             this.handleVisual(visual);
             this.handleSkyCover(sky);
             this.handleEarthCover(earth);
@@ -180,32 +167,46 @@ 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.baseInfo.workVisualAngleList.map((item) => {
+            if (item.navigationId == this.currentScene.id) {
+              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 +216,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 +230,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 +254,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 +263,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.baseInfo?.workCustomMaskList?.forEach((item, index) => {
+        if (this.currentScene.id == 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(() => {

+ 5 - 0
packages/qjkankan-editor/src/pages/edit.js

@@ -12,6 +12,10 @@ 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"));
@@ -41,6 +45,7 @@ debuggerHelper(true);
 // 热点图标默认大小
 window.g_hotspotCurrentScale = 1;
 
+// Vue.use(VueTreeList);
 Vue.use(Viewer, {
   defaultOptions: {
     toolbar: 0,

+ 22 - 0
packages/qjkankan-editor/src/utils/request.js

@@ -401,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({
@@ -544,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);
+  },
 };

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

@@ -547,7 +547,7 @@ export default {
       //   workCoverType: { ...this.workCoverType },
       //   ...this.info,
       // });
-      this.$store.commit("base/setData", {
+      this.$store.commit("base/initbaseInfo", {
         workCoverType: this.workCoverType,
       });
     } else {

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

@@ -43,14 +43,14 @@ export default {
         {},
         (res) => {
           let data = res.data;
-          this.$store.commit("base/setData", data);
+          this.$store.commit("base/initbaseInfo", data);
         },
         () => {}
       );
     },
   },
   mounted() {
-    this.getSettingsInfo();
+    // this.getSettingsInfo();
   },
 };
 </script>

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

@@ -1,46 +1,42 @@
 <template>
-  <div class="explanation-settings"  app-border dir-left>
+  <div class="explanation-settings" app-border dir-left>
     <template v-if="currentScene.type !== '4dkk'">
       <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,52 +69,53 @@ export default {
   },
   computed: {
     ...mapGetters({
-        info: "info",
-        currentScene: "scene/currentScene",
-        
+      baseInfo: "base/baseInfo",
+      currentScene: "scene/currentScene",
     }),
+    currentExplanation() {
+      return this.baseInfo.workExplanationList.find((item) => item.navigationId == this.currentScene.id);
+    },
   },
   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,
-          })
-        }
-      },
-    },
+    // 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>
@@ -148,7 +145,7 @@ export default {
     margin-top: 16px;
     width: 234px;
     height: 36px;
-    background: #1A1B1D;
+    background: #1a1b1d;
     border-radius: 2px;
     border: 1px solid #404040;
     color: #fff;
@@ -156,7 +153,7 @@ export default {
     text-align: center;
     position: relative;
     &:hover {
-      color: #0076F6;
+      color: #0076f6;
       border-color: @color;
       > .audio-control {
         display: inline-block;
@@ -190,7 +187,7 @@ export default {
       transform: translateY(-50%);
       right: 18px;
       &:hover {
-        color: #FA5555;
+        color: #fa5555;
       }
     }
   }
@@ -212,4 +209,4 @@ export default {
     font-size: 14px;
   }
 }
-</style>
+</style>

+ 43 - 95
packages/qjkankan-editor/src/views/mask/setting.vue

@@ -1,71 +1,39 @@
 <template>
   <div class="cover-panel">
-    <template v-if="currentScene.type !== '4dkk'">
+    <template v-if="currentScene?.type !== '4dkk'">
       <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,12 @@ export default {
   computed: {
     ...mapGetters({
       currentScene: "scene/currentScene",
+      baseInfo: "base/baseInfo",
     }),
+
+    currentMask() {
+      return this.baseInfo.workCustomMaskList.find((item) => item.navigationId == this.currentScene.id);
+    },
   },
   data() {
     return {
@@ -204,8 +142,9 @@ export default {
     sky: {
       handler: function (val) {
         if (this.currentScene.type === "pano" && val) {
-          this.currentScene.customMask.sky = val;
-          this.updateCurrentScene();
+          // this.currentScene.customMask.sky = val;
+          // this.updateCurrentScene();
+          console.error(this.currentMask);
           this.handlePeakStatus(val.isShow);
           this.handlePeakScale(val.scale);
           this.handlePeakURL(val.icon);
@@ -217,8 +156,8 @@ export default {
     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);
@@ -263,10 +202,13 @@ export default {
       },
       immediate: true,
     },
-    "currentScene.customMask": {
+    currentScene: {
       handler: function (newVal, oldVal) {
         if (newVal && newVal !== oldVal) {
-          const { sky, earth } = newVal;
+          let item = this.baseInfo.workCustomMaskList.find((item) => item.navigationId == this.currentScene.id);
+
+          const { sky, earth } = item.data;
+
           // this.isApplyEarthToAll = false;
           // this.isApplySkyToAll = false;
           this.sky = { ...sky };
@@ -288,6 +230,10 @@ 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");
     },
@@ -306,8 +252,10 @@ export default {
       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) {

+ 214 - 199
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, saveWorks, addWork } from "@/api";
+import { version } from "less";
 
 export default {
   components: {
@@ -221,7 +165,6 @@ export default {
     },
   },
   methods: {
-  
     refreshListDebounced: debounce(
       function () {
         this.list = [];
@@ -263,130 +206,204 @@ export default {
       //   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) {
@@ -489,10 +506,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);

+ 78 - 59
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,69 @@ 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",
       catalogTopology: "catalogTopology",
     }),
   },
   data() {
     return {};
   },
-  watch: {},
+  watch: {
+    "info.navigationTrees": {
+      // immediate: true,
+      handler: function (newVal) {
+        if (newVal) {
+          console.error("navigationTrees change");
+          this.$store.commit("scene/setSaveApiList", "navigation");
+        }
+      },
+      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 +139,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 +186,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 +194,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) => {
@@ -210,14 +241,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 +258,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 +277,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 +290,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);
           }
           // 删除该一级分组
@@ -360,8 +377,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 - 21
packages/qjkankan-editor/src/views/navigation/initialSceneSettings.vue

@@ -2,24 +2,10 @@
   <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">
       <button class="ui-button deepcancel" @click="deleteIndexInfo">
@@ -59,30 +45,51 @@ export default {
   components: {
     Selector,
   },
+  watch: {
+    "info.firstScene": {
+      immediate: true,
+      handler: function (newVal, oldVal) {
+        if (JSON.stringify(newVal) != JSON.stringify(oldVal)) {
+          console.error("firstScene change", JSON.stringify(newVal), "*****", JSON.stringify(oldVal));
+          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() {},

+ 60 - 36
packages/qjkankan-editor/src/views/screen/Setting.vue

@@ -2,24 +2,11 @@
   <div 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,8 @@ export default {
   computed: {
     ...mapGetters({
       currentScene: "scene/currentScene",
+      baseInfo: "base/baseInfo",
+      sceneList: "base/sceneList",
     }),
   },
   data() {
@@ -124,13 +109,41 @@ 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.baseInfo.workVisualAngleList.find((item) => item.navigationId == this.currentScene.id);
+
+          if (item) {
+            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,
@@ -151,11 +164,12 @@ 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);
             },
@@ -169,6 +183,16 @@ export default {
     },
   },
   methods: {
+    handleChange(value, index) {
+      this.baseInfo.workVisualAngleList.forEach((item, index) => {
+        if (item.navigationId == this.currentScene.id) {
+          item.vlookatmin = this.vlookat[0];
+          item.vlookatmax = this.vlookat[1];
+        }
+      });
+
+      // 拖动结束时的处理逻辑
+    },
     onClickGo4dkk() {
       window.open("/#/scene");
     },
@@ -179,11 +203,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) {

+ 1 - 1
packages/qjkankan-editor/vue.config.js

@@ -37,7 +37,7 @@ module.exports = {
         changeOrigin: true,
       },
       "/work": {
-        target: 'http://192.168.0.41:8002/qjkankan',
+        target: proxy_url,
         changeOrigin: true,
       },
       "/ucenter": {