gemercheung 2 år sedan
förälder
incheckning
aae39638e7

+ 1 - 1
packages/qjkankan-editor/.env.testprod

@@ -7,4 +7,4 @@ VUE_APP_PROXY_URL_ROOT='https://test.4dkankan.com'
 VUE_APP_PROXY_URL='https://test.4dkankan.com/qjkankan/'
 VUE_APP_PROXY_URL='https://test.4dkankan.com/qjkankan/'
 VUE_APP_ORIGIN=
 VUE_APP_ORIGIN=
 VUE_APP_URL_FILL=/qjkankan
 VUE_APP_URL_FILL=/qjkankan
-VUE_APP_DEBBUG_FLAG=0426-05
+VUE_APP_DEBBUG_FLAG=0427-01

+ 27 - 5
packages/qjkankan-editor/public/static/template/customTooltip.xml

@@ -32,7 +32,7 @@
         set(hotspot[get(hsp_name)].border,false);
         set(hotspot[get(hsp_name)].border,false);
         set(hotspot[get(hsp_name)].onclick,'js(__krfn.utils.linkopen(%7,%1));');
         set(hotspot[get(hsp_name)].onclick,'js(__krfn.utils.linkopen(%7,%1));');
         set(hotspot[get(hsp_name)].ondown,dragJQhotspot(););
         set(hotspot[get(hsp_name)].ondown,dragJQhotspot(););
-        set(hotspot[get(hsp_name)].onup,js(console.log('0');__krfn.angle.updateHotSpotData(get(xml.scene),get(name),get(ath),get(atv))));
+        set(hotspot[get(hsp_name)].onup,js(__krfn.angle.updateHotSpotData(get(xml.scene),get(name),get(ath),get(atv),0)));
 
 
         txtsplit(%10,'|',fontSize,pos,isHover,borderColor,fillColor,textColor,isShowLine,isTextWrap,lineDirection,textDirection,textNumPerLine,duration,frameNumber,framewidth);
         txtsplit(%10,'|',fontSize,pos,isHover,borderColor,fillColor,textColor,isShowLine,isTextWrap,lineDirection,textDirection,textNumPerLine,duration,frameNumber,framewidth);
         set(hotspot[get(hsp_name)].pos,pos);
         set(hotspot[get(hsp_name)].pos,pos);
@@ -174,7 +174,7 @@
         set(layer[get(tooltipname)].ondown,'dragJQlayer();');
         set(layer[get(tooltipname)].ondown,'dragJQlayer();');
         
         
         set(layer[get(tooltipname)].onclick,JQlayerClick);
         set(layer[get(tooltipname)].onclick,JQlayerClick);
-        set(layer[get(tooltipname)].onup,js(console.log('1');__krfn.angle.updateHotSpotData(get(xml.scene),get(hsName),get(ath),get(atv))));
+        set(layer[get(tooltipname)].onup,js(__krfn.angle.updateHotSpotData(get(xml.scene),get(hsName),get(ath),get(atv),1)));
         <!-- set(hotspot[get(hsp_name)].onup,js(__krfn.angle.updateHotSpotData(get(xml.scene),get(name),get(ath),get(atv)))); -->
         <!-- set(hotspot[get(hsp_name)].onup,js(__krfn.angle.updateHotSpotData(get(xml.scene),get(name),get(ath),get(atv)))); -->
         txtadd(labelCode, '<div style="display: flex; align-items: center; justify-content: center; position: relative; border: 1px solid ',get(borderColor),'; padding: 6px 10px; white-space: pre; border-radius: 5px; background: ',get(fillColor),'">','            <div style="text-align: left; font-size:',get(fontSize),'px; color: ',get(textColor),'; line-height: 1.4;">',get(hotspottitle),'</div>
         txtadd(labelCode, '<div style="display: flex; align-items: center; justify-content: center; position: relative; border: 1px solid ',get(borderColor),'; padding: 6px 10px; white-space: pre; border-radius: 5px; background: ',get(fillColor),'">','            <div style="text-align: left; font-size:',get(fontSize),'px; color: ',get(textColor),'; line-height: 1.4;">',get(hotspottitle),'</div>
         </div>'); 
         </div>'); 
@@ -220,6 +220,8 @@
             set(layer[get(tooltipname)].direction,get(lineDirection));
             set(layer[get(tooltipname)].direction,get(lineDirection));
             delayedcall(0.2,txtadd(tooltipname, 'tooltip_', get(name)); set_label_dir(get(tooltipname),get(layer[get(tooltipname)].direction),1); );
             delayedcall(0.2,txtadd(tooltipname, 'tooltip_', get(name)); set_label_dir(get(tooltipname),get(layer[get(tooltipname)].direction),1); );
           );
           );
+          txtreplace(labelCode,'font-size:15px;','font-size:14px;');
+
         );
         );
         <!-- set(layer[get(tooltipname)].visible,true); -->
         <!-- set(layer[get(tooltipname)].visible,true); -->
         copy(layer[get(tooltipname)].html, labelCode);
         copy(layer[get(tooltipname)].html, labelCode);
@@ -250,7 +252,7 @@
         set(layer[get(tooldot)].onclick,JQlayerClick);
         set(layer[get(tooldot)].onclick,JQlayerClick);
         set(layer[get(tooldot)].ondown,'dragJQlayer();');
         set(layer[get(tooldot)].ondown,'dragJQlayer();');
         set(layer[get(tooldot)].visible,false);
         set(layer[get(tooldot)].visible,false);
