Просмотр исходного кода

fix(web): 修复进度同步和返回书架同步

Xwite 3 лет назад
Родитель
Сommit
be08e33e91

+ 4 - 0
app/src/main/assets/updateLog.md

@@ -12,6 +12,10 @@
 * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
 
+**2023/05/09**
+
+* web: 修复进度同步
+
 **2023/05/08**
 
 * 更新cronet: 113.0.5672.76

+ 20 - 34
modules/web/src/components/ChapterContent.vue

@@ -1,6 +1,10 @@
 <template>
-  <div class="title">{{ title }}</div>
-  <div v-for="(para, index) in carray" :key="index">
+  <div class="title" wordCount="0">{{ title }}</div>
+  <div
+    v-for="(para, index) in contents"
+    :key="index"
+    :wordCount="calculateWordCount(para)"
+  >
     <img
       class="full"
       v-if="/^\s*<img[^>]*src[^>]+>$/.test(para)"
@@ -8,34 +12,19 @@
       @error.once="proxyImage"
       loading="lazy"
     />
-    <p v-else :style="style" v-html="para" />
+    <p v-else :style="{ fontFamily, fontSize }" v-html="para" />
   </div>
 </template>
 
 <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({
-  carray: { type: Array, required: true },
+  contents: { type: Array, required: true },
   title: { type: String, required: true },
   spacing: { type: Object, 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;
+  fontFamily: { type: String, required: true },
+  fontSize: { type: String, required: true },
 });
 
 const getImageSrc = (content) => {
@@ -48,16 +37,13 @@ const proxyImage = (event) => {
   event.target.src = getImageFromLegado(event.target.src);
 };
 
-watch(fontSize, () => {
-  store.setShowContent(false);
-  nextTick(() => {
-    store.setShowContent(true);
-  });
-});
+const calculateWordCount = (paragraph) => {
+  const imgPattern = /<img[^>]*src="[^"]*(?:"[^>]+\})?"[^>]*>/g;
+  //内嵌图片文字为1
+  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>
 
 <style lang="scss" scoped>
@@ -72,9 +58,9 @@ p {
   word-wrap: break-word;
   word-break: break-all;
 
-  letter-spacing: calc(v-bind("letterSpacing") * 1em);
-  line-height: v-bind("lineSpacing");
-  margin: calc(v-bind("paragraphSpacing") * 1em) 0;
+  letter-spacing: calc(v-bind("props.spacing.letter") * 1em);
+  line-height: calc(1 + v-bind("props.spacing.line"));
+  margin: calc(v-bind("props.spacing.paragraph") * 1em) 0;
 
   :deep(img) {
     height: 1em;

+ 5 - 10
modules/web/src/plugins/config.js

@@ -55,16 +55,11 @@ var settings = {
     },
   ],
   fonts: [
-    {
-      fontFamily:
-        "Microsoft YaHei, PingFangSC-Regular, HelveticaNeue-Light, Helvetica Neue Light, sans-serif",
-    },
-    {
-      fontFamily: "PingFangSC-Regular, -apple-system, Simsun",
-    },
-    {
-      fontFamily: "Kaiti",
-    },
+    "Microsoft YaHei, PingFangSC-Regular, HelveticaNeue-Light, Helvetica Neue Light, sans-serif",
+
+    "PingFangSC-Regular, -apple-system, Simsun",
+
+    "Kaiti",
   ],
 };
 export default settings;

+ 57 - 46
modules/web/src/views/BookChapter.vue

@@ -81,11 +81,18 @@
     <div class="chapter" ref="content" :style="chapterTheme">
       <div class="content">
         <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
-            :carray="data.content"
+            :contents="data.content"
             :title="data.title"
             :spacing="store.config.spacing"
+            :fontSize="fontSize"
+            :fontFamily="fontFamily"
             v-if="showContent"
           />
         </div>
@@ -124,10 +131,9 @@ try {
 } catch {
   localStorage.removeItem("config");
 }
-const loading = ref();
 
+const loading = ref();
 const noPoint = ref(true);
-
 const showToolBar = ref(false);
 const chapterData = ref([]);
 const scrollObserve = ref(null);
@@ -139,11 +145,32 @@ const {
   readSettingsVisible,
   miniInterface,
   showContent,
+  config,
+  readingBook,
 } = 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);
@@ -193,10 +220,7 @@ const rightBarTheme = computed(() => {
     display: miniInterface.value && !showToolBar.value ? "none" : "block",
   };
 });
-const isNight = ref(false);
-watchEffect(() => {
-  isNight.value = theme.value == 6;
-});
+const isNight = computed(() => theme.value == 6);
 
 watchEffect(() => {
   if (chapterData.value.length > 0) {
@@ -239,6 +263,7 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
         let data = res.data.data;
         let content = data.split(/\n+/);
         updateChapterData({ index, content, title }, reloadChapter);
+        toChapterPos(chapterPos);
       } else {
         ElMessage({ message: res.data.errorMsg, type: "error" });
         let content = [res.data.errorMsg];
@@ -263,39 +288,32 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
   );
 };
 const chapter = ref();
-const toChapterPos = (chapterPos) => {
-  if (!chapterPos) return;
+const toChapterPos = (pos) => {
   nextTick(() => {
-    //计算chapterPos对应的段落行数
     let wordCount = 0;
-    let index = chapterData.value[0].content.findIndex((paragraph) => {
-      wordCount += paragraph.length;
-      return wordCount >= chapterPos.value;
-    });
-    if (index == -1) index = chapterData.value[0].content.length - 1;
-    if (index == 0) return; //第一行不跳转
-    //跳转
-    jump(chapter.value[0].children[1].children[index], {
-      duration: 0,
-      callback: () => (chapterPos.value = 0),
-    });
+    for (let element of chapter.value[0].children) {
+      wordCount += parseInt(element.getAttribute("wordCount"));
+      if (wordCount >= pos) {
+        jump(element, {
+          duration: 0,
+        });
+        break;
+      }
+    }
   });
 };
 //计算当前章节阅读的字数
 const computeChapterPos = () => {
-  //dom没渲染时 返回0
-  if (!chapter.value[0]) return;
+  if (chapter.value.length == 0) return;
   //计算当前阅读进度对应的element
-  let index = chapterData.value.findIndex(
-    (chapter) => chapter.index == chapterIndex.value
+  let chapterElement = chapter.value.find(
+    (element) => element.getAttribute("chapterIndex") == chapterIndex.value
   );
-  if (index == -1) return;
-  let element = chapter.value[index].children[1].children;
+  if (!chapterElement) return;
   //计算已读字数
   let mChapterPos = 0;
-  for (let paragraph of element) {
-    let text = paragraph.innerText;
-    mChapterPos += text.length;
+  for (let paragraph of chapterElement.children) {
+    mChapterPos += parseInt(paragraph.getAttribute("wordCount"));
     if (paragraph.getBoundingClientRect().top >= 0) {
       chapterPos.value = mChapterPos;
       break;
@@ -431,9 +449,7 @@ const handleIScrollObserve = (entries) => {
 const handleIReadingObserve = (entries) => {
   nextTick(() => {
     for (let { isIntersecting, target, boundingClientRect } of entries) {
-      let titleElement = target.querySelector(".title");
-      if (!titleElement) return;
-      let chapterTitleIndex = parseInt(titleElement.getAttribute("index"));
+      let chapterTitleIndex = parseInt(target.getAttribute("chapterIndex"));
       if (isIntersecting) {
         chapterIndex.value = chapterTitleIndex;
       } else {
@@ -454,17 +470,13 @@ const addReadingObserve = () => {
     chapterElements.forEach((el) => readingObserve.value.observe(el));
   });
 };
-/*
+
 onBeforeRouteLeave((to, from, next) => {
-  if (
-    store.searchBooks.every((book) => book.bookUrl != store.readingBook.bookUrl)
-  ) {
-    next();
-  } else {
-    alert(111);
-    next(false);
-  }
+  computeChapterPos();
+  saveReadingBookProgressToBrowser(chapterIndex.value);
+  next();
 });
+/*
 window.addEventListener("beforeunload", (e) => {
   e.preventDefault();
   e.returnValue = "";
@@ -508,7 +520,6 @@ onMounted(() => {
       store.setReadingBook(book);
 
       getContent(chapterIndex, true, chapterPos);
-
       window.addEventListener("keyup", handleKeyPress);
       //监听底部加载
       scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {

+ 1 - 1
modules/web/src/views/BookShelf.vue

@@ -197,7 +197,7 @@ onMounted(async () => {
     }
   }
   showLoading.value = true;
-  //await store.saveBookProcess();
+  await store.saveBookProcess();
   fetchBookShelfData();
 });
 const fetchBookShelfData = () => {