web: 添加hooks

This commit is contained in:
Xwite
2023-05-13 11:33:25 +08:00
parent 5665bc1580
commit 3e873f23e7
4 changed files with 121 additions and 114 deletions
+8
View File
@@ -0,0 +1,8 @@
.el-loading-spinner {
font-size: 36px;
color: #b5b5b5;
}
.el-loading-text {
font-weight: 500;
color: #b5b5b5 !important;
}
+36
View File
@@ -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 };
};
+63 -89
View File
@@ -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) => { // loading spinner
if (!loading) return loadingSerive.value?.close(); const { isLoading, loadingWrapper } = useLoading(content, "正在获取信息");
loadingSerive.value = ElLoading.service({
target: content.value,
spinner: loadingSvg,
text: "正在获取信息",
lock: true,
});
});
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( loadingWrapper(
(res) => { API.getBookContent(bookUrl, chapterIndex).then(
if (res.data.isSuccess) { (res) => {
let data = res.data.data; if (res.data.isSuccess) {
let content = data.split(/\n+/); let data = res.data.data;
chapterData.value.push({ index, content, title }); let content = data.split(/\n+/);
if (reloadChapter) toChapterPos(chapterPos); chapterData.value.push({ index, content, title });
} else { if (reloadChapter) toChapterPos(chapterPos);
ElMessage({ message: res.data.errorMsg, type: "error" }); } else {
let content = [res.data.errorMsg]; 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));
} }
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( getContent(chapterIndex, true, chapterPos);
(res) => { window.addEventListener("keyup", handleKeyPress);
showLoading.value = false; // 兼容Safari < 14
if (!res.data.isSuccess) { document.addEventListener("visibilitychange", onVisibilityChange);
ElMessage({ message: res.data.errorMsg, type: "error" }); // 兼容Safari < 14.5
setTimeout(toShelf, 500); document.addEventListener("pagehide", onVisibilityChange);
return; //监听底部加载
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
View File
@@ -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); const { showLoading, closeLoading, loadingWrapper } = useLoading(
watch(showLoading, (loading) => { shelfWrapper,
if (!loading) return loadingSerive.value?.close(); "正在获取书籍信息"
loadingSerive.value = ElLoading.service({ );
target: shelfWrapper.value,
spinner: loadingSvg,
text: "正在获取书籍信息",
lock: true,
});
});
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; loadingWrapper(
store store
.saveBookProgress() .saveBookProgress()
//确保各种网络情况下同步请求先完成 //确保各种网络情况下同步请求先完成
.finally(fetchBookShelfData); .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("后端连接失败");