Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pic-reader
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
龙菲
pic-reader
Commits
87fd5b20
提交
87fd5b20
authored
8月 18, 2025
作者:
龙菲
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 增加pc端的全文搜索和跳转
上级
e979a9b4
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
170 行增加
和
5 行删除
+170
-5
index.js
src/api/index.js
+13
-0
components.d.ts
src/components.d.ts
+3
-3
index.vue
src/components/BookReader/index.vue
+0
-0
index.vue
src/components/SearchBar/index.vue
+121
-0
index.vue
src/views/detail/index.vue
+33
-2
没有找到文件。
src/api/index.js
浏览文件 @
87fd5b20
...
@@ -38,3 +38,16 @@ export function getDocumentBookmarks(id) {
...
@@ -38,3 +38,16 @@ export function getDocumentBookmarks(id) {
method
:
'get'
method
:
'get'
});
});
}
}
/**
* 全文搜索
* @returns {Promise} 返回文档详情数据
*/
export
function
searchText
(
params
)
{
return
request
({
url
:
`/search/text`
,
method
:
'get'
,
params
});
}
src/components.d.ts
浏览文件 @
87fd5b20
...
@@ -13,12 +13,12 @@ declare module 'vue' {
...
@@ -13,12 +13,12 @@ declare module 'vue' {
BookReader
:
typeof
import
(
'./components/BookReader/index.vue'
)[
'default'
]
BookReader
:
typeof
import
(
'./components/BookReader/index.vue'
)[
'default'
]
ElButton
:
typeof
import
(
'element-plus/es'
)[
'ElButton'
]
ElButton
:
typeof
import
(
'element-plus/es'
)[
'ElButton'
]
ElCol
:
typeof
import
(
'element-plus/es'
)[
'ElCol'
]
ElCol
:
typeof
import
(
'element-plus/es'
)[
'ElCol'
]
ElDialog
:
typeof
import
(
'element-plus/es'
)[
'ElDialog'
]
ElDrawer
:
typeof
import
(
'element-plus/es'
)[
'ElDrawer'
]
ElDrawer
:
typeof
import
(
'element-plus/es'
)[
'ElDrawer'
]
ElHeader
:
typeof
import
(
'element-plus/es'
)[
'ElHeader'
]
ElHeader
:
typeof
import
(
'element-plus/es'
)[
'ElHeader'
]
ElIcon
:
typeof
import
(
'element-plus/es'
)[
'ElIcon'
]
ElInput
:
typeof
import
(
'element-plus/es'
)[
'ElInput'
]
ElInput
:
typeof
import
(
'element-plus/es'
)[
'ElInput'
]
ElRow
:
typeof
import
(
'element-plus/es'
)[
'ElRow'
]
ElRow
:
typeof
import
(
'element-plus/es'
)[
'ElRow'
]
ElT
ext
:
typeof
import
(
'element-plus/es'
)[
'ElText
'
]
ElT
ag
:
typeof
import
(
'element-plus/es'
)[
'ElTag
'
]
ElTooltip
:
typeof
import
(
'element-plus/es'
)[
'ElTooltip'
]
ElTooltip
:
typeof
import
(
'element-plus/es'
)[
'ElTooltip'
]
FileUpload
:
typeof
import
(
'./components/FileUpload.vue'
)[
'default'
]
FileUpload
:
typeof
import
(
'./components/FileUpload.vue'
)[
'default'
]
GuideMobile
:
typeof
import
(
'./components/GuideMobile/index.vue'
)[
'default'
]
GuideMobile
:
typeof
import
(
'./components/GuideMobile/index.vue'
)[
'default'
]
...
@@ -28,10 +28,10 @@ declare module 'vue' {
...
@@ -28,10 +28,10 @@ declare module 'vue' {
IconEcosystem
:
typeof
import
(
'./components/icons/IconEcosystem.vue'
)[
'default'
]
IconEcosystem
:
typeof
import
(
'./components/icons/IconEcosystem.vue'
)[
'default'
]
IconSupport
:
typeof
import
(
'./components/icons/IconSupport.vue'
)[
'default'
]
IconSupport
:
typeof
import
(
'./components/icons/IconSupport.vue'
)[
'default'
]
IconTooling
:
typeof
import
(
'./components/icons/IconTooling.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'
]
PdfViewer
:
typeof
import
(
'./components/PdfViewer/index.vue'
)[
'default'
]
RouterLink
:
typeof
import
(
'vue-router'
)[
'RouterLink'
]
RouterLink
:
typeof
import
(
'vue-router'
)[
'RouterLink'
]
RouterView
:
typeof
import
(
'vue-router'
)[
'RouterView'
]
RouterView
:
typeof
import
(
'vue-router'
)[
'RouterView'
]
SearchBar
:
typeof
import
(
'./components/SearchBar/index.vue'
)[
'default'
]
SvgIcon
:
typeof
import
(
'./components/SvgIcon/index.vue'
)[
'default'
]
SvgIcon
:
typeof
import
(
'./components/SvgIcon/index.vue'
)[
'default'
]
Test
:
typeof
import
(
'./components/test/index.vue'
)[
'default'
]
Test
:
typeof
import
(
'./components/test/index.vue'
)[
'default'
]
VanCell
:
typeof
import
(
'vant/es'
)[
'Cell'
]
VanCell
:
typeof
import
(
'vant/es'
)[
'Cell'
]
...
...
src/components/BookReader/index.vue
浏览文件 @
87fd5b20
差异被折叠。
点击展开。
src/components/SearchBar/index.vue
0 → 100644
浏览文件 @
87fd5b20
<
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
>
src/views/detail/index.vue
浏览文件 @
87fd5b20
...
@@ -4,7 +4,15 @@
...
@@ -4,7 +4,15 @@
<el-button
type=
"primary"
link
class=
"back"
@
click=
"goBack"
<el-button
type=
"primary"
link
class=
"back"
@
click=
"goBack"
>
返回
</el-button
>
返回
</el-button
>
>
微方志
{{
title
||
"微方志"
}}
<el-button
type=
"text"
class=
"search"
:icon=
"Search"
@
click=
"handleOpenSearch"
>
全文搜索
</el-button>
</div>
</div>
<BookReader
<BookReader
:pages=
"pages"
:pages=
"pages"
...
@@ -12,12 +20,15 @@
...
@@ -12,12 +20,15 @@
:pdfUrl=
"pdfUrl"
:pdfUrl=
"pdfUrl"
:bookmarks=
"bookmarks"
:bookmarks=
"bookmarks"
:enableTextLayer=
"true"
:enableTextLayer=
"true"
ref=
"bookReaderRef"
/>
/>
<SearchBar
ref=
"searchBarRef"
@
locatePage=
"locatePage"
/>
</div>
</div>
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
getDocumentDetail
,
getDocumentBookmarks
}
from
"@/api"
;
import
{
getDocumentDetail
,
getDocumentBookmarks
}
from
"@/api"
;
import
BookReader
from
"@/components/BookReader/index.vue"
;
import
BookReader
from
"@/components/BookReader/index.vue"
;
import
{
Search
}
from
"@element-plus/icons-vue"
;
const
route
=
useRoute
();
const
route
=
useRoute
();
const
router
=
useRouter
();
const
router
=
useRouter
();
/* props */
/* props */
...
@@ -27,6 +38,9 @@ const title = ref("");
...
@@ -27,6 +38,9 @@ const title = ref("");
const
pages
=
ref
([]);
const
pages
=
ref
([]);
const
bookmarks
=
ref
([]);
const
bookmarks
=
ref
([]);
const
pdfUrl
=
ref
(
""
);
const
pdfUrl
=
ref
(
""
);
const
searchBarRef
=
ref
(
null
);
const
bookReaderRef
=
ref
(
null
);
/* computed */
/* computed */
/* methods */
/* methods */
async
function
loadData
(
id
)
{
async
function
loadData
(
id
)
{
...
@@ -63,6 +77,16 @@ async function fetchBookmarks(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
(()
=>
{
onMounted
(()
=>
{
const
{
id
}
=
route
.
params
;
const
{
id
}
=
route
.
params
;
title
.
value
=
route
.
query
.
title
;
title
.
value
=
route
.
query
.
title
;
...
@@ -85,12 +109,19 @@ onMounted(() => {
...
@@ -85,12 +109,19 @@ onMounted(() => {
align-items
:
center
;
align-items
:
center
;
position
:
relative
;
position
:
relative
;
height
:
46px
;
height
:
46px
;
padding
:
0
2
0px
;
padding
:
0
4
0px
;
.back
{
.back
{
position
:
absolute
;
position
:
absolute
;
left
:
20px
;
left
:
20px
;
top
:
50%
;
top
:
50%
;
transform
:
translateY
(
-50%
);
transform
:
translateY
(
-50%
);
}
}
.search
{
position
:
absolute
;
right
:
20px
;
top
:
50%
;
transform
:
translateY
(
-50%
);
color
:
#fff
;
}
}
}
</
style
>
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论