tremble %!s(int64=2) %!d(string=hai) anos
pai
achega
3dceff6a21
Modificáronse 96 ficheiros con 4842 adicións e 2437 borrados
  1. 1 1
      packages/qjkankan-editor/public/material.html
  2. 413 633
      packages/qjkankan-editor/public/static/template/skin/vtourskin.xml
  3. 11 0
      packages/qjkankan-editor/src/api/index.js
  4. BIN=BIN
      packages/qjkankan-editor/src/assets/images/commentary@2x.png
  5. 1 0
      packages/qjkankan-editor/src/assets/images/default/logo_white.svg
  6. BIN=BIN
      packages/qjkankan-editor/src/assets/images/icons/help_tips.png
  7. BIN=BIN
      packages/qjkankan-editor/src/assets/videos/小行星巡游开场_x264.mp4
  8. BIN=BIN
      packages/qjkankan-editor/src/assets/videos/小行星开场_x264.mp4
  9. BIN=BIN
      packages/qjkankan-editor/src/assets/videos/小行星缩放开场_x264.mp4
  10. BIN=BIN
      packages/qjkankan-editor/src/assets/videos/水平巡游开场_x264.mp4
  11. BIN=BIN
      packages/qjkankan-editor/src/assets/videos/水晶球开场_x264.mp4
  12. 4 3
      packages/qjkankan-editor/src/components/materialSelectorForManageCenter.vue
  13. 1 1
      packages/qjkankan-editor/src/components/selectedImageInEditor.vue
  14. 2 2
      packages/qjkankan-editor/src/components/shared/Editor.vue
  15. 50 18
      packages/qjkankan-editor/src/components/shared/message/Alert.vue
  16. 1 1
      packages/qjkankan-editor/src/components/shared/message/index.js
  17. 3 2
      packages/qjkankan-editor/src/framework/EditorHead.vue
  18. 15 0
      packages/qjkankan-editor/src/framework/MenuPC.vue
  19. 2 2
      packages/qjkankan-editor/src/framework/material/header.vue
  20. 70 30
      packages/qjkankan-editor/src/framework/play/pano/components/list.vue
  21. 28 3
      packages/qjkankan-editor/src/framework/play/pano/index.vue
  22. 288 290
      packages/qjkankan-editor/src/utils/browser.js
  23. 1 1
      packages/qjkankan-editor/src/views/base/Toolbar.vue
  24. 1 1
      packages/qjkankan-editor/src/views/base/customLogoSettings.vue
  25. 39 15
      packages/qjkankan-editor/src/views/base/openingAnimationSettings.vue
  26. 16 13
      packages/qjkankan-editor/src/views/hotspot/EditPanel.vue
  27. 49 42
      packages/qjkankan-editor/src/views/hotspot/HotSpotList.vue
  28. 4 4
      packages/qjkankan-editor/src/views/hotspot/hotspotType/textarea.vue
  29. 12 1
      packages/qjkankan-editor/src/views/material/audio/index.vue
  30. 13 2
      packages/qjkankan-editor/src/views/material/image/index.vue
  31. 15 2
      packages/qjkankan-editor/src/views/material/pano/index.vue
  32. 1 1
      packages/qjkankan-editor/src/views/material/popup/rename.vue
  33. 12 1
      packages/qjkankan-editor/src/views/material/video/index.vue
  34. 3 1
      packages/qjkankan-editor/src/views/material/works/index.vue
  35. 1 2
      packages/qjkankan-kankan-view/src/components/Controls/BottomControl.vue
  36. 21 372
      packages/qjkankan-kankan-view/src/components/Controls/Control.Mobile.vue
  37. 440 0
      packages/qjkankan-kankan-view/src/components/Controls/tours.mobile.vue
  38. 5 0
      packages/qjkankan-kankan-view/src/components/Controls/tours.vue
  39. 2 44
      packages/qjkankan-kankan-view/src/components/Information/View.Mobile.vue
  40. 505 440
      packages/qjkankan-kankan-view/src/pages/SMG.vue
  41. 1 2
      packages/qjkankan-kankan-view/src/pages/SPG.vue
  42. 42 10
      packages/qjkankan-kankan-view/src/pages/smg.js
  43. 10 8
      packages/qjkankan-kankan-view/src/store/index.js
  44. 5 0
      packages/qjkankan-kankan-view/src/utils/messageHandler.js
  45. 1 0
      packages/qjkankan-view/public/show.html
  46. 2 0
      packages/qjkankan-view/public/showMobile.html
  47. 5 0
      packages/qjkankan-view/public/showviewer/lib/jssor/jssor.slider-28.1.0.min.js
  48. 2 2
      packages/qjkankan-view/public/showviewer/lib/krpano/plugins/webvr.js
  49. 32 64
      packages/qjkankan-view/public/showviewer/lib/krpano/plugins/webvr.xml
  50. 29 29
      packages/qjkankan-view/public/showviewer/lib/krpano/skin/vtourskin.xml
  51. 1 1
      packages/qjkankan-view/src/apis/index.js
  52. BIN=BIN
      packages/qjkankan-view/src/assets/images/icon/pause.png
  53. BIN=BIN
      packages/qjkankan-view/src/assets/images/icon/preview.png
  54. 45 24
      packages/qjkankan-view/src/components/Fdkk/index.vue
  55. 144 0
      packages/qjkankan-view/src/components/Fdkk/v3fdkkmobile/iframe.vue
  56. 619 0
      packages/qjkankan-view/src/components/Fdkk/v3fdkkmobile/vheader.vue
  57. 15 3
      packages/qjkankan-view/src/components/Pano/index.vue
  58. 19 18
      packages/qjkankan-view/src/components/UIGather/control.vue
  59. 5 2
      packages/qjkankan-view/src/components/UIGather/control/telephone.vue
  60. 3 0
      packages/qjkankan-view/src/components/UIGather/control/text.vue
  61. 19 6
      packages/qjkankan-view/src/components/UIGather/index.vue
  62. 406 0
      packages/qjkankan-view/src/components/UIGather/list - 副本 (2).vue
  63. 306 0
      packages/qjkankan-view/src/components/UIGather/list - 副本.vue
  64. 98 79
      packages/qjkankan-view/src/components/UIGather/list.vue
  65. 49 23
      packages/qjkankan-view/src/components/UIGather/menu.vue
  66. 265 0
      packages/qjkankan-view/src/components/UIGather/mobile/control.fdkk.vue
  67. 1 24
      packages/qjkankan-view/src/components/UIGather/mobile/control.pano.vue
  68. 144 34
      packages/qjkankan-view/src/components/UIGather/mobile/control.right.vue
  69. 49 1
      packages/qjkankan-view/src/components/UIGather/mobile/control.vue
  70. 1 0
      packages/qjkankan-view/src/components/UIGather/mobile/control/link.vue
  71. 4 0
      packages/qjkankan-view/src/components/UIGather/mobile/control/text.vue
  72. 52 16
      packages/qjkankan-view/src/components/UIGather/mobile/index.vue
  73. 104 77
      packages/qjkankan-view/src/components/UIGather/mobile/list.vue
  74. 3 1
      packages/qjkankan-view/src/components/UIGather/mobile/logo.vue
  75. 1 1
      packages/qjkankan-view/src/components/UIGather/mobile/tips.vue
  76. 2 1
      packages/qjkankan-view/src/components/assembly/Error.vue
  77. 3 1
      packages/qjkankan-view/src/components/assembly/Loading.vue
  78. 3 2
      packages/qjkankan-view/src/components/assembly/MobileTags/index.vue
  79. 41 11
      packages/qjkankan-view/src/components/assembly/MobileTags/metas/fixaudio.vue
  80. 3 24
      packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-audio.vue
  81. 2 4
      packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-image.vue
  82. 3 0
      packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-text.vue
  83. 14 1
      packages/qjkankan-view/src/components/assembly/Share.vue
  84. 34 3
      packages/qjkankan-view/src/components/assembly/Tags/metas/fixaudio.vue
  85. 54 16
      packages/qjkankan-view/src/components/assembly/Tags/metas/metas-image.vue
  86. 7 2
      packages/qjkankan-view/src/components/assembly/Tags/metas/metas-text.vue
  87. 2 1
      packages/qjkankan-view/src/components/assembly/Tags/metas/metas-video.vue
  88. 1 0
      packages/qjkankan-view/src/components/assembly/Tags/metas/metas-web.vue
  89. 55 0
      packages/qjkankan-view/src/components/assembly/titieSlide.vue
  90. 2 1
      packages/qjkankan-view/src/global_components/components/dialog/index.js
  91. 1 1
      packages/qjkankan-view/src/locales/zh.json
  92. 30 5
      packages/qjkankan-view/src/pages/show.vue
  93. 36 8
      packages/qjkankan-view/src/pages/showMobile.vue
  94. 26 2
      packages/qjkankan-view/src/store/modules/fdkk.js
  95. 10 1
      packages/qjkankan-view/src/store/modules/functions.js
  96. 1 0
      packages/qjkankan-view/src/store/modules/scene.js

+ 1 - 1
packages/qjkankan-editor/public/material.html

@@ -15,7 +15,7 @@
     <link rel="stylesheet" href="<%= VUE_APP_STATIC_DIR %>/lib/tooltipster/tooltipster.bundle.min.css"/>
     <link rel="stylesheet" href="<%= VUE_APP_STATIC_DIR %>/lib/tooltipster/tooltipster-sideTip-borderless.min.css"/>
 
-    <title>四维·全景看看</title>
+    <title>四维全景</title>
   </head>
   <body>
     <div id="app"></div>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 413 - 633
packages/qjkankan-editor/public/static/template/skin/vtourskin.xml


+ 11 - 0
packages/qjkankan-editor/src/api/index.js

@@ -548,3 +548,14 @@ export function setListSort(data, ok, no) {
 }
 
 
+/**
+ * 检查用户空间
+ * @param {*} data 
+ * @param {*} ok 
+ * @param {*} no 
+ */
+ export function checkUserSize(data, ok, no) {
+    return http.get(`${URL_FILL}/manage/fodder/checkUserSize`, data, ok, no)
+}
+
+

BIN=BIN
packages/qjkankan-editor/src/assets/images/commentary@2x.png


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
packages/qjkankan-editor/src/assets/images/default/logo_white.svg


BIN=BIN
packages/qjkankan-editor/src/assets/images/icons/help_tips.png


BIN=BIN
packages/qjkankan-editor/src/assets/videos/小行星巡游开场_x264.mp4


BIN=BIN
packages/qjkankan-editor/src/assets/videos/小行星开场_x264.mp4


BIN=BIN
packages/qjkankan-editor/src/assets/videos/小行星缩放开场_x264.mp4


BIN=BIN
packages/qjkankan-editor/src/assets/videos/水平巡游开场_x264.mp4


BIN=BIN
packages/qjkankan-editor/src/assets/videos/水晶球开场_x264.mp4


+ 4 - 3
packages/qjkankan-editor/src/components/materialSelectorForManageCenter.vue

