搜索修改
This commit is contained in:
parent
2f3f56fbc2
commit
148c189a0a
|
|
@ -3,7 +3,9 @@
|
||||||
<div class="search-toolbar">
|
<div class="search-toolbar">
|
||||||
<el-input v-model="keyword" class="search-input" placeholder="输入关键字搜索 PDF" clearable
|
<el-input v-model="keyword" class="search-input" placeholder="输入关键字搜索 PDF" clearable
|
||||||
@keyup.enter.native="handleSearch" @clear="resetSearch">
|
@keyup.enter.native="handleSearch" @clear="resetSearch">
|
||||||
<el-button slot="append" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
<el-button slot="append" icon="el-icon-search" @click="handleSearch" :loading="searching" :disabled="searching">
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
</el-input>
|
</el-input>
|
||||||
<div class="search-status" v-if="searchResults.length">
|
<div class="search-status" v-if="searchResults.length">
|
||||||
<span class="result-indicator">{{ currentResultIndex + 1 }}/{{ searchResults.length }}</span>
|
<span class="result-indicator">{{ currentResultIndex + 1 }}/{{ searchResults.length }}</span>
|
||||||
|
|
@ -106,6 +108,7 @@ export default {
|
||||||
cMapUrl: '',
|
cMapUrl: '',
|
||||||
standardFontDataUrl: '',
|
standardFontDataUrl: '',
|
||||||
pdfAssetsBase: '',
|
pdfAssetsBase: '',
|
||||||
|
searching: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -510,25 +513,52 @@ export default {
|
||||||
container.classList.add('prefetched')
|
container.classList.add('prefetched')
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSearch: debounce(function () {
|
handleSearch: debounce(async function () {
|
||||||
if (!this.keyword) {
|
|
||||||
this.resetSearch()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const hasRenderedContent = this.pageTextDivs.some((divs) => divs && divs.length)
|
|
||||||
if (!hasRenderedContent) {
|
|
||||||
this.$message.warning('页面正在准备内容,请稍后或滚动加载后再试')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.highlightMatches()
|
|
||||||
}, 200),
|
|
||||||
|
|
||||||
highlightMatches() {
|
|
||||||
const keyword = this.keyword.trim()
|
const keyword = this.keyword.trim()
|
||||||
if (!keyword) {
|
if (!keyword) {
|
||||||
this.resetSearch()
|
this.resetSearch()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!this.pdfDoc) return
|
||||||
|
|
||||||
|
if (this.searching) return
|
||||||
|
this.searching = true
|
||||||
|
try {
|
||||||
|
const allPrepared = this.pageTextDivs.length === this.totalPages && this.pageTextDivs.every(items => items && items.length)
|
||||||
|
if (!allPrepared) {
|
||||||
|
try {
|
||||||
|
await this.ensureAllTextLayersLoaded()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('全文预处理失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.highlightMatches()
|
||||||
|
} finally {
|
||||||
|
this.searching = false
|
||||||
|
}
|
||||||
|
}, 200),
|
||||||
|
|
||||||
|
async ensureAllTextLayersLoaded() {
|
||||||
|
if (!this.pdfDoc || !this.totalPages) return
|
||||||
|
let total = this.totalPages
|
||||||
|
for (let pageNumber = 1; pageNumber <= total; pageNumber += 1) {
|
||||||
|
if (this.totalPages !== total) break
|
||||||
|
if (!this.pageTextDivs[pageNumber - 1] || !this.pageTextDivs[pageNumber - 1].length) {
|
||||||
|
await this.renderTextLayer(pageNumber, { visible: pageNumber === 1, force: true })
|
||||||
|
await this.$nextTick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
highlightMatches(options = {}) {
|
||||||
|
const { preserveIndex = false, skipNavigate = false } = options
|
||||||
|
const keyword = this.keyword.trim()
|
||||||
|
if (!keyword) {
|
||||||
|
this.resetSearch()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousIndex = preserveIndex ? this.currentResultIndex : 0
|
||||||
|
|
||||||
this.clearHighlights()
|
this.clearHighlights()
|
||||||
const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||||
|
|
@ -554,6 +584,7 @@ export default {
|
||||||
div.innerHTML = highlighted
|
div.innerHTML = highlighted
|
||||||
const marks = div.querySelectorAll('mark.search-highlight')
|
const marks = div.querySelectorAll('mark.search-highlight')
|
||||||
marks.forEach((mark) => {
|
marks.forEach((mark) => {
|
||||||
|
mark.dataset.matchIndex = results.length
|
||||||
results.push({
|
results.push({
|
||||||
pageIndex,
|
pageIndex,
|
||||||
element: mark,
|
element: mark,
|
||||||
|
|
@ -569,14 +600,36 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentResultIndex = 0
|
if (preserveIndex && previousIndex < results.length && previousIndex >= 0) {
|
||||||
this.focusCurrentResult()
|
this.currentResultIndex = previousIndex
|
||||||
|
} else {
|
||||||
|
this.currentResultIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipNavigate) {
|
||||||
|
this.navigateToResult(this.currentResultIndex, false)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
focusCurrentResult() {
|
focusCurrentResult() {
|
||||||
if (!this.searchResults.length || this.currentResultIndex < 0) return
|
this.navigateToResult(this.currentResultIndex)
|
||||||
this.searchResults.forEach((item, index) => {
|
},
|
||||||
if (index === this.currentResultIndex) {
|
|
||||||
|
async navigateToResult(index, ensureRendered = false) {
|
||||||
|
if (!this.searchResults.length || index < 0 || index >= this.searchResults.length) return
|
||||||
|
const currentResult = this.searchResults[index]
|
||||||
|
const pageNumber = currentResult.pageIndex + 1
|
||||||
|
|
||||||
|
if (ensureRendered || !this.pageTextDivs[pageNumber - 1] || !this.pageTextDivs[pageNumber - 1].length) {
|
||||||
|
await this.renderTextLayer(pageNumber, { visible: true, force: true })
|
||||||
|
await this.renderCanvas(pageNumber)
|
||||||
|
await this.$nextTick()
|
||||||
|
this.highlightMatches({ preserveIndex: true, skipNavigate: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentResultIndex = index
|
||||||
|
this.searchResults.forEach((item, idx) => {
|
||||||
|
if (idx === this.currentResultIndex) {
|
||||||
item.element.classList.add('is-active')
|
item.element.classList.add('is-active')
|
||||||
} else {
|
} else {
|
||||||
item.element.classList.remove('is-active')
|
item.element.classList.remove('is-active')
|
||||||
|
|
@ -585,30 +638,30 @@ export default {
|
||||||
|
|
||||||
const target = this.searchResults[this.currentResultIndex]?.element
|
const target = this.searchResults[this.currentResultIndex]?.element
|
||||||
if (!target) return
|
if (!target) return
|
||||||
|
|
||||||
const wrapper = this.$refs.pdfWrapper
|
const wrapper = this.$refs.pdfWrapper
|
||||||
if (!wrapper) return
|
if (!wrapper) return
|
||||||
const targetRect = target.getBoundingClientRect()
|
|
||||||
const wrapperRect = wrapper.getBoundingClientRect()
|
|
||||||
|
|
||||||
const offset = targetRect.top - wrapperRect.top - wrapper.clientHeight / 2
|
this.$nextTick(() => {
|
||||||
wrapper.scrollTo({
|
const targetRect = target.getBoundingClientRect()
|
||||||
top: wrapper.scrollTop + offset,
|
const wrapperRect = wrapper.getBoundingClientRect()
|
||||||
behavior: 'smooth',
|
const offset = targetRect.top - wrapperRect.top - wrapper.clientHeight / 2
|
||||||
|
wrapper.scrollTo({
|
||||||
|
top: wrapper.scrollTop + offset,
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
goToPrevious() {
|
goToPrevious() {
|
||||||
if (!this.searchResults.length) return
|
if (!this.searchResults.length) return
|
||||||
this.currentResultIndex =
|
const idx = (this.currentResultIndex - 1 + this.searchResults.length) % this.searchResults.length
|
||||||
(this.currentResultIndex - 1 + this.searchResults.length) % this.searchResults.length
|
this.navigateToResult(idx)
|
||||||
this.focusCurrentResult()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
goToNext() {
|
goToNext() {
|
||||||
if (!this.searchResults.length) return
|
if (!this.searchResults.length) return
|
||||||
this.currentResultIndex = (this.currentResultIndex + 1) % this.searchResults.length
|
const idx = (this.currentResultIndex + 1) % this.searchResults.length
|
||||||
this.focusCurrentResult()
|
this.navigateToResult(idx)
|
||||||
},
|
},
|
||||||
|
|
||||||
resetSearch() {
|
resetSearch() {
|
||||||
|
|
@ -755,9 +808,17 @@ export default {
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-preparing {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
color: #506dff;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.viewer-container {
|
.viewer-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%;
|
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue