|
@@ -0,0 +1,810 @@
|
|
|
+<template>
|
|
|
+ <div style="width: 100%; height: 100%; position: relative; overflow: hidden">
|
|
|
+ <iframe
|
|
|
+ :src="src"
|
|
|
+ width="100%"
|
|
|
+ scrolling="no"
|
|
|
+ height="100%"
|
|
|
+ style="position: relative; border: medium none;"
|
|
|
+ ref="iframeRef"
|
|
|
+ @load="onLoad"
|
|
|
+ >
|
|
|
+ </iframe>
|
|
|
+ <div id="pdf"></div>
|
|
|
+ <div class="close-icon" @click="emits('close')" v-if="false">
|
|
|
+ <CloseOutlined />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script setup>
|
|
|
+import { CloseOutlined } from '@ant-design/icons-vue';
|
|
|
+import { message } from 'ant-design-vue';
|
|
|
+import { nextTick, onMounted } from 'vue';
|
|
|
+import * as PDFJS from 'pdfjs-dist/build/pdf.js'
|
|
|
+import PdfjsWorker from 'pdfjs-dist/build/pdf.worker.js?worker';
|
|
|
+console.log("PdfjsWorker---------------------")
|
|
|
+console.log(PDFJS)
|
|
|
+
|
|
|
+PDFJS.GlobalWorkerOptions.workerSrc = PdfjsWorker
|
|
|
+const iframeRef = ref(null);
|
|
|
+const emits = defineEmits(['close', 'outline', 'search', 'load']);
|
|
|
+const searchList = ref([]);
|
|
|
+const pageContent = ref([]);
|
|
|
+const pageText = ref([]);
|
|
|
+const props = defineProps({
|
|
|
+ src: String
|
|
|
+});
|
|
|
+const type = ref('pdf');
|
|
|
+watch(
|
|
|
+ () => props.src,
|
|
|
+ (v) => {
|
|
|
+ if (v.endsWith('.pdf')) {
|
|
|
+ type.value = 'pdf';
|
|
|
+ } else if (v.endsWith('.txt')) {
|
|
|
+ type.value = 'txt';
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+);
|
|
|
+
|
|
|
+const onLoad = () => {
|
|
|
+ const doc = iframeRef.value.contentDocument || iframeRef.value.contentWindow.document;
|
|
|
+ //待定
|
|
|
+ // var toolbar = doc.getElementsByClassName('toolbar');
|
|
|
+ // if (toolbar && toolbar.length > 0) {
|
|
|
+ // toolbar[0].style.display = 'none';
|
|
|
+ // }
|
|
|
+ setTimeout(() => {
|
|
|
+ const script = doc.createElement('script');
|
|
|
+ script.textContent = `
|
|
|
+ window.addEventListener('message', (event) => {
|
|
|
+ const message = event.data;
|
|
|
+ if (message.action === 'locateElement') {
|
|
|
+ const element= document.getElementById('hightlight-'+message.index);
|
|
|
+ debugger;
|
|
|
+ if(element!=null&&element.offsetParent){
|
|
|
+ setTimeout(() => {
|
|
|
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
|
+ }, 10);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (message.action === 'mlLocateElement') {
|
|
|
+
|
|
|
+ const elements= document.getElementsByClassName(message.className);
|
|
|
+ if(elements!=null){
|
|
|
+ setTimeout(() => {
|
|
|
+ elements[message.index].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
|
+ }, 50);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });`;
|
|
|
+ doc.body.appendChild(script);
|
|
|
+ const style = doc.createElement('style');
|
|
|
+ style.textContent = `
|
|
|
+ #outerContainer{
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+ #page-container{
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+ #viewerContainer{
|
|
|
+ overflow-y: scroll;
|
|
|
+ }
|
|
|
+ .canvasWrapper {
|
|
|
+ position: relative;
|
|
|
+ /* display: flex;
|
|
|
+ justify-content: center; */
|
|
|
+}
|
|
|
+
|
|
|
+.textLayer {
|
|
|
+ text-align: initial;
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ opacity: 1;
|
|
|
+ line-height: 1;
|
|
|
+ text-size-adjust: none;
|
|
|
+ forced-color-adjust: none;
|
|
|
+}
|
|
|
+.textLayer span,
|
|
|
+.textLayer br {
|
|
|
+ color: transparent;
|
|
|
+ position: absolute;
|
|
|
+ white-space: pre;
|
|
|
+ cursor: text;
|
|
|
+ transform-origin: 0% 0%;
|
|
|
+}
|
|
|
+.highlight-layer {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ pointer-events: none;
|
|
|
+ opacity: 0.15;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.flash {
|
|
|
+ animation: flashing 0.6s;
|
|
|
+ animation-iteration-count: 2;
|
|
|
+}
|
|
|
+@keyframes flashing {
|
|
|
+ 0% {
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ opacity: 0.15;
|
|
|
+ }
|
|
|
+}
|
|
|
+.highlight {
|
|
|
+ background-color: #0076fa;
|
|
|
+ position: absolute;
|
|
|
+}
|
|
|
+ `;
|
|
|
+ doc.head.appendChild(style);
|
|
|
+ }, 400);
|
|
|
+ setTimeout(() => {
|
|
|
+ var el = doc.getElementsByClassName('page');
|
|
|
+ // var singleHeight = 1263;
|
|
|
+ var singleHeight = 1000;
|
|
|
+ if (iframeRef.value.height != singleHeight * el.length) {
|
|
|
+ // iframeRef.value.height = singleHeight * el.length;
|
|
|
+ }
|
|
|
+ setTimeout(() => {
|
|
|
+ pageText.value = [];
|
|
|
+ pageContent.value = [];
|
|
|
+ //获取每页数据
|
|
|
+ var textDivs = doc.getElementsByClassName('textLayer');
|
|
|
+ if (textDivs) {
|
|
|
+ for (var i = 0; i < textDivs.length; i++) {
|
|
|
+ var item = { page: i + 1, txt: textDivs[i].innerText };
|
|
|
+ var items = [];
|
|
|
+ for (var j = 0; j < textDivs[i].children.length; j++) {
|
|
|
+ var citem = {
|
|
|
+ str: textDivs[i].children[j].innerText,
|
|
|
+ width: textDivs[i].children[j].getBoundingClientRect().width,
|
|
|
+ top: textDivs[i].children[j].getBoundingClientRect().top,
|
|
|
+ height: textDivs[i].children[j].getBoundingClientRect().height
|
|
|
+ };
|
|
|
+ items.push(citem);
|
|
|
+ }
|
|
|
+ pageContent.value.push({ page: i + 1, items: items });
|
|
|
+ pageText.value.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 1000);
|
|
|
+ }, 1500);
|
|
|
+};
|
|
|
+
|
|
|
+const loadAndDisplayPdfByBlobUrl = async (blobUrl, container) => {
|
|
|
+ // 加载PDF文件
|
|
|
+ PDFJS.getDocument(blobUrl).promise.then(async (pdf) => {
|
|
|
+ const totalPages = pdf.numPages
|
|
|
+
|
|
|
+ // 循环绘制每个页面
|
|
|
+ for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
|
|
|
+ let page = await pdf.getPage(pageNum)
|
|
|
+ const viewport = page.getViewport({ scale: 1 })
|
|
|
+ const canvas = createCanvasDom(viewport.width, viewport.height)
|
|
|
+ container.appendChild(canvas)
|
|
|
+ const context = canvas.getContext('2d')
|
|
|
+
|
|
|
+ // 渲染页面到canvas
|
|
|
+ await page.render({
|
|
|
+ canvasContext: context,
|
|
|
+ viewport: viewport,
|
|
|
+ })
|
|
|
+ await sleep(100)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const createCanvasDom = (width, height) => {
|
|
|
+ const canvas = document.createElement('canvas')
|
|
|
+ canvas.width = width
|
|
|
+ canvas.height = height
|
|
|
+ return canvas
|
|
|
+}
|
|
|
+
|
|
|
+const sleep = (time) => {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ resolve(time)
|
|
|
+ }, time)
|
|
|
+ })
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ loadAndDisplayPdfByBlobUrl(props.src, document.querySelector('#pdf'))
|
|
|
+})
|
|
|
+const src = computed(() => {
|
|
|
+ // return `/lib/pdfjs/web/viewer.html?file=${props.src}&t=` + new Date().getTime();
|
|
|
+ return `/lib/pdfjs/web/viewer.html?file=${props.src}`;
|
|
|
+ // return `/aisearch/lib/pdfjs/web/viewer.html?file=${props.src}&t=` + new Date().getTime();
|
|
|
+
|
|
|
+ // return `/lib/pdfjs/web/viewer.html?file=http://121.40.148.47:8530/doc/knowledge_base/download_doc/国土资源部 国家发展和改革委员会+财政部+住房和城乡建设部农业部+中国人民银行+国家林业局+中国银行业监督管理委员会关于扩大国有土地有偿使用范围的意见%28279-283%29.pdf`
|
|
|
+});
|
|
|
+const scrollTo = (item) => {
|
|
|
+ nextTick(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ if (item.top) {
|
|
|
+ locateElement(item.cs, item.index, 1);
|
|
|
+ } else {
|
|
|
+ //搜索
|
|
|
+ locateElement(item.cs, item.index);
|
|
|
+ //改变选中颜色 其他黄色
|
|
|
+ searchList.value.forEach((citem, cindex) => {
|
|
|
+ const iframeDocument =
|
|
|
+ iframeRef.value.contentDocument || iframeRef.value.contentWindow.document;
|
|
|
+ const highlightedSpan = iframeDocument.getElementById('hightlight-' + cindex);
|
|
|
+ if (cindex == item.index) {
|
|
|
+ highlightedSpan.parentNode.innerHTML = highlightedSpan.parentNode.innerHTML.replace(
|
|
|
+ 'lightblue',
|
|
|
+ 'yellow'
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ highlightedSpan.parentNode.innerHTML = highlightedSpan.parentNode.innerHTML.replace(
|
|
|
+ 'yellow',
|
|
|
+ 'lightblue'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }, 10);
|
|
|
+ });
|
|
|
+};
|
|
|
+const locateElement = (className, index, type) => {
|
|
|
+ const message = {
|
|
|
+ action: type ? 'mlLocateElement' : 'locateElement',
|
|
|
+ className: className,
|
|
|
+ index: index
|
|
|
+ };
|
|
|
+ iframeRef.value.contentWindow.postMessage(message, '*');
|
|
|
+};
|
|
|
+const gotoElement = (element) => {
|
|
|
+ nextTick(() => {
|
|
|
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
+ });
|
|
|
+};
|
|
|
+const highlightText = (str) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ removeHighlight();
|
|
|
+ heightSingle(str);
|
|
|
+ }, 100);
|
|
|
+};
|
|
|
+const goLocation = (txt, flag) => {
|
|
|
+ removeHighlight();
|
|
|
+ searchList.value = [];
|
|
|
+ heightSingle(txt, flag);
|
|
|
+};
|
|
|
+//ai 问答
|
|
|
+const goSourceLocation = (content, type) => {
|
|
|
+ //获取页面
|
|
|
+ removeSourceHightLight();
|
|
|
+ const doc = iframeRef.value.contentDocument || iframeRef.value.contentWindow.document;
|
|
|
+
|
|
|
+ var pageNumber = 0;
|
|
|
+ if (content.indexOf('-') > -1 && content.indexOf('-') < 3) {
|
|
|
+ content = content.substring(content.indexOf('-') + 1);
|
|
|
+ }
|
|
|
+ var text = content;
|
|
|
+ var ptText = text.replaceAll('\n', '');
|
|
|
+ ptText = ptText.replaceAll(' ', '');
|
|
|
+ pageText.value.forEach((item) => {
|
|
|
+ item.txt = item.txt.replaceAll('\n', '');
|
|
|
+ item.txt = item.txt.replaceAll(' ', '');
|
|
|
+ console.log('==页=' + item.txt);
|
|
|
+ console.log('==t=' + ptText);
|
|
|
+ if (ptText.length > 30) {
|
|
|
+ if (item.txt.indexOf(ptText.substring(30, 48)) > -1) {
|
|
|
+ pageNumber = item.page;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (item.txt.indexOf(ptText.substring(0, 18)) > -1) {
|
|
|
+ pageNumber = item.page;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ text = text.replaceAll('\n', '');
|
|
|
+ text = text.replaceAll(' ', '');
|
|
|
+ if (text.endsWith('。') || text.endsWith(',') || text.endsWith('、')) {
|
|
|
+ text = text.substring(0, text.length - 1);
|
|
|
+ }
|
|
|
+ if (pageNumber == 0) {
|
|
|
+ message.info('未匹配到相关内容!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //'left: 133.17px; top: 152.408px; font-size: 31.3597px; font-family: sans-serif; transform: scaleX(1.01878);'
|
|
|
+ var arr = doc.getElementsByClassName('textLayer')[pageNumber - 1].getElementsByTagName('span');
|
|
|
+ if (arr) {
|
|
|
+ var hightList = [];
|
|
|
+ var startIndex = 0;
|
|
|
+ var endIndex = 0;
|
|
|
+ var thirdIndex = 0;
|
|
|
+ startIndex = getStartIndex(arr, text, text.length);
|
|
|
+ endIndex = getEndIndex(arr, text, text.length, startIndex);
|
|
|
+ if (startIndex && endIndex && endIndex < startIndex) {
|
|
|
+ endIndex = startIndex + text.length;
|
|
|
+ }
|
|
|
+ var currentPageIndex = 0;
|
|
|
+ var nextArr = [];
|
|
|
+ var thirdArr = [];
|
|
|
+ if (endIndex == undefined) {
|
|
|
+ currentPageIndex = arr.length;
|
|
|
+ if (doc.getElementsByClassName('textLayer')[pageNumber]) {
|
|
|
+ nextArr = doc.getElementsByClassName('textLayer')[pageNumber].getElementsByTagName('span');
|
|
|
+ endIndex = getEndIndex(nextArr, text, text.length, startIndex);
|
|
|
+ if (endIndex == undefined && doc.getElementsByClassName('textLayer')[pageNumber + 1]) {
|
|
|
+ thirdArr = doc
|
|
|
+ .getElementsByClassName('textLayer')
|
|
|
+ [pageNumber + 1].getElementsByTagName('span');
|
|
|
+ thirdIndex = getEndIndex(thirdArr, text, text.length, 0);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ endIndex = undefined;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ currentPageIndex = endIndex;
|
|
|
+ if (type == 1) {
|
|
|
+ if (endIndex && startIndex && endIndex > startIndex + content.length) {
|
|
|
+ currentPageIndex = startIndex + content.length;
|
|
|
+ }
|
|
|
+ //中间标点匹配问题
|
|
|
+ // var len = startIndex + content.length + 5;
|
|
|
+ // if (startIndex && endIndex && endIndex > (startIndex + content.length + 5)) {
|
|
|
+ // currentPageIndex = len;
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!startIndex) {
|
|
|
+ startIndex = 0;
|
|
|
+ }
|
|
|
+ for (var i = startIndex; i < currentPageIndex + 1; i++) {
|
|
|
+ var element = arr[i];
|
|
|
+ if (!element) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ var hItem = {};
|
|
|
+ var cssText = element.style.cssText;
|
|
|
+
|
|
|
+ var spanText = element.innerText;
|
|
|
+ var s = spanText.replaceAll(' ', '');
|
|
|
+ var d = spanText.endsWith('。') ? spanText.replaceAll('。', '') : spanText;
|
|
|
+
|
|
|
+ console.log(spanText);
|
|
|
+ if (
|
|
|
+ text.includes(spanText) ||
|
|
|
+ (s && text.includes(s)) ||
|
|
|
+ (d && text.includes(d)) ||
|
|
|
+ (spanText.substring(2) && text.includes(spanText.substring(2)))
|
|
|
+ ) {
|
|
|
+ var topLabel = cssText.split('top: ')[1].split(';')[0];
|
|
|
+ var leftLabel = cssText.split('left: ')[1].split(';')[0];
|
|
|
+ var currentItems = [];
|
|
|
+ pageContent.value.forEach((item) => {
|
|
|
+ if (item.page == pageNumber) {
|
|
|
+ currentItems = item.items;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ for (var m = 0; m < currentItems.length; m++) {
|
|
|
+ // citem.str=citem.str.replaceAll(' ','');
|
|
|
+ var citem = currentItems[m];
|
|
|
+ if (citem.str == spanText) {
|
|
|
+ hItem.width = parseFloat(citem.width);
|
|
|
+ hItem.height = parseFloat(citem.height);
|
|
|
+ hItem.text = spanText;
|
|
|
+ hItem.left = leftLabel;
|
|
|
+ hItem.top = topLabel;
|
|
|
+ hightList.push(hItem);
|
|
|
+ if (hightList.length == 1) {
|
|
|
+ gotoElement(element);
|
|
|
+ }
|
|
|
+ if (text.endsWith(citem.str)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type == 1 && hightList.length == 0) {
|
|
|
+ message.info('未匹配到相关内容!');
|
|
|
+ }
|
|
|
+ // if (type == 2) {
|
|
|
+ var nextHightList = [];
|
|
|
+ //跨页 目前只考虑跨1页
|
|
|
+ if (endIndex != undefined) {
|
|
|
+ for (var i = 0; i < nextArr.length; i++) {
|
|
|
+ var element = nextArr[i];
|
|
|
+ var hItem = {};
|
|
|
+ var cssText = element.style.cssText;
|
|
|
+ var spanText = element.innerText;
|
|
|
+ console.log(spanText);
|
|
|
+ if (text.includes(spanText)) {
|
|
|
+ var topLabel = cssText.split('top: ')[1].split(';')[0];
|
|
|
+ var leftLabel = cssText.split('left: ')[1].split(';')[0];
|
|
|
+ var currentItems = [];
|
|
|
+ pageContent.value.forEach((item) => {
|
|
|
+ if (item.page == pageNumber + 1) {
|
|
|
+ currentItems = item.items;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ for (var l = 0; l < currentItems.length; l++) {
|
|
|
+ var citem = currentItems[l];
|
|
|
+ if (citem.str == spanText) {
|
|
|
+ hItem.width = parseFloat(citem.width);
|
|
|
+ hItem.height = parseFloat(citem.height);
|
|
|
+ hItem.text = spanText;
|
|
|
+ hItem.left = leftLabel;
|
|
|
+ hItem.top = topLabel;
|
|
|
+ nextHightList.push(hItem);
|
|
|
+ if (text.endsWith(citem.str)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (text.endsWith(spanText)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ //跨页 目前只考虑跨2页
|
|
|
+ for (var i = 0; i < nextArr.length; i++) {
|
|
|
+ var element = nextArr[i];
|
|
|
+ var hItem = {};
|
|
|
+ var cssText = element.style.cssText;
|
|
|
+ var spanText = element.innerText;
|
|
|
+ console.log(spanText);
|
|
|
+
|
|
|
+ var topLabel = cssText.split('top: ')[1].split(';')[0];
|
|
|
+ var leftLabel = cssText.split('left: ')[1].split(';')[0];
|
|
|
+ var currentItems = [];
|
|
|
+ pageContent.value.forEach((item) => {
|
|
|
+ if (item.page == pageNumber + 1) {
|
|
|
+ currentItems = item.items;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ for (var l = 0; l < currentItems.length; l++) {
|
|
|
+ var citem = currentItems[l];
|
|
|
+ if (citem.str == spanText) {
|
|
|
+ hItem.width = parseFloat(citem.width);
|
|
|
+ hItem.height = parseFloat(citem.height);
|
|
|
+ hItem.text = spanText;
|
|
|
+ hItem.left = leftLabel;
|
|
|
+ hItem.top = topLabel;
|
|
|
+ nextHightList.push(hItem);
|
|
|
+ if (text.endsWith(citem.str)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var thirdHightList = [];
|
|
|
+ if (thirdIndex != 0 && thirdIndex != undefined) {
|
|
|
+ for (var z = 0; z < thirdIndex + 1; z++) {
|
|
|
+ var element = thirdArr[z];
|
|
|
+ var hItem = {};
|
|
|
+ var cssText = element.style.cssText;
|
|
|
+ var spanText = element.innerText;
|
|
|
+ console.log(spanText);
|
|
|
+ if (text.includes(spanText)) {
|
|
|
+ var topLabel = cssText.split('top: ')[1].split(';')[0];
|
|
|
+ var leftLabel = cssText.split('left: ')[1].split(';')[0];
|
|
|
+ var currentItems = [];
|
|
|
+ pageContent.value.forEach((item) => {
|
|
|
+ if (item.page == pageNumber + 2) {
|
|
|
+ currentItems = item.items;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ for (var l = 0; l < currentItems.length; l++) {
|
|
|
+ var citem = currentItems[l];
|
|
|
+ if (citem.str == spanText) {
|
|
|
+ hItem.width = parseFloat(citem.width);
|
|
|
+ hItem.height = parseFloat(citem.height);
|
|
|
+ hItem.text = spanText;
|
|
|
+ hItem.left = leftLabel;
|
|
|
+ hItem.top = topLabel;
|
|
|
+ thirdHightList.push(hItem);
|
|
|
+ if (text.endsWith(citem.str)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (text.endsWith(spanText)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //渲染
|
|
|
+ var canvas = doc.getElementsByClassName(`page`)[pageNumber - 1];
|
|
|
+ const hightDiv = document.createElement('div');
|
|
|
+ hightDiv.className = 'highlight-layer flash';
|
|
|
+ hightList.forEach((it) => {
|
|
|
+ const hDiv = document.createElement('div');
|
|
|
+ hDiv.className = 'highlight';
|
|
|
+ // 设置内联样式
|
|
|
+ hDiv.style.width = it.width + 'px';
|
|
|
+ hDiv.style.height = it.height * 1.4 + 'px';
|
|
|
+ hDiv.style.left = it.left;
|
|
|
+ hDiv.style.top = parseFloat(it.top.split('%')[0]) - 0.5 + '%';
|
|
|
+ hightDiv.appendChild(hDiv);
|
|
|
+ });
|
|
|
+ canvas.appendChild(hightDiv);
|
|
|
+ if (nextHightList && nextHightList.length > 0) {
|
|
|
+ //渲染
|
|
|
+ var nextCanvas = doc.getElementsByClassName(`page`)[pageNumber];
|
|
|
+ const hightDiv = document.createElement('div');
|
|
|
+ hightDiv.className = 'highlight-layer flash';
|
|
|
+ nextHightList.forEach((it) => {
|
|
|
+ const hDiv = document.createElement('div');
|
|
|
+ hDiv.className = 'highlight';
|
|
|
+ // 设置内联样式
|
|
|
+ hDiv.style.width = it.width + 'px';
|
|
|
+ hDiv.style.height = it.height * 1.4 + 'px';
|
|
|
+ hDiv.style.left = it.left;
|
|
|
+ hDiv.style.top = parseFloat(it.top.split('%')[0]) - 0.5 + '%';
|
|
|
+ hightDiv.appendChild(hDiv);
|
|
|
+ });
|
|
|
+ nextCanvas.appendChild(hightDiv);
|
|
|
+ }
|
|
|
+ if (thirdHightList && thirdHightList.length > 0) {
|
|
|
+ //渲染
|
|
|
+ var thirdCanvas = doc.getElementsByClassName(`page`)[pageNumber + 1];
|
|
|
+ const hightDiv = document.createElement('div');
|
|
|
+ hightDiv.className = 'highlight-layer flash';
|
|
|
+ thirdHightList.forEach((it) => {
|
|
|
+ const hDiv = document.createElement('div');
|
|
|
+ hDiv.className = 'highlight';
|
|
|
+ // 设置内联样式
|
|
|
+ hDiv.style.width = it.width + 'px';
|
|
|
+ hDiv.style.height = it.height * 1.4 + 'px';
|
|
|
+ hDiv.style.left = it.left;
|
|
|
+ hDiv.style.top = parseFloat(it.top.split('%')[0]) - 0.5 + '%';
|
|
|
+ hightDiv.appendChild(hDiv);
|
|
|
+ });
|
|
|
+ thirdCanvas.appendChild(hightDiv);
|
|
|
+ }
|
|
|
+ // }
|
|
|
+ }
|
|
|
+};
|
|
|
+//交集
|
|
|
+function commonContent(str1, str2) {
|
|
|
+ let result = '';
|
|
|
+ for (let i = 0; i < str1.length; i++) {
|
|
|
+ if (str2.includes(str1[i])) {
|
|
|
+ result += str1[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+const getStartIndex = (arr, text, total) => {
|
|
|
+ var indexs = [];
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ var element = arr[i];
|
|
|
+ var spanText = element.innerText;
|
|
|
+ console.log('===' + spanText);
|
|
|
+
|
|
|
+ if (
|
|
|
+ (spanText && text.startsWith(spanText)) ||
|
|
|
+ (spanText && spanText.substring(2) && spanText && text.startsWith(spanText.substring(2))) ||
|
|
|
+ (spanText.indexOf('。') > -1 &&
|
|
|
+ spanText.split('。')[1] &&
|
|
|
+ text.startsWith(spanText.split('。')[1])) ||
|
|
|
+ (spanText.endsWith('。') &&
|
|
|
+ spanText.split('。')[0] &&
|
|
|
+ text.startsWith(spanText.split('。')[0]))
|
|
|
+ ) {
|
|
|
+ indexs.push(i);
|
|
|
+ // return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (indexs.length == 0) {
|
|
|
+ return 0;
|
|
|
+ } else if (indexs.length == 2) {
|
|
|
+ console.log(JSON.stringify(indexs));
|
|
|
+ return indexs[0] == 0 ? indexs[1] : indexs[0];
|
|
|
+ } else {
|
|
|
+ return indexs[0];
|
|
|
+ }
|
|
|
+ //todo 有问题
|
|
|
+ // if (count == arr.length) {
|
|
|
+ // start.value = start.value + 1;
|
|
|
+ // text = text.substring(start.value, total);
|
|
|
+ // getStartIndex(arr, text, total);
|
|
|
+ // }
|
|
|
+};
|
|
|
+const getEndIndex = (arr, text, total, startIndex) => {
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ var element = arr[i];
|
|
|
+ var spanText = element.innerText;
|
|
|
+ var e = '';
|
|
|
+ if (spanText.endsWith('。')) {
|
|
|
+ spanText = spanText.substring(0, spanText.lastIndexOf('。'));
|
|
|
+ }
|
|
|
+ if (spanText.indexOf('。') > -1) {
|
|
|
+ e = spanText.substring(0, spanText.lastIndexOf('。'));
|
|
|
+ }
|
|
|
+ console.log('===' + spanText);
|
|
|
+ if (
|
|
|
+ (spanText && text.endsWith(spanText) && spanText.length > 0) ||
|
|
|
+ (spanText && spanText.indexOf(text) > -1) ||
|
|
|
+ (e && text.endsWith(e))
|
|
|
+ ) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+const heightSingle = (str, flag) => {
|
|
|
+ const iframeDocument = iframeRef.value.contentDocument || iframeRef.value.contentWindow.document;
|
|
|
+
|
|
|
+ const searchText = str;
|
|
|
+ var highlightColor = 'lightblue';
|
|
|
+
|
|
|
+ // 使用DOM操作在iframe中搜索并高亮文本
|
|
|
+ const textNodes = iframeDocument.evaluate(
|
|
|
+ ".//text()[contains(., '" + searchText + "')]",
|
|
|
+ iframeDocument,
|
|
|
+ null,
|
|
|
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
|
|
+ null
|
|
|
+ );
|
|
|
+ var firstFlag = false;
|
|
|
+ for (let i = 0, length = textNodes.snapshotLength; i < length; i++) {
|
|
|
+ const content = textNodes.snapshotItem(i);
|
|
|
+ const textNode = content.textContent;
|
|
|
+
|
|
|
+ const regex = new RegExp(searchText, 'gi');
|
|
|
+ var newContent = textNode.replace(regex, (match) => {
|
|
|
+ if (match == searchText) {
|
|
|
+ if (!firstFlag) {
|
|
|
+ firstFlag = true;
|
|
|
+ highlightColor = 'yellow';
|
|
|
+ } else {
|
|
|
+ highlightColor = 'lightblue';
|
|
|
+ }
|
|
|
+ return `<span id='hightlight-${searchList.value.length}' style="background-color: ${highlightColor};color: #333;margin-top:-3px;border-radius: 3px;">${match}</span>`;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (content.parentNode) {
|
|
|
+ var n = content.parentNode;
|
|
|
+ console.log('====' + n.offsetTop);
|
|
|
+ newContent = '<' + newContent.split('<')[1] + '</span>';
|
|
|
+ var h = content.parentNode.innerHTML;
|
|
|
+ if (!flag) {
|
|
|
+ content.parentNode.innerHTML = h.replace(searchText, newContent);
|
|
|
+ }
|
|
|
+ if (n) {
|
|
|
+ searchList.value.push({
|
|
|
+ cs: n.parentNode.className,
|
|
|
+ index: searchList.value.length,
|
|
|
+ n: n.parentNode
|
|
|
+ });
|
|
|
+ if (searchList.value.length == 1) {
|
|
|
+ // if (!flag) {
|
|
|
+ // locateElement(searchList.value[0].cs, 0);
|
|
|
+ // }
|
|
|
+ gotoElement(n);
|
|
|
+ }
|
|
|
+ //大纲只定位一次
|
|
|
+ if (flag) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!flag) {
|
|
|
+ if (i == textNodes.snapshotLength - 1) {
|
|
|
+ //查询结束
|
|
|
+ emits('search', searchList.value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+const removeSourceHightLight = () => {
|
|
|
+ //获取页面 ai定位移除
|
|
|
+ const doc = iframeRef.value.contentDocument || iframeRef.value.contentWindow.document;
|
|
|
+
|
|
|
+ const arr = doc.getElementsByClassName('highlight-layer');
|
|
|
+ const l = arr.length;
|
|
|
+ for (let i = l - 1; i >= 0; i--) {
|
|
|
+ if (arr[i] != null) {
|
|
|
+ arr[i].parentNode.removeChild(arr[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+const removeHighlight = () => {
|
|
|
+ removeSourceHightLight();
|
|
|
+ if (searchList.value) {
|
|
|
+ searchList.value.forEach((item, index) => {
|
|
|
+ const iframeDocument =
|
|
|
+ iframeRef.value.contentDocument || iframeRef.value.contentWindow.document;
|
|
|
+ const highlightedSpan = iframeDocument.getElementById('hightlight-' + index);
|
|
|
+ if (highlightedSpan != null) {
|
|
|
+ const span = highlightedSpan;
|
|
|
+ console.log(span.innerHTML);
|
|
|
+ setTimeout(() => {
|
|
|
+ span.parentNode.replaceChild(document.createTextNode(span.textContent), span);
|
|
|
+ }, 10);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ searchList.value = [];
|
|
|
+ emits('search', searchList.value);
|
|
|
+};
|
|
|
+defineExpose({ highlightText, removeHighlight, scrollTo, goLocation, goSourceLocation, location });
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.close-icon {
|
|
|
+ position: absolute;
|
|
|
+
|
|
|
+ top: 8px;
|
|
|
+ right: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+</style>
|
|
|
+<style>
|
|
|
+/* 不能用scoped,因为动态创建的元素不会被编译带上hash */
|
|
|
+/* 不加上这些css修饰,元素会错位,样式来自vue-pdf-embed这个库 */
|
|
|
+.canvasWrapper {
|
|
|
+ position: relative;
|
|
|
+ /* display: flex;
|
|
|
+ justify-content: center; */
|
|
|
+}
|
|
|
+.page {
|
|
|
+ position: relative;
|
|
|
+ box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.08);
|
|
|
+ margin: 30px auto;
|
|
|
+}
|
|
|
+.textLayer {
|
|
|
+ text-align: initial;
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ opacity: 0.2;
|
|
|
+ line-height: 1;
|
|
|
+ text-size-adjust: none;
|
|
|
+ forced-color-adjust: none;
|
|
|
+}
|
|
|
+.textLayer span,
|
|
|
+.textLayer br {
|
|
|
+ color: transparent;
|
|
|
+ position: absolute;
|
|
|
+ white-space: pre;
|
|
|
+ cursor: text;
|
|
|
+ transform-origin: 0% 0%;
|
|
|
+}
|
|
|
+.highlight-layer {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ pointer-events: none;
|
|
|
+ opacity: 0.15;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.flash {
|
|
|
+ animation: flashing 0.6s;
|
|
|
+ animation-iteration-count: 2;
|
|
|
+}
|
|
|
+@keyframes flashing {
|
|
|
+ 0% {
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ opacity: 0.15;
|
|
|
+ }
|
|
|
+}
|
|
|
+.highlight {
|
|
|
+ background-color: #0076fa;
|
|
|
+ position: absolute;
|
|
|
+}
|
|
|
+</style>
|