@@ -834,7 +834,7 @@ export default {
       }).length
     },
     panoListRealLength() {
-      return this.imageList.length + this.uploadStatusListPano.filter((item) => {
+      return this.panoList.length + this.uploadStatusListPano.filter((item) => {
         return item.status === 'SUCCESS'
       }).length
     },
@@ -884,6 +884,7 @@ export default {
   },
   methods: {
     selectItem(item, e) {
+      console.log(item,e);
       item.materialType = this.currentMaterialType // 三维场景数据没有type字段来表明自己是三维场景。所以统一加一个字段。
       if (false) {
         // 对于图片,大于600kb的,压缩?
@@ -1793,7 +1794,7 @@ export default {
     left: 50%;
     top: 50%;
     transform: translate(-50%, -50%);
-    border-radius: 50%;
+    // border-radius: 50%;
     border: 1px solid #D5D8DE;
     pointer-events: none;
   }
@@ -1804,7 +1805,7 @@ export default {
     left: 50%;
     top: 50%;
     transform: translate(-50%, -50%);
-    border-radius: 50%;
+    // border-radius: 50%;
     background: #0076F6;
     pointer-events: none;
     opacity: 0;

+ 1 - 1
packages/qjkankan-editor/src/components/selectedImageInEditor.vue

@@ -36,7 +36,7 @@ export default {
   > img {
     width: 100%;
     height: 100%;
-    object-fit: cover;
+    object-fit: contain;
   }
   > .cancel-btn-background {
     width: 52px;

+ 2 - 2
packages/qjkankan-editor/src/components/shared/Editor.vue

@@ -73,7 +73,7 @@ export default {
         this.quill.on("text-change", (delta, old, source) => {
             let html = this.quill.root.innerHTML;
             // 过滤emoji表情
-            html = html.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(char) {
+            html = html.replace(/(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/g, function(char) {
                 var H, L, code;
                 if (char.length === 2) {
                   return ""
@@ -131,7 +131,7 @@ export default {
     padding:0 0 30px 0;
     .ql-editor {
         padding: 10px;
-        user-select: text;
+        user-select: auto;
         font-size: 14px;
         white-space: normal !important;
         a{

+ 50 - 18
packages/qjkankan-editor/src/components/shared/message/Alert.vue

@@ -1,19 +1,35 @@
 <template>
     <popup ref="Message" :show="show">
-        <div class="ui-message ui-message-alert" :class="{'message-material':isMaterial,'message-mobile':isMobile}">
+        <div class="ui-message ui-message-confirm" :class="[isMaterial?'message-material':'dark']">
             <div class="ui-message-header" :class="{'header-material':isMaterial}">
                 <span>{{title}}</span>
-                <span @click="onClose">
+                <span @click="onNo" v-if="showCloseIcon">
                     <i class="iconfont icon_close"></i>
                 </span>
             </div>
             <div class="ui-message-main" :class="{'main-material':isMaterial}">
-                <div class="ui-message-icon" :class="[icon?icon:null]"></div>
+                <!-- <div class="ui-message-icon" :class="[icon?icon:null]"></div> -->
                 <div class="ui-message-title">{{tips}}</div>
                 <div class="ui-message-content" v-html="content"></div>
             </div>
-            <div class="ui-message-footer" :class="{'footer-material':isMaterial}">
+            <div class="ui-message-footer" :class="{'footer-material':isMaterial}" v-if="okLink">
+                <a
+                    :href="noLink"
+                    target="_blank"
+                    class="ui-button link cancel"
+                    @click="onNo"
+                >{{noText}}</a>
+                <a
+                    :href="okLink"
+                    target="_blank"
+                    class="ui-button link submit"
+                    @click="onOk"
+                >{{okText}}</a>
+            </div>
+            <div v-else class="ui-message-footer"  :class="{'footer-material':isMaterial}">
+                <!-- <button class="ui-button cancel" :class="{deepcancel:!isMaterial}" @click="onNo">{{noText}}</button> -->
                 <button class="ui-button submit" @click="onOk">{{okText}}</button>
+                <button v-if="ok2" class="ui-button submit" @click="onOk2">{{ok2Text}}</button>
             </div>
         </div>
     </popup>
@@ -21,25 +37,29 @@
 <script>
 import {i18n} from "@/lang"
 import Popup from "../popup";
-import config from '@/config'
 export default {
-    name: "ui-alert",
+    name: "ui-confirm",
     components: {
         Popup
     },
     data() {
         return {
-            isMaterial: window.location.pathname.indexOf('material.html')>-1,
-            isMobile:config.isMobile(),
+            isMaterial: window.location.pathname.indexOf('material.html')>-1 || window.location.pathname.indexOf('showMobile.html')>-1 || window.location.pathname.indexOf('show.html')>-1,
             show: false,
-            forceOK:false,
+            showCloseIcon: true,
             duration: 0,
             title: i18n.t("tips.title"),
-            icon: null,
             tips: "",
+            icon: null,
             content: "",
             okText: i18n.t("common.set"),
-            ok: null
+            ok2Text: i18n.t("common.set"),
+            noText: i18n.t("common.giveup"),
+            okLink: null,
+            noLink: null,
+            ok: null,
+            ok2: null,
+            no: null
         };
     },
     methods: {
@@ -47,14 +67,19 @@ export default {
             if (this.ok && this.ok(this) === false) {
                 return;
             }
-            setTimeout(() => {
-                this.show = false;
-                document.body.removeChild(this.$el);
-                this.$destroy();
-            }, this.duration);
+            this.onClose();
+        },
+        onOk2() {
+          if (this.ok2 && this.ok2(this) === false) {
+              return;
+          }
+          this.onClose();
+        },
+        onNo() {
+            this.no && this.no();
+            this.onClose();
         },
         onClose() {
-            this.forceOK && (this.ok && this.ok(this) === false)
             setTimeout(() => {
                 this.show = false;
                 document.body.removeChild(this.$el);
@@ -63,4 +88,11 @@ export default {
         }
     }
 };
-</script>
+</script>
+
+<style lang="less" scoped>
+.ui-message {
+  width: 400px;
+  height: 230px;
+}
+</style>

+ 1 - 1
packages/qjkankan-editor/src/components/shared/message/index.js

@@ -1,6 +1,6 @@
 import Vue from 'vue'
 import UITips from './Tips.vue'
-import UIAlert from './Confirm.vue'
+import UIAlert from './Alert.vue'
 import UIConfirm from './Confirm.vue'
 
 export const Tips = Vue.extend(UITips)

+ 3 - 2
packages/qjkankan-editor/src/framework/EditorHead.vue

@@ -5,12 +5,12 @@
       返回我的作品
     </a>
     <span class="app-head-title">{{ info.name }}</span>
-    <div class="app-head-save ui-button deepcancel app-head-view" @click="onView" :class="{ disable: !canLoad }">
+    <div class="app-head-save ui-button deepcancel app-head-view" @click="onView" :class="{ disable: !canLoad || isEditing }">
       <i class="iconfont iconeditor_preview"></i>
       预览
     </div>
 
-    <div class="ui-button submit app-head-save" @click="onSave" :class="{ disable: !canLoad }">
+    <div class="ui-button submit app-head-save" @click="onSave" :class="{ disable: !canLoad || isEditing }">
       <i class="iconfont iconeditor_save"></i>
       保存
     </div>
@@ -54,6 +54,7 @@ export default {
       isShow: "isShow",
       catalogTopology: "catalogTopology",
       currentScene: "scene/currentScene",
+      isEditing: "isEditing",
     }),
   },
   methods: {

+ 15 - 0
packages/qjkankan-editor/src/framework/MenuPC.vue

@@ -9,6 +9,11 @@
         </router-link>
       </li>
     </ul>
+
+    <a class="help" href="https://docs.4dkankan.com/#/product/4dpano/zh-cn/README" target="_blank">
+      <img v-tooltip="'帮助中心'"  :src="require(`@/assets/images/icons/help_tips.png`)" alt="">
+    </a>
+
   </div>
 </template>
 <script>
@@ -98,5 +103,15 @@ export default {
       font-size: 28px;
     }
   }
+  .help{
+    position: absolute;
+    bottom: 10px;
+    left: 50%;
+    width: 16px;
+    transform: translateX(-50%);
+    >img{
+      width: 100%;
+    }
+  }
 }
 </style>

+ 2 - 2
packages/qjkankan-editor/src/framework/material/header.vue

@@ -79,9 +79,9 @@ export default {
     .logo {
       font-size: 24px;
       font-weight: bold;
-      width: 188px;
+      height: 100%;
       >img{
-        width: 100%;
+        height: 100%;
         vertical-align: middle;
       }
     }

+ 70 - 30
packages/qjkankan-editor/src/framework/play/pano/components/list.vue

@@ -1,43 +1,49 @@
 <template>
   <div class="bar-list" v-if="show" :class="{ disable: isEditing }">
-    <div class="top-con">
-      <div class="swiper-container" id="swScenes" v-if="currentScenesList.length > 0">
+    <div class="top-con" :style="`width:${Math.max(scenesListW, secondaryW) + 120}px`">
+      <div class="swiper-container" :style="`width:${scenesListW}px`" id="swScenes" ref="sw"
+        v-swiper:mySwiper="swiperOptions" v-if="currentScenesList.length > 0">
         <ul class="swiper-wrapper">
-          <li v-tooltip="item.type==='4dkk'?'请前往四维看看个人中心编辑场景':''" @click="tabCurrentScene(item)" class="swiper-slide"
-            :class="{
+          <li v-tooltip="item.type === '4dkk' ? '请前往四维时代个人中心编辑场景' : ''" @click="tabCurrentScene(item)"
+            class="swiper-slide" :class="{
               active: currentScene.id == item.id,
               loopspan: item.sceneTitle.length > spanlength && currentScene.id == item.id,
-            }" :style="{ backgroundImage: `url(${item.icon})`, opacity:item.type==='4dkk'?0.5:1,}"
+            }" :style="{ backgroundImage: `url(${item.icon})`, opacity: item.type === '4dkk' ? 0.5 : 1, }"
             v-for="(item, i) in currentScenesList" :key="i">
             <i class="iconfont icon-edit_type_3d" :class="{ iconedit_type_panorama: item.type !== '4dkk' }"></i>
             <div>
-              <span>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle
+              <span v-if="currentScene.id == item.id">{{ item.sceneTitle }}</span>
+              <span v-else>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) :
+                  item.sceneTitle
               }}</span>
             </div>
           </li>
         </ul>
       </div>
 
-      <div class="swiper-container" id="swSecondary" v-if="secondaryList.length > 1">
+      <div class="swiper-container" :style="`width:${secondaryW}px`" id="swSecondary" ref="sw1"
+        v-swiper:mySwipera="swiperOptions" v-if="secondaryList.length > 1">
         <ul class="swiper-wrapper">
           <li class="swiper-slide" @click="tabSecondary(item)" :class="{
             active: currentSecondary.id == item.id,
             loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
           }" v-for="(item, i) in secondaryList" :key="i">
-            <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+            <span v-if="currentSecondary.id == item.id">{{ item.name }}</span>
+            <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
           </li>
         </ul>
       </div>
     </div>
 
-    <div class="swiper-container" id="swcatalogRoot"
-      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
+    <div class="swiper-container" :style="`width:${catalogRootW}px`" id="swcatalogRoot" ref="sw2"
+      v-swiper:mySwiperb="swiperOptions" v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
       <ul class="swiper-wrapper" v-if="metadata.catalogRoot.length > 1">
         <li class="swiper-slide" :class="{
           active: currentCatalogRoot.id == item.id,
           loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
         }" @click="tabRoot(item)" v-for="(item, i) in metadata.catalogRoot" :key="i">
-          <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+          <span v-if="currentCatalogRoot.id == item.id">{{ item.name }}</span>
+          <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
         </li>
       </ul>
     </div>
@@ -47,13 +53,27 @@
 <script>
 import { mapGetters } from "vuex";
 
+import { directive } from "vue-awesome-swiper";
+// import style (<= Swiper 5.x)
+import "swiper/css/swiper.css";
+
+
 export default {
   data() {
     return {
       spanlength: 6,
       show: false,
+      swidth: {
+        "swcatalogRoot": 104,
+        "swSecondary": 84,
+        "swScenes": 72,
+      }
     };
   },
+  directives: {
+    swiper: directive,
+  },
+
   watch: {
     currentSecondary() {
       this.loadList();
@@ -89,23 +109,42 @@ export default {
       currentScenesList: "scene/currentScenesList",
       isEditing: "isEditing",
     }),
+
+    scenesListW() {
+      return this.currentScenesList.length * (this.swidth['swScenes'] + 10)
+    },
+    secondaryW() {
+      return this.secondaryList.length * (this.swidth['swSecondary'] + 10)
+    },
+    catalogRootW() {
+      return this.metadata.catalogRoot.length * (this.swidth['swcatalogRoot'] + 10)
+    },
+    swiperOptions() {
+      return {
+        slidesPerView: "auto",
+        centeredSlides: true,
+        centerInsufficientSlides: true,
+        centeredSlidesBounds: true,
+        freeMode: true
+      };
+    },
   },
   methods: {
     loadList() {
       this.$nextTick(() => {
-        let t = setTimeout(() => {
-          clearTimeout(t);
-          ["#swcatalogRoot", "#swSecondary", "#swScenes"].forEach((item) => {
-            new Swiper(item, {
-              slidesPerView: "auto",
-              spaceBetween: 10,
-              centeredSlides: true,
-              centerInsufficientSlides: true,
-              centeredSlidesBounds: true,
-              freeMode: true,
-            });
-          });
-        }, 100);
+        // let t = setTimeout(() => {
+        //   clearTimeout(t);
+        //   ["#swcatalogRoot", "#swSecondary", "#swScenes"].forEach((item) => {
+        //     new Swiper(item, {
+        //       slidesPerView: "auto",
+        //       spaceBetween: 10,
+        //       centeredSlides: true,
+        //       centerInsufficientSlides: true,
+        //       centeredSlidesBounds: true,
+        //       freeMode: true,
+        //     });
+        //   });
+        // }, 100);
       });
     },
 
@@ -143,15 +182,16 @@ export default {
   text-align: center;
   max-width: @width;
   overflow: hidden;
-  min-width: 480px;
   transition: 0.3s all ease;
-
   .swiper-container {
     width: 100%;
     position: relative;
 
     >ul {
+      margin: 0 auto;
       >li {
+        margin: 0 5px;
+        white-space: nowrap;
 
         >span,
         >div>span {
@@ -180,10 +220,10 @@ export default {
   }
 
   .top-con {
-    margin-bottom: 10px;
-    padding: 10px 30px;
-    min-width: 400px;
-    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 8%, rgba(0, 0, 0, 0.5) 92%, rgba(0, 0, 0, 0) 100%);
+    margin: 0 auto 10px;
+    padding: 10px 0;
+    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.4) 25%, rgba(0, 0, 0, 0.4) 75%, rgba(0, 0, 0, 0) 100%);
+
   }
 
   #swcatalogRoot {

+ 28 - 3
packages/qjkankan-editor/src/framework/play/pano/index.vue

@@ -1,9 +1,13 @@
 <template>
   <div class="panocon">
     <div id="pano"></div>
+
+    <div class="showexplanation" v-if="showExplanation&&currentScene.explanation && currentScene.explanation.audioId">
+      <img :src="require(`@/assets/images/commentary@2x.png`)" alt="">
+    </div>
     <list></list>
 
-    <template v-if="showSnapshot&&currentScene">
+    <template v-if="showSnapshot && currentScene">
       <snapshot :showFlash="showFlash"></snapshot>
       <button class="ui-button submit" @click="onClick">将当前视角设为初始画面</button>
     </template>
@@ -39,6 +43,9 @@ export default {
     showSnapshot() {
       return this.$route.name == "screen";
     },
+    showExplanation() {
+      return this.$route.name == "explanation";
+    },
   },
   watch: {
     '$route.name': function (newVal) {
@@ -99,8 +106,8 @@ export default {
         this.inter = setInterval(() => {
           __krfn.utils.getCurrentMousePosition(this.$getKrpano(), newVal)
         }, 20);
-      } else{
-        this.$bus.emit('resethotspotTitle','')
+      } else {
+        this.$bus.emit('resethotspotTitle', '')
       }
     }
 
@@ -171,6 +178,24 @@ export default {
     height: 100%;
   }
 
+  .showexplanation {
+    position: absolute;
+    top: 20px;
+    z-index: 999;
+    right: 20px;
+    width: 35px;
+    height: 35px;
+    background: rgba(0, 0, 0, 0.4);
+    border-radius: 50%;
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    backdrop-filter: blur(6px);
+
+    >img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
   .ui-button {
     position: absolute;
     bottom: 260px;

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 288 - 290
packages/qjkankan-editor/src/utils/browser.js


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

@@ -132,7 +132,7 @@ export default {
   },
   methods: {
     emojistr() {
-      this.info.name = this.info.name.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function (char) {
+      this.info.name = this.info.name.replace(/(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/g, function (char) {
         if (char.length === 2) {
           return ""
         } else {

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

@@ -9,7 +9,7 @@
     <div class="bottom" :class="{disabled: !info.isLogo}">
         <SelectedImage
           :imgSrc="info.logo"
-          :defaultImgSrc="require('@/assets/images/default/img_logo_default.png')"
+          :defaultImgSrc="require('@/assets/images/default/logo_white.svg')"
           @cancel="onClickCancelCustomLogo"
         ></SelectedImage>
       <div class="bottom-right">

+ 39 - 15
packages/qjkankan-editor/src/views/base/openingAnimationSettings.vue

@@ -4,23 +4,16 @@
     <br>
     <div class="btns-and-video">
       <div class="btn-wrapper">
-        <button
-          v-for="item of openingTypeList" :key="item"
-          class="opening-selection-btn"
-          :class="{'active-opening-type': info.openingAnimationType === item}"
-          @click="info.openingAnimationType = item"
-        >
-          {{item}}
+        <button v-for="item of openingTypeList" :key="item" class="opening-selection-btn"
+          :class="{ 'active-opening-type': info.openingAnimationType === item }"
+          @click="info.openingAnimationType = item">
+          {{ item }}
         </button>
       </div>
       <div class="video-wrapper">
-        <video
-          v-for="item of openingTypeList" :key="item"
-          v-show="item === info.openingAnimationType"
-          :src="require(`@/assets/videos/${item}.mp4`)"
-          autoplay
-          loop
-        ></video>
+        <video ref="opvideo" :src="require(`@/assets/videos/${info.openingAnimationType || '小行星开场'}_x264.mp4`)" autoplay
+          loop></video>
+          <i v-if="bofanging" @click="bofang" class="iconfont iconshow_playback"></i>
       </div>
     </div>
   </div>
@@ -41,19 +34,35 @@ export default {
         '水平巡游开场',
         '水晶球开场',
       ],
+      bofanging: true
     }
   },
   computed: {
     ...mapGetters({
-      info:'info'
+      info: 'info'
     })
   },
   methods: {
+    bofang(){
+      this.$refs.opvideo.play()
+    }
   },
   mounted() {
     if (!this.info.openingAnimationType) {
       this.$set(this.info, 'openingAnimationType', this.openingTypeList[0])
     }
+
+    this.$nextTick(()=>{
+      console.log(this.$refs.opvideo);
+
+    this.$refs.opvideo && this.$refs.opvideo.addEventListener("playing", () => {
+      if (this.bofanging) {
+        this.bofanging = false;
+      }
+
+    });
+  })
+
   },
 }
 </script>
@@ -63,14 +72,17 @@ export default {
   padding: 24px 30px;
   background: #252526;
   height: 546px;
+
   .title {
     font-size: 18px;
     color: #FFFFFF;
   }
+
   .btns-and-video {
     margin-top: 16px;
     display: flex;
     align-items: flex-start;
+
     .btn-wrapper {
       .opening-selection-btn {
         width: 180px;
@@ -84,6 +96,7 @@ export default {
         color: #fff;
         cursor: pointer;
         display: block;
+
         &::after {
           content: '';
           height: 100%;
@@ -91,19 +104,30 @@ export default {
           display: inline-block;
         }
       }
+
       .active-opening-type {
         color: #0076F6;
       }
     }
+
     .video-wrapper {
       width: 469px;
       height: 264px;
       margin-left: 20px;
+      position: relative;
       video {
         width: 100%;
         height: 100%;
         object-fit: cover;
       }
+      .iconfont{
+        position: absolute;
+        font-size: 36px;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%,-50%);
+        cursor: pointer;
+      }
     }
   }
 }

+ 16 - 13
packages/qjkankan-editor/src/views/hotspot/EditPanel.vue

@@ -219,11 +219,14 @@ export default {
   },
   beforeDestroy() {
     this.$bus.removeListener('selectUrl', this.listerFn)
+    this.$bus.removeListener('resethotspotTitle', this.listerFnReset)
   },
   computed: {
     ...mapGetters({
       hotspot: 'hotspot',
-      backupHotSpot: 'backupHotSpot'
+      backupHotSpot: 'backupHotSpot',
+      currentHPName: "tags/currentHPName"
+
     }),
     effectSettingComponent() {
       let tmp = this.hotspot.hotspotType
@@ -235,9 +238,7 @@ export default {
       sceneCode: this.infoItem.link
     }
     this.$bus.on('selectUrl', this.listerFn)
-    this.$bus.on('resethotspotTitle', () => {
-      this.hotspot.hotspotTitle = ''
-    })
+    this.$bus.on('resethotspotTitle', this.listerFnReset)
 
     this.$bus.on('delhotspot', () => {
       this.cancel()
@@ -256,7 +257,10 @@ export default {
 
   methods: {
     handleSceneSelect(data) {
-      this.hotspot.secne = data
+      this.hotspot.secne = {
+        ...data,
+        someData: {}
+      }
     },
     onhotSpotTypeChange(data) {
       this.hotspot.hotspotType = data.id
@@ -283,6 +287,12 @@ export default {
           break;
       }
     },
+    listerFnReset(){
+      if (this.hotspot.hotspotTitle == '单击确定热点位置') {
+        this.hotspot.hotspotTitle = ''
+      }
+    },
+    
     listerFn(data) {
       this.selectItem = {
         sceneCode: data.sceneCode
@@ -304,12 +314,6 @@ export default {
       let flag = false
       let { hotspotTitle, hotspotType } = this.hotspot
       let item = HTMap[hotspotType]
-      console.log(
-        (hotspotTitle.trim() && hotspotTitle.trim() != '单击确定热点位置'),
-        this.hotspot[item.key],
-        (item.type == 'Array' && this.hotspot[item.key].length > 0)
-      );
-
       if (
         (hotspotTitle.trim() && hotspotTitle.trim() != '单击确定热点位置')
         || this.hotspot[item.key]
@@ -321,7 +325,6 @@ export default {
     },
 
     confirmCancel() {
-      console.log(this.isAddChange());
       if (this.editTitle != '编辑') {
         if (this.isAddChange()) {
           this.$confirm({
@@ -330,7 +333,7 @@ export default {
               this.cancel()
             }
           });
-        } else{
+        } else {
           this.cancel()
         }
       } else {

+ 49 - 42
packages/qjkankan-editor/src/views/hotspot/HotSpotList.vue

@@ -2,13 +2,9 @@
   <div class="hot-spot-list" app-border dir-left>
     <div class="title">
       热点设置
-      <i class="iconfont icon-material_prompt tool-tip-for-editor" v-tooltip="'在全景图中添加图标热点,并设置热点的效果。'"/>
+      <i class="iconfont icon-material_prompt tool-tip-for-editor" v-tooltip="'在全景图中添加图标热点,并设置热点的效果。'" />
     </div>
-    <button
-      class="ui-button submit"
-      :class="{ disable: !currentScene }"
-      @click="open(null)"
-    >
+    <button class="ui-button submit" :class="{ disable: !currentScene }" @click="open(null)">
       添加热点
     </button>
 
@@ -17,23 +13,13 @@
     </div>
     <div class="hots">
       <ul v-if="someData.hotspots.length > 0">
-        <li
-          v-for="(item, key) in someData.hotspots"
-          :key="key"
-          @click="open(item)"
-        >
+        <li v-for="(item, key) in someData.hotspots" :key="key" @click="open(item)">
           <img class="hot-spot-thumb" :src="item.img" alt="">
           <span class="hot-spot-title" v-title="item.hotspotTitle">{{ item.hotspotTitle }}</span>
-          <i
-            class="iconfont icon-editor_list_delete icon-delete"
-            v-tooltip="'删除'"
-            @click.stop="deleIndex = key"
-          />
+          <i class="iconfont icon-editor_list_delete icon-delete" v-tooltip="'删除'" @click.stop="deleIndex = key" />
           <div class="deletion-confirm-wrap">
-            <div class="deletion-confirm" :class="deleIndex == key ? 'show' : 'hide'"
-              v-clickoutside="clickoutside"
-              @click.stop="deleteHot(item)"
-            >
+            <div class="deletion-confirm" :class="deleIndex == key ? 'show' : 'hide'" v-clickoutside="clickoutside"
+              @click.stop="deleteHot(item)">
               删除
             </div>
           </div>
@@ -44,14 +30,8 @@
         <div>暂无热点信息~</div>
       </div>
     </div>
-    <EditPanel
-      class="adding-hotspot-panel"
-      v-if="showPanel"
-      :editTitle="editTitle"
-      @save="save"
-      :show="showPanel"
-      @close="close"
-    ></EditPanel>
+    <EditPanel class="adding-hotspot-panel" v-if="showPanel" :editTitle="editTitle" @save="save" :show="showPanel"
+      @close="close"></EditPanel>
   </div>
 </template>
 
@@ -62,10 +42,10 @@ import browser from "@/utils/browser"
 
 
 let mapFontSize = {
-  12:0.5,
-  14:1,
-  17:1.5,
-  20:2,
+  12: 0.5,
+  14: 1,
+  17: 1.5,
+  20: 2,
 }
 
 export default {
@@ -77,7 +57,7 @@ export default {
     ...mapGetters({
       currentScene: "scene/currentScene",
       hotspot: 'hotspot',
-      info: "info"
+      info: "info",
     }),
   },
   data() {
@@ -130,8 +110,12 @@ export default {
 
     this.$bus.on("openHotspot", (data) => {
       let idx = this.someData.hotspots.findIndex((item) => item.name == data)
+      console.log(data);
       if (data == this.hotspot.name) {
         window.__krfn.utils.looktohotspot(this.$getKrpano(), this.hotspot.name)
+        if (!this.showPanel) {
+          this.open(this.someData.hotspots[idx])
+        }
         return
       }
       if (this.editTitle == '新增') {
@@ -220,7 +204,7 @@ export default {
     },
     open(data) {
       this.editTitle = "新增"
-      let temp = data ? browser.CloneObject(data) : {
+      let temp = data ? browser.CloneObject(data)  : {
         name: "_" + this.$randomWord(true, 8, 8),
         hotspotTitle: '单击确定热点位置',
         fontSize: 12,
@@ -266,29 +250,35 @@ export default {
   flex-direction: column;
   background: #252526;
   position: relative;
-  > .title {
+
+  >.title {
     font-size: 18px;
     color: #fff;
     flex: 0 0 auto;
-    > i {
+
+    >i {
       font-size: 12px;
       position: relative;
       top: -2px;
     }
   }
-  > button {
+
+  >button {
     flex: 0 0 auto;
     width: 100%;
     margin-top: 16px;
+
     i {
       font-size: 14px;
     }
   }
+
   .total-count {
     flex: 0 0 auto;
     margin-top: 24px;
     font-size: 18px;
     color: #FFFFFF;
+
     .number {
       font-size: 14px;
       color: rgba(255, 255, 255, 0.6);
@@ -296,6 +286,7 @@ export default {
       top: -1px;
     }
   }
+
   .hots {
     flex: 1 0 1px;
     margin-top: 16px;
@@ -305,8 +296,10 @@ export default {
     border: 1px solid #404040;
     position: relative;
     overflow: auto;
+
     ul {
       padding: 10px;
+
       li {
         position: relative;
         height: 40px;
@@ -314,32 +307,39 @@ export default {
         display: flex;
         align-items: center;
         padding: 0 5px 0 10px;
+
         &:hover {
           background: #252526;
+
           .icon-delete {
             display: block;
           }
         }
-        > .hot-spot-thumb {
+
+        >.hot-spot-thumb {
           width: 18px;
         }
-        > .hot-spot-title {
+
+        >.hot-spot-title {
           flex: 1 1 auto;
           margin-left: 10px;
           text-overflow: ellipsis;
           overflow: hidden;
           white-space: nowrap;
         }
-        > .icon-delete {
+
+        >.icon-delete {
           margin-left: 12px;
           display: none;
           cursor: pointer;
           padding: 5px;
+
           &:hover {
             color: #fa5555;
           }
         }
-        > .deletion-confirm-wrap {
+
+        >.deletion-confirm-wrap {
           position: absolute;
           top: 0;
           bottom: 0;
@@ -349,7 +349,8 @@ export default {
           pointer-events: none;
           border-top-right-radius: 2px;
           border-bottom-right-radius: 2px;
-          > .deletion-confirm {
+
+          >.deletion-confirm {
             position: absolute;
             top: 0;
             bottom: 0;
@@ -361,15 +362,18 @@ export default {
             font-size: 12px;
             color: #fff;
             pointer-events: auto;
+
             &::after {
               content: '';
               height: 100%;
               vertical-align: middle;
               display: inline-block;
             }
+
             &.show {
               right: 0;
             }
+
             &.hide {
               right: -44px;
             }
@@ -385,9 +389,11 @@ export default {
       width: 100%;
       top: 50%;
       transform: translateY(-50%);
+
       img {
         width: 125px;
       }
+
       div {
         margin-top: 20px;
         color: rgba(255, 255, 255, 0.6);
@@ -399,6 +405,7 @@ export default {
   .ui-button {
     width: 100%;
   }
+
   .adding-hotspot-panel {
     position: absolute;
     top: 0;

+ 4 - 4
packages/qjkankan-editor/src/views/hotspot/hotspotType/textarea.vue

@@ -3,13 +3,13 @@
     <div class="textarea-wrapper">
       <!-- <textarea
         v-model.trim="text"
-        maxlength="200"
-        placeholder="请输入文字内容,限200字"
+        maxlength="500"
+        placeholder="请输入文字内容,限500字"
         type="text"
       /> -->
 
-      <editor ref="editor" :html="text" :placeholder="'请输入文字内容,限200字'" :maxlength="200" @change="onEditorChange"></editor>
-      <span class="count">{{size}}/200</span>
+      <editor ref="editor" :html="text" :placeholder="'请输入文字内容,限500字'" :maxlength="500" @change="onEditorChange"></editor>
+      <span class="count">{{size}}/500</span>
     </div>
   </div>
 </template>

+ 12 - 1
packages/qjkankan-editor/src/views/material/audio/index.vue

@@ -7,7 +7,7 @@
       <div class="btn">
         <button
           @mouseover.stop="showList = true"
-          @click="$refs.uploadFile.click()"
+          @click="onUploadFile"
           class="ui-button submit"
         >
           <span>上传素材</span>
@@ -125,6 +125,7 @@ import {
   uploadMaterial,
   editMaterial,
   delMaterial,
+  checkUserSize
 } from "@/api";
 
 const TYPE = "audio";
@@ -179,6 +180,16 @@ export default {
     },
   },
   methods: {
+    onUploadFile(){
+      checkUserSize({},(data)=>{
+        //判断已用是否大于3G
+        if ((data.data / 1024 / 1024) > 3) {
+          this.$refs.uploadFile.click()
+        }else{
+          this.$alert({ content: "空间已满" });
+        }
+      })
+    },
     onFilterFocus() {
       this.isFilterFocus = true
     },

+ 13 - 2
packages/qjkankan-editor/src/views/material/image/index.vue

@@ -7,7 +7,7 @@
       <div class="btn">
         <button
           @mouseover.stop="showList = true"
-          @click="$refs.uploadFile.click()"
+          @click="onUploadFile"
           class="ui-button submit"
         >
           <span>上传素材</span>
@@ -77,7 +77,7 @@
           <div v-else-if="sub.type == 'image'" class="img">
             <img
               :id="'img' + item.id"
-              :src="data + `?x-oss-process=image/resize,p_10&${Math.random()}`"
+              :src="data + (Number(item.fileSize)>512 ? `?x-oss-process=image/resize,p_10&${Math.random()}` : '') "
               alt=""
               @click="previewImage(item)"
             />
@@ -135,6 +135,7 @@ import {
   uploadMaterial,
   editMaterial,
   delMaterial,
+  checkUserSize
 } from "@/api";
 
 const TYPE = "image";
@@ -188,6 +189,16 @@ export default {
     },
   },
   methods: {
+    onUploadFile(){
+      checkUserSize({},(data)=>{
+        //判断已用是否大于3G
+        if ((data.data / 1024 / 1024) > 3) {
+          this.$refs.uploadFile.click()
+        }else{
+          this.$alert({ content: "空间已满" });
+        }
+      })
+    },
     onFilterFocus() {
       this.isFilterFocus = true
     },

+ 15 - 2
packages/qjkankan-editor/src/views/material/pano/index.vue

@@ -7,7 +7,7 @@
       <div class="btn">
         <button
           @mouseover.stop="showList = true"
-          @click="$refs.uploadFile.click()"
+          @click="onUploadFile"
           class="ui-button submit"
         >
           <span>上传素材</span>
@@ -93,7 +93,9 @@
             v-else-if="sub.type == 'image'"
             @click="previewImage(item)"
           >
-            <img :src="data + `?x-oss-process=image/resize,p_10&${Math.random()}`" alt="" />
+            <img 
+            :src="data + (Number(item.fileSize)>512 ? `?x-oss-process=image/resize,p_10&${Math.random()}` : '') "
+            alt="" />
           </div>
           <span style="cursor: pointer;" @click="previewImage(item)" v-else-if="sub.key == 'name'">{{ data || "-" }}
           </span>
@@ -158,6 +160,7 @@ import {
   delMaterial,
   uploadCover,
   checkMStatus,
+  checkUserSize
 } from "@/api";
 
 const TYPE = "pano";
@@ -234,6 +237,16 @@ export default {
     },
   },
   methods: {
+    onUploadFile(){
+      checkUserSize({},(data)=>{
+        //判断已用是否大于3G
+        if ((data.data / 1024 / 1024) > 3) {
+          this.$refs.uploadFile.click()
+        }else{
+          this.$alert({ content: "空间已满" });
+        }
+      })
+    },
     onFilterFocus() {
       this.isFilterFocus = true
     },

+ 1 - 1
packages/qjkankan-editor/src/views/material/popup/rename.vue

@@ -42,7 +42,7 @@ export default {
   },
   methods: {
     emojistr() {
-      this.key = this.key.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function (char) {
+      this.key = this.key.replace(/(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/g, function (char) {
         if (char.length === 2) {
           return ""
         } else {

+ 12 - 1
packages/qjkankan-editor/src/views/material/video/index.vue

@@ -7,7 +7,7 @@
       <div class="btn">
         <button
           @mouseover.stop="showList = true"
-          @click="$refs.uploadFile.click()"
+          @click="onUploadFile"
           class="ui-button submit"
         >
           <span>上传素材</span>
@@ -134,6 +134,7 @@ import {
   uploadMaterial,
   editMaterial,
   delMaterial,
+  checkUserSize
 } from "@/api";
 
 const TYPE = "video";
@@ -188,6 +189,16 @@ export default {
     },
   },
   methods: {
+    onUploadFile(){
+      checkUserSize({},(data)=>{
+        //判断已用是否大于3G
+        if ((data.data / 1024 / 1024) > 3) {
+          this.$refs.uploadFile.click()
+        }else{
+          this.$alert({ content: "空间已满" });
+        }
+      })
+    },
     previewVedio(clickItem) {
       const index = this.list.findIndex((eachItem) => {
         return eachItem.id === clickItem.id

+ 3 - 1
packages/qjkankan-editor/src/views/material/works/index.vue

@@ -236,7 +236,9 @@ export default {
             {
               id: this.newWorkId,
               password: '',
-              someData: { ...this.info, status: 1 },
+              someData: { ...this.info,
+                 status: 1,
+                icon: this.info.scenes[0].icon },
             },
             // 保存成功
             () => {

+ 1 - 2
packages/qjkankan-kankan-view/src/components/Controls/BottomControl.vue

@@ -2,7 +2,6 @@
     <div class="bottom-controls" :class="{ hidden: isHidden }" :style="{ bottom }" v-show="show">
         <FloorSwitch />
         <tours />
-        <!-- <RightButtons /> -->
     </div>
 </template>
 <script setup>
@@ -12,7 +11,6 @@ import { ref } from 'vue'
 import { useApp, getApp } from '@/app'
 import FloorSwitch from './FloorSwitch'
 import tours from './tours'
-import RightButtons from './RightButtons'
 import { useMusicPlayer } from '@/utils/sound'
 
 const musicPlayer = useMusicPlayer()
@@ -32,6 +30,7 @@ useApp().then(app => {
 musicPlayer.on('play', () => (showMusicPlaying.value = true))
 musicPlayer.on('pause', () => (showMusicPlaying.value = false))
 </script>
+
 <style lang="scss" scoped>
 .bottom-controls {
     width: 100%;

+ 21 - 372
packages/qjkankan-kankan-view/src/components/Controls/Control.Mobile.vue

@@ -1,386 +1,35 @@
 <template>
   <div>
     <FloorSwitch />
-    <!-- <div v-show="player.showWidgets" class="toolbar" :class="{ collapse: isCollapse || player.showToolbar == false || player.showVR }" @touchmove.prevent @touchstart.stop> -->
-    <div
-      v-show="player.showWidgets"
-      v-if="
-        tours.length > 0 ||
-        (controls.showPanorama && !controls.showMap) ||
-        (controls.showFloorplan && !controls.showMap) ||
-        (controls.showDollhouse && !controls.showMap)
-      "
-      class="toolbar"
-      :class="{ collapse: isCollapse || !player.showToolbar || player.showVR }"
-      @touchstart.stop
-    >
-      <div v-if="tours.length > 0" class="tour-control" :class="{ only: controls.showMap }">
-        <div class="btn-box">
-          <ui-icon :type="isPlay ? 'pause' : 'preview'" @click.stop="playTour"></ui-icon>
-          <div @click.stop="openTours" class="btn-text">
-            <!-- <span>{{ $t('common.tour') }}</span> -->
-            <ui-icon class="pull-icon" :class="{ up: showTours }" type="pull-down"></ui-icon>
-          </div>
-        </div>
-        <!-- <div class="part-content" :class="{ ban: flying || isSelect }" v-show="!isCollapse && player.showToolbar && !player.showVR && showTours"> -->
-        <transition
-          appear
-          name="custom-classes-transition"
-          enter-active-class="animated fadeInUp short faster"
-          leave-active-class="animated fadeOutDown short faster"
-        >
-          <div class="part-content" v-show="!isCollapse && player.showToolbar && !player.showVR && showTours">
-            <div class="slide-box" ref="tourScroll">
-              <div class="tour-list part-list" v-if="tours.length > 1">
-                <div
-                  class="tour-item"
-                  @click="changeFrame(1, index)"
-                  :class="{ disabled: isPlay && partId != index }"
-                  :name="index"
-                  :style="`background-image:url(${i.frameId ? common.changeUrl(i.list[i.frameId].enter.cover) : common.changeUrl(i.list[0].enter.cover)});`"
-                  v-for="(i, index) in tours"
-                >
-                  <div class="mask-item">
-                    <span class="name">{{ i.name }}</span>
-                    <div v-if="partId == index && progressNum > 0" class="precent" :style="`width:${progressNum}%;`"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="tour-list farm-list" v-else>
-                <div
-                  class="tour-item"
-                  @click="changeFrame(2, index)"
-                  :class="{ disabled: isPlay && frameId != index }"
-                  v-for="(i, index) in tours[0].list"
-                  :name="index"
-                  :style="`background-image:url(${common.changeUrl(i.enter.cover)});`"
-                >
-                  <div class="mask-item">
-                    <span class="name">{{ i.name }}</span>
-                    <div v-if="frameId == index && progressNum > 0" class="precent" :style="`width:${progressNum}%;`"></div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-        </transition>
-      </div>
-      <div class="ctrls" v-if="!controls.showMap" :class="{ disabled: flying || isPlay }">
-        <div :class="{ active: mode == 'panorama' }" @click="onModeSwitch('panorama')" v-if="controls.showPanorama">
-          <i class="iconfont" :class="[mode == 'panorama' ? 'icon-show_roaming_selected' : 'icon-show_roaming_normal']"></i>
-          <!-- <span :class="{ show: modeTips == 'panorama' }">{{ $t('mode.panorama') }}</span> -->
-        </div>
-        <div :class="{ active: mode == 'floorplan' }" @click="onModeSwitch('floorplan')" v-if="controls.showFloorplan">
-          <i class="iconfont" :class="[mode == 'floorplan' ? 'icon-show_plane_selected' : 'icon-show_plane_normal']"></i>
-          <!-- <span :class="{ show: modeTips == 'floorplan' }">{{ $t('mode.floorplan') }}</span> -->
-        </div>
-        <div :class="{ active: mode == 'dollhouse' }" @click="onModeSwitch('dollhouse')" v-if="controls.showDollhouse">
-          <i class="iconfont" :class="[mode == 'dollhouse' ? 'icon-show_3d_selected' : 'icon-show_3d_normal']"></i>
-          <!-- <span :class="{ show: modeTips == 'dollhouse' }">{{ $t('mode.dollhouse') }}</span> -->
-        </div>
-      </div>
-      <div class="switch" @click="onCollapse()">
-        <i class="iconfont icon-show_function_collect"></i>
-      </div>
-    </div>
+    <tours />
   </div>
 </template>
-
 <script setup>
-import { useStore } from "vuex";
-import { onMounted, watch, computed, ref, nextTick } from "vue";
-import { useApp, getApp } from "@/app";
-import FloorSwitch from "./FloorSwitch";
-// import { Scrollbar, Dialog } from '@qjkankan/components'
-import { Scrollbar, Dialog } from "@/global_components/";
-const store = useStore();
-const tourScroll = ref(null);
-import common from "@/utils/common";
-import { useMusicPlayer } from "@/utils/sound";
-const musicPlayer = useMusicPlayer();
-const metadata = computed(() => store.getters["scene/metadata"]);
-const flying = computed(() => store.getters["flying"]);
-const controls = computed(() => {
-  return metadata.value.controls;
-});
-const player = computed(() => store.getters["player"]);
-const mode = computed(() => store.getters["mode"]);
-let timer = null;
-const modeTips = ref("");
-const isCollapse = ref(false);
-const progressNum = ref(0);
-const isInit = ref(false);
-const tours = computed(() => {
-  let tours = store.getters["tour/tours"];
-  if (tours.length > 0) {
-    if (tourScroll.value && !isInit.value) {
-      isInit.value = true;
-      new Scrollbar(tourScroll.value, { onlyHorizontal: true });
-    }
-  }
-  return tours;
-});
-const isSelect = ref(false);
-const labelTimer = ref(null);
-const showTours = computed(() => store.getters["tour/showTours"]);
-const partId = computed(() => {
-  let id = store.getters["tour/partId"];
-  if (isPlay.value) {
-    slideScroll();
-  }
-  return id;
-});
-
-const frameId = computed(() => {
-  let id = store.getters["tour/frameId"];
-  if (isPlay.value) {
-    slideScroll();
-  }
-  return id;
-});
-const isPlay = computed(() => {
-  let status = store.getters["tour/isPlay"];
-  let map = document.querySelector(".kankan-app div[xui_min_map]");
-  if (map) {
-    if (status) {
-      map.classList.add("disabled");
-    } else {
-      map.classList.remove("disabled");
-    }
-  }
-  return status;
-});
-const onModeSwitch = (mode) => {
-  if (labelTimer.value) {
-    modeTips.value = "";
-    clearTimeout(labelTimer.value);
-  }
-  modeTips.value = mode;
-  labelTimer.value = setTimeout(() => {
-    modeTips.value = "";
-  }, 2000);
-  console.log(mode);
-  // flyToNewMode(mode)
-  store.commit("setMode", mode);
-};
-
-const onCollapse = () => {
-  if (player.value.showToolbar == false) {
-    isCollapse.value = false;
-    let show = !player.value.showToolbar;
-    store.commit("SetPlayerOptions", {
-      showMore: false,
-      showMap: show == false,
-      showToolbar: show,
-      showDescription: false,
-    });
-  } else {
-    isCollapse.value = !isCollapse.value;
-    store.commit("tour/setData", { showTours: false });
-  }
-};
-const playTour = async () => {
-  let player = await getApp().TourManager.player;
-  if (isPlay.value) {
-    store.commit("tour/setData", { isPlay: true });
-    player.pause();
-  } else {
-    store.commit("tour/setData", { isPlay: true });
-    player.play(partId.value);
-  }
-};
-
-const hanlderTourPartPlay = (time) => {
-  if (!timer) {
-    timer = KanKan.Animate.transitions.start((progress) => {
-      progressNum.value = progress * 100;
-    }, time);
-  }
-};
-const cancelTimer = () => {
-  if (timer) {
-    KanKan.Animate.transitions.cancel(timer);
-    timer = null;
-  }
-};
-const slideScroll = (show) => {
-  nextTick(() => {
-    let t = setTimeout(() => {
-      clearTimeout(t);
-      let id = tours.value.length > 1 ? partId.value : frameId.value;
-      let item = document.querySelector(`.tour-item[name="${id}"]`);
-
-      // let itemLeft = (item.offsetWidth + 10) * (id + 1)
-      // let Scroll = document.querySelector('.part-content .x-scrollbar__container')
-      // // let contentW = slideType == 1 ? document.querySelector('.part-content').offsetWidth : document.querySelector('.iframe-content').offsetWidth
-      // let ScrollW = Scroll.offsetWidth
-      // Scroll.scrollTo({ left: itemLeft - ScrollW + 10, behavior: 'smooth' })
-
-      item.scrollIntoView({ block: "center", behavior: "smooth", inline: "center" });
-    }, 100);
-  });
-};
-const hanlderTour = async () => {
-  let player = await getApp().TourManager.player;
-  player.on("play", (data) => {
-    musicPlayer.pause(true);
-    // if (tours.value.length > 1) {
-    //     let time = getPartTime(data.partId)
-    //     hanlderTourPartPlay(time)
-    // }
-  });
-  player.on("pause", (tours) => {
-    console.log("pause", player);
-    musicPlayer.resume();
-
-    progressNum.value = 0;
-    cancelTimer();
-    store.commit("tour/setData", { isPlay: false });
-  });
-  player.on("end", (tours) => {
-    musicPlayer.resume();
-    progressNum.value = 100;
-    slideScroll();
-    store.commit("tour/setData", { isPlay: false });
-    cancelTimer();
-  });
-  let currPartId = null;
-  let currProgress = 0;
-  let currFrames = 0;
-  player.on("progress", (data) => {
-    if (tours.value.length == 1) {
-      progressNum.value = data.progress * 100;
-    } else {
-      // let time = getPartTime(data.partId)
-
-      // hanlderTourPartPlay(time)
-
-      if (currPartId != data.partId) {
-        currPartId = data.partId;
-        currFrames = tours.value[data.partId].list.length;
-        currProgress = 0;
-      } else {
-        currProgress += data.progress / currFrames;
-        if (currProgress >= 100) {
-          currProgress = 100;
-        }
+import { computed } from 'vue'
+import { useStore } from 'vuex'
+import { ref } from 'vue'
+import { useApp, getApp } from '@/app'
+import FloorSwitch from './FloorSwitch'
+import tours from './tours.mobile'
+import { useMusicPlayer } from '@/utils/sound'
 
-        progressNum.value = currProgress;
-      }
-    }
-
-    store.commit("tour/setData", { partId: data.partId, frameId: data.frameId, isPlay: true });
-  });
+const musicPlayer = useMusicPlayer()
 
-  // nextTick(() => {
-  //     editorMain.value = document.querySelector('.ui-editor-main')
-  // })
-};
-const getPartTime = (partId) => {
-  cancelTimer();
-  let time = 0;
-  // for (let i = 0; i < tours.value[partId].list.length - 1; i++) {
-  //     time += tours.value[partId].list[i].time - 0
-  // }
-  // if (tours.value[partId].list[tours.value[partId].list.length - 1]._end) {
-  //     if (tours.value[partId].list.length > 2) {
-  //         time += (tours.value[partId].list.length - 1) * 1000
-  //     }
-  // } else {
-  //     time += (tours.value[partId].list.length - 1) * 1000
-  // }
-  for (let i = 0; i < tours.value[partId].list.length; i++) {
-    if (!tours.value[partId].list[i]._end) {
-      time += tours.value[partId].list[i].time - 0;
-      if (!tours.value[partId].list[i]._notrans) {
-        time += 1000;
-      }
-    }
-  }
+const showMusicPlaying = ref(false)
 
-  return time;
-};
+const store = useStore()
+const show = ref(false)
+const isHidden = ref(false)
+const bottom = computed(() => {
+    return store.getters.controlsBottom
+})
+useApp().then(app => {
+    app.Scene.on('loaded', () => (show.value = true))
+})
 
-const openTours = () => {
-  // showTours.value = !showTours.value
-  window.parent.postMessage({ source: "4dage", event: "showTours", data: !showTours.value }, "*");
-
-  store.commit("tour/setData", { showTours: !showTours.value });
-  nextTick(() => {
-    if (isPlay.value) {
-      slideScroll();
-    }
-  });
-};
-const changeFrame = async (type, id) => {
-  if (flying.value || isSelect.value) {
-    return;
-  }
-  progressNum.value = 0;
-  // recorder.selectFrame(id)
-  let player = await getApp().TourManager.player;
-  // player.selectFrame(id)
-  isSelect.value = true;
-  if (type == 1) {
-    player.selectPart(id);
-    let f_id = 0;
-    if (tours.value[id].frameId) {
-      f_id = tours.value[id].frameId;
-    }
-    player.selectFrame(f_id).then(() => {
-      isSelect.value = false;
-    });
-    store.commit("tour/setData", {
-      frameId: f_id,
-      partId: id,
-    });
-  } else {
-    player.selectFrame(id).then(() => {
-      isSelect.value = false;
-    });
-    store.commit("tour/setData", {
-      frameId: id,
-    });
-  }
-
-  slideScroll();
-};
-const onClickHandler = async () => {
-  if (isPlay.value) {
-    let player = await getApp().TourManager.player;
-    player.pause();
-    musicPlayer.resume();
-  }
-};
-onMounted(() => {
-  useApp().then(async (sdk) => {
-    hanlderTour();
-  });
-
-  nextTick(() => {
-    let player = document.querySelector('.player[name="main"]');
-    player.addEventListener("touchstart", onClickHandler);
-
-    window.addEventListener("message", (e) => {
-      if (e.data.source != "qjkankan") {
-        return;
-      }
-
-      if (e.data.event == "showList") {
-        if (e.data.data) {
-          store.commit("tour/setData", { showTours: false });
-          nextTick(() => {
-            if (isPlay.value) {
-              slideScroll();
-            }
-          });
-        }
-      }
-    });
-  });
-});
+musicPlayer.on('play', () => (showMusicPlaying.value = true))
+musicPlayer.on('pause', () => (showMusicPlaying.value = false))
 </script>
-
 <style lang="scss" scoped>
 .disable {
   opacity: 1;

+ 440 - 0
packages/qjkankan-kankan-view/src/components/Controls/tours.mobile.vue

@@ -0,0 +1,440 @@
+<template>
+  <div class="tour-list" v-if="tours.length > 0" :class="{ ban: flying || isSelect,barshow: showTours }">
+    <div class="part-content" ref="tourScroll">
+      <!-- 多个片段 -->
+      <ul class="part-list" v-if="tours.length>1">
+        <li class="part-item" v-for="(item, index) in tours" :key="index" @click="changeFrame(1, index)" :class="{ 
+        active: partId == index && progressNum > 0,
+        loopspan: item.name.length > spanlength && partId == index,
+        disabled: isPlay && partId != index }" :name="index">
+          <span v-if="partId == index">{{item.name}}</span>
+          <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+          <div v-if="partId == index && progressNum > 0" class="tourbar">
+            <div :style="`width:${progressNum}%;`" class="tourline"></div>
+          </div>
+        </li>
+      </ul>
+      <!-- 只有一个片段 -->
+      <ul class="part-list part-frame" v-else>
+        <li class="part-item " v-for="(item, index) in tours[0].list" :key="index" @click="changeFrame(2, index)"
+          :style="`background-image:url(${common.changeUrl(item.enter.cover)});`" :class="{ 
+          active: frameId == index && progressNum > 0,
+          activeborder: frameId == index&& progressNum <= 0,
+          disabled: isPlay && frameId != index }" :name="index">
+          <div v-if="frameId == index && progressNum > 0" class="tourbar">
+            <div :style="`width:${progressNum}%;`" class="tourline"></div>
+          </div>
+        </li>
+      </ul>
+
+    </div>
+  </div>
+</template>
+<script setup>
+import { computed, inject, onMounted, watch, ref, nextTick } from 'vue'
+import { Scrollbar, Dialog } from '@/global_components'
+import { useApp, getApp } from '@/app'
+import { useStore } from 'vuex'
+import common from '@/utils/common'
+import { useMusicPlayer } from '@/utils/sound'
+const musicPlayer = useMusicPlayer()
+
+
+const spanlength = ref(5)
+
+const triggerTour = inject("triggerTour");
+const isOpenTours = inject("isOpenTours");
+
+let timer = null
+const isSelect = ref(false)
+const store = useStore()
+const tourScroll = ref(null)
+const flying = computed(() => store.getters['flying'])
+const controls = computed(() => store.getters['scene/metadata'].controls || {})
+const showTours = computed(() => store.getters['tour/showTours'])
+const partId = computed(() => {
+  let id = store.getters['tour/partId']
+  if (isPlay.value) {
+    slideScroll()
+  }
+  return id
+})
+
+const frameId = computed(() => {
+  let id = store.getters['tour/frameId']
+  if (isPlay.value) {
+    slideScroll()
+  }
+  return id
+})
+const progressNum = ref(0)
+const isPlay = computed(() => {
+  let status = store.getters['tour/isPlay']
+  let map = document.querySelector('.kankan-app div[xui_min_map]')
+  if (map) {
+    if (status) {
+      map.classList.add('disabled')
+    } else {
+      map.classList.remove('disabled')
+    }
+  }
+  return status
+})
+const isInit = ref(false)
+const tours = computed(() => {
+  let tours = store.getters['tour/tours']
+  if (tours.length > 0) {
+    if (tourScroll.value && !isInit.value) {
+      isInit.value = true
+      new Scrollbar(tourScroll.value, { onlyHorizontal: true })
+    }
+  }
+  return tours
+})
+const onModeChange = name => {
+  store.commit('setMode', name)
+}
+
+const playTour = async () => {
+  let player = await getApp().TourManager.player
+  if (isPlay.value) {
+    store.commit('tour/setData', { isPlay: true })
+    player.pause()
+  } else {
+    store.commit('tour/setData', { isPlay: true })
+    player.play(partId.value)
+  }
+}
+
+
+
+
+const hanlderTourPartPlay = time => {
+  if (!timer) {
+    timer = KanKan.Animate.transitions.start(progress => {
+      progressNum.value = progress * 100
+    }, time)
+  }
+}
+const cancelTimer = () => {
+  if (timer) {
+    KanKan.Animate.transitions.cancel(timer)
+    timer = null
+  }
+}
+const slideScroll = () => {
+  nextTick(() => {
+    let t = setTimeout(() => {
+      clearTimeout(t)
+      let id = tours.value.length > 1 ? partId.value : frameId.value
+      let item = document.querySelector(`.part-item[name="${id}"]`)
+      item.scrollIntoView({ block: 'center', behavior: 'smooth', inline: 'center' })
+    }, 100)
+  })
+}
+const hanlderTour = async () => {
+  let player = await getApp().TourManager.player
+  player.on('play', data => {
+    musicPlayer.pause(true)
+  })
+  player.on('pause', tours => {
+    console.log('pause', player)
+    musicPlayer.resume()
+
+    progressNum.value = 0
+    cancelTimer()
+    store.commit('tour/setData', { isPlay: false })
+  })
+  player.on('end', tours => {
+    musicPlayer.resume()
+    progressNum.value = 100
+    slideScroll()
+    store.commit('tour/setData', { isPlay: false })
+    cancelTimer()
+  })
+
+  let currPartId = null
+  let currProgress = 0
+  let currFrames = 0
+
+  player.on('progress', data => {
+    if (tours.value.length == 1) {
+      progressNum.value = data.progress * 100
+    } else {
+      if (currPartId != data.partId) {
+        currPartId = data.partId
+        currFrames = tours.value[data.partId].list.length
+        currProgress = 0
+      } else {
+        currProgress += data.progress / currFrames
+        if (currProgress >= 100) {
+          currProgress = 100
+        }
+
+        progressNum.value = currProgress
+      }
+    }
+    store.commit('tour/setData', { partId: data.partId, frameId: data.frameId, isPlay: true })
+  })
+
+}
+const getPartTime = partId => {
+  cancelTimer()
+  let time = 0
+  for (let i = 0; i < tours.value[partId].list.length; i++) {
+    if (!tours.value[partId].list[i]._end) {
+      time += tours.value[partId].list[i].time - 0
+      if (!tours.value[partId].list[i]._notrans) {
+        time += 1000
+      }
+    }
+  }
+  return time
+}
+
+const openTours = () => {
+  // showTours.value = !showTours.value
+  store.commit('tour/setData', { showTours: !showTours.value })
+
+  nextTick(() => {
+    if (isPlay.value) {
+      slideScroll()
+    }
+  })
+}
+const changeFrame = async (type, id) => {
+  progressNum.value = 0
+  // recorder.selectFrame(id)
+  let player = await getApp().TourManager.player
+  // player.selectFrame(id)
+  isSelect.value = true
+  if (type == 1) {
+    player.selectPart(id)
+    console.log(tours.value[id].frameId)
+    let f_id = 0
+    if (tours.value[id].frameId) {
+      f_id = tours.value[id].frameId
+    }
+    player.selectFrame(f_id).then(() => {
+      isSelect.value = false
+    })
+    store.commit('tour/setData', {
+      frameId: f_id,
+      partId: id,
+    })
+  } else {
+    player.selectFrame(id).then(() => {
+      isSelect.value = false
+    })
+    store.commit('tour/setData', {
+      frameId: id,
+    })
+  }
+
+  slideScroll()
+}
+const onClickHandler = async () => {
+  if (isPlay.value) {
+    let player = await getApp().TourManager.player
+    player.pause()
+    musicPlayer.resume()
+  }
+}
+
+
+watch(triggerTour, () => {
+  playTour()
+})
+
+watch(isOpenTours, () => {
+  openTours()
+})
+
+watch(isPlay, () => {
+  window.parent.postMessage(
+    {
+      source: "qjkankan",
+      event: "isPlayTours",
+      params: {
+        isPlay: isPlay.value,
+      },
+    },
+    "*"
+  );
+})
+
+
+onMounted(() => {
+  useApp().then(async sdk => {
+    hanlderTour()
+  })
+
+  nextTick(() => {
+    let player = document.querySelector('.player[name="main"]')
+    player.addEventListener('click', onClickHandler)
+  })
+})
+</script>
+
+<style lang="scss" scoped>
+
+.controls-left-buttons {
+  margin-left: 20px;
+  margin-bottom: 20px;
+  display: flex;
+}
+
+.buttons.tour {
+  margin-right: 10px;
+
+  >div {
+    margin-left: 0px;
+    margin-right: 0px;
+    padding: 0 10px;
+
+    &.show-list {
+      border-left: solid 1px var(--editor-font-color);
+    }
+
+    .icon-pull-down {
+      font-size: 12px;
+    }
+
+    span {
+      right: -10px;
+    }
+  }
+}
+
+.tour-list {
+  position: fixed;
+  bottom: 90px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  overflow: hidden;
+  max-height: 0;
+  transition: .3s all ease;
+  z-index: 9;
+  max-width: 90vw;
+  &.ban {
+    pointer-events: none;
+  }
+
+  .part-content {
+    display: flex;
+    flex-direction: row;
+    overflow: hidden;
+    padding: 6px;
+    background: rgba(0,0,0,0.5);
+    border: 1px solid rgba(255,255,255,0.1);
+    border-radius: 4px;
+    .part-list {
+      display: flex;
+
+      >li {
+        width: 90px;
+        height: 40px;
+        background: rgba(24, 24, 24, .5);
+        line-height: 40px;
+        margin: 0 6px;
+        cursor: pointer;
+        border-radius: 4px;
+
+        >span,
+        >div>span {
+          cursor: pointer;
+          display: inline-block;
+          color: rgba(255, 255, 255, 0.8);
+        }
+
+        &.loopspan {
+
+          >span,
+          >div>span {
+            animation: 5s wordsLoop linear infinite normal;
+          }
+        }
+
+        &.active {
+          position: relative;
+          >span{
+          color: var(--colors-primary-base);
+
+          }
+          .tourbar {
+            position: absolute;
+            width: 78px;
+            left: 6px;
+            right: 6px;
+            bottom: 4px;
+            height: 2px;
+            border-radius: 2px;
+            background: #000;
+            overflow: hidden;
+
+            .tourline {
+              width: 50%;
+              height: 100%;
+              background: var(--colors-primary-base);
+            }
+          }
+        }
+
+        &:hover {
+          opacity: 0.7;
+        }
+      }
+      .activeborder{
+        border: 1px solid var(--editor-main-color);
+      }
+
+    }
+
+    .part-frame {
+      >li {
+        width: 120px;
+        height: 80px;
+        background-size: cover;
+        &.active {
+
+        .tourbar {
+            position: absolute;
+            width: 100%;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            height: 4px;
+            background: #000;
+            overflow: hidden;
+            opacity: 0.5;
+
+            .tourline {
+              width: 50%;
+              height: 100%;
+              background: var(--colors-primary-base);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.barshow {
+  max-height: 102px;
+}
+
+
+@keyframes wordsLoop {
+  0% {
+    transform: translateX(100%);
+    -webkit-transform: translateX(100%);
+  }
+
+  100% {
+    transform: translateX(-100%);
+    -webkit-transform: translateX(-100%);
+  }
+}
+</style>

+ 5 - 0
packages/qjkankan-kankan-view/src/components/Controls/tours.vue

@@ -19,6 +19,7 @@
         <li class="part-item " v-for="(item, index) in tours[0].list" :key="index" @click="changeFrame(2, index)"
           :style="`background-image:url(${common.changeUrl(item.enter.cover)});`" :class="{ 
           active: frameId == index && progressNum > 0,
+          activeborder: frameId == index&& progressNum <= 0,
           disabled: isPlay && frameId != index }" :name="index">
           <div v-if="frameId == index && progressNum > 0" class="tourbar">
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
@@ -339,6 +340,7 @@ $width: 1150px;
         line-height: 40px;
         margin: 0 6px;
         cursor: pointer;
+        border-radius: 4px;
 
         >span,
         >div>span {
@@ -385,6 +387,9 @@ $width: 1150px;
         }
       }
 
+      .activeborder{
+        border: 1px solid var(--editor-main-color);
+      }
 
     }
 

+ 2 - 44
packages/qjkankan-kankan-view/src/components/Information/View.Mobile.vue

@@ -21,50 +21,8 @@
         <i class="iconfont icon-pull-down"></i>
       </div>
     </div>
-    <div class="right" v-if="false" :class="{ more: player.showMore }" v-show="player.showWidgets" @click="onShowMore">
-      <i class="iconfont icon-show_more"></i>
-      <keep-alive>
-        <transition
-          appear
-          name="custom-classes-transition"
-          enter-active-class="animated fadeInUp short faster"
-          leave-active-class="animated fadeOutDown short faster"
-        >
-          <div v-if="player.showMore">
-            <ul>
-              <!-- <li @click.stop="onMenuClick('measure')" v-if="showNavigations.measure"> -->
-              <!-- <li @click.stop="onMenuClick('measure')">
-                                <i class="iconfont icon-rule"></i>
-                                <span>测量工具</span>
-                            </li> -->
-              <!-- <li @click.stop="onMenuClick('vr')" v-if="showNavigations.vr"> -->
-              <li v-if="controls.showVR" @click.stop="onMenuClick('vr')">
-                <i class="iconfont icon-vr"></i>
-                <span>VR模式</span>
-              </li>
-              <li v-if="showMusic" @click.stop="onMenuClick('music')">
-                <i class="iconfont icon-show_more_music"></i>
-                <b v-show="isMusicPlaying">
-                  <i class="iconfont icon-show_more_finish"></i>
-                </b>
-                <span v-html="`音乐 ${showMusicPlaying ? '开' : '关'}`"></span>
-              </li>
-
-              <!-- <li @click.stop="onMenuClick('share')">
-                                <i class="iconfont icon-show_more_share"></i>
-                                <span>分享链接</span>
-                            </li> -->
-            </ul>
-            <template v-if="!loadingLogoFile && deploy != 'local'">
-              <div class="home">
-                <a href="/">
-                  <img :src="'https://4dkk.4dage.com/v3-test/img/logos/logo_zh.png'" alt="" />
-                </a>
-              </div>
-            </template>
-          </div>
-        </transition>
-      </keep-alive>
+    <div class="right">
+     
     </div>
     <transition
       appear

+ 505 - 440
packages/qjkankan-kankan-view/src/pages/SMG.vue

@@ -1,120 +1,111 @@
 <template>
-    <Password />
-    <LoadingLogo :thumb="true" />
-    <Guide />
-    <div class="ui-view-layout" :class="{ show: show }" is-mobile="true">
-      <div class="scene" ref="scene$"></div>
-      <template v-if="dataLoaded">
-        <Information />
-        <Control />
-        <teleport v-if="refMiniMap && player.showWidgets" :to="refMiniMap">
-          <span class="button-switch" @click.stop="toggleMap">
-            <ui-icon type="show_map_collect"></ui-icon>
-          </span>
-  
-          <p class="change" v-if="controls.showDollhouse" @click="changeMode('dollhouse')">
-            <ui-icon type="show_3d_normal"></ui-icon>
-            3D模型
-          </p>
-        </teleport>
-        <template v-if="refMiniMap">
-          <div :class="{ disabled: flying }" v-show="mode != 'panorama'" v-if="controls.showFloorplan && controls.showDollhouse" class="tab-layer">
-            <div class="tabs" v-if="controls.showMap">
-              <span :class="{ active: mode === 'floorplan' }" ref="floorplan_ref" @click="changeMode('floorplan', $event)">
-                <ui-icon :type="mode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
-                二维
-              </span>
-              <span :class="{ active: mode === 'dollhouse' }" ref="dollhouse_ref" @click="changeMode('dollhouse', $event)">
-                <ui-icon :type="mode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
-  
-                三维
-              </span>
-              <div class="background" ref="background"></div>
-            </div>
+  <Password />
+  <LoadingLogo :thumb="true" />
+  <Guide />
+  <div class="ui-view-layout" :class="{ show: show }" is-mobile="true">
+    <div class="scene" ref="scene$"></div>
+    <template v-if="dataLoaded">
+      <Information />
+      <Control />
+      <teleport v-if="refMiniMap && player.showWidgets" :to="refMiniMap">
+        <span class="button-switch" @click.stop="toggleMap">
+          <ui-icon type="show_map_collect"></ui-icon>
+        </span>
+
+        <p class="change" v-if="controls.showDollhouse" @click="changeMode('dollhouse')">
+          <ui-icon type="show_3d_normal"></ui-icon>
+          3D模型
+        </p>
+      </teleport>
+      <template v-if="refMiniMap">
+        <div :class="{ disabled: flying }" v-show="mode != 'panorama'"
+          v-if="controls.showFloorplan && controls.showDollhouse" class="tab-layer">
+          <div class="tabs" v-if="controls.showMap">
+            <span :class="{ active: mode === 'floorplan' }" ref="floorplan_ref"
+              @click="changeMode('floorplan', $event)">
+              <ui-icon :type="mode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
+              二维
+            </span>
+            <span :class="{ active: mode === 'dollhouse' }" ref="dollhouse_ref"
+              @click="changeMode('dollhouse', $event)">
+              <ui-icon :type="mode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
+
+              三维
+            </span>
+            <div class="background" ref="background"></div>
           </div>
-        </template>
+        </div>
       </template>
-      <UiTags />
-    </div>
-  </template>
-  
-  <script setup>
-  import { useMusicPlayer } from "@/utils/sound";
-  import UiTags from "@/components/Tags";
-  
-  import Information from "@/components/Information";
-  import Control from "@/components/Controls/Control.Mobile.vue";
-  import LoadingLogo from "@/components/shared/Loading.vue";
-  import OpenVideo from "@/components/openVideo/";
-  import Guide from "@/components/shared/Guide.vue";
-  import Password from "@/components/shared/Password.vue";
-  
-  import { createApp } from "@/app";
-  import { ref, onMounted, computed, nextTick, watch } from "vue";
-  import { useStore } from "vuex";
-  import browser from "@/utils/browser";
-  import { useApp, getApp } from "@/app";
+    </template>
+    <UiTags />
+  </div>
+</template>
   
-  const musicPlayer = useMusicPlayer();
-  
-  let app = null;
-  
-  const closetagtype = () => {
-    store.commit("tag/setTagClickType", {
-      type: "",
-      data: {},
-    });
-  };
-  
-  const store = useStore();
-  const tags = computed(() => {
-    return store.getters["tag/tags"] || [];
-  });
-  const player = computed(() => store.getters["player"]);
-  const flying = computed(() => store.getters["flying"]);
-  const metadata = computed(() => store.getters["scene/metadata"]);
-  const controls = computed(() => {
-    return metadata.value.controls;
+<script setup>
+import { useMusicPlayer } from "@/utils/sound";
+import UiTags from "@/components/Tags";
+
+import { listenMessage } from '@/utils/messageHandler'
+
+
+import Information from "@/components/Information";
+import Control from "@/components/Controls/Control.Mobile.vue";
+import LoadingLogo from "@/components/shared/Loading.vue";
+import OpenVideo from "@/components/openVideo/";
+import Guide from "@/components/shared/Guide.vue";
+import Password from "@/components/shared/Password.vue";
+
+import { createApp } from "@/app";
+import { ref, onMounted, computed, nextTick, watch } from "vue";
+import { useStore } from "vuex";
+import browser from "@/utils/browser";
+import { useApp, getApp } from "@/app";
+
+const musicPlayer = useMusicPlayer();
+
+let app = null;
+
+listenMessage()
+
+const closetagtype = () => {
+  store.commit("tag/setTagClickType", {
+    type: "",
+    data: {},
   });
-  const mode = computed(() => store.getters["mode"]);
-  const showNavigations = computed(() => store.getters["showNavigations"]);
-  const scene$ = ref(null);
-  const show = ref(false);
-  const dataLoaded = ref(false);
-  const refMiniMap = ref(null);
-  const isCollapse = ref(false);
-  const background = ref(null);
-  const resize = () => {
-    if (this.$refs.background && (this.mode == "floorplan" || this.mode == "dollhouse")) {
-      this.$nextTick(() => {
-        let $active = $(this.$el).find(".tabs .active");
-        background.value.style.width = $active[0].getBoundingClientRect().width + "px";
-        background.value.style.left = $active.position().left + "px";
-      });
-    }
-  };
-  
-  watch(
-    () => player.value.showMap,
-    (val, old) => {
-      if (!isCollapse.value) {
-        let $minmap = document.querySelector("[xui_min_map]");
-        if ($minmap) {
-          if (val) {
-            $minmap.classList.remove("collapse");
-          } else {
-            $minmap.classList.add("collapse");
-          }
-        }
-      }
-    },
-    {
-      deep: true,
-    }
-  );
-  watch(
-    () => player.value.showWidgets,
-    (val, old) => {
+};
+
+const store = useStore();
+const tags = computed(() => {
+  return store.getters["tag/tags"] || [];
+});
+const player = computed(() => store.getters["player"]);
+const flying = computed(() => store.getters["flying"]);
+const metadata = computed(() => store.getters["scene/metadata"]);
+const controls = computed(() => {
+  return metadata.value.controls;
+});
+const mode = computed(() => store.getters["mode"]);
+const showNavigations = computed(() => store.getters["showNavigations"]);
+const scene$ = ref(null);
+const show = ref(false);
+const dataLoaded = ref(false);
+const refMiniMap = ref(null);
+const isCollapse = ref(false);
+const background = ref(null);
+const resize = () => {
+  if (this.$refs.background && (this.mode == "floorplan" || this.mode == "dollhouse")) {
+    this.$nextTick(() => {
+      let $active = $(this.$el).find(".tabs .active");
+      background.value.style.width = $active[0].getBoundingClientRect().width + "px";
+      background.value.style.left = $active.position().left + "px";
+    });
+  }
+};
+
+watch(
+  () => player.value.showMap,
+  (val, old) => {
+    if (!isCollapse.value) {
       let $minmap = document.querySelector("[xui_min_map]");
       if ($minmap) {
         if (val) {
@@ -123,363 +114,437 @@
           $minmap.classList.add("collapse");
         }
       }
-    },
-    {
-      deep: true,
     }
-  );
-  
-  watch(
-    () => mode.value,
-    (val, old) => {
-      console.log(val);
-      let timer = setTimeout(() => {
-        clearTimeout(timer);
-        if (val == "floorplan") {
-          if (floorplan_ref.value && floorplan_ref.value) {
-            background.value.style.width = floorplan_ref.value.getBoundingClientRect().width + "px";
-            background.value.style.left = floorplan_ref.value.offsetLeft + "px";
-          }
-        } else if (val == "dollhouse") {
-          if (dollhouse_ref.value && dollhouse_ref.value) {
-            background.value.style.width = dollhouse_ref.value.getBoundingClientRect().width + "px";
-            background.value.style.left = dollhouse_ref.value.offsetLeft + "px";
-          }
-        }
-      }, 0);
-    },
-    {
-      deep: true,
-    }
-  );
-  
-  const floorplan_ref = ref(null);
-  const dollhouse_ref = ref(null);
-  const changeMode = (name, e) => {
-    if (e) {
-      if (!flying.value) {
-        // background.value.style.width = e.srcElement.getBoundingClientRect().width + 'px'
-        // background.value.style.left = e.srcElement.offsetLeft + 'px'
-        store.commit("setMode", name);
-      }
-    } else {
-      // let t = setTimeout(() => {
-      // background.value.style.width = dollhouse_ref.value.getBoundingClientRect().width + 'px'
-      // background.value.style.left = dollhouse_ref.value.offsetLeft + 'px'
-      store.commit("setMode", name);
-      // }, 0)
-    }
-  };
-  // console.dir(document.querySelector(".tabs>span:last-of-type"));
-  const toggleMap = () => {
-    isCollapse.value = !isCollapse.value;
+  },
+  {
+    deep: true,
+  }
+);
+watch(
+  () => player.value.showWidgets,
+  (val, old) => {
     let $minmap = document.querySelector("[xui_min_map]");
     if ($minmap) {
-      if (!isCollapse.value) {
+      if (val) {
         $minmap.classList.remove("collapse");
       } else {
         $minmap.classList.add("collapse");
       }
     }
-  };
-  
-  const onClickTagInfo = (el) => {
-    el.stopPropagation();
-    let item = tags.value.find((item) => item.sid == el.target.dataset.id);
-    if (item.type == "commodity") {
-      store.commit("tag/setTagClickType", {
-        type: "goodlist",
-        data: item,
-      });
-    }
-  };
-  
-  onMounted(() => {
-    const app = createApp({
-      num: browser.getURLParam("m"),
-      dom: scene$.value,
-      mobile: true,
-    });
-    app.Scene.lock();
-    app.use("MinMap", { theme: { camera_fillStyle: "#0076f6" } });
-    app.use("Tag");
-    app.use("TourPlayer");
-    app.Scene.on("ready", () => {
-      show.value = true;
-    });
-    app.Scene.on("error", (data) => {
-      console.error(data);
-      switch (data.code) {
-        case 5033:
-          window.location.replace(`/5033.html?m=` + browser.getURLParam("m"));
-          break;
-        case 5034:
-          window.location.replace(`/5034.html?m=` + browser.getURLParam("m"));
-          break;
-        case 5009:
-          window.location.replace(`/5034.html?m=` + browser.getURLParam("m"));
-          break;
-        case 5005:
-          window.location.replace(`/#/404.html?m=` + browser.getURLParam("m"));
-          break;
-      }
-    });
-    app.Scene.on("loaded", (pano) => {
-      refMiniMap.value = "[xui_min_map]";
-      store.commit("setFloorId", pano.floorIndex);
-      useMusicPlayer();
-    });
-    app.Scene.on("panorama.videorenderer.resumerender", () => {
-      musicPlayer.pause(true);
-    });
-  
-    app.Scene.on("panorama.videorenderer.suspendrender", async () => {
-      let player = await getApp().TourManager.player;
-      if (!player.isPlaying) {
-        musicPlayer.resume();
-      }
-    });
-    app.store.on("metadata", (metadata) => {
-      let div = document.createElement("div");
-      div.innerHTML = metadata.description;
-      store.commit("scene/load", metadata);
-      if (!metadata.controls.showMap) {
-        app.MinMap.hide(true);
-      }
-      dataLoaded.value = true;
-    });
-    app.store.on("tags", (tags) => {
-      store.commit("tag/load", tags);
-    });
-    app.Camera.on("mode.beforeChange", ({ fromMode, toMode, floorIndex }) => {
-      if (fromMode) {
-        store.commit("setFlying", true);
+  },
+  {
+    deep: true,
+  }
+);
+
+watch(
+  () => mode.value,
+  (val, old) => {
+    console.log(val);
+    let timer = setTimeout(() => {
+      clearTimeout(timer);
+      if (val == "floorplan") {
+        if (floorplan_ref.value && floorplan_ref.value) {
+          background.value.style.width = floorplan_ref.value.getBoundingClientRect().width + "px";
+          background.value.style.left = floorplan_ref.value.offsetLeft + "px";
+        }
+      } else if (val == "dollhouse") {
+        if (dollhouse_ref.value && dollhouse_ref.value) {
+          background.value.style.width = dollhouse_ref.value.getBoundingClientRect().width + "px";
+          background.value.style.left = dollhouse_ref.value.offsetLeft + "px";
+        }
       }
+    }, 0);
+  },
+  {
+    deep: true,
+  }
+);
+
+const floorplan_ref = ref(null);
+const dollhouse_ref = ref(null);
+const changeMode = (name, e) => {
+  if (e) {
+    if (!flying.value) {
+      // background.value.style.width = e.srcElement.getBoundingClientRect().width + 'px'
+      // background.value.style.left = e.srcElement.offsetLeft + 'px'
+      store.commit("setMode", name);
+    }
+  } else {
+    // let t = setTimeout(() => {
+    // background.value.style.width = dollhouse_ref.value.getBoundingClientRect().width + 'px'
+    // background.value.style.left = dollhouse_ref.value.offsetLeft + 'px'
+    store.commit("setMode", name);
+    // }, 0)
+  }
+};
+// console.dir(document.querySelector(".tabs>span:last-of-type"));
+const toggleMap = () => {
+  isCollapse.value = !isCollapse.value;
+  let $minmap = document.querySelector("[xui_min_map]");
+  if ($minmap) {
+    if (!isCollapse.value) {
+      $minmap.classList.remove("collapse");
+    } else {
+      $minmap.classList.add("collapse");
+    }
+  }
+};
+
+const onClickTagInfo = (el) => {
+  el.stopPropagation();
+  let item = tags.value.find((item) => item.sid == el.target.dataset.id);
+  if (item.type == "commodity") {
+    store.commit("tag/setTagClickType", {
+      type: "goodlist",
+      data: item,
     });
-    app.Camera.on("mode.afterChange", ({ toMode, floorIndex }) => {
-      store.commit("setFlying", false);
-    });
-    app.Camera.on("flying.started", (pano) => {
+  }
+};
+
+onMounted(() => {
+  const app = createApp({
+    num: browser.getURLParam("m"),
+    dom: scene$.value,
+    mobile: true,
+  });
+  app.Scene.lock();
+  app.use("MinMap", { theme: { camera_fillStyle: "#0076f6" } });
+  app.use("Tag");
+  app.use("TourPlayer");
+  app.Scene.on("ready", () => {
+    show.value = true;
+  });
+  app.Scene.on("error", (data) => {
+    switch (data.code) {
+      case 5033:
+        window.location.replace(`/5033.html?m=` + browser.getURLParam("m"));
+        break;
+      case 5034:
+        window.location.replace(`/5034.html?m=` + browser.getURLParam("m"));
+        break;
+      case 5009:
+        window.location.replace(`/5034.html?m=` + browser.getURLParam("m"));
+        break;
+      case 5005:
+        window.location.replace(`/mobile.html#/404`);
+        break;
+    }
+  });
+  app.Scene.on("loaded", (pano) => {
+    refMiniMap.value = "[xui_min_map]";
+    store.commit("setFloorId", pano.floorIndex);
+    useMusicPlayer();
+    let music = store.getters['scene/musicURL']
+
+    window.parent.postMessage(
+      {
+        source: "qjkankan",
+        event: "fdkkBgmLink",
+        params: {
+          music,
+        },
+      },
+      "*"
+    );
+  });
+  app.Scene.on("panorama.videorenderer.resumerender", () => {
+    window.parent.postMessage(
+      {
+        source: "qjkankan",
+        event: "togglefdkkBGM",
+        params: {
+          paly: false,
+        },
+      },
+      "*"
+    );
+  });
+
+  app.Scene.on("panorama.videorenderer.suspendrender", async () => {
+    let player = await getApp().TourManager.player;
+    if (!player.isPlaying) {
+      window.parent.postMessage(
+        {
+          source: "qjkankan",
+          event: "togglefdkkBGM",
+          params: {
+            paly: true,
+          },
+        },
+        "*"
+      );
+    }
+  });
+  app.store.on("metadata", (metadata) => {
+    let div = document.createElement("div");
+    div.innerHTML = metadata.description;
+    store.commit("scene/load", metadata);
+    if (!metadata.controls.showMap) {
+      app.MinMap.hide(true);
+    }
+    dataLoaded.value = true;
+  });
+  app.store.on("tags", (tags) => {
+    store.commit("tag/load", tags);
+  });
+  app.Camera.on("mode.beforeChange", ({ fromMode, toMode, floorIndex }) => {
+    if (fromMode) {
       store.commit("setFlying", true);
+    }
+  });
+  app.Camera.on("mode.afterChange", ({ toMode, floorIndex }) => {
+    store.commit("setFlying", false);
+  });
+  app.Camera.on("flying.started", (pano) => {
+    store.commit("setFlying", true);
+  });
+  app.Camera.on("flying.ended", ({ targetPano }) => {
+    store.commit("setFlying", false);
+    store.commit("setPanoId", targetPano.id);
+  });
+  app.TourManager.on('loaded', async (tour) => {
+    let tours = JSON.parse(
+      JSON.stringify(app.TourManager.tours, (key, val) => {
+        if (key === 'audio') {
+          return null
+        } else {
+          return val
+        }
+      })
+    )
+    store.commit('tour/setData', {
+      tours: tours,
+    })
+
+    window.parent.postMessage(
+      {
+        source: "qjkankan",
+        event: "toursList",
+        params: {
+          tours,
+        },
+      },
+      "*"
+    );
+
+    store.commit("tour/setBackUp", {
+      tours: JSON.parse(
+        JSON.stringify(app.TourManager.tours, (key, val) => {
+          if (key === "audio") {
+            return null;
+          } else {
+            return val;
+          }
+        })
+      ),
     });
-    app.Camera.on("flying.ended", ({ targetPano }) => {
-      store.commit("setFlying", false);
-      store.commit("setPanoId", targetPano.id);
-    });
-    app.TourManager.on('loaded', async (tour) => {
-      store.commit("tour/setData", {
-        tours: JSON.parse(
-          JSON.stringify(app.TourManager.tours, (key, val) => {
-            if (key === "audio") {
-              return null;
-            } else {
-              return val;
-            }
-          })
-        ),
-      });
-      store.commit("tour/setBackUp", {
-        tours: JSON.parse(
-          JSON.stringify(app.TourManager.tours, (key, val) => {
-            if (key === "audio") {
-              return null;
-            } else {
-              return val;
-            }
-          })
-        ),
-      });
-    });
-    app.store.on("floorcad", (floor) => store.commit("scene/loadFloorData", floor));
-  
-    app.render();
   });
-  </script>
+  app.store.on("floorcad", (floor) => store.commit("scene/loadFloorData", floor));
+
+  app.render();
+});
+</script>
   
-  <style lang="scss">
-  .tab-layer {
-    width: 100%;
-    text-align: center;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    z-index: 10;
-    position: fixed;
-    left: 50%;
-    transform: translateX(-50%);
-    top: 2.3rem;
-    pointer-events: none;
+<style lang="scss">
+.tab-layer {
+  width: 100%;
+  text-align: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 10;
+  position: fixed;
+  left: 50%;
+  transform: translateX(-50%);
+  top: 2.3rem;
+  pointer-events: none;
+}
+
+.tabs {
+  pointer-events: auto;
+  position: relative;
+  display: flex;
+  background: #222222;
+  border-radius: 6px;
+  padding: 2px;
+  justify-content: center;
+  align-items: center;
+  border: 1px solid rgba(255, 255, 255, 0.1);
+  box-shadow: inset 0px 0px 6px 0px rgba(0, 0, 0, 0.5);
+
+  .background {
+    position: absolute;
+    left: 2px;
+    top: 2px;
+    bottom: 2px;
+    width: 50%;
+    border-radius: 4px;
+    background: #444444;
+    box-shadow: 2px 0px 4px 0px rgba(0, 0, 0, 0.3);
+    z-index: 0;
+    transition: left 0.3s;
   }
-  .tabs {
-    pointer-events: auto;
-    position: relative;
-    display: flex;
-    background: #222222;
+
+  span {
+    flex: 1;
+    color: #fff;
+    opacity: 0.5;
     border-radius: 6px;
-    padding: 2px;
-    justify-content: center;
+    height: 0.94737rem;
+    font-size: 0.36842rem;
+    transition: all 0.3s ease;
+    display: flex;
     align-items: center;
-    border: 1px solid rgba(255, 255, 255, 0.1);
-    box-shadow: inset 0px 0px 6px 0px rgba(0, 0, 0, 0.5);
-    .background {
-      position: absolute;
-      left: 2px;
-      top: 2px;
-      bottom: 2px;
-      width: 50%;
-      border-radius: 4px;
-      background: #444444;
-      box-shadow: 2px 0px 4px 0px rgba(0, 0, 0, 0.3);
-      z-index: 0;
-      transition: left 0.3s;
-    }
-    span {
-      flex: 1;
-      color: #fff;
-      opacity: 0.5;
-      border-radius: 6px;
-      height: 0.94737rem;
-      font-size: 0.36842rem;
-      transition: all 0.3s ease;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      padding-left: 10px;
-      padding-right: 10px;
-      white-space: nowrap;
-      z-index: 1;
-      i {
-        font-size: 0.47368rem;
-        margin-right: 4px;
-        pointer-events: none;
-      }
-    }
-  
-    span.active {
-      opacity: 1;
+    justify-content: center;
+    padding-left: 10px;
+    padding-right: 10px;
+    white-space: nowrap;
+    z-index: 1;
+
+    i {
+      font-size: 0.47368rem;
+      margin-right: 4px;
+      pointer-events: none;
     }
   }
-  
-  [xui_tags_view] {
-    .tag-body {
-      /* display: none; */
-      position: absolute;
-      left: 50%;
-      bottom: 50px;
-      transform: translateX(-50%) scale(0);
-      transform-origin: bottom;
-      transition: all 0.3s cubic-bezier(0.35, 0.32, 0.65, 0.63);
-      // pointer-events: none;
-      .tag-commodity {
-        min-width: 230px;
-        height: 76px;
-        background: rgba(255, 255, 255, 0.8);
-        box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.16);
+
+  span.active {
+    opacity: 1;
+  }
+}
+
+[xui_tags_view] {
+  .tag-body {
+    /* display: none; */
+    position: absolute;
+    left: 50%;
+    bottom: 50px;
+    transform: translateX(-50%) scale(0);
+    transform-origin: bottom;
+    transition: all 0.3s cubic-bezier(0.35, 0.32, 0.65, 0.63);
+
+    // pointer-events: none;
+    .tag-commodity {
+      min-width: 230px;
+      height: 76px;
+      background: rgba(255, 255, 255, 0.8);
+      box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.16);
+      border-radius: 2px;
+      position: relative;
+      margin-bottom: 30px;
+
+      &::before {
+        content: "";
+        display: inline-block;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 2px;
+        height: 28px;
+        bottom: -30px;
+        background: linear-gradient(145deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+        position: absolute;
+      }
+
+      .tag-avatar {
+        position: absolute;
+        z-index: 99;
+        width: 80px;
+        height: 80px;
+        background: #ffffff;
+        box-shadow: 0px 3px 6px 0px rgb(0 0 0 / 16%);
         border-radius: 2px;
-        position: relative;
-        margin-bottom: 30px;
-        &::before {
-          content: "";
-          display: inline-block;
-          left: 50%;
-          transform: translateX(-50%);
-          width: 2px;
-          height: 28px;
-          bottom: -30px;
-          background: linear-gradient(145deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
-          position: absolute;
-        }
-        .tag-avatar {
-          position: absolute;
-          z-index: 99;
-          width: 80px;
-          height: 80px;
-          background: #ffffff;
-          box-shadow: 0px 3px 6px 0px rgb(0 0 0 / 16%);
-          border-radius: 2px;
-          top: -14px;
-          left: -12px;
-          background-size: cover;
-          pointer-events: none;
-        }
-        > p {
-          color: #131d34;
-          font-size: 16px;
-          pointer-events: none;
-        }
-        .tag-title {
-          padding: 10px 10px 10px 76px;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          width: 240px;
-        }
-        .tag-info {
-          padding: 0 20px 0 76px;
-          font-size: 12px;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-        }
+        top: -14px;
+        left: -12px;
+        background-size: cover;
+        pointer-events: none;
       }
-      &.show {
-        transform: translateX(-50%) scale(1);
+
+      >p {
+        color: #131d34;
+        font-size: 16px;
+        pointer-events: none;
       }
-    }
-  
-    .coupon {
-      width: 64px !important;
-      height: 64px !important;
-      &::after {
-        content: "發現好禮";
-        width: 100%;
-        color: #ed5d18;
-        position: absolute;
-        bottom: -24px;
-        text-align: center;
-        font-size: 14px;
+
+      .tag-title {
+        padding: 10px 10px 10px 76px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        width: 240px;
       }
-    }
-  
-    .waterfall {
-      width: 70px !important;
-      height: 70px !important;
-    }
-  
-    .applet_link {
-      width: 64px !important;
-      height: 64px !important;
-      border-radius: 50%;
-      background-color: #fff;
-      border: 1px solid #ed5d18;
-      position: relative;
-      overflow: hidden;
-      &::after {
-        content: "直播中";
-        width: 100%;
-        height: 20px;
-        background: #ed5d18;
-        position: absolute;
-        bottom: 0;
-        text-align: center;
-        line-height: 1.2;
+
+      .tag-info {
+        padding: 0 20px 0 76px;
         font-size: 12px;
-        border-radius: 26%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
       }
     }
+
+    &.show {
+      transform: translateX(-50%) scale(1);
+    }
   }
-  
-  @media (orientation: landscape) {
-    .tab-layer {
-      top: 1.2rem;
-      .tabs {
+
+  .coupon {
+    width: 64px !important;
+    height: 64px !important;
+
+    &::after {
+      content: "發現好禮";
+      width: 100%;
+      color: #ed5d18;
+      position: absolute;
+      bottom: -24px;
+      text-align: center;
+      font-size: 14px;
+    }
+  }
+
+  .waterfall {
+    width: 70px !important;
+    height: 70px !important;
+  }
+
+  .applet_link {
+    width: 64px !important;
+    height: 64px !important;
+    border-radius: 50%;
+    background-color: #fff;
+    border: 1px solid #ed5d18;
+    position: relative;
+    overflow: hidden;
+
+    &::after {
+      content: "直播中";
+      width: 100%;
+      height: 20px;
+      background: #ed5d18;
+      position: absolute;
+      bottom: 0;
+      text-align: center;
+      line-height: 1.2;
+      font-size: 12px;
+      border-radius: 26%;
+    }
+  }
+}
+
+@media (orientation: landscape) {
+  .tab-layer {
+    top: 1.2rem;
+
+    .tabs {
+      height: 0.7rem;
+
+      >span {
         height: 0.7rem;
-        > span {
-          height: 0.7rem;
-          font-size: 0.25rem;
-        }
+        font-size: 0.25rem;
       }
     }
   }
-  </style>
+}
+</style>
   

+ 1 - 2
packages/qjkankan-kankan-view/src/pages/SPG.vue

@@ -77,7 +77,7 @@ onMounted(() => {
                 window.location.replace(`/5034.html?m=` + browser.getURLParam('m'))
                 break
             case 5005:
-                window.location.replace(`/#/404.html?m=` + browser.getURLParam('m'))
+                window.location.replace(`/#/404`)
                 break
         }
     })
@@ -85,7 +85,6 @@ onMounted(() => {
         store.commit('setFloorId', pano.floorIndex)
         useMusicPlayer()
         let music = store.getters['scene/musicURL']
-
         window.parent.postMessage(
             {
                 source: "qjkankan",

+ 42 - 10
packages/qjkankan-kankan-view/src/pages/smg.js

@@ -1,13 +1,45 @@
 import "@/assets/theme.editor.scss";
 import ClickOutSide from "@/utils/fns/ClickOutSide";
+import browser from '../utils/browser'
 import Components from "@/global_components";
-import { createApp } from "vue";
-import store from "@/store";
-import App from "./SMG.vue";
-
-const app = createApp(App);
-// set i18n language
-app.use(store);
-app.use(Components);
-app.directive("click-outside", ClickOutSide);
-app.mount("#app");
+import Delegate from '../utils/fns/Delegate'
+import { getApp } from '../app'
+import { createApp } from 'vue'
+import store from '../store'
+import SMG from "./SMG.vue";
+import Checkbrowser from '../components/shared/Checkbrowser.vue'
+import i18n, { getLocale, setI18nLanguage, loadLocaleMessages } from '../i18n'
+let App
+if (browser.detectChrome() || browser.detectSafari() || browser.detectFirefox() || browser.detectEdge() || browser.detectWeixin() || browser.detectWeixinMiniProgram() || browser.detectAlipay()) {
+  App = SMG
+} else {
+  App = Checkbrowser
+}
+
+const local = getLocale()
+loadLocaleMessages(i18n, local).then(() => {
+  setI18nLanguage(i18n, local)
+
+  const app = (window.__app = createApp(App))
+  app.use(i18n)
+  app.use(store)
+  app.use(Components)
+  app.directive('click-outside', ClickOutSide)
+  app.mount('#app')
+  const handleUserInputFocus = () => {
+    setTimeout(() => {
+      getApp().config.useShortcutKeys = false
+    }, 200)
+  }
+
+  const handleUserInputBlur = () => {
+    setTimeout(() => {
+      getApp().config.useShortcutKeys = true
+    }, 200)
+  }
+
+  Delegate(document, 'focus', 'input', () => handleUserInputFocus())
+  Delegate(document, 'focus', '[contenteditable]', () => handleUserInputFocus())
+  Delegate(document, 'blur', 'input', () => handleUserInputBlur())
+  Delegate(document, 'blur', '[contenteditable]', () => handleUserInputBlur())
+})

+ 10 - 8
packages/qjkankan-kankan-view/src/store/index.js

@@ -57,19 +57,21 @@ const store = createStore({
          * @param {*} payload
          */
         setMode(state, payload) {
-            if (payload == state.mode) {
+            if (payload == state.mode || !payload) {
                 return
             }
             store.commit('setFlying', true)
 
             useApp().then(sdk => {
-                sdk.Camera[payload]()
-                    .then(() => {
-                        store.commit('setFlying', false)
-                    })
-                    .catch(() => {
-                        store.commit('setFlying', false)
-                    })
+                if (payload) {
+                    sdk.Camera[payload] && sdk.Camera[payload]()
+                        .then(() => {
+                            store.commit('setFlying', false)
+                        })
+                        .catch(() => {
+                            store.commit('setFlying', false)
+                        })
+                }
             })
 
             if (payload == 'dollhouse') {

+ 5 - 0
packages/qjkankan-kankan-view/src/utils/messageHandler.js

@@ -34,6 +34,11 @@ export const listenMessage = () => {
         store.commit('setMode', params.name)
       }
 
+      if (event == "enterVr") {
+        store.commit("showVR");
+      }
+      
+
         //相机模式改变
       if (event == "toggleBGM") {
         

+ 1 - 0
packages/qjkankan-view/public/show.html

@@ -20,6 +20,7 @@
     <script src="<%= VUE_APP_STATIC_DIR %>/lib/krpano/js/tour.js"></script>
     <script src="<%= VUE_APP_STATIC_DIR %>/lib/howler/howler.min.js"></script>
     <script src="<%= VUE_APP_STATIC_DIR %>/lib/swiper/swiper-bundle.min.js"></script>
+    <!-- <script src="<%= VUE_APP_STATIC_DIR %>/lib/jssor/jssor.slider-28.1.0.min.js"></script> -->
    
   </body>
 </html>

+ 2 - 0
packages/qjkankan-view/public/showMobile.html

@@ -21,6 +21,8 @@
     <script src="<%= VUE_APP_STATIC_DIR %>/lib/krpano/js/tour.js"></script>
     <script src="<%= VUE_APP_STATIC_DIR %>/lib/howler/howler.min.js"></script>
     <script src="<%= VUE_APP_STATIC_DIR %>/lib/swiper/swiper-bundle.min.js"></script>
+    <!-- <script src="<%= VUE_APP_STATIC_DIR %>/lib/jssor/jssor.slider-28.1.0.min.js"></script> -->
+
    
   </body>
 </html>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 5 - 0
packages/qjkankan-view/public/showviewer/lib/jssor/jssor.slider-28.1.0.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 2
packages/qjkankan-view/public/showviewer/lib/krpano/plugins/webvr.js


+ 32 - 64
packages/qjkankan-view/public/showviewer/lib/krpano/plugins/webvr.xml

@@ -1,8 +1,4 @@
 <krpano>
-	<!--
-		webvr.xml
-		- krpano 1.19
-	-->
 
 	<!-- load the WebVR plugin and assign it to a 'webvr' variable for easier usage -->
 	<plugin name="WebVR" devices="html5" keep="true"
@@ -10,7 +6,6 @@
 	        onloaded="copy(webvr, plugin[WebVR]);"
 	        mousespeed="0.00125"
 	        multireslock="true"
-	        fullscreen_mirroring="true"
 	        mobilevr_support="true"
 	        mobilevr_ipd="63.5"
 	        mobilevr_screensize="auto"
@@ -20,10 +15,11 @@
 	        mobilevr_lens_dist2="1|0|0|0"
 	        mobilevr_lens_ca="0.0"
 	        mobilevr_lens_vign="100"
+	        mobilevr_webvr_dist="false"
 	        mobilevr_wakelock="true"
 	        mobilevr_sensor_mode="3"
 	        mobilevr_autocalibration="false"
-	        mobilevr_touch_support="true"
+	        mobilevr_touch_support="false"
 	        mobilevr_fake_support="false"
 	        vr_cursor="hotspot[vr_cursor]"
 	        vr_cursor_enabled="true"
@@ -31,7 +27,7 @@
 	        vr_cursor_onout="tween(hotspot[vr_cursor].scale,0.3,0.1);"
 	        onavailable="webvr_onavailable();"
 	        onunavailable=""
-	        onunknowndevice="webvr_onunknowndevice();"
+	        onunknowndevice="set(ask_user_for_screensize,true);"
 	        onentervr="webvr_onentervr();"
 	        onexitvr="webvr_onexitvr();"
 	        />
@@ -68,10 +64,9 @@
 			if(%1 != null, set(vr_aclk_timeout, %1), set(vr_aclk_timeout, 2000));
 			copy(vr_aclk_t1, timertick);
 			set(vr_aclk_waiting, true);
-			copy(vr_aclk_hotspot, name);
 			set(hotspot[vr_cursor].crop,'0|0|80|80');
 
-			asyncloop(vr_aclk_waiting AND vr_aclk_hotspot == name,
+			asyncloop(vr_aclk_waiting,
 				sub(dt, timertick,vr_aclk_t1);
 
 				if(!hovering,
@@ -102,31 +97,16 @@
 
 	<!-- by pressing SPACE the Oculus Rift could be re-centered -->
 	<events name="webvr_events" devices="html5" keep="true"
-	        onkeydown="if(webvr AND webvr.isenabled AND keycode==32, webvr.resetSensor() );"
+	        onkeydown="if(keycode==32, webvr.resetSensor(0) );"
 	        onmousedown="if(webvr AND webvr.isenabled, webvr_showbuttons() );"
 	        />
 
 
 	<!-- when WebVR support is available show an EnterVR button -->
 	<action name="webvr_onavailable">
-		webvr.loadsettings();
+
 		delayedcall(0.5, tween(layer[webvr_enterbutton].alpha,1.0); );
 	</action>
-	
-	
-	<action name="webvr_onunknowndevice">
-		if(webvr.isfake AND device.desktop AND webvr.havesettings == false,
-			<!-- set the 'no distortion' headset for fake desktop usage -->
-			set(webvr.mobilevr_lens_overlap, 1.0);
-			set(webvr.mobilevr_lens_fov, 96.0);
-			set(webvr.mobilevr_lens_dist, 0.0);
-			set(webvr.mobilevr_lens_dist2, '1|0|0|0');
-			set(webvr.mobilevr_lens_ca, 0.0);
-			set(webvr.mobilevr_lens_vign, 100);
-		  ,
-			set(ask_user_for_screensize,true);
-		  );
-	</action>
 
 
 	<action name="webvr_onentervr">
@@ -138,7 +118,7 @@
 		<!-- when the screen size is unknown an no custom size is set, open the setup screen on entering the VR mode -->
 		if(webvr.ismobilevr == true AND !webvr.isfake AND ask_user_for_screensize == true AND webvr.mobilevr_screensize == 'auto',
 			set(ask_user_for_screensize, false);
-			vr_setup();
+			<!-- vr_setup(); -->
 		  );
 		if(webvr.isfake,
 			webvr_show_fakemode_info(true);
@@ -151,7 +131,7 @@
 
 		tween(layer[webvr_enterbutton].alpha,1);
 		tween(layer[webvr_exitbutton].alpha,0);
-		tween(layer[webvr_setupbutton].alpha,0);
+		<!-- tween(layer[webvr_setupbutton].alpha,0); -->
 		
 		webvr_show_fakemode_info(false);
 
@@ -180,7 +160,7 @@
 	</action>
 	
 	<action name="webvr_show_fakemode_info">
-		<!-- if('%1' == 'true',
+		if('%1' == 'true',
 			addlayer(webvr_fakemode_info);
 			set(layer[webvr_fakemode_info].url, '%SWFPATH%/plugins/textfield.swf');
 			set(layer[webvr_fakemode_info].keep, true);
@@ -188,10 +168,10 @@
 			set(layer[webvr_fakemode_info].y, 80);
 			set(layer[webvr_fakemode_info].background, false);
 			set(layer[webvr_fakemode_info].css, 'color:#FFFFFF;text-align:center;');
-			set(layer[webvr_fakemode_info].html, '[i][u]Simulated WebVR Mode![/u][/i][br]For real WebVR with headset tracking, either use a [a href="http://webvr.info" target="_blank" style="color:#FFFFFF;"]WebVR-API-capable[/a] desktop browser or a mobile device and a VR headset.');
+			set(layer[webvr_fakemode_info].html, '');
 		  ,
 			removelayer(webvr_fakemode_info);
-		  ); -->
+		  );
 	</action>
 	
 	
@@ -222,21 +202,16 @@
 
 	<layer name="webvr_exitbutton" keep="true" vr="true"
 	       style="webvr_button_style"
-	       html="退出VR"
+	       html="退出VR模式"
 	       align="top" y="24"
 	       autoalpha="true" alpha="0.0"
 	       onclick="webvr.exitVR();"
 	       />
 
 	<layer name="webvr_setupbutton" keep="true" vr="true"
-	       style="webvr_button_style"
-	       html="VR设置"
-	       align="bottom" y="24"
 	       autoalpha="true" alpha="0.0"
-	       onclick="vr_setup()"
 	       />
 
-
 	<action name="webvr_showbuttons">
 		stopdelayedcall(vr_button_fadeout);
 		if(webvr.ismobilevr,
@@ -359,30 +334,30 @@
 		
 		<!-- create the text elements -->
 		set(vr_setup_text_parent, 'vr_setup_m1');
-		vr_setup_createtext(vr_setup_title, 'MOBILE VR SETUP',       center, center, 0, -225, #FFFFFF,     false);
+		vr_setup_createtext(vr_setup_title, 'VR设置',       center, center, 0, -225, #FFFFFF,     false);
 
-		vr_setup_createtext(vr_setup_dvn1, 'Device:',         center, right,  0, -145, #FFFFFF,     true, vr_setup_select('screen') );
+		vr_setup_createtext(vr_setup_dvn1, '设备:',         center, right,  0, -145, #FFFFFF,     true, vr_setup_select('screen') );
 		vr_setup_createtext(vr_setup_dvn2, get(i_devicename), center, left,   0, -145, get(sizcol), true, vr_setup_select('screen') );
-		vr_setup_createtext(vr_setup_siz1, 'Screensize:',     center, right,  0, -105, #FFFFFF,     true, vr_setup_select('screen') );
+		vr_setup_createtext(vr_setup_siz1, '屏幕尺寸:',     center, right,  0, -105, #FFFFFF,     true, vr_setup_select('screen') );
 		vr_setup_createtext(vr_setup_siz2, get(i_screensize), center, left,   0, -105, get(sizcol), true, vr_setup_select('screen') );
 
-		vr_setup_createtext(vr_setup_ipd1, 'IPD:',            center, right,  0,  -35, #FFFFFF,     true, vr_setup_select('ipd') );
+		vr_setup_createtext(vr_setup_ipd1, '瞳距:',            center, right,  0,  -35, #FFFFFF,     true, vr_setup_select('ipd') );
 		vr_setup_createtext(vr_setup_ipd2, get(i_ipd),        center, left,   0,  -35, #FFFFFF,     true, vr_setup_select('ipd') );
 
-		vr_setup_createtext(vr_setup_hmd1, 'VR Headset:',     center, right,  0,  +35, #FFFFFF,     true, vr_setup_select('headset') );
+		vr_setup_createtext(vr_setup_hmd1, '设置头显:',     center, right,  0,  +35, #FFFFFF,     true, vr_setup_select('headset') );
 		vr_setup_createtext(vr_setup_hmd2, get(i_headset),    center, left,   0,  +35, #FFFFFF,     true, vr_setup_select('headset') );
 		
-		vr_setup_createtext(vr_setup_hmd3, 'Customize',       center, center, 0,  +75, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_customize_headset() );
+		vr_setup_createtext(vr_setup_hmd3, '调节头显',       center, center, 0,  +75, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_customize_headset() );
 
 		
 
 		if(webvr.iswebvr == false,
-			vr_setup_createtext(vr_setup_cal, 'Calibrate Gyroscope',   center, center,    0, +145, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_calibration() );
+			vr_setup_createtext(vr_setup_cal, '陀螺仪校准',   center, center,    0, +145, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_calibration() );
 		  );
 
-		vr_setup_createtext(vr_setup_sav, 'SAVE',          center, center, -200, +225, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_save() );
-		vr_setup_createtext(vr_setup_rst, 'RESET',         center, center,    0, +225, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_reset() );
-		vr_setup_createtext(vr_setup_cls, 'CLOSE',         center, center, +200, +225, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_close() );
+		vr_setup_createtext(vr_setup_sav, '保存',          center, center, -200, +225, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_save() );
+		vr_setup_createtext(vr_setup_rst, '重置',         center, center,    0, +225, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_reset() );
+		vr_setup_createtext(vr_setup_cls, '关闭',         center, center, +200, +225, #FFFFFF,     true, set(background,true), set(background,false), vr_setup_close() );
 		
 		<!-- and the adjusting buttons -->
 		vr_setup_createbutton(vr_setup_btn1, '&#60;', left,  left,  5%, -35, #FFFFFF, true, null);
@@ -391,7 +366,7 @@
 		
 		<!-- create the customize_headset text elements -->
 		set(vr_setup_text_parent, 'vr_setup_m3');
-		vr_setup_createtext(vr_setup_m31, 'VR HEADSET', center, center, 0, -225, #FFFFFF, false);
+		vr_setup_createtext(vr_setup_m31, '头显设置', center, center, 0, -225, #FFFFFF, false);
 		
 		vr_setup_createtext(vr_setup_fov1,  'FOV:',           center, right,  0,  -80,  #FFFFFF,    true, vr_setup_select('fov') );
 		vr_setup_createtext(vr_setup_fov2, get(i_fov),        center, left,   0,  -80,  #FFFFFF,    true, vr_setup_select('fov') );
@@ -411,16 +386,16 @@
 		vr_setup_createtext(vr_setup_olp1, 'Overlap:',        center, right,  0, +160,  #FFFFFF,    true, vr_setup_select('overlap') );
 		vr_setup_createtext(vr_setup_olp2, get(i_overlap),    center, left,   0, +160,  #FFFFFF,    true, vr_setup_select('overlap') );
 				
-		vr_setup_createtext(vr_setup_m35, 'CLOSE',       center, center, 0, +225, #FFFFFF, true, set(background,true), set(background,false), vr_setup_close_sub_menus() );
+		vr_setup_createtext(vr_setup_m35, '关闭',       center, center, 0, +225, #FFFFFF, true, set(background,true), set(background,false), vr_setup_close_sub_menus() );
 		
 		
 		<!-- create the calibration text elements -->
 		set(vr_setup_text_parent, 'vr_setup_m2');
-		vr_setup_createtext(vr_setup_cb1, 'GYROSCOPE', center, center, 0, -225, #FFFFFF, false);
-		vr_setup_createtext(vr_setup_cb2, 'Place the device on a flat and[br]stable surface and tab calibrate[br]to correct a gyroscope drifting.', center, center, 0, -95, #FFFFFF, false, vr_setup_select('screen') );
-		vr_setup_createtext(vr_setup_cb3, 'CALIBRATE',   center, center, 0,  +55, #FFFFFF, true, set(background,true), set(background,false), vr_setup_do_calibration() );
-		vr_setup_createtext(vr_setup_cb4, 'RESET',       center, center, 0, +125, #FFFFFF, true, set(background,true), set(background,false), webvr.resetcalibration() );
-		vr_setup_createtext(vr_setup_cb5, 'CLOSE',       center, center, 0, +225, #FFFFFF, true, set(background,true), set(background,false), vr_setup_close_sub_menus() );
+		vr_setup_createtext(vr_setup_cb1, '陀螺仪', center, center, 0, -225, #FFFFFF, false);
+		vr_setup_createtext(vr_setup_cb2, '水平放置后校准', center, center, 0, -95, #FFFFFF, false, vr_setup_select('screen') );
+		vr_setup_createtext(vr_setup_cb3, '校准',   center, center, 0,  +55, #FFFFFF, true, set(background,true), set(background,false), vr_setup_do_calibration() );
+		vr_setup_createtext(vr_setup_cb4, '重置',       center, center, 0, +125, #FFFFFF, true, set(background,true), set(background,false), webvr.resetcalibration() );
+		vr_setup_createtext(vr_setup_cb5, '关闭',       center, center, 0, +225, #FFFFFF, true, set(background,true), set(background,false), vr_setup_close_sub_menus() );
 		
 		vr_setup_createtext(vr_setup_cb6, 'Calibrating...',      bottom, center, 0, 40, #FFFFFF, false, null );
 		vr_setup_createtext(vr_setup_cb7, 'Calibration okay.',   bottom, center, 0, 40, #FFFFFF, false, null );
@@ -442,6 +417,7 @@
 	</action>
 
 
+
 	<action name="vr_setup_createtext">
 		<!--
 			%1 = name
@@ -493,7 +469,7 @@
 		<!-- reset to the defaults -->
 		set(webvr.mobilevr_screensize, 'auto');
 		copy(i_screensize, webvr.devicesize);
-		if(i_screensize LE 0, set(i_screensize, 5.0); );
+		if(i_screensize LE 0, set(i_screensize, 5.0));
 		roundval(i_screensize, 1);
 		set(layer[vr_setup_dvn2].html, get(webvr.devicename));
 		txtadd(layer[vr_setup_siz2].html, get(i_screensize), ' inch');
@@ -506,18 +482,10 @@
 		<!-- set fake custom lens settings and call 'next' headset to switch to the default 'Cardboard' settings -->
 		set(webvr.mobilevr_lens_fov, 100);
 		set(webvr.mobilevr_lens_dist, 0.5);
-		set(webvr.mobilevr_lens_dist2, '1|0|0|0');
 		set(webvr.mobilevr_lens_vign, 100);
 		set(webvr.mobilevr_lens_overlap, 1.0);
 		set(webvr.mobilevr_lens_ca, 0.0);
-		
-		if(webvr.isfake AND device.desktop,
-			<!-- select 'no distortion' headset for fake desktop usage -->
-			vr_setup_change_headset(-1);
-		  ,
-			<!-- select 'Cardboard A' headset for Mobile-VR usage -->
-			vr_setup_change_headset(+1);	
-		  );
+		vr_setup_change_headset(+1);
 
 		vr_setup_select(get(selected_var));
 	</action>

+ 29 - 29
packages/qjkankan-view/public/showviewer/lib/krpano/skin/vtourskin.xml

@@ -67,13 +67,13 @@
 			if(aspect != lastaspect OR '%1' == 'true',
 				copy(lastaspect, aspect);
 				if(stagewidth GT stageheight,
-									<!-- landscape orientation - use stereo rendering and a direct/fast gyro sensor mode -->
+											<!-- landscape orientation - use stereo rendering and a direct/fast gyro sensor mode -->
 					set(display.stereo, true);
 					set(webvr.mobilevr_sensor_mode, 3);
 					webvr.update();
 					tween(layer[webvr_rotate_to_landscape_request].alpha, 0.0, 0.0);
 				  ,
-									<!-- portrait orientation - use normal rendering and a smoother/slower gyro sensor mode -->
+											<!-- portrait orientation - use normal rendering and a smoother/slower gyro sensor mode -->
 					set(display.stereo, false);
 					set(webvr.mobilevr_sensor_mode, 1);
 					webvr.update();
@@ -324,7 +324,7 @@
 			copy(layer[skin_thumbs_scrollleft].y, halfheight);
 			copy(layer[skin_thumbs_scrollright].y, halfheight);
 
-							<!-- set(thumb_cnt,0);
+									<!-- set(thumb_cnt,0);
 			for(set(i,0), i LT scene.count, inc(i),
 				inc(thumb_cnt);
 				txtadd(thumbname,'skin_thumb_',get(i));
@@ -386,7 +386,7 @@
 			activatespot(spot0);
 		  );
 
-						<!-- zoom and pan the map to see all spots at the same time -->
+								<!-- zoom and pan the map to see all spots at the same time -->
 		zoomToSpotsExtent();
 	</action>
 
@@ -400,7 +400,7 @@
 		copy(lp_limitview, view.limitview);
 
     if(%1 == 1,
-		<!-- 小行星开场 -->
+				<!-- 小行星开场 -->
 			set(view.fovmax, 170);
 			set(view.limitview, lookat);
 			set(view.vlookatmin, 90);
@@ -420,7 +420,7 @@
 		);
 
     if(%1 == 2,
-		<!-- 水平巡游开场 -->
+				<!-- 水平巡游开场 -->
 			copy(view.hlookat, get(lp_hlookat));
 			sub(view.hlookat, 360);
 			set(events[lp_events].onpreviewcomplete,
@@ -434,7 +434,7 @@
 		);
 
     if(%1 == 3,
-		<!-- 小行星巡游 -->
+				<!-- 小行星巡游 -->
 			set(view.fov, 70);
 					set(view.vlookat, -40);
 					set(view.hlookat, get(lp_hlookat));
@@ -455,7 +455,7 @@
 		);
 
     if(%1 == 4,
-				<!-- 水晶球开场 -->
+						<!-- 水晶球开场 -->
 				set(view.fov, 110);
 				set(view.hlookat, get(lp_hlookat));
 				sub(view.hlookat, 360);
@@ -472,7 +472,7 @@
 		);
 
     if(%1 == 5,
-			<!-- 小行星缩放开场 -->
+					<!-- 小行星缩放开场 -->
 				set(view.fov, 150);
 				set(view.vlookat, -65);
 				set(view.hlookat, get(lp_hlookat));
@@ -493,16 +493,16 @@
 
 	<action name="skin_lookat">
 		if(webvr.isenabled,
-							<!-- adjust the VR prev/next hotspots for the view change -->
+									<!-- adjust the VR prev/next hotspots for the view change -->
 			calc(hlookat_offset, %1 - view.hlookat);
 			add(hotspot[webvr_prev_scene].ath, hlookat_offset);
 			add(hotspot[webvr_next_scene].ath, hlookat_offset);
 		  );
 		if(plugin[skin_gyro].enabled,
-							<!-- reset the gyro tracking -->
+									<!-- reset the gyro tracking -->
 			plugin[skin_gyro].resetsensor(%1);
 		  );
-						<!-- change the view -->
+								<!-- change the view -->
 		lookat(%1, %2, %3);
 	</action>
 
@@ -872,10 +872,10 @@
 
 
 	<action name="skin_keydown_event">
-		if(keycode == 33, skin_nextscene_loop(-1) );            					<!-- Page Up   - previous scene -->
-		if(keycode == 34, skin_nextscene_loop(+1) );            					<!-- Page Dowm - next scene -->
-		if(keycode == 35, skin_gotoscene(calc(scene.count-1)) );					<!-- End       - last scene -->
-		if(keycode == 36, skin_gotoscene(0) );                  					<!-- Home/Pos1 - first scene -->
+		if(keycode == 33, skin_nextscene_loop(-1) );            							<!-- Page Up   - previous scene -->
+		if(keycode == 34, skin_nextscene_loop(+1) );            							<!-- Page Dowm - next scene -->
+		if(keycode == 35, skin_gotoscene(calc(scene.count-1)) );							<!-- End       - last scene -->
+		if(keycode == 36, skin_gotoscene(0) );                  							<!-- Home/Pos1 - first scene -->
 	</action>
 
 
@@ -1101,7 +1101,7 @@
 		tween(view.distortion,    2.8, distance(1.0,0.5),easeOutQuad,
 				skin_view_look_straight();
 			);
-		</action>
+	</action>
 
 	<action name="skin_view_littleplanet">
 		tween(view.architectural, 0.0, distance(1.0,0.5));
@@ -1198,7 +1198,7 @@
         set(plugin[get(tooltipname)].borderalpha,0.2);
         set(plugin[get(tooltipname)].glow,0); 
         set(plugin[get(tooltipname)].glowcolor,0xFFFFFF);
-        				<!-- set(plugin[get(tooltipname)].css,'text-align:center; color:#FFFFFF; font-family:STXihei; font-size:14px;');
+        						<!-- set(plugin[get(tooltipname)].css,'text-align:center; color:#FFFFFF; font-family:STXihei; font-size:14px;');
         if(device.mobile,
         set(plugin[get(tooltipname)].css,'text-align:center; color:#FFFFFF;
         font-family:STXihei; font-weight:bold; font-size:14px;'); ); -->
@@ -1231,16 +1231,16 @@
 
 	<!-- 场景切换方式 -->
 	<blendmodes name="simple crossblending" blend="BLEND(1.0, easeInCubic)" />
-	<blendmodes name="zoom blend"           blend="ZOOMBLEND(2.0, 2.0, easeInOutSine)" />
-	<blendmodes name="black-out"            blend="COLORBLEND(2.0, 0x000000, easeOutSine)" />
-	<blendmodes name="white-flash"          blend="LIGHTBLEND(1.0, 0xFFFFFF, 2.0, linear)" />
-	<blendmodes name="right-to-left"        blend="SLIDEBLEND(1.0, 0.0, 0.2, linear)" />
-	<blendmodes name="top-to-bottom"        blend="SLIDEBLEND(1.0, 90.0, 0.01, linear)" />
-	<blendmodes name="diagonal"             blend="SLIDEBLEND(1.0, 135.0, 0.4, linear)" />
-	<blendmodes name="vertical open"        blend="OPENBLEND(0.7, 1.0, 0.1, 0.0, linear)" />
-	<blendmodes name="circle open"          blend="OPENBLEND(1.0, 0.0, 0.2, 0.0, linear)" />
-	<blendmodes name="horizontal open"      blend="OPENBLEND(1.0, -1.0, 0.3, 0.0, linear)" />
-	<blendmodes name="elliptic + zoom"      blend="OPENBLEND(1.0, -0.5, 0.3, 0.8, linear)" />
+	<blendmodes name="zoom blend" blend="ZOOMBLEND(2.0, 2.0, easeInOutSine)" />
+	<blendmodes name="black-out" blend="COLORBLEND(2.0, 0x000000, easeOutSine)" />
+	<blendmodes name="white-flash" blend="LIGHTBLEND(1.0, 0xFFFFFF, 2.0, linear)" />
+	<blendmodes name="right-to-left" blend="SLIDEBLEND(1.0, 0.0, 0.2, linear)" />
+	<blendmodes name="top-to-bottom" blend="SLIDEBLEND(1.0, 90.0, 0.01, linear)" />
+	<blendmodes name="diagonal" blend="SLIDEBLEND(1.0, 135.0, 0.4, linear)" />
+	<blendmodes name="vertical open" blend="OPENBLEND(0.7, 1.0, 0.1, 0.0, linear)" />
+	<blendmodes name="circle open" blend="OPENBLEND(1.0, 0.0, 0.2, 0.0, linear)" />
+	<blendmodes name="horizontal open" blend="OPENBLEND(1.0, -1.0, 0.3, 0.0, linear)" />
+	<blendmodes name="elliptic + zoom" blend="OPENBLEND(1.0, -0.5, 0.3, 0.8, linear)" />
 
 	<!-- startup action - load the first scene -->
 	<action name="startup" autorun="onstart">
@@ -1265,7 +1265,7 @@
 			set(control.usercontrol, all);
 			js_sceneReadyCallback();
 
-			<!-- setup_pano_settings();
+					<!-- setup_pano_settings();
 			setup_global_settings();
 			js_sceneReadyCallback();
 			setup_autoplay();

+ 1 - 1
packages/qjkankan-view/src/apis/index.js

@@ -45,5 +45,5 @@ export function getPanoInfo(ok, no) {
  * @param {*} no
  */
 export function checkWork(ok, no) {
-  return http.get(`${URL_FILL}/web/common/checkWork/${number()}?${Math.random()}`, {}, ok, no);
+  return http.get(`${URL_FILL}/web/common/checkWork/${number()}?visit=1111`, {}, ok, no);
 }

BIN=BIN
packages/qjkankan-view/src/assets/images/icon/pause.png


BIN=BIN
packages/qjkankan-view/src/assets/images/icon/preview.png


+ 45 - 24
packages/qjkankan-view/src/components/Fdkk/index.vue

@@ -1,13 +1,23 @@
 <template>
   <div class="fdkkcon">
-    <iframe v-if="fdkkCurrentVersion=='v3'" id="fdkkifr"
-      :src="`/${isMobile?'smobile':'spc'}.html?m=${currentScene.sceneCode}`"
-      frameborder="0"></iframe>
-    <iframe v-else id="fdkkifr"
-      :src="`${isMobile?'smg':'spg'}.html?m=${currentScene.sceneCode}`"
-      frameborder="0"></iframe>
-
-      
+    <template v-if="fdkkCurrentVersion == 'v3'">
+      <iframe :key="currentScene.sceneCode" v-if="!isMobile" id="fdkkifr" :src="`/spc.html?m=${currentScene.sceneCode}`"
+        frameborder="0"></iframe>
+      <!-- <iframe :key="currentScene.sceneCode" v-if="!isMobile" id="fdkkifr" :src="`https://test.4dkankan.com/spc.html?m=${currentScene.sceneCode}`" -->
+
+      <!-- v3场景移动端,用embed.html定制开发 -->
+      <v3mobile v-else @changeUrl="handleChange" @otherUrl="handleOther" :somedatainfo="somedatainfo"
+        :key="embeM || currentScene.sceneCode"
+        :url="otherLink ? otherLink : `/embed.html?from=mingyuan&m=${embeM || currentScene.sceneCode}&lang=zh&scene-link=1&rnd=${rnd}`" />
+    </template>
+    <!-- :url="otherLink ? otherLink : `https://test.4dkankan.com/embed.html?from=mingyuan&m=${embeM || currentScene.sceneCode}&lang=zh&scene-link=1&rnd=${rnd}`" /> -->
+
+
+    <iframe :key="currentScene.sceneCode" v-else id="fdkkifr"
+      :src="`${isMobile ? 'smg' : 'spg'}.html?m=${currentScene.sceneCode}`" frameborder="0"></iframe>
+    <!-- :src="`http://192.168.20.66:8080/${isMobile ? 'smg' : 'spg'}.html?m=${currentScene.sceneCode}`" -->
+
+
     <!-- <iframe id="fdkkifr" :src="`${isMobile?'smg':'spg'}.html?m=${currentScene.sceneCode}`" frameborder="0"></iframe> -->
   </div>
 </template>
@@ -17,7 +27,8 @@ import { ref, watch, computed, onUnmounted, onMounted, nextTick } from "vue";
 import { useStore } from "vuex";
 import browser from "@/utils/browser";
 import { useMusicPlayer } from '@/utils/sound'
-
+import v3mobile from "./v3fdkkmobile/iframe.vue";
+import { getApp } from '@/app'
 
 //背景音乐
 const musicPlayer = useMusicPlayer()
@@ -28,6 +39,15 @@ const isMobile = computed(() => browser.isMobile());
 const currentScene = computed(() => store.getters["scene/currentScene"]);
 const fdkkCurrentVersion = computed(() => store.getters["scene/fdkkCurrentVersion"]);
 
+const somedatainfo = ref({})
+
+const embeM = ref(null)
+
+const otherLink = ref(null)
+
+const rnd = ref(null)
+
+
 const handleMessage = (res) => {
   if (Object.prototype.toString.call(res.data) == '[object Object]') {
     if (res.data.source !== 'qjkankan') {
@@ -55,28 +75,32 @@ const handleMessage = (res) => {
       store.commit('fdkk/setMode', params.mode)
     }
 
-    //场景本身的背景音乐
+    //场景本身的背景音乐 (同时可判断场景已经加载完成)
     if (event == "fdkkBgmLink") {
       store.commit('fdkk/setFdkkBGM', params.music)
       if (params.music) {
         useMusicPlayer.player.watchPlay(true)
       }
+
+      // loading完毕
+      getApp().Scene.emit("ready")
+
     }
 
     if (event == "fdkkMetadata") {
       store.commit('fdkk/setMetadata', params.metadata)
-      console.log(params.metadata,'params.metadata');
+      console.log(params.metadata, 'params.metadata');
     }
 
 
     if (event == "toggleFdkkHotspot") {
-      if (params.status=='open') {
+      if (params.status == 'open') {
         store.commit("functions/setShowScenesList", false);
         store.commit('fdkk/setShowToursList', false)
       }
 
     }
-    
+
   }
 }
 
@@ -92,19 +116,16 @@ onUnmounted(() => {
   window.removeEventListener('message', handleMessage)
 })
 
+const handleOther = (data) => {
+  otherLink.value = data;
+  rnd.value = Math.random();
+}
+const handleChange = (data) => {
+  embeM.value = data;
+  rnd.value = Math.random();
+}
 
 
-  // window.parent.postMessage(
-  //   {
-  //     source: "cdf",
-  //     event: "audioMuted",
-  //     params: {
-  //       audioMuted: audioMuted.value,
-  //     },
-  //   },
-  //   "*"
-  // );
-
 </script>
 
 <style lang="scss" scoped>

+ 144 - 0
packages/qjkankan-view/src/components/Fdkk/v3fdkkmobile/iframe.vue

@@ -0,0 +1,144 @@
+<template>
+  <div class="ifrcon">
+    <vvheader :somedatainfo="somedatainfo" :bgmstatus="bgmstatus" :params="params" class="hhh" />
+    <iframe allowfullscreen="true" ref="iframe" id="showifr" :src="url" frameborder="0" @load="onIframLoad"></iframe>
+  </div>
+</template>
+
+<script>
+import browser from "@/utils/browser";
+import vvheader from "./vheader.vue";
+import { useStore } from "vuex";
+import { getApp } from '@/app'
+
+
+export default {
+  props: ["url", "bgmUrl", "somedatainfo"],
+  components: { vvheader },
+  data() {
+    return {
+      store: useStore(),
+      params: "",
+      bgmstatus: false,
+    };
+  },
+  computed: {
+    tempBgm() {
+      return this.bgmUrl;
+    },
+  },
+  methods: {
+    onIframLoad() {
+      window.onmessage = (e) => {
+
+        if (e.data.source != "4dage") {
+          return;
+        }
+        console.log(e.data, 'e.data.params');
+
+        getApp().Scene.emit("ready")
+
+        if (e.data.event == "guide-stop") {
+          this.store.commit('fdkk/setV3ToursStatus', false)
+        }
+
+
+        if (e.data.event == "guide-rooms") {
+          this.store.commit('fdkk/setToursList', e.data.params)
+        }
+
+        if (e.data.event == "link-click") {
+          if (
+            e.data.params.url.indexOf("https://www.4dkankan.com/spc.html?") > -1 ||
+            e.data.params.url.indexOf("https://www.4dkankan.com/smobile.html?") > -1 ||
+            e.data.params.url.indexOf("https://test.4dkankan.com/spc.html?") > -1 ||
+            e.data.params.url.indexOf("https://test.4dkankan.com/smobile.html?") > -1
+          ) {
+            let m = browser.urlHasValueFromUrl("m", e.data.params.url);
+            this.$emit("changeUrl", m);
+          } else {
+            this.$emit("otherUrl", e.data.params.url);
+          }
+        }
+
+        if (e.data.event == "action") {
+          // this.$bus.emit("currentMode", e.data.params);
+
+          if (e.data.params.type == "playMusic") {
+            console.log(e.data.params.data.status, "e.data");
+            this.bgmstatus = e.data.params.data.status;
+          }
+
+          if (e.data.params.type == "mode") {
+
+            if (e.data.params.data.mode != 'pano') {
+              this.store.commit("functions/setShowScenesList", false)
+            }
+          }
+
+
+
+          // store.commit('fdkk/setIsPlayTours', params.isPlay)
+
+        }
+
+        if (e.data.event == "loaded") {
+          console.log(e.data.params, 'e.data.params');
+          this.params = e.data.params;
+          this.$refs.iframe.contentWindow.postMessage(
+            {
+              source: "mingyuan",
+              event: "guide-rooms",
+            },
+            "*"
+          );
+
+          this.$refs.iframe.contentWindow.postMessage(
+            {
+              source: "mingyuan",
+              event: "settings",
+              params: {
+                playMusic: true,
+              },
+            },
+            "*"
+          );
+        }
+      };
+    },
+    handleBGM(status) {
+      this.$refs.iframe.contentWindow.postMessage(
+        {
+          source: "mingyuan",
+          event: "settings",
+          params: {
+            playMusic: status,
+          },
+        },
+        "*"
+      );
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.ifrcon {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  >iframe {
+    width: 100%;
+    height: 100%;
+  }
+
+  .hhh {
+    position: absolute;
+    left: 50%;
+    transform: translate(-50%);
+    z-index: 999;
+    top: 10px;
+  }
+}
+</style>

+ 619 - 0
packages/qjkankan-view/src/components/Fdkk/v3fdkkmobile/vheader.vue

@@ -0,0 +1,619 @@
+<template>
+  <div class="header app" v-if="params.title" @touchmove.prevent>
+    <div class="left show">
+      <div class="back" v-if="isVR" @click="outVR()">
+        <i class="iconfont icon_back"></i>
+      </div>
+    </div>
+    <div v-show="!isVR" class="title" :class="{ up: showDescription, empty: false }" @click="onShowDescription">
+      <div>
+        <span>
+          {{ params.title }}
+        </span>
+        <i class="iconfont icon-show_drop-down"></i>
+      </div>
+    </div>
+    <div class="right">
+
+    </div>
+    <transition appear name="custom-classes-transition" enter-active-class="animated fadeInUp short faster"
+      leave-active-class="animated fadeOutDown short faster">
+      <div class="content" v-if="showDescription" @click="onShowDescription">
+        <div>
+          <div v-html="params.description || '四维看看,让空间说故事'"></div>
+        </div>
+      </div>
+    </transition>
+    <template v-if="true">
+      <transition appear name="custom-classes-transition" enter-active-class="animated slideInUp faster"
+        leave-active-class="animated slideOutDown faster">
+        <div class="app-share" v-if="false">
+          <ul>
+            <li @click="onShare('weixin')">
+              <i class="iconfont icon_wechat"></i>
+              <div>微信</div>
+            </li>
+            <li @click="onShare('weixinFriend')">
+              <i class="iconfont icon_friend"></i>
+              <div>朋友圈</div>
+            </li>
+            <li @click="onShare('qq')">
+              <i class="iconfont icon_qq"></i>
+              <div>QQ</div>
+            </li>
+            <li @click="onShare('copy')">
+              <i class="iconfont iconlink"></i>
+              <div>复制链接</div>
+            </li>
+          </ul>
+          <div @click="showShare = false">取消</div>
+        </div>
+      </transition>
+    </template>
+    <div class="url-share" v-show="false">
+      <div>
+        <div class="tips">
+          <h4>分享</h4>
+          <i class="iconfont iconshow_cancel" @click="showCopy = false"></i>
+        </div>
+        <div class="url">分享链接</div>
+        <div class="btns">
+          <button class="ui-button cancel" @click="showCopy = false">
+            取消
+          </button>
+          <button class="ui-button submit btn-share-copy" :data-clipboard-text="shareURL">
+            分享
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { useStore } from "vuex";
+
+
+export default {
+  props: ["description", "params", "bgmUrl", "bgmstatus", "somedatainfo"],
+  data() {
+    return {
+      showDescription: false,
+      showMore: false,
+      shareURL: "",
+      store: useStore(),
+      isMusicPlaying: this.bgmstatus,
+    };
+  },
+
+  computed: {
+    isVR() {
+      return this.store.getters["functions/vrStatus"];
+    },
+  },
+
+  methods: {
+    onMenuClick(name) {
+      this.$nextTick(() => {
+        if (name == "music") {
+          this.isMusicPlaying = !this.isMusicPlaying
+        }
+      });
+    },
+    onShowDescription() {
+      this.showDescription = !this.showDescription;
+      this.showMore = false;
+    },
+    onShowMore() {
+      this.showMore = !this.showMore;
+      this.showDescription = false;
+    },
+    outVR() {
+      let ele = document.getElementById("showifr");
+      ele.contentWindow.postMessage(
+        {
+          source: "mingyuan",
+          event: "vr-out",
+        },
+        "*"
+      );
+      this.store.commit("functions/setVrStatus", false);
+    },
+  },
+
+  watch: {
+    bgmUrl(newVal) {
+      if (newVal) {
+        this.isMusicPlaying = true
+      }
+    },
+    isMusicPlaying(newVal) {
+      this.$emit('toggleBGM', newVal)
+    },
+    bgmstatus(newVal) {
+      this.isMusicPlaying = newVal
+    }
+  },
+
+  mounted() {
+
+  },
+};
+</script>
+<style lang="scss" scoped>
+.disable {
+  opacity: 1;
+}
+
+.header {
+  font-size: 16px;
+  position: absolute;
+  top: 12px;
+  left: 0;
+  height: 38px;
+  width: 100%;
+  z-index: 101;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.4);
+
+  &.app {
+    top: 26px;
+  }
+
+  .left {
+    width: 40px;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    &.show {
+      visibility: visible;
+      pointer-events: auto;
+
+      .back {
+        visibility: visible;
+      }
+    }
+
+    .back {
+      width: 26px;
+      height: 26px;
+      border-radius: 50%;
+      background-color: rgba(0, 0, 0, 0.3);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      visibility: hidden;
+
+      i {
+        font-size: 14px;
+      }
+    }
+
+    .back-pano {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      padding-left: 15px;
+
+      i {
+        font-size: 14px;
+      }
+    }
+  }
+
+  .right {
+    position: relative;
+    width: 1.28rem;
+    height: 100%;
+    padding-right: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+
+    >i {
+      font-size: 20px;
+    }
+
+    >div {
+      position: absolute;
+      display: flex;
+      flex-direction: column;
+      right: 10px;
+      top: 50px;
+      padding: 4px 16px;
+      background-color: rgba(0, 0, 0, 0.5);
+      border-radius: 5px;
+
+      &::after {
+        content: "";
+        position: absolute;
+        top: -5px;
+        right: 10px;
+        width: 0;
+        height: 0;
+        border-width: 0 5px 5px;
+        border-style: solid;
+        border-color: transparent transparent rgba(0, 0, 0, 0.5);
+      }
+
+      &.flex {
+        li {
+          span {
+            flex: 1;
+            width: auto;
+            // min-width: 2.8rem;
+          }
+        }
+      }
+
+      li {
+        position: relative;
+        display: flex;
+        align-items: center;
+        margin: 0.3rem 0;
+        padding: 0.3rem 0;
+        font-size: 0;
+
+        i {
+          position: absolute;
+          left: 0;
+          top: 50%;
+          transform: translateY(-50%);
+          font-size: 16px;
+        }
+
+        b {
+          position: absolute;
+          left: 0.32rem;
+          top: 0.33rem;
+          width: 7px;
+          height: 7px;
+          border-radius: 50%;
+          background-color: #00c2c4;
+
+          i {
+            color: #fff;
+            font-size: 12px;
+            transform: scale(0.3, 0.3);
+            top: -4px;
+            left: -3px;
+          }
+        }
+
+        span {
+          width: 72px;
+          white-space: nowrap;
+          text-align: left;
+          margin-left: 28px;
+          font-size: 14px;
+          text-indent: 8px;
+        }
+      }
+
+      .home {
+        border-top: solid 1px rgba(255, 255, 255, 0.4);
+
+        a {
+          width: 72px;
+          margin-top: 12px;
+          margin-bottom: 0;
+          margin-left: auto;
+          margin-right: auto;
+          display: block;
+          text-decoration: none;
+          overflow: hidden;
+
+          img {
+            width: 100%;
+            outline: none;
+            border: none;
+          }
+        }
+      }
+    }
+  }
+
+  .title {
+    display: flex;
+    flex: 1;
+    width: 100%;
+    height: 100%;
+    font-size: 14px;
+    letter-spacing: 1px;
+    align-items: center;
+    justify-content: center;
+
+    >div {
+      display: flex;
+      transition: background 0.3s ease, min-width 0.3s ease, border-radius 0.3s ease;
+      align-items: center;
+      justify-content: center;
+      padding-right: 12px;
+      padding-left: 12px;
+      white-space: nowrap;
+      position: absolute;
+      height: 100%;
+      min-width: 100%;
+      overflow: visible;
+      pointer-events: none;
+      background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.15) 50%, rgba(0, 0, 0, 0) 100%);
+    }
+
+    span {
+      display: inline-block;
+      position: relative;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      max-width: 80%;
+    }
+
+    i {
+      transition: all 0.3s;
+      position: absolute;
+      left: 50%;
+      bottom: 0;
+      font-size: 0.2rem;
+      transform: translateX(-50%);
+      margin-bottom: 0.07rem;
+    }
+
+    &.up {
+      i {
+        transform: translateX(-50%) rotate(180deg);
+      }
+
+      >div {
+        min-width: 0;
+        //position: static;
+        flex-shrink: 0;
+        background-color: rgba(0, 0, 0, 0.5);
+        border-radius: 44px;
+      }
+
+      &.drak {
+        >div {
+          background-color: rgba(0, 0, 0, 0.8);
+        }
+      }
+    }
+
+    &.empty {
+      i {
+        display: none;
+      }
+    }
+  }
+
+  .content {
+    position: absolute;
+    top: 60px;
+    left: 30px;
+    right: 30px;
+    padding: 10px;
+    background: rgba(0, 0, 0, 0.5);
+    border-radius: 5px;
+    font-size: 14px;
+    text-align: center;
+
+    &.drak {
+      background: rgba(0, 0, 0, 0.8);
+    }
+
+    >div {
+      display: inline-block;
+      text-align: left;
+      letter-spacing: 1px;
+      word-break: break-all;
+      white-space: normal;
+      line-height: 1.5;
+      // h4 {
+      //     margin: 0;
+      //     padding: 0;
+      //     margin-bottom: 0.28rem;
+      //     font-size: 0.43rem;
+      //     padding-left: 0.18789rem;
+      //     width: 100%;
+      //     position: relative;
+      //     &::before {
+      //         content: "";
+      //         position: absolute;
+      //         left: 0;
+      //         top: 50%;
+      //         height: 80%;
+      //         transform: translateY(-50%);
+      //         width: 2px;
+      //         background-color: @color;
+      //     }
+      // }
+
+    }
+
+    // &::after {
+    //     content: "";
+    //     position: absolute;
+    //     top: -6px;
+    //     left: 50%;
+    //     margin-left: -3px;
+    //     width: 0;
+    //     height: 0;
+    //     border-width: 0 7px 6px;
+    //     border-style: solid;
+    //     border-color: transparent transparent rgba(0, 0, 0, 0.5);
+    // }
+  }
+
+  .url-share {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.1);
+
+    >div {
+      position: absolute;
+      left: 0.6rem;
+      right: 0.6rem;
+      top: 50%;
+      transform: translateY(-50%);
+      border-radius: 4px;
+      background-color: rgba(0, 0, 0, 0.5);
+      padding: 0.31579rem;
+      font-size: 0.36842rem;
+
+      .tips {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        h4 {
+          margin: 0;
+          font-size: 0.42105rem;
+        }
+
+        i {
+          font-size: 0.6rem;
+        }
+      }
+
+      .url {
+        display: -webkit-box;
+        color: #fff;
+        text-align: center;
+        padding: 0.26316rem;
+        width: 100%;
+        height: 1.8rem;
+        margin: 0.7rem 0rem;
+        border-radius: 0.07rem;
+        line-height: 1.99;
+        font-size: 0.36842rem;
+        background-color: rgba(0, 0, 0, 0.35);
+        overflow: hidden;
+        text-overflow: ellipsis;
+        word-break: break-all;
+        word-wrap: break-word;
+        -webkit-line-clamp: 2; //在第几行加省略号
+        -webkit-box-orient: vertical;
+      }
+
+      .btns {
+        display: flex;
+        justify-content: space-between;
+
+        button {
+          font-size: 0.36842rem;
+          width: 47%;
+          height: 1.05263rem;
+          border-radius: 1.05263rem;
+
+          &.submit {
+            background: #00c2c4;
+          }
+        }
+      }
+    }
+  }
+
+  .app-share {
+    position: fixed;
+    left: 0;
+    bottom: 0;
+    width: 100%;
+    background-color: #fff;
+    text-shadow: none;
+
+    i {
+      font-size: 2rem;
+    }
+
+    ul {
+      display: flex;
+      justify-content: space-around;
+
+      div {
+        text-align: center;
+        margin-top: -0.3rem;
+        font-size: 0.34rem;
+      }
+
+      li {
+        padding-bottom: 0.5rem;
+      }
+    }
+
+    >div {
+      font-size: 16px;
+      text-align: center;
+      padding: 0.5rem;
+      border-top: solid 1px #eeeeee;
+    }
+  }
+}
+
+@media (orientation: landscape) {
+  .header {
+    height: 45px;
+
+    &.app {
+      top: 0.2rem;
+    }
+
+    .left {
+      .back {}
+    }
+
+    .right {
+      >i {
+        font-size: 20px;
+      }
+    }
+
+    .title {
+      font-size: 14px;
+    }
+
+    .content {
+      padding: 10px;
+
+      >div {
+        line-height: 1.5;
+      }
+    }
+  }
+}
+</style>
+<style lang="scss">
+.animated.short {
+  &.faster {
+    animation-duration: 0.3s;
+  }
+
+  @keyframes fadeInUp {
+    0% {
+      opacity: 0;
+      transform: translate3d(0, 1rem, 0);
+    }
+
+    to {
+      opacity: 1;
+      transform: translateZ(0);
+    }
+  }
+
+  @keyframes fadeOutDown {
+    0% {
+      opacity: 1;
+    }
+
+    to {
+      opacity: 0;
+      transform: translate3d(0, 1rem, 0);
+    }
+  }
+}
+</style>

+ 15 - 3
packages/qjkankan-view/src/components/Pano/index.vue

@@ -10,6 +10,7 @@ import { useApp, getApp } from "@/app";
 import Fdkk from "../Fdkk";
 import { getFdkkInfo } from "@/apis";
 import { useMusicPlayer, useSoundPlayer } from '@/utils/sound'
+import browser from "@/utils/browser";
 
 
 //背景音乐
@@ -27,17 +28,20 @@ const currentPlaying = computed(() => store.getters["functions/currentPlaying"])
 const loadScene = async (currentScene) => {
   let app = await getApp();
   if (app.krpanoDom) {
-    let { sceneCode, initVisual } = currentScene;
+    let { sceneCode, initVisual,someData } = currentScene;
     app.krpanoDom.call(`skin_loadscene('scene_${sceneCode}',${initVisual ? initVisual.vlookat : '0'},${initVisual ? initVisual.hlookat : '0'})`);
+    console.log(sceneCode,someData);
 
-    if (currentScene.someData && currentScene.someData.hotspots.length > 0) {
-      app.Tags.initHotspot(currentScene.someData.hotspots, false);
+    if (someData && someData.hotspots &&someData.hotspots.length > 0) {
+      app.Tags.initHotspot(someData.hotspots, false);
     }
   }
 };
 
 watch(currentScene, (newVal) => {
   history.replaceState(null, null, "".concat(window.location.pathname, "?").concat(`id=${metadata.value.id}&vr=${newVal.sceneCode}`));
+
+  //默认版本是v4
   store.commit("scene/setFdkkCurrentVersion", 'v4');
   if (newVal.type == 'pano') {
     if (store.getters['fdkk/fdkkBGM']) {
@@ -52,6 +56,14 @@ watch(currentScene, (newVal) => {
     getFdkkInfo({ num: newVal.sceneCode }).then((data) => {
       if (data.data.isUpgrade != void 0) {
         store.commit("scene/setFdkkCurrentVersion", data.data.isUpgrade !== 0 ? 'v4' : 'v3');
+        if (data.data.isUpgrade === 0) {
+          // 该v3场景有背景音乐
+          if (data.data.bgMusic) {
+            if (!browser.isMobile()) {
+              musicPlayer.pause()
+            }
+          }
+        }
       }
     })
   }

+ 19 - 18
packages/qjkankan-view/src/components/UIGather/control.vue

@@ -22,8 +22,9 @@
     </li>
 
     <!-- 解说音频 -->
-    <li @click="onIsCommentary" v-if="currentScene.explanation&&currentScene.explanation.audioUrl">
-      <img :src="require(`@/assets/images/icon/${showCommentaryPlaying ? 'commentary.svg' : 'commentary_disabled.svg'}`)"
+    <li @click="onIsCommentary" v-if="currentScene.explanation && currentScene.explanation.audioUrl">
+      <img
+        :src="require(`@/assets/images/icon/${showCommentaryPlaying ? 'commentary.svg' : 'commentary_disabled.svg'}`)"
         alt="" />
     </li>
 
@@ -32,7 +33,7 @@
         alt="" />
     </li>
 
-    <li @click="onVR" v-if="currentScene.type != '4dkk' || (currentScene.type=='pano' && fdkkmetadata.controls.showVR)">
+    <li @click="onVR" v-if="currentScene.type != '4dkk'">
       <img :src="require(`@/assets/images/icon/vr@2x.png`)" alt="" />
     </li>
 
@@ -61,7 +62,7 @@ import { useApp } from "@/app";
 import introduce from "./control/text";
 import telephone from "./control/telephone";
 import clink from "./control/link";
-import { useMusicPlayer,useSoundPlayer } from '@/utils/sound'
+import { useMusicPlayer, useSoundPlayer } from '@/utils/sound'
 
 
 //背景音乐
@@ -95,21 +96,21 @@ watchEffect(() => {
   if (currentScene.value.explanation) {
     let { audioUrl, openByDefault, repeat } = currentScene.value.explanation
     if (audioUrl) {
-        store.commit("functions/setCommentaryUrl", {
-          src:audioUrl,
-          loop:repeat,
-          openByDefault:openByDefault
-        });
-       
-
-        useSoundPlayer.player.isLock = false
-        useSoundPlayer.player.watchPlay()
-    }else{
+      store.commit("functions/setCommentaryUrl", {
+        src: audioUrl,
+        loop: repeat,
+        openByDefault: openByDefault
+      });
+
+
+      useSoundPlayer.player.isLock = false
+      useSoundPlayer.player.watchPlay()
+    } else {
       store.commit("functions/setCommentaryUrl", '');
       useSoundPlayer.player.isLock = true
       soundPlayer.pause()
     }
-  } else{
+  } else {
     store.commit("functions/setCommentaryUrl", '');
     useSoundPlayer.player.isLock = true
     soundPlayer.pause()
@@ -226,13 +227,13 @@ onMounted(() => {
   })
 })
 
-watch(()=>[showCommentaryPlaying.value,showMusicPlaying.value],()=>{
+watch(() => [showCommentaryPlaying.value, showMusicPlaying.value], () => {
   if (!showCommentaryPlaying.value && !showMusicPlaying.value) {
     store.commit("functions/setCurrentPlaying", '');
-  } else{
+  } else {
     if (showCommentaryPlaying.value) {
       store.commit("functions/setCurrentPlaying", 'commentary');
-    } else if(showMusicPlaying.value){
+    } else if (showMusicPlaying.value) {
       store.commit("functions/setCurrentPlaying", 'bgm');
     }
   }

+ 5 - 2
packages/qjkankan-view/src/components/UIGather/control/telephone.vue

@@ -57,9 +57,9 @@ const close = () => {
       width: 240px;
       background: rgba(0, 0, 0, 0.6);
       border-radius: 10px;
-      font-size: 14px;
+      font-size: 18px;
       color: #fff;
-      padding: 14px 26px;
+      padding: 26px;
       max-height: calc(100vh - 100px);
       overflow-y: auto;
       .theader {
@@ -75,9 +75,12 @@ const close = () => {
       .telephone{
         display: flex;
         align-items: center;
+        margin-left: -10px;
         >span{
           margin-top: 6px;
           display: inline-block;
+          font-size: 14px;
+
         }
       }
 

+ 3 - 0
packages/qjkankan-view/src/components/UIGather/control/text.vue

@@ -43,6 +43,9 @@ const close = () => {
   height: 100%;
   width: 100%;
   padding-top: 70px;
+  :deep(p) {
+    line-height: 24px;
+  }
 
   .txtbody {
     width: 744px;

+ 19 - 6
packages/qjkankan-view/src/components/UIGather/index.vue

@@ -2,14 +2,15 @@
       <Logo />
       <Tips />
       <div v-show="showUI">
-            <Control v-if="fdkkCurrentVersion=='v4'" />
-            <Menu v-if="fdkkCurrentVersion=='v4'" />
-            <div @click="onIsShowList" v-else class="v3daolan" :class="{active:isShowScenesList}">
+            <Control v-if="fdkkCurrentVersion == 'v4'" />
+            <Menu v-if="fdkkCurrentVersion == 'v4'" />
+            <div @click="onIsShowList" v-else class="v3daolan" :class="{ active: isShowScenesList }">
                   <img :src="require(`@/assets/images/icon/${isShowScenesList ? 'function_off.svg' : 'function_on.svg'}`)"
                         alt="" />
                   <span>场景导航</span>
             </div>
             <sceneList />
+            <div class="btnmask"></div>
       </div>
 </template>
   
@@ -33,13 +34,13 @@ const showUI = ref(false)
 
 
 const onIsShowList = (data) => {
-   store.commit("functions/setShowScenesList", !isShowScenesList.value);
+      store.commit("functions/setShowScenesList", !isShowScenesList.value);
 };
 
 
 useApp().then((app) => {
-      app.Scene.on("ready", (data) => {
-            if (currentScene.value.type == '4dkk') {
+      app.Scene.on("ready", () => {
+            if (currentScene && currentScene.value.type == '4dkk' && !showUI.value) {
                   showUI.value = true
             }
       })
@@ -74,4 +75,16 @@ useApp().then((app) => {
 
       }
 }
+
+.btnmask {
+      width: 100%;
+      position: fixed;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      pointer-events: none;
+      opacity: 0.3;
+      height: 50px;
+      background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 52%, #000000 100%);
+}
 </style>

+ 406 - 0
packages/qjkankan-view/src/components/UIGather/list - 副本 (2).vue

@@ -0,0 +1,406 @@
+<template>
+  <div class="bar-list"
+    v-if="show && !((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)"
+    :class="{ barshow: isShowScenesList }">
+    <div class="top-con">
+      <div class="jssor-container" id="swScenes"
+        :style="`width:${currentScenesList.length * (swidth['swScenes'] + 10) - 10}px`"
+        v-if="currentScenesList.length > 0">
+        <ul class="jssor-wrapper" data-u="slides"
+          :style="`width:${currentScenesList.length * (swidth['swScenes'] + 10) - 10}px`">
+          <li @click="tabCurrentScene(item)" class="jssor-slide" :class="{
+            active: currentScene.sceneCode == item.sceneCode,
+            loopspan: item.sceneTitle.length > spanlength && currentScene.sceneCode == item.sceneCode,
+          }" v-for="(item, i) in currentScenesList" :key="i">
+            <div :style="{ backgroundImage: `url(${item.icon})`, backgroundSize: 'cover' }">
+              <i class="iconfont " :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
+              <div>
+                <span>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle
+                }}</span>
+              </div>
+            </div>
+          </li>
+        </ul>
+      </div>
+
+      <div class="jssor-container" id="swSecondary" v-if="secondaryList.length > 1"
+        :style="`width:${secondaryList.length * (swidth['swSecondary'] + 10) - 10}px`">
+        <ul data-u="slides" class="jssor-wrapper"
+          :style="`width:${secondaryList.length * (swidth['swSecondary'] + 10) - 10}px`">
+          <li class="jssor-slide" @click="tabSecondary(item)" :class="{
+            active: currentSecondary.id == item.id,
+            loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
+          }" v-for="(item, i) in secondaryList" :key="i">
+            <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+          </li>
+        </ul>
+      </div>
+    </div>
+
+    <div class="jssor-container" id="swcatalogRoot"
+      :style="`width:${metadata.catalogRoot.length * (swidth['swcatalogRoot'] + 10) - 10}px`"
+      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
+      <ul data-u="slides" class="jssor-wrapper" v-if="metadata.catalogRoot.length > 1"
+        :style="`width:${metadata.catalogRoot.length * (swidth['swcatalogRoot'] + 10) - 10}px`">
+        <li class="jssor-slide" :class="{
+          active: currentCatalogRoot.id == item.id,
+          loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
+        }" :style="{ backgroundColor: `rgba(0,0,0,0)` }" @click="tabRoot(item)"
+          v-for="(item, i) in metadata.catalogRoot" :key="i">
+
+          <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch, computed, onMounted, nextTick } from "vue";
+import { useStore } from "vuex";
+import { useApp } from "@/app";
+const store = useStore();
+
+const spanlength = ref(6);
+
+let jssoroptions = {
+  $AutoPlay: 0,
+  $AutoPlaySteps: 10,
+  $SlideDuration: 160,
+  $SlideSpacing: 10,
+  $ArrowNavigatorOptions: 0,
+  $Loop: 0,
+  $BulletNavigatorOptions: 0
+}
+
+
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
+const currentSecondary = computed(() => store.getters["scene/currentSecondary"]);
+
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+
+const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
+
+
+const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
+
+const show = ref(false);
+
+const tabCurrentScene = (data) => {
+  store.commit("scene/setCurrentScene", data);
+};
+
+const tabSecondary = (data) => {
+  store.commit("scene/setCurrentSecondary", data);
+};
+
+const tabRoot = (data) => {
+  store.commit("scene/setCurrentCatalogRoot", data);
+};
+
+
+const swcatalogRoot = ref(null)
+const swSecondary = ref(null)
+const swScenes = ref(null)
+
+
+const swidth = ref({
+  "swcatalogRoot": 104,
+  "swSecondary": 84,
+  "swScenes": 72,
+})
+
+
+const loadSceneList = () => {
+
+  if (swScenes.value) {
+    swScenes.value.$Destroy()
+    swScenes.value = null
+  }
+
+
+
+  // 场景分组
+  if (document.querySelector('#swScenes')) {
+    swScenes.value = new $JssorSlider$('swScenes', {
+      ...jssoroptions,
+      $SlideWidth: swidth.value['swScenes'],
+    });
+  }
+
+}
+
+
+const loadSecondaryList = () => {
+
+  let swSecondaryDom = document.querySelector('#swSecondary')
+
+  if (swSecondary.value) {
+    swSecondary.value.$ReloadSlides();
+  }
+
+  //二级分组
+  if (swSecondaryDom) {
+  console.log(swSecondaryDom.innerHTML,'swSecondary.value');
+
+    swSecondary.value = new $JssorSlider$('swSecondary', {
+      ...jssoroptions,
+      $SlideWidth: swidth.value['swSecondary'],
+    });
+  }
+};
+
+watch(currentSecondary, () => {
+  loadSceneList();
+});
+
+watch(currentCatalogRoot, () => {
+  loadSecondaryList();
+});
+
+onMounted(() => {
+  useApp().then(async (app) => {
+    show.value = true;
+
+    nextTick(() => {
+
+      swcatalogRoot.value = new $JssorSlider$('swcatalogRoot', {
+        ...jssoroptions,
+        $SlideWidth: swidth.value['swcatalogRoot'],
+      });
+
+      loadSecondaryList();
+      loadSceneList();
+
+    })
+
+
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+$width: 1150px;
+
+.bar-list {
+  position: absolute;
+  bottom: 68px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  max-width: $width;
+  overflow: hidden;
+  max-height: 0;
+  transition: .3s all ease;
+  z-index: 9;
+
+  .jssor-container {
+    position: relative;
+
+    .jssor-wrapper {
+      margin: 0 auto;
+
+      .jssor-slide {
+        >div {
+          >img {
+            width: 100%;
+          }
+
+          >span,
+          >div>span {
+            cursor: pointer;
+            display: inline-block;
+            color: rgba(255, 255, 255, 0.6);
+          }
+        }
+
+        &.loopspan {
+          >div {
+
+            >span,
+            >div>span {
+              animation: 5s wordsLoop linear infinite normal;
+            }
+          }
+        }
+
+        &.active {
+          >div {
+
+            >span,
+            >div>span {
+              color: rgba(255, 255, 255, 1);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .top-con {
+    margin-bottom: 10px;
+    padding: 10px 60px;
+    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 8%, rgba(0, 0, 0, 0.5) 92%, rgba(0, 0, 0, 0) 100%);
+  }
+
+  #swcatalogRoot {
+    height: 26px;
+    margin: 0 auto;
+
+    ul {
+      height: 26px;
+
+      >li {
+        width: 104px;
+        background: rgba(0, 0, 0, 0.5);
+        border-radius: 4px;
+        padding: 4px 10px;
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        box-sizing: border-box;
+        overflow: hidden;
+        cursor: pointer;
+
+        >span {
+          width: 100%;
+          word-break: keep-all;
+          color: rgba(255, 255, 255, 0.5);
+        }
+
+        &.active {
+          border: 1px solid rgba(255, 255, 255, 1);
+
+          >span {
+            color: #fff;
+          }
+        }
+      }
+    }
+  }
+
+  #swSecondary {
+    margin: 20px auto 10px;
+    height: 20px;
+
+    ul {
+      height: 20px;
+
+      >li {
+        width: 84px;
+        box-sizing: border-box;
+        overflow: hidden;
+        padding-bottom: 6px;
+        cursor: pointer;
+
+        >span {
+          width: 100%;
+          word-break: keep-all;
+        }
+
+        &.active {
+          position: relative;
+
+          &::before {
+            content: "";
+            display: inline-block;
+            position: absolute;
+            bottom: 0;
+            width: 20px;
+            height: 2px;
+            z-index: 9999;
+            left: 50%;
+            transform: translateX(-50%);
+            background: var(--colors-primary-base);
+          }
+        }
+      }
+    }
+  }
+
+  #swScenes {
+    height: 72px;
+    margin: 0 auto;
+
+    ul {
+      height: 72px;
+
+      >li {
+        cursor: pointer;
+        width: 72px;
+        height: 72px;
+        border-radius: 6px;
+        border: 1px solid #ffffff;
+        position: relative;
+        overflow: hidden;
+
+        >div {
+          width: 100%;
+          height: 100%;
+          background-size: cover;
+
+          .iconfont {
+            position: absolute;
+            left: 4px;
+            top: 4px;
+            z-index: 99;
+
+            &::after {
+              background: rgba(0, 0, 0, 0.3);
+              content: "";
+              width: 14px;
+              height: 14px;
+              display: inline-block;
+              position: absolute;
+              top: 50%;
+              left: 50%;
+              transform: translate(-50%, -50%);
+              z-index: -1;
+              filter: blur(4px);
+            }
+          }
+
+          >div {
+            position: absolute;
+            bottom: 0;
+            left: 0;
+            height: 20px;
+            background: rgba(0, 0, 0, 0.5);
+            width: 100%;
+            overflow: hidden;
+            background-size: cover !important;
+
+            >span {
+              width: 100%;
+              line-height: 20px;
+              word-break: keep-all;
+            }
+          }
+        }
+
+        &.active {
+          border: 1px solid var(--colors-primary-base);
+        }
+      }
+    }
+  }
+}
+
+.barshow {
+  max-height: 190px;
+}
+
+@keyframes wordsLoop {
+  0% {
+    transform: translateX(100%);
+    -webkit-transform: translateX(100%);
+  }
+
+  100% {
+    transform: translateX(-100%);
+    -webkit-transform: translateX(-100%);
+  }
+}
+</style>

+ 306 - 0
packages/qjkankan-view/src/components/UIGather/list - 副本.vue

@@ -0,0 +1,306 @@
+<template>
+  <div class="bar-list" v-if="show && !((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)" :class="{barshow:isShowScenesList}">
+    <div class="top-con" >
+      <div class="swiper-container" id="swScenes" v-if="currentScenesList.length > 0">
+        <ul class="swiper-wrapper">
+          <li
+            @click="tabCurrentScene(item)"
+            class="swiper-slide"
+            :class="{
+              active: currentScene.sceneCode == item.sceneCode,
+              loopspan: item.sceneTitle.length > spanlength && currentScene.sceneCode == item.sceneCode,
+            }"
+            :style="{ backgroundImage: `url(${item.icon})` }"
+            v-for="(item, i) in currentScenesList"
+            :key="i"
+          >
+            <i class="iconfont " :class="item.type == '4dkk'?'icon-editor_3d':'icon-editor_panoramic'"></i>
+            <div>
+              <span>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle }}</span>
+            </div>
+          </li>
+        </ul>
+      </div>
+
+      <div class="swiper-container" id="swSecondary" v-if="secondaryList.length > 1">
+        <ul class="swiper-wrapper">
+          <li
+            class="swiper-slide"
+            @click="tabSecondary(item)"
+            :class="{
+              active: currentSecondary.id == item.id,
+              loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
+            }"
+            v-for="(item, i) in secondaryList"
+            :key="i"
+          >
+            <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+          </li>
+        </ul>
+      </div>
+    </div>
+
+    <div class="swiper-container" id="swcatalogRoot" v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
+      <ul class="swiper-wrapper" v-if="metadata.catalogRoot.length > 1">
+        <li
+          class="swiper-slide"
+          :class="{
+            active: currentCatalogRoot.id == item.id,
+            loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
+          }"
+          @click="tabRoot(item)"
+          v-for="(item, i) in metadata.catalogRoot"
+          :key="i"
+        >
+          <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch, computed, onMounted, nextTick } from "vue";
+import { useStore } from "vuex";
+import { useApp } from "@/app";
+const store = useStore();
+
+const spanlength = ref(6);
+
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
+const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
+const currentSecondary = computed(() => store.getters["scene/currentSecondary"]);
+
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+
+const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
+
+
+const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
+
+const show = ref(false);
+
+const tabCurrentScene = (data) => {
+  store.commit("scene/setCurrentScene", data);
+};
+
+const tabSecondary = (data) => {
+  store.commit("scene/setCurrentSecondary", data);
+};
+
+const tabRoot = (data) => {
+  store.commit("scene/setCurrentCatalogRoot", data);
+};
+
+const loadList = () => {
+  nextTick(() => {
+    let t = setTimeout(() => {
+      clearTimeout(t);
+      ["#swcatalogRoot", "#swSecondary", "#swScenes"].forEach((item) => {
+      new Swiper(item, {
+        slidesPerView: "auto",
+        centeredSlides: true,
+        spaceBetween: 10,
+        centerInsufficientSlides: true,
+        centeredSlidesBounds: true,
+        freeMode: true,
+      });
+    });
+    }, 100);
+  });
+};
+
+watch(currentSecondary, () => {
+  loadList();
+});
+
+watch(currentScenesList, () => {
+  loadList();
+});
+
+onMounted(() => {
+  useApp().then(async (app) => {
+    show.value = true;
+    loadList();
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+$width: 1150px;
+.bar-list {
+  position: absolute;
+  bottom: 68px;
+  left: 50%;
+  transform: translateX(-50%);
+  text-align: center;
+  max-width: $width;
+  min-width: 480px;
+  overflow: hidden;
+  max-height: 0;
+  transition: .3s all ease;
+  z-index: 9;
+  .swiper-container {
+    width: 100%;
+    position: relative;
+    > ul {
+      > li {
+        > span,
+        > div > span {
+          cursor: pointer;
+          display: inline-block;
+          color: rgba(255, 255, 255, 0.6);
+        }
+        &.loopspan {
+          > span,
+          > div > span {
+            animation: 5s wordsLoop linear infinite normal;
+          }
+        }
+        &.active{
+          > span,
+          > div > span {
+            color: rgba(255, 255, 255, 1);
+          }
+        }
+      }
+    }
+  }
+
+  .top-con {
+    margin-bottom: 10px;
+    padding: 10px 60px;
+    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 8%, rgba(0, 0, 0, 0.5) 92%, rgba(0, 0, 0, 0) 100%);
+  }
+
+  #swcatalogRoot {
+    > ul {
+      > li {
+        width: 104px;
+        background: rgba(0, 0, 0, 0.5);
+        border-radius: 4px;
+        padding: 4px 10px;
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        box-sizing: border-box;
+        overflow: hidden;
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+        &.active {
+          border: 1px solid rgba(255, 255, 255, 1);
+        }
+      }
+    }
+  }
+  #swSecondary {
+    margin: 20px auto 10px;
+    > ul {
+      > li {
+        width: 84px;
+        box-sizing: border-box;
+        overflow: hidden;
+        padding-bottom: 6px;
+        > span {
+          width: 100%;
+          word-break: keep-all;
+        }
+        &.active {
+          position: relative;
+          &::before {
+            content: "";
+            display: inline-block;
+            position: absolute;
+            bottom: 0;
+            width: 20px;
+            height: 2px;
+            z-index: 9999;
+            left: 50%;
+            transform: translateX(-50%);
+            background: var(--colors-primary-base);
+          }
+        }
+      }
+    }
+  }
+
+  #swScenes {
+    > ul {
+      > li {
+        cursor: pointer;
+        width: 72px;
+        height: 72px;
+        border-radius: 6px;
+        border: 1px solid #ffffff;
+        background-size: cover;
+        position: relative;
+        overflow: hidden;
+        .iconfont {
+          position: absolute;
+          left: 4px;
+          top: 4px;
+          z-index: 99;
+
+          &::after {
+            background: rgba(0, 0, 0, 0.3);
+            content: "";
+            width: 14px;
+            height: 14px;
+            display: inline-block;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            z-index: -1;
+            filter: blur(4px);
+          }
+        }
+        > div {
+          position: absolute;
+          bottom: 0;
+          left: 0;
+          height: 20px;
+          background: rgba(0, 0, 0, 0.5);
+          width: 100%;
+          overflow: hidden;
+
+          > span {
+            width: 100%;
+            line-height: 20px;
+            word-break: keep-all;
+          }
+        }
+        &.active {
+          border: 1px solid var(--colors-primary-base);
+          > div {
+            > span {
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.barshow{
+  max-height: 190px;
+}
+
+@keyframes wordsLoop {
+  0% {
+    transform: translateX(100%);
+    -webkit-transform: translateX(100%);
+  }
+
+  100% {
+    transform: translateX(-100%);
+    -webkit-transform: translateX(-100%);
+  }
+}
+
+
+
+</style>

+ 98 - 79
packages/qjkankan-view/src/components/UIGather/list.vue

@@ -1,58 +1,50 @@
 <template>
-  <div class="bar-list" v-if="show && !((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)" :class="{barshow:isShowScenesList}">
-    <div class="top-con" >
-      <div class="swiper-container" id="swScenes" v-if="currentScenesList.length > 0">
+  <div class="bar-list"
+    v-if="show && !((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)"
+    :class="{ barshow: isShowScenesList }">
+    <div class="top-con" 
+    :style="`width:${Math.max(scenesListW,secondaryW) + 120}px`">
+
+      <div class="swiper-container" :style="`width:${scenesListW}px`"
+        id="swScenes" v-if="currentScenesList.length > 0">
         <ul class="swiper-wrapper">
-          <li
-            @click="tabCurrentScene(item)"
-            class="swiper-slide"
-            :class="{
-              active: currentScene.sceneCode == item.sceneCode,
-              loopspan: item.sceneTitle.length > spanlength && currentScene.sceneCode == item.sceneCode,
-            }"
-            :style="{ backgroundImage: `url(${item.icon})` }"
-            v-for="(item, i) in currentScenesList"
-            :key="i"
-          >
-            <i class="iconfont " :class="item.type == '4dkk'?'icon-editor_3d':'icon-editor_panoramic'"></i>
+          <li @click="tabCurrentScene(item)" class="swiper-slide" :class="{
+            active: currentScene.sceneCode == item.sceneCode,
+            loopspan: item.sceneTitle.length > spanlength && currentScene.id == item.id,
+          }" :style="{ backgroundImage: `url(${item.icon})` }" v-for="(item, i) in currentScenesList" :key="i">
+            <i class="iconfont " :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
             <div>
-              <span>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle }}</span>
+              <span v-if="currentScene.id == item.id">{{item.sceneTitle}}</span>
+              <span v-else>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle
+              }}</span>
             </div>
           </li>
         </ul>
       </div>
 
-      <div class="swiper-container" id="swSecondary" v-if="secondaryList.length > 1">
+      <div class="swiper-container" id="swSecondary" :style="`width:${secondaryW}px`" v-if="secondaryList.length > 1">
         <ul class="swiper-wrapper">
-          <li
-            class="swiper-slide"
-            @click="tabSecondary(item)"
-            :class="{
-              active: currentSecondary.id == item.id,
-              loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
-            }"
-            v-for="(item, i) in secondaryList"
-            :key="i"
-          >
-            <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+          <li class="swiper-slide" @click="tabSecondary(item)" :class="{
+            active: currentSecondary.id == item.id,
+            loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
+          }" v-for="(item, i) in secondaryList" :key="i">
+            <span v-if="currentSecondary.id == item.id">{{item.name}}</span>
+            <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
           </li>
         </ul>
       </div>
     </div>
 
-    <div class="swiper-container" id="swcatalogRoot" v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
+    <div class="swiper-container" id="swcatalogRoot"
+    :style="`width:${catalogRootW}px`"
+      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
       <ul class="swiper-wrapper" v-if="metadata.catalogRoot.length > 1">
-        <li
-          class="swiper-slide"
-          :class="{
-            active: currentCatalogRoot.id == item.id,
-            loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
-          }"
-          @click="tabRoot(item)"
-          v-for="(item, i) in metadata.catalogRoot"
-          :key="i"
-        >
-          <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+        <li class="swiper-slide" :class="{
+          active: currentCatalogRoot.id == item.id,
+          loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
+        }" @click="tabRoot(item)" v-for="(item, i) in metadata.catalogRoot" :key="i">
+          <span v-if="currentCatalogRoot.id == item.id">{{item.name}}</span>
+          <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
         </li>
       </ul>
     </div>
@@ -78,11 +70,23 @@ const secondaryList = computed(() => store.getters["scene/secondaryList"]);
 
 const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
 
-
 const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
 
 const show = ref(false);
 
+
+const swidth = ref({
+  "swcatalogRoot": 104,
+  "swSecondary": 84,
+  "swScenes": 72,
+})
+
+const scenesListW = computed(()=>currentScenesList.value.length * (swidth.value['swScenes'] + 10) - 10)
+const secondaryW = computed(()=>secondaryList.value.length * (swidth.value['swSecondary'] + 10) - 10)
+const catalogRootW = computed(()=>metadata.value.catalogRoot.length * (swidth.value['swcatalogRoot'] + 10) - 10)
+
+
+
 const tabCurrentScene = (data) => {
   store.commit("scene/setCurrentScene", data);
 };
@@ -100,15 +104,15 @@ const loadList = () => {
     let t = setTimeout(() => {
       clearTimeout(t);
       ["#swcatalogRoot", "#swSecondary", "#swScenes"].forEach((item) => {
-      new Swiper(item, {
-        slidesPerView: "auto",
-        centeredSlides: true,
-        spaceBetween: 10,
-        centerInsufficientSlides: true,
-        centeredSlidesBounds: true,
-        freeMode: true,
+        new Swiper(item, {
+          slidesPerView: "auto",
+          centeredSlides: true,
+          spaceBetween: 10,
+          centerInsufficientSlides: true,
+          centeredSlidesBounds: true,
+          freeMode: true,
+        });
       });
-    });
     }, 100);
   });
 };
@@ -131,6 +135,7 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 $width: 1150px;
+
 .bar-list {
   position: absolute;
   bottom: 68px;
@@ -138,31 +143,38 @@ $width: 1150px;
   transform: translateX(-50%);
   text-align: center;
   max-width: $width;
-  min-width: 480px;
   overflow: hidden;
   max-height: 0;
   transition: .3s all ease;
   z-index: 9;
+
   .swiper-container {
     width: 100%;
     position: relative;
-    > ul {
-      > li {
-        > span,
-        > div > span {
+    margin: 0 auto;
+
+    >ul {
+      >li {
+        white-space: nowrap;
+        >span,
+        >div>span {
           cursor: pointer;
           display: inline-block;
           color: rgba(255, 255, 255, 0.6);
         }
+
         &.loopspan {
-          > span,
-          > div > span {
+
+          >span,
+          >div>span {
             animation: 5s wordsLoop linear infinite normal;
           }
         }
-        &.active{
-          > span,
-          > div > span {
+
+        &.active {
+
+          >span,
+          >div>span {
             color: rgba(255, 255, 255, 1);
           }
         }
@@ -171,14 +183,14 @@ $width: 1150px;
   }
 
   .top-con {
-    margin-bottom: 10px;
-    padding: 10px 30px;
-    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 8%, rgba(0, 0, 0, 0.5) 92%, rgba(0, 0, 0, 0) 100%);
+    margin: 0 auto 10px;
+    padding: 10px 0;
+    background: linear-gradient(268deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.4) 25%, rgba(0, 0, 0, 0.4) 75%, rgba(0, 0, 0, 0) 100%);
   }
 
   #swcatalogRoot {
-    > ul {
-      > li {
+    >ul {
+      >li {
         width: 104px;
         background: rgba(0, 0, 0, 0.5);
         border-radius: 4px;
@@ -186,30 +198,37 @@ $width: 1150px;
         border: 1px solid rgba(255, 255, 255, 0.5);
         box-sizing: border-box;
         overflow: hidden;
-        > span {
+
+        >span {
           width: 100%;
           word-break: keep-all;
         }
+
         &.active {
           border: 1px solid rgba(255, 255, 255, 1);
         }
       }
     }
   }
+
   #swSecondary {
     margin: 20px auto 10px;
-    > ul {
-      > li {
+
+    >ul {
+      >li {
         width: 84px;
         box-sizing: border-box;
         overflow: hidden;
         padding-bottom: 6px;
-        > span {
+
+        >span {
           width: 100%;
           word-break: keep-all;
         }
+
         &.active {
           position: relative;
+
           &::before {
             content: "";
             display: inline-block;
@@ -228,8 +247,8 @@ $width: 1150px;
   }
 
   #swScenes {
-    > ul {
-      > li {
+    >ul {
+      >li {
         cursor: pointer;
         width: 72px;
         height: 72px;
@@ -238,6 +257,7 @@ $width: 1150px;
         background-size: cover;
         position: relative;
         overflow: hidden;
+
         .iconfont {
           position: absolute;
           left: 4px;
@@ -258,7 +278,8 @@ $width: 1150px;
             filter: blur(4px);
           }
         }
-        > div {
+
+        >div {
           position: absolute;
           bottom: 0;
           left: 0;
@@ -267,17 +288,18 @@ $width: 1150px;
           width: 100%;
           overflow: hidden;
 
-          > span {
+          >span {
             width: 100%;
             line-height: 20px;
             word-break: keep-all;
           }
         }
+
         &.active {
           border: 1px solid var(--colors-primary-base);
-          > div {
-            > span {
-            }
+
+          >div {
+            >span {}
           }
         }
       }
@@ -285,7 +307,7 @@ $width: 1150px;
   }
 }
 
-.barshow{
+.barshow {
   max-height: 190px;
 }
 
@@ -300,7 +322,4 @@ $width: 1150px;
     -webkit-transform: translateX(-100%);
   }
 }
-
-
-
 </style>

+ 49 - 23
packages/qjkankan-view/src/components/UIGather/menu.vue

@@ -1,29 +1,48 @@
 <template>
-  <ul class="menu" >
-    <li @click="onIsShowList" v-if="!((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)">
+  <ul class="menu">
+    <li @click="onIsShowList"
+      v-if="!((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)">
       <img :src="require(`@/assets/images/icon/${isShowScenesList ? 'function_off.svg' : 'function_on.svg'}`)" alt="" />
     </li>
-   <template v-if="currentScene.type == '4dkk'">
-    <li v-if="fdkkmetadata&&fdkkmetadata.controls.showPanorama" :class="{ disabled: isPlayTours || flying }"  @click="onModeChange('panorama')">
-      <img :src="require(`@/assets/images/icon/${mode=='panorama'?'roaming_selected':'roaming_normal'}.svg`)" alt="" />
-    </li>
 
-    <li v-if="fdkkmetadata&&fdkkmetadata.controls.showFloorplan" :class="{ disabled: isPlayTours || flying }"  @click="onModeChange('floorplan')">
-      <img :src="require(`@/assets/images/icon/${mode=='floorplan'?'plane_selected':'plane_normal'}.svg`)" alt="" />
-    </li>
 
-    <li v-if="fdkkmetadata&&fdkkmetadata.controls.showDollhouse" :class="{ disabled: isPlayTours || flying }"  @click="onModeChange('dollhouse')">
-      <img :src="require(`@/assets/images/icon/${mode=='dollhouse'?'3d_selected':'3d_normal'}.svg`)" alt="" />
-    </li>
+    <div class="v-link"
+    v-if="currentScene.type == '4dkk'&&fdkkmetadata&&(fdkkmetadata.controls.showPanorama||fdkkmetadata.controls.showFloorplan||fdkkmetadata.controls.showDollhouse)"
+    ></div>
 
-    <li class="daolan" @click.stop="playTour" v-if="toursList.length>0">
-      <img :src="require(`@/assets/images/icon/${isPlayTours?'pause01':'playing01'}.svg`)" alt="" />
-      <span>导览</span>
-      <img @click.stop="openTours" :class="{active:showTours}" class="jiantou"
-        :src="require(`@/assets/images/icon/expand_arrows@2x.png`)" alt="" />
-    </li>
+    <template v-if="currentScene.type == '4dkk'">
+   
+      <li v-if="fdkkmetadata && fdkkmetadata.controls.showPanorama" :class="{ disabled: isPlayTours || flying }"
+        @click="onModeChange('panorama')">
+        <img :src="require(`@/assets/images/icon/${mode == 'panorama' ? 'roaming_selected' : 'roaming_normal'}.svg`)"
+          alt="" />
+      </li>
+
+      <li v-if="fdkkmetadata && fdkkmetadata.controls.showFloorplan" :class="{ disabled: isPlayTours || flying }"
+        @click="onModeChange('floorplan')">
+        <img :src="require(`@/assets/images/icon/${mode == 'floorplan' ? 'plane_selected' : 'plane_normal'}.svg`)"
+          alt="" />
+      </li>
+
+      <li v-if="fdkkmetadata && fdkkmetadata.controls.showDollhouse" :class="{ disabled: isPlayTours || flying }"
+        @click="onModeChange('dollhouse')">
+        <img :src="require(`@/assets/images/icon/${mode == 'dollhouse' ? '3d_selected' : '3d_normal'}.svg`)" alt="" />
+      </li>
+
+      <div class="v-link"
+    v-if="toursList.length > 0&&fdkkmetadata&&(fdkkmetadata.controls.showPanorama||fdkkmetadata.controls.showFloorplan||fdkkmetadata.controls.showDollhouse)"
+      
+      ></div>
+
+      <li class="daolan" @click.stop="playTour" v-if="toursList.length > 0">
+        <img :src="require(`@/assets/images/icon/${isPlayTours ? 'pause01' : 'playing01'}.svg`)" alt="" />
+        <span>导览</span>
+        <img @click.stop="openTours" :class="{ active: showTours }" class="jiantou"
+          :src="require(`@/assets/images/icon/expand_arrows@2x.png`)" alt="" />
+      </li>
+
+    </template>
 
-   </template>
   </ul>
 </template>
 
@@ -54,7 +73,7 @@ const openTours = () => {
   if (isShowScenesList.value) {
     store.commit("functions/setShowScenesList", false);
   }
-  
+
   store.commit('fdkk/setShowToursList', !showTours.value)
 }
 
@@ -74,12 +93,12 @@ const onModeChange = name => {
     {
       source: "qjkankan",
       event: "setMode",
-      params:{
-        name:name
+      params: {
+        name: name
       }
     },
     "*"
-  );    
+  );
 }
 
 
@@ -107,6 +126,13 @@ onMounted(() => {
     }
   }
 
+  .v-link{
+    width: 1px;
+    height: 26px;
+    margin-top: 5px;
+    background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #FFFFFF 49%, rgba(255, 255, 255, 0) 100%);
+  }
+
   .daolan {
     display: flex;
     width: auto;

+ 265 - 0
packages/qjkankan-view/src/components/UIGather/mobile/control.fdkk.vue

@@ -0,0 +1,265 @@
+<template>
+  <ul class="control-fdkk">
+
+    <li class="daolan" :class="{ v3daolan: fdkkCurrentVersion == 'v3' }" @click.stop="playTour"
+      v-if="toursList.length > 0">
+      <img
+        :src="require(`@/assets/images/icon/${(fdkkCurrentVersion == 'v3' ? v3toursStatus : isPlayTours) ? 'pause01@2x' : 'playing01@2x'}.png`)"
+        alt="" />
+      <span>导览</span>
+      <img @click.stop="openTours" :class="{ active: showTours }" class="jiantou"
+        :src="require(`@/assets/images/icon/expand_arrows@2x.png`)" alt="" />
+      <div class="vlink" ></div>
+    </li>
+
+    <template v-if="fdkkCurrentVersion != 'v3'">
+      <li v-if="fdkkmetadata && fdkkmetadata.controls.showPanorama" :class="{ disabled: isPlayTours || flying }"
+        @click="onModeChange('panorama')">
+        <img :src="require(`@/assets/images/icon/${mode == 'panorama' ? 'roaming_selected@2x' : 'roaming_normal@2x'}.png`)"
+          alt="" />
+      </li>
+
+      <li v-if="fdkkmetadata && fdkkmetadata.controls.showFloorplan" :class="{ disabled: isPlayTours || flying }"
+        @click="onModeChange('floorplan')">
+        <img :src="require(`@/assets/images/icon/${mode == 'floorplan' ? 'plane_selected@2x' : 'plane_normal@2x'}.png`)"
+          alt="" />
+      </li>
+
+      <li v-if="fdkkmetadata && fdkkmetadata.controls.showDollhouse" :class="{ disabled: isPlayTours || flying }"
+        @click="onModeChange('dollhouse')">
+        <img :src="require(`@/assets/images/icon/${mode == 'dollhouse' ? '3d_selected@2x' : '3d_normal@2x'}.png`)" alt="" />
+      </li>
+
+      <li class="vlink"
+        v-if="fdkkmetadata && (fdkkmetadata.controls.showPanorama || fdkkmetadata.controls.showFloorplan || fdkkmetadata.controls.showDollhouse)">
+      </li>
+
+    </template>
+
+
+
+    <!-- 自定义链接 -->
+    <li @click="onLink" v-if="customLink && customLink.isShow">
+      <img :src="require(`@/assets/images/icon/link@2x.png`)" alt="" />
+    </li>
+
+    <!-- 联系电话 -->
+    <li v-if="customTelephone && customTelephone.isShow">
+      <a :href="`tel:${customTelephone.value}`">
+        <img :src="require(`@/assets/images/icon/telephone@2x.png`)" alt="" />
+      </a>
+    </li>
+
+    <!-- 简介 -->
+    <li @click="onIntroduce" v-if="metadata.description">
+      <img :src="require(`@/assets/images/icon/text@2x.png`)" alt="" />
+    </li>
+
+
+    <!-- 背景音乐 -->
+    <li @click="onIsBGM" v-if="metadata.backgroundMusic && metadata.backgroundMusic.id">
+      <img :src="require(`@/assets/images/icon/${showMusicPlaying ? 'music@2x.png' : 'music_disabled@2x.png'}`)"
+        alt="" />
+    </li>
+
+
+  </ul>
+
+  <teleport to='body'>
+    <introduce v-if="showIntroduce" />
+    <telephone v-if="showTelephone" />
+    <clink v-if="showLink" />
+  </teleport>
+
+</template>
+
+<script setup>
+import { ref, watch, computed, onMounted, nextTick } from "vue";
+import introduce from "./control/text";
+import telephone from "./control/telephone";
+import clink from "./control/link";
+import { useStore } from "vuex";
+import { useMusicPlayer } from '@/utils/sound'
+
+//背景音乐
+const musicPlayer = useMusicPlayer()
+
+const showMusicPlaying = ref(false)
+
+const store = useStore();
+const toursList = computed(() => store.getters["fdkk/toursList"]);
+
+const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
+const toursStatus = computed(() => store.getters["fdkk/toursStatus"]);
+
+const v3toursStatus = computed(() => store.getters["fdkk/v3toursStatus"]);
+
+const isPlayTours = computed(() => store.getters["fdkk/isPlayTours"]);
+const flying = computed(() => store.getters["fdkk/isFlying"]);
+const mode = computed(() => store.getters["fdkk/mode"]);
+
+
+const showTours = computed(() => store.getters["fdkk/isShowToursList"]);
+
+
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+const fdkkmetadata = computed(() => store.getters["fdkk/metadata"]);
+
+const fdkkCurrentVersion = computed(() => store.getters["scene/fdkkCurrentVersion"]);
+
+
+
+const customTelephone = computed(() => store.getters["scene/customTelephone"]);
+
+const customLink = computed(() => store.getters["scene/customLink"]);
+
+
+const showIntroduce = computed(() => store.getters["functions/showIntroduce"]);
+const showTelephone = computed(() => store.getters["functions/showTelephone"]);
+const showLink = computed(() => store.getters["functions/showLink"]);
+
+
+const onIntroduce = () => {
+  store.commit("functions/setShowIntroduce", true);
+}
+
+
+const onLink = () => {
+  store.commit("functions/setShowLink", true);
+}
+
+const onIsBGM = () => {
+  showMusicPlaying.value ? musicPlayer.pause() : musicPlayer.play()
+}
+
+const openTours = () => {
+  if (isShowScenesList.value) {
+    store.commit("functions/setShowScenesList", false);
+  }
+
+  store.commit('fdkk/setShowToursList', !showTours.value)
+}
+
+const playTour = () => {
+  if (fdkkCurrentVersion.value == 'v3') {
+    store.commit('fdkk/setV3ToursStatus', !v3toursStatus.value)
+    let ele = document.getElementById('showifr');
+    ele && ele.contentWindow.postMessage({
+      source: "mingyuan",
+      event: v3toursStatus.value ? 'guide-start' : 'guide-pause',
+    }, "*")
+    return
+  }
+  store.commit('fdkk/setToursStatus', !toursStatus.value)
+
+}
+
+const onIsShowList = (data) => {
+  if (showTours.value) {
+    store.commit('fdkk/setShowToursList', false)
+  }
+  store.commit("functions/setShowScenesList", !isShowScenesList.value);
+};
+
+const onModeChange = name => {
+  document.querySelector('#fdkkifr') && document.querySelector('#fdkkifr').contentWindow.postMessage(
+    {
+      source: "qjkankan",
+      event: "setMode",
+      params: {
+        name: name
+      }
+    },
+    "*"
+  );
+}
+
+
+onMounted(() => {
+})
+
+
+musicPlayer.on('play', () => {
+  showMusicPlaying.value = true
+})
+musicPlayer.on('pause', () => (showMusicPlaying.value = false))
+
+</script>
+
+<style lang="scss" scoped>
+.control-fdkk {
+  display: flex;
+  align-items: center;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+
+  >li {
+    width: 36px;
+    height: 36px;
+    margin: 0 2px;
+    cursor: pointer;
+    white-space: nowrap;
+    display: inline-block;
+    flex-shrink: 0;
+
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .daolan {
+    display: flex;
+    width: auto;
+    align-items: center;
+
+    >img {
+      width: 36px;
+      height: 36px;
+    }
+
+    .jiantou {
+      margin-left: -4px;
+      transform: rotate(180deg);
+      transition: .3s ease transform;
+
+      &.active {
+        transform: none;
+      }
+    }
+
+    >span {
+      margin-left: -4px;
+    }
+
+
+  }
+
+  .v3daolan {
+    >img {
+      &:last-of-type {
+        display: none;
+      }
+    }
+
+    >span {
+      margin-left: 0;
+    }
+  .vlink {
+    margin-left: 10px;
+  }
+  }
+
+  .vlink {
+    width: 1px;
+    height: 20px;
+    background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #FFFFFF 49%, rgba(255, 255, 255, 0) 100%);
+    opacity: 0.5;
+  }
+}
+</style>

+ 1 - 24
packages/qjkankan-view/src/components/UIGather/mobile/control.pano.vue

@@ -7,7 +7,7 @@
     </li>
 
     <!-- 联系电话 -->
-    <li @click="onTelephone" v-if="customTelephone &&customTelephone.isShow">
+    <li  v-if="customTelephone &&customTelephone.isShow">
       <a :href="`tel:${customTelephone.value}`">
         <img :src="require(`@/assets/images/icon/telephone@2x.png`)" alt="" />
       </a>
@@ -78,29 +78,6 @@ const customTelephone = computed(() => store.getters["scene/customTelephone"]);
 
 const customLink = computed(() => store.getters["scene/customLink"]);
 
-
-
-watchEffect(() => {
-  if (currentScene.value.explanation) {
-    let { audioUrl, openByDefault, repeat } = currentScene.value.explanation
-
-    if (audioUrl) {
-        store.commit("functions/setCommentaryUrl", {
-          src:audioUrl,
-          loop:repeat,
-          openByDefault:openByDefault
-        });
-        useSoundPlayer.player.isLock = false
-        useSoundPlayer.player.watchPlay()
-    }else{
-      store.commit("functions/setCommentaryUrl", '');
-      useSoundPlayer.player.isLock = true
-
-    }
-  }
-})
-
-
 const showIntroduce = computed(() => store.getters["functions/showIntroduce"]);
 const showTelephone = computed(() => store.getters["functions/showTelephone"]);
 const showLink = computed(() => store.getters["functions/showLink"]);

+ 144 - 34
packages/qjkankan-view/src/components/UIGather/mobile/control.right.vue

@@ -1,8 +1,12 @@
 <template>
-  <div class="more-icon" :class="{show:showMore}">
-    <ui-icon @click="showMore=!showMore" class="more" :type="showMore?'collect':'more'"></ui-icon>
-    <ul>
-      <li @click="onVR">
+
+  <div class="rightcon">
+    <div class="more-icon" :class="{ show: showMore }">
+      <ui-icon @click="showMore = !showMore" class="more" :type="showMore ? 'collect' : 'more'"></ui-icon>
+    </div>
+    <ul v-if="showMore" @click="showMore = false">
+      <li @click="onVR"
+        v-if="currentScene.type == 'pano' || (fdkkCurrentVersion == 'v3') || (currentScene.type == '4dkk' && (fdkkmetadata.controls && fdkkmetadata.controls.showVR))">
         <img :src="require(`@/assets/images/icon/vr@2x.png`)" alt="" />
       </li>
 
@@ -16,66 +20,172 @@
 <script setup>
 import { useApp } from "@/app";
 import { useStore } from "vuex";
+import { Dialog } from "@/global_components";
+
 import { ref, watch, computed, onMounted, watchEffect, nextTick } from "vue";
 
 const store = useStore();
 const showMore = ref(false)
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+const fdkkmetadata = computed(() => store.getters["fdkk/metadata"]);
+const fdkkCurrentVersion = computed(() => store.getters["scene/fdkkCurrentVersion"]);
+const vrStatus = computed(() => store.getters["functions/vrStatus"]);
 
-const onVR = (data) => {
-  useApp().then((app) => {
-    app.krpanoDom.call("webvr.enterVR()")
+// 授权
+const $iosGrantedTips = (cb) => {
+  var ua = navigator.userAgent.toLowerCase();
+  if (ua.indexOf("like mac os x") > 0) {
+    var reg = /os [\d._]*/gi;
+    var verinfo = ua.match(reg);
+    var version = (verinfo + "")
+      .replace(/[^0-9|_.]/gi, "")
+      .replace(/_/gi, ".");
+    var arr = version.split(".");
+    console.log(arr);
+
+    if (arr[0] > 12 && arr[1] >= 0) {
+      //对13.3以后的版本处理,包括13.3,
+      DeviceMotionEvent && DeviceMotionEvent.requestPermission()
+        .then((permissionState) => {
+          if (permissionState == "granted") {
+            cb({
+              msg: '授权成功',
+              code: 1
+            })
+            this.reload();
+          } else {
+            cb({
+              msg: '授权失败',
+              code: 0
+            })
+          }
+        })
+    }
+    else {
+      //13.3以前的版本
+      cb({
+        msg: '无需授权,直接成功',
+        code: 1
+      })
+    }
+  } else {
+    cb({
+      msg: '无需授权,直接成功',
+      code: 1
+    })
+  }
+}
+
+const onVR = () => {
+  $iosGrantedTips((data) => {
+    console.log(data);
+    if (data.code == 1) {
+      store.commit("functions/setVrStatus", !vrStatus.value);
+      if (currentScene.value.type == 'pano') {
+        useApp().then((app) => {
+          app.krpanoDom.call(vrStatus.value ? "webvr.enterVR()" : "webvr.exitVR()")
+        });
+      } else {
+        if (fdkkCurrentVersion.value == 'v4') {
+          document.querySelector('#fdkkifr') && document.querySelector('#fdkkifr').contentWindow.postMessage(
+            {
+              source: "qjkankan",
+              event: "enterVr",
+              params: {
+                toggle: vrStatus.value
+              }
+            },
+            "*"
+          );
+        } else {
+          let ele = document.getElementById("showifr");
+          ele && ele.contentWindow.postMessage(
+            {
+              source: "mingyuan",
+              event: vrStatus.value ? "vr-in" : "vr-out",
+            },
+            "*"
+          );
+        }
+      }
+    } else {
+      Dialog.alert('运动和方向访问失败,您需要完全关闭此应用,然后再次打开,并允许访问运动与方向')
+    }
   });
+
 };
 
 const onShare = () => {
   store.commit("functions/setShareUrl", true);
 };
 
+watch(currentScene, () => {
+  store.commit("functions/setVrStatus", false);
+  if (currentScene.value.type == 'pano') {
+    useApp().then((app) => {
+      app.krpanoDom.call('webvr.exitVR()')
+    });
+  }
+})
+
 </script>>
 
 <style lang="scss" scoped>
-.more-icon {
+.rightcon {
   width: 36px;
   height: 36px;
-  background: rgba(0, 0, 0, 0.4);
-  border-radius: 50%;
-  border: 1px solid rgba(255, 255, 255, 0.2);
-  backdrop-filter: blur(6px);
-  display: flex;
-  justify-content: center;
-  align-items: center;
   position: relative;
-  .more {
-    font-size: 26px;
+
+  .more-icon {
+    width: 100%;
+    height: 100%;
+    background: rgba(0, 0, 0, 0.4);
+    border-radius: 50%;
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    backdrop-filter: blur(6px);
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: relative;
+
+    .more {
+      font-size: 34px;
+    }
+
+
+    &.show {
+      backdrop-filter: blur(0);
+      background: rgba(0, 0, 0, 0);
+
+      >ul {
+        padding: 6px 0 30px;
+        height: 114px;
+        border: 1px solid rgba(255, 255, 255, 0.2);
+      }
+    }
   }
-  >ul{
+
+  >ul {
     width: 36px;
     padding: 0;
     position: absolute;
-    z-index: -1;
     bottom: 0;
+    z-index: -2;
     width: 36px;
-    background: rgba(0,0,0,0.4);
+    background: rgba(0, 0, 0, 0.4);
     border-radius: 18px;
-    backdrop-filter: blur(10px);
-    overflow: hidden;
-    max-height: 0;
-    >li{
+    backdrop-filter: blur(6px);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    padding-bottom: 38px;
+
+    >li {
       width: 100%;
-      >img{
+
+      >img {
         width: 100%;
       }
     }
   }
 
-  &.show{
-    backdrop-filter: blur(0);
-    background: rgba(0, 0, 0, 0);
-    >ul{
-      padding: 6px 0 30px;
-      max-height: 114px;
-      border: 1px solid rgba(255,255,255,0.2);
-    }
-  }
 }
 </style>

+ 49 - 1
packages/qjkankan-view/src/components/UIGather/mobile/control.vue

@@ -1,11 +1,56 @@
 <template>
   <div class="controlcon">
-    <Pano />
+    <Pano v-if="currentScene.type == 'pano'" />
+    <Fdkk v-else />
+
   </div>
 </template>
 
 <script setup>
 import Pano from "./control.pano";
+import Fdkk from "./control.fdkk";
+import { ref, watch, computed, onMounted, watchEffect, nextTick } from "vue";
+import { useStore } from "vuex";
+import { useMusicPlayer,useSoundPlayer } from '@/utils/sound'
+
+
+//背景音乐
+const musicPlayer = useMusicPlayer()
+
+//解说音乐
+const soundPlayer = useSoundPlayer()
+
+const store = useStore();
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
+
+
+watchEffect(() => {
+  if (currentScene.value.explanation) {
+    let { audioUrl, openByDefault, repeat } = currentScene.value.explanation
+    if (audioUrl) {
+      store.commit("functions/setCommentaryUrl", {
+        src: audioUrl,
+        loop: repeat,
+        openByDefault: openByDefault
+      });
+
+
+      useSoundPlayer.player.isLock = false
+      useSoundPlayer.player.watchPlay()
+    } else {
+      store.commit("functions/setCommentaryUrl", '');
+      useSoundPlayer.player.isLock = true
+      soundPlayer.pause()
+    }
+  } else {
+    store.commit("functions/setCommentaryUrl", '');
+    useSoundPlayer.player.isLock = true
+    soundPlayer.pause()
+  }
+
+
+})
 
 </script>
 
@@ -16,5 +61,8 @@ import Pano from "./control.pano";
   border-radius: 18px;
   border: 1px solid rgba(255, 255, 255, 0.2);
   backdrop-filter: blur(1px);
+  max-width: calc(90vw - 100px);
+  overflow-x: auto;
+  overflow-y: hidden;
 }
 </style>

+ 1 - 0
packages/qjkankan-view/src/components/UIGather/mobile/control/link.vue

@@ -46,6 +46,7 @@ const close = () => {
     padding: 0 20px;
     justify-content: space-between;
     align-items: center;
+    font-size: 18px;
     height: 50px;
     border-bottom: solid 1px rgba(255, 255, 255, 0.16);
 

+ 4 - 0
packages/qjkankan-view/src/components/UIGather/mobile/control/text.vue

@@ -45,6 +45,7 @@ const close = () => {
     align-items: center;
     height: 50px;
     border-bottom: solid 1px rgba(255, 255, 255, 0.16);
+    font-size: 18px;
 
     .close {
       color: rgba(255, 255, 255, 0.6);
@@ -58,6 +59,9 @@ const close = () => {
     justify-content: center;
     height: 100%;
     width: 100%;
+    :deep(p) {
+      line-height: 24px;
+    }
 
     .txtbody {
       width: 100%;

+ 52 - 16
packages/qjkankan-view/src/components/UIGather/mobile/index.vue

@@ -2,17 +2,22 @@
       <Logo />
       <Tips />
       <!-- <Menu /> -->
-      <sceneList />
+      <div v-show="showUI">
+            <sceneList />
+            <div class="btn-style">
+                  <div class="menu-icon">
+                        <div @click="onIsShowList"
+                              v-if="!((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)">
+                              <img :src="require(`@/assets/images/icon/${isShowScenesList ? 'function_off@2x.png' : 'function_on@2x.png'}`)"
+                                    alt="" />
+                        </div>
+                  </div>
 
-      <div class="btn-style">
-            <div class="menu-icon" @click="onIsShowList">
-                  <img :src="require(`@/assets/images/icon/${isShowScenesList ? 'function_off@2x.png' : 'function_on@2x.png'}`)"
-                        alt="" />
+                  <Control />
+                  <ControlRight />
             </div>
-            <Control />
-            <ControlRight />
-      </div>
 
+      </div>
 
 </template>
 
@@ -22,18 +27,23 @@ import Tips from "./tips";
 import sceneList from "./list";
 import Control from "./control";
 import ControlRight from "./control.right";
-
 import Menu from "./menu";
-import browser from "@/utils/browser";
 import { ref, watch, computed, onMounted, nextTick } from "vue";
+import { useApp, getApp } from "@/app";
 
 import { useStore } from "vuex";
 const store = useStore();
+const showUI = ref(false)
 
 
 const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
 const showTours = computed(() => store.getters["fdkk/isShowToursList"]);
 
+const metadata = computed(() => store.getters["scene/metadata"]);
+const scenes = computed(() => store.getters["scene/list"]);
+const secondaryList = computed(() => store.getters["scene/secondaryList"]);
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
 const onIsShowList = (data) => {
       if (showTours.value) {
             store.commit('fdkk/setShowToursList', false)
@@ -41,6 +51,26 @@ const onIsShowList = (data) => {
       store.commit("functions/setShowScenesList", !isShowScenesList.value);
 };
 
+watch(currentScene, () => {
+      if (currentScene.value.type == '4dkk' && isShowScenesList.value) {
+            let t = setTimeout(() => {
+                  clearTimeout(t)
+                  store.commit("functions/setShowScenesList", false);
+            }, 1500);
+      }
+})
+
+useApp().then((app) => {
+      app.Scene.on("ready", (data) => {
+            if (currentScene && currentScene.value.type == '4dkk' && !showUI.value) {
+                  showUI.value = true
+            }
+      })
+      app.Scene.on("sceneReady", () => {
+            showUI.value = true
+      })
+})
+
 </script>
 
 <style lang="scss" scoped>
@@ -57,13 +87,19 @@ const onIsShowList = (data) => {
       .menu-icon {
             width: 36px;
             height: 36px;
-            >img {
+
+            >div {
                   width: 100%;
-                  height: 100%;
-                  background: rgba(0, 0, 0, 0.4);
-                  border-radius: 50%;
-                  border: 1px solid rgba(255, 255, 255, 0.2);
-                  backdrop-filter: blur(6px);
+                  height: 36px;
+
+                  >img {
+                        width: 100%;
+                        height: 100%;
+                        background: rgba(0, 0, 0, 0.4);
+                        border-radius: 50%;
+                        border: 1px solid rgba(255, 255, 255, 0.2);
+                        backdrop-filter: blur(6px);
+                  }
             }
       }
 }

+ 104 - 77
packages/qjkankan-view/src/components/UIGather/mobile/list.vue

@@ -1,58 +1,51 @@
 <template>
-  <div class="bar-list" v-if="show" :class="{barshow:isShowScenesList}">
+  <div class="bar-list"
+    v-if="show && !((metadata.catalogRoot && metadata.catalogRoot.length == 1) && scenes.length == 1 && secondaryList.length == 1)"
+    :class="{ barshow: isShowScenesList }">
     <div class="top-con">
-      <div class="swiper-container" id="swScenes" v-if="currentScenesList.length > 0">
+      <div class="swiper-container" id="swScenes" :style="`width:${Math.min(scenesListW, innerW)}px;
+      padding:${scenesListW > innerW ? '0 15px' : '0'}`" v-if="currentScenesList.length > 0">
         <ul class="swiper-wrapper">
-          <li
-            @click="tabCurrentScene(item)"
-            class="swiper-slide"
-            :class="{
-              active: currentScene.sceneCode == item.sceneCode,
-              loopspan: item.sceneTitle.length > spanlength && currentScene.sceneCode == item.sceneCode,
-            }"
-            :style="{ backgroundImage: `url(${item.icon})` }"
-            v-for="(item, i) in currentScenesList"
-            :key="i"
-          >
-            <i class="iconfont " :class="item.type == '4dkk'?'icon-editor_3d':'icon-editor_panoramic'"></i>
+          <li @click="tabCurrentScene(item)" class="swiper-slide" :class="{
+            active: currentScene.sceneCode == item.sceneCode,
+            loopspan: item.sceneTitle.length > spanlength && currentScene.id == item.id,
+          }" :style="{ backgroundImage: `url(${item.icon})` }" v-for="(item, i) in currentScenesList" :key="i">
+            <i class="iconfont " :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
             <div>
-              <span>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle }}</span>
+              <span v-if="currentScene.id == item.id">{{item.sceneTitle}}</span>
+              <span v-else>{{ item.sceneTitle.length > spanlength ? item.sceneTitle.slice(0, spanlength) : item.sceneTitle
+              }}</span>
             </div>
           </li>
         </ul>
       </div>
 
-      <div class="swiper-container" id="swSecondary" v-if="secondaryList.length > 1">
+      <div class="swiper-container" id="swSecondary" :style="`width:${Math.min(secondaryW, innerW)}px;
+      padding:${secondaryW > innerW ? '0 15px' : '0'}`" v-if="secondaryList.length > 1">
         <ul class="swiper-wrapper">
-          <li
-            class="swiper-slide"
-            @click="tabSecondary(item)"
-            :class="{
-              active: currentSecondary.id == item.id,
-              loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
-            }"
-            v-for="(item, i) in secondaryList"
-            :key="i"
-          >
-            <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
-          </li>
+          <li class="swiper-slide" @click="tabSecondary(item)" :class="{
+            active: currentSecondary.id == item.id,
+            loopspan: item.name.length > spanlength && currentSecondary.id == item.id,
+          }" v-for="(item, i) in secondaryList" :key="i">
+            <span v-if="currentSecondary.id == item.id">{{item.name}}</span>
+            <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+           </li>
         </ul>
       </div>
     </div>
 
-    <div class="swiper-container" id="swcatalogRoot" v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
+    <div class="swiper-container" id="swcatalogRoot"
+    :style="`width:${Math.min(catalogRootW, innerW)}px;
+      padding:${catalogRootW > innerW ? '0 15px' : '0'}`"
+      v-if="metadata.catalogRoot.length > 0 && metadata.catalogs.length > 1">
       <ul class="swiper-wrapper" v-if="metadata.catalogRoot.length > 1">
-        <li
-          class="swiper-slide"
-          :class="{
-            active: currentCatalogRoot.id == item.id,
-            loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
-          }"
-          @click="tabRoot(item)"
-          v-for="(item, i) in metadata.catalogRoot"
-          :key="i"
-        >
-          <span>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+        <li class="swiper-slide" :class="{
+          active: currentCatalogRoot.id == item.id,
+          loopspan: item.name.length > spanlength && currentCatalogRoot.id == item.id,
+        }" @click="tabRoot(item)" v-for="(item, i) in metadata.catalogRoot" :key="i">
+          <span v-if="currentCatalogRoot.id == item.id">{{item.name}}</span>
+          <span v-else>{{ item.name.length > spanlength ? item.name.slice(0, spanlength) : item.name }}</span>
+     
         </li>
       </ul>
     </div>
@@ -78,9 +71,23 @@ const secondaryList = computed(() => store.getters["scene/secondaryList"]);
 
 const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
 
-
 const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
 
+
+const swidth = ref({
+  "swcatalogRoot": 104,
+  "swSecondary": 84,
+  "swScenes": 72,
+})
+
+const innerW = computed(() => window.innerWidth)
+
+const scenesListW = computed(() => currentScenesList.value.length * (swidth.value['swScenes'] + 10) - 10)
+const secondaryW = computed(() => secondaryList.value.length * (swidth.value['swSecondary'] + 10) - 10)
+const catalogRootW = computed(() => metadata.value.catalogRoot.length * (swidth.value['swcatalogRoot'] + 10) - 10)
+
+
+
 const show = ref(false);
 
 const tabCurrentScene = (data) => {
@@ -100,15 +107,15 @@ const loadList = () => {
     let t = setTimeout(() => {
       clearTimeout(t);
       ["#swcatalogRoot", "#swSecondary", "#swScenes"].forEach((item) => {
-      new Swiper(item, {
-        slidesPerView: "auto",
-        centeredSlides: true,
-        spaceBetween: 10,
-        centerInsufficientSlides: true,
-        centeredSlidesBounds: true,
-        freeMode: true,
+        new Swiper(item, {
+          slidesPerView: "auto",
+          centeredSlides: true,
+          spaceBetween: 10,
+          centerInsufficientSlides: true,
+          centeredSlidesBounds: true,
+          freeMode: true,
+        });
       });
-    });
     }, 100);
   });
 };
@@ -141,26 +148,35 @@ onMounted(() => {
   transition: .3s all ease;
   z-index: 9;
   width: 100%;
+
   .swiper-container {
     width: 100%;
     position: relative;
-    > ul {
-      > li {
-        > span,
-        > div > span {
+    margin: 0 auto;
+
+    >ul {
+      >li {
+        white-space: nowrap;
+
+        >span,
+        >div>span {
           cursor: pointer;
           display: inline-block;
           color: rgba(255, 255, 255, 0.6);
         }
+
         &.loopspan {
-          > span,
-          > div > span {
+
+          >span,
+          >div>span {
             animation: 5s wordsLoop linear infinite normal;
           }
         }
-        &.active{
-          > span,
-          > div > span {
+
+        &.active {
+
+          >span,
+          >div>span {
             color: rgba(255, 255, 255, 1);
           }
         }
@@ -170,14 +186,16 @@ onMounted(() => {
 
   .top-con {
     margin-bottom: 10px;
-    padding: 10px 15px;
+    padding: 10px 0;
     width: 100%;
     background: rgba(0, 0, 0, 0.5);
   }
 
   #swcatalogRoot {
-    > ul {
-      > li {
+    padding: 0 15px;
+
+    >ul {
+      >li {
         width: 104px;
         background: rgba(0, 0, 0, 0.5);
         border-radius: 4px;
@@ -185,30 +203,38 @@ onMounted(() => {
         border: 1px solid rgba(255, 255, 255, 0.5);
         box-sizing: border-box;
         overflow: hidden;
-        > span {
+
+        >span {
           width: 100%;
           word-break: keep-all;
         }
+
         &.active {
           border: 1px solid rgba(255, 255, 255, 1);
         }
       }
     }
   }
+
   #swSecondary {
     margin: 20px auto 10px;
-    > ul {
-      > li {
+    padding: 0 15px;
+
+    >ul {
+      >li {
         width: 84px;
         box-sizing: border-box;
         overflow: hidden;
         padding-bottom: 6px;
-        > span {
+
+        >span {
           width: 100%;
           word-break: keep-all;
         }
+
         &.active {
           position: relative;
+
           &::before {
             content: "";
             display: inline-block;
@@ -227,9 +253,10 @@ onMounted(() => {
   }
 
   #swScenes {
-    margin: 0;
-    > ul {
-      > li {
+
+    // padding: 0 15px;
+    >ul {
+      >li {
         cursor: pointer;
         width: 72px;
         height: 72px;
@@ -238,6 +265,7 @@ onMounted(() => {
         background-size: cover;
         position: relative;
         overflow: hidden;
+
         .iconfont {
           position: absolute;
           left: 4px;
@@ -258,7 +286,8 @@ onMounted(() => {
             filter: blur(4px);
           }
         }
-        > div {
+
+        >div {
           position: absolute;
           bottom: 0;
           left: 0;
@@ -267,17 +296,18 @@ onMounted(() => {
           width: 100%;
           overflow: hidden;
 
-          > span {
+          >span {
             width: 100%;
             line-height: 20px;
             word-break: keep-all;
           }
         }
+
         &.active {
           border: 1px solid var(--colors-primary-base);
-          > div {
-            > span {
-            }
+
+          >div {
+            >span {}
           }
         }
       }
@@ -285,7 +315,7 @@ onMounted(() => {
   }
 }
 
-.barshow{
+.barshow {
   max-height: 190px;
 }
 
@@ -300,7 +330,4 @@ onMounted(() => {
     -webkit-transform: translateX(-100%);
   }
 }
-
-
-
 </style>

+ 3 - 1
packages/qjkankan-view/src/components/UIGather/mobile/logo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="logo" v-if="metadata.isLogo">
+  <div class="logo" v-if="metadata.isLogo && currentScene.type=='pano'">
     <img :src="metadata.logo || require('@/assets/images/default/logo_white.svg')" alt="">
   </div>
 </template>
@@ -10,6 +10,8 @@ import { ref, onMounted, computed, watch, nextTick } from "vue";
 
 const store = useStore();
 const metadata = computed(() => store.getters['scene/metadata'])
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+
 
 </script>
 

+ 1 - 1
packages/qjkankan-view/src/components/UIGather/mobile/tips.vue

@@ -1,6 +1,6 @@
 <template>
   <div v-if="show" class="user-tips-overlay">
-    <img :src="metadata.appIcon || require('@/assets/images/default/show/img_tipspc_default.png')" alt="" />
+    <img :src="metadata.appIcon || require('@/assets/images/default/show/img_tipsmb_default.png')" alt="" />
   </div>
 </template>
 <script setup>

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

@@ -2,7 +2,7 @@
   <div class="page-error">
     <div>
       <img :src="require('@/assets/images/default/img_noresults@2x.png')" alt="" />
-      <p>作品已被删除</p>
+      <p>作品已被删除</p>
     </div>
   </div>
 </template>
@@ -18,6 +18,7 @@
   height: 100%;
   background: #fff;
   position: relative;
+  z-index: 9999;
     > div {
       position: absolute;
       top: 50%;

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

@@ -40,7 +40,9 @@ const currentScene = computed(() => store.getters["scene/currentScene"]);
 
 useApp().then((app) => {
   app.Scene.on("ready", () => {
-    show.value = false;
+    if (show.value) {
+      show.value = false;
+    }
   });
 });
 </script>

+ 3 - 2
packages/qjkankan-view/src/components/assembly/MobileTags/index.vue

@@ -37,8 +37,8 @@ const close = () => {
 
 <style lang="scss" scoped>
 .tag-layer {
-  width: 100vw;
-  height: 100vh;
+  width: 100%;
+  height: 100%;
   z-index: 10000;
   top: 0;
   position: fixed;
@@ -52,6 +52,7 @@ const close = () => {
     justify-content: space-between;
     align-items: center;
     height: 50px;
+    font-size: 18px;
     border-bottom: solid 1px rgba(255, 255, 255, 0.16);
     color: #fff;
 

+ 41 - 11
packages/qjkankan-view/src/components/assembly/MobileTags/metas/fixaudio.vue

@@ -1,14 +1,15 @@
 <template>
   <div class="audioinner">
     <audio id="audioTag" class="noshow" autoplay :src="hotspot.audio.ossPath"></audio>
-    <div class="audio-pan">
+    <div class="audio-pan" :class="{rotatepan:isPlaying}">
+
       <img :src="require('@/assets/images/icon/player_pic01.png')" alt="" />
     </div>
 
     <div class="audio-content">
 
       <div class="ad-div">
-        <ui-icon @click="bofang" :type="isPlay?'player_pause':'player_playback'"></ui-icon>
+        <img @click="bofang" :src="require(`@/assets/images/icon/${isPlay?'pause':'preview'}.png`)" alt="" />
         <div class="adcon">
           <div class="bar">
             <div class="activeLine" @click="seekTime"></div>
@@ -37,6 +38,7 @@ export default {
       volumePosi: 100,
       allTime: 0,
       timer: null,
+      isPlaying:false,
       isMute: false
     };
   },
@@ -117,8 +119,17 @@ export default {
         false
       );
 
-      $("#audioTag").on("timeupdate", () => {
-        this.updateProgress();
+      $("#audioTag").on("playing", () => {
+        if (!this.isPlaying) {
+          console.log(this.isPlaying);
+          this.isPlaying = true
+        }
+      });
+
+      $("#audioTag").on("pause", () => {
+        if (this.isPlaying) {
+          this.isPlaying = false
+        }
       });
 
       $("#audioTag").on("timeupdate", () => {
@@ -138,7 +149,7 @@ export default {
 <style lang="scss" scoped>
 .audioinner {
   width: 100vw;
-  height: calc(100vh - 80px);
+  height: 100%;
   color: #fff;
   position: relative;
 
@@ -148,18 +159,27 @@ export default {
     width: 80vw;
     top: 40%;
     transform: translate(-50%, -50%);
-
+    text-align: center;
     >img {
       width: 100%;
       height: auto;
+      max-width: 236px;
     }
   }
 
+  .rotatepan{
+    >img {
+      animation: rotatete infinite linear 10s;
+      transform-origin: center;
+    }
+
+  }
+
   .audio-content {
     position: absolute;
     left: 0;
     right: 0;
-    bottom: 60px;
+    bottom: 80px;
     justify-content: space-around;
     display: flex;
     align-items: center;
@@ -170,14 +190,14 @@ export default {
       width: 90%;
       align-items: center;
 
-      >i {
-        font-size: 40px;
+      >img {
         margin-right: 20px;
+        width: 36px;
       }
 
       .adcon {
         width: calc(100vw - 60px);
-
+        margin-top: 6px;
         .bar {
           position: relative;
           background: none;
@@ -237,7 +257,7 @@ export default {
 
         .time {
           width: 100px;
-          margin-top: 10px;
+          margin-top: 6px;
 
           >span {
             font-size: 12px;
@@ -259,4 +279,14 @@ export default {
   opacity: 0 !important;
   pointer-events: none !important;
 }
+
+
+@keyframes rotatete{
+  0% {
+    transform: rotate(0);
+  }
+  100%{
+    transform: rotate(360deg);
+  }
+}
 </style>

+ 3 - 24
packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-audio.vue

@@ -25,30 +25,9 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
   justify-content: center;
   height: 100%;
   width: 100%;
-  .txtbody{
-    width: 744px;
-    background: rgba(0,0,0,0.6);
-    border-radius: 10px;
-    font-size: 14px;
-    color: #fff;
-    padding: 30px;
-    max-height: calc(100vh - 100px);
-    overflow-y: auto;
-  }
-  .title{
-    position: absolute;
-    left: 20px;
-    top: 20px;
-    height: 36px;
-    line-height: 36px;
-    padding: 0 30px;
-    background: rgba(0,0,0,0.6);
-    border-radius: 20px;
-    color: #fff;
-    font-size: 14px;
-    >i{
-      margin-right: 4px;
-    }
+  >div{
+    height: 100%;
   }
+
 }
 </style>

+ 2 - 4
packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-image.vue

@@ -1,7 +1,3 @@
-<template>
-
-</template>
-
 <script setup>
 import PhotoSwipe from 'photoswipe';
 import 'photoswipe/style.css';
@@ -21,6 +17,8 @@ let options = {
   mainClass: 'm-img-tag',
   errorMsg: '图片加载失败',
   closeSVG: closeicon,
+  pinchToClose:false,
+  closeOnVerticalDrag:false,
   showHideAnimationType: 'none'
 };
 

+ 3 - 0
packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-text.vue

@@ -19,5 +19,8 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
   max-height: calc(100vh - 80px);
   color: #fff;
   text-align: justify;
+  :deep(p) {
+    line-height: 24px;
+  }
 }
 </style>

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

@@ -7,7 +7,7 @@
       </div>
       <div class="url">{{ copyLink }}</div>
       <div class="btns">
-        <ui-button class="cancel" @click="onNo">{{ $t("common.cancel") }}</ui-button>
+        <ui-button class="ccc" @click="onNo">{{ $t("common.cancel") }}</ui-button>
         <ui-button class="primary" :data-clipboard-text="copyLink" ref="copy$">{{ $t("share.fastCopy") }}</ui-button>
       </div>
     </div>
@@ -119,6 +119,19 @@ onMounted(() => {
         width: 88px;
         margin-left: 10px;
       }
+      .ccc{
+        background: #313131!important;
+        border-radius: 4px;
+        border-color: transparent;
+      }
+    }
+  }
+}
+
+@media screen and (max-width: 600px) {
+  .url-share {
+    >div{
+      width: 90%;
     }
   }
 }

+ 34 - 3
packages/qjkankan-view/src/components/assembly/Tags/metas/fixaudio.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="audioinner">
     <audio id="audioTag" class="noshow" autoplay :src="hotspot.audio.ossPath"></audio>
-    <div class="audio-pan">
+    <div class="audio-pan" :class="{rotatepan:isPlaying}">
       <img :src="require('@/assets/images/icon/player_pic01.png')" alt="" />
     </div>
 
@@ -51,7 +51,8 @@ export default {
       volumePosi: 100,
       allTime: 0,
       timer: null,
-      isMute:false
+      isMute:false,
+      isPlaying:false
     };
   },
   methods: {
@@ -135,6 +136,19 @@ export default {
         this.updateProgress();
       });
 
+      $("#audioTag").on("playing", () => {
+        if (!this.isPlaying) {
+          console.log(this.isPlaying);
+          this.isPlaying = true
+        }
+      });
+
+      $("#audioTag").on("pause", () => {
+        if (this.isPlaying) {
+          this.isPlaying = false
+        }
+      });
+
       $("#audioTag").on("timeupdate", () => {
         this.updateProgress();
       });
@@ -162,12 +176,20 @@ export default {
     position: absolute;
     left: 12px;
     width: 120px;
-    bottom: 10px;
+    bottom: 14px;
+    font-size: 0;
 
     >img {
       width: 100%;
     }
   }
+  .rotatepan{
+    >img {
+      animation: rotatete infinite linear 10s;
+      transform-origin: center;
+    }
+
+  }
 
   .audio-content {
     position: absolute;
@@ -290,4 +312,13 @@ export default {
   opacity: 0 !important;
   pointer-events: none !important;
 }
+
+@keyframes rotatete{
+  0% {
+    transform: rotate(0);
+  }
+  100%{
+    transform: rotate(360deg);
+  }
+}
 </style>

+ 54 - 16
packages/qjkankan-view/src/components/assembly/Tags/metas/metas-image.vue

@@ -2,31 +2,35 @@
   <div class="imagecon">
     <div class="title">
       <i class="iconfont icon-material_image title-icon" />
-      {{currentTag.hotspotTitle}}
+      {{ currentTag.hotspotTitle }}
     </div>
-    <img class="image" :src="currentTag.image[currentIndex].ossPath" :style="imageStyle"
-      @wheel.prevent="onImageWheel" />
+
+    <div class="realimgcon" :class="{ fullimgcon: objectFit === 'contain' }">
+      <img class="image" :src="currentTag.image[currentIndex].ossPath" :style="imageStyle"
+        @wheel.prevent="onImageWheel" />
+    </div>
+
     <div class="toolbar">
-      <i class="iconfont icon-material_preview_previous hover-tips" :class="{disabled: currentIndex === 0}"
-        @click="onClickPrevious()">
+      <i v-if="currentTag.image.length > 1" class="iconfont icon-material_preview_previous hover-tips"
+        :class="{ disabled: currentIndex === 0 }" @click="onClickPrevious()">
         <div>
           <div class="remark">上一张</div>
         </div>
       </i>
-      <i class="iconfont icon-material_preview_next1 hover-tips append-splitter"
-        :class="{disabled: currentIndex === currentTag.image.length - 1}" @click="onClickNext()">
+      <i v-if="currentTag.image.length > 1" class="iconfont icon-material_preview_next1 hover-tips append-splitter"
+        :class="{ disabled: currentIndex === currentTag.image.length - 1 }" @click="onClickNext()">
         <div>
           <div class="remark">下一张</div>
         </div>
       </i>
-      <i :class="{disabled: scaleRate>=2}" class="iconfont icon-material_preview_enlarge hover-tips"
+      <i :class="{ disabled: scaleRate >= 2 }" class="iconfont icon-material_preview_enlarge hover-tips"
         @click="onClickZoomIn()">
         <div>
           <div class="remark">放大</div>
         </div>
       </i>
-      <span>{{Math.floor(scaleRate.toFixed(2) * 100)}}%</span>
-      <i :class="{disabled: scaleRate<=0.5}" class="iconfont icon-material_preview_narrow hover-tips"
+      <span>{{ Math.floor(scaleRate.toFixed(2) * 100) }}%</span>
+      <i :class="{ disabled: scaleRate <= 0.5 }" class="iconfont icon-material_preview_narrow hover-tips"
         @click="onClickZoomOut()">
         <div>
           <div class="remark">缩小</div>
@@ -46,6 +50,8 @@
         </div>
       </i>
     </div>
+
+    <div class="btnmask"></div>
   </div>
 </template>
 
@@ -95,10 +101,10 @@ const onImageWheel = (e) => {
 }
 
 const onClickZoomIn = () => {
-  scaleRate.value = Math.min(scaleRate.value * 1.1,2)
+  scaleRate.value = Math.min(scaleRate.value * 1.1, 2)
 }
 const onClickZoomOut = () => {
-  scaleRate.value = Math.max(scaleRate.value * 0.9,0.5)
+  scaleRate.value = Math.max(scaleRate.value * 0.9, 0.5)
 }
 
 const onClickPrevious = () => {
@@ -142,11 +148,12 @@ const onClickCancelFullScreen = () => {
     top: 20px;
     height: 36px;
     line-height: 36px;
-    padding: 0 30px;
+    padding: 0 16px;
     background: rgba(0, 0, 0, 0.6);
     border-radius: 20px;
     color: #fff;
     font-size: 14px;
+    z-index: 999;
 
     >i {
       margin-right: 4px;
@@ -203,14 +210,33 @@ const onClickCancelFullScreen = () => {
     }
   }
 
-  .image {
-    top: 0;
+  .realimgcon {
+    top: 8%;
     left: 0;
     width: 100%;
-    height: 100%;
+    height: 84%;
     position: absolute;
     z-index: 1;
+
+    .image {
+      width: 100%;
+      height: 100%;
+      // top: 0;
+      // left: 0;
+      // width: 100%;
+      // height: 100%;
+      // position: absolute;
+      // z-index: 1;
+    }
   }
+
+  .fullimgcon {
+    top: 0;
+    height: 100%;
+
+  }
+
+
 }
 
 
@@ -264,5 +290,17 @@ const onClickCancelFullScreen = () => {
       color: #fff;
     }
   }
+
+
 }
+.btnmask {
+    width: 100%;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    pointer-events: none;
+    height: 60px;
+    background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.7) 52%, #000000 100%);
+  }
 </style>

+ 7 - 2
packages/qjkankan-view/src/components/assembly/Tags/metas/metas-text.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="txtcon">
     <div class="title">
-      <i class="iconfont icon-edit_textview"/>
+      <i class="iconfont icon-material_text"/>
       {{currentTag.hotspotTitle}} 
     </div>
 
@@ -27,6 +27,7 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
   height: 100%;
   width: 100%;
   padding-top: 70px;
+  
   .txtbody{
     width: 744px;
     background: rgba(0,0,0,0.6);
@@ -37,6 +38,9 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
     max-height: calc(100vh - 100px);
     overflow-y: auto;
     line-height: 1.5;
+    :deep(p) {
+      line-height: 24px;
+    }
   }
   .title{
     position: absolute;
@@ -44,11 +48,12 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
     top: 20px;
     height: 36px;
     line-height: 36px;
-    padding: 0 30px;
+    padding: 0 16px;
     background: rgba(0,0,0,0.6);
     border-radius: 20px;
     color: #fff;
     font-size: 14px;
+    z-index: 999;
     >i{
       margin-right: 4px;
     }

+ 2 - 1
packages/qjkankan-view/src/components/assembly/Tags/metas/metas-video.vue

@@ -34,11 +34,12 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
     top: 20px;
     height: 36px;
     line-height: 36px;
-    padding: 0 30px;
+    padding: 0 16px;
     background: rgba(0,0,0,0.6);
     border-radius: 20px;
     color: #fff;
     font-size: 14px;
+    z-index: 999;
     >i{
       margin-right: 4px;
     }

+ 1 - 0
packages/qjkankan-view/src/components/assembly/Tags/metas/metas-web.vue

@@ -35,6 +35,7 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
     border-radius: 20px;
     color: #fff;
     font-size: 14px;
+    z-index: 999;
     >i{
       margin-right: 4px;
     }

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

@@ -0,0 +1,55 @@
+<template>
+  <div v-if="currentScene.type == 'pano'">
+    <transition name="fade">
+      <div v-if="titleshow" class="titieSlide">
+        {{ currentScene.sceneTitle }}
+      </div>
+    </transition>
+  </div>
+</template>
+
+
+<script setup>
+import { ref, onMounted, computed, watch, nextTick } from "vue";
+import { useStore } from "vuex";
+
+const store = useStore();
+const currentScene = computed(() => store.getters["scene/currentScene"]);
+const timer = ref(null)
+const titleshow = ref(false)
+
+watch(currentScene, () => {
+  if (!currentScene.value) {
+    return
+  }
+  if (timer.value) {
+    clearInterval(timer.value)
+    timer.value = null
+  }
+
+  titleshow.value = true
+  timer.value = setTimeout(() => {
+    titleshow.value = false
+  }, 3000);
+})
+</script>
+
+<style lang="scss" scoped>
+.titieSlide {
+  position: absolute;
+  top: 60px;
+  left: 50%;
+  transform: translateX(-50%);
+  font-weight: bold;
+  font-size: 24px;
+  z-index: 999;
+  color: #FFFFFF;
+  text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);
+}
+
+@media screen and (max-width: 800px) {
+  .titieSlide {
+    font-size: 20px;
+  }
+}
+</style>

+ 2 - 1
packages/qjkankan-view/src/global_components/components/dialog/index.js

@@ -8,6 +8,7 @@ import { mount } from '../../utils/componentHelper'
 
 Dialog.use = function use(app) {
     Dialog.toast = function (options) {
+        Dialog.toast.hide() //清除上一个toast
         if (typeof options == 'string') {
             options = {
                 content: options,
@@ -18,7 +19,7 @@ Dialog.use = function use(app) {
             app,
             props: {
                 ...options,
-                destroy,
+                destroy: () => destroy() 
             },
         })
 

+ 1 - 1
packages/qjkankan-view/src/locales/zh.json

@@ -34,7 +34,7 @@
     "fastCopy": "复制链接"
   },
   "toast": {
-    "copySuccess": "场景链接复制成功",
+    "copySuccess": "复制成功",
     "inputPassword": "请输入密码",
     "checkBrowser": "无法打开页面,请升级或更换浏览器后重新打开",
     "changeBrowser": "建议使用以下浏览器"

+ 30 - 5
packages/qjkankan-view/src/pages/show.vue

@@ -1,5 +1,5 @@
 <template>
-  <LoadingLogo />
+  <LoadingLogo v-if="workEnable" />
   <template v-if="workEnable">
     <Password />
     <Share />
@@ -7,6 +7,7 @@
       <Pano />
       <Tags />
       <UiGather />
+      <TitieSlide />
     </div>
   </template>
 
@@ -19,6 +20,8 @@ import Tags from "@/components/assembly/Tags";
 import Password from "@/components/assembly/Password";
 import Share from "@/components/assembly/Share";
 import Error from "@/components/assembly/Error";
+import TitieSlide from "@/components/assembly/titieSlide";
+
 import UiGather from "@/components/UIGather/";
 
 import LoadingLogo from "@/components/assembly/Loading";
@@ -57,12 +60,13 @@ const skyMask = computed(() => store.getters["scene/skyMask"]);
 
 onMounted(async () => {
   if (browser.isMobile()) {
-    window.location.href = window.location.href.replace('show.html','showMobile.html')
+    window.location.href = window.location.href.replace('show.html', 'showMobile.html')
     return
   }
   let res = await checkWork();
   if (!res.data) {
-    workEnable.value = !res.data;
+    workEnable.value = res.data;
+    console.log(res, 'wqdqdqwdqw');
     return;
   }
   getPanoInfo().then(async (data) => {
@@ -82,6 +86,26 @@ onMounted(async () => {
 
     store.commit("scene/setCurrentScene", firstScene || data.scenes[0]);
 
+
+    // 过滤空分组
+    let ttt = data.catalogRoot.filter((item) => {
+      let flag = ''
+
+      if (item.children) {
+        item.children.some((sub) => {
+          flag = data.scenes.some(son => {
+            // console.log(String(son.category).toLowerCase(), String(sub).toLowerCase());
+            return String(son.category).toLowerCase() == String(sub).toLowerCase()
+          })
+          return flag
+        });
+      }
+      return flag
+    });
+
+    data.catalogRoot = ttt
+
+
     let catalog = data.catalogs.find((item) => item.id == currentScene.value.category);
 
     // 查询初始场景的所在1级分组
@@ -133,7 +157,7 @@ onMounted(async () => {
             }
           }
 
-          
+
           if (skyMask.value) {
             app.krpanoDom.set(`hotspot[peaklogo].visible`, skyMask.value.isShow);
             if (skyMask.value.icon) {
@@ -143,7 +167,7 @@ onMounted(async () => {
         }
         app.Tags.initHotspot(currentScene.value.someData.hotspots, false);
       });
-      
+
     }
   });
 
@@ -153,5 +177,6 @@ onMounted(async () => {
 <style lang="scss" scoped>
 .ui-view-layout {
   position: relative;
+
 }
 </style>

+ 36 - 8
packages/qjkankan-view/src/pages/showMobile.vue

@@ -1,5 +1,5 @@
 <template>
-  <LoadingLogo />
+  <LoadingLogo  v-if="workEnable" />
   <template v-if="workEnable">
     <Password />
     <Share />
@@ -7,6 +7,7 @@
       <Pano />
       <Tags />
       <UiGather />
+      <TitieSlide/>
     </div>
   </template>
 
@@ -20,6 +21,7 @@ import Password from "@/components/assembly/Password";
 import Share from "@/components/assembly/Share";
 import Error from "@/components/assembly/Error";
 import UiGather from "@/components/UIGather/mobile";
+import TitieSlide from "@/components/assembly/titieSlide";
 
 import LoadingLogo from "@/components/assembly/Loading";
 import { createApp } from "@/app";
@@ -62,7 +64,7 @@ onMounted(async () => {
   }
   let res = await checkWork();
   if (!res.data) {
-    workEnable.value = !res.data;
+    workEnable.value = res.data;
     return;
   }
   getPanoInfo().then(async (data) => {
@@ -83,6 +85,24 @@ onMounted(async () => {
 
     store.commit("scene/setCurrentScene", firstScene || data.scenes[0]);
 
+    // 过滤空分组
+    let ttt = data.catalogRoot.filter((item) => {
+      let flag = ''
+
+      if (item.children) {
+        item.children.some((sub) => {
+          flag = data.scenes.some(son => {
+            // console.log(String(son.category).toLowerCase(), String(sub).toLowerCase());
+            return String(son.category).toLowerCase() == String(sub).toLowerCase()
+          })
+          return flag
+        });
+      }
+      return flag
+    });
+
+    data.catalogRoot = ttt
+
     let catalog = data.catalogs.find((item) => item.id == currentScene.value.category);
 
     // 查询初始场景的所在1级分组
@@ -102,6 +122,7 @@ onMounted(async () => {
     show.value = true;
 
     let isHavePano = data.scenes.some(item => item.type == 'pano')
+    document.body.setAttribute('is-mobile', true)
 
     const app = createApp({
       // xml: "%HTMLPATH%/static/template/tour.xml",
@@ -125,19 +146,26 @@ onMounted(async () => {
     app.render();
 
     if (isHavePano) {
-      app.Scene.on("ready", () => {
-        console.log(1111);
+      app.Scene.on("sceneReady", () => {
         if (app.krpanoDom) {
           if (earthMask.value) {
-            app.krpanoDom.set(`hotspot[nadirlogo].url`, earthMask.value);
+            app.krpanoDom.set(`hotspot[nadirlogo].visible`, earthMask.value.isShow);
+            if (earthMask.value.icon) {
+              app.krpanoDom.set(`hotspot[nadirlogo].url`, earthMask.value.icon);
+            }
           }
+
+          
           if (skyMask.value) {
-            app.krpanoDom.set(`hotspot[peaklogo].url`, skyMask.value);
+            app.krpanoDom.set(`hotspot[peaklogo].visible`, skyMask.value.isShow);
+            if (skyMask.value.icon) {
+              app.krpanoDom.set(`hotspot[peaklogo].url`, skyMask.value.icon);
+            }
           }
         }
-
-        app.Tags.initHotspot(currentScene.value.someData.hotspots, false);
+        app.Tags.initHotspot(currentScene.value.someData ? currentScene.value.someData.hotspots : [], false);
       });
+      
     }
   });
 

+ 26 - 2
packages/qjkankan-view/src/store/modules/fdkk.js

@@ -6,19 +6,24 @@ export default {
       // 导览列表
       toursList: [],
       toursStatus: false,
+      v3toursStatus: false,
       isShowToursList: false,
       isPlayTours: false,
       isFlying: false,
+      v3IsVr: false,
+
       mode: 'panorama',
       //当时场景的背景音乐
       fdkkBGM: '',
       //当时场景的info
-      metadata:''
+      metadata: ''
     };
   },
   getters: {
     toursList: (state) => state.toursList,
     isShowToursList: (state) => state.isShowToursList,
+    v3IsVr: (state) => state.v3IsVr,
+    v3toursStatus: (state) => state.v3toursStatus,
     toursStatus: (state) => state.toursStatus,
     isPlayTours: (state) => state.isPlayTours,
     isFlying: (state) => state.isFlying,
@@ -41,6 +46,10 @@ export default {
       );
     },
 
+    setV3IsInVR(state, payload) {
+      state.v3IsVr = payload;
+
+    },
     setToursStatus(state, payload) {
       state.toursStatus = payload;
       document.querySelector('#fdkkifr') && document.querySelector('#fdkkifr').contentWindow.postMessage(
@@ -50,7 +59,10 @@ export default {
         },
         "*"
       );
+    },
 
+    setV3ToursStatus(state, payload) {
+      state.v3toursStatus = payload;
     },
     setIsPlayTours(state, payload) {
       state.isPlayTours = payload;
@@ -61,6 +73,9 @@ export default {
 
     setMode(state, payload) {
       state.mode = payload
+      if (state.mode != 'panorama') {
+        this.commit("functions/setShowScenesList", false)
+      }
     },
 
     setMetadata(state, payload) {
@@ -83,7 +98,16 @@ export default {
 
     setFdkkBGM(state, payload) {
       if (payload) {
-        state.fdkkBGM = 'https://test.4dkankan.com/panorama/' + payload;
+        let bgm = ''
+        if (payload.indexOf('http') > -1) {
+          bgm = payload
+        } else {
+          bgm = window.location.href.indexOf("www.4dkankan.com") > -1
+            ? ('https://www.4dkankan.com/panorama/' + payload)
+            : ('https://test.4dkankan.com/panorama/' + payload)
+        }
+
+        state.fdkkBGM = bgm
       } else {
         state.fdkkBGM = null
       }

+ 10 - 1
packages/qjkankan-view/src/store/modules/functions.js

@@ -29,7 +29,8 @@ export default {
       currentPlaying: '',
       //打断音频的来源
       pauseFrom: '',
-      
+      //当前vr状态
+      vrStatus: false,
 
     };
   },
@@ -46,6 +47,7 @@ export default {
     isCommentary: (state) => state.isCommentary,
     currentPlaying: (state) => state.currentPlaying,
     pauseFrom: (state) => state.pauseFrom,
+    vrStatus: (state) => state.vrStatus,
   },
   mutations: {
     setMenuList(state, payload) {
@@ -100,5 +102,12 @@ export default {
     setCommentaryUrl(state, payload) {
       state.commentary = payload;
     },
+
+    
+    setVrStatus(state, payload) {
+      state.vrStatus = payload;
+    },
+
+
   },
 };

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

@@ -127,6 +127,7 @@ export default {
             }
           });
         });
+
       this.commit('scene/setSecondaryList', temp)
     },