gemercheung 2 years ago
parent
commit
0aebfdaacd

+ 47 - 17
packages/qjkankan-editor/public/static/template/customTooltip.xml

@@ -48,7 +48,7 @@
                
                 if(%2 == 0,
                 txtreplace(iconUrl,'.svg','.png');
-                trace('新增::',iconUrl);
+                <!-- trace('新增::',iconUrl); -->
                 set(hotspot[get(hsp_name)].url,get(iconUrl));
                 set(hotspot[get(hsp_name)].animatiedOn,1);
                 set(hotspot[get(hsp_name)].frameNumber,40);
@@ -71,7 +71,9 @@
         );
         
         <!-- 个性化标签(3) -->
-        if(%2 == 3,        
+        if(%2 == 3,
+            trace('isShowLine::',isShowLine);  
+          
             set(hotspot[get(hsp_name)].height,80);
             set(hotspot[get(hsp_name)].width,1);
             set(hotspot[get(hsp_name)].padding,0);
@@ -79,12 +81,26 @@
             set(hotspot[get(hsp_name)].visible,false);
             txtadd(line,'<div style="background-color: ',get(borderColor),';width:1px;height:80px;"></div>');
             copy(hotspot[get(hsp_name)].html,line);
-    
-            set_label_dir(get(hsp_name),get(lineDirection),0);
+            
+             if(isShowLine == 1, set_label_dir(get(hsp_name),get(lineDirection),0););
+           
+             if(isShowLine == 0, 
+                txtadd(tooltipname, 'tooltip_', get(hsp_name)); 
+                txtadd(tooltipdot, 'tooldot_', get(hsp_name)); 
+                <!-- set_label_dir(get(hsp_name),get(lineDirection),0); -->
+                set(hotspot[get(hsp_name)].width,0);
+                trace('隐藏标线::',get(tooltipdot));
+                set(layer[get(tooltipname)].x,0);
+                set(layer[get(tooltipname)].y,0);
+                set(layer[get(tooltipname)].rotate,0);
+                set(hotspot[get(hsp_name)].visible,true);
+                set(layer[get(tooltipname)].visible,true);
+                set(layer[get(tooltipdot)].visible,false);
+             );  
         );
      
         txtadd(hotspot[get(hsp_name)].onloaded,"do_crop_animation(get(framewidth),get(framewidth),get(frameRate));");
-        trace('last_add::',hotspot[get(hsp_name)].onloaded);
+        <!-- trace('last_add::',hotspot[get(hsp_name)].onloaded); -->
         addhotspot(get(hsp_name));
     </action>
 
@@ -178,11 +194,12 @@
 
         <!-- 个性化标签(3) -->
         if(get(hotspottype) == 3,    
-          trace('add_tooltip_label::',get(lineDirection));
+          trace('tl-isShowLine::',get(hotspottitle),get(isShowLine));
+          if(
+            isShowLine==1,
             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); );
-      
-
+          );
         );
         copy(layer[get(tooltipname)].html, labelCode);
         
@@ -216,10 +233,10 @@
 
         <!-- 个性化标签(3) -->
         if(get(hotspottype) == 3,    
-            <!-- trace('lineDirection3::',lineDirection);  -->
-                            <!-- call(set_label_dir); -->
-            set(layer[get(tooldot)].visible,true);
-            set_label_dir(get(tooldot),get(lineDirection),2);            
+            trace('add_tooltip_dot::',get(isShowLine)); 
+            if(isShowLine ==1,set(layer[get(tooldot)].visible,true);set_label_dir(get(tooldot),get(lineDirection),2););
+            if(isShowLine ==0,set(layer[get(tooldot)].visible,false););
+                     
         );
 
     </action>
@@ -308,8 +325,9 @@
 
         );
 
-             if(get(dir)==0, set(hotspot[get(dirItem)].visible,true););
-
+          if(get(dir)==0, set(hotspot[get(dirItem)].visible,true););
+          if(get(dir)==1, set(layer[get(dirItem)].visible,true););
+          if(get(dir)==2, set(layer[get(dirItem)].visible,true););
     </action>
 
     <action name='set_label_pos'>
@@ -422,10 +440,22 @@
         txtadd(line,'<div style="background-color: ',get(borderColor),';width:1px;height:80px;"></div>');
         <!-- txtadd(dot,'<div style="background-color: ',get(borderColor),';width:10px;height:10px;"></div>'); -->
         copy(hotspot[get(hsp_name)].html,line);
