litian
4 天以前 bc1f3bb645daafa84b3a5e70a2331e40137de885
src/books/artAndDance/view/components/index.vue
@@ -1,63 +1,45 @@
<template>
  <div class="page-main" @scroll="throttledScrollHandler">
    <div
      class="page-content"
      :style="{
        fontSize: fontSize ? fontSize + 'px' : '16px',
        transform: `scale(${pageZoom ? pageZoom : 1})`,
        transformOrigin: 'center top',
      }"
    >
      <pageHeader
        v-if="showCatalogList.indexOf(1) > -1"
        :showPageList="loadPageList"
      ></pageHeader>
      <chapterOne
        @saveCharacters="saveCharacters"
        v-if="showCatalogList.indexOf(2) > -1"
        :showPageList="loadPageList"
      ></chapterOne>
      <chapterTwo
        v-if="showCatalogList.indexOf(3) > -1"
        :showPageList="loadPageList"
      >
      </chapterTwo>
      <chapterThree
      v-if="showCatalogList.indexOf(4) > -1"
        :showPageList="loadPageList"
      ></chapterThree>
      <chapterFour
      v-if="showCatalogList.indexOf(5) > -1"
        :showPageList="loadPageList"
      ></chapterFour>
      <chapterFive
      v-if="showCatalogList.indexOf(6) > -1"
        :showPageList="loadPageList"
      ></chapterFive>
      <chapterSix
        v-if="showCatalogList.indexOf(7) > -1"
        :showPageList="loadPageList"
      ></chapterSix>
    <div id="searchDomBox" style="display: none">
      <div id="searchContent"></div>
    </div>
    <div class="page-content" :style="{
      fontSize: fontSize ? fontSize + 'px' : '16px',
      transform: `scale(${pageZoom ? pageZoom : 1})`,
      transformOrigin: 'center top',
    }">
      <pageHeader v-if="showCatalogList.indexOf(1) > -1" :showPageList="loadPageList"></pageHeader>
      <chapterOne v-if="showCatalogList.indexOf(2) > -1" :showPageList="loadPageList"></chapterOne>
      <chapterTwo v-if="showCatalogList.indexOf(3) > -1" :showPageList="loadPageList">
      </chapterTwo>
      <chapterThree v-if="showCatalogList.indexOf(4) > -1" :showPageList="loadPageList"></chapterThree>
      <chapterFour v-if="showCatalogList.indexOf(5) > -1" :showPageList="loadPageList"></chapterFour>
      <chapterFive v-if="showCatalogList.indexOf(6) > -1" :showPageList="loadPageList"></chapterFive>
      <chapterSix v-if="showCatalogList.indexOf(7) > -1" :showPageList="loadPageList"></chapterSix>
    </div>
    <miniAudio :path="audioPath" :currentTime="currentTime" @closeMiniAudio="closeMiniAudio" ref="audioPlayer">
    </miniAudio>
  </div>
</template>
<script>
import Vue from "vue";
import pageHeader from "./header.vue";
import chapterOne from "./chapter001.vue";
import chapterTwo from "./chapter002.vue";
import chapterThree from "./chapter003.vue"
import chapterFour from './chapter004.vue'
import chapterFive from './chapter005.vue'
import chapterSix from './chapter006.vue'
import chapterThree from "./chapter003.vue";
import chapterFour from "./chapter004.vue";
import chapterFive from "./chapter005.vue";
import chapterSix from "./chapter006.vue";
import NoteIcon from "@/assets/images/biji.png";
import miniAudio from "@/components/miniAudio/index.vue";
import _ from "lodash";
import Swiper from "swiper/bundle";
import "swiper/swiper-bundle.css";
import Viewer from "viewerjs";
import "viewerjs/dist/viewer.css";
export default {
  name: "pageContent",
  data() {
    return {
      catalogLength: 7, // 总章节数
@@ -69,8 +51,12 @@
      observer: null,
      loadPageObserver: null,
      loadPageList: [],
      questionData: {},
      questionDataMap: {},
      renderSignMap: {},
      highlightData: null,
      audioPath: "",
      currentTime: null,
      videoList: [],
    };
  },
  computed: {
@@ -105,7 +91,17 @@
        setTimeout(() => {
          this.initSwiper();
          this.initViewer();
          this.closeAudio();
          this.closeVideo();
        }, 200);
      },
    },
    pageZoom: {
      handler(newVal, oldVal) {
        const scrollBox = (
          this.container ? this.container : document
        ).querySelector(".page-main");
        scrollBox.scrollTop = (scrollBox.scrollTop / oldVal) * newVal;
      },
    },
  },
