pdf 搜索优化

This commit is contained in:
cwchen 2025-11-27 18:06:19 +08:00
parent 7ce5ccdda6
commit 0db26d2459
1 changed files with 111 additions and 25 deletions

View File

@ -1,31 +1,36 @@
<template>
<div class="document-search">
<div class="viewer-container">
<div class="floating-search">
<div class="search-toolbar">
<div class="search-box">
<el-input v-model="keyword" class="search-input" placeholder="输入关键字搜索" clearable
@keyup.enter.native="handleSearch" @clear="resetSearch">
</el-input>
<button class="icon-btn search-icon" :disabled="searching" @click="handleSearch">
<i class="el-icon-search" v-if="!searching"></i>
<i class="el-icon-loading" v-else></i>
</button>
</div>
<div class="search-status">
<span class="result-indicator">{{ resultDisplay }}</span>
<button class="icon-btn" :disabled="!searchResults.length" @click="goToPrevious">
<i class="el-icon-arrow-up"></i>
</button>
<button class="icon-btn" :disabled="!searchResults.length" @click="goToNext">
<i class="el-icon-arrow-down"></i>
</button>
<button class="icon-btn" :disabled="!keyword" @click="resetSearch">
<i class="el-icon-close"></i>
</button>
<transition name="slide-fade">
<div v-if="showSearchBar" class="floating-search">
<div class="search-toolbar" :class="{ 'is-searching': searching }">
<div class="search-box">
<el-input v-model="keyword" class="search-input" placeholder="输入关键字搜索" clearable
ref="keywordInput" @keyup.enter.native="handleSearch" @clear="resetSearch">
</el-input>
<button class="icon-btn search-icon" :disabled="searching" @click="handleSearch">
<i class="el-icon-search" v-if="!searching"></i>
<i class="el-icon-loading" v-else></i>
</button>
</div>
<div class="search-status">
<span class="result-indicator">{{ resultDisplay }}</span>
<button class="icon-btn" :disabled="!searchResults.length" @click="goToPrevious">
<i class="el-icon-arrow-up"></i>
</button>
<button class="icon-btn" :disabled="!searchResults.length" @click="goToNext">
<i class="el-icon-arrow-down"></i>
</button>
<button class="icon-btn" @click="handleCloseSearch">
<i class="el-icon-close"></i>
</button>
</div>
</div>
</div>
</div>
</transition>
<button v-if="!showSearchBar" class="floating-search-btn" @click="toggleSearchBar">
<i class="el-icon-search"></i>
</button>
<div ref="pdfWrapper" class="pdf-wrapper"></div>
<transition name="fade">
@ -77,7 +82,8 @@ export default {
fileUrl: {
type: String,
default: ''
}
},
showSearchBar: true
},
computed: {
overlayType() {
@ -178,6 +184,19 @@ export default {
},
},
methods: {
toggleSearchBar() {
this.showSearchBar = true
this.$nextTick(() => {
if (this.$refs.keywordInput) {
this.$refs.keywordInput.focus()
}
})
},
handleCloseSearch() {
this.keyword = ''
this.resetSearch()
this.showSearchBar = false
},
applyPdfUrl(url) {
const resolved = url || ''
if (resolved === this.pdfUrl) {
@ -1156,10 +1175,35 @@ export default {
right: 20px;
width: auto;
max-width: calc(100% - 40px);
z-index: 5;
z-index: 6;
pointer-events: none;
}
.floating-search-btn {
position: absolute;
top: 20px;
right: 20px;
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: #2b68ff;
color: #fff;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 26px rgba(31, 114, 234, 0.25);
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
z-index: 5;
}
.floating-search-btn:hover {
transform: translateY(-1px);
box-shadow: 0 12px 30px rgba(31, 114, 234, 0.3);
}
.search-toolbar {
display: flex;
align-items: center;
@ -1170,6 +1214,48 @@ export default {
box-shadow: 0 8px 26px rgba(31, 114, 234, 0.18);
border: 1px solid rgba(226, 231, 239, 0.8);
pointer-events: auto;
position: relative;
overflow: hidden;
}
.search-toolbar::after {
content: '';
position: absolute;
inset: 4px;
border-radius: 999px;
border: 1px solid transparent;
pointer-events: none;
}
.search-toolbar.is-searching::after {
border-color: rgba(43, 104, 255, 0.4);
animation: search-pulse 1.2s ease-in-out infinite;
}
@keyframes search-pulse {
0% {
opacity: 0.25;
transform: scale(0.98);
}
50% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0.25;
transform: scale(0.98);
}
}
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
transform: translateY(-6px);
}
.search-box {