提交 87fd5b20 authored 作者: 龙菲's avatar 龙菲

feat: 增加pc端的全文搜索和跳转

上级 e979a9b4
......@@ -38,3 +38,16 @@ export function getDocumentBookmarks(id) {
method: 'get'
});
}
/**
* 全文搜索
* @returns {Promise} 返回文档详情数据
*/
export function searchText(params) {
return request({
url: `/search/text`,
method: 'get',
params
});
}
......@@ -13,12 +13,12 @@ declare module 'vue' {
BookReader: typeof import('./components/BookReader/index.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElRow: typeof import('element-plus/es')['ElRow']
ElText: typeof import('element-plus/es')['ElText']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
FileUpload: typeof import('./components/FileUpload.vue')['default']
GuideMobile: typeof import('./components/GuideMobile/index.vue')['default']
......@@ -28,10 +28,10 @@ declare module 'vue' {
IconEcosystem: typeof import('./components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./components/icons/IconTooling.vue')['default']
IndexFirst: typeof import('./components/PdfViewer/index-first.vue')['default']
PdfViewer: typeof import('./components/PdfViewer/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SearchBar: typeof import('./components/SearchBar/index.vue')['default']
SvgIcon: typeof import('./components/SvgIcon/index.vue')['default']
Test: typeof import('./components/test/index.vue')['default']
VanCell: typeof import('vant/es')['Cell']
......
<template>
<el-drawer
v-loading="loading"
v-model="showDrawer"
title="全文搜索"
direction="ttb"
:before-close="handleClose"
:size="searchResult.length > 0 ? '500px' : '200px'"
>
<div class="wrapper">
<el-input
v-model="keyword"
@keyup.enter="onSearch"
placeholder="请输入关键词进行全文搜索"
clearable
>
<template #append>
<el-button :icon="Search" @click="onSearch" />
</template>
</el-input>
<div class="list">
<div
class="list-item"
v-for="value in searchResult"
:key="value.book_id"
@click="handleLocatePage(value.page_num)"
>
<div v-html="value.text" class="line-clamp-3"></div>
<el-tag type="primary" class="pageNum"
>第{{ value.page_num }}页</el-tag
>
</div>
</div>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { searchText } from "@/api";
import { Search } from "@element-plus/icons-vue";
/* props */
/* emit */
const $emit = defineEmits(["locatePage"]);
/* data */
const showDrawer = ref(false);
const keyword = ref("");
const loading = ref(false);
const searchResult = ref([]);
/* computed */
/* methods */
function show() {
showDrawer.value = true;
}
function handleClose() {
showDrawer.value = false;
}
async function onSearch() {
console.log(keyword.value);
try {
loading.value = true;
const params = { q: keyword.value };
const res = await searchText(params);
const { results } = res;
searchResult.value = results.map((item) => {
return {
...item,
url: import.meta.env.VITE_API_BASE_URL + "/static/" + item.url,
};
});
} catch (error) {
console.log(error);
}
}
function handleLocatePage(param) {
$emit("locatePage", param);
showDirectory.value = false;
}
defineExpose({
show,
});
</script>
<style lang="scss" scoped>
.list {
padding: 20px 0;
height: 300px;
overflow: auto;
.list-item {
padding: 20px;
font-size: 14px;
color: #666;
border-bottom: 1px solid #eee;
display: flex;
cursor: pointer;
&:hover {
background-color: #eee;
}
}
}
:deep(.el-drawer__body) {
height: 500px;
}
.wrapper {
padding: 0 50px;
}
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
word-break: break-all;
}
</style>
......@@ -4,7 +4,15 @@
<el-button type="primary" link class="back" @click="goBack"
>返回</el-button
>
微方志
{{ title || "微方志" }}
<el-button
type="text"
class="search"
:icon="Search"
@click="handleOpenSearch"
>
全文搜索
</el-button>
</div>
<BookReader
:pages="pages"
......@@ -12,12 +20,15 @@
:pdfUrl="pdfUrl"
:bookmarks="bookmarks"
:enableTextLayer="true"
ref="bookReaderRef"
/>
<SearchBar ref="searchBarRef" @locatePage="locatePage" />
</div>
</template>
<script setup>
import { getDocumentDetail, getDocumentBookmarks } from "@/api";
import BookReader from "@/components/BookReader/index.vue";
import { Search } from "@element-plus/icons-vue";
const route = useRoute();
const router = useRouter();
/* props */
......@@ -27,6 +38,9 @@ const title = ref("");
const pages = ref([]);
const bookmarks = ref([]);
const pdfUrl = ref("");
const searchBarRef = ref(null);
const bookReaderRef = ref(null);
/* computed */
/* methods */
async function loadData(id) {
......@@ -63,6 +77,16 @@ async function fetchBookmarks(id) {
}
}
function handleOpenSearch() {
searchBarRef.value.show();
}
function locatePage(pageNum) {
if (bookReaderRef.value) {
bookReaderRef.value.goToPage(pageNum);
}
}
onMounted(() => {
const { id } = route.params;
title.value = route.query.title;
......@@ -85,12 +109,19 @@ onMounted(() => {
align-items: center;
position: relative;
height: 46px;
padding: 0 20px;
padding: 0 40px;
.back {
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%);
}
.search {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
color: #fff;
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论