浏览代码

fix[mobile]: --

chenlei 9 月之前
父节点
当前提交
c9292eb8ed

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "pinia": "^2.2.5",
     "tslib": "^2.8.0"
   }
 }

+ 2 - 0
packages/base/src/theme.scss

@@ -7,6 +7,7 @@ html {
   --text-color-secondary: rgba(70, 70, 70, 0.8);
   --text-color-placeholder: rgba(70, 70, 70, 0.5);
   --color-primary-opacity-5: rgba(209, 187, 158, 0.5);
+  --btn-bg-color: rgba(0, 0, 0, 0.3);
   --icon-color: #d1bb9e;
   --icon-mb-color: #464646;
   --icon-close-color: #585757;
@@ -27,6 +28,7 @@ html.van-theme-dark {
   --text-color-secondary: rgba(236, 236, 236, 0.8);
   --text-color-placeholder: rgba(236, 236, 236, 0.5);
   --color-primary-opacity-5: rgba(209, 187, 158, 0.5);
+  --btn-bg-color: rgba(0, 0, 0, 0.3);
   --icon-color: #2c2c2c;
   --icon-mb-color: white;
   --icon-close-color: white;

+ 3 - 1
packages/mobile/package.json

@@ -14,7 +14,6 @@
     "@lsq/base": "workspace:^",
     "@vueuse/router": "^11.1.0",
     "lodash": "^4.17.21",
-    "pinia": "^2.2.4",
     "swiper": "^11.1.14",
     "vant": "^4.9.7",
     "vconsole": "^3.15.1",
@@ -32,5 +31,8 @@
     "unplugin-vue-components": "^0.27.4",
     "vite": "^5.4.8",
     "vite-plugin-svg-icons": "^2.0.1"
+  },
+  "peerDependencies": {
+    "pinia": "2.*"
   }
 }

文件差异内容过多而无法显示
+ 7 - 0
packages/mobile/src/assets/svgs/icon_share.svg


+ 6 - 3
packages/mobile/src/main.js

@@ -15,12 +15,15 @@ import router from "./router";
 import SvgIcon from "./components/SvgIcon";
 import { Lazyload } from "vant";
 
-import VConsole from "vconsole";
-const vConsole = new VConsole();
+// import VConsole from "vconsole";
+// const vConsole = new VConsole();
 
 const app = createApp(App);
+const pinia = createPinia();
 
-app.use(createPinia());
+window.pinia = pinia;
+
+app.use(pinia);
 app.use(router);
 app.use(Lazyload);
 app.component("svg-icon", SvgIcon);

+ 9 - 0
packages/mobile/src/router/index.js

@@ -66,6 +66,15 @@ const router = createRouter({
         hideTabbar: true,
       },
     },
+    {
+      path: "/404",
+      name: "404",
+      component: () => import("@/views/NotFound/index.vue"),
+    },
+    {
+      path: "/:pathMatch(.*)",
+      redirect: "/404",
+    },
   ],
 });
 

+ 2 - 7
packages/mobile/src/views/Detail/index.vue

@@ -16,7 +16,7 @@
         />
 
         <div class="detail-info-inner">
-          <h3>{{ detail?.name }}</h3>
+          <h3 class="limit-line">{{ detail?.name }}</h3>
           <p>{{ detail?.author }}</p>
           <p>{{ detail?.press }} 编</p>
 
@@ -47,7 +47,6 @@
 <script setup>
 import { useRoute, useRouter } from "vue-router";
 import { ref, computed, watch } from "vue";
-import { showNotify } from "vant";
 import { storeToRefs } from "pinia";
 import { useRouteQuery } from "@vueuse/router";
 import { useBaseStore } from "@/stores";
@@ -119,11 +118,7 @@ const getBookDetail = async () => {
     route.params.id
   );
   if (!data) {
-    showNotify({
-      type: "warning",
-      message: "图书丢失了",
-    });
-    router.replace({ name: "home" });
+    router.replace({ name: "404" });
     return;
   }
   detail.value = data;

+ 2 - 2
packages/mobile/src/views/Home/components/RankPane.vue

@@ -6,9 +6,9 @@
         <span>排行榜</span>
       </div>
 
-      <p class="rank-pane-header__more" @click="emits('more')">
+      <!-- <p class="rank-pane-header__more" @click="emits('more')">
         More <span>+</span>
-      </p>
+      </p> -->
     </div>
 
     <van-loading v-if="loading" class="rank-pane__loading" />

