Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pic-reader
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
龙菲
pic-reader
Commits
49dfc88f
提交
49dfc88f
authored
4月 24, 2025
作者:
龙菲
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加懒加载
上级
5c9fde46
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
196 行增加
和
98 行删除
+196
-98
BookReader.vue
src/components/BookReader.vue
+195
-90
request copy.js
src/utils/request copy.js
+1
-8
没有找到文件。
src/components/BookReader.vue
浏览文件 @
49dfc88f
...
@@ -12,8 +12,13 @@
...
@@ -12,8 +12,13 @@
'odd': !page.isWide
&&
index % 2 !== 0,
'odd': !page.isWide
&&
index % 2 !== 0,
'even': !page.isWide
&&
index % 2 === 1
'even': !page.isWide
&&
index % 2 === 1
}]">
}]">
<img
:src=
"page.src"
:alt=
"`第 $
{page.page_num} 页`" @error="handleImageError(index)"
<img
:class="['page-image', { 'wide-image': page.isWide }]" />
:data-page-index=
"index"
:src=
"page.src"
:alt=
"`第 $
{page.page_num} 页`"
@error="handleImageError(index)"
:class="['page-image', { 'wide-image': page.isWide }]"
/>
<!-- 添加小图叠加层 -->
<!-- 添加小图叠加层 -->
<div
v-if=
"page.images && page.images.length > 0"
class=
"small-images-overlay"
>
<div
v-if=
"page.images && page.images.length > 0"
class=
"small-images-overlay"
>
...
@@ -82,84 +87,111 @@ const loading = ref(true)
...
@@ -82,84 +87,111 @@ const loading = ref(true)
const
showExitMessage
=
ref
(
false
)
const
showExitMessage
=
ref
(
false
)
const
zoomLevel
=
ref
(
1
)
const
zoomLevel
=
ref
(
1
)
const
isInitialized
=
ref
(
false
)
const
isInitialized
=
ref
(
false
)
const
imageMetadata
=
ref
([])
const
imageMetadata
=
ref
([])
const
imageObserver
=
ref
(
null
)
const
loadingQueue
=
ref
(
new
Set
())
const
maxConcurrentLoads
=
5
const
imageCache
=
ref
(
new
Map
())
const
loadingTimeouts
=
ref
(
new
Map
())
// 修改预览相关的状态
// 修改预览相关的状态
const
showViewer
=
ref
(
false
)
const
showViewer
=
ref
(
false
)
const
currentImageIndex
=
ref
(
0
)
const
currentImageIndex
=
ref
(
0
)
const
previewImages
=
ref
([])
const
previewImages
=
ref
([])
// 处理页面数据,移除宽图处理
// 将 processedPages 改为普通数据属性
const
processedPages
=
computed
(()
=>
{
const
processedPages
=
ref
([])
return
props
.
pages
.
map
((
item
,
index
)
=>
({
src
:
item
.
page_url
,
// 修改处理页面的函数,只处理必要的属性
const
processPages
=
(
pages
)
=>
{
return
pages
.
map
((
item
,
index
)
=>
({
src
:
''
,
// 初始不设置src
originalIndex
:
index
,
originalIndex
:
index
,
page_num
:
item
.
page_num
,
// 添加页码
page_num
:
item
.
page_num
,
images
:
item
.
images
||
[],
images
:
item
.
images
||
[],
isWide
:
item
.
isWide
isWide
:
false
,
// 初始设置为false
isLoaded
:
false
,
url
:
item
.
page_url
// 保存原始URL
}))
}))
})
const
checkImageDimensions
=
async
(
src
)
=>
{
return
new
Promise
((
resolve
)
=>
{
const
img
=
new
Image
()
img
.
onload
=
()
=>
{
// 如果宽高比大于 1.8,认为是宽图
const
isWide
=
(
img
.
width
/
img
.
height
)
>
1.8
resolve
({
width
:
img
.
width
,
height
:
img
.
height
,
isWide
})
}
img
.
onerror
=
()
=>
{
resolve
({
width
:
0
,
height
:
0
,
isWide
:
false
})
}
img
.
src
=
src
})
}
}
const
destroyTurn
=
()
=>
{
// 修改图片加载函数
if
(
magazine
.
value
&&
isInitialized
.
value
)
{
const
loadImage
=
async
(
pageIndex
)
=>
{
try
{
const
page
=
processedPages
.
value
[
pageIndex
]
const
$magazine
=
$
(
magazine
.
value
)
if
(
!
page
||
page
.
isLoaded
)
return
true
// 移除所有 turn.js 相关的事件和数据
$magazine
.
off
(
'.turn'
)
const
src
=
page
.
url
$magazine
.
removeData
(
'turn'
)
if
(
imageCache
.
value
.
has
(
src
))
{
isInitialized
.
value
=
false
page
.
isLoaded
=
true
}
catch
(
error
)
{
page
.
src
=
src
console
.
error
(
'Error destroying turn.js:'
,
error
)
return
true
}
}
if
(
loadingQueue
.
value
.
size
>=
maxConcurrentLoads
)
{
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
50
))
return
loadImage
(
pageIndex
)
}
loadingQueue
.
value
.
add
(
pageIndex
)
const
timeoutId
=
setTimeout
(()
=>
{
loadingQueue
.
value
.
delete
(
pageIndex
)
loadingTimeouts
.
value
.
delete
(
pageIndex
)
},
5000
)
loadingTimeouts
.
value
.
set
(
pageIndex
,
timeoutId
)
try
{
const
img
=
new
Image
()
await
new
Promise
((
resolve
,
reject
)
=>
{
img
.
onload
=
()
=>
{
clearTimeout
(
timeoutId
)
loadingTimeouts
.
value
.
delete
(
pageIndex
)
imageCache
.
value
.
set
(
src
,
true
)
page
.
isLoaded
=
true
page
.
src
=
src
resolve
()
}
img
.
onerror
=
()
=>
{
clearTimeout
(
timeoutId
)
loadingTimeouts
.
value
.
delete
(
pageIndex
)
reject
(
new
Error
(
'Image load failed'
))
}
img
.
src
=
src
})
return
true
}
catch
(
error
)
{
return
false
}
finally
{
loadingQueue
.
value
.
delete
(
pageIndex
)
}
}
}
}
const
loadVisiblePages
=
(
currentPage
)
=>
{
// 修改加载可见页面的函数
const
loadVisiblePages
=
async
(
currentPage
)
=>
{
if
(
!
magazine
.
value
)
return
if
(
!
magazine
.
value
)
return
const
$magazine
=
$
(
magazine
.
value
)
const
$magazine
=
$
(
magazine
.
value
)
const
totalPages
=
$magazine
.
turn
(
'pages'
)
const
totalPages
=
$magazine
.
turn
(
'pages'
)
//
计算需要加载的页面范围(当前页和前后各两页)
//
只加载当前页和下一页
const
startPage
=
Math
.
max
(
1
,
currentPage
-
2
)
const
startPage
=
currentPage
const
endPage
=
Math
.
min
(
totalPages
,
currentPage
+
2
)
const
endPage
=
Math
.
min
(
totalPages
,
currentPage
+
1
)
// 加载范围内的页面
const
loadTasks
=
[]
for
(
let
i
=
startPage
;
i
<=
endPage
;
i
++
)
{
for
(
let
i
=
startPage
;
i
<=
endPage
;
i
++
)
{
const
$page
=
$magazine
.
find
(
`.p
${
i
}
`
)
const
pageIndex
=
i
-
1
const
$img
=
$page
.
find
(
'img'
)
if
(
pageIndex
>=
0
&&
pageIndex
<
processedPages
.
value
.
length
&&
!
processedPages
.
value
[
pageIndex
].
isLoaded
)
{
loadTasks
.
push
(
loadImage
(
pageIndex
))
if
(
$img
.
length
&&
!
$img
[
0
].
complete
)
{
// 使用新的 Image 对象预加载
const
img
=
new
Image
()
img
.
onload
=
()
=>
{
$img
[
0
].
src
=
img
.
src
}
img
.
src
=
$img
[
0
].
src
}
}
}
}
try
{
await
Promise
.
all
(
loadTasks
)
}
catch
(
error
)
{
console
.
error
(
'加载页面失败:'
,
error
)
}
}
}
// 修改初始化函数
const
initBook
=
async
()
=>
{
const
initBook
=
async
()
=>
{
if
(
!
magazine
.
value
||
!
processedPages
.
value
.
length
)
return
if
(
!
magazine
.
value
||
!
processedPages
.
value
.
length
)
return
...
@@ -171,13 +203,11 @@ const initBook = async () => {
...
@@ -171,13 +203,11 @@ const initBook = async () => {
const
pageHeight
=
Math
.
min
(
800
,
viewportHeight
-
100
)
const
pageHeight
=
Math
.
min
(
800
,
viewportHeight
-
100
)
await
nextTick
()
await
nextTick
()
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
100
))
try
{
try
{
const
$magazine
=
$
(
magazine
.
value
)
const
$magazine
=
$
(
magazine
.
value
)
const
$viewport
=
$
(
'.magazine-viewport'
)
const
$viewport
=
$
(
'.magazine-viewport'
)
// 确保视口样式固定
$viewport
.
css
({
$viewport
.
css
({
'overflow'
:
'hidden'
,
'overflow'
:
'hidden'
,
'position'
:
'relative'
,
'position'
:
'relative'
,
...
@@ -185,7 +215,6 @@ const initBook = async () => {
...
@@ -185,7 +215,6 @@ const initBook = async () => {
'height'
:
'100%'
'height'
:
'100%'
})
})
// 设置页面样式
$magazine
.
children
().
each
(
function
()
{
$magazine
.
children
().
each
(
function
()
{
const
$page
=
$
(
this
)
const
$page
=
$
(
this
)
$page
.
css
({
$page
.
css
({
...
@@ -195,7 +224,6 @@ const initBook = async () => {
...
@@ -195,7 +224,6 @@ const initBook = async () => {
height
:
pageHeight
height
:
pageHeight
})
})
// 设置图片样式
const
$img
=
$page
.
find
(
'img'
)
const
$img
=
$page
.
find
(
'img'
)
if
(
$img
.
length
)
{
if
(
$img
.
length
)
{
$img
.
css
({
$img
.
css
({
...
@@ -206,7 +234,6 @@ const initBook = async () => {
...
@@ -206,7 +234,6 @@ const initBook = async () => {
}
}
})
})
// 初始化 turn.js
$magazine
.
turn
({
$magazine
.
turn
({
width
:
pageWidth
*
2
,
width
:
pageWidth
*
2
,
height
:
pageHeight
,
height
:
pageHeight
,
...
@@ -218,44 +245,28 @@ const initBook = async () => {
...
@@ -218,44 +245,28 @@ const initBook = async () => {
autoCenter
:
true
,
autoCenter
:
true
,
page
:
1
,
page
:
1
,
when
:
{
when
:
{
turning
:
(
event
,
page
,
view
)
=>
{
turning
:
async
(
event
,
page
,
view
)
=>
{
loading
.
value
=
true
loading
.
value
=
true
loadVisiblePages
(
page
)
await
loadVisiblePages
(
page
)
},
},
turned
:
(
event
,
page
,
view
)
=>
{
turned
:
async
(
event
,
page
,
view
)
=>
{
await
loadVisiblePages
(
page
)
loading
.
value
=
false
loading
.
value
=
false
loadVisiblePages
(
page
)
}
}
}
}
})
})
isInitialized
.
value
=
true
isInitialized
.
value
=
true
await
loadVisiblePages
(
1
)
// 初始加载前几页
loadVisiblePages
(
1
)
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'Turn.js initialization error:'
,
error
)
console
.
error
(
'Turn.js initialization error:'
,
error
)
}
}
}
}
// 修改加载图片函数
const
loadImages
=
async
()
=>
{
const
loadImages
=
async
()
=>
{
loading
.
value
=
true
loading
.
value
=
true
try
{
try
{
// 获取所有图片的尺寸信息
const
metadataPromises
=
props
.
pages
.
map
(
item
=>
checkImageDimensions
(
item
.
page_url
))
imageMetadata
.
value
=
await
Promise
.
all
(
metadataPromises
)
// 等待所有图片加载完成
await
Promise
.
all
(
props
.
pages
.
map
(
item
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
img
=
new
Image
()
img
.
onload
=
resolve
img
.
onerror
=
reject
img
.
src
=
item
.
page_url
})
}))
await
initBook
()
await
initBook
()
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'加载图片失败:'
,
error
)
console
.
error
(
'加载图片失败:'
,
error
)
...
@@ -264,8 +275,100 @@ const loadImages = async () => {
...
@@ -264,8 +275,100 @@ const loadImages = async () => {
}
}
}
}
const
handleImageError
=
(
index
)
=>
{
// 监听 props.pages 的变化
console
.
error
(
`图片加载失败:
${
props
.
pages
[
index
].
page_url
}
`
)
watch
(()
=>
props
.
pages
,
(
newPages
)
=>
{
if
(
newPages
&&
newPages
.
length
>
0
)
{
processedPages
.
value
=
processPages
(
newPages
)
loadImages
()
}
},
{
immediate
:
true
})
const
checkImageDimensions
=
async
(
src
)
=>
{
return
new
Promise
((
resolve
)
=>
{
const
img
=
new
Image
()
img
.
onload
=
()
=>
{
// 如果宽高比大于 1.8,认为是宽图
const
isWide
=
(
img
.
width
/
img
.
height
)
>
1.8
resolve
({
width
:
img
.
width
,
height
:
img
.
height
,
isWide
})
}
img
.
onerror
=
()
=>
{
resolve
({
width
:
0
,
height
:
0
,
isWide
:
false
})
}
img
.
src
=
src
})
}
const
destroyTurn
=
()
=>
{
if
(
magazine
.
value
&&
isInitialized
.
value
)
{
try
{
const
$magazine
=
$
(
magazine
.
value
)
// 移除所有 turn.js 相关的事件和数据
$magazine
.
off
(
'.turn'
)
$magazine
.
removeData
(
'turn'
)
isInitialized
.
value
=
false
}
catch
(
error
)
{
console
.
error
(
'Error destroying turn.js:'
,
error
)
}
}
}
// 初始化 Intersection Observer
const
initImageObserver
=
()
=>
{
imageObserver
.
value
=
new
IntersectionObserver
((
entries
)
=>
{
entries
.
forEach
(
entry
=>
{
if
(
entry
.
isIntersecting
)
{
const
img
=
entry
.
target
const
pageIndex
=
parseInt
(
img
.
dataset
.
pageIndex
)
if
(
!
processedPages
.
value
[
pageIndex
].
isLoaded
)
{
img
.
src
=
processedPages
.
value
[
pageIndex
].
src
processedPages
.
value
[
pageIndex
].
isLoaded
=
true
}
}
})
},
{
root
:
null
,
rootMargin
:
'50px'
,
threshold
:
0.1
})
}
// 修改图片错误处理函数
const
handleImageError
=
async
(
index
)
=>
{
const
src
=
processedPages
.
value
[
index
].
src
// 清除超时
if
(
loadingTimeouts
.
value
.
has
(
index
))
{
clearTimeout
(
loadingTimeouts
.
value
.
get
(
index
))
loadingTimeouts
.
value
.
delete
(
index
)
}
// 尝试重新加载
const
retryCount
=
3
for
(
let
i
=
0
;
i
<
retryCount
;
i
++
)
{
try
{
const
success
=
await
loadImage
(
index
)
if
(
success
)
{
return
}
}
catch
(
error
)
{
// 忽略错误
}
// 递增重试延迟
const
delay
=
1000
*
(
i
+
1
)
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
delay
))
}
// 如果所有重试都失败,使用占位图
const
$img
=
$
(
magazine
.
value
).
find
(
`.p
${
index
+
1
}
img`
)
if
(
$img
.
length
)
{
$img
[
0
].
src
=
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iI2YwZjBmMCIvPjx0ZXh0IHg9IjUwJSIgeT0iNTAlIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTYiIGZpbGw9IiM5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIj7lvIDlp4vmlbDmja7lupM8L3RleHQ+PC9zdmc+'
}
}
}
const
next
=
()
=>
{
const
next
=
()
=>
{
...
@@ -477,18 +580,20 @@ onMounted(async () => {
...
@@ -477,18 +580,20 @@ onMounted(async () => {
})
})
onUnmounted
(()
=>
{
onUnmounted
(()
=>
{
// 清除所有超时
loadingTimeouts
.
value
.
forEach
(
timeoutId
=>
clearTimeout
(
timeoutId
))
loadingTimeouts
.
value
.
clear
()
imageCache
.
value
.
clear
()
loadingQueue
.
value
.
clear
()
if
(
imageObserver
.
value
)
{
imageObserver
.
value
.
disconnect
()
}
destroyTurn
()
destroyTurn
()
window
.
removeEventListener
(
'keydown'
,
handleKeyDown
)
window
.
removeEventListener
(
'keydown'
,
handleKeyDown
)
window
.
removeEventListener
(
'resize'
,
handleResize
)
window
.
removeEventListener
(
'resize'
,
handleResize
)
})
})
// 监听页面数据变化
watch
(()
=>
props
.
pages
,
async
(
newPages
)
=>
{
if
(
newPages
&&
newPages
.
length
>
0
)
{
await
loadImages
()
}
},
{
deep
:
true
})
// 修改小图点击事件处理函数
// 修改小图点击事件处理函数
const
handleSmallImageClick
=
(
smallImage
,
pageNum
)
=>
{
const
handleSmallImageClick
=
(
smallImage
,
pageNum
)
=>
{
// 获取当前页面所有小图的URL
// 获取当前页面所有小图的URL
...
...
src/utils/request copy.js
浏览文件 @
49dfc88f
/*
* @Author: 龙菲 1373694886@qq.com
* @Date: 2025-04-23 22:37:01
* @LastEditors: 龙菲 1373694886@qq.com
* @LastEditTime: 2025-04-23 22:41:24
* @FilePath: \pic-reader\src\utils\request.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import
axios
from
'axios'
;
import
axios
from
'axios'
;
import
{
ElMessage
}
from
'element-plus'
;
import
{
ElMessage
}
from
'element-plus'
;
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论