+        if(isShowLine == 1, 
+            set_label_dir(get(hsp_name),get(lineDirection),0);
+            set_label_dir(get(hsLabel),get(lineDirection),1);
+            set_label_dir(get(hsDot),get(lineDirection),2);
+        );
+        if(isShowLine == 0, 
+            set(hotspot[get(hsp_name)].width,0);
+            trace('编辑标注线');
+            set(layer[get(hsLabel)].x,0);
+            set(layer[get(hsLabel)].y,0);
+            set(layer[get(hsLabel)].rotate,0);
+            set(hotspot[get(hsp_name)].visible,true);
+            set(layer[get(hsLabel)].visible,true);
+            set(layer[get(hsDot)].visible,false);
+        );
 
-        set_label_dir(get(hsp_name),get(lineDirection),0);
-        set_label_dir(get(hsLabel),get(lineDirection),1);
-        set_label_dir(get(hsDot),get(lineDirection),2);
     
        );
       if(get(hsp_type) LE 2,

+ 1 - 0
packages/qjkankan-editor/src/core/hotspot.js

@@ -67,6 +67,7 @@ const convertBaseStyle = (dest, origin) => {
         dest.style.textColor = origin.personalizedTagInfo.textColor
         dest.style.textDirection = origin.personalizedTagInfo.textDirection
         dest.style.textNumPerLine = origin.personalizedTagInfo.textNumPerLine
+        dest.style.isShowLine = origin.personalizedTagInfo.isShowLine
     }
 
 }

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

@@ -40,19 +40,19 @@
         ref="editor"
         :html="hotspot.imageTextInfo.text"
         :placeholder="$i18n.t('hotspot.text_placeholder')"
-        :maxlength="200"
+        :maxlength="500"
         @change="onEditorChange"
       />
-      <span class="count">{{textLength}}/200</span>
+      <span class="count">{{textLength}}/500</span>
     </div>
 
-    <div class="switcher-wrap">
+    <!-- <div class="switcher-wrap">
     <span class="remark">{{$i18n.t('hotspot.apply_to_all')}}</span>
       <Switcher
         :value="hotspot.imageTextInfo.isApplyToAll"
         @change="onSwitcherChange"
       />
-    </div>
+    </div> -->
     
     <div class="remark">{{$i18n.t('hotspot.add_audio')}}</div>
     <button class="add-btn" v-if="!hotspot.imageTextInfo.audio.id" @click="isShowAudioSelect = true">