+ 2 - 1
packages/mobile/src/views/Home/index.vue

@@ -37,7 +37,8 @@ onMounted(() => {
   };
   // 鼠标移入
   window.hoverObject = (val) => {
-    const item = list.value[val.target.index];
+    const item = list.value.find((i) => i.thumb.indexOf(val.imgName) > -1);
+    if (!item) return;
 
     txt.value = {
       title: item.name,

+ 1 - 1
packages/mobile/src/views/Mine/index.vue

@@ -133,7 +133,7 @@ const getMyBookList = async () => {
 };
 
 const handleUpload = () => {
-  toClipboard(location.href);
+  toClipboard(location.origin + "/pc/index.html");
   showToast({
     message: "由于设备限制,请在pc端完成图书上传.链接已复制",
     duration: 4000,

+ 21 - 0
packages/mobile/src/views/NotFound/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <nav-bar back-color="var(--icon-close-color)" :need-bg-color="false" />
+
+  <van-empty
+    :image-size="200"
+    :image="$route.query.icon"
+    :description="$route.query.description || '页面不见了'"
+    class="not-found"
+  />
+</template>
+
+<script setup>
+import NavBar from "@/components/NavBar.vue";
+</script>
+
+<style lang="scss" scoped>
+.not-found {
+  padding-top: 200px;
+  --van-empty-description-font-size: 40px;
+}
+</style>

+ 31 - 0
packages/mobile/src/views/Reader/index.scss

@@ -19,4 +19,35 @@
   #reader {
     flex: 1;
   }
+  &-head {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+  }
+  &__share {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 80px;
+    height: 80px;
+    border-radius: 50%;
+    background: var(--btn-bg-color);
+  }
+  &__tag {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 5px;
+    width: 250px;
+    height: 80px;
+    color: white;
+    border-top-left-radius: 50px;
+    border-bottom-left-radius: 50px;
+    background: var(--btn-bg-color);
+
+    img {
+      width: 60px;
+      height: 60px;
+    }
+  }
 }

+ 102 - 37
packages/mobile/src/views/Reader/index.vue

@@ -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();
 });

+ 24 - 11
packages/mobile/src/views/Stack/components/ClassifyScroll.vue

@@ -44,27 +44,41 @@ const props = defineProps({
   },
 });
 const isStorage = computed(() => props.activeTab === 0);
-const emits = defineEmits(["storageId", "exhibitId", "init"]);
+const emits = defineEmits(["storageId", "exhibitId", "tabChange"]);
 const storageActive = ref(0);
 const exhibitActive = ref(0);
-const storageList = ref([]);
-const exhibitList = ref([]);
+const storageList = ref([
+  {
+    name: "全部",
+    id: 0,
+  },
+]);
+const exhibitList = ref([
+  {
+    name: "全部",
+    id: 0,
+  },
+]);
 
 const getStorageTree = async () => {
   const data = await getStorageTreeApi();
-  storageList.value = data;
+  storageList.value = storageList.value.concat(data);
 
   if (props.storageInitVal) {
-    storageActive.value = data.findIndex((i) => i.id === props.storageInitVal);
+    storageActive.value = storageList.value.findIndex(
+      (i) => i.id === props.storageInitVal
+    );
   }
 };
 
 const getExhibitTypeList = async () => {
   const data = await getExhibitTypeListApi();
-  exhibitList.value = data;
+  exhibitList.value = exhibitList.value.concat(data);
 
   if (props.exhibitInitVal) {
-    exhibitActive.value = data.findIndex((i) => i.id === props.exhibitInitVal);
+    exhibitActive.value = exhibitList.value.findIndex(
+      (i) => i.id === props.exhibitInitVal
+    );
   }
 };
 
@@ -81,13 +95,12 @@ const handleClick = (item, index) => {
 watch(
   () => props.activeTab,
   async () => {
-    if (isStorage.value && !storageList.value.length) {
+    if (isStorage.value && storageList.value.length === 1) {
       await getStorageTree();
-      emits("init", storageList.value[0].id);
-    } else if (!isStorage.value && !exhibitList.value.length) {
+    } else if (!isStorage.value && exhibitList.value.length === 1) {
       await getExhibitTypeList();
-      emits("init", exhibitList.value[0].id);
     }
+    emits("tabChange");
   },
   {
     immediate: true,

+ 8 - 14
packages/mobile/src/views/Stack/index.vue

@@ -16,7 +16,7 @@
         :exhibit-init-val="exhibitId"
         @storageId="handleStorage"
         @exhibitId="handleExhibit"
-        @init="handleInitClassify"
+        @tab-change="handleChange"
       />
     </template>
 
@@ -87,7 +87,7 @@ const {
 } = usePagination(async (params) => {
   return getBookListApi({
     ...params,
-    storageId: storageId.value,
+    storageId: storageId.value || null,
   });
 }, PaginationType.CONCAT);
 
@@ -102,7 +102,7 @@ const {
 } = usePagination(async (params) => {
   return getBookListApi({
     ...params,
-    exhibitTypeId: exhibitId.value,
+    exhibitTypeId: exhibitId.value || null,
   });
 }, PaginationType.CONCAT);
 
@@ -136,19 +136,13 @@ const handleExhibitEnd = () => {
   }
 };
 
-const handleInitClassify = (id) => {
+const handleChange = () => {
   if (activeTab.value === 0) {
-    if (storageId.value === 0) {
-      handleStorage(id);
-    } else {
-      getBookList();
-    }
+    // 按书籍分类
+    !bookList.value.length && getBookList();
   } else {
-    if (exhibitId.value === 0) {
-      handleExhibit(id);
-    } else {
-      getExhibitList();
-    }
+    // 按藏品分类
+    !exhibitList.value.length && getExhibitList();
   }
 };
 </script>

+ 3 - 1
packages/pc/package.json

@@ -17,7 +17,6 @@
     "@vueuse/router": "^11.1.0",
     "element-plus": "^2.8.3",
     "lodash": "^4.17.21",
-    "pinia": "^2.1.7",
     "vue": "^3.4.29",
     "vue-clipboard3": "^2.0.0",
     "vue-qrcode": "^2.2.2",
@@ -32,5 +31,8 @@
     "unplugin-vue-components": "^0.27.4",
     "vite": "^5.3.1",
     "vite-plugin-svg-icons": "^2.0.1"
+  },
+  "peerDependencies": {
+    "pinia": "2.*"
   }
 }

+ 40 - 3
pnpm-lock.yaml

@@ -8,6 +8,9 @@ importers:
 
   .:
     dependencies:
+      pinia:
+        specifier: ^2.2.5
+        version: 2.2.5(vue@3.5.12)
       tslib:
         specifier: ^2.8.0
         version: 2.8.0
@@ -19,7 +22,7 @@ importers:
         version: 2.1.1
       pinia:
         specifier: 2.*
-        version: 2.2.4(vue@3.5.11)
+        version: 2.2.5(vue@3.5.11)
       vue:
         specifier: 3.*
         version: 3.5.11
@@ -42,7 +45,7 @@ importers:
         specifier: ^4.17.21
         version: 4.17.21
       pinia:
-        specifier: ^2.2.4
+        specifier: 2.*
         version: 2.2.4(vue@3.5.12)
       swiper:
         specifier: ^11.1.14
@@ -118,7 +121,7 @@ importers:
         specifier: ^4.17.21
         version: 4.17.21
       pinia:
-        specifier: ^2.1.7
+        specifier: 2.*
         version: 2.2.4(vue@3.5.11)
       vue:
         specifier: ^3.4.29
@@ -3016,6 +3019,40 @@ packages:
       vue-demi: 0.14.10(vue@3.5.12)
     dev: false
 
+  /pinia@2.2.5(vue@3.5.11):
+    resolution: {integrity: sha512-T4PEQ4uFv2KIRC8A1Y3k1ceQGTDtxtd7nngYGu1IJUUSpuQoYfGq7w7rOc+f5YN1vx3mEs2NjjtN2IFbNS7jqA==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.0
+      typescript: '>=4.4.4'
+      vue: ^2.6.14 || ^3.5.11
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      typescript:
+        optional: true
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.11
+      vue-demi: 0.14.10(vue@3.5.11)
+    dev: false
+
+  /pinia@2.2.5(vue@3.5.12):
+    resolution: {integrity: sha512-T4PEQ4uFv2KIRC8A1Y3k1ceQGTDtxtd7nngYGu1IJUUSpuQoYfGq7w7rOc+f5YN1vx3mEs2NjjtN2IFbNS7jqA==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.0
+      typescript: '>=4.4.4'
+      vue: ^2.6.14 || ^3.5.11
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      typescript:
+        optional: true
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.12
+      vue-demi: 0.14.10(vue@3.5.12)
+    dev: false
+
   /pkg-types@1.2.1:
     resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
     dependencies: