Browse Source

fix: 新增 search

liutian 1 year ago
parent
commit
3b31dc8d8b

+ 1 - 50
packages/core/src/index.js

@@ -377,6 +377,7 @@ class ComPDFKitViewer {
           //   });
         })
         await this.load(pdfDocument);
+        this.search('PDF')
         return { pwd: !!this.#pwd };
       },
       reason => {
@@ -840,7 +841,6 @@ class ComPDFKitViewer {
     eventBus._on("switchannotationeditormode", this.webViewerSwitchAnnotationEditorMode.bind(this));
     eventBus._on("annotationsCountChanged", this.webViewerAnnotationsCountChanged.bind(this))
     eventBus._on("distanceChanged", this.webViewerDistanceChanged.bind(this));
-    eventBus._on("search", this.webViewerSearch.bind(this));
 
     eventBus._on("annotationChange", this.handleAnnotationChange.bind(this));
     eventBus._on("createSignature", this.handleCreateSignature.bind(this));
@@ -912,10 +912,6 @@ class ComPDFKitViewer {
     this._pdfId = null
     this.annotations = null
 
-    if (this.findBar && this.findBar.opened) {
-      this.findBar.close();
-    }
-
     await Promise.all(promises);
   }
 
@@ -1034,10 +1030,6 @@ class ComPDFKitViewer {
     this.addAnnotations(data)
   }
 
-  webViewerSearch(value) {
-    this.findBar?.dispatchEvent("", false, value)
-  }
-
   webViewerDistanceChanged(evt) {
     this.distanceChangedCallback(evt.distance)
   }
@@ -1258,23 +1250,6 @@ class ComPDFKitViewer {
       pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
     }
 
-    this.findBar = new PDFFindBar({
-      bar: document.getElementById("findbar"),
-      toggleButton: document.getElementById("viewFind"),
-      findField: document.getElementById("findInput"),
-      emptyInput: document.getElementById("emptyInput"),
-      highlightAllCheckbox: document.getElementById("findHighlightAll"),
-      caseSensitiveCheckbox: document.getElementById("findMatchCase"),
-      matchDiacriticsCheckbox: document.getElementById("findMatchDiacritics"),
-      entireWordCheckbox: document.getElementById("findEntireWord"),
-      findMsg: document.getElementById("findMsg"),
-      findResultsCount: document.getElementById("findResultsCount"),
-      findPreviousButton: document.getElementById("findPrevious"),
-      findNextButton: document.getElementById("findNext"),
-      findbarView,
-      $t: this.$t
-    }, this.eventBus);
-
     this.pdfCursorTools = new PDFCursorTools({
       container,
       eventBus: this.eventBus,
@@ -1609,30 +1584,6 @@ class ComPDFKitViewer {
         this.appConfig.toolbar.pageNumber.select();
         break;
 
-      case "Find":
-        if (!this.supportsIntegratedFind) {
-          if (!evt.close || (evt.close && evt.close === 4 && this.findBar.opened)) {
-            this.findBar.toggle();
-          }
-        }
-        break;
-
-      case "FindOpen":
-        if (!this.supportsIntegratedFind) {
-          if (!evt.close || (evt.close && evt.close === 4 && this.findBar.opened)) {
-            this.findBar.open();
-          }
-        }
-        break;
-
-      case "FindClose":
-        if (!this.supportsIntegratedFind) {
-          if (!evt.close || (evt.close && evt.close === 4 && this.findBar.opened)) {
-            this.findBar.close();
-          }
-        }
-        break;
-
       case "Print":
         const url = await this.download(false, null, 3)
         this.triggerPrinting(url);

+ 7 - 5
packages/core/src/worker/compdfkit_worker.js

@@ -503,8 +503,10 @@ class CPDFWorker {
 
       const searchResults = []
       for (let i = 0; i < pagesPtr.length; i++) {
-        const pagePtr = pagesPtr[i]
-        const textFindPtr = Module._InitTextFind(pagePtr.textPtr)
+        const ptr = pagesPtr[i]
+        const pagePtr = ptr.pagePtr
+        const textPtr = ptr.textPtr
+        const textFindPtr = Module._InitTextFind(textPtr)
         const res = Module._FindStart(textPtr, textFindPtr, text, 0, 0)
 
         let resFind = 1
@@ -513,15 +515,15 @@ class CPDFWorker {
           resFind = Module._FindNext(pagePtr, textPtr, textFindPtr)
 
           if (resFind) {
-            !searchResults[i] && (searchResults[i] = [])
             const { Left: left, Top: top, Right: right, Bottom: bottom, Content: content } = TextFindItem
-            searchResults[i].push({
+            searchResults.push({
               pageNum: i + 1,
               left,
               top,
               right,
               bottom,
-              content
+              content,
+              searchValue: value
             })
           }
         } while (resFind)

+ 1 - 0
packages/webview/package.json

@@ -15,6 +15,7 @@
     "axios": "^0.27.2",
     "dayjs": "^1.11.6",
     "file-saver": "^2.0.5",
+    "lodash.debounce": "^4.0.8",
     "pinia": "^2.0.36",
     "vue": "^3.2.41",
     "vue-i18n": "9"

+ 0 - 110
packages/webview/src/components/FindbarContainer/FindbarContent.vue

@@ -1,110 +0,0 @@
-<template>
-  <div v-show="searchResults.length" class="findbar-number"><div id="findResultsCount" class="toolbarLabel"></div><div>{{ $t('leftPanel.page') }}</div></div>
-  <div class="findbar-view">
-    <template v-if="searchResults.length" v-for="(results, pageNumber) in searchResults">
-      <div v-if="results">
-        <div class="page-title">{{ pageNumber * 1 + 1 }}</div>
-        <template v-for="(item) in results">
-          <div class="item-content" v-html="item"></div>
-        </template>
-      </div>
-    </template>
-    <div v-else class="no-annotations">{{ $t('leftPanel.noResults') }}</div>
-  </div>
-</template>
-
-<script setup>
-  import { computed, ref, getCurrentInstance } from 'vue';
-  import dayjs from 'dayjs'
-  import core from '@/core'
-  import { useDocumentStore } from '@/stores/modules/document'
-
-  const searchResults = ref('')
-
-  const setSearchList = (results) => {
-    console.log(results)
-    searchResults.value = results
-    const instance = getCurrentInstance();
-    instance?.proxy?.$forceUpdate();
-  }
-  core.addEvent('updateFindMatches', setSearchList)
-
-  const goToPage = (page) => {
-    core.pageNumberChanged({
-      value: (page * 1 + 1).toString()
-    })
-  }
-</script>
-
-<style lang="scss">
-  .findbar-number {
-    display: flex;
-    justify-content: space-between;
-    padding: 8px 16px;
-    font-size: 14px;
-    line-height: 16px;
-    color: var(--c-side-text);
-    background-color: var(--c-findbar-page-number-bg);
-  }
-  .findbar-view {
-    position: relative;
-    height: calc(100% - 95px);
-    overflow: auto;
-    .page-title {
-      padding: 8px 16px;
-      color: var(--c-side-title);
-      background-color: var(--c-findbar-bg);
-      font-size: 14px;
-      line-height: 16px;
-      text-align: right;
-    }
-    .item-content {
-      padding: 6px 16px;
-      color: var(--c-side-text);
-      font-size: 14px;
-      line-height: 20px;
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      .highlight {
-        background: rgba(255, 236, 102, 1);
-      }
-    }
-    .findbar-item {
-      padding: 4px 16px 12px;
-      .item-header {
-        display: flex;
-        align-items: center;
-        padding: 6px 0;
-        color: var(--c-side-text);
-        span {
-          margin-left: 8px;
-          font-size: 14px;
-          line-height: 20px;
-        }
-      }
-      .item-content {
-        font-size: 14px;
-        line-height: 20px;
-        text-overflow: ellipsis;
-        display: -webkit-box;
-        -webkit-box-orient: vertical;
-        -webkit-line-clamp: 2;
-        overflow: hidden;
-        color: var(--c-side-findbar-text);
-        word-break: break-all;
-      }
-    }
-    .no-annotations {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, 50%);
-      color: var(--c-side-text);
-      text-align: center;
-      font-weight: 700;
-      font-size: 14px;
-      line-height: 16px;
-    }
-  }
-</style>

+ 0 - 172
packages/webview/src/components/FindbarContainer/FindbarHeader.vue

@@ -1,172 +0,0 @@
-<template>
-  <div id="findbar" class="findbar">
-    <div id="findbarInputContainer" class="findbar-input-container">
-      <div class="input-container">
-        <input id="findInput" class="toolbarField" :placeholder="$t('leftPanel.searchPdf')">
-        <EmptyInput id="emptyInput" class="empty-input hidden" />
-      </div>
-      <div class="button-container">
-        <Button
-          img="icon-previous-left"
-          id="findPrevious"
-          class="toolbarButton disabled"
-          :title="$t('leftPanel.previousPhrase')"
-        ><ArrowPrev />
-        </Button>
-        <Button
-          img="icon-next-right"
-          id="findNext"
-          class="toolbarButton disabled"
-          :title="$t('leftPanel.nextPhrase')"
-        ><ArrowNext />
-        </Button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-  import core from '@/core'
-  import { computed, ref } from 'vue';
-  import { useDocumentStore } from '@/stores/modules/document'
-  import { useViewerStore } from '@/stores/modules/viewer'
-  import { uploadFile } from '@/helpers/utils'
- 
-  const useDocument = useDocumentStore()
-  const useViewer = useViewerStore()
-  const { removeAllAnnotations, importAnnotations, downloadXfdf } = core
-  const onImport = () => {
-    
-    uploadFile('.xfdf').then((data) => {
-      importAnnotations(data)
-    })
-  }
-
-  const annotations = computed(() => useDocument.getAllAnnotations)
-  const optionUrl = computed(() => useDocument.getOptionUrl)
-  const info = computed(() => useDocument.info)
-  const fileName = info.value && info.value.token && atob(info.value.token).replace('.pdf', '.xfdf');
-  const fetchWithBlob = (uri) =>
-    fetch(uri, {
-      responseType: 'blob',
-    }).then((res) => {
-      if (!res.ok) {
-        return Promise.reject({
-          status: res.status,
-          message: res.statusText
-        })
-      }
-      return res.blob()
-    }).catch((error) => {
-      return Promise.reject({ error: true, message: error.message })
-    });
-
-  const onExport = async () => {
-    // if (annotationsCount.value < 1) return
-    // const outputPath = `${optionUrl.value.baseUrl}${optionUrl.value.getAnnotations}?f=${info.value.token}`
-    // fetchWithBlob(outputPath)
-    // .then(data => {
-    //   const blob = new Blob([data], { type: "application/vnd.adobe.xfdf" });
-    //   downloadFileWithUri(fileName, outputPath, blob);
-    // })
-    try {
-      useViewer.setDownloading(true)
-      const res = await downloadXfdf()
-      useViewer.setDownloading(false)
-      if (!res) {
-        useViewer.setDownloadError('Downloaded')
-      }
-    } catch (error) {
-      console.log(error)
-      useViewer.setDownloading(false)
-      useViewer.setDownloadError('Downloaded')
-    }
-  }
-
-
-const downloadFileWithUri = (name, uri, blob) => {
-  if (!blob) {
-    // URL.createObjectURL is not supported
-    download(uri, name);
-    return;
-  }
-  const blobUrl = URL.createObjectURL(blob);
-  download(blobUrl, name);
-};
-
-function download(blobUrl, filename) {
-  const a = document.createElement("a");
-  if (!a.click) {
-    throw new Error('DownloadManager: "a.click()" is not supported.');
-  }
-  a.href = blobUrl;
-  a.target = "_blank";
-  // Use a.download if available. This increases the likelihood that
-  // the file is downloaded instead of opened by another PDF plugin.
-  if ("download" in a) {
-    a.download = filename;
-  }
-  // <a> must be in the document for recent Firefox versions,
-  // otherwise .click() is ignored.
-  (document.body || document.documentElement).appendChild(a);
-  a.click();
-  a.remove();
-}
-
-  const deleteAll = () => {
-    removeAllAnnotations()
-  }
-</script>
-
-<style lang="scss">
-  .findbar {
-    padding: 8px 0 8px 8px;
-    .findbar-input-container {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-    }
-    .input-container {
-      position: relative;
-      flex-grow: 1;
-      display: flex;
-      width: 180px;
-      padding: 0 8px;
-      border-radius: 1px;
-      border: 1px solid var(--c-findbar-input-border);
-      background-color: var(--c-findbar-input-bg);
-    }
-    input {
-      flex-grow: 1;
-      width: 0;
-      height: 30px;
-      outline: none;
-      padding: 5px 8px;
-      box-shadow: none;
-      border: none;
-      background-color: var(--c-findbar-input-bg);
-      color: var(--c-findbar-text);
-    }
-    .empty-input {
-      position: absolute;
-      right: 4px;
-      top: 7px;
-      cursor: pointer;
-    }
-    .toolbarLabel {
-      font-size: 14px;
-      line-height: 30px;
-      color: var(--c-findbar-text);
-      white-space: nowrap;
-    }
-    .button-container {
-      display: flex;
-      align-items: center;
-      button {
-        margin-left: 8px;
-        padding: 0;
-        color: var(--c-findbar-text);
-      }
-    }
-  }
-</style>

+ 1 - 1
packages/webview/src/components/HeaderItems/HeaderItems.vue

@@ -52,7 +52,7 @@
 
     <div class="right-container">
       <template v-for="(item, index) in rightItems" :key="`${item.type}-${item.dataElement || index}`">
-        <SearchContainer v-if="item.type === 'searchButton'" :item="item" />
+        <SearchButton v-if="item.type === 'searchButton'" :item="item" />
         <ToggleRightPanelButton v-if="item.type === 'toggleRightPanelButton'" :item="item" :disabled="!load" />
         <OpenFileButton v-if="item.type === 'openFileButton'" :item="item" />
       </template>

+ 1 - 2
packages/webview/src/components/LeftPanel/LeftPanel.vue

@@ -27,8 +27,7 @@
       </div>
     </div>
     <div class="findbar-container" :class="{ hidden: activePanelTab !== 'SEARCH' }">
-      <FindbarHeader />
-      <FindbarContent />
+      <SearchContainer />
     </div>
   </div>
 </template>

+ 45 - 0
packages/webview/src/components/SearchButton/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="search-container">
+    <Button
+      id="viewFind"
+      v-bind="{ ...item }"
+      :isActive="searchStatus && isLeftPanelOpen"
+      :onClick="onClick"
+      :class="{ disabled: !load }"
+      :title="$t('header.search')"
+    >
+      <Search />
+    </Button>
+  </div>
+</template>
+
+<script setup>
+  import { ref, computed } from 'vue'
+  import { useViewerStore } from '@/stores/modules/viewer'
+  import core from '@/core'
+  const { item } = defineProps(['item'])
+  
+  const useViewer = useViewerStore()
+
+  const searchStatus = computed(() => useViewer.getSearchStatus)
+  const load = computed(() => useViewer.getUpload)
+
+  const isLeftPanelOpen = computed(() => useViewer.isElementOpen('leftPanel'))
+  const activeLeftPanelTab = computed(() => useViewer.getActiveElementTab('leftPanelTab'))
+
+  const onClick = () => {
+    core.webViewerNamedAction('Find')
+    if (isLeftPanelOpen.value && activeLeftPanelTab.value !== 'SEARCH') {
+      useViewer.setSearchStatus(true)
+    } else if (isLeftPanelOpen.value) {
+      useViewer.setSearchStatus(false)
+      useViewer.toggleElement('leftPanel')
+      core.toggleSidebar()
+    } else {
+      useViewer.setSearchStatus(true)
+      useViewer.toggleElement('leftPanel')
+      core.toggleSidebar()
+    }
+    useViewer.setActiveElementTab('leftPanelTab', 'SEARCH')
+  }
+</script>

+ 46 - 0
packages/webview/src/components/SearchContainer/SearchContent.vue

@@ -0,0 +1,46 @@
+<template>
+  <div v-show="searchResults.length" class="search-number"><div id="findResultsCount" class="toolbarLabel"></div><div>{{ $t('leftPanel.page') }}</div></div>
+  <div class="search-container">
+    <template v-if="searchResults.length" v-for="(result, currentIndex) in searchResults">
+      <SearchItem :searchResults="searchResults" :result="result" :currentIndex="currentIndex" />
+    </template>
+    <div v-else class="no-annotations">{{ $t('leftPanel.noResults') }}</div>
+  </div>
+</template>
+
+<script setup>
+  import { computed } from 'vue';
+  import { useDocumentStore } from '@/stores/modules/document'
+
+  const useDocument = useDocumentStore()
+
+  const searchResults = computed(() => useDocument.getSearchResults)
+</script>
+
+<style lang="scss">
+  .search-number {
+    display: flex;
+    justify-content: space-between;
+    padding: 8px 16px;
+    font-size: 14px;
+    line-height: 16px;
+    color: var(--c-side-text);
+    background-color: var(--c-findbar-page-number-bg);
+  }
+  .search-container {
+    position: relative;
+    height: calc(100% - 95px);
+    overflow: auto;
+    .no-annotations {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, 50%);
+      color: var(--c-side-text);
+      text-align: center;
+      font-weight: 700;
+      font-size: 14px;
+      line-height: 16px;
+    }
+  }
+</style>

+ 113 - 0
packages/webview/src/components/SearchContainer/SearchHeader.vue

@@ -0,0 +1,113 @@
+<template>
+  <div id="findbar" class="findbar">
+    <div id="findbarInputContainer" class="findbar-input-container">
+      <div class="input-container">
+        <input type="text" autocomplete="off" :placeholder="$t('leftPanel.searchPdf')" v-model="searchValue">
+        <EmptyInput id="emptyInput" class="empty-input" :class="!searchValue && 'hidden'" @click="clearSearchResults" />
+      </div>
+      <div class="button-container">
+        <Button
+          img="icon-previous-left"
+          id="findPrevious"
+          class="toolbarButton disabled"
+          :title="$t('leftPanel.previousPhrase')"
+        ><ArrowPrev />
+        </Button>
+        <Button
+          img="icon-next-right"
+          id="findNext"
+          class="toolbarButton disabled"
+          :title="$t('leftPanel.nextPhrase')"
+        ><ArrowNext />
+        </Button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import debounce from 'lodash.debounce'
+  import { ref, watch } from "vue"
+  import core from '@/core'
+  import { useDocumentStore } from '@/stores/modules/document'
+
+  const waitTime = 200
+  const isSearching = ref(false)
+  const searchValue = ref('')
+  const useDocument = useDocumentStore()
+
+  const clearSearchResults = () => {
+    isSearching.value = false
+    searchValue.value = ''
+    useDocument.setSearchResults([])
+  }
+  const debouncedSearch = debounce(async (searchValue) => {
+    if (searchValue && searchValue.length > 0) {
+      isSearching.value = true
+      const searchResults = await core.search(searchValue)
+      console.log(...searchResults)
+      useDocument.setSearchResults([...searchResults])
+    } else {
+      clearSearchResults()
+    }
+  }, waitTime)
+  const textInputChange = (searchValue) => {
+    debouncedSearch(searchValue)
+  }
+  watch(searchValue, () => {
+    textInputChange(searchValue.value)
+  })
+</script>
+
+<style lang="scss">
+  .findbar {
+    padding: 8px 0 8px 8px;
+    .findbar-input-container {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
+    .input-container {
+      position: relative;
+      flex-grow: 1;
+      display: flex;
+      width: 180px;
+      padding: 0 8px;
+      border-radius: 1px;
+      border: 1px solid var(--c-findbar-input-border);
+      background-color: var(--c-findbar-input-bg);
+    }
+    input {
+      flex-grow: 1;
+      width: 0;
+      height: 30px;
+      outline: none;
+      padding: 5px 8px;
+      box-shadow: none;
+      border: none;
+      background-color: var(--c-findbar-input-bg);
+      color: var(--c-findbar-text);
+    }
+    .empty-input {
+      position: absolute;
+      right: 4px;
+      top: 7px;
+      cursor: pointer;
+    }
+    .toolbarLabel {
+      font-size: 14px;
+      line-height: 30px;
+      color: var(--c-findbar-text);
+      white-space: nowrap;
+    }
+    .button-container {
+      display: flex;
+      align-items: center;
+      button {
+        margin-left: 8px;
+        padding: 0;
+        color: var(--c-findbar-text);
+      }
+    }
+  }
+</style>

+ 52 - 0
packages/webview/src/components/SearchContainer/SearchItem.vue

@@ -0,0 +1,52 @@
+<template>
+  <div v-if="isFirstItem || isDifferentPage" class="page-title">{{ result.pageNum }}</div>
+  <div class="item-content" v-html="content"></div>
+</template>
+
+<script setup>
+import { getCurrentInstance } from 'vue';
+const {searchResults, result, currentIndex} = defineProps(['searchResults', 'result', 'currentIndex'])
+const instance = getCurrentInstance();
+instance?.proxy?.$forceUpdate();
+const previousIndex = currentIndex === 0 ? currentIndex : currentIndex - 1
+const currentItem = searchResults[currentIndex]
+const previousItem = searchResults[previousIndex]
+const isFirstItem = previousItem === currentItem
+const isDifferentPage = previousItem.pageNum !== currentItem.pageNum
+const regex = new RegExp(result.searchValue, "gi")
+const content = result.content.replace(regex,  function (match, capture) {
+  console.log(match, capture)
+  return `<span class="highlight">${match}</span>`
+})
+console.log(searchResults)
+const goToPage = (page) => {
+  core.pageNumberChanged({
+    value: (page * 1 + 1).toString()
+  })
+}
+</script>
+
+<style>
+.search-container {
+  .page-title {
+    padding: 8px 16px;
+    color: var(--c-side-title);
+    background-color: var(--c-findbar-bg);
+    font-size: 14px;
+    line-height: 16px;
+    text-align: right;
+  }
+  .item-content {
+    padding: 6px 16px;
+    color: var(--c-side-text);
+    font-size: 14px;
+    line-height: 20px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    .highlight {
+      background: rgba(255, 236, 102, 1);
+    }
+  }
+}
+</style>

+ 2 - 43
packages/webview/src/components/SearchContainer/index.vue

@@ -1,45 +1,4 @@
 <template>
-  <div class="search-container">
-    <Button
-      id="viewFind"
-      v-bind="{ ...item }"
-      :isActive="searchStatus && isLeftPanelOpen"
-      :onClick="onClick"
-      :class="{ disabled: !load }"
-      :title="$t('header.search')"
-    >
-      <Search />
-    </Button>
-  </div>
+  <SearchHeader />
+  <SearchContent />
 </template>
-
-<script setup>
-  import { ref, computed } from 'vue'
-  import { useViewerStore } from '@/stores/modules/viewer'
-  import core from '@/core'
-  const { item } = defineProps(['item'])
-  
-  const useViewer = useViewerStore()
-
-  const searchStatus = computed(() => useViewer.getSearchStatus)
-  const load = computed(() => useViewer.getUpload)
-
-  const isLeftPanelOpen = computed(() => useViewer.isElementOpen('leftPanel'))
-  const activeLeftPanelTab = computed(() => useViewer.getActiveElementTab('leftPanelTab'))
-
-  const onClick = () => {
-    core.webViewerNamedAction('Find')
-    if (isLeftPanelOpen.value && activeLeftPanelTab.value !== 'SEARCH') {
-      useViewer.setSearchStatus(true)
-    } else if (isLeftPanelOpen.value) {
-      useViewer.setSearchStatus(false)
-      useViewer.toggleElement('leftPanel')
-      core.toggleSidebar()
-    } else {
-      useViewer.setSearchStatus(true)
-      useViewer.toggleElement('leftPanel')
-      core.toggleSidebar()
-    }
-    useViewer.setActiveElementTab('leftPanelTab', 'SEARCH')
-  }
-</script>

+ 3 - 2
packages/webview/src/core/index.js

@@ -45,7 +45,7 @@ import handleCreateStatus from './handleCreateStatus'
 import triggerPrinting from './triggerPrinting'
 import setTextProperty from './setTextProperty'
 import getOutlines from './getOutlines'
-
+import search from './search'
 
 export default {
   getDocumentViewer,
@@ -97,5 +97,6 @@ export default {
   handleCreateStatus,
   triggerPrinting,
   setTextProperty,
-  getOutlines
+  getOutlines,
+  search
 }

+ 3 - 0
packages/webview/src/core/search.js

@@ -0,0 +1,3 @@
+import core from '@/core'
+
+export default (value, docNum) => core.getDocumentViewer(docNum = 1).search(value)

+ 10 - 1
packages/webview/src/stores/modules/document.js

@@ -137,7 +137,8 @@ export const useDocumentStore = defineStore({
     fileHasPwd: false,
     compareResult: null, // 内容对比结果
     outlines: [],
-    activeOutlineId: null
+    activeOutlineId: null,
+    searchResults: []
   }),
   getters: {
     getTotalPages () {
@@ -254,6 +255,9 @@ export const useDocumentStore = defineStore({
     },
     getActiveOutlineId () {
       return this.activeOutlineId
+    },
+    getSearchResults () {
+      return this.searchResults
     }
   },
   actions: {
@@ -263,6 +267,8 @@ export const useDocumentStore = defineStore({
       this.annotationsCount = 0
       this.annotations = null
       this.outlines = []
+      this.activeOutlineId = null
+      this.searchResults = []
     },
     setTotalPages (totalPages) {
       this.totalPages = totalPages
@@ -394,6 +400,9 @@ export const useDocumentStore = defineStore({
     },
     setActiveOutlineId (outlineId) {
       this.activeOutlineId = outlineId
+    },
+    setSearchResults (searchResults) {
+      this.searchResults = searchResults
     }
   }
 })

+ 4 - 1
pnpm-lock.yaml

@@ -79,7 +79,9 @@ importers:
       dayjs: ^1.11.6
       eslint: ^8.22.0
       eslint-plugin-vue: ^9.3.0
+      file-saver: ^2.0.5
       jsdom: ^20.0.1
+      lodash.debounce: ^4.0.8
       naive-ui: ^2.34.3
       pinia: ^2.0.36
       sass: ^1.55.0
@@ -95,6 +97,8 @@ importers:
       '@vueuse/integrations': 9.13.0_axios@0.27.2+vue@3.2.47
       axios: 0.27.2
       dayjs: 1.11.7
+      file-saver: 2.0.5
+      lodash.debounce: 4.0.8
       pinia: 2.0.36_vue@3.2.47
       vue: 3.2.47
       vue-i18n: 9.3.0_vue@3.2.47
@@ -3702,7 +3706,6 @@ packages:
 
   /lodash.debounce/4.0.8:
     resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
-    dev: true
 
   /lodash.merge/4.6.2:
     resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}