<template>
|
<div class="page-main" @scroll="throttledScrollHandler">
|
<div class="page-content" :style="{
|
fontSize: fontSize ? fontSize + 'px' : '18px',
|
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 "./chapterr001.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 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: 1, // 总章节数
|
showCatalogList: [], // 显示的章节
|
loadThreshold: 300, // 触发加载阈值
|
throttleThreshold: 100, // 节流阈值
|
previousScrollTop: 0,
|
throttledScrollHandler: null,
|
observer: null,
|
loadPageObserver: null,
|
loadPageList: [],
|
questionDataMap: {},
|
renderSignMap: {},
|
highlightData: null,
|
audioPath: "",
|
currentTime: null,
|
videoList: [],
|
};
|
},
|
computed: {
|
fontSize() {
|
return this.$store.state.qiankun.fontSize;
|
},
|
pageZoom() {
|
return this.$store.state.qiankun.scale / 100;
|
},
|
},
|
watch: {
|
showCatalogList: {
|
handler(newVal, oldVal) {
|
if (
|
this.$store.state.qiankun &&
|
this.$store.state.qiankun.catalogChange
|
) {
|
// 调用父层方法
|
this.$store.state.qiankun.catalogChange({
|
showCatalogList: newVal,
|
});
|
}
|
// 启动页码观察
|
setTimeout(() => {
|
this.initObservation();
|
this.initThemeColor();
|
}, 500);
|
},
|
},
|
loadPageList: {
|
handler(newVal, oldVal) {
|
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;
|
},
|
},
|
},
|
mounted() {
|
// 默认加载章节
|
this.showCatalogList = [1];
|
// 滚动监听节流
|
this.throttledScrollHandler = _.throttle(
|
this.scrollFun,
|
this.throttleThreshold,
|
{ leading: true, trailing: false }
|
);
|
// 定义子层方法
|
if (this.setGlobalState) {
|
// 提供页面跳转功能
|
this.setGlobalState({
|
gotoPage: (catalog, page) => {
|
this.gotoPage(catalog, page);
|
},
|
// 渲染笔记、高亮、划线
|
renderSign: (type, data) => {
|
// 因为调整为页面懒加载,所以渲染标记也需要按照页面进行处理,先储存数据,页面加载完成再渲染对应的标记;
|
this.handelSignData(type, data);
|
// this.renderSign(type, data);
|
},
|
// 删除笔记、高亮、划线
|
delSign: (data) => {
|
this.delSign(data);
|
},
|
// 全文检索
|
searchBookByKeyword: (keyword) => {
|
return this.searchTextByPage(keyword);
|
},
|
// 跳转检索结果位置
|
jumpSearchItem: (data) => {
|
this.searchItemLocation(data);
|
},
|
});
|
}
|
|
// 创建一个新的 Intersection Observer 实例,用于观察目标元素和执行相应的回调函数。
|
// new IntersectionObserver(callback, options):使用之前定义的 callback 回调函数和 options 配置选项来初始化 Intersection Observer 实例。
|
this.observer = new IntersectionObserver(this.pageChangeCallback, {
|
root: null, // 指定根元素,这里设为 null,表示选取整个视窗作为根元素。
|
rootMargin: "0px", // 指定根元素的边界,这里设为 "0px",表示根元素的边界和视窗的边界重合
|
threshold: 0.5, // 指定交叉比例,这里设为 0.5,表示当目标元素一半或更多显示在视窗中时触发回调函数。
|
});
|
|
this.loadPageObserver = new IntersectionObserver(this.loadPageCallback, {
|
root: null, // 指定根元素,这里设为 null,表示选取整个视窗作为根元素。
|
rootMargin: "0px", // 指定根元素的边界,这里设为 "0px",表示根元素的边界和视窗的边界重合
|
threshold: 0, // 指定交叉比例,这里设为 0.5,表示当目标元素一半或更多显示在视窗中时触发回调函数。
|
});
|
|
// 启动页码观察
|
setTimeout(() => {
|
this.initObservation();
|
this.initThemeColor();
|
}, 500);
|
|
// 测试页面跳转
|
// setTimeout(() => {
|
// this.gotoPage(1, 14);
|
// setTimeout(() => {
|
// this.renderSign("Highlight", {
|
// id: "2ACA9359",
|
// txt: "题一学习主题一 运动",
|
// page: "10",
|
// type: "Highlight",
|
// color: "#F5E12A"
|
// });
|
// setTimeout(() => {
|
// this.delSign({
|
// ids: ["2ACA9359"]
|
// });
|
// }, 2000);
|
// }, 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;
|
if (
|
currentScrollTop >=
|
event.target.scrollHeight - this.loadThreshold
|
) {
|
// 到达阈值
|
if (
|
this.showCatalogList[this.showCatalogList.length - 1] <
|
this.catalogLength
|
) {
|
// 加载下一章
|
this.showCatalogList.push(
|
this.showCatalogList[this.showCatalogList.length - 1] + 1
|
);
|
if (this.showCatalogList.length > 3) {
|
// 超过三章隐藏顶部一章
|
this.showCatalogList.shift();
|
}
|
}
|
}
|
} else if (event.target.scrollTop < this.previousScrollTop) {
|
this.handleAudio();
|
// 向上
|
const currentScrollTop = event.target.scrollTop;
|
if (currentScrollTop <= this.loadThreshold) {
|
// 到达阈值
|
if (this.showCatalogList[0] > 0) {
|
// 加载上一章
|
this.showCatalogList.unshift(this.showCatalogList[0] - 1);
|
if (this.showCatalogList.length > 3) {
|
// 超过三章隐藏底部一章
|
this.showCatalogList.pop();
|
}
|
}
|
}
|
}
|
// showCatalogList 当前显示的三个章节,watch监听传递给主应用
|
// 更新上一次滚动的位置
|
this.previousScrollTop = event.target.scrollTop;
|
},
|
// 章节、页面跳转
|
gotoPage(catalog, page) {
|
if (catalog >= 0 && catalog <= this.catalogLength) {
|
// 处理渲染章节
|
if (catalog == 0) {
|
this.showCatalogList = [0, 1];
|
} else if (catalog == this.catalogLength) {
|
this.showCatalogList = [
|
this.catalogLength - 2,
|
this.catalogLength - 1,
|
this.catalogLength,
|
];
|
} else {
|
this.showCatalogList = [catalog - 1, catalog, catalog + 1];
|
}
|
setTimeout(() => {
|
// 跳转页码
|
const pageDom = (
|
this.container ? this.container : document
|
).querySelector(`[page="${page}"]`);
|
if (pageDom) {
|
pageDom.scrollIntoView();
|
} else {
|
console.log("页码错误!");
|
}
|
}, 500);
|
} else {
|
console.log("章节错误!");
|
}
|
},
|
|
// 处理标记数据
|
handelSignData(type, data) {
|
if (this.loadPageList.indexOf(Number(data.page)) > -1) {
|
// 立即渲染
|
this.renderSign(type, data);
|
}
|
|
// 储存数据
|
if (!this.renderSignMap[type]) this.renderSignMap[type] = {};
|
if (!this.renderSignMap[type][data.page])
|
this.renderSignMap[type][data.page] = [];
|
this.renderSignMap[type][data.page].push(data);
|
},
|
|
// 渲染标记
|
renderSign(type, data) {
|
// 父层设置禁止渲染标记时不再进行渲染
|
if (this.$store.state.qiankun.disableSign) {
|
return false;
|
}
|
const existence = (
|
this.container ? this.container : document
|
).querySelector(`[dataid="${data.id}"]`);
|
// 去重
|
if (!existence) {
|
const pageDom = (
|
this.container ? this.container : document
|
).querySelector(`[page="${data.page}"]`);
|
// 创建 createTreeWalker 迭代器,用于遍历文本节点,保存到一个数组
|
const treeWalker = document.createTreeWalker(
|
pageDom,
|
NodeFilter.SHOW_TEXT
|
);
|
const allTextNodes = [];
|
let currentNode = treeWalker.nextNode();
|
while (currentNode) {
|
allTextNodes.push(currentNode);
|
currentNode = treeWalker.nextNode();
|
}
|
for (let i = 0; i < allTextNodes.length; i++) {
|
const textDom = allTextNodes[i];
|
if (textDom.textContent.indexOf(data.txt) > -1) {
|
let reg = new RegExp(`${data.txt}`, "ig");
|
switch (type) {
|
case "Highlight":
|
// 高亮
|
textDom.parentNode.innerHTML =
|
textDom.parentNode.innerHTML.replace(
|
reg,
|
`<span datatype="Highlight" dataid="${data.id}" style="background: ${data.color};" class="highLight" onclick="signClick('Highlight','${data.id}','${data.chapterNum}')">${data.txt}</span>`
|
);
|
break;
|
case "Dashing":
|
// 划线
|
textDom.parentNode.innerHTML =
|
textDom.parentNode.innerHTML.replace(
|
reg,
|
`<span datatype="Dashing" dataid="${data.id}" style="text-decoration-color:${data.color};" class="underline" onclick="signClick('Dashing','${data.id}','${data.chapterNum}')">${data.txt}</span>`
|
);
|
break;
|
case "Note":
|
// 笔记
|
textDom.parentNode.innerHTML =
|
textDom.parentNode.innerHTML.replace(
|
reg,
|
`<span datatype="Note" dataid="${data.id}" style="border-bottom-color:${data.color}" class="notesline" onclick="signClick('Note','${data.id}','${data.chapterNum}')" onmouseover="noteHover('Note','${data.id}','${data.chapterNum}')" onmouseout="noteOut('Note')">${data.txt}<img src="${NoteIcon}"/></span>`
|
);
|
break;
|
}
|
}
|
}
|
}
|
},
|
// 删除标记渲染
|
delSign({ ids, type }) {
|
if (ids && ids.length) {
|
for (let i = 0; i < ids.length; i++) {
|
const id = ids[i];
|
const dom = (
|
this.container ? this.container : document
|
).querySelector(`[dataid="${id}"]`);
|
dom.parentNode.innerHTML = dom.parentNode.innerHTML.replace(
|
dom.outerHTML,
|
dom.outerText
|
);
|
}
|
}
|
if (type) {
|
const doms = (
|
this.container ? this.container : document
|
).querySelectorAll(`[datatype="${type}"]`);
|
for (let i = 0; i < doms.length; i++) {
|
const dom = doms[i];
|
dom.parentNode.innerHTML = dom.parentNode.innerHTML.replace(
|
dom.outerHTML,
|
dom.outerText
|
);
|
}
|
}
|
},
|
initObservation() {
|
const sections = (
|
this.container ? this.container : document
|
).querySelectorAll(".page-box");
|
sections.forEach((section) => {
|
if (this.config.activeBook && this.config.activeBook.tryPageCount) {
|
const page = section.getAttribute("page");
|
if (Number(page) > this.config.activeBook.tryPageCount) {
|
let chapterDom = this.getParentWithClass(section, "chapter");
|
const chapterNum = chapterDom.getAttribute("num");
|
this.catalogLength = Number(chapterNum) - 1;
|
section.remove();
|
return false;
|
}
|
}
|
// observer 观察每个元素,以便在它们进入或离开视窗时触发回调函数。
|
const isObserver = section.getAttribute("observer");
|
const isLoadObserver = section.getAttribute("loadObserver");
|
if (!isObserver) {
|
this.observer.observe(section);
|
section.setAttribute("observer", "1");
|
}
|
if (!isLoadObserver) {
|
this.loadPageObserver.observe(section);
|
section.setAttribute("loadObserver", "1");
|
}
|
});
|
},
|
initThemeColor() {
|
// 获取各种需要主题色的节点
|
const colorDom = (
|
this.container ? this.container : document
|
).querySelectorAll(".theme-color");
|
const backgroundColorDom = (
|
this.container ? this.container : document
|
).querySelectorAll(".theme-back");
|
const borderColorDom = (
|
this.container ? this.container : document
|
).querySelectorAll(".theme-border");
|
// 获取配置的主题色
|
const bookThemeColor =
|
this.config.activeBook && this.config.activeBook.bookThemeColor
|
? this.config.activeBook.bookThemeColor
|
: null;
|
const chapterThemeColor =
|
this.config.activeBook && this.config.activeBook.chapterThemeColor
|
? this.config.activeBook.chapterThemeColor
|
: null;
|
const pageThemeColor =
|
this.config.activeBook && this.config.activeBook.pageThemeColor
|
? this.config.activeBook.pageThemeColor
|
: null;
|
colorDom.forEach((domItem) => {
|
// 获取章节、页码
|
let pageDom = this.getParentWithClass(domItem, "page-box");
|
let chapterDom = this.getParentWithClass(domItem, "chapter");
|
let page, chapterNum;
|
if (pageDom) page = pageDom.getAttribute("page");
|
if (chapterDom) chapterNum = chapterDom.getAttribute("num");
|
// 向上匹配主题色
|
const themeColor =
|
page && pageThemeColor && pageThemeColor[page]
|
? pageThemeColor[page]
|
: chapterNum && chapterThemeColor && chapterThemeColor[chapterNum]
|
? chapterThemeColor[chapterNum]
|
: bookThemeColor;
|
if (themeColor) {
|
domItem.style.color = themeColor;
|
}
|
});
|
backgroundColorDom.forEach((domItem) => {
|
// 获取章节、页码
|
let pageDom = this.getParentWithClass(domItem, "page-box");
|
let chapterDom = this.getParentWithClass(domItem, "chapter");
|
let page, chapterNum;
|
if (pageDom) page = pageDom.getAttribute("page");
|
if (chapterDom) chapterNum = chapterDom.getAttribute("num");
|
// 向上匹配主题色
|
const themeColor =
|
page && pageThemeColor && pageThemeColor[page]
|
? pageThemeColor[page]
|
: chapterNum && chapterThemeColor && chapterThemeColor[chapterNum]
|
? chapterThemeColor[chapterNum]
|
: bookThemeColor;
|
if (themeColor) {
|
domItem.style.backgroundColor = themeColor;
|
}
|
});
|
borderColorDom.forEach((domItem) => {
|
// 获取章节、页码
|
let pageDom = this.getParentWithClass(domItem, "page-box");
|
let chapterDom = this.getParentWithClass(domItem, "chapter");
|
let page, chapterNum;
|
if (pageDom) page = pageDom.getAttribute("page");
|
if (chapterDom) chapterNum = chapterDom.getAttribute("num");
|
// 向上匹配主题色
|
const themeColor =
|
page && pageThemeColor && pageThemeColor[page]
|
? pageThemeColor[page]
|
: chapterNum && chapterThemeColor && chapterThemeColor[chapterNum]
|
? 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)) {
|
return element;
|
}
|
}
|
},
|
pageChangeCallback(entries, observer) {
|
//entries:代表观察到的目标元素的集合。 observer:代表观察者对象。
|
entries.forEach((entry) => {
|
//entry.isIntersecting:检查当前目标元素是否与根元素相交。
|
if (entry.isIntersecting) {
|
const target = entry.target;
|
//entry.target:获取当前目标元素
|
const page = target.getAttribute("page");
|
const catalogDom = this.tool.getParentNodeByClassName(
|
target,
|
"chapter"
|
);
|
const catalog = catalogDom.getAttribute("num");
|
let text = null;
|
if (target.querySelector("p")) {
|
text = target.querySelector("p").textContent.substring(0, 50);
|
}
|
// 返回页码和章节信息
|
if (this.$store.state.qiankun && this.$store.state.qiankun.pageChange)
|
this.$store.state.qiankun.pageChange({
|
page: page,
|
catalog: catalog,
|
text,
|
});
|
// const sections = Array.from(document.querySelectorAll(".section"));
|
//sections:获取所有具有 .section 类名的元素,并转换为数组。
|
// let index = sections.findIndex((section) => section === target) + 1;
|
//index:查找当前目标元素在 sections 数组中的索引,并加 1,用于确定当前页码。
|
}
|
});
|
},
|
loadPageCallback(entries, observer) {
|
entries.forEach(async (entry) => {
|
if (entry.isIntersecting) {
|
const target = entry.target;
|
const page = target.getAttribute("page");
|
if (this.loadPageList.indexOf(Number(page)) == -1) {
|
const catalogDom = this.tool.getParentNodeByClassName(
|
target,
|
"chapter"
|
);
|
// 添加页码
|
this.loadPageList.push(Number(page));
|
const catalog = catalogDom.getAttribute("num");
|
// if (!this.questionDataMap[page]) {
|
// if (testData && testData[catalog]) {
|
// if (testData[catalog][page]) {
|
// if (Array.isArray(testData[catalog][page])) {
|
// this.questionDataMap[page] = await getQuestionList(
|
// page,
|
// testData[catalog][page],
|
// this.config.activeBook
|
// );
|
// } else {
|
// const obj = {};
|
// for (let key in testData[catalog][page]) {
|
// obj[key] = await getQuestionList(
|
// [],
|
// testData[catalog][page][key],
|
// this.config.activeBook
|
// );
|
// }
|
// this.questionDataMap[page] = obj;
|
// }
|
// console.log("题目", this.questionDataMap);
|
// }
|
// }
|
// }
|
// 渲染这一页的标记
|
for (const key in this.renderSignMap) {
|
if (this.renderSignMap[key][page]) {
|
this.renderSignMap[key][page].forEach((item) => {
|
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页
|
this.loadPageList.shift();
|
}
|
}
|
}
|
});
|
},
|
initSwiper() {
|
const doms = (
|
this.container ? this.container : document
|
).querySelectorAll(".swiper-img");
|
for (let i = 0; i < doms.length; i++) {
|
const dom = doms[i];
|
new Swiper(dom, {
|
loop: false, // 无缝
|
autoplay: {
|
//自动开始
|
delay: 3000, //时间间隔
|
disableOnInteraction: false, //*手动操作轮播图后不会暂停*
|
},
|
paginationClickable: true,
|
slidesPerView: 1, // 一组三个
|
spaceBetween: 30, // 间隔
|
// 如果需要前进后退按钮
|
navigation: {
|
nextEl: dom.querySelector(".swiper-button-next"),
|
prevEl: dom.querySelector(".swiper-button-prev"),
|
},
|
// 窗口变化,重新init,针对F11全屏和放大缩小,必须加
|
// observer: true,
|
// observeParents: true
|
// // 如果需要分页器
|
// pagination: {
|
// el: (this.container ? this.container : document).querySelector(
|
// ".swiper-pagination"
|
// ),
|
// clickable: true // 分页器可以点击
|
// }
|
});
|
}
|
const pptDoms = (
|
this.container ? this.container : document
|
).querySelectorAll(".swiper_ppt");
|
for (let i = 0; i < pptDoms.length; i++) {
|
const dom = pptDoms[i];
|
new Swiper(dom, {
|
loop: false, // 无缝
|
autoplay: {
|
delay: 3000, // 设置自动播放的间隔时间为3000毫秒(3秒)
|
disableOnInteraction: false, // 用户操作后不禁用自动播放
|
},
|
paginationClickable: true,
|
slidesPerView: 1, // 一组三个
|
spaceBetween: 30, // 间隔
|
// 如果需要前进后退按钮
|
navigation: {
|
nextEl: dom.querySelector(".swiper-button-next"),
|
prevEl: dom.querySelector(".swiper-button-prev"),
|
},
|
// 窗口变化,重新init,针对F11全屏和放大缩小,必须加
|
observer: true,
|
observeParents: true,
|
on: {
|
init: (value) => {
|
let currentPage = value.activeIndex + 1; // 获取当前页(从1开始计数)
|
let totalPages = value.slides.length; // 获取总页数
|
var paginationInfoEl = dom.querySelector(".pageBox");
|
if (paginationInfoEl)
|
paginationInfoEl.textContent = currentPage + "/" + totalPages;
|
},
|
slideChange: (value) => {
|
let currentPage = value.activeIndex + 1; // 获取当前页(从1开始计数)
|
let totalPages = value.slides.length; // 获取总页数
|
var paginationInfoEl = dom.querySelector(".pageBox");
|
if (paginationInfoEl)
|
paginationInfoEl.textContent = currentPage + "/" + totalPages;
|
},
|
},
|
});
|
}
|
},
|
initViewer() {
|
const doms = (
|
this.container ? this.container : document
|
).querySelectorAll(".openImgBox");
|
for (let i = 0; i < doms.length; i++) {
|
const dom = doms[i];
|
new Viewer(dom, {
|
container: this.container
|
? this.container.querySelector("#app")
|
: "body",
|
navbar: true, // 显示导航栏
|
toolbar: true, // 显示工具栏
|
title: true, // 显示标题
|
});
|
}
|
},
|
// 根据关键字全文检索
|
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();
|
});
|
}
|
},
|
// 点击视频关闭其他
|
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) {
|
playVudio.requestPictureInPicture();
|
}
|
}
|
}
|
},
|
components: {
|
pageHeader,
|
// chapterOne,
|
// chapterTwo,
|
// chapterThree,
|
// chapterFour,
|
// chapterFive,
|
// chapterSix,
|
// miniAudio,
|
},
|
};
|
</script>
|
|
<style lang="less" scoped>
|
.page-main {
|
width: 100%;
|
height: 100%;
|
overflow: auto;
|
|
.page-content {
|
max-width: 816px;
|
min-width: 375px;
|
margin: 0 auto;
|
padding-bottom: 100px;
|
}
|
}
|
</style>
|
|