@@ -133,6 +133,8 @@ export default {
     },
     onEditorChange(content) {
       this.textLength = content.size || 0
+    //  console.log('content',content);
+     this.hotspot.imageTextInfo.text = content.html;
     },
     onSwitcherChange(v) {
       this.hotspot.imageTextInfo.isApplyToAll = v

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

@@ -21,7 +21,8 @@
     "tiny-emitter": "^2.1.0",
     "vue": "^3.2.13",
     "vue-i18n": "^9.1.10",
-    "vuex": "^4.0.0"
+    "vuex": "^4.0.0",
+    "vue3-lazyload":"^0.3.6"
   },
   "devDependencies": {
     "@babel/core": "^7.12.16",

+ 14 - 15
packages/qjkankan-view/src/components/assembly/MobileTags/metas/metas-imagetext.vue

@@ -1,36 +1,35 @@
 <template>
   <div class="txtcon">
     <div class="title">
-      <i class="iconfont icon-material_text"/>
-      {{currentTag.hotspotTitle}} 
+      <i class="iconfont icon-material_text" />
+      {{ currentTag.hotspotTitle }}
     </div>
 
-    <div class="txtbody" v-html="currentTag.articleInfo.html">
-    </div>
+    <div class="txtbody" v-html="currentTag.articleInfo.html"></div>
   </div>
 </template>
 
 <script setup>
-import { reactive, defineEmits, onBeforeMount, onMounted, ref, watchEffect, computed, watch, nextTick } from "vue";
+import { computed, onMounted } from "vue";
 import { useStore } from "vuex";
 const store = useStore();
 
-const currentTag = computed(() => store.getters['tags/currentTag'])
-  
+const currentTag = computed(() => store.getters["tags/currentTag"]);
+
 </script>
 
 <style lang="scss" scoped>
-.txtcon{
+.txtcon {
   display: flex;
   align-items: flex-start;
   justify-content: center;
   height: 100%;
   width: 100%;
   padding-top: 70px;
-  
-  .txtbody{
+
+  .txtbody {
     width: 744px;
-    background: rgba(0,0,0,0.6);
+    background: rgba(0, 0, 0, 0.6);
     border-radius: 10px;
     font-size: 14px;
     color: #fff;
@@ -42,21 +41,21 @@ const currentTag = computed(() => store.getters['tags/currentTag'])
       line-height: 24px;
     }
   }
-  .title{
+  .title {
     position: absolute;
     left: 20px;
     top: 20px;
     height: 36px;
     line-height: 36px;
     padding: 0 16px;
-    background: rgba(0,0,0,0.6);
+    background: rgba(0, 0, 0, 0.6);
     border-radius: 20px;
     color: #fff;
     font-size: 14px;
     z-index: 999;
-    >i{
+    > i {
       margin-right: 4px;
     }
   }
 }
-</style>
+</style>

+ 460 - 27
packages/qjkankan-view/src/components/assembly/Tags/metas/metas-imagetext.vue

@@ -1,62 +1,495 @@
 <template>
-  <div class="txtcon">
+  <div class="imagetxtcon">
     <div class="title">
-      <i class="iconfont icon-material_text"/>
-      {{currentTag.hotspotTitle}} 
+      <i class="iconfont icon-material_image title-icon" />
+      {{ currentTag.hotspotTitle }}
     </div>
 
-    <div class="txtbody" v-html="currentTag.articleInfo.html">
+    <div class="main-text-container">
+      <div class="image-left">
+        <div
+          class="realimgcon"
+          :class="{ fullimgcon: objectFit === 'contain' }"
+        >
+          <img
+            class="image"
+            :src="currentTag.imageTextInfo.imageList[currentIndex].ossPath"
+            :style="imageStyle"
+            @wheel.prevent="onImageWheel"
+          />
+        </div>
+        <div class="toolbar-container">
+          <div class="toolbar">
+            <div class="left">
+              <i
+                v-if="currentTag.imageTextInfo.imageList.length > 1"
+                class="iconfont icon-material_preview_previous hover-tips"
+                :class="{ disabled: currentIndex === 0 }"
+                @click="onClickPrevious()"
+              >
+                <div>
+                  <div class="remark">{{ $t("common.prev") }}</div>
+                </div>
+              </i>
+              <i
+                v-if="currentTag.imageTextInfo.imageList.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">{{ $t("common.next") }}</div>
+                </div>
+              </i>
+            </div>
+            <div class="right">
+              <i
+                :class="{ disabled: scaleRate >= 2 }"
+                class="iconfont icon-material_preview_enlarge hover-tips"
+                @click="onClickZoomIn()"
+              >
+                <div>
+                  <div class="remark">{{ $t("common.zoomIn") }}</div>
+                </div>
+              </i>
+
+              <i
+                :class="{ disabled: scaleRate <= 0.5 }"
+                class="iconfont icon-material_preview_narrow hover-tips"
+                @click="onClickZoomOut()"
+              >
+                <div>
+                  <div class="remark">{{ $t("common.zoomOut") }}</div>
+                </div>
+              </i>
+
+              <i
+                v-if="canFullScreen && objectFit === 'scale-down'"
+                class="iconfont icon-material_preview_full_screen hover-tips"
+                @click="onClickFullScreen()"
+              >
+                <div>
+                  <div class="remark">{{ $t("common.fullScene") }}</div>
+                </div>
+              </i>
+
+              <i
+                v-if="canFullScreen && objectFit === 'contain'"
+                class="iconfont icon-material_preview_drop_out hover-tips"
+                @click="onClickCancelFullScreen()"
+              >
+                <div>
+                  <div class="remark">{{ $t("common.exitFullScene") }}</div>
+                </div>
+              </i>
+            </div>
+          </div>
+          <div class="audio-control">
+            <ui-icon @click="pauseAudio" type="player_pause" v-if="isPlaying"></ui-icon>
+            <ui-icon @click="playAudio"  type="player_playback" v-else></ui-icon>
+            <span class="timeline">{{ time }} / {{ audioAllTime }}</span>
+          </div>
+          <audio
+            id="audioTag"
+            class="noshow"
+            autoplay
+            :src="currentTag.imageTextInfo.audio.ossPath"
+          ></audio>
+        </div>
+      </div>
+      <!-- 文字 -->
+      <div class="text-right" v-if="currentTag.imageTextInfo.text.length > 0">
+        <div class="txtbody" v-html="currentTag.imageTextInfo.text"></div>
+      </div>
     </div>
+    <div class="btnmask"></div>
   </div>
 </template>
 
 <script setup>
-import { reactive, defineEmits, onBeforeMount, onMounted, ref, watchEffect, computed, watch, nextTick } from "vue";
+import { nextTick } from "process";
+import { ref, computed, unref, onMounted, onUnmounted } from "vue";
 import { useStore } from "vuex";
 const store = useStore();
+const hasAudio = computed(
+  () => currentTag.value.imageTextInfo.audio.ossPath.length > 0
+);
+
+const scaleRate = ref(1);
+
+const objectFit = ref("scale-down");
+
+const canFullScreen = ref(true);
+
+const imageStyle = computed(() => {
+  return {
+    transform: `scale(${scaleRate.value})`,
+    objectFit: objectFit.value,
+  };
+});
+
+const currentTag = computed(() => store.getters["tags/currentTag"]);
+
+const currentIndex = ref(0);
+
+const onImageWheel = (e) => {
+  if (e.deltaY < 0) {
+    let scle = scaleRate.value * 1.1;
+    if (scle > 2) {
+      scaleRate.value = 2;
+    } else {
+      scaleRate.value = scle;
+    }
+  } else {
+    let scle = scaleRate.value * 0.9;
+    if (scle < 0.5) {
+      scaleRate.value = 0.5;
+    } else {
+      scaleRate.value = scle;
+    }
+  }
+};
+
+const onClickZoomIn = () => {
+  scaleRate.value = Math.min(scaleRate.value * 1.1, 2);
+};
+const onClickZoomOut = () => {
+  scaleRate.value = Math.max(scaleRate.value * 0.9, 0.5);
+};
+
+const onClickPrevious = () => {
+  if (currentIndex.value > 0) {
+    currentIndex.value--;
+  }
+};
+const onClickNext = () => {
+  if (
+    currentIndex.value <
+    unref(currentTag).imageTextInfo.imageList.length - 1
+  ) {
+    currentIndex.value++;
+    console.log("currentIndex", unref(currentIndex));
+  }
+};
 
-const currentTag = computed(() => store.getters['tags/currentTag'])
-  
+const onClickFullScreen = () => {
+  scaleRate.value = 1;
+  objectFit.value = "contain";
+};
+const onClickCancelFullScreen = () => {
+  scaleRate.value = 1;
+  objectFit.value = "scale-down";
+};
+function transTime(time) {
+  var duration = parseInt(time);
+  var minute = parseInt(duration / 60);
+  var sec = (duration % 60) + "";
+  var isM0 = ":";
+  if (minute == 0) {
+    minute = "00";
+  } else if (minute < 10) {
+    minute = "0" + minute;
+  }
+  if (sec.length == 1) {
+    sec = "0" + sec;
+  }
+  return minute + isM0 + sec;
+}
+const time = ref("00:00");
+const audioAllTime = ref("");
+const isPlaying = ref(false);
+onMounted(() => {
+  const audio = document.querySelector("#audioTag");
+  nextTick(() => {
+    if (unref(hasAudio)) {
+      audio.addEventListener("loadedmetadata", (e) => {
+        audioAllTime.value = transTime(e.currentTarget.duration);
+        console.log("e", unref(audioAllTime));
+        isPlaying.value = true;
+      });
+
+      console.log("audio", audio);
+      audio.addEventListener(
+        "playing",
+        function (e) {
+          isPlaying.value = true;
+        },
+        false
+      );
+      audio.addEventListener(
+        "pause",
+        function (e) {
+          isPlaying.value = false;
+        },
+        false
+      );
+      audio.addEventListener(
+        "timeupdate",
+        function (event) {
+          time.value = transTime(event.target.currentTime);
+        },
+        false
+      );
+    }
+  });
+});
+
+const playAudio = () => {
+  const audio = document.querySelector("#audioTag");
+  audio && audio.play();
+};
+const pauseAudio = () => {
+  const audio = document.querySelector("#audioTag");
+  audio && audio.pause();
+};
+onUnmounted(()=>{
+  pauseAudio();
+})
 </script>
 
 <style lang="scss" scoped>
-.txtcon{
+.imagetxtcon {
   display: flex;
-  align-items: flex-start;
+  align-items: center;
   justify-content: center;
   height: 100%;
   width: 100%;
-  padding-top: 70px;
-  
-  .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;
-    line-height: 1.5;
-    :deep(p) {
-      line-height: 24px;
+
+  > video {
+    max-width: 960px;
+    height: 540px;
+  }
+
+  .main-text-container {
+    width: 100%;
+    height: 550px;
+    // max-width: 1920px;
+    max-width: 998px;
+    display: flex;
+    background: rgba(0, 0, 0, 0.8);
+    .image-left {
+      flex: 1 1 58%;
+      overflow: hidden;
+      position: relative;
+    }
+    .text-right {
+      flex: 0 0 32%;
+      overflow: hidden;
+      color: white;
+      .txtbody {
+        display: block;
+        word-wrap: break-word;
+        margin: 30px;
+      }
     }
   }
-  .title{
+  .title {
     position: absolute;
     left: 20px;
     top: 20px;
     height: 36px;
     line-height: 36px;
     padding: 0 16px;
-    background: rgba(0,0,0,0.6);
+    background: rgba(0, 0, 0, 0.6);
     border-radius: 20px;
     color: #fff;
     font-size: 14px;
     z-index: 999;
-    >i{
+
+    > i {
       margin-right: 4px;
     }
   }
+
+  .toolbar-container {
+    position: absolute;
+    bottom: 14px;
+    left: 50%;
+    transform: translateX(-50%);
+    display: flex;
+    flex-direction: row;
+  }
+  .iconfont {
+    cursor: pointer;
+    color: white;
+    margin: 0 12px;
+    font-size: 22px;
+    // width: 24px;
+    // height: 24px;
+
+    &.disabled {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+  }
+  .toolbar {
+    height: 60px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 292px;
+    height: 60px;
+    border-radius: 8px;
+    background: rgba(0, 0, 0, 0.6);
+
+    z-index: 2;
+    .left {
+      width: 122px;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      position: relative;
+      &::after {
+        pointer-events: none;
+        content: "";
+        position: absolute;
+        height: 20px;
+        width: 2px;
+        right: -1px;
+        top: 50%;
+        transform: translateY(-50%);
+        // right: -18px;
+        // top: -4px;
+        // font-size: 20px;
+        background: rgba(255, 255, 255, 0.5);
+      }
+    }
+    .right {
+      width: 169px;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+
+    > span {
+      color: #fff;
+      text-align: center;
+      display: inline-block;
+      min-width: 50px;
+    }
+
+    // .append-splitter {
+    // &::after {
+    //   pointer-events: none;
+    //   content: "|";
+    //   position: absolute;
+    //   right: -18px;
+    //   top: -4px;
+    //   font-size: 20px;
+    //   color: rgba(255, 255, 255, 0.5);
+    // }
+    // }
+  }
+  .audio-control {
+    height: 60px;
+    border-radius: 8px;
+    background: rgba(0, 0, 0, 0.6);
+    width: 181px;
+    margin-left: 20px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    .iconfont {
+      margin-left: 0;
+    }
+    .timeline {
+      color: #fff;
+    }
+  }
+
+  .realimgcon {
+    width: 100%;
+    height: 100%;
+    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%;
+  }
+}
+
+.hover-tips,
+.hover-tips-warn {
+  position: relative;
+  font-size: 18px;
+
+  &:hover {
+    > div {
+      display: block;
+    }
+  }
+
+  // tip的方框
+  > div {
+    background: rgba(0, 0, 0, 0.6);
+    border: none;
+    cursor: default;
+    display: none;
+    z-index: 10000;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    top: -50px;
+    color: #fff;
+    pointer-events: none;
+    text-align: center;
+    word-break: keep-all;
+    padding: 0 8px;
+    font-size: 12px;
+    border-radius: 3px;
+
+    // tip的箭头
+    &::before {
+      border: 7px solid transparent;
+      border-top: 7px solid rgba(0, 0, 0, 0.6);
+      width: 0;
+      height: 0px;
+      content: "";
+      display: inline-block;
+      position: absolute;
+      bottom: -14px;
+      left: 50%;
+      transform: translateX(-50%);
+    }
+
+    // tip的文字
+    .remark {
+      line-height: 2.5;
+      color: #fff;
+    }
+  }
+}
+.btnmask {
+  width: 100%;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  pointer-events: none;
+  opacity: 0.5;
+  z-index: 1;
+  height: 60px;
+  background: linear-gradient(
+    180deg,
+    rgba(0, 0, 0, 0) 0%,
+    rgba(0, 0, 0, 0.7) 52%,
+    #000000 100%
+  );
 }
-</style>
+</style>