Xwite 3 anni fa
parent
commit
3e873f23e7

+ 8 - 0
modules/web/src/hooks/loading.css

@@ -0,0 +1,8 @@
+.el-loading-spinner {
+  font-size: 36px;
+  color: #b5b5b5;
+}
+.el-loading-text {
+ font-weight: 500;
+ color: #b5b5b5 !important;
+}

+ 36 - 0
modules/web/src/hooks/loading.js

@@ -0,0 +1,36 @@
+import { watch, unref, onUnmounted } from "vue";
+import { ElLoading } from "element-plus";
+import loadingSvg from "@element-plus/icons-svg/loading.svg?raw";
+import "element-plus/theme-chalk/el-loading.css";
+import "./loading.css";
+
+export const useLoading = (target, text, spinner = loadingSvg) => {
+  // loading spinner
+  const isLoading = ref(false);
+  let loadingInstance = null;
+  const closeLoading = () => (isLoading.value = false);
+  const showLoading = () => (isLoading.value = true);
+  watch(isLoading, (loading) => {
+    if (!loading) return loadingInstance?.close();
+    loadingInstance = ElLoading.service({
+      target: unref(target),
+      spinner: spinner,
+      text: text,
+      lock: true,
+      background: "rgba(0, 0, 0, 0)",
+    });
+  });
+
+  const loadingWrapper = (promise) => {
+    if (!(promise instanceof Promise))
+      throw TypeError("loadingWrapper argument must be Promise");
+    showLoading();
+    return promise.finally(closeLoading);
+  };
+
+  onUnmounted(() => {
+    closeLoading();
+  });
+
+  return { isLoading, showLoading, closeLoading, loadingWrapper };
+};

+ 64 - 90
modules/web/src/views/BookChapter.vue

@@ -110,23 +110,13 @@
 import jump from "@/plugins/jump";
 import jump from "@/plugins/jump";
 import settings from "@/plugins/config";
 import settings from "@/plugins/config";
 import API from "@api";
 import API from "@api";
-import loadingSvg from "@element-plus/icons-svg/loading.svg?raw";
+import { useLoading } from "@/hooks/loading";
 
 
-// loading spinner
-const showLoading = ref(false);
-const loadingSerive = ref(null);
 const content = ref();
 const content = ref();
-watch(showLoading, (loading) => {
-  if (!loading) return loadingSerive.value?.close();
-  loadingSerive.value = ElLoading.service({
-    target: content.value,
-    spinner: loadingSvg,
-    text: "正在获取信息",
-    lock: true,
-  });
-});
-
+// loading spinner
+const { isLoading, loadingWrapper } = useLoading(content, "正在获取信息");
 const store = useBookStore();
 const store = useBookStore();
