[skip ci] web: 重构章节内容及其进度
This commit is contained in:
@@ -4,7 +4,7 @@ import { ElMessage } from "element-plus/es";
|
|||||||
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */
|
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */
|
||||||
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */
|
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */
|
||||||
|
|
||||||
const { hostname, port } = new URL(import.meta.env.VITE_API || location.href);
|
const { hostname, port } = new URL(import.meta.env.VITE_API || location.origin);
|
||||||
|
|
||||||
const isSourecEditor = /source/i.test(location.href);
|
const isSourecEditor = /source/i.test(location.href);
|
||||||
const APIExceptionHandler = (error) => {
|
const APIExceptionHandler = (error) => {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="title" wordCount="0">{{ title }}</div>
|
<div class="title" data-chapterpos="0" ref="titleRef">{{ title }}</div>
|
||||||
<div
|
<div
|
||||||
v-for="(para, index) in contents"
|
v-for="(para, index) in contents"
|
||||||
:key="index"
|
:key="index"
|
||||||
:wordCount="wordCounts[index]"
|
ref="paragraphRef"
|
||||||
|
:data-chapterpos="chapterPos[index]"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="full"
|
class="full"
|
||||||
@@ -18,8 +19,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getImageFromLegado, isLegadoUrl } from "@/plugins/utils";
|
import { getImageFromLegado, isLegadoUrl } from "@/plugins/utils";
|
||||||
|
import jump from "@/plugins/jump";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
chapterIndex: { type: Number, required: true },
|
||||||
contents: { type: Array, required: true },
|
contents: { type: Array, required: true },
|
||||||
title: { type: String, required: true },
|
title: { type: String, required: true },
|
||||||
spacing: { type: Object, required: true },
|
spacing: { type: Object, required: true },
|
||||||
@@ -43,8 +46,60 @@ const calculateWordCount = (paragraph) => {
|
|||||||
const imagePlaceHolder = " ";
|
const imagePlaceHolder = " ";
|
||||||
return paragraph.replaceAll(imgPattern, imagePlaceHolder).length;
|
return paragraph.replaceAll(imgPattern, imagePlaceHolder).length;
|
||||||
};
|
};
|
||||||
const wordCounts = computed(() => {
|
const chapterPos = computed(() => {
|
||||||
return Array.from(props.contents, (content) => calculateWordCount(content));
|
let pos = -1;
|
||||||
|
return Array.from(props.contents, (content) => {
|
||||||
|
pos += calculateWordCount(content) + 1; //计算上一段的换行符
|
||||||
|
return pos;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const titleRef = ref();
|
||||||
|
const paragraphRef = ref();
|
||||||
|
const scrollToReadedLength = (length) => {
|
||||||
|
if (length === 0) return;
|
||||||
|
console.log("已读长度", length);
|
||||||
|
let paragraphIndex = chapterPos.value.findIndex(
|
||||||
|
(wordCount) => wordCount >= length
|
||||||
|
);
|
||||||
|
if (paragraphIndex === -1) return;
|
||||||
|
nextTick(() => {
|
||||||
|
jump(paragraphRef.value[paragraphIndex], {
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
defineExpose({
|
||||||
|
scrollToReadedLength,
|
||||||
|
});
|
||||||
|
const observer = ref(null);
|
||||||
|
const emit = defineEmits(["readedLengthChange"]);
|
||||||
|
onMounted(() => {
|
||||||
|
observer.value = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
for (let { target, isIntersecting } of entries) {
|
||||||
|
if (isIntersecting) {
|
||||||
|
emit(
|
||||||
|
"readedLengthChange",
|
||||||
|
props.chapterIndex,
|
||||||
|
parseInt(target.dataset.chapterpos)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rootMargin: `0px 0px -${window.innerHeight - 24}px 0px`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
observer.value.observe(titleRef.value);
|
||||||
|
paragraphRef.value.forEach((element) => {
|
||||||
|
observer.value.observe(element);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
observer.value?.disconnect();
|
||||||
|
observer.value = null;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,23 @@ export const useBookStore = defineStore("book", {
|
|||||||
readSettingsVisible: false,
|
readSettingsVisible: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getters: {
|
||||||
|
bookProgress: (state) => {
|
||||||
|
if (state.catalog.length == 0) return;
|
||||||
|
// @ts-ignore
|
||||||
|
const { index, chapterPos, bookName, bookAuthor } = state.readingBook;
|
||||||
|
let title = state.catalog[index]?.title;
|
||||||
|
if (!title) return;
|
||||||
|
return {
|
||||||
|
name: bookName,
|
||||||
|
author: bookAuthor,
|
||||||
|
durChapterIndex: index,
|
||||||
|
durChapterPos: chapterPos,
|
||||||
|
durChapterTime: new Date().getTime(),
|
||||||
|
durChapterTitle: title,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setConnectStatus(connectStatus) {
|
setConnectStatus(connectStatus) {
|
||||||
this.connectStatus = connectStatus;
|
this.connectStatus = connectStatus;
|
||||||
@@ -81,21 +98,9 @@ export const useBookStore = defineStore("book", {
|
|||||||
this.searchBooks = [];
|
this.searchBooks = [];
|
||||||
},
|
},
|
||||||
//保存进度到app
|
//保存进度到app
|
||||||
async saveBookProcess() {
|
async saveBookProgress() {
|
||||||
if (this.catalog.length == 0) return;
|
if (!this.bookProgress) return Promise.resolve();
|
||||||
// @ts-ignore
|
return API.saveBookProcess(this.bookProgress);
|
||||||
const { index, chapterPos, bookName, bookAuthor } = this.readingBook;
|
|
||||||
let title = this.catalog[index]?.title;
|
|
||||||
if (!title) return;
|
|
||||||
|
|
||||||
API.saveBookProcess({
|
|
||||||
name: bookName,
|
|
||||||
author: bookAuthor,
|
|
||||||
durChapterIndex: index,
|
|
||||||
durChapterPos: chapterPos,
|
|
||||||
durChapterTime: new Date().getTime(),
|
|
||||||
durChapterTitle: title,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -88,11 +88,14 @@
|
|||||||
ref="chapter"
|
ref="chapter"
|
||||||
>
|
>
|
||||||
<chapter-content
|
<chapter-content
|
||||||
|
ref="chapterRef"
|
||||||
|
:chapterIndex="data.index"
|
||||||
:contents="data.content"
|
:contents="data.content"
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:spacing="store.config.spacing"
|
:spacing="store.config.spacing"
|
||||||
:fontSize="fontSize"
|
:fontSize="fontSize"
|
||||||
:fontFamily="fontFamily"
|
:fontFamily="fontFamily"
|
||||||
|
@readedLengthChange="onReadedLengthChange"
|
||||||
v-if="showContent"
|
v-if="showContent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -109,10 +112,10 @@ import settings from "@/plugins/config";
|
|||||||
import API from "@api";
|
import API from "@api";
|
||||||
import loadingSvg from "@element-plus/icons-svg/loading.svg?raw";
|
import loadingSvg from "@element-plus/icons-svg/loading.svg?raw";
|
||||||
|
|
||||||
|
// loading spinner
|
||||||
const showLoading = ref(false);
|
const showLoading = ref(false);
|
||||||
const loadingSerive = ref(null);
|
const loadingSerive = ref(null);
|
||||||
const content = ref();
|
const content = ref();
|
||||||
|
|
||||||
watch(showLoading, (loading) => {
|
watch(showLoading, (loading) => {
|
||||||
if (!loading) return loadingSerive.value?.close();
|
if (!loading) return loadingSerive.value?.close();
|
||||||
loadingSerive.value = ElLoading.service({
|
loadingSerive.value = ElLoading.service({
|
||||||
@@ -132,13 +135,6 @@ try {
|
|||||||
localStorage.removeItem("config");
|
localStorage.removeItem("config");
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref();
|
|
||||||
const noPoint = ref(true);
|
|
||||||
const showToolBar = ref(false);
|
|
||||||
const chapterData = ref([]);
|
|
||||||
const scrollObserve = ref(null);
|
|
||||||
const readingObserve = ref(null);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
catalog,
|
catalog,
|
||||||
popCataVisible,
|
popCataVisible,
|
||||||
@@ -147,6 +143,7 @@ const {
|
|||||||
showContent,
|
showContent,
|
||||||
config,
|
config,
|
||||||
readingBook,
|
readingBook,
|
||||||
|
bookProgress,
|
||||||
} = storeToRefs(store);
|
} = storeToRefs(store);
|
||||||
const chapterPos = computed({
|
const chapterPos = computed({
|
||||||
get: () => readingBook.value.chapterPos,
|
get: () => readingBook.value.chapterPos,
|
||||||
@@ -201,6 +198,7 @@ const chapterTheme = computed(() => {
|
|||||||
width: readWidth.value,
|
width: readWidth.value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const showToolBar = ref(false);
|
||||||
const leftBarTheme = computed(() => {
|
const leftBarTheme = computed(() => {
|
||||||
return {
|
return {
|
||||||
background: popupColor.value,
|
background: popupColor.value,
|
||||||
@@ -221,28 +219,25 @@ const rightBarTheme = computed(() => {
|
|||||||
});
|
});
|
||||||
const isNight = computed(() => theme.value == 6);
|
const isNight = computed(() => theme.value == 6);
|
||||||
|
|
||||||
watchEffect(() => {
|
// 顶部底部跳转
|
||||||
if (chapterData.value.length > 0) {
|
|
||||||
store.setContentLoading(false);
|
|
||||||
//添加章节内容到observe
|
|
||||||
addReadingObserve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
document.title = catalog.value[chapterIndex.value]?.title || document.title;
|
|
||||||
store.saveBookProcess();
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (!infiniteLoading.value) {
|
|
||||||
scrollObserve.value?.disconnect();
|
|
||||||
} else {
|
|
||||||
scrollObserve.value?.observe(loading.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const top = ref();
|
const top = ref();
|
||||||
|
const bottom = ref();
|
||||||
|
const toTop = () => {
|
||||||
|
jump(top.value);
|
||||||
|
};
|
||||||
|
const toBottom = () => {
|
||||||
|
jump(bottom.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 书架路由切换
|
||||||
|
const router = useRouter();
|
||||||
|
const toShelf = () => {
|
||||||
|
router.push("/");
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取章节内容
|
||||||
|
const chapterData = ref([]);
|
||||||
|
const noPoint = ref(true);
|
||||||
const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
||||||
if (reloadChapter) {
|
if (reloadChapter) {
|
||||||
//展示进度条
|
//展示进度条
|
||||||
@@ -252,6 +247,7 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
|||||||
jump(top.value, { duration: 0 });
|
jump(top.value, { duration: 0 });
|
||||||
//从目录,按钮切换章节时保存进度 预加载时不保存
|
//从目录,按钮切换章节时保存进度 预加载时不保存
|
||||||
saveReadingBookProgressToBrowser(index, chapterPos);
|
saveReadingBookProgressToBrowser(index, chapterPos);
|
||||||
|
chapterData.value = [];
|
||||||
}
|
}
|
||||||
let bookUrl = sessionStorage.getItem("bookUrl");
|
let bookUrl = sessionStorage.getItem("bookUrl");
|
||||||
let { title, index: chapterIndex } = catalog.value[index];
|
let { title, index: chapterIndex } = catalog.value[index];
|
||||||
@@ -261,12 +257,12 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
|||||||
if (res.data.isSuccess) {
|
if (res.data.isSuccess) {
|
||||||
let data = res.data.data;
|
let data = res.data.data;
|
||||||
let content = data.split(/\n+/);
|
let content = data.split(/\n+/);
|
||||||
updateChapterData({ index, content, title }, reloadChapter);
|
chapterData.value.push({ index, content, title });
|
||||||
if (reloadChapter) toChapterPos(chapterPos);
|
if (reloadChapter) toChapterPos(chapterPos);
|
||||||
} else {
|
} else {
|
||||||
ElMessage({ message: res.data.errorMsg, type: "error" });
|
ElMessage({ message: res.data.errorMsg, type: "error" });
|
||||||
let content = [res.data.errorMsg];
|
let content = [res.data.errorMsg];
|
||||||
updateChapterData({ index, content, title }, reloadChapter);
|
chapterData.value.push({ index, content, title });
|
||||||
}
|
}
|
||||||
store.setContentLoading(true);
|
store.setContentLoading(true);
|
||||||
showLoading.value = false;
|
showLoading.value = false;
|
||||||
@@ -279,55 +275,69 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
|||||||
(err) => {
|
(err) => {
|
||||||
ElMessage({ message: "获取章节内容失败", type: "error" });
|
ElMessage({ message: "获取章节内容失败", type: "error" });
|
||||||
let content = ["获取章节内容失败!"];
|
let content = ["获取章节内容失败!"];
|
||||||
updateChapterData({ index, content, title }, reloadChapter);
|
chapterData.value.push({ index, content, title });
|
||||||
showLoading.value = false;
|
showLoading.value = false;
|
||||||
store.setShowContent(true);
|
store.setShowContent(true);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 章节进度跳转和计算
|
||||||
const chapter = ref();
|
const chapter = ref();
|
||||||
|
const chapterRef = ref();
|
||||||
const toChapterPos = (pos) => {
|
const toChapterPos = (pos) => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
let wordCount = 0;
|
if (chapterRef.value.length === 1)
|
||||||
if (chapter.value.length != 1) return;
|
chapterRef.value[0].scrollToReadedLength(pos);
|
||||||
for (let element of chapter.value[0].children) {
|
|
||||||
wordCount += parseInt(element.getAttribute("wordCount")) + 1; //计算换行符
|
|
||||||
if (wordCount - 1 >= pos) {
|
|
||||||
//最后一段没有换行符
|
|
||||||
jump(element, {
|
|
||||||
duration: 0,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
//计算当前章节阅读的字数
|
const onReadedLengthChange = (index, pos) => {
|
||||||
const computeChapterPos = () => {
|
saveReadingBookProgressToBrowser(index, pos);
|
||||||
if (chapter.value.length == 0) return;
|
};
|
||||||
//计算当前阅读进度对应的element
|
|
||||||
let chapterElement = chapter.value.find(
|
// 文档标题
|
||||||
(element) => element.getAttribute("chapterIndex") == chapterIndex.value
|
watchEffect(() => {
|
||||||
|
document.title = catalog.value[chapterIndex.value]?.title || document.title;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阅读记录保存浏览器
|
||||||
|
const saveReadingBookProgressToBrowser = (index, pos) => {
|
||||||
|
//保存localStorage
|
||||||
|
let bookUrl = sessionStorage.getItem("bookUrl");
|
||||||
|
var book = JSON.parse(localStorage.getItem(bookUrl));
|
||||||
|
book.index = index;
|
||||||
|
book.chapterPos = pos;
|
||||||
|
localStorage.setItem(bookUrl, JSON.stringify(book));
|
||||||
|
//最近阅读
|
||||||
|
book = JSON.parse(localStorage.getItem("readingRecent"));
|
||||||
|
book.chapterIndex = index;
|
||||||
|
book.chapterPos = pos;
|
||||||
|
localStorage.setItem("readingRecent", JSON.stringify(book));
|
||||||
|
//保存vuex
|
||||||
|
chapterIndex.value = index;
|
||||||
|
chapterPos.value = pos;
|
||||||
|
//保存sessionStorage
|
||||||
|
sessionStorage.setItem("chapterIndex", index);
|
||||||
|
sessionStorage.setItem("chapterPos", String(pos));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 进度同步
|
||||||
|
// 返回同步 同步请求会在获取书架前完成
|
||||||
|
// 刷新 关闭页面
|
||||||
|
const syncBookProgress = () => {
|
||||||
|
console.log("page hide");
|
||||||
|
if (!bookProgress.value) return;
|
||||||
|
// 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
|
||||||
|
navigator.sendBeacon(
|
||||||
|
`${import.meta.env.VITE_API || location.origin}/saveBookProgress`,
|
||||||
|
JSON.stringify(bookProgress.value)
|
||||||
);
|
);
|
||||||
if (!chapterElement) return;
|
|
||||||
//计算已读字数
|
|
||||||
let mChapterPos = 0;
|
|
||||||
for (let paragraph of chapterElement.children) {
|
|
||||||
mChapterPos += parseInt(paragraph.getAttribute("wordCount")) + 1; //计算换行符
|
|
||||||
if (paragraph.getBoundingClientRect().top >= 0) {
|
|
||||||
chapterPos.value = mChapterPos - 1; //最后一段没有换行符
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const bottom = ref();
|
|
||||||
const toTop = () => {
|
|
||||||
jump(top.value);
|
|
||||||
};
|
|
||||||
const toBottom = () => {
|
|
||||||
jump(bottom.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 定时同步
|
||||||
|
|
||||||
|
// 章节切换
|
||||||
const toNextChapter = () => {
|
const toNextChapter = () => {
|
||||||
store.setContentLoading(true);
|
store.setContentLoading(true);
|
||||||
let index = chapterIndex.value + 1;
|
let index = chapterIndex.value + 1;
|
||||||
@@ -360,42 +370,33 @@ const toPreChapter = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const saveReadingBookProgressToBrowser = (index, pos = chapterPos.value) => {
|
|
||||||
//保存localStorage
|
// 无限滚动
|
||||||
let bookUrl = sessionStorage.getItem("bookUrl");
|
const scrollObserve = ref(null);
|
||||||
var book = JSON.parse(localStorage.getItem(bookUrl));
|
const loading = ref();
|
||||||
book.index = index;
|
watchEffect(() => {
|
||||||
book.chapterPos = pos;
|
if (!infiniteLoading.value) {
|
||||||
localStorage.setItem(bookUrl, JSON.stringify(book));
|
scrollObserve.value?.disconnect();
|
||||||
//最近阅读
|
} else {
|
||||||
book = JSON.parse(localStorage.getItem("readingRecent"));
|
scrollObserve.value?.observe(loading.value);
|
||||||
book.chapterIndex = index;
|
|
||||||
book.chapterPos = pos;
|
|
||||||
localStorage.setItem("readingRecent", JSON.stringify(book));
|
|
||||||
//保存vuex
|
|
||||||
chapterIndex.value = index;
|
|
||||||
chapterPos.value = pos;
|
|
||||||
//保存sessionStorage
|
|
||||||
sessionStorage.setItem("chapterIndex", index);
|
|
||||||
sessionStorage.setItem("chapterPos", String(pos));
|
|
||||||
};
|
|
||||||
const updateChapterData = async (data, reloadChapter) => {
|
|
||||||
if (reloadChapter) {
|
|
||||||
chapterData.value.splice(0);
|
|
||||||
}
|
}
|
||||||
chapterData.value.push(data);
|
});
|
||||||
};
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
let index = chapterData.value.slice(-1)[0].index;
|
let index = chapterData.value.slice(-1)[0].index;
|
||||||
if (catalog.value.length - 1 > index) {
|
if (catalog.value.length - 1 > index) {
|
||||||
getContent(index + 1, false);
|
getContent(index + 1, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const router = useRouter();
|
// IntersectionObserver回调 底部加载
|
||||||
const toShelf = () => {
|
const handleIScrollObserve = (entries) => {
|
||||||
router.push("/");
|
if (showLoading.value) return;
|
||||||
|
for (let { isIntersecting } of entries) {
|
||||||
|
if (!isIntersecting) return;
|
||||||
|
loadMore();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
//监听方向键
|
|
||||||
|
// 监听方向键
|
||||||
const handleKeyPress = (event) => {
|
const handleKeyPress = (event) => {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case "ArrowLeft":
|
case "ArrowLeft":
|
||||||
@@ -438,52 +439,6 @@ const handleKeyPress = (event) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//IntersectionObserver回调 底部加载
|
|
||||||
const handleIScrollObserve = (entries) => {
|
|
||||||
if (showLoading.value) return;
|
|
||||||
for (let { isIntersecting } of entries) {
|
|
||||||
if (!isIntersecting) return;
|
|
||||||
loadMore();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//IntersectionObserver回调 当前阅读章节序号
|
|
||||||
const handleIReadingObserve = (entries) => {
|
|
||||||
nextTick(() => {
|
|
||||||
for (let { isIntersecting, target, boundingClientRect } of entries) {
|
|
||||||
let chapterTitleIndex = parseInt(target.getAttribute("chapterIndex"));
|
|
||||||
if (isIntersecting) {
|
|
||||||
chapterIndex.value = chapterTitleIndex;
|
|
||||||
} else {
|
|
||||||
if (boundingClientRect.top < 0) {
|
|
||||||
chapterIndex.value = chapterTitleIndex + 1;
|
|
||||||
} else {
|
|
||||||
chapterIndex.value = chapterTitleIndex - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
//添加所有章节到observe
|
|
||||||
const addReadingObserve = () => {
|
|
||||||
nextTick(() => {
|
|
||||||
let chapterElements = chapter.value;
|
|
||||||
if (!chapterElements) return;
|
|
||||||
chapterElements.forEach((el) => readingObserve.value.observe(el));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
|
||||||
computeChapterPos();
|
|
||||||
saveReadingBookProgressToBrowser(chapterIndex.value);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
window.addEventListener("beforeunload", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.returnValue = "";
|
|
||||||
alert(111);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
showLoading.value = true;
|
showLoading.value = true;
|
||||||
@@ -523,13 +478,12 @@ onMounted(() => {
|
|||||||
|
|
||||||
getContent(chapterIndex, true, chapterPos);
|
getContent(chapterIndex, true, chapterPos);
|
||||||
window.addEventListener("keyup", handleKeyPress);
|
window.addEventListener("keyup", handleKeyPress);
|
||||||
|
window.addEventListener("visibilitychange", syncBookProgress);
|
||||||
//监听底部加载
|
//监听底部加载
|
||||||
scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {
|
scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {
|
||||||
rootMargin: "-100% 0% 20% 0%",
|
rootMargin: "-100% 0% 20% 0%",
|
||||||
});
|
});
|
||||||
infiniteLoading.value && scrollObserve.value.observe(loading.value);
|
infiniteLoading.value && scrollObserve.value.observe(loading.value);
|
||||||
//监听当前阅读章节
|
|
||||||
readingObserve.value = new IntersectionObserver(handleIReadingObserve);
|
|
||||||
//第二次点击同一本书 页面标题不会变化
|
//第二次点击同一本书 页面标题不会变化
|
||||||
document.title = null;
|
document.title = null;
|
||||||
document.title = bookName + " | " + catalog.value[chapterIndex].title;
|
document.title = bookName + " | " + catalog.value[chapterIndex].title;
|
||||||
@@ -544,10 +498,10 @@ onMounted(() => {
|
|||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener("keyup", handleKeyPress);
|
window.removeEventListener("keyup", handleKeyPress);
|
||||||
|
window.removeEventListener("visibilitychange", syncBookProgress);
|
||||||
readSettingsVisible.value = false;
|
readSettingsVisible.value = false;
|
||||||
popCataVisible.value = false;
|
popCataVisible.value = false;
|
||||||
scrollObserve.value?.disconnect();
|
scrollObserve.value?.disconnect();
|
||||||
readingObserve.value?.disconnect();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ const toDetail = (bookUrl, bookName, bookAuthor, chapterIndex, chapterPos) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(() => {
|
||||||
//获取最近阅读书籍
|
//获取最近阅读书籍
|
||||||
let readingRecentStr = localStorage.getItem("readingRecent");
|
let readingRecentStr = localStorage.getItem("readingRecent");
|
||||||
if (readingRecentStr != null) {
|
if (readingRecentStr != null) {
|
||||||
@@ -197,8 +197,10 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
showLoading.value = true;
|
showLoading.value = true;
|
||||||
await store.saveBookProcess();
|
store
|
||||||
fetchBookShelfData();
|
.saveBookProgress()
|
||||||
|
//确保各种网络情况下同步请求先完成
|
||||||
|
.finally(fetchBookShelfData);
|
||||||
});
|
});
|
||||||
const fetchBookShelfData = () => {
|
const fetchBookShelfData = () => {
|
||||||
API.getBookShelf()
|
API.getBookShelf()
|
||||||
|
|||||||
Reference in New Issue
Block a user