mirror of
https://github.com/gedoor/legado.git
synced 2024-09-01 09:34:25 +08:00
fix(web): 修复进度同步和返回书架同步
This commit is contained in:
parent
271aa770b1
commit
02a215202c
@ -12,6 +12,10 @@
|
|||||||
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
||||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||||
|
|
||||||
|
**2023/05/09**
|
||||||
|
|
||||||
|
* web: 修复进度同步
|
||||||
|
|
||||||
**2023/05/08**
|
**2023/05/08**
|
||||||
|
|
||||||
* 更新cronet: 113.0.5672.76
|
* 更新cronet: 113.0.5672.76
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="title">{{ title }}</div>
|
<div class="title" wordCount="0">{{ title }}</div>
|
||||||
<div v-for="(para, index) in carray" :key="index">
|
<div
|
||||||
|
v-for="(para, index) in contents"
|
||||||
|
:key="index"
|
||||||
|
:wordCount="calculateWordCount(para)"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
class="full"
|
class="full"
|
||||||
v-if="/^\s*<img[^>]*src[^>]+>$/.test(para)"
|
v-if="/^\s*<img[^>]*src[^>]+>$/.test(para)"
|
||||||
@ -8,34 +12,19 @@
|
|||||||
@error.once="proxyImage"
|
@error.once="proxyImage"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<p v-else :style="style" v-html="para" />
|
<p v-else :style="{ fontFamily, fontSize }" v-html="para" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import config from "../plugins/config";
|
import { getImageFromLegado, isLegadoUrl } from "@/plugins/utils";
|
||||||
import { getImageFromLegado, isLegadoUrl } from "../plugins/utils";
|
|
||||||
|
|
||||||
const store = useBookStore();
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
carray: { 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 },
|
||||||
});
|
fontFamily: { type: String, required: true },
|
||||||
|
fontSize: { type: String, required: true },
|
||||||
const fontFamily = computed(() => {
|
|
||||||
if (store.config.font >= 0) {
|
|
||||||
return config.fonts[store.config.font];
|
|
||||||
}
|
|
||||||
return { fontFamily: store.config.customFontName };
|
|
||||||
});
|
|
||||||
const fontSize = computed(() => {
|
|
||||||
return store.config.fontSize + "px";
|
|
||||||
});
|
|
||||||
const style = computed(() => {
|
|
||||||
let style = fontFamily.value;
|
|
||||||
style.fontSize = fontSize.value;
|
|
||||||
return style;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const getImageSrc = (content) => {
|
const getImageSrc = (content) => {
|
||||||
@ -48,16 +37,13 @@ const proxyImage = (event) => {
|
|||||||
event.target.src = getImageFromLegado(event.target.src);
|
event.target.src = getImageFromLegado(event.target.src);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(fontSize, () => {
|
const calculateWordCount = (paragraph) => {
|
||||||
store.setShowContent(false);
|
const imgPattern = /<img[^>]*src="[^"]*(?:"[^>]+\})?"[^>]*>/g;
|
||||||
nextTick(() => {
|
//内嵌图片文字为1
|
||||||
store.setShowContent(true);
|
const imagePlaceHolder = " ";
|
||||||
});
|
return paragraph.trim().replaceAll(imgPattern, imagePlaceHolder).length;
|
||||||
});
|
};
|
||||||
|
|
||||||
const letterSpacing = computed(() => props.spacing.letter); //字间距 倍数
|
|
||||||
const lineSpacing = computed(() => 1 + props.spacing.line); //行距 基础行距1
|
|
||||||
const paragraphSpacing = computed(() => props.spacing.paragraph); //段距
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -72,9 +58,9 @@ p {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
letter-spacing: calc(v-bind("letterSpacing") * 1em);
|
letter-spacing: calc(v-bind("props.spacing.letter") * 1em);
|
||||||
line-height: v-bind("lineSpacing");
|
line-height: calc(1 + v-bind("props.spacing.line"));
|
||||||
margin: calc(v-bind("paragraphSpacing") * 1em) 0;
|
margin: calc(v-bind("props.spacing.paragraph") * 1em) 0;
|
||||||
|
|
||||||
:deep(img) {
|
:deep(img) {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
@ -55,16 +55,11 @@ var settings = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
fonts: [
|
fonts: [
|
||||||
{
|
"Microsoft YaHei, PingFangSC-Regular, HelveticaNeue-Light, Helvetica Neue Light, sans-serif",
|
||||||
fontFamily:
|
|
||||||
"Microsoft YaHei, PingFangSC-Regular, HelveticaNeue-Light, Helvetica Neue Light, sans-serif",
|
"PingFangSC-Regular, -apple-system, Simsun",
|
||||||
},
|
|
||||||
{
|
"Kaiti",
|
||||||
fontFamily: "PingFangSC-Regular, -apple-system, Simsun",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fontFamily: "Kaiti",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
export default settings;
|
export default settings;
|
||||||
|
@ -81,11 +81,18 @@
|
|||||||
<div class="chapter" ref="content" :style="chapterTheme">
|
<div class="chapter" ref="content" :style="chapterTheme">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="top-bar" ref="top"></div>
|
<div class="top-bar" ref="top"></div>
|
||||||
<div v-for="data in chapterData" :key="data.index" ref="chapter">
|
<div
|
||||||
|
v-for="data in chapterData"
|
||||||
|
:key="data.index"
|
||||||
|
:chapterIndex="data.index"
|
||||||
|
ref="chapter"
|
||||||
|
>
|
||||||
<chapter-content
|
<chapter-content
|
||||||
:carray="data.content"
|
:contents="data.content"
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:spacing="store.config.spacing"
|
:spacing="store.config.spacing"
|
||||||
|
:fontSize="fontSize"
|
||||||
|
:fontFamily="fontFamily"
|
||||||
v-if="showContent"
|
v-if="showContent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -124,10 +131,9 @@ try {
|
|||||||
} catch {
|
} catch {
|
||||||
localStorage.removeItem("config");
|
localStorage.removeItem("config");
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref();
|
const loading = ref();
|
||||||
|
|
||||||
const noPoint = ref(true);
|
const noPoint = ref(true);
|
||||||
|
|
||||||
const showToolBar = ref(false);
|
const showToolBar = ref(false);
|
||||||
const chapterData = ref([]);
|
const chapterData = ref([]);
|
||||||
const scrollObserve = ref(null);
|
const scrollObserve = ref(null);
|
||||||
@ -139,11 +145,32 @@ const {
|
|||||||
readSettingsVisible,
|
readSettingsVisible,
|
||||||
miniInterface,
|
miniInterface,
|
||||||
showContent,
|
showContent,
|
||||||
|
config,
|
||||||
|
readingBook,
|
||||||
} = storeToRefs(store);
|
} = storeToRefs(store);
|
||||||
|
|
||||||
const { chapterPos, index: chapterIndex } = toRefs(store.readingBook);
|
const chapterPos = computed({
|
||||||
|
get: () => readingBook.value.chapterPos,
|
||||||
|
set: (value) => (readingBook.value.chapterPos = value),
|
||||||
|
});
|
||||||
|
const chapterIndex = computed({
|
||||||
|
get: () => readingBook.value.index,
|
||||||
|
set: (value) => (readingBook.value.index = value),
|
||||||
|
});
|
||||||
|
|
||||||
const { theme, infiniteLoading } = toRefs(store.config);
|
const theme = computed(() => config.value.theme);
|
||||||
|
const infiniteLoading = computed(() => config.value.infiniteLoading);
|
||||||
|
|
||||||
|
// 字体
|
||||||
|
const fontFamily = computed(() => {
|
||||||
|
if (store.config.font >= 0) {
|
||||||
|
return settings.fonts[store.config.font];
|
||||||
|
}
|
||||||
|
return store.config.customFontName;
|
||||||
|
});
|
||||||
|
const fontSize = computed(() => {
|
||||||
|
return store.config.fontSize + "px";
|
||||||
|
});
|
||||||
|
|
||||||
// 主题部分
|
// 主题部分
|
||||||
const bodyColor = computed(() => settings.themes[theme.value].body);
|
const bodyColor = computed(() => settings.themes[theme.value].body);
|
||||||
@ -193,10 +220,7 @@ const rightBarTheme = computed(() => {
|
|||||||
display: miniInterface.value && !showToolBar.value ? "none" : "block",
|
display: miniInterface.value && !showToolBar.value ? "none" : "block",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const isNight = ref(false);
|
const isNight = computed(() => theme.value == 6);
|
||||||
watchEffect(() => {
|
|
||||||
isNight.value = theme.value == 6;
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (chapterData.value.length > 0) {
|
if (chapterData.value.length > 0) {
|
||||||
@ -239,6 +263,7 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
|||||||
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);
|
updateChapterData({ index, content, title }, 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];
|
||||||
@ -263,39 +288,32 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
const chapter = ref();
|
const chapter = ref();
|
||||||
const toChapterPos = (chapterPos) => {
|
const toChapterPos = (pos) => {
|
||||||
if (!chapterPos) return;
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
//计算chapterPos对应的段落行数
|
|
||||||
let wordCount = 0;
|
let wordCount = 0;
|
||||||
let index = chapterData.value[0].content.findIndex((paragraph) => {
|
for (let element of chapter.value[0].children) {
|
||||||
wordCount += paragraph.length;
|
wordCount += parseInt(element.getAttribute("wordCount"));
|
||||||
return wordCount >= chapterPos.value;
|
if (wordCount >= pos) {
|
||||||
});
|
jump(element, {
|
||||||
if (index == -1) index = chapterData.value[0].content.length - 1;
|
duration: 0,
|
||||||
if (index == 0) return; //第一行不跳转
|
});
|
||||||
//跳转
|
break;
|
||||||
jump(chapter.value[0].children[1].children[index], {
|
}
|
||||||
duration: 0,
|
}
|
||||||
callback: () => (chapterPos.value = 0),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
//计算当前章节阅读的字数
|
//计算当前章节阅读的字数
|
||||||
const computeChapterPos = () => {
|
const computeChapterPos = () => {
|
||||||
//dom没渲染时 返回0
|
if (chapter.value.length == 0) return;
|
||||||
if (!chapter.value[0]) return;
|
|
||||||
//计算当前阅读进度对应的element
|
//计算当前阅读进度对应的element
|
||||||
let index = chapterData.value.findIndex(
|
let chapterElement = chapter.value.find(
|
||||||
(chapter) => chapter.index == chapterIndex.value
|
(element) => element.getAttribute("chapterIndex") == chapterIndex.value
|
||||||
);
|
);
|
||||||
if (index == -1) return;
|
if (!chapterElement) return;
|
||||||
let element = chapter.value[index].children[1].children;
|
|
||||||
//计算已读字数
|
//计算已读字数
|
||||||
let mChapterPos = 0;
|
let mChapterPos = 0;
|
||||||
for (let paragraph of element) {
|
for (let paragraph of chapterElement.children) {
|
||||||
let text = paragraph.innerText;
|
mChapterPos += parseInt(paragraph.getAttribute("wordCount"));
|
||||||
mChapterPos += text.length;
|
|
||||||
if (paragraph.getBoundingClientRect().top >= 0) {
|
if (paragraph.getBoundingClientRect().top >= 0) {
|
||||||
chapterPos.value = mChapterPos;
|
chapterPos.value = mChapterPos;
|
||||||
break;
|
break;
|
||||||
@ -431,9 +449,7 @@ const handleIScrollObserve = (entries) => {
|
|||||||
const handleIReadingObserve = (entries) => {
|
const handleIReadingObserve = (entries) => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
for (let { isIntersecting, target, boundingClientRect } of entries) {
|
for (let { isIntersecting, target, boundingClientRect } of entries) {
|
||||||
let titleElement = target.querySelector(".title");
|
let chapterTitleIndex = parseInt(target.getAttribute("chapterIndex"));
|
||||||
if (!titleElement) return;
|
|
||||||
let chapterTitleIndex = parseInt(titleElement.getAttribute("index"));
|
|
||||||
if (isIntersecting) {
|
if (isIntersecting) {
|
||||||
chapterIndex.value = chapterTitleIndex;
|
chapterIndex.value = chapterTitleIndex;
|
||||||
} else {
|
} else {
|
||||||
@ -454,17 +470,13 @@ const addReadingObserve = () => {
|
|||||||
chapterElements.forEach((el) => readingObserve.value.observe(el));
|
chapterElements.forEach((el) => readingObserve.value.observe(el));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
/*
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (
|
computeChapterPos();
|
||||||
store.searchBooks.every((book) => book.bookUrl != store.readingBook.bookUrl)
|
saveReadingBookProgressToBrowser(chapterIndex.value);
|
||||||
) {
|
next();
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
alert(111);
|
|
||||||
next(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
/*
|
||||||
window.addEventListener("beforeunload", (e) => {
|
window.addEventListener("beforeunload", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.returnValue = "";
|
e.returnValue = "";
|
||||||
@ -508,7 +520,6 @@ onMounted(() => {
|
|||||||
store.setReadingBook(book);
|
store.setReadingBook(book);
|
||||||
|
|
||||||
getContent(chapterIndex, true, chapterPos);
|
getContent(chapterIndex, true, chapterPos);
|
||||||
|
|
||||||
window.addEventListener("keyup", handleKeyPress);
|
window.addEventListener("keyup", handleKeyPress);
|
||||||
//监听底部加载
|
//监听底部加载
|
||||||
scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {
|
scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {
|
||||||
|
@ -197,7 +197,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
showLoading.value = true;
|
showLoading.value = true;
|
||||||
//await store.saveBookProcess();
|
await store.saveBookProcess();
|
||||||
fetchBookShelfData();
|
fetchBookShelfData();
|
||||||
});
|
});
|
||||||
const fetchBookShelfData = () => {
|
const fetchBookShelfData = () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user