@@ -135,6 +131,14 @@
        delSign: (data) => {
          this.delSign(data);
        },
        // 全文检索
        searchBookByKeyword: (keyword) => {
          return this.searchTextByPage(keyword);
        },
        // 跳转检索结果位置
        jumpSearchItem: (data) => {
          this.searchItemLocation(data);
        },
      });
    }
@@ -160,7 +164,7 @@
    // 测试页面跳转
    // setTimeout(() => {
    //   this.gotoPage(1, 10);
    //   this.gotoPage(6, 30);
    //   setTimeout(() => {
    //     this.renderSign("Highlight", {
    //       id: "2ACA9359",
@@ -174,14 +178,43 @@
    //     ids: ["2ACA9359"]
    //   });
    // }, 2000);
    //   }, 5000);
    // }, 1000);
    // }, 500);
    // const pageDom = (this.container ? this.container : document)
    //   .querySelector("#app")
    //   .querySelectorAll(".page-box");
    // 检索
    // console.log(this.searchTextByPage("保护内脏器官"), "searchTextByPage");
    // 检索跳转
    // this.searchItemLocation({
    //   catalog: 2,
    //   page: 10,
    //   txt: " 运动系统是由骨、骨连结和骨骼肌三部分组成的。全身的骨通过骨连结组成人体骨骼(见图1-1)。骨骼是人体的支架,具有保护内脏器官、供肌肉附着和作为肌肉运动的杠杆等作用。在神经系统的支配下,肌肉收缩牵动所附着的骨绕着关节转动,使身体产生各种动作。所以,运动系统具有运动、支持和保护等功能,幼年时期的骨骼还具有造血功能。 ",
    //   txtIndex: 57
    // });
    // }, 500);
  },
  methods: {
    // setZoom1() {
    //   let scale = this.$store.state.qiankun.scale + 10;
    //   const scrollBox = (
    //     this.container ? this.container : document
    //   ).querySelector(".page-main");
    //   this.$store.commit("setZoom", scale);
    // },
    // setZoom2() {
    //   let scale = this.$store.state.qiankun.scale - 10;
    //   const scrollBox = (
    //     this.container ? this.container : document
    //   ).querySelector(".page-main");
    //   this.$store.commit("setZoom", scale);
    // },
    // 滚动监听
    scrollFun(event) {
      this.handleVideoPicture()
      // 判断向上滚动还是向下滚动
      if (event.target.scrollTop > this.previousScrollTop) {
        this.getAduio();
        // 向下
        const currentScrollTop =
          event.target.scrollTop + event.target.offsetHeight;
@@ -205,6 +238,7 @@
          }
        }
      } else if (event.target.scrollTop < this.previousScrollTop) {
        this.handleAudio();
        // 向上
        const currentScrollTop = event.target.scrollTop;
        if (currentScrollTop <= this.loadThreshold) {
@@ -270,6 +304,10 @@
    // 渲染标记
    renderSign(type, data) {
      // 父层设置禁止渲染标记时不再进行渲染
      if (this.$store.state.qiankun.disableSign) {
        return false;
      }
      const existence = (
        this.container ? this.container : document
      ).querySelector(`[dataid="${data.id}"]`);
@@ -414,8 +452,8 @@
          page && pageThemeColor && pageThemeColor[page]
            ? pageThemeColor[page]
            : chapterNum && chapterThemeColor && chapterThemeColor[chapterNum]
            ? chapterThemeColor[chapterNum]
            : bookThemeColor;
              ? chapterThemeColor[chapterNum]
              : bookThemeColor;
        if (themeColor) {
          domItem.style.color = themeColor;
        }
@@ -432,8 +470,8 @@
          page && pageThemeColor && pageThemeColor[page]
            ? pageThemeColor[page]
            : chapterNum && chapterThemeColor && chapterThemeColor[chapterNum]
            ? chapterThemeColor[chapterNum]
            : bookThemeColor;
              ? chapterThemeColor[chapterNum]
              : bookThemeColor;
        if (themeColor) {
          domItem.style.backgroundColor = themeColor;
        }
@@ -450,14 +488,15 @@
          page && pageThemeColor && pageThemeColor[page]
            ? pageThemeColor[page]
            : chapterNum && chapterThemeColor && chapterThemeColor[chapterNum]
            ? chapterThemeColor[chapterNum]
            : bookThemeColor;
              ? chapterThemeColor[chapterNum]
              : bookThemeColor;
        if (themeColor) {
          domItem.style.borderColor = themeColor;
        }
      });
    },
    getParentWithClass(element, className) {
      console.log(element, className, "element, className");
      while (element.parentElement) {
        element = element.parentElement;
        if (element.classList.contains(className)) {
@@ -480,7 +519,7 @@
          const catalog = catalogDom.getAttribute("num");
          let text = null;
          if (target.querySelector("p")) {
            text = target.querySelector("p").textContent.substring(0, 20);
            text = target.querySelector("p").textContent.substring(0, 50);
          }
          // 返回页码和章节信息
          if (this.$store.state.qiankun && this.$store.state.qiankun.pageChange)
@@ -506,12 +545,14 @@
              target,
              "chapter"
            );
            // 添加页码
            this.loadPageList.push(Number(page));
            const catalog = catalogDom.getAttribute("num");
            // if (!this.questionData[page]) {
            // if (!this.questionDataMap[page]) {
            //   if (testData && testData[catalog]) {
            //     if (testData[catalog][page]) {
            //       if (Array.isArray(testData[catalog][page])) {
            //         this.questionData[page] = await getQuestionList(
            //         this.questionDataMap[page] = await getQuestionList(
            //           page,
            //           testData[catalog][page],
            //           this.config.activeBook
@@ -525,14 +566,12 @@
            //             this.config.activeBook
            //           );
            //         }
            //         this.questionData[page] = obj;
            //         this.questionDataMap[page] = obj;
            //       }
            //       console.log("题目", this.questionData);
            //       console.log("题目", this.questionDataMap);
            //     }
            //   }
            // }
            // 添加页码
            this.loadPageList.push(Number(page));
            // 渲染这一页的标记
            for (const key in this.renderSignMap) {
              if (this.renderSignMap[key][page]) {
@@ -540,6 +579,39 @@
                  this.renderSign(key, item);
                });
              }
            }
            // 处理高亮
            if (this.highlightData) {
              // 高亮行
              setTimeout(() => {
                // 获取页面所有text节点
                const pageTextList = document.createTreeWalker(
                  target,
                  NodeFilter.SHOW_TEXT
                );
                // 匹配关键字
                const allPageTextNodes = [];
                let currentNode = pageTextList.nextNode();
                while (currentNode) {
                  allPageTextNodes.push(currentNode);
                  currentNode = pageTextList.nextNode();
                }
                for (let i = 0; i < allPageTextNodes.length; i++) {
                  const textDom = allPageTextNodes[i];
                  let txtIndex = textDom.textContent.indexOf(
                    this.highlightData.txt
                  );
                  if (txtIndex > -1) {
                    textDom.parentNode.style.transition =
                      "background-color 0.8s";
                    textDom.parentNode.scrollIntoView();
                    textDom.parentNode.style.backgroundColor = "#79bbf0";
                    setTimeout(() => {
                      textDom.parentNode.style.backgroundColor = "";
                    }, 1000);
                  }
                }
              }, 100);
            }
            if (this.loadPageList.length > 5) {
              // 超过5页
@@ -567,16 +639,12 @@
          spaceBetween: 30, // 间隔
          // 如果需要前进后退按钮
          navigation: {
            nextEl: (this.container ? this.container : document).querySelector(
              ".swiper-button-next"
            ),
            prevEl: (this.container ? this.container : document).querySelector(
              ".swiper-button-prev"
            ),
            nextEl: dom.querySelector(".swiper-button-next"),
            prevEl: dom.querySelector(".swiper-button-prev"),
          },
          // 窗口变化,重新init,针对F11全屏和放大缩小,必须加
          observer: true,
          observeParents: true,
          // observer: true,
          // observeParents: true
          // // 如果需要分页器
          // pagination: {
          //   el: (this.container ? this.container : document).querySelector(
@@ -599,12 +667,8 @@
          spaceBetween: 30, // 间隔
          // 如果需要前进后退按钮
          navigation: {
            nextEl: (this.container ? this.container : document).querySelector(
              ".swiper-button-next"
            ),
            prevEl: (this.container ? this.container : document).querySelector(
              ".swiper-button-prev"
            ),
            nextEl: dom.querySelector(".swiper-button-next"),
            prevEl: dom.querySelector(".swiper-button-prev"),
          },
          // 窗口变化,重新init,针对F11全屏和放大缩小,必须加
          observer: true,
@@ -644,19 +708,225 @@
        });
      }
    },
    // 点击事件:将生僻单词传给 主应用 从而调用词典功能
    saveCharacters(event, word) {
      console.log("生僻字", event, word);
      if (this.$store.state.qiankun && this.$store.state.qiankun.chooseWords) {
        this.$store.state.qiankun.chooseWords({
          type: "word",
          word,
          x: event.pageX,
          y: event.pageY,
    // 根据关键字全文检索
    searchTextByPage(keyword) {
      const searchResult = [];
      let catalogIndex = 0;
      // 所有章节组件(每本书制作时单独配置)
      const pageData = {
        pageHeader,
        chapterOne,
        chapterTwo,
        chapterThree,
        chapterFour,
        chapterFive,
        chapterSix,
      };
      // 遍历所有章节文件
      for (const key in pageData) {
        catalogIndex++;
        let pageComponent, pageExample;
        // 先渲染一次当前章节文件(这时页面的内容为空),获取页码信息
        pageComponent = Vue.extend(pageData[key]);
        pageExample = new pageComponent({
          propsData: {
            showPageList: [],
            questionData: {},
            isSearch: true,
          },
        });
        pageExample.$mount(
          (this.container ? this.container : document).querySelector(
            "#searchContent"
          )
        );
        // 获取页码
        const pageDom = (this.container ? this.container : document)
          .querySelector("#searchDomBox")
          .querySelectorAll(".page-box");
        const pages = [];
        for (let i = 0; i < pageDom.length; i++) {
          const pageDomItem = pageDom[i];
          pages.push(Number(pageDomItem.getAttribute("page")));
        }
        // 获取页面结束,卸载销毁
        pageExample.$destroy();
        (this.container ? this.container : document).querySelector(
          "#searchDomBox"
        ).innerHTML = '<div id="searchContent"></div>';
        // 遍历页码
        if (pages.length) {
          for (let i = 0; i < pages.length; i++) {
            const pageNum = pages[i];
            // 动态渲染对应章节的页码
            pageComponent = Vue.extend(pageData[key]);
            pageExample = new pageComponent({
              propsData: {
                showPageList: [pageNum],
                questionData: {},
                isSearch: true,
              },
            });
            pageExample.$mount(
              (this.container ? this.container : document).querySelector(
                "#searchContent"
              )
            );
            // 获取对应页面dom
            const thisPageDom = (this.container ? this.container : document)
              .querySelector("#searchDomBox")
              .querySelector(`[page="${pageNum}"]`);
            if (thisPageDom) {
              // 获取页面所有text节点
              const pageTextList = document.createTreeWalker(
                thisPageDom,
                NodeFilter.SHOW_TEXT
              );
              // 匹配关键字
              const allPageTextNodes = [];
              let currentNode = pageTextList.nextNode();
              while (currentNode) {
                allPageTextNodes.push(currentNode);
                currentNode = pageTextList.nextNode();
              }
              for (let i = 0; i < allPageTextNodes.length; i++) {
                const textDom = allPageTextNodes[i];
                let txtIndex = textDom.textContent.indexOf(keyword);
                if (txtIndex > -1) {
                  // 记录关键字所在页码、章节以及匹配到的段落
                  searchResult.push({
                    page: pageNum,
                    catalog: catalogIndex,
                    txt: textDom.textContent,
                    txtIndex: txtIndex,
                  });
                }
              }
              // 结束,卸载销毁
              pageExample.$destroy();
              (this.container ? this.container : document).querySelector(
                "#searchDomBox"
              ).innerHTML = '<div id="searchContent"></div>';
            }
          }
        }
      }
      // 输出搜索结果
      console.log(searchResult);
      return searchResult;
    },
    // 根据检索结果跳转对应位置并高亮
    searchItemLocation(data) {
      // 记录高亮信息
      this.highlightData = data;
      // 跳转
      this.gotoPage(data.catalog, data.page, () => { });
    },
    // 页面向下滚动,音频小窗播放功能
    getAduio() {
      let allVideo = (
        this.container ? this.container : document
      ).querySelectorAll(".audio");
      allVideo = Array.from(allVideo);
      this.videoList = allVideo;
      if (allVideo.length) {
        // 查找播放状态的最后一条音频
        const playAudio = allVideo
          .reverse()
          .find((item) => item.paused == false);
        if (playAudio) {
          const bottomGap = playAudio.getBoundingClientRect().bottom;
          if (bottomGap < 0) {
            playAudio.pause();
            this.audioPath = playAudio.src;
            this.currentTime = playAudio.currentTime;
          }
        }
      }
    },
    // 页面向上滚动,音频小窗回收
    handleAudio() {
      if (!this.audioPath) return false;
      let allVideo = (
        this.container ? this.container : document
      ).querySelectorAll(".audio");
      allVideo = Array.from(allVideo);
      if (allVideo.length) {
        //查找与小窗播放音频同源的页面audio DOM
        const playAudio = allVideo.find((item) => item.src == this.audioPath);
        if (playAudio) {
          const bottomGap = playAudio.getBoundingClientRect().bottom;
          if (bottomGap >= 0) {
            if (this.$refs.audioPlayer) {
              const playerState = this.$refs.audioPlayer.getVideoPlayer();
              this.audioPath = "";
              playAudio.currentTime = playerState.currentTime;
              if (!playerState.paused) playAudio.play();
            }
          }
        }
      }
    },
    // 关闭mini video
    closeMiniAudio() {
      this.audioPath = "";
    },
    // 点击音频播放,关闭其他音频
    closeAudio() {
      let allAudio = (
        this.container ? this.container : document
      ).querySelectorAll(".audio");
      for (let index = 0; index < allAudio.length; index++) {
        const item = allAudio[index];
        item.addEventListener("play", () => {
          const audioList = Array.from(allAudio);
          for (let cindex = 0; cindex < audioList.length; cindex++) {
            const citem = audioList[cindex];
            if (citem.currentSrc != item.src) {
              citem.pause();
            }
          }
          this.closeMiniAudio();
        });
      }
      // chooseWords
    },
    // 点击视频关闭其他
    closeVideo() {
      let allVideo = (
        this.container ? this.container : document
      ).querySelectorAll(".video");
      for (let index = 0; index < allVideo.length; index++) {
        const item = allVideo[index];
        item.addEventListener("playing", (item) => {
          const path = item.srcElement.src;
          const videoList = Array.from(allVideo);
          for (let cindex = 0; cindex < videoList.length; cindex++) {
            const citem = videoList[cindex];
            if (citem.currentSrc != path && path) {
              citem.pause();
            }
          }
        });
      }
    },
    // 视频小窗
    handleVideoPicture() {
      let doms = (
        this.container ? this.container : document
      ).querySelectorAll(".video");
      doms = Array.from(doms)
      if (!doms.length) return false
      const playVudio = doms
        .reverse()
        .find((item) => item.paused == false);
      if (playVudio) {
        const bottomGap = playVudio.getBoundingClientRect().bottom;
        const topGap = playVudio.getBoundingClientRect().top;
        if (bottomGap < 0 || topGap > window.innerHeight) {
          if(playVudio.readyState) playVudio.requestPictureInPicture();
        }
      }
    }
  },
  components: {
    pageHeader,
@@ -665,7 +935,8 @@
    chapterThree,
    chapterFour,
    chapterFive,
    chapterSix
    chapterSix,
    miniAudio,
  },
};
</script>
@@ -675,6 +946,7 @@
  width: 100%;
  height: 100%;
  overflow: auto;
  .page-content {
    max-width: 816px;
    min-width: 375px;