|
@@ -1,6 +1,21 @@
|
|
|
<template>
|
|
|
<div class="reader-page">
|
|
|
- <nav-bar back-color="var(--icon-close-color)" :need-bg-color="false" />
|
|
|
+ <nav-bar back-color="var(--icon-close-color)" :need-bg-color="false">
|
|
|
+ <div class="reader-page-head">
|
|
|
+ <div class="reader-page__share" @click="handleClipboard">
|
|
|
+ <svg-icon
|
|
|
+ width="24px"
|
|
|
+ height="24px"
|
|
|
+ name="icon_share"
|
|
|
+ color="white"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="reader-page__tag" @click="handleCollect">
|
|
|
+ <img :src="isCollect ? CollectIcon : UnCollectIcon" />
|
|
|
+ <span>{{ isCollect ? "移除书架" : "加入书架" }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </nav-bar>
|
|
|
|
|
|
<div id="reader" />
|
|
|
|
|
@@ -22,86 +37,126 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { computed, onMounted, ref } from "vue";
|
|
|
-import { useRoute } from "vue-router";
|
|
|
import { storeToRefs } from "pinia";
|
|
|
-import { showLoadingToast } from "vant";
|
|
|
+import { computed, onMounted, ref } from "vue";
|
|
|
+import { useRoute, useRouter } from "vue-router";
|
|
|
+import { showLoadingToast, showToast } from "vant";
|
|
|
+import useClipboard from "vue-clipboard3";
|
|
|
import NavBar from "@/components/NavBar.vue";
|
|
|
import { useEpubStore, useBaseStore, useReaderStore } from "@/stores";
|
|
|
-import { getBookDetail2Api, getBookDetailApi, getLabelListApi } from "@/api";
|
|
|
+import {
|
|
|
+ getBookDetail2Api,
|
|
|
+ getBookDetailApi,
|
|
|
+ updateBookCollectApi,
|
|
|
+} from "@/api";
|
|
|
import { getBaseUrl } from "@/utils";
|
|
|
-import { THEMES } from "./constants";
|
|
|
+import UnCollectIcon from "@/assets/images/icon_like_normal@2x-min.png";
|
|
|
+import CollectIcon from "@/assets/images/icon_like_active@2x-min.png";
|
|
|
import ToolbarPopover from "./components/ToolbarPopover.vue";
|
|
|
import ToolbarMenu from "./components/ToolbarMenu.vue";
|
|
|
+import { THEMES } from "./constants";
|
|
|
|
|
|
+const { toClipboard } = useClipboard();
|
|
|
const route = useRoute();
|
|
|
+const router = useRouter();
|
|
|
const baseUrl = getBaseUrl();
|
|
|
-const epubStore = useEpubStore();
|
|
|
+
|
|
|
+const epubStore = useEpubStore(window.pinia);
|
|
|
const baseStore = useBaseStore();
|
|
|
const readerStore = useReaderStore();
|
|
|
const { noteList } = storeToRefs(readerStore);
|
|
|
const { isLogin } = storeToRefs(baseStore);
|
|
|
+
|
|
|
const selectedCfi = ref("");
|
|
|
const selectMenuStyle = ref({});
|
|
|
const detail = ref(null);
|
|
|
+const isCollect = computed(() => detail.value?.isCollect === 1);
|
|
|
const paneBgColor = computed(
|
|
|
() => THEMES.find((theme) => theme.key === epubStore.curTheme).background
|
|
|
);
|
|
|
|
|
|
+const handleCollect = async () => {
|
|
|
+ if (!baseStore.loginValidator()) return;
|
|
|
+
|
|
|
+ const temp = isCollect.value;
|
|
|
+ try {
|
|
|
+ detail.value.isCollect = temp ? 0 : 1;
|
|
|
+ await updateBookCollectApi(detail.value.id, {
|
|
|
+ status: temp ? 0 : 1,
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ detail.value.isCollect = temp;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const getBookDetail = async () => {
|
|
|
const data = await (isLogin.value ? getBookDetail2Api : getBookDetailApi)(
|
|
|
route.params.id
|
|
|
);
|
|
|
+
|
|
|
+ if (!data) {
|
|
|
+ router.replace({ name: "404" });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
detail.value = data;
|
|
|
|
|
|
initEpub();
|
|
|
};
|
|
|
|
|
|
-const initEpub = () => {
|
|
|
+const initEpub = async () => {
|
|
|
const toast = showLoadingToast({
|
|
|
duration: 0,
|
|
|
message: "书本加载中...",
|
|
|
forbidClick: true,
|
|
|
});
|
|
|
|
|
|
- epubStore.init({
|
|
|
- url: baseUrl + detail.value.filePath,
|
|
|
- themes: THEMES,
|
|
|
- });
|
|
|
+ try {
|
|
|
+ await epubStore.init({
|
|
|
+ url: baseUrl + detail.value.filePath,
|
|
|
+ themes: THEMES,
|
|
|
+ });
|
|
|
|
|
|
- const parentWidth = window.innerWidth;
|
|
|
- epubStore.rendition.hooks.render.register((v) => {
|
|
|
- v.document.addEventListener("click", async (e) => {
|
|
|
- // 如果显示冒泡,则不翻页
|
|
|
- if (selectMenuStyle.value.visibility === "visible") return;
|
|
|
+ const parentWidth = window.innerWidth;
|
|
|
+ epubStore.rendition.hooks.render.register((v) => {
|
|
|
+ v.document.addEventListener("click", async (e) => {
|
|
|
+ // 如果显示冒泡,则不翻页
|
|
|
+ if (selectMenuStyle.value.visibility === "visible") return;
|
|
|
|
|
|
- const { page } = await epubStore.refreshLocation();
|
|
|
- const clientX = e.clientX;
|
|
|
- const screenX = clientX - page * parentWidth;
|
|
|
+ const { page } = await epubStore.refreshLocation();
|
|
|
+ const clientX = e.clientX;
|
|
|
+ const screenX = clientX - page * parentWidth;
|
|
|
|
|
|
- if (screenX < parentWidth * 0.5) epubStore.prePage();
|
|
|
- else if (screenX > parentWidth * 0.5) epubStore.nextPage();
|
|
|
- });
|
|
|
+ if (screenX < parentWidth * 0.5) epubStore.prePage();
|
|
|
+ else if (screenX > parentWidth * 0.5) epubStore.nextPage();
|
|
|
+ });
|
|
|
|
|
|
- v.document.body.addEventListener("touchend", (e) => {
|
|
|
- const selection = v.window.getSelection();
|
|
|
+ v.document.body.addEventListener("touchend", (e) => {
|
|
|
+ const selection = v.window.getSelection();
|
|
|
|
|
|
- setTimeout(() => {
|
|
|
- if (!selection.isCollapsed) selected(e, v);
|
|
|
- else hideSelectMenu();
|
|
|
- }, 50);
|
|
|
+ setTimeout(() => {
|
|
|
+ if (!selection.isCollapsed) selected(e, v);
|
|
|
+ else hideSelectMenu();
|
|
|
+ }, 50);
|
|
|
+ });
|
|
|
});
|
|
|
- });
|
|
|
|
|
|
- epubStore.rendition.on("selected", (v) => {
|
|
|
- selectedCfi.value = v;
|
|
|
- });
|
|
|
+ epubStore.rendition.on("selected", (v) => {
|
|
|
+ selectedCfi.value = v;
|
|
|
+ });
|
|
|
|
|
|
- epubStore.rendition.on("rendered", () => {
|
|
|
- toast.close();
|
|
|
+ epubStore.rendition.on("rendered", () => {
|
|
|
+ toast.close();
|
|
|
|
|
|
- isLogin.value && getLabelList();
|
|
|
- });
|
|
|
+ isLogin.value && getLabelList();
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ toast.close();
|
|
|
+ router.replace({
|
|
|
+ name: "404",
|
|
|
+ query: { icon: "error", description: "图书加载失败" },
|
|
|
+ });
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const selected = (e, v) => {
|
|
@@ -153,6 +208,16 @@ const getLabelList = async () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+const handleClipboard = async () => {
|
|
|
+ try {
|
|
|
+ await toClipboard(location.href);
|
|
|
+ showToast({
|
|
|
+ message: "链接已复制",
|
|
|
+ duration: 4000,
|
|
|
+ });
|
|
|
+ } catch (err) {}
|
|
|
+};
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
getBookDetail();
|
|
|
});
|