From 31e10b301042e7fcdf1614229ea971b107870ef6 Mon Sep 17 00:00:00 2001 From: Xwite <1797350009@qq.com> Date: Sat, 13 May 2023 09:42:28 +0800 Subject: [PATCH] =?UTF-8?q?web:=20=E6=B7=BB=E5=8A=A0hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/web/src/hooks/loading.css | 8 ++ modules/web/src/hooks/loading.js | 36 ++++++ modules/web/src/views/BookChapter.vue | 152 +++++++++++--------------- modules/web/src/views/BookShelf.vue | 39 +++---- 4 files changed, 121 insertions(+), 114 deletions(-) create mode 100644 modules/web/src/hooks/loading.css create mode 100644 modules/web/src/hooks/loading.js diff --git a/modules/web/src/hooks/loading.css b/modules/web/src/hooks/loading.css new file mode 100644 index 000000000..f0c8609c3 --- /dev/null +++ b/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; +} diff --git a/modules/web/src/hooks/loading.js b/modules/web/src/hooks/loading.js new file mode 100644 index 000000000..537f6d715 --- /dev/null +++ b/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 }; +}; diff --git a/modules/web/src/views/BookChapter.vue b/modules/web/src/views/BookChapter.vue index f477cb941..114279b23 100644 --- a/modules/web/src/views/BookChapter.vue +++ b/modules/web/src/views/BookChapter.vue @@ -110,23 +110,13 @@ import jump from "@/plugins/jump"; import settings from "@/plugins/config"; 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(); -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(); + // 读取阅读配置 try { const browerConfig = JSON.parse(localStorage.getItem("config")); @@ -242,7 +232,6 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => { if (reloadChapter) { //展示进度条 store.setShowContent(false); - showLoading.value = true; //强制滚回顶层 jump(top.value, { duration: 0 }); //从目录,按钮切换章节时保存进度 预加载时不保存 @@ -252,34 +241,34 @@ const getContent = (index, reloadChapter = true, chapterPos = 0) => { let bookUrl = sessionStorage.getItem("bookUrl"); 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 }); + 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 const onVisibilityChange = () => { if (!bookProgress.value) return; - if (document.visibilityState == 'hidden') { + if (document.visibilityState == "hidden") { // Safari > 14 和 非Safari移除额外pagehide event document.removeEventListener("pagehide", onVisibilityChange); // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon navigator.sendBeacon( `${import.meta.env.VITE_API || location.origin}/saveBookProgress`, JSON.stringify(bookProgress.value) - ); + ); } }; @@ -392,7 +381,7 @@ const loadMore = () => { }; // IntersectionObserver回调 底部加载 const handleIScrollObserve = (entries) => { - if (showLoading.value) return; + if (isLoading.value) return; for (let { isIntersecting } of entries) { if (!isIntersecting) return; loadMore(); @@ -444,7 +433,6 @@ const handleKeyPress = (event) => { }; onMounted(() => { - showLoading.value = true; //获取书籍数据 let bookUrl = sessionStorage.getItem("bookUrl"); let bookName = sessionStorage.getItem("bookName"); @@ -466,39 +454,38 @@ onMounted(() => { }; localStorage.setItem(bookUrl, JSON.stringify(book)); } + 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); - API.getChapterList(bookUrl).then( - (res) => { - showLoading.value = false; - if (!res.data.isSuccess) { - ElMessage({ message: res.data.errorMsg, type: "error" }); - setTimeout(toShelf, 500); - return; + 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; 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 { overflow: hidden; font-size: 18px; diff --git a/modules/web/src/views/BookShelf.vue b/modules/web/src/views/BookShelf.vue index 99f9b8032..3d37b1eec 100644 --- a/modules/web/src/views/BookShelf.vue +++ b/modules/web/src/views/BookShelf.vue @@ -78,8 +78,8 @@ import "@/assets/fonts/shelffont.css"; import { useBookStore } from "@/store"; import githubUrl from "@/assets/imgs/github.png"; +import { useLoading } from "@/hooks/loading"; import { Search } from "@element-plus/icons-vue"; -import loadingSvg from "@element-plus/icons-svg/loading.svg?raw"; import API from "@api"; const store = useBookStore(); @@ -92,23 +92,13 @@ const readingRecent = ref({ chapterIndex: 0, chapterPos: 0, }); -const showLoading = ref(false); 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([]); -watchEffect(() => { - if (books.value.length > 0) showLoading.value = false; -}); const search = ref(""); const isSearching = ref(false); @@ -131,7 +121,7 @@ const searchBook = () => { if (search.value == "") return; books.value = []; store.clearSearchBooks(); - showLoading.value = true; + showLoading(); isSearching.value = true; API.search( search.value, @@ -145,7 +135,7 @@ const searchBook = () => { } }, () => { - showLoading.value = false; + closeLoading(); if (books.value.length == 0) { ElMessage.info("搜索结果为空"); } @@ -196,14 +186,15 @@ onMounted(() => { readingRecent.value.chapterIndex = 0; } } - showLoading.value = true; - store - .saveBookProgress() - //确保各种网络情况下同步请求先完成 - .finally(fetchBookShelfData); + loadingWrapper( + store + .saveBookProgress() + //确保各种网络情况下同步请求先完成 + .finally(fetchBookShelfData) + ); }); const fetchBookShelfData = () => { - API.getBookShelf() + return API.getBookShelf() .then((response) => { store.setConnectType("success"); if (response.data.isSuccess) { @@ -217,13 +208,11 @@ const fetchBookShelfData = () => { ); } else { ElMessage.error(response.data.errorMsg); - showLoading.value = false; } store.setConnectStatus("已连接 "); store.setNewConnect(false); }) .catch(function (error) { - showLoading.value = false; store.setConnectType("danger"); store.setConnectStatus("连接失败"); ElMessage.error("后端连接失败");