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 个修改的文件
包含
392 行增加
和
139 行删除
+392
-139
index.js
src/api/index.js
+13
-0
components.d.ts
src/components.d.ts
+3
-3
index.vue
src/components/BookReader/index.vue
+222
-134
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) {
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' {
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'
]
ElT
ext
:
typeof
import
(
'element-plus/es'
)[
'ElText
'
]
ElT
ag
:
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'
]
...
...
src/components/BookReader/index.vue
浏览文件 @
87fd5b20
...
...
@@ -147,7 +147,6 @@
<svg-icon
name=
"to-end"
:size=
"30"
@
click=
"toEnd"
></svg-icon>
</el-tooltip>
</el-col>
</el-row>
</div>
</
template
>
...
...
@@ -215,7 +214,7 @@ const calculatePageDimensions = (viewportWidth, viewportHeight, zoom = 1.0) => {
baseWidth
,
baseHeight
,
scaledWidth
,
scaledHeight
scaledHeight
,
};
}
catch
(
error
)
{
console
.
error
(
"Error calculating page dimensions:"
,
error
);
...
...
@@ -233,17 +232,16 @@ const updatePageDimensions = (dimensions, $magazine) => {
$magazine
.
turn
(
"size"
,
dimensions
.
scaledWidth
*
2
,
dimensions
.
scaledHeight
);
}
// 逐页强制刷新 flip wrapper
const
data
=
$magazine
.
data
();
for
(
let
page
in
data
.
pages
)
{
if
(
data
.
pages
.
hasOwnProperty
(
page
))
{
data
.
pages
[
page
].
flip
(
'resize'
,
true
);
data
.
pages
[
page
].
flip
(
"resize"
,
true
);
}
}
// 更新 z-index 和交互区域
$magazine
.
turn
(
'update'
);
$magazine
.
turn
(
"update"
);
};
// 更新视口样式
...
...
@@ -268,9 +266,12 @@ const restoreLoadedImages = (visiblePages, $magazine) => {
};
// 新增:封装 PDF viewport 尺寸计算
const
calculatePdfViewportScale
=
(
pdfPage
,
containerWidth
,
containerHeight
)
=>
{
const
calculatePdfViewportScale
=
(
pdfPage
,
containerWidth
,
containerHeight
)
=>
{
try
{
const
originalViewport
=
pdfPage
.
getViewport
({
scale
:
1.0
});
const
viewportWidth
=
originalViewport
.
viewBox
[
2
];
const
viewportHeight
=
originalViewport
.
viewBox
[
3
];
...
...
@@ -285,14 +286,14 @@ const calculatePdfViewportScale = (pdfPage, containerWidth, containerHeight) =>
}
// 计算补偿后的 scale
const
cssUnits
=
pdfjsViewer
.
CSS_UNITS
||
(
96.0
/
72.0
)
;
const
cssUnits
=
pdfjsViewer
.
CSS_UNITS
||
96.0
/
72.0
;
const
compensatedScale
=
scale
/
cssUnits
;
return
{
scale
,
compensatedScale
,
viewportWidth
,
viewportHeight
viewportHeight
,
};
}
catch
(
error
)
{
console
.
error
(
"计算 PDF viewport scale 失败:"
,
error
);
...
...
@@ -321,18 +322,28 @@ const getVisiblePageInfo = ($magazine, processedPages) => {
if
(
!
processedPages
.
value
.
length
)
return
[];
const
currentPage
=
$magazine
.
turn
(
"page"
);
const
visibleIndexes
=
getVisiblePageIndexes
(
currentPage
,
processedPages
.
value
.
length
,
isMobile
.
value
);
const
visibleIndexes
=
getVisiblePageIndexes
(
currentPage
,
processedPages
.
value
.
length
,
isMobile
.
value
);
return
visibleIndexes
.
map
(
index
=>
({
return
visibleIndexes
.
map
(
(
index
)
=>
({
page
:
index
+
1
,
isLoaded
:
processedPages
.
value
[
index
]?.
isLoaded
||
false
isLoaded
:
processedPages
.
value
[
index
]?.
isLoaded
||
false
,
}));
};
// 新增:封装创建 PDFPageView
const
createPdfPageView
=
(
pageDiv
,
pageNum
,
compensatedScale
,
newViewport
,
eventBus
)
=>
{
const
createPdfPageView
=
(
pageDiv
,
pageNum
,
compensatedScale
,
newViewport
,
eventBus
)
=>
{
if
(
!
pdfjsViewer
)
{
throw
new
Error
(
'PDF.js viewer not loaded'
);
throw
new
Error
(
"PDF.js viewer not loaded"
);
}
return
new
pdfjsViewer
.
PDFPageView
({
...
...
@@ -348,7 +359,7 @@ const createPdfPageView = (pageDiv, pageNum, compensatedScale, newViewport, even
enableXfa
:
false
,
l10n
:
null
,
textLayerFactory
:
new
pdfjsViewer
.
DefaultTextLayerFactory
(),
useOnlyCssZoom
:
false
useOnlyCssZoom
:
false
,
});
};
...
...
@@ -391,16 +402,16 @@ const loadPdfJs = async () => {
if
(
window
.
pdfjsLib
&&
window
.
pdfjsViewer
)
{
pdfjsLib
=
window
.
pdfjsLib
;
pdfjsViewer
=
window
.
pdfjsViewer
;
console
.
log
(
'PDF.js already loaded from script tags'
);
console
.
log
(
"PDF.js already loaded from script tags"
);
return
;
}
// 如果全局变量不存在,则动态创建script标签加载
await
loadScript
(
'/pdfjs-dist/build/pdf.js'
);
await
loadScript
(
'/pdfjs-dist/web/pdf_viewer.js'
);
await
loadScript
(
"/pdfjs-dist/build/pdf.js"
);
await
loadScript
(
"/pdfjs-dist/web/pdf_viewer.js"
);
// 等待一小段时间确保脚本加载完成
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
100
));
await
new
Promise
(
(
resolve
)
=>
setTimeout
(
resolve
,
100
));
// 从全局变量获取
if
(
window
.
pdfjsLib
&&
window
.
pdfjsViewer
)
{
...
...
@@ -411,12 +422,12 @@ const loadPdfJs = async () => {
// if (pdfjsLib.GlobalWorkerOptions) {
// pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs-dist/web/pdf.worker.js';
// }
console
.
log
(
'PDF.js loaded successfully via script tags'
);
console
.
log
(
"PDF.js loaded successfully via script tags"
);
}
else
{
throw
new
Error
(
'PDF.js failed to load'
);
throw
new
Error
(
"PDF.js failed to load"
);
}
}
catch
(
error
)
{
console
.
error
(
'Failed to load PDF.js:'
,
error
);
console
.
error
(
"Failed to load PDF.js:"
,
error
);
}
};
...
...
@@ -429,7 +440,7 @@ const loadScript = (src) => {
return
;
}
const
script
=
document
.
createElement
(
'script'
);
const
script
=
document
.
createElement
(
"script"
);
script
.
src
=
src
;
script
.
onload
=
resolve
;
script
.
onerror
=
reject
;
...
...
@@ -464,7 +475,6 @@ const props = defineProps({
type
:
Boolean
,
default
:
false
,
},
});
const
magazine
=
ref
(
null
);
...
...
@@ -502,7 +512,8 @@ const textLayerEnabled = ref(props.enableTextLayer); // 文字层开关状态
// SVG渲染器开关状态
const
svgRendererEnabled
=
ref
(
localStorage
.
getItem
(
"bookReaderSvgRenderer"
)
===
"true"
||
props
.
enableSvgRenderer
localStorage
.
getItem
(
"bookReaderSvgRenderer"
)
===
"true"
||
props
.
enableSvgRenderer
);
// 文本层可见性状态(用于调试)
...
...
@@ -639,7 +650,9 @@ const loadVisiblePages = async (currentPageNum) => {
const
endPage
=
Math
.
min
(
totalPages
,
currentPageNum
+
1
);
const
highPriority
=
[
currentPageNum
,
currentPageNum
+
1
];
// 优先加载当前和下一页
console
.
log
(
`预加载页面:
${
currentPageNum
}
, 范围:
${
startPage
}
-
${
endPage
}
, 总页数:
${
totalPages
}
`
);
console
.
log
(
`预加载页面:
${
currentPageNum
}
, 范围:
${
startPage
}
-
${
endPage
}
, 总页数:
${
totalPages
}
`
);
try
{
// 静默加载,不显示loading状态
...
...
@@ -677,7 +690,9 @@ const renderVisiblePdfPages = async (currentPageNum) => {
const
endPage
=
Math
.
min
(
totalPages
,
currentPageNum
+
1
);
const
highPriority
=
[
currentPageNum
,
currentPageNum
+
1
];
// 优先渲染当前和下一页
console
.
log
(
`渲染PDF页面:
${
currentPageNum
}
, 范围:
${
startPage
}
-
${
endPage
}
, 总页数:
${
totalPages
}
`
);
console
.
log
(
`渲染PDF页面:
${
currentPageNum
}
, 范围:
${
startPage
}
-
${
endPage
}
, 总页数:
${
totalPages
}
`
);
try
{
console
.
log
(
`开始渲染高优先级页面:
${
highPriority
.
join
(
", "
)}
`
);
...
...
@@ -715,7 +730,9 @@ const initBook = async () => {
const
expectedPages
=
processedPages
.
value
.
length
;
const
currentPages
=
$magazine
.
children
().
length
;
console
.
log
(
`初始化turn.js: 期望页面数=
${
expectedPages
}
, 当前DOM页面数=
${
currentPages
}
`
);
console
.
log
(
`初始化turn.js: 期望页面数=
${
expectedPages
}
, 当前DOM页面数=
${
currentPages
}
`
);
// 如果DOM中的页面数量不正确,重新创建页面元素
if
(
currentPages
!==
expectedPages
)
{
...
...
@@ -724,14 +741,14 @@ const initBook = async () => {
// 为每个页面创建DOM元素
processedPages
.
value
.
forEach
((
page
,
index
)
=>
{
const
pageDiv
=
document
.
createElement
(
'div'
);
const
pageDiv
=
document
.
createElement
(
"div"
);
pageDiv
.
className
=
`page p
${
index
+
1
}
`
;
pageDiv
.
setAttribute
(
'data-page-index'
,
index
);
pageDiv
.
setAttribute
(
'data-page-num'
,
page
.
page_num
);
pageDiv
.
setAttribute
(
"data-page-index"
,
index
);
pageDiv
.
setAttribute
(
"data-page-num"
,
page
.
page_num
);
// 如果是PDF页面,添加特殊标识
if
(
page
.
isPdf
)
{
pageDiv
.
setAttribute
(
'data-is-pdf'
,
'true'
);
pageDiv
.
setAttribute
(
"data-is-pdf"
,
"true"
);
}
$magazine
.
append
(
pageDiv
);
...
...
@@ -747,7 +764,11 @@ const initBook = async () => {
const
viewportHeight
=
window
.
innerHeight
;
// 使用封装的函数计算页面尺寸
const
dimensions
=
calculatePageDimensions
(
viewportWidth
,
viewportHeight
,
1.0
);
const
dimensions
=
calculatePageDimensions
(
viewportWidth
,
viewportHeight
,
1.0
);
if
(
!
dimensions
)
{
console
.
error
(
"计算页面尺寸失败"
);
return
;
...
...
@@ -762,8 +783,16 @@ const initBook = async () => {
// 添加调试信息
console
.
log
(
`视口尺寸:
${
viewportWidth
}
x
${
viewportHeight
}
`
);
console
.
log
(
`计算出的页面尺寸:
${
pageWidth
}
x
${
pageHeight
}
, 比例:
${(
pageWidth
/
pageHeight
).
toFixed
(
3
)}
`
);
console
.
log
(
`PDF标准比例:
${
PDF_RATIO
}
, 实际比例:
${(
pageWidth
/
pageHeight
).
toFixed
(
3
)}
`
);
console
.
log
(
`计算出的页面尺寸:
${
pageWidth
}
x
${
pageHeight
}
, 比例:
${(
pageWidth
/
pageHeight
).
toFixed
(
3
)}
`
);
console
.
log
(
`PDF标准比例:
${
PDF_RATIO
}
, 实际比例:
${(
pageWidth
/
pageHeight
).
toFixed
(
3
)}
`
);
await
nextTick
();
...
...
@@ -799,7 +828,11 @@ const initBook = async () => {
});
// 移动端使用单页显示
console
.
log
(
`turn.js初始化参数: 宽度=
${
isMobile
.
value
?
pageWidth
:
pageWidth
*
2
}
, 高度=
${
pageHeight
}
`
);
console
.
log
(
`turn.js初始化参数: 宽度=
${
isMobile
.
value
?
pageWidth
:
pageWidth
*
2
}
, 高度=
${
pageHeight
}
`
);
$magazine
.
turn
({
width
:
isMobile
.
value
?
pageWidth
:
pageWidth
*
2
,
height
:
pageHeight
,
...
...
@@ -858,10 +891,16 @@ const initBook = async () => {
initialLoading
.
value
=
false
;
// 确认turn.js事件绑定状态
console
.
log
(
'turn.js初始化完成,事件绑定状态:'
);
console
.
log
(
'- turning事件:'
,
$magazine
.
data
(
'turn'
)?.
when
?.
turning
?
'已绑定'
:
'未绑定'
);
console
.
log
(
'- turned事件:'
,
$magazine
.
data
(
'turn'
)?.
when
?.
turned
?
'已绑定'
:
'未绑定'
);
console
.
log
(
'- 当前页码:'
,
$magazine
.
turn
(
"page"
));
console
.
log
(
"turn.js初始化完成,事件绑定状态:"
);
console
.
log
(
"- turning事件:"
,
$magazine
.
data
(
"turn"
)?.
when
?.
turning
?
"已绑定"
:
"未绑定"
);
console
.
log
(
"- turned事件:"
,
$magazine
.
data
(
"turn"
)?.
when
?.
turned
?
"已绑定"
:
"未绑定"
);
console
.
log
(
"- 当前页码:"
,
$magazine
.
turn
(
"page"
));
// 只处理图片页面,PDF页面在loadPdf中处理
if
(
!
props
.
isPdf
)
{
...
...
@@ -912,15 +951,18 @@ const loadPdf = async () => {
// 加载PDF文档
if
(
!
pdfjsLib
)
{
throw
new
Error
(
'PDF.js not loaded'
);
throw
new
Error
(
"PDF.js not loaded"
);
}
const
loadingTask
=
pdfjsLib
.
getDocument
(
props
.
pdfUrl
);
const
rawPdfDocument
=
await
loadingTask
.
promise
;
pdfDocument
.
value
=
rawPdfDocument
;
totalPdfPages
.
value
=
rawPdfDocument
.
numPages
;
console
.
log
(
'pdfjsViewer exists:'
,
!!
pdfjsViewer
);
console
.
log
(
'DefaultTextLayerFactory exists:'
,
!!
pdfjsViewer
.
DefaultTextLayerFactory
);
console
.
log
(
"pdfjsViewer exists:"
,
!!
pdfjsViewer
);
console
.
log
(
"DefaultTextLayerFactory exists:"
,
!!
pdfjsViewer
.
DefaultTextLayerFactory
);
// 设置PDF链接服务
if
(
pdfLinkService
.
value
)
{
...
...
@@ -961,10 +1003,16 @@ const loadPdf = async () => {
// 确认PDF模式下的turn.js事件绑定状态
if
(
magazine
.
value
)
{
const
$magazine
=
$
(
magazine
.
value
);
console
.
log
(
'PDF加载完成,turn.js事件绑定状态:'
);
console
.
log
(
'- turning事件:'
,
$magazine
.
data
(
'turn'
)?.
when
?.
turning
?
'已绑定'
:
'未绑定'
);
console
.
log
(
'- turned事件:'
,
$magazine
.
data
(
'turn'
)?.
when
?.
turned
?
'已绑定'
:
'未绑定'
);
console
.
log
(
'- 当前页码:'
,
$magazine
.
turn
(
"page"
));
console
.
log
(
"PDF加载完成,turn.js事件绑定状态:"
);
console
.
log
(
"- turning事件:"
,
$magazine
.
data
(
"turn"
)?.
when
?.
turning
?
"已绑定"
:
"未绑定"
);
console
.
log
(
"- turned事件:"
,
$magazine
.
data
(
"turn"
)?.
when
?.
turned
?
"已绑定"
:
"未绑定"
);
console
.
log
(
"- 当前页码:"
,
$magazine
.
turn
(
"page"
));
}
console
.
log
(
"PDF渲染完成"
);
...
...
@@ -979,7 +1027,12 @@ watch(
()
=>
props
.
pages
,
(
newPages
,
oldPages
)
=>
{
// 避免在组件初始化时重复加载
if
(
newPages
&&
newPages
.
length
>
0
&&
!
props
.
isPdf
&&
newPages
!==
oldPages
)
{
if
(
newPages
&&
newPages
.
length
>
0
&&
!
props
.
isPdf
&&
newPages
!==
oldPages
)
{
processedPages
.
value
=
processPages
(
newPages
);
loadImages
();
}
...
...
@@ -1026,7 +1079,6 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
// 检查页面是否已经渲染过
if
(
renderedPages
.
value
.
has
(
pageIndex
))
{
if
(
forceCheck
)
{
if
(
!
pageInfo
)
{
console
.
warn
(
`页面
${
pageNum
}
信息获取失败,跳过scale检查`
);
return
;
...
...
@@ -1045,7 +1097,6 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
console
.
log
(
`开始渲染PDF页面
${
pageNum
}
,使用PdfViewer`
);
try
{
// 获取页面容器
...
...
@@ -1056,20 +1107,22 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
}
// 这里是为了读取页面尺寸,这个比较准
let
pageWrapper
=
magazine
.
value
.
querySelector
(
`.turn-page-wrapper[page="
${
pageNum
}
"]`
);
let
pageWrapper
=
magazine
.
value
.
querySelector
(
`.turn-page-wrapper[page="
${
pageNum
}
"]`
);
if
(
!
pageWrapper
)
{
console
.
warn
(
`Page wrapper not found for page
${
pageNum
}
`
);
return
;
}
// 清理容器内容
pageContainer
.
innerHTML
=
''
;
pageContainer
.
innerHTML
=
""
;
// 创建页面div元素
// todo 这里可能能够优化一下
const
pageDiv
=
document
.
createElement
(
'div'
);
pageDiv
.
className
=
'pdfViewer'
;
pageDiv
.
setAttribute
(
'data-page-number'
,
pageNum
);
const
pageDiv
=
document
.
createElement
(
"div"
);
pageDiv
.
className
=
"pdfViewer"
;
pageDiv
.
setAttribute
(
"data-page-number"
,
pageNum
);
// 将页面div添加到容器中
pageContainer
.
appendChild
(
pageDiv
);
...
...
@@ -1089,7 +1142,11 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
const
pdfPage
=
await
rawPdfDocument
.
getPage
(
pageNum
);
// 使用封装的函数计算缩放比例
const
scaleInfo
=
calculatePdfViewportScale
(
pdfPage
,
containerWidth
,
containerHeight
);
const
scaleInfo
=
calculatePdfViewportScale
(
pdfPage
,
containerWidth
,
containerHeight
);
if
(
!
scaleInfo
)
{
console
.
error
(
`页面
${
pageNum
}
scale 计算失败`
);
return
;
...
...
@@ -1099,7 +1156,13 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
const
newViewport
=
pdfPage
.
getViewport
(
scaleInfo
.
compensatedScale
);
// 使用封装的函数创建 PDFPageView
const
pageView
=
createPdfPageView
(
pageDiv
,
pageNum
,
scaleInfo
.
compensatedScale
,
newViewport
,
eventBus
);
const
pageView
=
createPdfPageView
(
pageDiv
,
pageNum
,
scaleInfo
.
compensatedScale
,
newViewport
,
eventBus
);
// 设置PDF页面
pageView
.
setPdfPage
(
pdfPage
);
...
...
@@ -1113,10 +1176,9 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
// 标记页面已渲染
renderedPages
.
value
.
add
(
pageIndex
);
// 验证渲染结果
const
canvas
=
pageDiv
.
querySelector
(
'canvas'
);
const
svg
=
pageDiv
.
querySelector
(
'svg'
);
const
canvas
=
pageDiv
.
querySelector
(
"canvas"
);
const
svg
=
pageDiv
.
querySelector
(
"svg"
);
const
renderElement
=
canvas
||
svg
;
if
(
renderElement
)
{
...
...
@@ -1129,8 +1191,9 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
);
if
(
canvas
)
{
console
.
log
(
`Canvas CSS尺寸:
${
elementStyle
.
width
}
x
${
elementStyle
.
height
}
, 实际尺寸:
${
canvas
.
width
}
x
${
canvas
.
height
}
`
);
console
.
log
(
`Canvas CSS尺寸:
${
elementStyle
.
width
}
x
${
elementStyle
.
height
}
, 实际尺寸:
${
canvas
.
width
}
x
${
canvas
.
height
}
`
);
}
}
...
...
@@ -1138,7 +1201,6 @@ const renderPdfPage = async (pageIndex, forceCheck = true) => {
if
(
processedPages
.
value
[
pageIndex
])
{
processedPages
.
value
[
pageIndex
].
isLoaded
=
true
;
}
}
catch
(
error
)
{
console
.
error
(
`渲染PDF页面
${
pageNum
}
失败:`
,
error
);
}
...
...
@@ -1164,7 +1226,7 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
let
currentScale
=
newScale
;
// 默认值
console
.
log
(
`before pageView:
${
pageView
}
`
);
if
(
pageView
.
viewport
&&
typeof
pageView
.
viewport
.
scale
===
'number'
)
{
if
(
pageView
.
viewport
&&
typeof
pageView
.
viewport
.
scale
===
"number"
)
{
currentScale
=
pageView
.
viewport
.
scale
;
console
.
log
(
`从pageView.viewport.scale获取当前scale:
${
currentScale
}
`
);
}
else
{
...
...
@@ -1174,13 +1236,17 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
const
scaleDifference
=
Math
.
abs
(
currentScale
-
newScale
);
const
scaleThreshold
=
0.01
;
// 允许的scale差异阈值
console
.
log
(
`页面
${
pageNum
}
scale检查: 当前=
${
currentScale
}
, 新计算=
${
newScale
}
, 差异=
${
scaleDifference
}
, 阈值=
${
scaleThreshold
}
`
);
console
.
log
(
`页面
${
pageNum
}
scale检查: 当前=
${
currentScale
}
, 新计算=
${
newScale
}
, 差异=
${
scaleDifference
}
, 阈值=
${
scaleThreshold
}
`
);
if
(
scaleDifference
>
scaleThreshold
)
{
console
.
log
(
`页面
${
pageNum
}
scale不一致,需要重新创建PDFPageView,差异:
${
scaleDifference
}
`
);
console
.
log
(
`页面
${
pageNum
}
scale不一致,需要重新创建PDFPageView,差异:
${
scaleDifference
}
`
);
// 销毁旧的PDFPageView实例
if
(
pageView
&&
typeof
pageView
.
destroy
===
'function'
)
{
if
(
pageView
&&
typeof
pageView
.
destroy
===
"function"
)
{
pageView
.
destroy
();
}
...
...
@@ -1189,17 +1255,19 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
pdfViewerInstances
.
value
.
delete
(
pageIndex
);
// 找到原来的pdfViewer div并清空内容
const
existingPdfDiv
=
pageContainer
.
querySelector
(
`.pdfViewer[data-page-number="
${
pageNum
}
"]`
);
const
existingPdfDiv
=
pageContainer
.
querySelector
(
`.pdfViewer[data-page-number="
${
pageNum
}
"]`
);
if
(
existingPdfDiv
)
{
// 直接清空现有div的内容,而不是重新创建
existingPdfDiv
.
innerHTML
=
''
;
existingPdfDiv
.
innerHTML
=
""
;
console
.
log
(
`找到现有pdfViewer div (页面
${
pageNum
}
),已清空内容`
);
}
else
{
console
.
warn
(
`未找到页面
${
pageNum
}
的pdfViewer div,创建新的`
);
// 如果没有找到,才创建新的
const
pageDiv
=
document
.
createElement
(
'div'
);
pageDiv
.
className
=
'pdfViewer'
;
pageDiv
.
setAttribute
(
'data-page-number'
,
pageNum
);
const
pageDiv
=
document
.
createElement
(
"div"
);
pageDiv
.
className
=
"pdfViewer"
;
pageDiv
.
setAttribute
(
"data-page-number"
,
pageNum
);
pageContainer
.
appendChild
(
pageDiv
);
}
...
...
@@ -1215,7 +1283,11 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
const
containerHeight
=
pageContainer
.
clientHeight
;
// 使用封装的函数计算缩放比例
const
scaleInfo
=
calculatePdfViewportScale
(
pdfPage
,
containerWidth
,
containerHeight
);
const
scaleInfo
=
calculatePdfViewportScale
(
pdfPage
,
containerWidth
,
containerHeight
);
if
(
!
scaleInfo
)
{
console
.
error
(
`页面
${
pageNum
}
scale检查时重新计算scale失败`
);
return
false
;
...
...
@@ -1225,10 +1297,17 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
const
newViewport
=
pdfPage
.
getViewport
(
scaleInfo
.
compensatedScale
);
// 使用现有的或新创建的div
const
targetDiv
=
existingPdfDiv
||
pageContainer
.
querySelector
(
'.pdfViewer'
);
const
targetDiv
=
existingPdfDiv
||
pageContainer
.
querySelector
(
".pdfViewer"
);
// 使用封装的函数创建新的 PDFPageView
const
newPageView
=
createPdfPageView
(
targetDiv
,
pageNum
,
scaleInfo
.
compensatedScale
,
newViewport
,
eventBus
);
const
newPageView
=
createPdfPageView
(
targetDiv
,
pageNum
,
scaleInfo
.
compensatedScale
,
newViewport
,
eventBus
);
// 设置PDF页面
newPageView
.
setPdfPage
(
pdfPage
);
...
...
@@ -1242,13 +1321,14 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
// 重新标记页面已渲染
renderedPages
.
value
.
add
(
pageIndex
);
console
.
log
(
`页面
${
pageNum
}
PDFPageView重新创建完成,新scale:
${
scaleInfo
.
compensatedScale
}
`
);
console
.
log
(
`页面
${
pageNum
}
PDFPageView重新创建完成,新scale:
${
scaleInfo
.
compensatedScale
}
`
);
return
true
;
}
else
{
console
.
log
(
`页面
${
pageNum
}
scale一致,无需刷新`
);
return
false
;
}
}
catch
(
error
)
{
console
.
error
(
`检查页面
${
pageNum
}
scale失败:`
,
error
);
return
false
;
...
...
@@ -1258,7 +1338,9 @@ const checkAndUpdatePdfPageScale = async (pageIndex) => {
const
getPageInfoByPageNum
=
(
pageNum
)
=>
{
try
{
// 首先尝试查找翻页容器,这个容器时动态计算的
let
pageContainer
=
magazine
.
value
.
querySelector
(
`.turn-page-wrapper[page="
${
pageNum
}
"]`
);
let
pageContainer
=
magazine
.
value
.
querySelector
(
`.turn-page-wrapper[page="
${
pageNum
}
"]`
);
// 检查容器尺寸
const
clientWidth
=
pageContainer
.
clientWidth
;
...
...
@@ -1285,7 +1367,11 @@ const getPageInfoByPageNum = (pageNum) => {
}
// 计算缩放比例
const
scaleInfo
=
calculatePdfViewportScale
(
pdfPage
,
clientWidth
,
clientHeight
);
const
scaleInfo
=
calculatePdfViewportScale
(
pdfPage
,
clientWidth
,
clientHeight
);
if
(
!
scaleInfo
)
{
console
.
warn
(
`页面
${
pageNum
}
scale计算失败`
);
return
null
;
...
...
@@ -1294,13 +1380,13 @@ const getPageInfoByPageNum = (pageNum) => {
return
{
pageContainer
,
pageView
,
scaleInfo
scaleInfo
,
};
}
catch
(
error
)
{
console
.
error
(
`获取页面
${
pageNum
}
信息时出错:`
,
error
);
return
null
;
}
}
}
;
// 重新调整PDF页面尺寸
const
resizePdfPage
=
async
(
pageIndex
)
=>
{
...
...
@@ -1309,12 +1395,10 @@ const resizePdfPage = async (pageIndex) => {
const
pageNum
=
pageIndex
+
1
;
try
{
// 使用通用的scale检查函数
await
checkAndUpdatePdfPageScale
(
pageIndex
);
console
.
log
(
`页面
${
pageNum
}
resize 完成`
);
}
catch
(
error
)
{
console
.
error
(
`调整页面
${
pageNum
}
尺寸失败:`
,
error
);
}
...
...
@@ -1332,9 +1416,15 @@ const resizeVisiblePdfPages = async () => {
const
totalPages
=
processedPages
.
value
.
length
;
// 使用封装的函数获取可见页面索引
const
visiblePages
=
getVisiblePageIndexes
(
currentPage
,
totalPages
,
isMobile
.
value
);
const
visiblePages
=
getVisiblePageIndexes
(
currentPage
,
totalPages
,
isMobile
.
value
);
console
.
log
(
`开始检查可见页面:
${
visiblePages
.
map
(
i
=>
i
+
1
).
join
(
', '
)}
`
);
console
.
log
(
`开始检查可见页面:
${
visiblePages
.
map
((
i
)
=>
i
+
1
).
join
(
", "
)}
`
);
// 对所有可见页面进行检查和调整
for
(
const
pageIndex
of
visiblePages
)
{
...
...
@@ -1349,8 +1439,7 @@ const resizeVisiblePdfPages = async () => {
}
}
console
.
log
(
'所有可见 PDF 页面 resize 完成'
);
console
.
log
(
"所有可见 PDF 页面 resize 完成"
);
}
catch
(
error
)
{
console
.
error
(
"重新调整PDF页面尺寸失败:"
,
error
);
}
...
...
@@ -1424,7 +1513,6 @@ const zoomOut = () => {
// 提取公共的缩放更新逻辑
const
updateZoomCommon
=
async
(
isResize
=
false
)
=>
{
if
(
!
magazine
.
value
||
!
isInitialized
.
value
)
return
;
const
$magazine
=
$
(
magazine
.
value
);
...
...
@@ -1434,7 +1522,11 @@ const updateZoomCommon = async (isResize = false) => {
const
viewportWidth
=
window
.
innerWidth
;
const
viewportHeight
=
window
.
innerHeight
;
const
dimensions
=
calculatePageDimensions
(
viewportWidth
,
viewportHeight
,
zoomLevel
.
value
);
const
dimensions
=
calculatePageDimensions
(
viewportWidth
,
viewportHeight
,
zoomLevel
.
value
);
if
(
!
dimensions
)
return
;
baseWidth
.
value
=
dimensions
.
baseWidth
;
...
...
@@ -1442,11 +1534,9 @@ const updateZoomCommon = async (isResize = false) => {
updatePageDimensions
(
dimensions
,
$magazine
);
// 等待DOM更新完成后再处理PDF页面
await
nextTick
();
// 使用封装的函数获取可见页面信息
// if (processedPages.value.length > 0) {
// const visiblePages = getVisiblePageInfo($magazine, processedPages);
...
...
@@ -1489,9 +1579,6 @@ const handleKeyDown = (e) => {
}
};
const
initAudio
=
()
=>
{
try
{
pageTurnSound
.
value
=
new
Audio
(
audioFlip
);
...
...
@@ -1555,7 +1642,11 @@ const toggleTextLayer = () => {
const
toggleRenderer
=
()
=>
{
svgRendererEnabled
.
value
=
!
svgRendererEnabled
.
value
;
console
.
log
(
`切换到
${
svgRendererEnabled
.
value
?
'SVG'
:
'Canvas'
}
渲染模式,重新渲染所有可见页面`
);
console
.
log
(
`切换到
${
svgRendererEnabled
.
value
?
"SVG"
:
"Canvas"
}
渲染模式,重新渲染所有可见页面`
);
// 清除渲染状态,重新渲染所有可见页面
renderedPages
.
value
.
clear
();
...
...
@@ -1573,16 +1664,16 @@ const toggleRenderer = () => {
// 专门处理turn.js坐标变换的函数
const
fixTurnJsCoordinateSystem
=
()
=>
{
try
{
const
allPages
=
document
.
querySelectorAll
(
'.pdf-page-container'
);
const
allPages
=
document
.
querySelectorAll
(
".pdf-page-container"
);
let
totalFixed
=
0
;
allPages
.
forEach
(
pageDiv
=>
{
const
textLayer
=
pageDiv
.
querySelector
(
'.textLayer'
);
allPages
.
forEach
(
(
pageDiv
)
=>
{
const
textLayer
=
pageDiv
.
querySelector
(
".textLayer"
);
if
(
!
textLayer
)
return
;
const
textDivs
=
textLayer
.
querySelectorAll
(
'div'
);
const
container
=
pageDiv
.
closest
(
'.pdf-page-container'
);
const
turnPage
=
container
?
container
.
closest
(
'.page'
)
:
null
;
const
textDivs
=
textLayer
.
querySelectorAll
(
"div"
);
const
container
=
pageDiv
.
closest
(
".pdf-page-container"
);
const
turnPage
=
container
?
container
.
closest
(
".page"
)
:
null
;
if
(
!
turnPage
||
!
container
)
return
;
...
...
@@ -1593,9 +1684,9 @@ const fixTurnJsCoordinateSystem = () => {
const
scaleX
=
pageRect
.
width
/
containerRect
.
width
;
const
scaleY
=
pageRect
.
height
/
containerRect
.
height
;
textDivs
.
forEach
(
div
=>
{
textDivs
.
forEach
(
(
div
)
=>
{
// 获取原始位置数据
const
calculatedPos
=
div
.
getAttribute
(
'data-calculated-pos'
);
const
calculatedPos
=
div
.
getAttribute
(
"data-calculated-pos"
);
if
(
calculatedPos
)
{
try
{
const
pos
=
JSON
.
parse
(
calculatedPos
);
...
...
@@ -1609,12 +1700,12 @@ const fixTurnJsCoordinateSystem = () => {
div
.
style
.
top
=
`
${
correctedY
}
px !important`
;
// 更新CSS变量
div
.
style
.
setProperty
(
'--text-left'
,
`
${
correctedX
}
px`
);
div
.
style
.
setProperty
(
'--text-top'
,
`
${
correctedY
}
px`
);
div
.
style
.
setProperty
(
"--text-left"
,
`
${
correctedX
}
px`
);
div
.
style
.
setProperty
(
"--text-top"
,
`
${
correctedY
}
px`
);
totalFixed
++
;
}
catch
(
e
)
{
console
.
error
(
'解析位置数据失败:'
,
e
);
console
.
error
(
"解析位置数据失败:"
,
e
);
}
}
});
...
...
@@ -1626,12 +1717,11 @@ const fixTurnJsCoordinateSystem = () => {
return
totalFixed
;
}
catch
(
error
)
{
console
.
error
(
'修正turn.js坐标变换失败:'
,
error
);
console
.
error
(
"修正turn.js坐标变换失败:"
,
error
);
return
0
;
}
};
// 添加调试函数:检查页面状态
const
debugPageStatus
=
()
=>
{
if
(
!
magazine
.
value
)
return
;
...
...
@@ -1654,13 +1744,20 @@ const debugPageStatus = () => {
if
(
pageView
)
{
// 使用更安全的方式检查状态,避免访问私有字段
try
{
const
hasTextLayer
=
pageView
.
textLayer
&&
typeof
pageView
.
textLayer
===
'object'
;
const
hasAnnotationLayer
=
pageView
.
annotationLayer
&&
typeof
pageView
.
annotationLayer
===
'object'
;
const
hasTextLayer
=
pageView
.
textLayer
&&
typeof
pageView
.
textLayer
===
"object"
;
const
hasAnnotationLayer
=
pageView
.
annotationLayer
&&
typeof
pageView
.
annotationLayer
===
"object"
;
console
.
log
(
`当前页面PdfViewer状态: 实例存在=true, 文字层=
${
hasTextLayer
?
'启用'
:
'禁用'
}
, 注释层=
${
hasAnnotationLayer
?
'启用'
:
'禁用'
}
`
`当前页面PdfViewer状态: 实例存在=true, 文字层=
${
hasTextLayer
?
"启用"
:
"禁用"
}
, 注释层=
${
hasAnnotationLayer
?
"启用"
:
"禁用"
}
`
);
}
catch
(
error
)
{
console
.
log
(
`当前页面PdfViewer状态: 实例存在=true, 状态检查失败=
${
error
.
message
}
`
);
console
.
log
(
`当前页面PdfViewer状态: 实例存在=true, 状态检查失败=
${
error
.
message
}
`
);
}
}
else
{
console
.
log
(
`当前页面没有PdfViewer实例`
);
...
...
@@ -1714,25 +1811,25 @@ onMounted(async () => {
// 根据类型选择加载方式
if
(
props
.
isPdf
&&
props
.
pdfUrl
)
{
console
.
log
(
'组件挂载时加载PDF:'
,
props
.
pdfUrl
);
console
.
log
(
"组件挂载时加载PDF:"
,
props
.
pdfUrl
);
await
loadPdf
();
}
else
if
(
props
.
pages
&&
props
.
pages
.
length
>
0
)
{
console
.
log
(
'组件挂载时加载图片'
);
console
.
log
(
"组件挂载时加载图片"
);
await
loadImages
();
}
else
{
console
.
log
(
'组件挂载时无需加载内容'
);
console
.
log
(
"组件挂载时无需加载内容"
);
}
initAudio
();
window
.
addEventListener
(
"keydown"
,
handleKeyDown
);
// 添加防抖的resize监听器
console
.
log
(
'绑定resize事件监听器'
);
console
.
log
(
"绑定resize事件监听器"
);
window
.
addEventListener
(
"resize"
,
()
=>
{
console
.
log
(
'resize事件触发'
);
console
.
log
(
"resize事件触发"
);
clearTimeout
(
resizeTimeout
);
resizeTimeout
=
setTimeout
(()
=>
{
console
.
log
(
'调用handleResize'
);
console
.
log
(
"调用handleResize"
);
handleResize
();
},
250
);
});
...
...
@@ -1740,7 +1837,7 @@ onMounted(async () => {
// Load directory images
loadDirectoryImages
();
console
.
log
(
'onMounted完成,事件绑定完成'
);
console
.
log
(
"onMounted完成,事件绑定完成"
);
});
onUnmounted
(()
=>
{
...
...
@@ -1757,11 +1854,11 @@ onUnmounted(() => {
// 清理PdfViewer实例
pdfViewerInstances
.
value
.
forEach
((
pageView
)
=>
{
if
(
pageView
&&
typeof
pageView
.
destroy
===
'function'
)
{
if
(
pageView
&&
typeof
pageView
.
destroy
===
"function"
)
{
try
{
pageView
.
destroy
();
}
catch
(
error
)
{
console
.
warn
(
'清理PdfViewer实例时出错:'
,
error
);
console
.
warn
(
"清理PdfViewer实例时出错:"
,
error
);
}
}
});
...
...
@@ -1863,22 +1960,13 @@ const showGuide = () => {
guidePcRef
.
value
.
show
();
}
};
defineExpose
({
goToPage
,
});
</
script
>
<
style
scoped
lang=
"scss"
>
// .page {
// width: 100%;
// height: 100%;
// background-color: white !important; // 确保翻页时有白色背景
// overflow: hidden;
// backface-visibility: visible; // 确保背面可见
// img {
// width: 100%;
// height: 100%;
// object-fit: contain;
// }
// }
.book-reader-container
{
position
:
relative
;
width
:
100%
;
...
...
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 @@
<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
2
0px
;
padding
:
0
4
0px
;
.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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论