-        set(layer[get(tooldot)].onup,js(console.log('2');__krfn.angle.updateHotSpotData(get(xml.scene),get(hsName),get(ath),get(atv))));
+        set(layer[get(tooldot)].onup,js(__krfn.angle.updateHotSpotData(get(xml.scene),get(hsName),get(ath),get(atv),2)));
 
 
         <!-- 个性化标签(3) -->
         <!-- 个性化标签(3) -->
         if(get(hotspottype) == 3,    
         if(get(hotspottype) == 3,    
@@ -320,8 +322,28 @@
             if(get(dir)==2,set(layer[get(dirItem)].x,0);set(layer[get(dirItem)].y,-40););
             if(get(dir)==2,set(layer[get(dirItem)].x,0);set(layer[get(dirItem)].y,-40););
         );
         );
         if( get(lineDirection) == 'left-top',
         if( get(lineDirection) == 'left-top',
-            if(get(dir)==0,set(hotspot[get(dirItem)].rotate,-35));
-            if(get(dir)==1,add(allHeight,labelHeight,80);div(allHeight,2);mul(allHeight,-1);set(layer[get(dirItem)].rotate,0);set(layer[get(dirItem)].x,0);set(layer[get(dirItem)].y,get(allHeight));set(layer[get(dirItem)].rotate,35););
+            if(get(dir)==0,
+              set(hotspot[get(dirItem)].rotate,-35);
+            );
+            if(get(dir)==1,
+                add(allHeight,labelHeight,80);
+                div(allHeight,2);mul(allHeight,-1);
+                set(layer[get(dirItem)].rotate,0);
+            
+                Math.cos(dtrCos,calc(-35* Math.PI/180));
+                Math.sin(dtrSin,calc(-35* Math.PI/180));
+                set(newX,calc(40 * dtrCos));
+                set(newY,calc(40 * dtrSin + 40));
+                <!-- newx = allHeight * Math.cos(dtrCos) + x -->
+                trace('newX::',get(newX));
+                trace('newY::',get(newY));
+                <!-- trace('dtrSin::',dtrSin); -->
+                set(layer[get(dirItem)].x,0);
+                copy(layer[get(dirItem)].y,allHeight);
+                <!-- set(layer[get(dirItem)].offset,'100%'); -->
+                set(layer[get(dirItem)].rotate,35);
+                 <!-- set(layer[get(dirItem)].transfrom,'tranlate(100%)'); -->
+            );
             if(get(dir)==2,set(layer[get(dirItem)].x,0);set(layer[get(dirItem)].y,40););
             if(get(dir)==2,set(layer[get(dirItem)].x,0);set(layer[get(dirItem)].y,40););
         );
         );
         if(
         if(

+ 2 - 2
packages/qjkankan-editor/src/core/angle.js

@@ -16,8 +16,8 @@ export default class Angle {
    * @param {*} atv 
    * @param {*} atv 
    * @param {*} hotspotType 
    * @param {*} hotspotType 
    */
    */
-  updateHotSpotData(hsName, hpname, ath, atv) {
-    console.log('updateHotSpotData', arguments);
+  updateHotSpotData(hsName, hpname, ath, atv, from) {
+    console.log('updateHotSpotData', hsName, from);
     this.ath = ath;
     this.ath = ath;
     this.atv = atv;
     this.atv = atv;
     if (ath && atv) {
     if (ath && atv) {

+ 3 - 3
packages/qjkankan-editor/src/utils/other.js

@@ -235,8 +235,8 @@ export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
     return null
     return null
   }
   }
 
 
-  console.log('nodeIdList', nodeIdList)
-  console.log('nodeTree', nodeTree)
+  // console.log('nodeIdList', nodeIdList)
+  // console.log('nodeTree', nodeTree)
 
 
   let ret = [
   let ret = [
     {
     {
@@ -249,7 +249,7 @@ export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
     const nextLevelRoot = nodeTree.children.find((item) => {
     const nextLevelRoot = nodeTree.children.find((item) => {
       return item.id === nodeIdList[1]
       return item.id === nodeIdList[1]
     })
     })
-    console.log('nextLevelRoot', nextLevelRoot)
+    // console.log('nextLevelRoot', nextLevelRoot)
     // console.assert(nextLevelRoot, 'nodeIdList2nodeInfoListByNodeTree: invalid param 2!')
     // console.assert(nextLevelRoot, 'nodeIdList2nodeInfoListByNodeTree: invalid param 2!')
     ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot))
     ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot))
   }
   }

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

@@ -29,7 +29,7 @@
                 <ui-icon type="pull-down" :class="{ active: showTours }"></ui-icon>
                 <ui-icon type="pull-down" :class="{ active: showTours }"></ui-icon>
             </div>
             </div>
             <!-- <teleport :to="editorMain"> -->
             <!-- <teleport :to="editorMain"> -->
-            <div class="tour-list" :class="{ ban: flying || isSelect }" :style="`height:${showTours ? '120px' : '0px'};`">
+            <div class="tour-list 111" :class="{ ban: flying || isSelect }" :style="`height:${showTours ? '120px' : '0px'};`">
                 <div class="part-content" ref="tourScroll">
                 <div class="part-content" ref="tourScroll">
                     <!-- 多个片段 -->
                     <!-- 多个片段 -->
                     <div class="part-list" v-if="tours.length > 1">
                     <div class="part-list" v-if="tours.length > 1">

+ 275 - 203
packages/qjkankan-kankan-view/src/components/Controls/tours.mobile.vue

@@ -1,14 +1,50 @@
 <template>
 <template>
-  <div class="tour-list" v-if="tours.length > 0" :class="{ ban: flying || isSelect,barshow: showTours }">
+  <div
+    class="tour-list 222"
+    v-if="tours.length > 0"
+    :class="{ ban: flying || isSelect, barshow: showTours }"
+  >
+    <teleport to=".kankan-app">
+      <!-- 导览字幕 -->
+      <div
+        class="tours-captions"
+        :class="{ active: showTours }"
+        v-if="tours.length > 0 && isPlay && !tours[partId].list[frameId].tagId"
+      >
+        <!-- <div class="tours-captions" :class="{ active: showTours }" > -->
+        <div class="captions-title" v-if="tours[partId] && tours[partId].title">
+          {{ tours[partId].title || "" }}
+        </div>
+        <div
+          class="captions-desc"
+          v-if="tours[partId] && tours[partId].description"
+        >
+          {{ tours[partId].description || "" }}
+        </div>
+      </div>
+    </teleport>
+
     <div class="part-content" ref="tourScroll">
     <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>
+      <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 v-if="partId == index && progressNum > 0" class="tourbar">
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
           </div>
           </div>
@@ -16,279 +52,292 @@
       </ul>
       </ul>
       <!-- 只有一个片段 -->
       <!-- 只有一个片段 -->
       <ul class="part-list part-frame" v-else>
       <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">
+        <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 v-if="frameId == index && progressNum > 0" class="tourbar">
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
           </div>
           </div>
         </li>
         </li>
       </ul>
       </ul>
-
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 <script setup>
 <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()
-
+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 spanlength = ref(5);
 
 
 const triggerTour = inject("triggerTour");
 const triggerTour = inject("triggerTour");
 const isOpenTours = inject("isOpenTours");
 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'])
+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(() => {
 const partId = computed(() => {
-  let id = store.getters['tour/partId']
+  let id = store.getters["tour/partId"];
   if (isPlay.value) {
   if (isPlay.value) {
-    slideScroll()
+    slideScroll();
   }
   }
-  return id
-})
+  return id;
+});
 
 
 const frameId = computed(() => {
 const frameId = computed(() => {
-  let id = store.getters['tour/frameId']
+  let id = store.getters["tour/frameId"];
   if (isPlay.value) {
   if (isPlay.value) {
-    slideScroll()
+    slideScroll();
   }
   }
-  return id
-})
-const progressNum = ref(0)
+  return id;
+});
+const progressNum = ref(0);
 const isPlay = computed(() => {
 const isPlay = computed(() => {
-  let status = store.getters['tour/isPlay']
-  let map = document.querySelector('.kankan-app div[xui_min_map]')
+  let status = store.getters["tour/isPlay"];
+  let map = document.querySelector(".kankan-app div[xui_min_map]");
   if (map) {
   if (map) {
     if (status) {
     if (status) {
-      map.classList.add('disabled')
+      map.classList.add("disabled");
     } else {
     } else {
-      map.classList.remove('disabled')
+      map.classList.remove("disabled");
     }
     }
   }
   }
-  return status
-})
-const isInit = ref(false)
+  return status;
+});
+const isInit = ref(false);
 const tours = computed(() => {
 const tours = computed(() => {
-  let tours = store.getters['tour/tours']
+  let tours = store.getters["tour/tours"];
   if (tours.length > 0) {
   if (tours.length > 0) {
     if (tourScroll.value && !isInit.value) {
     if (tourScroll.value && !isInit.value) {
-      isInit.value = true
-      new Scrollbar(tourScroll.value, { onlyHorizontal: true })
+      isInit.value = true;
+      new Scrollbar(tourScroll.value, { onlyHorizontal: true });
     }
     }
   }
   }
-  return tours
-})
-const onModeChange = name => {
-  store.commit('setMode', name)
-}
+  return tours;
+});
+const onModeChange = (name) => {
+  store.commit("setMode", name);
+};
 
 
 const playTour = async () => {
 const playTour = async () => {
-  let player = await getApp().TourManager.player
+  let player = await getApp().TourManager.player;
   if (isPlay.value) {
   if (isPlay.value) {
-    store.commit('tour/setData', { isPlay: true })
-    player.pause()
+    store.commit("tour/setData", { isPlay: true });
+    player.pause();
   } else {
   } else {
-    store.commit('tour/setData', { isPlay: true })
-    player.play(partId.value)
+    store.commit("tour/setData", { isPlay: true });
+    player.play(partId.value);
   }
   }
-}
-
-
+};
 
 
-
-const hanlderTourPartPlay = time => {
+const hanlderTourPartPlay = (time) => {
   if (!timer) {
   if (!timer) {
-    timer = KanKan.Animate.transitions.start(progress => {
-      progressNum.value = progress * 100
-    }, time)
+    timer = KanKan.Animate.transitions.start((progress) => {
+      progressNum.value = progress * 100;
+    }, time);
   }
   }
-}
+};
 const cancelTimer = () => {
 const cancelTimer = () => {
   if (timer) {
   if (timer) {
-    KanKan.Animate.transitions.cancel(timer)
-    timer = null
+    KanKan.Animate.transitions.cancel(timer);
+    timer = null;
   }
   }
-}
+};
 const slideScroll = () => {
 const slideScroll = () => {
   nextTick(() => {
   nextTick(() => {
     let t = setTimeout(() => {
     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)
-  })
-}
+      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 () => {
 const hanlderTour = async () => {
-  let player = await getApp().TourManager.player
-  player.on('play', data => {
+  let player = await getApp().TourManager.player;
+  player.on("play", (data) => {
     // musicPlayer.pause(true)
     // musicPlayer.pause(true)
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: false,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: false,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
-  })
-  player.on('pause', tours => {
-    console.log('pause', player)
+  });
+  player.on("pause", (tours) => {
+    console.log("pause", player);
     // musicPlayer.resume()
     // musicPlayer.resume()
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: true,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: true,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
 
 
-    progressNum.value = 0
-    cancelTimer()
-    store.commit('tour/setData', { isPlay: false })
-  })
-  player.on('end', tours => {
+    progressNum.value = 0;
+    cancelTimer();
+    store.commit("tour/setData", { isPlay: false });
+  });
+  player.on("end", (tours) => {
     // musicPlayer.resume()
     // musicPlayer.resume()
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: true,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: true,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
-    progressNum.value = 100
-    slideScroll()
-    store.commit('tour/setData', { isPlay: false })
-    cancelTimer()
-  })
+    progressNum.value = 100;
+    slideScroll();
+    store.commit("tour/setData", { isPlay: false });
+    cancelTimer();
+  });
 
 
-  let currPartId = null
-  let currProgress = 0
-  let currFrames = 0
+  let currPartId = null;
+  let currProgress = 0;
+  let currFrames = 0;
 
 
-  player.on('progress', data => {
+  player.on("progress", (data) => {
     if (tours.value.length == 1) {
     if (tours.value.length == 1) {
-      progressNum.value = data.progress * 100
+      progressNum.value = data.progress * 100;
     } else {
     } else {
       if (currPartId != data.partId) {
       if (currPartId != data.partId) {
-        currPartId = data.partId
-        currFrames = tours.value[data.partId].list.length
-        currProgress = 0
+        currPartId = data.partId;
+
+        currFrames = tours.value[data.partId]
+          ? tours.value[data.partId].list.length
+          : 0;
+        currProgress = 0;
       } else {
       } else {
-        currProgress += data.progress / currFrames
+        currProgress += data.progress / currFrames;
         if (currProgress >= 100) {
         if (currProgress >= 100) {
-          currProgress = 100
+          currProgress = 100;
         }
         }
 
 
-        progressNum.value = currProgress
+        progressNum.value = currProgress;
       }
       }
     }
     }
-    store.commit('tour/setData', { partId: data.partId, frameId: data.frameId, isPlay: true })
-  })
-
-}
-const getPartTime = partId => {
-  cancelTimer()
-  let time = 0
+    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++) {
   for (let i = 0; i < tours.value[partId].list.length; i++) {
     if (!tours.value[partId].list[i]._end) {
     if (!tours.value[partId].list[i]._end) {
-      time += tours.value[partId].list[i].time - 0
+      time += tours.value[partId].list[i].time - 0;
       if (!tours.value[partId].list[i]._notrans) {
       if (!tours.value[partId].list[i]._notrans) {
-        time += 1000
+        time += 1000;
       }
       }
     }
     }
   }
   }
-  return time
-}
+  return time;
+};
 
 
 const openTours = () => {
 const openTours = () => {
   // showTours.value = !showTours.value
   // showTours.value = !showTours.value
-  store.commit('tour/setData', { showTours: !showTours.value })
+  store.commit("tour/setData", { showTours: !showTours.value });
 
 
   nextTick(() => {
   nextTick(() => {
     if (isPlay.value) {
     if (isPlay.value) {
-      slideScroll()
+      slideScroll();
     }
     }
-  })
-}
+  });
+};
 const changeFrame = async (type, id) => {
 const changeFrame = async (type, id) => {
-  progressNum.value = 0
+  progressNum.value = 0;
   // recorder.selectFrame(id)
   // recorder.selectFrame(id)
-  let player = await getApp().TourManager.player
+  let player = await getApp().TourManager.player;
   // player.selectFrame(id)
   // player.selectFrame(id)
-  isSelect.value = true
+  isSelect.value = true;
   if (type == 1) {
   if (type == 1) {
-    player.selectPart(id)
-    console.log(tours.value[id].frameId)
-    let f_id = 0
+    player.selectPart(id);
+    console.log(tours.value[id].frameId);
+    let f_id = 0;
     if (tours.value[id].frameId) {
     if (tours.value[id].frameId) {
-      f_id = tours.value[id].frameId
+      f_id = tours.value[id].frameId;
     }
     }
     player.selectFrame(f_id).then(() => {
     player.selectFrame(f_id).then(() => {
-      isSelect.value = false
-    })
-    store.commit('tour/setData', {
+      isSelect.value = false;
+    });
+    store.commit("tour/setData", {
       frameId: f_id,
       frameId: f_id,
       partId: id,
       partId: id,
-    })
+    });
   } else {
   } else {
     player.selectFrame(id).then(() => {
     player.selectFrame(id).then(() => {
-      isSelect.value = false
-    })
-    store.commit('tour/setData', {
+      isSelect.value = false;
+    });
+    store.commit("tour/setData", {
       frameId: id,
       frameId: id,
-    })
+    });
   }
   }
 
 
-  slideScroll()
-}
+  slideScroll();
+};
 const onClickHandler = async () => {
 const onClickHandler = async () => {
   if (isPlay.value) {
   if (isPlay.value) {
-    let player = await getApp().TourManager.player
-    player.pause()
+    let player = await getApp().TourManager.player;
+    player.pause();
     // musicPlayer.resume()
     // musicPlayer.resume()
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: true,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: true,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
   }
   }
-}
-
+};
 
 
 watch(triggerTour, () => {
 watch(triggerTour, () => {
-  playTour()
-})
+  playTour();
+});
 
 
 watch(isOpenTours, () => {
 watch(isOpenTours, () => {
-  openTours()
-})
+  openTours();
+});
 
 
 watch(isPlay, () => {
 watch(isPlay, () => {
   window.parent.postMessage(
   window.parent.postMessage(
@@ -301,23 +350,21 @@ watch(isPlay, () => {
     },
     },
     "*"
     "*"
   );
   );
-})
-
+});
 
 
 onMounted(() => {
 onMounted(() => {
-  useApp().then(async sdk => {
-    hanlderTour()
-  })
+  useApp().then(async (sdk) => {
+    hanlderTour();
+  });
 
 
   nextTick(() => {
   nextTick(() => {
-    let player = document.querySelector('.player[name="main"]')
-    player.addEventListener('click', onClickHandler)
-  })
-})
+    let player = document.querySelector('.player[name="main"]');
+    player.addEventListener("click", onClickHandler);
+  });
+});
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-
 .controls-left-buttons {
 .controls-left-buttons {
   margin-left: 20px;
   margin-left: 20px;
   margin-bottom: 20px;
   margin-bottom: 20px;
@@ -327,7 +374,7 @@ onMounted(() => {
 .buttons.tour {
 .buttons.tour {
   margin-right: 10px;
   margin-right: 10px;
 
 
-  >div {
+  > div {
     margin-left: 0px;
     margin-left: 0px;
     margin-right: 0px;
     margin-right: 0px;
     padding: 0 10px;
     padding: 0 10px;
@@ -354,7 +401,7 @@ onMounted(() => {
   text-align: center;
   text-align: center;
   overflow: hidden;
   overflow: hidden;
   max-height: 0;
   max-height: 0;
-  transition: .3s all ease;
+  transition: 0.3s all ease;
   z-index: 9;
   z-index: 9;
   max-width: 90vw;
   max-width: 90vw;
   &.ban {
   &.ban {
@@ -366,41 +413,39 @@ onMounted(() => {
     flex-direction: row;
     flex-direction: row;
     overflow: hidden;
     overflow: hidden;
     padding: 6px;
     padding: 6px;
-    background: rgba(0,0,0,0.5);
-    border: 1px solid rgba(255,255,255,0.1);
+    background: rgba(0, 0, 0, 0.5);
+    border: 1px solid rgba(255, 255, 255, 0.1);
     border-radius: 4px;
     border-radius: 4px;
     .part-list {
     .part-list {
       display: flex;
       display: flex;
 
 
-      >li {
+      > li {
         width: 90px;
         width: 90px;
         height: 40px;
         height: 40px;
-        background: rgba(24, 24, 24, .5);
+        background: rgba(24, 24, 24, 0.5);
         line-height: 40px;
         line-height: 40px;
         margin: 0 6px;
         margin: 0 6px;
         cursor: pointer;
         cursor: pointer;
         border-radius: 4px;
         border-radius: 4px;
 
 
-        >span,
-        >div>span {
+        > span,
+        > div > span {
           cursor: pointer;
           cursor: pointer;
           display: inline-block;
           display: inline-block;
           color: rgba(255, 255, 255, 0.8);
           color: rgba(255, 255, 255, 0.8);
         }
         }
 
 
         &.loopspan {
         &.loopspan {
-
-          >span,
-          >div>span {
+          > span,
+          > div > span {
             animation: 5s wordsLoop linear infinite normal;
             animation: 5s wordsLoop linear infinite normal;
           }
           }
         }
         }
 
 
         &.active {
         &.active {
           position: relative;
           position: relative;
-          >span{
-          color: var(--colors-primary-base);
-
+          > span {
+            color: var(--colors-primary-base);
           }
           }
           .tourbar {
           .tourbar {
             position: absolute;
             position: absolute;
@@ -425,20 +470,18 @@ onMounted(() => {
           opacity: 0.7;
           opacity: 0.7;
         }
         }
       }
       }
-      .activeborder{
+      .activeborder {
         border: 1px solid var(--editor-main-color);
         border: 1px solid var(--editor-main-color);
       }
       }
-
     }
     }
 
 
     .part-frame {
     .part-frame {
-      >li {
+      > li {
         width: 120px;
         width: 120px;
         height: 80px;
         height: 80px;
         background-size: cover;
         background-size: cover;
         &.active {
         &.active {
-
-        .tourbar {
+          .tourbar {
             position: absolute;
             position: absolute;
             width: 100%;
             width: 100%;
             left: 0;
             left: 0;
@@ -465,7 +508,6 @@ onMounted(() => {
   max-height: 102px;
   max-height: 102px;
 }
 }
 
 
-
 @keyframes wordsLoop {
 @keyframes wordsLoop {
   0% {
   0% {
     transform: translateX(100%);
     transform: translateX(100%);
@@ -478,3 +520,33 @@ onMounted(() => {
   }
   }
 }
 }
 </style>
 </style>
+<style lang="scss">
+.tours-captions {
+  pointer-events: none;
+  position: absolute;
+  bottom: 2.7rem;
+  // left: -0.4267rem;
+  left: 0;
+  width: 100vw;
+  word-break: break-all;
+  text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
+  text-align: justify;
+  padding: 0 0.2667rem;
+  box-sizing: border-box;
+  transition: all 0.3s;
+  transform: translateX(0);
+  z-index: 10;
+  &.active {
+    bottom: 5rem;
+  }
+  .captions-title {
+    font-size: 0.4267rem;
+    margin-bottom: 0.2667rem;
+    font-weight: bold;
+  }
+  .captions-desc {
+    font-size: 0.3733rem;
+    line-height: 0.44rem;
+  }
+}
+</style>

+ 275 - 200
packages/qjkankan-kankan-view/src/components/Controls/tours.vue

@@ -1,14 +1,50 @@
 <template>
 <template>
-  <div class="tour-list" v-if="tours.length > 0" :class="{ ban: flying || isSelect,barshow: showTours }">
+  <div
+    class="tour-list 333"
+    v-if="tours.length > 0"
+    :class="{ ban: flying || isSelect, barshow: showTours }"
+  >
+    <teleport to=".kankan-app">
+      <!-- 导览字幕 -->
+      <div
+        class="tours-captions"
+        :class="{ active: showTours }"
+        v-if="tours.length > 0 && isPlay && !tours[partId].list[frameId].tagId"
+      >
+        <!-- <div class="tours-captions" :class="{ active: showTours }" > -->
+        <div class="captions-title" v-if="tours[partId] && tours[partId].title">
+          {{ tours[partId].title || "" }}
+        </div>
+        <div
+          class="captions-desc"
+          v-if="tours[partId] && tours[partId].description"
+        >
+          {{ tours[partId].description || "" }}
+        </div>
+      </div>
+    </teleport>
+
     <div class="part-content" ref="tourScroll">
     <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>
+      <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 v-if="partId == index && progressNum > 0" class="tourbar">
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
           </div>
           </div>
@@ -16,278 +52,289 @@
       </ul>
       </ul>
       <!-- 只有一个片段 -->
       <!-- 只有一个片段 -->
       <ul class="part-list part-frame" v-else>
       <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">
+        <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 v-if="frameId == index && progressNum > 0" class="tourbar">
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
             <div :style="`width:${progressNum}%;`" class="tourline"></div>
           </div>
           </div>
         </li>
         </li>
       </ul>
       </ul>
-
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 <script setup>
 <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()
-
+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 spanlength = ref(5);
 
 
 const triggerTour = inject("triggerTour");
 const triggerTour = inject("triggerTour");
 const isOpenTours = inject("isOpenTours");
 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'])
+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(() => {
 const partId = computed(() => {
-  let id = store.getters['tour/partId']
+  let id = store.getters["tour/partId"];
   if (isPlay.value) {
   if (isPlay.value) {
-    slideScroll()
+    slideScroll();
   }
   }
-  return id
-})
+  return id;
+});
 
 
 const frameId = computed(() => {
 const frameId = computed(() => {
-  let id = store.getters['tour/frameId']
+  let id = store.getters["tour/frameId"];
   if (isPlay.value) {
   if (isPlay.value) {
-    slideScroll()
+    slideScroll();
   }
   }
-  return id
-})
-const progressNum = ref(0)
+  return id;
+});
+const progressNum = ref(0);
 const isPlay = computed(() => {
 const isPlay = computed(() => {
-  let status = store.getters['tour/isPlay']
-  let map = document.querySelector('.kankan-app div[xui_min_map]')
+  let status = store.getters["tour/isPlay"];
+  let map = document.querySelector(".kankan-app div[xui_min_map]");
   if (map) {
   if (map) {
     if (status) {
     if (status) {
-      map.classList.add('disabled')
+      map.classList.add("disabled");
     } else {
     } else {
-      map.classList.remove('disabled')
+      map.classList.remove("disabled");
     }
     }
   }
   }
-  return status
-})
-const isInit = ref(false)
+  return status;
+});
+const isInit = ref(false);
 const tours = computed(() => {
 const tours = computed(() => {
-  let tours = store.getters['tour/tours']
+  let tours = store.getters["tour/tours"];
   if (tours.length > 0) {
   if (tours.length > 0) {
     if (tourScroll.value && !isInit.value) {
     if (tourScroll.value && !isInit.value) {
-      isInit.value = true
-      new Scrollbar(tourScroll.value, { onlyHorizontal: true })
+      isInit.value = true;
+      new Scrollbar(tourScroll.value, { onlyHorizontal: true });
     }
     }
   }
   }
-  return tours
-})
-const onModeChange = name => {
-  store.commit('setMode', name)
-}
+  return tours;
+});
+const onModeChange = (name) => {
+  store.commit("setMode", name);
+};
 
 
 const playTour = async () => {
 const playTour = async () => {
-  let player = await getApp().TourManager.player
+  let player = await getApp().TourManager.player;
   if (isPlay.value) {
   if (isPlay.value) {
-    store.commit('tour/setData', { isPlay: true })
-    player.pause()
+    store.commit("tour/setData", { isPlay: true });
+    player.pause();
   } else {
   } else {
-    store.commit('tour/setData', { isPlay: true })
-    player.play(partId.value)
+    store.commit("tour/setData", { isPlay: true });
+    player.play(partId.value);
   }
   }
-}
+};
 
 
-
-
-
-const hanlderTourPartPlay = time => {
+const hanlderTourPartPlay = (time) => {
   if (!timer) {
   if (!timer) {
-    timer = KanKan.Animate.transitions.start(progress => {
-      progressNum.value = progress * 100
-    }, time)
+    timer = KanKan.Animate.transitions.start((progress) => {
+      progressNum.value = progress * 100;
+    }, time);
   }
   }
-}
+};
 const cancelTimer = () => {
 const cancelTimer = () => {
   if (timer) {
   if (timer) {
-    KanKan.Animate.transitions.cancel(timer)
-    timer = null
+    KanKan.Animate.transitions.cancel(timer);
+    timer = null;
   }
   }
-}
+};
 const slideScroll = () => {
 const slideScroll = () => {
   nextTick(() => {
   nextTick(() => {
     let t = setTimeout(() => {
     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)
-  })
-}
+      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 () => {
 const hanlderTour = async () => {
-  let player = await getApp().TourManager.player
-  player.on('play', data => {
+  let player = await getApp().TourManager.player;
+  player.on("play", (data) => {
     // musicPlayer.pause(true)
     // musicPlayer.pause(true)
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: false,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: false,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
-  })
-  player.on('pause', tours => {
-    console.log('pause', player)
+  });
+  player.on("pause", (tours) => {
+    console.log("pause", player);
     // musicPlayer.resume()
     // musicPlayer.resume()
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: true,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: true,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
-    progressNum.value = 0
-    cancelTimer()
-    store.commit('tour/setData', { isPlay: false })
-  })
-  player.on('end', tours => {
+    progressNum.value = 0;
+    cancelTimer();
+    store.commit("tour/setData", { isPlay: false });
+  });
+  player.on("end", (tours) => {
     // musicPlayer.resume()
     // musicPlayer.resume()
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: true,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: true,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
-    progressNum.value = 100
-    slideScroll()
-    store.commit('tour/setData', { isPlay: false })
-    cancelTimer()
-  })
+    progressNum.value = 100;
+    slideScroll();
+    store.commit("tour/setData", { isPlay: false });
+    cancelTimer();
+  });
 
 
-  let currPartId = null
-  let currProgress = 0
-  let currFrames = 0
+  let currPartId = null;
+  let currProgress = 0;
+  let currFrames = 0;
 
 
-  player.on('progress', data => {
+  player.on("progress", (data) => {
     if (tours.value.length == 1) {
     if (tours.value.length == 1) {
-      progressNum.value = data.progress * 100
+      progressNum.value = data.progress * 100;
     } else {
     } else {
       if (currPartId != data.partId) {
       if (currPartId != data.partId) {
-        currPartId = data.partId
-        currFrames = tours.value[data.partId].list.length
-        currProgress = 0
+        currPartId = data.partId;
+        currFrames = tours.value[data.partId].list.length;
+        currProgress = 0;
       } else {
       } else {
-        currProgress += data.progress / currFrames
+        currProgress += data.progress / currFrames;
         if (currProgress >= 100) {
         if (currProgress >= 100) {
-          currProgress = 100
+          currProgress = 100;
         }
         }
 
 
-        progressNum.value = currProgress
+        progressNum.value = currProgress;
       }
       }
     }
     }
-    store.commit('tour/setData', { partId: data.partId, frameId: data.frameId, isPlay: true })
-  })
-
-}
-const getPartTime = partId => {
-  cancelTimer()
-  let time = 0
+    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++) {
   for (let i = 0; i < tours.value[partId].list.length; i++) {
     if (!tours.value[partId].list[i]._end) {
     if (!tours.value[partId].list[i]._end) {
-      time += tours.value[partId].list[i].time - 0
+      time += tours.value[partId].list[i].time - 0;
       if (!tours.value[partId].list[i]._notrans) {
       if (!tours.value[partId].list[i]._notrans) {
-        time += 1000
+        time += 1000;
       }
       }
     }
     }
   }
   }
-  return time
-}
+  return time;
+};
 
 
 const openTours = () => {
 const openTours = () => {
   // showTours.value = !showTours.value
   // showTours.value = !showTours.value
-  store.commit('tour/setData', { showTours: !showTours.value })
+  store.commit("tour/setData", { showTours: !showTours.value });
 
 
   nextTick(() => {
   nextTick(() => {
     if (isPlay.value) {
     if (isPlay.value) {
-      slideScroll()
+      slideScroll();
     }
     }
-  })
-}
+  });
+};
 const changeFrame = async (type, id) => {
 const changeFrame = async (type, id) => {
-  progressNum.value = 0
+  progressNum.value = 0;
   // recorder.selectFrame(id)
   // recorder.selectFrame(id)
-  let player = await getApp().TourManager.player
+  let player = await getApp().TourManager.player;
   // player.selectFrame(id)
   // player.selectFrame(id)
-  isSelect.value = true
+  isSelect.value = true;
   if (type == 1) {
   if (type == 1) {
-    player.selectPart(id)
-    console.log(tours.value[id].frameId)
-    let f_id = 0
+    player.selectPart(id);
+    console.log(tours.value[id].frameId);
+    let f_id = 0;
     if (tours.value[id].frameId) {
     if (tours.value[id].frameId) {
-      f_id = tours.value[id].frameId
+      f_id = tours.value[id].frameId;
     }
     }
     player.selectFrame(f_id).then(() => {
     player.selectFrame(f_id).then(() => {
-      isSelect.value = false
-    })
-    store.commit('tour/setData', {
+      isSelect.value = false;
+    });
+    store.commit("tour/setData", {
       frameId: f_id,
       frameId: f_id,
       partId: id,
       partId: id,
-    })
+    });
   } else {
   } else {
     player.selectFrame(id).then(() => {
     player.selectFrame(id).then(() => {
-      isSelect.value = false
-    })
-    store.commit('tour/setData', {
+      isSelect.value = false;
+    });
+    store.commit("tour/setData", {
       frameId: id,
       frameId: id,
-    })
+    });
   }
   }
 
 
-  slideScroll()
-}
+  slideScroll();
+};
 const onClickHandler = async () => {
 const onClickHandler = async () => {
   if (isPlay.value) {
   if (isPlay.value) {
-    let player = await getApp().TourManager.player
-    player.pause()
+    let player = await getApp().TourManager.player;
+    player.pause();
     // musicPlayer.resume()
     // musicPlayer.resume()
     window.parent.postMessage(
     window.parent.postMessage(
-        {
-            source: "qjkankan",
-            event: "toggleBgmStatus",
-            params: {
-                status: true,
-            },
+      {
+        source: "qjkankan",
+        event: "toggleBgmStatus",
+        params: {
+          status: true,
         },
         },
-        "*"
+      },
+      "*"
     );
     );
   }
   }
-}
-
+};
 
 
 watch(triggerTour, () => {
 watch(triggerTour, () => {
-  playTour()
-})
+  playTour();
+});
 
 
 watch(isOpenTours, () => {
 watch(isOpenTours, () => {
-  openTours()
-})
+  openTours();
+});
 
 
 watch(isPlay, () => {
 watch(isPlay, () => {
   window.parent.postMessage(
   window.parent.postMessage(
@@ -300,19 +347,18 @@ watch(isPlay, () => {
     },
     },
     "*"
     "*"
   );
   );
-})
-
+});
 
 
 onMounted(() => {
 onMounted(() => {
-  useApp().then(async sdk => {
-    hanlderTour()
-  })
+  useApp().then(async (sdk) => {
+    hanlderTour();
+  });
 
 
   nextTick(() => {
   nextTick(() => {
-    let player = document.querySelector('.player[name="main"]')
-    player.addEventListener('click', onClickHandler)
-  })
-})
+    let player = document.querySelector('.player[name="main"]');
+    player.addEventListener("click", onClickHandler);
+  });
+});
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -327,7 +373,7 @@ $width: 1150px;
 .buttons.tour {
 .buttons.tour {
   margin-right: 10px;
   margin-right: 10px;
 
 
-  >div {
+  > div {
     margin-left: 0px;
     margin-left: 0px;
     margin-right: 0px;
     margin-right: 0px;
     padding: 0 10px;
     padding: 0 10px;
@@ -355,7 +401,7 @@ $width: 1150px;
   max-width: $width;
   max-width: $width;
   overflow: hidden;
   overflow: hidden;
   max-height: 0;
   max-height: 0;
-  transition: .3s all ease;
+  transition: 0.3s all ease;
   z-index: 9;
   z-index: 9;
 
 
   &.ban {
   &.ban {
@@ -367,40 +413,44 @@ $width: 1150px;
     flex-direction: row;
     flex-direction: row;
     overflow: hidden;
     overflow: hidden;
     padding: 10px 30px;
     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%);
+    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%
+    );
 
 
     .part-list {
     .part-list {
       display: flex;
       display: flex;
 
 
-      >li {
+      > li {
         width: 90px;
         width: 90px;
         height: 40px;
         height: 40px;
-        background: rgba(24, 24, 24, .5);
+        background: rgba(24, 24, 24, 0.5);
         line-height: 40px;
         line-height: 40px;
         margin: 0 6px;
         margin: 0 6px;
         cursor: pointer;
         cursor: pointer;
         border-radius: 4px;
         border-radius: 4px;
 
 
-        >span,
-        >div>span {
+        > span,
+        > div > span {
           cursor: pointer;
           cursor: pointer;
           display: inline-block;
           display: inline-block;
           color: rgba(255, 255, 255, 0.8);
           color: rgba(255, 255, 255, 0.8);
         }
         }
 
 
         &.loopspan {
         &.loopspan {
-
-          >span,
-          >div>span {
+          > span,
+          > div > span {
             animation: 5s wordsLoop linear infinite normal;
             animation: 5s wordsLoop linear infinite normal;
           }
           }
         }
         }
 
 
         &.active {
         &.active {
           position: relative;
           position: relative;
-          >span{
-          color: var(--colors-primary-base);
-
+          > span {
+            color: var(--colors-primary-base);
           }
           }
           .tourbar {
           .tourbar {
             position: absolute;
             position: absolute;
@@ -426,20 +476,18 @@ $width: 1150px;
         }
         }
       }
       }
 
 
-      .activeborder{
+      .activeborder {
         border: 1px solid var(--editor-main-color);
         border: 1px solid var(--editor-main-color);
       }
       }
-
     }
     }
 
 
     .part-frame {
     .part-frame {
-      >li {
+      > li {
         width: 120px;
         width: 120px;
         height: 80px;
         height: 80px;
         background-size: cover;
         background-size: cover;
         &.active {
         &.active {
-
-        .tourbar {
+          .tourbar {
             position: absolute;
             position: absolute;
             width: 100%;
             width: 100%;
             left: 0;
             left: 0;
@@ -466,7 +514,6 @@ $width: 1150px;
   max-height: 102px;
   max-height: 102px;
 }
 }
 
 
-
 @keyframes wordsLoop {
 @keyframes wordsLoop {
   0% {
   0% {
     transform: translateX(100%);
     transform: translateX(100%);
@@ -479,3 +526,31 @@ $width: 1150px;
   }
   }
 }
 }
 </style>
 </style>
+
+<style lang="scss">
+.tours-captions {
+  position: absolute;
+  bottom: 64px;
+  left: 20px;
+  width: 480px;
+  word-break: break-all;
+  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
+  text-align: justify;
+  color: #fff;
+  pointer-events: none;
+  z-index: 1;
+  transition: all 0.3s;
+  &.active {
+    bottom: 5rem;
+  }
+  .captions-title {
+    font-size: 28px;
+    margin-bottom: 10px;
+    font-weight: 700;
+  }
+  .captions-desc {
+    font-size: 16px;
+    line-height: 24px;
+  }
+}
+</style>

+ 1 - 1
packages/qjkankan-view/.env.testprod

@@ -6,4 +6,4 @@ VUE_APP_PROXY_URL='https://test.4dkankan.com/qjkankan/'
 VUE_APP_URL_FILL=/qjkankan
 VUE_APP_URL_FILL=/qjkankan
 # 接口请求地址
 # 接口请求地址
 VUE_APP_APIS_URL=https://test.4dkankan.com/
 VUE_APP_APIS_URL=https://test.4dkankan.com/
-VUE_APP_DEBBUG_FLAG=0426-05
+VUE_APP_DEBBUG_FLAG=0427-02

+ 3 - 1
packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-imagetext.vue

@@ -216,11 +216,13 @@ function handlePageRender(_, el) {
     background-color: #fff;
     background-color: #fff;
     box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
     box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
     min-height: 90vh;
     min-height: 90vh;
-    padding: 30px 20px 25px;
+    padding: 30px 20px 135px 20px;
     word-wrap: break-word;
     word-wrap: break-word;
     border-top-left-radius: 10px;
     border-top-left-radius: 10px;
     border-top-right-radius: 10px;
     border-top-right-radius: 10px;
     position: relative;
     position: relative;
+    overflow-y: scroll;
+
     &::before {
     &::before {
       content: "";
       content: "";
       width: 35px;
       width: 35px;

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

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

+ 13 - 9
packages/qjkankan-view/src/components/assembly/OpeningMobile.vue

@@ -124,7 +124,7 @@ const props = defineProps({
 const store = useStore();
 const store = useStore();
 const openvideo$ = ref(null);
 const openvideo$ = ref(null);
 const videoNeedPlay = ref(true);
 const videoNeedPlay = ref(true);
-
+const isToApp = ref(false);
 const isMixinMode = computed(
 const isMixinMode = computed(
   () => props.coverData.coverSelect.toLowerCase().indexOf("and") > -1
   () => props.coverData.coverSelect.toLowerCase().indexOf("and") > -1
 );
 );
@@ -322,16 +322,20 @@ watch(
   }
   }
 );
 );
 
 
+
 //跳转到app
 //跳转到app
 const toApp = () => {
 const toApp = () => {
-  imgShow.value = false;
-  videoShow.value = false;
-
-  useApp().then((app) => {
-    setTimeout(() => {
-      app.render();
-    }, 1000);
-  });
+  if (!unref(isToApp)) {
+    console.log("跳转到app", unref(isToApp));
+    imgShow.value = false;
+    videoShow.value = false;
+    isToApp.value = true;
+    useApp().then((app) => {
+      setTimeout(() => {
+        app.render();
+      }, 1000);
+    });
+  }
 };
 };
 
 
 onMounted(() => {
 onMounted(() => {

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

@@ -291,6 +291,7 @@ watchEffect(() => {
       flex: 0 0 32%;
       flex: 0 0 32%;
       overflow: hidden;
       overflow: hidden;
       color: white;
       color: white;
+      overflow-y: scroll;
       .txtbody {
       .txtbody {
         display: block;
         display: block;
         word-wrap: break-word;
         word-wrap: break-word;