Browse Source

重写目录的定位逻辑

yangdr 2 months ago
parent
commit
baa06b253e

BIN
public/pdf.pdf


+ 3 - 3
public/pdf2js/web/viewer.js

@@ -14725,11 +14725,11 @@
 
         async _writeToStorage() {
           const databaseStr = JSON.stringify(this.database);
-          localStorage.setItem("pdfjs.history", databaseStr);
+          // localStorage.setItem("pdfjs.history", databaseStr);
         }
 
         async _readFromStorage() {
-          return localStorage.getItem("pdfjs.history");
+          // return localStorage.getItem("pdfjs.history");
         }
 
         async set(name, val) {
@@ -14798,7 +14798,7 @@
 
       class GenericPreferences extends _preferences.BasePreferences {
         async _writeToStorage(prefObj) {
-          // localStorage.setItem("pdfjs.preferences", JSON.stringify(prefObj));
+          localStorage.setItem("pdfjs.preferences", JSON.stringify(prefObj));
         }
 
         async _readFromStorage(prefObj) {

+ 16 - 1
src/api/config.js

@@ -2,13 +2,27 @@
 const testUrl = ''
 
 const host = window.location.hostname;
-const imgHost = host.indexOf("localhost")>-1?'http://192.168.65.96:82':('http://'+host+':82');
+const imgHost = host.indexOf("localhost") > -1 ? 'http://192.168.65.96:82' : ('http://' + host + ':82');
+
+let pdfProxy = ""
+let knowledgeGraphAddr = ""
+//通过环境判断
+if (process.env.NODE_ENV === 'development') {
+  pdfProxy = 'http://173.18.12.205:8001'
+  knowledgeGraphAddr = 'http://173.18.12.191:1388'
+} else if (process.env.NODE_ENV === 'production') {
+  pdfProxy = window.location.origin
+  knowledgeGraphAddr = 'http://' + window.location.hostname + ':1388'
+}
+
 
 export default {
   //imgHost: 'http://192.168.2.236:82',      //富文本编辑器图片回传地址
   // imgHost: 'http://192.168.2.121:82',      //富文本编辑器图片回传地址
   imgHost: imgHost,      //富文本编辑器图片回传地址
   delayTime: 500,
+  knowledgeGraphAddr,
+  pdfProxy,
   urls: {
     /* 登录注册相关接口 */
     'getImgVerification': '/api/user/userver/getImgVerification',
@@ -759,3 +773,4 @@ export default {
     // },
   ],
 }
+

+ 5 - 0
src/routes.js

@@ -286,6 +286,11 @@ export default [
                 path: 'entry',
                 component: () => import("@/views/KnowledgeGraphRetrievalTool/Entry.vue")
             },
+            // {
+            //     name: "kgrt-entry-test",
+            //     path: 'entry-test',
+            //     component: () => import("@/views/KnowledgeGraphRetrievalTool/EntryTest.vue")
+            // },
             {
                 name: "kgrt-evidence",
                 path: 'evidence',

+ 186 - 130
src/views/KnowledgeGraphRetrievalTool/Entry.vue

@@ -1,13 +1,13 @@
 <template>
-  <div class="kgrt-entry" v-cloak>
+  <div class="kgrt-entry" v-cloak v-loading="loading">
     <header class="entry-topbar">
       <button class="back" @click="handleBack">返回</button>
-      <span class="title">知识图谱检索工具</span>
+      <span class="title">知识溯源检索</span>
       <input type="text" v-model.trim="keyword" @keydown.enter="handleRetrieval(keyword)" />
       <button class="retrieval" @click="handleRetrieval(keyword)">检索</button>
     </header>
     <div class="entry-content">
-      <div class="left" v-loading="loading" ref="leftRef">
+      <div class="left" ref="leftRef">
         <div class="result-num" v-show="pagination.total">
           搜索结果:{{ pagination.total }}条
         </div>
@@ -18,7 +18,7 @@
                 <i @click.stop="handleClick(record.name, record.category)" style="cursor: pointer;">
                   {{ record.name }}
                 </i>
-                <i class="distance">{{ record.distance }}</i>
+                <!-- <i class="distance">{{ record.distance }}</i> -->
                 <i :class="record.isShow ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
                   style="margin-left: auto; color: #2780ff"></i>
               </h3>
@@ -29,7 +29,9 @@
                 <div class="menu">
                   <div style="font-weight: bold;margin-bottom: 20px;" v-show="record.props.length > 0">目录</div>
                   <div class="anchor" v-for="prop in record.props" :key="prop.id">
-                    <a :href="'#anchor' + prop.id">{{ prop.prop_title }}</a>
+                    <a :href="'#anchor' + prop.id"
+                      @click.prevent="handleAnchor('.entry-content', '#anchor' + prop.id)">{{ prop.prop_title
+                      }}</a>
                     <!-- <el-button type="text" :targetId="'anchor' + prop.id">{{ prop.prop_title }}</el-button> -->
                   </div>
                 </div>
@@ -39,7 +41,7 @@
                     <div class="prop-content">
                       <div v-if="prop.answer">
                         <i v-for="(answer, index) in prop.answer" :key="index">
-                          <i>{{ answer.sentence }}</i>
+                          <i v-html="answer.sentence"></i>
                           <i v-if="answer.flag" class="pdf-icon" @click="handlePdf(answer.flag, prop.references)">
                             {{ answer.flag }}&nbsp;PDF
                           </i>
@@ -120,8 +122,8 @@
             <!-- <PdfViewer :pdfUrl="pdfUrl" :pdfPage="pdfPage" @closePdf="closePdf"></PdfViewer> -->
             <i class="el-icon-circle-close pdf2js-close-icon" @click="pdfShow = false"></i>
             <iframe style="width: 100%;height: 100%;" id="pdf2js"
-              :src="pdf2jsUrl + '?file=' + encodeURIComponent(pdfUrl)"></iframe>
-
+              :src="pdf2jsUrl + '?file=' + encodeURIComponent(pdfProxy + decodeURIComponent(pdfUrl))">
+            </iframe>
           </div>
 
         </div>
@@ -143,15 +145,19 @@
 import $ from 'jquery'
 import PdfViewer from "./PdfViewer.vue"
 const markdown = require("markdown").markdown;
-const knowledgeGraphAddr = "http://172.16.8.57:8080";
-// const knowledgeGraphAddr = "http://173.18.12.191:1388";
-
+import config from "@api/config.js"
+const knowledgeGraphAddr = config.knowledgeGraphAddr
+// console.log(config.pdfProxy, knowledgeGraphAddr)
+let timer1
+let timer2
 export default {
   data() {
     return {
+      pdfProxy: config.pdfProxy,
       radio: "检索推荐",
       loading: false,
       keyword: "",
+      distance: 1,
       records: [],
       pagination: {},
       activeName: "1",
@@ -195,8 +201,14 @@ export default {
         }
       }
     },
+
+
     viewPdf(pdfUrl, page, reference) {
-      let timer
+      clearInterval(timer1)
+      clearInterval(timer2)
+      this.pdfPage = page || 1
+      this.pdfDialogData.visible = false
+      this.pdfShow = true;
 
       const iframeLoadEvent = async () => {
         const iframe = document.getElementById('pdf2js');
@@ -204,66 +216,57 @@ export default {
         // console.log("PDFViewerApplication", viewerApp, viewerApp.eventBus)
         await viewerApp.initializedPromise //等待pdf查看器初始化完成
         // 监听 PDF 加载完成事件
-        // viewerApp.page = this.pdfPage1  // 传入需要让跳转页码的值
-        viewerApp.eventBus.on('documentloaded', () => {
+        // viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
+
+        // 监听目标页面渲染完成
+        const onTargetPageRendered = (event) => {
+          if (event.pageNumber === this.pdfPage) { // 目标页码
+            let cycleIndex = 1
+            viewerApp.page = this.pdfPage;
+            // console.log(`第${this.pdfPage}页渲染完成!`);
+            timer2 = setInterval(() => {
+              viewerApp.page = this.pdfPage;
+              const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+              const pdfData = {
+                htmlElements: [],
+                textList: [],
+                page: this.pdfPage,
+                trunkId: this.pdfDialogData.reference.id
+              }
+              $(iframeDocument).find(`.pdfViewer .page[data-page-number="${this.pdfPage}"] .textLayer  span[role="presentation"]`).each((index, element) => {
+                // console.log(index, $(element).text())
+                pdfData.htmlElements.push(element)
+                pdfData.textList.push($(element).text())
+                // $(element).addClass("highlight")
+              })
+              if (pdfData.htmlElements.length === 0 && cycleIndex <= 500) {
+                // console.log("1没有获取到文本!")
+              } else {
+                clearInterval(timer2)
+                this.matchHighlight(pdfData)
+              }
+            }, 200)
+            viewerApp.eventBus.off('pagerendered', onTargetPageRendered); // 移除监听
+            viewerApp.eventBus.off('documentloaded', onTargetDocumentLoaded); // 移除监听
+          }
+        };
+        const onTargetDocumentLoaded = () => {
           viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
-          iframe.contentWindow.postMessage("", '*') //传递要匹配的字符串
-          iframe.contentWindow.addEventListener('message', (e) => { //搜索高亮
-            viewerApp.findBar.findField.value = e.data
-            viewerApp.findBar.highlightAll.checked = true
-            viewerApp.findBar.dispatchEvent('highlightallchange')
-          }, false)
-          // console.log("pagesCount", viewerApp.pagesCount) //获取pdf总页数
-          // console.log('文档加载完毕!')
-
-          timer = setInterval(() => {
-            viewerApp.page = this.pdfPage
-            const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
-            const pdfData = {
-              htmlElements: [],
-              textList: [],
-              trunkId: this.pdfDialogData.reference.id
-            }
-            $(iframeDocument).find(`.pdfViewer .page[data-page-number="${this.pdfPage}"] .textLayer  span[role="presentation"]`).each((index, element) => {
-              // console.log(index, $(element).text())
-              pdfData.htmlElements.push(element)
-              pdfData.textList.push($(element).text())
-              // $(element).addClass("highlight")
-            })
-            if (pdfData.htmlElements.length === 0) {
-              console.log("1没有获取到文本!")
+          const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+          let cycleIndex = 1
+          timer1 = setInterval(() => {
+            if (+$(iframeDocument).find("#pageNumber.toolbarField").val() !== this.pdfPage && cycleIndex <= 500) {
+              viewerApp.page = this.pdfPage;
+              cycleIndex++
+              // console.log("viewerApp.page", viewerApp.page !== this.pdfPage)
             } else {
-              clearInterval(timer)
-              this.matchHighlight(pdfData)
+              // console.log("viewerApp.page", viewerApp.page === this.pdfPage, viewerApp.page, this.pdfPage)
+              clearInterval(timer1)
             }
           }, 500)
-        });
-        // viewerApp.eventBus.on('pagesloaded', () => {
-        //   alert('pagesloaded')
-        //   viewerApp.page = this.pdfPage
-        //   let timer = null
-        //   timer = setInterval(() => {
-        //     viewerApp.page = this.pdfPage
-        //     const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
-        //     const pdfData = {
-        //       htmlElements: [],
-        //       textList: [],
-        //       trunkId: reference.id
-        //     }
-        //     $(iframeDocument).find(`.pdfViewer .page[data-page-number="${page}"] .textLayer  span[role="presentation"]`).each((index, element) => {
-        //       // console.log(index, $(element).text())
-        //       pdfData.htmlElements.push(element)
-        //       pdfData.textList.push($(element).text())
-        //       // $(element).addClass("highlight")
-        //     })
-        //     if (pdfData.htmlElements.length === 0) {
-        //     } else {
-        //       clearInterval(timer)
-        //       this.matchHighlight(pdfData)
-        //     }
-        //   }, 200)
-        //   // console.log('PDF 页面渲染完成');
-        // });
+        }
+        viewerApp.eventBus.on('pagerendered', onTargetPageRendered);
+        viewerApp.eventBus.on('documentloaded', onTargetDocumentLoaded);
       }
       if (!this.pdfUrl) {
         document.getElementById('pdf2js').addEventListener('load', iframeLoadEvent);
@@ -271,9 +274,6 @@ export default {
 
       const isPdfUrlChange = this.pdfUrl !== pdfUrl;
       this.pdfUrl = pdfUrl
-      this.pdfDialogData.visible = false
-      this.pdfShow = true;
-      this.pdfPage = page || 1
 
       this.$nextTick(() => {
         // 获取 iframe 元素
@@ -283,33 +283,31 @@ export default {
           // iframe.removeEventListener('load', iframeLoadEvent);
           // iframe.addEventListener('load', iframeLoadEvent);
         } else {
+
           const viewerApp = iframe.contentWindow.PDFViewerApplication;
           viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
-          // iframe.contentWindow.postMessage("", '*') //传递要匹配的字符串
-          // iframe.contentWindow.addEventListener('message', (e) => { //搜索高亮
-          //   viewerApp.findBar.findField.value = e.data
-          //   viewerApp.findBar.highlightAll.checked = true
-          //   viewerApp.findBar.dispatchEvent('highlightallchange')
-          // }, false)
-
-          timer = setInterval(() => {
+          let cycleIndex = 1
+          timer2 = setInterval(() => {
             viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
             const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
             const pdfData = {
               htmlElements: [],
               textList: [],
+              page: this.pdfPage,
               trunkId: this.pdfDialogData.reference.id
             }
             $(iframeDocument).find(`.pdfViewer .page[data-page-number="${page}"] .textLayer  span[role="presentation"]`).each((index, element) => {
               // console.log(index, $(element).text())
               pdfData.htmlElements.push(element)
               pdfData.textList.push($(element).text())
-              // $(element).addClass("highlight")
+              // 移除之前的高亮
+              $(element).removeClass("highlight")
             })
-            if (pdfData.htmlElements.length === 0) {
-              console.log("2没有获取到文本!")
+            if (pdfData.htmlElements.length === 0 && cycleIndex <= 500) {
+              // console.log("2没有获取到文本!")
+              cycleIndex++
             } else {
-              clearInterval(timer)
+              clearInterval(timer2)
               this.matchHighlight(pdfData)
             }
           }, 200)
@@ -319,15 +317,38 @@ export default {
     closePdf(data) {
       this.pdfShow = false
     },
+    handleAnchor(parentClass, anchor) {
+      // console.log(parentClass, anchor, $(anchor).offset().top)
+      // $(parentClass).animate({
+      //   scrollTop: $(anchor).offset().top - $(parentClass).offset().top
+      // });  // 1000 表示滚动的动画持续时间(毫秒)
+
+      const container = $(parentClass);
+      const target = $(anchor);
+
+      // 获取父容器当前滚动量
+      const currentScroll = container.scrollTop();
+      // 计算目标元素的原始偏移(需父容器有 position: relative)
+      const targetOffset = target.position().top;
+
+      // 最终滚动位置 = 原始偏移 + 当前已滚动量
+      container.animate({ scrollTop: currentScroll + targetOffset });
+    },
     matchHighlight: async function (pdfData) {
       try {
         // clearInterval(timer)
-        const { data, success } = await this.$http.post(`/kgrt_api/saas/trunks/${pdfData.trunkId}/highlight`, pdfData.textList)
+
+        const { data, success } = await this.$http.post(`/knowledge/saas/trunks/${pdfData.trunkId}/highlight`, pdfData.textList)
         // console.log(data)
         if (success) {
+          // const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+          // const divArray = $(iframeDocument).find(`.pdfViewer .page[data-page-number="${pdfData.page}"] .textLayer  span[role="presentation"]`).map(function () { return this; }).get()
+          // console.log(pdfData, data, divArray)
           for (let i = 0; i < data.length; i++) {
             // console.log(data[i], pdfData.htmlElements[data[i]])
+            // $(pdfData.htmlElements[data[i]]).css('backgroundColor', 'red');
             $(pdfData.htmlElements[data[i]]).addClass("highlight");
+
           }
         }
       } catch (e) {
@@ -373,9 +394,10 @@ export default {
         const params = {
           "node_id": this.records[index].id,
           "props_ids": this.records[index].props.map(it => it.id),
+          symptoms: this.$route.query.mw ? this.$route.query.mw.split(';') : []
           // "conversation_id": "string"
         }
-        let res = await this.axios.post("/kgrt_api/text/eb_search", params)
+        let res = await this.axios.post("/knowledge/text/eb_search", params)
         const { data, code, msg, success } = res.data
         if (success) {
           // Object.assign(this.records[index], data)
@@ -391,28 +413,26 @@ export default {
       }
     },
     getPaginatedSearch: async function (keyword, category = "", pageNo = 1, limit = 10) {
-      if (!keyword) {
-        this.pagination = {};
-        this.records = [];
-        return;
-      }
       try {
+        if (!keyword) {
+          this.pagination = {};
+          this.records = [];
+          return;
+        }
         this.loading = true;
         let resData = await this.axios.post(
-          "/kgrt_api/saas/nodes/paginated_search",
+          "/knowledge/saas/nodes/paginated_search",
           {
             keyword: keyword,
             category: category,
             pageNo: pageNo,
             limit: limit,
+            distance: this.distance,
             // knowledge_ids: ["ER"],
           }
         );
-
         this.records = resData.data.data.records;
-        // this.records.forEach((it) => {
-        //   it.isShow = false
-        // })
+
         // console.log("records", this.records);
         this.pagination = resData.data.data.pagination;
         if (this.records.length > 0) {
@@ -424,21 +444,27 @@ export default {
         this.$nextTick(() => {
           // 回到顶部
           document.querySelector(".kgrt-entry").scrollTop = 0;
+          if (this.distance === 0) {
+            // this.records.forEach((it) => {
+            //   it.isShow = true
+            // })
+            $(".main-content-list .record-title").trigger('click')
+          }
         });
       } catch (e) {
-        console.log("getPaginatedSearch出错了", e);
+        console.log(e);
       } finally {
         this.loading = false;
       }
     },
     handlePaginationChange(page) {
       // console.log(page);
-      this.getPaginatedSearch(this.$route.params.keyword || this.keyword, page);
+      this.getPaginatedSearch(this.$route.params.keyword || this.keyword, this.$route.query.category || "", page);
     },
     getVectorSearch: async function (text) {
       try {
         let resData = await this.axios.post(
-          "/kgrt_api/saas/trunks/vector_search",
+          "/knowledge/saas/trunks/vector_search",
           {
             text: text,
             limit: 10,
@@ -447,14 +473,24 @@ export default {
         );
         this.vectorSearch = resData.data.data;
       } catch (e) {
-        console.log("getVectorSearch出错了", e);
+        console.log(e);
       }
     },
   },
 
   created() {
     this.keyword = this.$route.query.keyword;
-    let category = this.$route.query.category
+    const category = this.$route.query.category
+    if (this.$route.query.hasOwnProperty('exact')) {
+      if (this.$route.query.exact === null) {
+        this.distance = 0
+      } else {
+        this.distance = Number(this.$route.query.exact) || 0
+      }
+    } else {
+      this.distance = 1
+    }
+    // console.log('exact', this.$route.query.exact, this.distance)
     this.getPaginatedSearch(this.keyword, category);
   },
   mounted() { },
@@ -462,17 +498,22 @@ export default {
 </script>
 
 <style scoped lang="less">
+div {
+  box-sizing: border-box;
+}
+
 .pdf-icon {
   display: inline;
-  height: 20px;
-  min-width: 40px;
+  height: 12px;
+  font-size: 12px;
+  // min-width: 40px;
   cursor: pointer;
   color: white;
-  font-weight: bolder;
+  // font-weight: bolder;
   border-radius: 10px;
-  padding: 0px 10px;
+  padding: 0px;
   text-align: right;
-  line-height: 20px;
+  // line-height: 20px;
   background-color: #FECFCB;
   white-space: nowrap;
 }
@@ -499,9 +540,9 @@ export default {
   width: 100vw;
   height: 100vh;
   box-sizing: border-box;
-  padding: 10px 10px 20px 20px;
-  overflow-y: auto;
-  overflow-x: hidden;
+  padding: 0px;
+  overflow: auto;
+  // overflow-x: hidden;
   display: flex;
   flex-direction: column;
 
@@ -510,7 +551,9 @@ export default {
     flex: 0 0 auto;
     display: flex;
     align-items: center;
-    padding: 0px 5px;
+    padding: 0px 5px 20px;
+    border-bottom: 1px solid #e0e0e0;
+    margin: 0px 15px;
 
     // min-width: 500px;
     .back {
@@ -547,37 +590,45 @@ export default {
   }
 
   .entry-content {
-    margin-top: 20px;
-    border-top: 1px solid #e0e0e0;
+    // margin-top: 20px;
+    // margin: 20px 15px 0px 15px;
+
     display: flex;
     flex-wrap: nowrap;
     flex: 1 1 auto;
-    overflow: hidden;
+    overflow-y: auto;
+    // overflow-x: hidden;
+    box-sizing: border-box;
+    padding: 0px 15px;
+    position: relative;
+
+    &::-webkit-scrollbar {
+      // display: none;
+      width: 10px;
+      height: 10px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background-color: rgb(226, 218, 218);
+      border-radius: 10px;
+    }
+
+    &::-webkit-scrollbar-track {
+      border-radius: 2px;
+    }
 
     .left {
       // flex: 1 1 1;
       // background: skyblue;
-      overflow-y: auto;
-      overflow-x: hidden;
+      // overflow-y: auto;
+      // overflow-x: hidden;
       width: 50%;
-      height: 100%;
+      // height: 100%;
 
       min-width: 500px;
       padding-top: 10px;
 
-      &::-webkit-scrollbar {
-        // display: none;
-        width: 10px;
-      }
 
-      &::-webkit-scrollbar-thumb {
-        background-color: rgb(226, 218, 218);
-        border-radius: 10px;
-      }
-
-      &::-webkit-scrollbar-track {
-        border-radius: 2px;
-      }
 
       .result-num {
         padding-left: 5px;
@@ -721,19 +772,24 @@ export default {
       // flex: 0 0 1;
       // background: orange;
       padding: 10px 5px 5px 30px;
+      position: sticky;
+      /* 使右边盒子固定 */
+      top: 0;
 
+      /* 顶部固定 */
       .aside {
         position: relative;
         height: 100%;
+        width: 100%;
         display: flex;
         flex-direction: column;
 
         .pdf {
           position: absolute;
-          top: 0px;
-          width: 100%;
+          top: -18px;
+          width: calc(100% + 20px);
           min-height: 500px;
-          height: calc(100vh - 130px);
+          height: calc(100vh - 80px);
           // overflow: auto;
           overflow: hidden;
           background: white;

+ 214 - 74
src/views/KnowledgeGraphRetrievalTool/EntryTest.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="kgrt-entry" v-cloak>
+  <div class="kgrt-entry" v-cloak v-loading="loading">
     <header class="entry-topbar">
       <button class="back" @click="handleBack">返回</button>
       <span class="title">知识图谱检索工具</span>
@@ -7,7 +7,7 @@
       <button class="retrieval" @click="handleRetrieval(keyword)">检索</button>
     </header>
     <div class="entry-content">
-      <div class="left" v-loading="loading" ref="leftRef">
+      <div class="left" ref="leftRef">
         <div class="result-num" v-show="pagination.total">
           搜索结果:{{ pagination.total }}条
         </div>
@@ -18,13 +18,13 @@
                 <i @click.stop="handleClick(record.name, record.category)" style="cursor: pointer;">
                   {{ record.name }}
                 </i>
-                <i class="distance">{{ record.distance }}</i>
+                <!-- <i class="distance">{{ record.distance }}</i> -->
                 <i :class="record.isShow ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
                   style="margin-left: auto; color: #2780ff"></i>
               </h3>
               <div class="category">{{ record.category }}</div>
             </div>
-            <div :style="record.isShow ? { maxHeight: '2000px' } : { maxHeight: '0px' }" class="record-content-box">
+            <div :style="record.isShow ? { maxHeight: '10000px' } : { maxHeight: '0px' }" class="record-content-box">
               <div class="record-content-box-item1">
                 <div class="menu">
                   <div style="font-weight: bold;margin-bottom: 20px;" v-show="record.props.length > 0">目录</div>
@@ -56,8 +56,7 @@
               <div class="literature" v-if="Array.isArray(record.files) && record.files.length > 0">
                 <div>来源</div>
                 <ul>
-                  <li v-for="file in record.files.sort((a, b) => { return +a.index - +b.index; })"
-                    :key="file.file_name">
+                  <li v-for="file in record.files" :key="file.file_name">
                     {{ file.index }}.<i class="file-type-icon" v-if="file.file_type">{{ file.file_type }}</i>{{
                       decodeURIComponent(file.file_name) }}
                   </li>
@@ -128,10 +127,14 @@
         </div>
       </div>
     </div>
-    <el-dialog title="文献引用" width="600px" custom-class="pdf-dialog" :modal="true" :visible.sync="pdfDialogData.visible">
+    <el-dialog title="文献引用" width="600px" custom-class="pdf-dialog" :modal="false"
+      :visible.sync="pdfDialogData.visible">
       <pre style="white-space: pre-line;">{{ pdfDialogData.content }}</pre>
       <span class="view-pdf"
-        @click="viewPdf(pdfDialogData.pdfUrl, pdfDialogData.page, pdfDialogData.content)">查看PDF</span>
+        @click="viewPdf(pdfDialogData.pdfUrl, pdfDialogData.page, pdfDialogData.reference)">查看PDF</span>
+      <span class="reference-source">来源:{{ pdfDialogData.reference &&
+        decodeURIComponent(pdfDialogData.reference.file_name)
+      }}</span>
     </el-dialog>
   </div>
 
@@ -140,8 +143,9 @@
 import $ from 'jquery'
 import PdfViewer from "./PdfViewer.vue"
 const markdown = require("markdown").markdown;
-const knowledgeGraphAddr = "http://172.16.8.57:8080";
-// const knowledgeGraphAddr = "http://173.18.12.191:1388";
+// const knowledgeGraphAddr = "http://172.16.8.57:8080";
+const knowledgeGraphAddr = "http://173.18.12.191:1388";
+let timer
 export default {
   data() {
     return {
@@ -162,7 +166,10 @@ export default {
         content: "",
         pdfUrl: "",
         page: 1,
+        oldPage: 1,
+        reference: null,
       }
+
     };
   },
   components: {
@@ -175,49 +182,153 @@ export default {
       if (Array.isArray(references)) {
         for (let i = 0; i < references.length; i++) {
           if (references[i].index === flag) {
-
             const pdfUrl = references[i].referrence
             // alert(pdfUrl)
             this.pdfDialogData.content = references[i].content
             this.pdfDialogData.pdfUrl = pdfUrl
             this.pdfDialogData.visible = true
             this.pdfDialogData.page = references[i].page_no
+            this.pdfDialogData.reference = references[i]
             // alert(references.page_no)
             break
           }
         }
       }
     },
-    viewPdf(pdfUrl, page, content) {
-      this.pdfUrl = pdfUrl
+    viewPdf(pdfUrl, page, reference) {
+      clearInterval(timer)
+      this.pdfPage = page || 1
       this.pdfDialogData.visible = false
       this.pdfShow = true;
-      this.pdfPage = page
+
+      const iframeLoadEvent = async () => {
+        const iframe = document.getElementById('pdf2js');
+        const viewerApp = iframe.contentWindow.PDFViewerApplication;
+        // console.log("PDFViewerApplication", viewerApp, viewerApp.eventBus)
+        await viewerApp.initializedPromise //等待pdf查看器初始化完成
+        // 监听 PDF 加载完成事件
+        // viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
+
+        // 监听目标页面渲染完成
+        const onTargetPageRendered = (event) => {
+          if (event.pageNumber === this.pdfPage) { // 目标页码
+            let cycleIndex = 1
+            viewerApp.page = this.pdfPage;
+            console.log(`第${this.pdfPage}页渲染完成!`);
+            timer = setInterval(() => {
+              viewerApp.page = this.pdfPage;
+              const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+              const pdfData = {
+                htmlElements: [],
+                textList: [],
+                page: this.pdfPage,
+                trunkId: this.pdfDialogData.reference.id
+              }
+              $(iframeDocument).find(`.pdfViewer .page[data-page-number="${this.pdfPage}"] .textLayer  span[role="presentation"]`).each((index, element) => {
+                // console.log(index, $(element).text())
+                pdfData.htmlElements.push(element)
+                pdfData.textList.push($(element).text())
+                // $(element).addClass("highlight")
+              })
+              if (pdfData.htmlElements.length === 0 && cycleIndex <= 500) {
+                console.log("1没有获取到文本!")
+              } else {
+                clearInterval(timer)
+                this.matchHighlight(pdfData)
+              }
+            }, 500)
+            viewerApp.eventBus.off('pagerendered', onTargetPageRendered); // 移除监听
+            viewerApp.eventBus.off('pagerendered', onTargetDocumentLoaded); // 移除监听
+          }
+        };
+        const onTargetDocumentLoaded = () => {
+          viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
+          viewerApp.eventBus.on('pagerendered', onTargetPageRendered);
+          const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+          let cycleIndex = 1
+          let timer1 = setInterval(() => {
+            if (+$(iframeDocument).find("#pageNumber.toolbarField").val() !== this.pdfPage && cycleIndex <= 1000) {
+              viewerApp.page = this.pdfPage;
+              cycleIndex++
+              console.log("viewerApp.page", viewerApp.page !== this.pdfPage)
+            } else {
+              console.log("viewerApp.page", viewerApp.page === this.pdfPage, viewerApp.page, this.pdfPage)
+              clearInterval(timer1)
+            }
+          }, 300)
+        }
+        viewerApp.eventBus.on('documentloaded', onTargetDocumentLoaded);
+      }
+      if (!this.pdfUrl) {
+        document.getElementById('pdf2js').addEventListener('load', iframeLoadEvent);
+      }
+
+      const isPdfUrlChange = this.pdfUrl !== pdfUrl;
+      this.pdfUrl = pdfUrl
+
       this.$nextTick(() => {
-        setTimeout(() => {
-          // 获取 iframe 元素
-          const iframe = document.getElementById('pdf2js');
-          // 获取 iframe 中的 document 对象
-          // const iframeDocument = iframe.contentWindow.document;
-          // $(iframeDocument).find("#findInput").val(content)
-          // $(iframeDocument).find("#findNext").click()
-          const pdfFrame = iframe.contentWindow
-          pdfFrame.PDFViewerApplication.page = page || 1  // 传入需要让跳转的值
-
-          iframe.contentWindow.postMessage(content, '*')
-          iframe.contentWindow.addEventListener('message', (e) => {
-            iframe.contentWindow.PDFViewerApplication.findBar.findField.value = e.data
-            iframe.contentWindow.PDFViewerApplication.findBar.highlightAll.checked = true
-            iframe.contentWindow.PDFViewerApplication.findBar.dispatchEvent('highlightallchange')
-          }, false)
-          iframe.contentWindow.PDFViewerApplication.pagesCount
-        }, 3000)
+        // 获取 iframe 元素
+        // console.log("我是nextTick")
+        const iframe = document.getElementById('pdf2js');
+        if (isPdfUrlChange) { //pdf文件链接有改变,需要等待文件加载完毕
+          // iframe.removeEventListener('load', iframeLoadEvent);
+          // iframe.addEventListener('load', iframeLoadEvent);
+        } else {
+
+          const viewerApp = iframe.contentWindow.PDFViewerApplication;
+          viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
+          let cycleIndex = 1
+          timer = setInterval(() => {
+            viewerApp.page = this.pdfPage  // 传入需要让跳转页码的值
+            const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+            const pdfData = {
+              htmlElements: [],
+              textList: [],
+              page: this.pdfPage,
+              trunkId: this.pdfDialogData.reference.id
+            }
+            $(iframeDocument).find(`.pdfViewer .page[data-page-number="${page}"] .textLayer  span[role="presentation"]`).each((index, element) => {
+              // console.log(index, $(element).text())
+              pdfData.htmlElements.push(element)
+              pdfData.textList.push($(element).text())
+              // 移除之前的高亮
+              $(element).removeClass("highlight")
+            })
+            if (pdfData.htmlElements.length === 0 && cycleIndex <= 1000) {
+              console.log("2没有获取到文本!")
+              cycleIndex++
+            } else {
+              clearInterval(timer)
+              this.matchHighlight(pdfData)
+            }
+          }, 200)
+        }
       })
-
     },
     closePdf(data) {
       this.pdfShow = false
     },
+    matchHighlight: async function (pdfData) {
+      try {
+        // clearInterval(timer)
+
+        const { data, success } = await this.$http.post(`/kgrt_api/saas/trunks/${pdfData.trunkId}/highlight`, pdfData.textList)
+        // console.log(data)
+        if (success) {
+          // const iframeDocument = document.getElementById('pdf2js').contentWindow.document;
+          // const divArray = $(iframeDocument).find(`.pdfViewer .page[data-page-number="${pdfData.page}"] .textLayer  span[role="presentation"]`).map(function () { return this; }).get()
+          // console.log(pdfData, data, divArray)
+          for (let i = 0; i < data.length; i++) {
+            // console.log(data[i], pdfData.htmlElements[data[i]])
+            // $(pdfData.htmlElements[data[i]]).css('backgroundColor', 'red');
+            $(pdfData.htmlElements[data[i]]).addClass("highlight");
+
+          }
+        }
+      } catch (e) {
+        console.log(e)
+      }
+    },
     handleClick(name, category) {
       // this.getVectorSearch(name);
       // this.keyword = name;
@@ -257,33 +368,36 @@ export default {
         const params = {
           "node_id": this.records[index].id,
           "props_ids": this.records[index].props.map(it => it.id),
-          "conversation_id": "string"
+          // "conversation_id": "string"
         }
         let res = await this.axios.post("/kgrt_api/text/eb_search", params)
         const { data, code, msg, success } = res.data
         if (success) {
           // Object.assign(this.records[index], data)
+          // const copyData = JSON.parse(JSON.stringify(this.records[index]))
+          this.records[index].files = data.files
           this.records[index].props = data.props
-          console.log("record", this.records[index])
+          // this.records.splice(index, 1, copyData)
+
+          // console.log("record", this.records[index])
         }
       } catch (e) {
         console.log(e)
       }
     },
-    getPaginatedSearch: async function (keyword, pageNo, limit) {
+    getPaginatedSearch: async function (keyword, category = "", pageNo = 1, limit = 10) {
       if (!keyword) {
         this.pagination = {};
         this.records = [];
         return;
       }
-      if (!pageNo) pageNo = 1;
-      if (!limit) limit = 10;
       try {
         this.loading = true;
         let resData = await this.axios.post(
           "/kgrt_api/saas/nodes/paginated_search",
           {
             keyword: keyword,
+            category: category,
             pageNo: pageNo,
             limit: limit,
             // knowledge_ids: ["ER"],
@@ -294,7 +408,7 @@ export default {
         // this.records.forEach((it) => {
         //   it.isShow = false
         // })
-        console.log("records", this.records);
+        // console.log("records", this.records);
         this.pagination = resData.data.data.pagination;
         if (this.records.length > 0) {
           const name = this.records[0].name;
@@ -307,14 +421,14 @@ export default {
           document.querySelector(".kgrt-entry").scrollTop = 0;
         });
       } catch (e) {
-        console.log("getPaginatedSearch出错了", e);
+        console.log(e);
       } finally {
         this.loading = false;
       }
     },
     handlePaginationChange(page) {
       // console.log(page);
-      this.getPaginatedSearch(this.$route.params.keyword || this.keyword, page);
+      this.getPaginatedSearch(this.$route.params.keyword || this.keyword, this.$route.query.category || "", page);
     },
     getVectorSearch: async function (text) {
       try {
@@ -328,31 +442,37 @@ export default {
         );
         this.vectorSearch = resData.data.data;
       } catch (e) {
-        console.log("getVectorSearch出错了", e);
+        console.log(e);
       }
     },
   },
 
   created() {
     this.keyword = this.$route.query.keyword;
-    this.getPaginatedSearch(this.$route.query.keyword);
+    let category = this.$route.query.category
+    this.getPaginatedSearch(this.keyword, category);
   },
   mounted() { },
 };
 </script>
 
 <style scoped lang="less">
+div {
+  box-sizing: border-box;
+}
+
 .pdf-icon {
   display: inline;
-  height: 20px;
-  min-width: 40px;
+  height: 12px;
+  font-size: 12px;
+  // min-width: 40px;
   cursor: pointer;
   color: white;
-  font-weight: bolder;
+  // font-weight: bolder;
   border-radius: 10px;
-  padding: 0px 10px;
+  padding: 0px;
   text-align: right;
-  line-height: 20px;
+  // line-height: 20px;
   background-color: #FECFCB;
   white-space: nowrap;
 }
@@ -379,9 +499,9 @@ export default {
   width: 100vw;
   height: 100vh;
   box-sizing: border-box;
-  padding: 10px 10px 20px 20px;
-  overflow-y: auto;
-  overflow-x: hidden;
+  padding: 0px;
+  overflow: auto;
+  // overflow-x: hidden;
   display: flex;
   flex-direction: column;
 
@@ -390,7 +510,9 @@ export default {
     flex: 0 0 auto;
     display: flex;
     align-items: center;
-    padding: 0px 5px;
+    padding: 0px 5px 20px;
+    border-bottom: 1px solid #e0e0e0;
+    margin: 0px 15px;
 
     // min-width: 500px;
     .back {
@@ -427,37 +549,44 @@ export default {
   }
 
   .entry-content {
-    margin-top: 20px;
-    border-top: 1px solid #e0e0e0;
+    // margin-top: 20px;
+    // margin: 20px 15px 0px 15px;
+
     display: flex;
     flex-wrap: nowrap;
     flex: 1 1 auto;
-    overflow: hidden;
+    overflow-y: auto;
+    // overflow-x: hidden;
+    box-sizing: border-box;
+    padding: 0px 15px;
+
+    &::-webkit-scrollbar {
+      // display: none;
+      width: 10px;
+      height: 10px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background-color: rgb(226, 218, 218);
+      border-radius: 10px;
+    }
+
+    &::-webkit-scrollbar-track {
+      border-radius: 2px;
+    }
 
     .left {
       // flex: 1 1 1;
       // background: skyblue;
-      overflow-y: auto;
-      overflow-x: hidden;
+      // overflow-y: auto;
+      // overflow-x: hidden;
       width: 50%;
-      height: 100%;
+      // height: 100%;
 
       min-width: 500px;
       padding-top: 10px;
 
-      &::-webkit-scrollbar {
-        // display: none;
-        width: 10px;
-      }
-
-      &::-webkit-scrollbar-thumb {
-        background-color: rgb(226, 218, 218);
-        border-radius: 10px;
-      }
 
-      &::-webkit-scrollbar-track {
-        border-radius: 2px;
-      }
 
       .result-num {
         padding-left: 5px;
@@ -503,7 +632,7 @@ export default {
           // display: flex;
           flex-wrap: wrap;
           overflow: hidden;
-          transition: max-height 0.5s linear;
+          transition: max-height 1s linear;
 
           .record-content-box-item1 {
             display: flex;
@@ -601,19 +730,24 @@ export default {
       // flex: 0 0 1;
       // background: orange;
       padding: 10px 5px 5px 30px;
+      position: sticky;
+      /* 使右边盒子固定 */
+      top: 0;
 
+      /* 顶部固定 */
       .aside {
         position: relative;
         height: 100%;
+        width: 100%;
         display: flex;
         flex-direction: column;
 
         .pdf {
           position: absolute;
-          top: 0px;
-          width: 100%;
+          top: -18px;
+          width: calc(100% + 20px);
           min-height: 500px;
-          height: calc(100vh - 130px);
+          height: calc(100vh - 80px);
           // overflow: auto;
           overflow: hidden;
           background: white;
@@ -692,6 +826,12 @@ export default {
     display: inline-block;
   }
 
+  .reference-source {
+    color: red;
+    font-size: 20px;
+    margin-left: 20px;
+  }
+
   .el-dialog__body {
     // background-color: #F9F9F8;
     // border-radius: 10px;

+ 4 - 4
src/views/KnowledgeGraphRetrievalTool/EvidenceBased.vue

@@ -219,7 +219,7 @@ export default {
     },
     getTextSearch: async function (text) {
       try {
-        let resData = await this.axios.post("/kgrt_api/text/search", {
+        let resData = await this.axios.post("/knowledge/text/search", {
           text: text,
           limit: 1,
         });
@@ -234,7 +234,7 @@ export default {
         this.evidence.references[+index - 1].content = this.evidence.references[
           +index - 1
         ].content.replace(/<\/?[^>]+(>|$)/g, "");
-        let resData = await this.axios.post("/kgrt_api/text/match", {
+        let resData = await this.axios.post("/knowledge/text/match", {
           text: this.evidence.references[+index - 1].content,
           sentence: this.evidence.answer[+sentenceIndex],
         });
@@ -255,7 +255,7 @@ export default {
     },
     getTextMrSearch: async function (text) {
       try {
-        let resData = await this.axios.post("/kgrt_api/text/mr_search", {
+        let resData = await this.axios.post("/knowledge/text/mr_search", {
           text,
         });
         // console.log("text", text, text.replace("\\n", "<br>"));
@@ -285,7 +285,7 @@ export default {
           // console.log("URL 解码错误:", e);
         } finally {
           console.log(index);
-          let resData = await this.axios.post("/kgrt_api/text/mr_match", {
+          let resData = await this.axios.post("/knowledge/text/mr_match", {
             origin: decodedData,
             similar: this.evidence.references[+index].content,
           });

+ 9 - 3
src/views/KnowledgeGraphRetrievalTool/index.vue

@@ -3,7 +3,13 @@
   <router-view></router-view>
 </template>
 
-<script></script>
+<script>
+export default {
+  name: "KGRT",
+  data() {
+    return {}
+  }
+}
+</script>
 
-<style>
-</style>
+<style lang="less" scoped></style>

+ 4 - 4
vue.config.js

@@ -28,13 +28,13 @@ module.exports = {
           "^/other_api": ""
         }
       },
-      '/kgrt_api': {
+      '/knowledge': {
         target: "http://173.18.12.205:8001",
         changeOrigin: true,
         secure: false,
-        pathRewrite: {
-          "^/kgrt_api": ""
-        }
+        // pathRewrite: {
+        //   "^/kgrt_api": ""
+        // }
       },
       '/pdf_api': {
         target: "http://173.18.12.197:9200",