+
 // 读取阅读配置
 // 读取阅读配置
 try {
 try {
   const browerConfig = JSON.parse(localStorage.getItem("config"));
   const browerConfig = JSON.parse(localStorage.getItem("config"));
@@ -242,7 +232,6 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
   if (reloadChapter) {
   if (reloadChapter) {
     //展示进度条
     //展示进度条
     store.setShowContent(false);
     store.setShowContent(false);
-    showLoading.value = true;
     //强制滚回顶层
     //强制滚回顶层
     jump(top.value, { duration: 0 });
     jump(top.value, { duration: 0 });
     //从目录,按钮切换章节时保存进度 预加载时不保存
     //从目录,按钮切换章节时保存进度 预加载时不保存
@@ -252,34 +241,34 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => {
   let bookUrl = sessionStorage.getItem("bookUrl");
   let bookUrl = sessionStorage.getItem("bookUrl");
   let { title, index: chapterIndex } = catalog.value[index];
   let { title, index: chapterIndex } = catalog.value[index];
 
 
-  API.getBookContent(bookUrl, chapterIndex).then(
-    (res) => {
-      if (res.data.isSuccess) {
-        let data = res.data.data;
-        let content = data.split(/\n+/);
-        chapterData.value.push({ index, content, title });
-        if (reloadChapter) toChapterPos(chapterPos);
-      } else {
-        ElMessage({ message: res.data.errorMsg, type: "error" });
-        let content = [res.data.errorMsg];
+  loadingWrapper(
+    API.getBookContent(bookUrl, chapterIndex).then(
+      (res) => {
+        if (res.data.isSuccess) {
+          let data = res.data.data;
+          let content = data.split(/\n+/);
+          chapterData.value.push({ index, content, title });
+          if (reloadChapter) toChapterPos(chapterPos);
+        } else {
+          ElMessage({ message: res.data.errorMsg, type: "error" });
+          let content = [res.data.errorMsg];
+          chapterData.value.push({ index, content, title });
+        }
+        store.setContentLoading(true);
+        noPoint.value = false;
+        store.setShowContent(true);
+        if (!res.data.isSuccess) {
+          throw res.data;
+        }
+      },
+      (err) => {
+        ElMessage({ message: "获取章节内容失败", type: "error" });
+        let content = ["获取章节内容失败!"];
         chapterData.value.push({ index, content, title });
         chapterData.value.push({ index, content, title });
+        store.setShowContent(true);
+        throw err;
       }
       }
-      store.setContentLoading(true);
-      showLoading.value = false;
-      noPoint.value = false;
-      store.setShowContent(true);
-      if (!res.data.isSuccess) {
-        throw res.data;
-      }
-    },
-    (err) => {
-      ElMessage({ message: "获取章节内容失败", type: "error" });
-      let content = ["获取章节内容失败!"];
-      chapterData.value.push({ index, content, title });
-      showLoading.value = false;
-      store.setShowContent(true);
-      throw err;
-    }
+    )
   );
   );
 };
 };
 
 
@@ -327,14 +316,14 @@ const saveReadingBookProgressToBrowser = (index, pos) => {
 // 关闭页面 切换tab 返回桌面 等操作 https://developer.mozilla.org/zh-CN/docs/Web/API/Document/visibilitychange_event
 // 关闭页面 切换tab 返回桌面 等操作 https://developer.mozilla.org/zh-CN/docs/Web/API/Document/visibilitychange_event
 const onVisibilityChange = () => {
 const onVisibilityChange = () => {
   if (!bookProgress.value) return;
   if (!bookProgress.value) return;
-  if (document.visibilityState == 'hidden') {
+  if (document.visibilityState == "hidden") {
     // Safari > 14 和 非Safari移除额外pagehide event
     // Safari > 14 和 非Safari移除额外pagehide event
     document.removeEventListener("pagehide", onVisibilityChange);
     document.removeEventListener("pagehide", onVisibilityChange);
     // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
     // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
     navigator.sendBeacon(
     navigator.sendBeacon(
       `${import.meta.env.VITE_API || location.origin}/saveBookProgress`,
       `${import.meta.env.VITE_API || location.origin}/saveBookProgress`,
       JSON.stringify(bookProgress.value)
       JSON.stringify(bookProgress.value)
-  );
+    );
   }
   }
 };
 };
 
 
@@ -392,7 +381,7 @@ const loadMore = () => {
 };
 };
 // IntersectionObserver回调 底部加载
 // IntersectionObserver回调 底部加载
 const handleIScrollObserve = (entries) => {
 const handleIScrollObserve = (entries) => {
-  if (showLoading.value) return;
+  if (isLoading.value) return;
   for (let { isIntersecting } of entries) {
   for (let { isIntersecting } of entries) {
     if (!isIntersecting) return;
     if (!isIntersecting) return;
     loadMore();
     loadMore();
@@ -444,7 +433,6 @@ const handleKeyPress = (event) => {
 };
 };
 
 
 onMounted(() => {
 onMounted(() => {
-  showLoading.value = true;
   //获取书籍数据
   //获取书籍数据
   let bookUrl = sessionStorage.getItem("bookUrl");
   let bookUrl = sessionStorage.getItem("bookUrl");
   let bookName = sessionStorage.getItem("bookName");
   let bookName = sessionStorage.getItem("bookName");
@@ -466,39 +454,38 @@ onMounted(() => {
     };
     };
     localStorage.setItem(bookUrl, JSON.stringify(book));
     localStorage.setItem(bookUrl, JSON.stringify(book));
   }
   }
-
-  API.getChapterList(bookUrl).then(
-    (res) => {
-      showLoading.value = false;
-      if (!res.data.isSuccess) {
-        ElMessage({ message: res.data.errorMsg, type: "error" });
-        setTimeout(toShelf, 500);
-        return;
+  loadingWrapper(
+    API.getChapterList(bookUrl).then(
+      (res) => {
+        if (!res.data.isSuccess) {
+          ElMessage({ message: res.data.errorMsg, type: "error" });
+          setTimeout(toShelf, 500);
+          return;
+        }
+        let data = res.data.data;
+        store.setCatalog(data);
+        store.setReadingBook(book);
+
+        getContent(chapterIndex, true, chapterPos);
+        window.addEventListener("keyup", handleKeyPress);
+        // 兼容Safari < 14
+        document.addEventListener("visibilitychange", onVisibilityChange);
+        // 兼容Safari < 14.5
+        document.addEventListener("pagehide", onVisibilityChange);
+        //监听底部加载
+        scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {
+          rootMargin: "-100% 0% 20% 0%",
+        });
+        infiniteLoading.value && scrollObserve.value.observe(loading.value);
+        //第二次点击同一本书 页面标题不会变化
+        document.title = null;
+        document.title = bookName + " | " + catalog.value[chapterIndex].title;
+      },
+      (err) => {
+        ElMessage({ message: "获取书籍目录失败", type: "error" });
+        throw err;
       }
       }
-      let data = res.data.data;
-      store.setCatalog(data);
-      store.setReadingBook(book);
-
-      getContent(chapterIndex, true, chapterPos);
-      window.addEventListener("keyup", handleKeyPress);
-       // 兼容Safari < 14
-      document.addEventListener("visibilitychange", onVisibilityChange);
-       // 兼容Safari < 14.5
-      document.addEventListener("pagehide", onVisibilityChange);
-      //监听底部加载
-      scrollObserve.value = new IntersectionObserver(handleIScrollObserve, {
-        rootMargin: "-100% 0% 20% 0%",
-      });
-      infiniteLoading.value && scrollObserve.value.observe(loading.value);
-      //第二次点击同一本书 页面标题不会变化
-      document.title = null;
-      document.title = bookName + " | " + catalog.value[chapterIndex].title;
-    },
-    (err) => {
-      showLoading.value = false;
-      ElMessage({ message: "获取书籍目录失败", type: "error" });
-      throw err;
-    }
+    )
   );
   );
 });
 });
 
 
@@ -608,19 +595,6 @@ onUnmounted(() => {
     width: 670px;
     width: 670px;
     margin: 0 auto;
     margin: 0 auto;
 
 
-    :deep(.el-loading-mask) {
-      background-color: rgba(0, 0, 0, 0);
-    }
-    :deep(.el-loading-spinner) {
-      font-size: 36px;
-      color: #b5b5b5;
-    }
-
-    :deep(.el-loading-text) {
-      font-weight: 500;
-      color: #b5b5b5;
-    }
-
     .content {
     .content {
       overflow: hidden;
       overflow: hidden;
       font-size: 18px;
       font-size: 18px;

+ 14 - 25
modules/web/src/views/BookShelf.vue

@@ -78,8 +78,8 @@
 import "@/assets/fonts/shelffont.css";
 import "@/assets/fonts/shelffont.css";
 import { useBookStore } from "@/store";
 import { useBookStore } from "@/store";
 import githubUrl from "@/assets/imgs/github.png";
 import githubUrl from "@/assets/imgs/github.png";
+import { useLoading } from "@/hooks/loading";
 import { Search } from "@element-plus/icons-vue";
 import { Search } from "@element-plus/icons-vue";
-import loadingSvg from "@element-plus/icons-svg/loading.svg?raw";
 import API from "@api";
 import API from "@api";
 
 
 const store = useBookStore();
 const store = useBookStore();
@@ -92,23 +92,13 @@ const readingRecent = ref({
   chapterIndex: 0,
   chapterIndex: 0,
   chapterPos: 0,
   chapterPos: 0,
 });
 });
-const showLoading = ref(false);
 const shelfWrapper = ref(null);
 const shelfWrapper = ref(null);
-const loadingSerive = ref(null);
-watch(showLoading, (loading) => {
-  if (!loading) return loadingSerive.value?.close();
-  loadingSerive.value = ElLoading.service({
-    target: shelfWrapper.value,
-    spinner: loadingSvg,
-    text: "正在获取书籍信息",
-    lock: true,
-  });
-});
+const { showLoading, closeLoading, loadingWrapper } = useLoading(
+  shelfWrapper,
+  "正在获取书籍信息"
+);
 
 
 const books = ref([]);
 const books = ref([]);
-watchEffect(() => {
-  if (books.value.length > 0) showLoading.value = false;
-});
 
 
 const search = ref("");
 const search = ref("");
 const isSearching = ref(false);
 const isSearching = ref(false);
@@ -131,7 +121,7 @@ const searchBook = () => {
   if (search.value == "") return;
   if (search.value == "") return;
   books.value = [];
   books.value = [];
   store.clearSearchBooks();
   store.clearSearchBooks();
-  showLoading.value = true;
+  showLoading();
   isSearching.value = true;
   isSearching.value = true;
   API.search(
   API.search(
     search.value,
     search.value,
@@ -145,7 +135,7 @@ const searchBook = () => {
       }
       }
     },
     },
     () => {
     () => {
-      showLoading.value = false;
+      closeLoading();
       if (books.value.length == 0) {
       if (books.value.length == 0) {
         ElMessage.info("搜索结果为空");
         ElMessage.info("搜索结果为空");
       }
       }
@@ -196,14 +186,15 @@ onMounted(() => {
       readingRecent.value.chapterIndex = 0;
       readingRecent.value.chapterIndex = 0;
     }
     }
   }
   }
-  showLoading.value = true;
-  store
-    .saveBookProgress()
-    //确保各种网络情况下同步请求先完成
-    .finally(fetchBookShelfData);
+  loadingWrapper(
+    store
+      .saveBookProgress()
+      //确保各种网络情况下同步请求先完成
+      .finally(fetchBookShelfData)
+  );
 });
 });
 const fetchBookShelfData = () => {
 const fetchBookShelfData = () => {
-  API.getBookShelf()
+  return API.getBookShelf()
     .then((response) => {
     .then((response) => {
       store.setConnectType("success");
       store.setConnectType("success");
       if (response.data.isSuccess) {
       if (response.data.isSuccess) {
@@ -217,13 +208,11 @@ const fetchBookShelfData = () => {
         );
         );
       } else {
       } else {
         ElMessage.error(response.data.errorMsg);
         ElMessage.error(response.data.errorMsg);
-        showLoading.value = false;
       }
       }
       store.setConnectStatus("已连接 ");
       store.setConnectStatus("已连接 ");
       store.setNewConnect(false);
       store.setNewConnect(false);
     })
     })
     .catch(function (error) {
     .catch(function (error) {
-      showLoading.value = false;
       store.setConnectType("danger");
       store.setConnectType("danger");
       store.setConnectStatus("连接失败");
       store.setConnectStatus("连接失败");
       ElMessage.error("后端连接失败");
       ElMessage.error("后端连接失败");