src/books/aviationBasicSkills/view/components/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/books/aviationEtiquette/view/components/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/books/aviationSafety/view/components/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/books/civilAviation/view/components/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/books/civilServices/view/components/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/books/aviationBasicSkills/view/components/index.vue
@@ -1,171 +1,205 @@ <template> <div class="page-main" @scroll="throttledScrollHandler"> <div id="searchDomBox" style="display: none"> <div id="searchContent"></div> </div> <div class="page-content" :style="{ <div class="page-main" @scroll="throttledScrollHandler"> <div id="searchDomBox" style="display: none"> <div id="searchContent"></div> </div> <div class="page-content" :style="{ fontSize: fontSize + 'px', transform: `scale(${pageZoom})`, 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> <chapterSeven v-if="showCatalogList.indexOf(8) > -1" :showPageList="loadPageList"></chapterSeven> <chapterEight v-if="showCatalogList.indexOf(9) > -1" :showPageList="loadPageList"></chapterEight> </div> }" > <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> <chapterSeven v-if="showCatalogList.indexOf(8) > -1" :showPageList="loadPageList" ></chapterSeven> <chapterEight v-if="showCatalogList.indexOf(9) > -1" :showPageList="loadPageList" ></chapterEight> </div> </template> <script> 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 chapterSeven from "./chapter007.vue"; import chapterEight from "./chapter008.vue"; import NoteIcon from "@/assets/images/biji.png"; 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: 9, // 总章节数 showCatalogList: [], // 显示的章节 loadThreshold: 300, // 触发加载阈值 throttleThreshold: 100, // 节流阈值 previousScrollTop: 0, throttledScrollHandler: null, observer: null, loadPageObserver: null, loadPageList: [], questionDataMap: {}, renderSignMap: {}, highlightData: null, audioPath: "", currentTime: null, videoList: [], }; </div> </template> <script> 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 chapterSeven from "./chapter007.vue"; import chapterEight from "./chapter008.vue"; import NoteIcon from "@/assets/images/biji.png"; 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: 9, // 总章节数 showCatalogList: [], // 显示的章节 loadThreshold: 300, // 触发加载阈值 throttleThreshold: 100, // 节流阈值 previousScrollTop: 0, throttledScrollHandler: null, observer: null, loadPageObserver: null, loadPageList: [], questionDataMap: {}, renderSignMap: {}, highlightData: null, audioPath: "", currentTime: null, videoList: [], }; }, computed: { fontSize() { this.transformDom(this.$store.state.qiankun.fontSize); return this.$store.state.qiankun.fontSize ? this.$store.state.qiankun.fontSize : 18; }, computed: { fontSize() { this.transformDom(this.$store.state.qiankun.fontSize) return this.$store.state.qiankun.fontSize ? this.$store.state.qiankun.fontSize : 18; }, pageZoom() { return this.$store.state.qiankun.scale ? this.$store.state.qiankun.scale / 100 : 1; pageZoom() { return this.$store.state.qiankun.scale ? this.$store.state.qiankun.scale / 100 : 1; }, }, 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); }, }, 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.transformDom(this.$store.state.qiankun.fontSize) 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; }, loadPageList: { handler(newVal, oldVal) { setTimeout(() => { this.transformDom(this.$store.state.qiankun.fontSize); this.initSwiper(); this.initViewer(); this.closeAudio(); this.closeVideo(); }, 200); }, }, 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,表示当目标元素一半或更多显示在视窗中时触发回调函数。 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); }, }); 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,1); } // 创建一个新的 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, 1); // setTimeout(() => { // this.renderSign("Highlight", { // id: "2ACA9359", @@ -179,611 +213,416 @@ // 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; }, 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 ) { console.log(1); // 到达阈值 if ( currentScrollTop >= event.target.scrollHeight - this.loadThreshold this.showCatalogList[this.showCatalogList.length - 1] < this.catalogLength ) { console.log(1); // 到达阈值 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(); } // 加载下一章 this.showCatalogList.push( this.showCatalogList[this.showCatalogList.length - 1] + 1 ); if (this.showCatalogList.length > 3) { // 超过三章隐藏顶部一章 this.showCatalogList.shift(); } } } // 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("页码错误!"); } 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(); } }, 500); } } } // 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 { console.log("章节错误!"); this.showCatalogList = [catalog - 1, catalog, catalog + 1]; } }, // 处理标记数据 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) { setTimeout(() => { // 跳转页码 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(); ).querySelector(`[page="${page}"]`); if (pageDom) { pageDom.scrollIntoView(); } else { console.log("页码错误!"); } 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; } }, 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 = ( } }, // 删除标记渲染 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 ).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 ); ).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; } } }, 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) { while (element.parentElement) { element = element.parentElement; if (element.classList.contains(className)) { return element; } // observer 观察每个元素,以便在它们进入或离开视窗时触发回调函数。 const isObserver = section.getAttribute("observer"); const isLoadObserver = section.getAttribute("loadObserver"); if (!isObserver) { this.observer.observe(section); section.setAttribute("observer", "1"); } }, pageChangeCallback(entries, observer) { //entries:代表观察到的目标元素的集合。 observer:代表观察者对象。 entries.forEach((entry) => { //entry.isIntersecting:检查当前目标元素是否与根元素相交。 if (entry.isIntersecting) { const target = entry.target; //entry.target:获取当前目标元素 const page = target.getAttribute("page"); 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) { 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"); 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 // 分页器可以点击 // 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); // } // } // } }); } 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: 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, chapterSeven, chapterEight }; // 遍历所有章节文件 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) { // 渲染这一页的标记 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( thisPageDom, target, NodeFilter.SHOW_TEXT ); // 匹配关键字 @@ -795,181 +634,377 @@ } for (let i = 0; i < allPageTextNodes.length; i++) { const textDom = allPageTextNodes[i]; let txtIndex = textDom.textContent.indexOf(keyword); let txtIndex = textDom.textContent.indexOf( this.highlightData.txt ); if (txtIndex > -1) { // 记录关键字所在页码、章节以及匹配到的段落 searchResult.push({ page: pageNum, catalog: catalogIndex, txt: textDom.textContent, txtIndex: txtIndex, }); textDom.parentNode.style.transition = "background-color 0.8s"; textDom.parentNode.scrollIntoView(); textDom.parentNode.style.backgroundColor = "#79bbf0"; setTimeout(() => { textDom.parentNode.style.backgroundColor = ""; }, 1000); } } // 结束,卸载销毁 pageExample.$destroy(); (this.container ? this.container : document).querySelector( "#searchDomBox" ).innerHTML = '<div id="searchContent"></div>'; } }, 100); } if (this.loadPageList.length > 5) { // 超过5页 this.loadPageList.shift(); } } } // 输出搜索结果 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; }); }, 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: 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, chapterSeven, chapterEight, }; // 遍历所有章节文件 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>'; } } } }, // 页面向上滚动,音频小窗回收 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 } // 输出搜索结果 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 (playVudio) { const bottomGap = playVudio.getBoundingClientRect().bottom; const topGap = playVudio.getBoundingClientRect().top; if (bottomGap < 0 || topGap > window.innerHeight) { playVudio.requestPictureInPicture(); if (playAudio) { const bottomGap = playAudio.getBoundingClientRect().bottom; if (bottomGap < 0) { playAudio.pause(); this.audioPath = playAudio.src; this.currentTime = playAudio.currentTime; } } }, //其他类名下字体大小变化 transformDom(fs) { if(!fs) return let doms = ( this.container ? this.container : document ).querySelectorAll('.block') if(!doms.length) return for (let index = 0; index < doms.length; index++) { const dom = doms[index]; dom.style.fontSize = fs + 'px' } } }, components: { pageHeader, chapterOne, chapterTwo, chapterThree, chapterFour, chapterFive, chapterSix, chapterSeven, chapterEight // 页面向上滚动,音频小窗回收 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(); } } } } }, }; </script> <style lang="less" scoped> .page-main { width: 100% !important; height: 100% !important; overflow: auto; .page-content { max-width: 816px; min-width: 375px; margin: 0 auto; padding-bottom: 100px; } // 关闭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) { try { if (playVudio.readyState) playVudio.requestPictureInPicture(); } catch (error) { console.log(error, "小窗错误error"); } } } }, //其他类名下字体大小变化 transformDom(fs) { if (!fs) return; let doms = (this.container ? this.container : document).querySelectorAll( ".block" ); if (!doms.length) return; for (let index = 0; index < doms.length; index++) { const dom = doms[index]; dom.style.fontSize = fs + "px"; } }, }, components: { pageHeader, chapterOne, chapterTwo, chapterThree, chapterFour, chapterFive, chapterSix, chapterSeven, chapterEight, }, }; </script> <style lang="less" scoped> .page-main { width: 100% !important; height: 100% !important; overflow: auto; .page-content { max-width: 816px; min-width: 375px; margin: 0 auto; padding-bottom: 100px; } </style> } </style> src/books/aviationEtiquette/view/components/index.vue
@@ -917,7 +917,11 @@ const bottomGap = playVudio.getBoundingClientRect().bottom; const topGap = playVudio.getBoundingClientRect().top; if (bottomGap < 0 || topGap > window.innerHeight) { playVudio.requestPictureInPicture(); try { if(playVudio.readyState) playVudio.requestPictureInPicture(); } catch (error) { console.log(error,"小窗错误error"); } } } }, src/books/aviationSafety/view/components/index.vue
@@ -927,7 +927,11 @@ const bottomGap = playVudio.getBoundingClientRect().bottom; const topGap = playVudio.getBoundingClientRect().top; if (bottomGap < 0 || topGap > window.innerHeight) { playVudio.requestPictureInPicture(); try { if(playVudio.readyState) playVudio.requestPictureInPicture(); } catch (error) { console.log(error,"小窗错误error"); } } } }, src/books/civilAviation/view/components/index.vue
@@ -3,17 +3,38 @@ <div id="searchDomBox" style="display: none"> <div id="searchContent"></div> </div> <div class="page-content" :style="{ fontSize: fontSize + 'px', transform: `scale(${pageZoom})`, 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> <div class="page-content" :style="{ fontSize: fontSize + 'px', transform: `scale(${pageZoom})`, 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> </div> </div> </template> @@ -24,7 +45,7 @@ import chapterTwo from "./chapter002.vue"; import chapterThree from "./chapter003.vue"; import chapterfour from "./chapter004.vue"; import chapterfive from "./chapter005.vue" import chapterfive from "./chapter005.vue"; import NoteIcon from "@/assets/images/biji.png"; import _ from "lodash"; import Swiper from "swiper/bundle"; @@ -54,11 +75,15 @@ }, computed: { fontSize() { this.transformDom(this.$store.state.qiankun.fontSize) return this.$store.state.qiankun.fontSize ? this.$store.state.qiankun.fontSize : 18; this.transformDom(this.$store.state.qiankun.fontSize); return this.$store.state.qiankun.fontSize ? this.$store.state.qiankun.fontSize : 18; }, pageZoom() { return this.$store.state.qiankun.scale ? this.$store.state.qiankun.scale / 100 : 1; return this.$store.state.qiankun.scale ? this.$store.state.qiankun.scale / 100 : 1; }, }, watch: { @@ -83,7 +108,7 @@ loadPageList: { handler(newVal, oldVal) { setTimeout(() => { this.transformDom(this.$store.state.qiankun.fontSize) this.transformDom(this.$store.state.qiankun.fontSize); this.initSwiper(); this.initViewer(); this.closeAudio(); @@ -159,20 +184,20 @@ // 测试页面跳转 setTimeout(() => { this.gotoPage(3,52); // setTimeout(() => { // this.renderSign("Highlight", { // id: "2ACA9359", // txt: "题一学习主题一 运动", // page: "10", // type: "Highlight", // color: "#F5E12A" // }); // setTimeout(() => { // this.delSign({ // ids: ["2ACA9359"] // }); // }, 2000); this.gotoPage(3, 52); // 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) @@ -206,7 +231,7 @@ // }, // 滚动监听 scrollFun(event) { this.handleVideoPicture() this.handleVideoPicture(); // 判断向上滚动还是向下滚动 if (event.target.scrollTop > this.previousScrollTop) { this.getAduio(); @@ -449,8 +474,8 @@ page && pageThemeColor && pageThemeColor[page] ? pageThemeColor[page] : chapterNum && chapterThemeColor && chapterThemeColor[chapterNum] ? chapterThemeColor[chapterNum] : bookThemeColor; ? chapterThemeColor[chapterNum] : bookThemeColor; if (themeColor) { domItem.style.color = themeColor; } @@ -467,8 +492,8 @@ page && pageThemeColor && pageThemeColor[page] ? pageThemeColor[page] : chapterNum && chapterThemeColor && chapterThemeColor[chapterNum] ? chapterThemeColor[chapterNum] : bookThemeColor; ? chapterThemeColor[chapterNum] : bookThemeColor; if (themeColor) { domItem.style.backgroundColor = themeColor; } @@ -485,8 +510,8 @@ page && pageThemeColor && pageThemeColor[page] ? pageThemeColor[page] : chapterNum && chapterThemeColor && chapterThemeColor[chapterNum] ? chapterThemeColor[chapterNum] : bookThemeColor; ? chapterThemeColor[chapterNum] : bookThemeColor; if (themeColor) { domItem.style.borderColor = themeColor; } @@ -815,7 +840,7 @@ // 记录高亮信息 this.highlightData = data; // 跳转 this.gotoPage(data.catalog, data.page, () => { }); this.gotoPage(data.catalog, data.page, () => {}); }, // 页面向下滚动,音频小窗播放功能 getAduio() { @@ -906,34 +931,36 @@ }, // 视频小窗 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); 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(); try { if (playVudio.readyState) playVudio.requestPictureInPicture(); } catch (error) { console.log(error, "小窗错误error"); } } } }, //其他类名下字体大小变化 transformDom(fs) { if(!fs) return let doms = ( this.container ? this.container : document ).querySelectorAll('.block') if(!doms.length) return if (!fs) return; let doms = (this.container ? this.container : document).querySelectorAll( ".block" ); if (!doms.length) return; for (let index = 0; index < doms.length; index++) { const dom = doms[index]; dom.style.fontSize = fs + 'px' dom.style.fontSize = fs + "px"; } } }, }, components: { pageHeader, src/books/civilServices/view/components/index.vue
@@ -1,777 +1,608 @@ <template> <div class="page-main" @scroll="throttledScrollHandler"> <div id="searchDomBox" style="display: none"> <div id="searchContent"></div> </div> <div class="page-content" :style="{ <div class="page-main" @scroll="throttledScrollHandler"> <div id="searchDomBox" style="display: none"> <div id="searchContent"></div> </div> <div class="page-content" :style="{ fontSize: fontSize + 'px', transform: `scale(${pageZoom})`, 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> </div> }" > <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> </div> </template> <script> import pageHeader from "./header.vue"; import chapterOne from "./chapter001.vue"; import chapterTwo from "./chpater002.vue"; import chapterThree from "./chapter003.vue"; import chapterFour from "./chapter004.vue"; import NoteIcon from "@/assets/images/biji.png"; 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: 5, // 总章节数 showCatalogList: [], // 显示的章节 loadThreshold: 300, // 触发加载阈值 throttleThreshold: 100, // 节流阈值 previousScrollTop: 0, throttledScrollHandler: null, observer: null, loadPageObserver: null, loadPageList: [], questionDataMap: {}, renderSignMap: {}, highlightData: null, audioPath: "", currentTime: null, videoList: [], }; </div> </template> <script> import pageHeader from "./header.vue"; import chapterOne from "./chapter001.vue"; import chapterTwo from "./chpater002.vue"; import chapterThree from "./chapter003.vue"; import chapterFour from "./chapter004.vue"; import NoteIcon from "@/assets/images/biji.png"; 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: 5, // 总章节数 showCatalogList: [], // 显示的章节 loadThreshold: 300, // 触发加载阈值 throttleThreshold: 100, // 节流阈值 previousScrollTop: 0, throttledScrollHandler: null, observer: null, loadPageObserver: null, loadPageList: [], questionDataMap: {}, renderSignMap: {}, highlightData: null, audioPath: "", currentTime: null, videoList: [], }; }, computed: { fontSize() { this.transformDom(this.$store.state.qiankun.fontSize); return this.$store.state.qiankun.fontSize ? this.$store.state.qiankun.fontSize : 18; }, computed: { fontSize() { this.transformDom(this.$store.state.qiankun.fontSize) return this.$store.state.qiankun.fontSize ? this.$store.state.qiankun.fontSize : 18; }, pageZoom() { return this.$store.state.qiankun.scale ? this.$store.state.qiankun.scale / 100 : 1; pageZoom() { return this.$store.state.qiankun.scale ? this.$store.state.qiankun.scale / 100 : 1; }, }, 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); }, }, 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.transformDom(this.$store.state.qiankun.fontSize) 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; }, loadPageList: { handler(newVal, oldVal) { setTimeout(() => { this.transformDom(this.$store.state.qiankun.fontSize); this.initSwiper(); this.initViewer(); this.closeAudio(); this.closeVideo(); }, 200); }, }, 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(3, 92); // 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 ) { console.log(1); // 到达阈值 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 = ( pageZoom: { handler(newVal, oldVal) { const scrollBox = ( this.container ? this.container : document ).querySelector(`[dataid="${data.id}"]`); // 去重 if (!existence) { ).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(3, 92); // 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 ) { console.log(1); // 到达阈值 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="${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(); ).querySelector(`[page="${page}"]`); if (pageDom) { pageDom.scrollIntoView(); } else { console.log("页码错误!"); } 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; } }, 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 = ( } }, // 删除标记渲染 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 ).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 ); ).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; } } }, 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) { while (element.parentElement) { element = element.parentElement; if (element.classList.contains(className)) { return element; } // observer 观察每个元素,以便在它们进入或离开视窗时触发回调函数。 const isObserver = section.getAttribute("observer"); const isLoadObserver = section.getAttribute("loadObserver"); if (!isObserver) { this.observer.observe(section); section.setAttribute("observer", "1"); } }, pageChangeCallback(entries, observer) { //entries:代表观察到的目标元素的集合。 observer:代表观察者对象。 entries.forEach((entry) => { //entry.isIntersecting:检查当前目标元素是否与根元素相交。 if (entry.isIntersecting) { const target = entry.target; //entry.target:获取当前目标元素 const page = target.getAttribute("page"); 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) { 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"); 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 // 分页器可以点击 // 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); // } // } // } }); } 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: 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, }; // 遍历所有章节文件 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) { // 渲染这一页的标记 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( thisPageDom, target, NodeFilter.SHOW_TEXT ); // 匹配关键字 @@ -783,177 +614,369 @@ } for (let i = 0; i < allPageTextNodes.length; i++) { const textDom = allPageTextNodes[i]; let txtIndex = textDom.textContent.indexOf(keyword); let txtIndex = textDom.textContent.indexOf( this.highlightData.txt ); if (txtIndex > -1) { // 记录关键字所在页码、章节以及匹配到的段落 searchResult.push({ page: pageNum, catalog: catalogIndex, txt: textDom.textContent, txtIndex: txtIndex, }); textDom.parentNode.style.transition = "background-color 0.8s"; textDom.parentNode.scrollIntoView(); textDom.parentNode.style.backgroundColor = "#79bbf0"; setTimeout(() => { textDom.parentNode.style.backgroundColor = ""; }, 1000); } } // 结束,卸载销毁 pageExample.$destroy(); (this.container ? this.container : document).querySelector( "#searchDomBox" ).innerHTML = '<div id="searchContent"></div>'; } }, 100); } if (this.loadPageList.length > 5) { // 超过5页 this.loadPageList.shift(); } } } // 输出搜索结果 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; }); }, 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: 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, }; // 遍历所有章节文件 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>'; } } } }, // 页面向上滚动,音频小窗回收 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 } // 输出搜索结果 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 (playVudio) { const bottomGap = playVudio.getBoundingClientRect().bottom; const topGap = playVudio.getBoundingClientRect().top; if (bottomGap < 0 || topGap > window.innerHeight) { if(playVudio.readyState) playVudio.requestPictureInPicture(); if (playAudio) { const bottomGap = playAudio.getBoundingClientRect().bottom; if (bottomGap < 0) { playAudio.pause(); this.audioPath = playAudio.src; this.currentTime = playAudio.currentTime; } } }, //其他类名下字体大小变化 transformDom(fs) { if(!fs) return let doms = ( this.container ? this.container : document ).querySelectorAll('.block') if(!doms.length) return for (let index = 0; index < doms.length; index++) { const dom = doms[index]; dom.style.fontSize = fs + 'px' } } }, components: { pageHeader, chapterOne, chapterTwo, chapterThree, chapterFour, // 页面向上滚动,音频小窗回收 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(); } } } } }, }; </script> <style lang="less" scoped> .page-main { width: 100% !important; height: 100% !important; overflow: auto; .page-content { max-width: 816px; min-width: 375px; margin: 0 auto; padding-bottom: 100px; } // 关闭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) { try { if (playVudio.readyState) playVudio.requestPictureInPicture(); } catch (error) { console.log(error, "小窗错误error"); } } } }, //其他类名下字体大小变化 transformDom(fs) { if (!fs) return; let doms = (this.container ? this.container : document).querySelectorAll( ".block" ); if (!doms.length) return; for (let index = 0; index < doms.length; index++) { const dom = doms[index]; dom.style.fontSize = fs + "px"; } }, }, components: { pageHeader, chapterOne, chapterTwo, chapterThree, chapterFour, }, }; </script> <style lang="less" scoped> .page-main { width: 100% !important; height: 100% !important; overflow: auto; .page-content { max-width: 816px; min-width: 375px; margin: 0 auto; padding-bottom: 100px; } </style> } </style>