chenlei 9 місяців тому
батько
коміт
c416123297
32 змінених файлів з 264 додано та 103 видалено
  1. 3 3
      packages/base/src/stores/epub.js
  2. 5 0
      packages/mobile/src/api/index.js
  3. 1 1
      packages/mobile/src/views/Detail/index.vue
  4. 5 1
      packages/mobile/src/views/Home/components/SecondPane/index.vue
  5. 1 1
      packages/mobile/src/views/Home/index.vue
  6. 6 1
      packages/mobile/src/views/Reader/index.vue
  7. 16 1
      packages/mobile/src/views/Stack/index.vue
  8. 1 1
      packages/pc/package.json
  9. 1 1
      packages/pc/public/three/index.js
  10. 10 1
      packages/pc/src/api/index.js
  11. 35 7
      packages/pc/src/components/BookCard/index.vue
  12. 1 0
      packages/pc/src/components/TopNav/index.scss
  13. 1 1
      packages/pc/src/components/TopNav/index.vue
  14. 3 1
      packages/pc/src/main.js
  15. 4 1
      packages/pc/src/views/Bookshelf/components/UploadDialog.vue
  16. 3 1
      packages/pc/src/views/Bookshelf/components/UploadList.vue
  17. 5 1
      packages/pc/src/views/Bookshelf/index.vue
  18. 1 1
      packages/pc/src/views/Detail/components/Bookmark.vue
  19. 2 1
      packages/pc/src/views/Detail/components/Directory.vue
  20. 1 1
      packages/pc/src/views/Detail/components/IntroductionDialog.vue
  21. 10 2
      packages/pc/src/views/Detail/components/Reader/index.vue
  22. 1 1
      packages/pc/src/views/Detail/components/Search.vue
  23. 1 1
      packages/pc/src/views/Detail/components/Setting.vue
  24. 1 1
      packages/pc/src/views/Detail/components/Toolbar/index.vue
  25. 1 1
      packages/pc/src/views/Detail/index.vue
  26. 25 8
      packages/pc/src/views/Home/index.scss
  27. 52 40
      packages/pc/src/views/Home/index.vue
  28. 15 3
      packages/pc/src/views/Home2/index.vue
  29. 1 2
      packages/pc/src/views/Stack/components/Sidebar/index.vue
  30. 8 0
      packages/pc/src/views/Stack/index.scss
  31. 29 15
      packages/pc/src/views/Stack/index.vue
  32. 15 3
      pnpm-lock.yaml

+ 3 - 3
packages/base/src/stores/epub.js

@@ -73,9 +73,9 @@ export const useEpubStore = defineStore("epub", () => {
       `${EPUB_LOCATION}-${fileName.value}`
     );
     if (locationStorage) {
-      const arr = locationStorage.split("-");
+      const match = locationStorage.match(/^(.*?)-(\d+)$/);
 
-      goToChapter(arr[0], arr[1]);
+      goToChapter(match[1], match[2]);
     } else {
       goToChapter();
     }
@@ -223,7 +223,7 @@ export const useEpubStore = defineStore("epub", () => {
    * @param {String | Number} page
    */
   const goToChapter = async (cfi, page) => {
-    await rendition.value.display();
+    await rendition.value.display(cfi);
 
     if (page) {
       nextTick(() => {

+ 5 - 0
packages/mobile/src/api/index.js

@@ -178,3 +178,8 @@ export const getMediaListApi = (params) => {
     },
   });
 };
+
+// 图书-儿子节点列表
+export const getStorageSonListApi = (id) => {
+  return requestByPost(`/api/show/storage/getSonList/${id}`);
+};

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

@@ -3,7 +3,7 @@
     <nav-bar>
       <div class="detail__tag" @click="handleCollect">
         <img :src="isCollect ? CollectIcon : UnCollectIcon" />
-        <span>{{ isCollect ? "移书架" : "加入书架" }}</span>
+        <span>{{ isCollect ? "移书架" : "加入书架" }}</span>
       </div>
     </nav-bar>
 

+ 5 - 1
packages/mobile/src/views/Home/components/SecondPane/index.vue

@@ -80,7 +80,11 @@ const getRecommendList = async () => {
       pageSize: 10,
       type: "rank",
     });
-    recommendList.value = data;
+    recommendList.value = data.map((i) => ({
+      ...i,
+      _id: i.id,
+      id: i.bookId,
+    }));
   } finally {
     recommendLoading.value = false;
   }

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

@@ -31,7 +31,7 @@ onMounted(() => {
     router.push({
       name: "detail",
       params: {
-        id: item.id,
+        id: item.bookId,
       },
     });
   };

+ 6 - 1
packages/mobile/src/views/Reader/index.vue

@@ -12,7 +12,7 @@
         </div>
         <div class="reader-page__tag" @click="handleCollect">
           <img :src="isCollect ? CollectIcon : UnCollectIcon" />
-          <span>{{ isCollect ? "移书架" : "加入书架" }}</span>
+          <span>{{ isCollect ? "移书架" : "加入书架" }}</span>
         </div>
       </div>
     </nav-bar>
@@ -191,8 +191,11 @@ const hideSelectMenu = () => {
   selectMenuStyle.value = { visibility: "hidden" };
 };
 
+const isLoadedNote = ref(false);
 // 获取笔记列表
 const getLabelList = async () => {
+  if (isLoadedNote.value) return;
+
   await readerStore.getLabelList(detail.value.id);
 
   noteList.value.forEach((item) => {
@@ -206,6 +209,8 @@ const getLabelList = async () => {
       }
     );
   });
+
+  isLoadedNote.value = true;
 };
 
 const handleClipboard = async () => {

+ 16 - 1
packages/mobile/src/views/Stack/index.vue

@@ -67,7 +67,7 @@
 <script setup>
 import { usePagination, PaginationType } from "@lsq/base";
 import { useRouteQuery } from "@vueuse/router";
-import { getBookListApi } from "@/api";
+import { getBookListApi, getStorageSonListApi } from "@/api";
 import { RecycleScroller } from "vue-virtual-scroller";
 import BookCard2 from "@/components/BookCard2.vue";
 import ClassifyScroll from "./components/ClassifyScroll.vue";
@@ -85,6 +85,21 @@ const {
   getList: getBookList,
   resetParams: resetBookParams,
 } = usePagination(async (params) => {
+  // 查找书籍分类指定分类
+  if (storageId.value) {
+    const data = await getStorageSonListApi(storageId.value);
+
+    // 模拟分页
+    return {
+      pages: 1,
+      total: data.length,
+      records: data.slice(
+        (params.pageNum - 1) * params.pageSize,
+        params.pageNum * params.pageSize
+      ),
+    };
+  }
+
   return getBookListApi({
     ...params,
     storageId: storageId.value || null,

+ 1 - 1
packages/pc/package.json

@@ -10,7 +10,7 @@
   },
   "dependencies": {
     "@dage/service": "^1.0.3",
-    "@dage/utils": "^1.0.2",
+    "@dage/utils": "^1.1.0",
     "@element-plus/icons-vue": "^2.3.1",
     "@lsq/base": "workspace:^",
     "@vueuse/core": "^11.1.0",

+ 1 - 1
packages/pc/public/three/index.js

@@ -156,7 +156,7 @@ Viewer.prototype.preLoadCards = function () {
                 this.addCard()
             }
         }
-        setTimeout(add,  100/* 40000 * Math.random() * this.getDensity() */)  //当前视野中密度越小 添加越频繁
+        setTimeout(add,  1000/* 40000 * Math.random() * this.getDensity() */)  //当前视野中密度越小 添加越频繁
     }
     add()
 

+ 10 - 1
packages/pc/src/api/index.js

@@ -97,7 +97,11 @@ export const getUserCollectedBookCountApi = () => {
 
 // 保存收藏图书&保存浏览节点
 export const updateBookCollectApi = (bootId, query) => {
-  return requestByGet(`/api/wx/book/collect/${bootId}`, query);
+  return requestByGet(`/api/wx/book/collect/${bootId}`, query, {
+    meta: {
+      showError: false,
+    },
+  });
 };
 
 // 退出登录
@@ -154,3 +158,8 @@ export const deleteMessageApi = (id) => {
 export const getMediaListApi = (params) => {
   return requestByPost("/api/show/video/pageList", params);
 };
+
+// 图书-儿子节点列表
+export const getStorageSonListApi = (id) => {
+  return requestByPost(`/api/show/storage/getSonList/${id}`);
+};

+ 35 - 7
packages/pc/src/components/BookCard/index.vue

@@ -1,26 +1,28 @@
 <template>
   <router-link
-    :to="{ name: 'detail', params: { id: item.id, type: 'reader' } }"
+    :to="{ name: 'detail', params: { id: _item.id, type: 'reader' } }"
     class="book-card"
     :class="{ row: isRow, large: size === 'large' }"
   >
     <div class="book-card-img">
-      <el-image :src="`${baseUrl}${item.thumb}`" fit="cover" />
+      <el-image :src="`${baseUrl}${_item.thumb}`" fit="cover" />
 
       <img
         v-if="showLike"
         class="book-card-img__like"
-        src="@/assets/images/icon_like.png"
+        :src="isLike ? LikeIcon : UnLikeIcon"
         draggable="false"
+        :title="isLike ? '移除书架' : '加入书架'"
+        @click.prevent="handleLike"
       />
 
       <div v-if="showRead" class="book-card-img__tag">读过</div>
     </div>
 
     <div class="book-card-inner">
-      <p class="book-card__name limit-line">{{ item.name }}</p>
-      <p v-if="isRow" class="limit-line">{{ item.author }}</p>
-      <p class="limit-line">{{ item.press }}</p>
+      <p class="book-card__name limit-line">{{ _item.name }}</p>
+      <p v-if="isRow" class="limit-line">{{ _item.author }}</p>
+      <p class="limit-line">{{ _item.press }}</p>
     </div>
   </router-link>
 </template>
@@ -28,6 +30,9 @@
 <script setup>
 import { computed } from "vue";
 import { getBaseUrl } from "@/utils";
+import { updateBookCollectApi } from "@/api";
+import LikeIcon from "@/assets/images/icon_like.png";
+import UnLikeIcon from "@/assets/images/icon_unlike.png";
 
 const props = defineProps({
   // 模式 row-横版 column-竖版
@@ -55,9 +60,32 @@ const props = defineProps({
     required: true,
   },
 });
+const emits = defineEmits(["update:item"]);
 
 const baseUrl = getBaseUrl();
 const isRow = computed(() => props.type === "row");
+const isLike = computed(() => _item.value.isCollect === 1);
+
+const _item = computed({
+  get() {
+    return props.item;
+  },
+  set(v) {
+    emits("update:item", v);
+  },
+});
+
+const handleLike = async () => {
+  const temp = isLike.value;
+  try {
+    _item.value.isCollect = temp ? 2 : 1;
+    await updateBookCollectApi(_item.value.id, {
+      status: temp ? 2 : 1,
+    });
+  } catch (err) {
+    _item.value.isCollect = temp;
+  }
+};
 </script>
 
 <style lang="scss" scoped>
@@ -86,7 +114,7 @@ const isRow = computed(() => props.type === "row");
       .book-card__name {
         margin-bottom: 20px;
         font-size: 24px;
-        line-height: 28px;
+        line-height: 34px;
       }
     }
     .book-card-img {

+ 1 - 0
packages/pc/src/components/TopNav/index.scss

@@ -114,6 +114,7 @@
         border: 1px solid var(--el-color-primary);
         border-radius: 3px;
         box-sizing: border-box;
+        white-space: nowrap;
         cursor: pointer;
       }
     }

+ 1 - 1
packages/pc/src/components/TopNav/index.vue

@@ -180,7 +180,7 @@ const detailStore = useDetailStore();
 const { introductionVisible, detail } = storeToRefs(detailStore);
 const baseStore = useBaseStore();
 const { isLogin, loginVisible, userInfo } = storeToRefs(baseStore);
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const { isSimplified } = storeToRefs(epubStore);
 
 const isDetail = ref(false);

+ 3 - 1
packages/pc/src/main.js

@@ -16,8 +16,10 @@ import SvgIcon from "./components/SvgIcon";
 import PagePane from "./components/PagePane.vue";
 
 const app = createApp(App);
+const pinia = createPinia();
+window.pinia = pinia;
 
-app.use(createPinia());
+app.use(pinia);
 app.use(router);
 app.component("svg-icon", SvgIcon);
 app.component("page-pane", PagePane);

+ 4 - 1
packages/pc/src/views/Bookshelf/components/UploadDialog.vue

@@ -188,6 +188,7 @@ const DEFAULT_FORM = {
   author: "",
   year: "",
   thumb: "",
+  thumbPc: "",
   storageId: "",
   exhibitTypeId: "",
   num: "",
@@ -260,9 +261,11 @@ const handleUpload = async (params) => {
   const formData = new FormData();
   formData.append("file", params.file);
   formData.append("type", "img");
+  formData.append("isCompress", "true");
   try {
     const data = await uploadApi(formData);
-    form.thumb = data.filePath;
+    form.thumb = data.thumb || data.filePath;
+    form.thumbPc = data.filePath;
     params.onSuccess();
   } catch (err) {
     ElMessage.error("上传封面失败");

+ 3 - 1
packages/pc/src/views/Bookshelf/components/UploadList.vue

@@ -34,7 +34,9 @@
         #default="scope"
         label="操作"
       >
-        <el-link type="error" @click="handleDelete(scope.row.id)">删除</el-link>
+        <el-link type="danger" @click="handleDelete(scope.row.id)"
+          >删除</el-link
+        >
       </el-table-column>
     </el-table>
   </div>

+ 5 - 1
packages/pc/src/views/Bookshelf/index.vue

@@ -60,7 +60,11 @@
 
         <div v-loading="bookLoading" style="min-height: 300px">
           <ul>
-            <li v-for="item in bookList" :key="item.id">
+            <!-- isCollect = 2 移出书架 -->
+            <li
+              v-for="item in bookList.filter((i) => i.isCollect !== 2)"
+              :key="item.id"
+            >
               <book-card
                 type="column"
                 size="large"

+ 1 - 1
packages/pc/src/views/Detail/components/Bookmark.vue

@@ -37,7 +37,7 @@ import Drawer from "./Drawer.vue";
 
 const props = defineProps(["visible"]);
 const emits = defineEmits(["update:visible", "close"]);
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const detailStore = useDetailStore();
 const list = ref([]);
 const loading = ref(false);

+ 2 - 1
packages/pc/src/views/Detail/components/Directory.vue

@@ -15,6 +15,7 @@
         <li
           v-for="item in epubStore.navigation"
           :key="item.id"
+          class="limit-line"
           @click="goToChapter(item.href)"
         >
           {{ item.label }}
@@ -35,7 +36,7 @@ const props = defineProps(["visible"]);
 const emits = defineEmits(["update:visible", "close"]);
 
 const baseUrl = getBaseUrl();
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const detailStore = useDetailStore();
 const { detail } = storeToRefs(detailStore);
 const show = computed({

+ 1 - 1
packages/pc/src/views/Detail/components/IntroductionDialog.vue

@@ -9,7 +9,7 @@
       <el-image
         fit="cover"
         class="introduction-dialog-left__cover"
-        :src="baseUrl + detail.thumb"
+        :src="baseUrl + (detail.thumbPc || detail.thumb)"
       />
       <div class="introduction-dialog-left-info">
         <h3>{{ detail.name }}</h3>

+ 10 - 2
packages/pc/src/views/Detail/components/Reader/index.vue

@@ -64,7 +64,10 @@
             epubStore.nextPage();
             if (isLogin && detail && typeof detail.isCollect !== 'number') {
               // 标记为有阅读过
-              updateBookCollectApi(detail.id);
+              updateBookCollectApi(detail.id, {
+                status: 0,
+              });
+              detail.isCollect = 0;
             }
           }
         "
@@ -90,7 +93,7 @@ import { updateBookCollectApi, saveLabelApi, deleteLabelApi } from "@/api";
 const props = defineProps(["detail", "isLogin"]);
 
 const baseStore = useBaseStore();
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const detailStore = useDetailStore();
 const { isLogin } = storeToRefs(baseStore);
 const { toClipboard } = useClipboard();
@@ -134,7 +137,10 @@ onBeforeUnmount(() => {
   epubStore.clear();
 });
 
+const isLoadedNote = ref(false);
 const getLabelList = () => {
+  if (isLoadedNote.value) return;
+
   detailStore.getLabelList().then(() => {
     detailStore.noteList.forEach((item) => {
       epubStore.rendition?.annotations.highlight(
@@ -147,6 +153,8 @@ const getLabelList = () => {
         }
       );
     });
+
+    isLoadedNote.value = true;
   });
 };
 

+ 1 - 1
packages/pc/src/views/Detail/components/Search.vue

@@ -49,7 +49,7 @@ import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
 const props = defineProps(["visible"]);
 const emits = defineEmits(["update:visible", "close"]);
 
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const detailStore = useDetailStore();
 const list = ref([]);
 const loading = ref(false);

+ 1 - 1
packages/pc/src/views/Detail/components/Setting.vue

@@ -50,7 +50,7 @@ const emits = defineEmits(["update:visible", "close"]);
 
 const isDark = useDark();
 const toggleDark = useToggle(isDark);
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 
 const show = computed({
   get() {

+ 1 - 1
packages/pc/src/views/Detail/components/Toolbar/index.vue

@@ -39,7 +39,7 @@ import Bookmark from "../Bookmark.vue";
 import Comment from "../Comment.vue";
 
 const { isFullscreen, isSupported, toggle } = useFullscreen();
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const detailStore = useDetailStore();
 const baseStore = useBaseStore();
 const active = ref(-1);

+ 1 - 1
packages/pc/src/views/Detail/index.vue

@@ -27,7 +27,7 @@ import IntroductionDialog from "./components/IntroductionDialog.vue";
 const route = useRoute();
 const router = useRouter();
 const baseStore = useBaseStore();
-const epubStore = useEpubStore();
+const epubStore = useEpubStore(window.pinia);
 const detailStore = useDetailStore();
 const { isLogin } = storeToRefs(baseStore);
 const { introductionVisible, detail } = storeToRefs(detailStore);

+ 25 - 8
packages/pc/src/views/Home/index.scss

@@ -1,4 +1,9 @@
 .home {
+  margin-top: calc(var(--topnav-height) * -1);
+  height: 100vh;
+  overflow: hidden;
+  background: url("@/assets/images/bg-min.jpg") no-repeat center / cover;
+
   &-iframe {
     position: absolute;
     top: 0;
@@ -12,7 +17,7 @@
     display: flex;
     flex-direction: column;
     align-items: center;
-    margin: calc(45px + var(--topnav-height)) auto 0;
+    margin: calc(45px + var(--topnav-height) * 2) auto 0;
     width: 800px;
     z-index: 999;
   }
@@ -80,18 +85,18 @@
     left: 50%;
     bottom: 36px;
     display: flex;
-    flex-direction: column;
-    gap: 5px;
     align-items: center;
+    justify-content: center;
+    gap: 5px;
+    width: 131px;
+    height: 42px;
     font-size: 16px;
-    color: var(--el-color-primary);
+    color: white;
     transform: translateX(-50%);
     cursor: pointer;
+    background: url("@/assets/images/btn_01-min.png") no-repeat center / cover;
     z-index: 2;
-
-    img {
-      width: 30px;
-    }
+    animation: breathing ease-in-out 2s forwards infinite;
   }
 }
 
@@ -124,3 +129,15 @@
     line-height: 24px;
   }
 }
+
+@keyframes breathing {
+  0% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.6;
+  }
+  100% {
+    opacity: 1;
+  }
+}

+ 52 - 40
packages/pc/src/views/Home/index.vue

@@ -1,47 +1,49 @@
 <template>
-  <iframe
-    id="iframe"
-    class="home-iframe"
-    src="./three/index.html"
-    frameborder="0"
-  ></iframe>
-
-  <div ref="txtDom" class="txt" :style="{ opacity: txt.show ? '0.8' : '0' }">
-    <h2>{{ txt.title }}</h2>
-    <p>{{ txt.con }}</p>
-  </div>
+  <div class="home">
+    <iframe
+      id="iframe"
+      class="home-iframe"
+      src="./three/index.html"
+      frameborder="0"
+    ></iframe>
+
+    <div ref="txtDom" class="txt" :style="{ opacity: txt.show ? '0.8' : '0' }">
+      <h2>{{ txt.title }}</h2>
+      <p>{{ txt.con }}</p>
+    </div>
 
-  <div class="home-main">
-    <img
-      class="home-main__logo"
-      draggable="false"
-      src="@/assets/images/logo_01-min.png"
-    />
-
-    <div class="home-search">
-      <input
-        v-model="keyword"
-        class="home-search__input"
-        type="text"
-        placeholder="请输入关键词..."
-        @keyup.enter="handleSearch"
+    <div class="home-main">
+      <img
+        class="home-main__logo"
+        draggable="false"
+        src="@/assets/images/logo_01-min.png"
       />
-      <button class="home-search__btn" @click="handleSearch">搜索</button>
-    </div>
 
-    <div class="home-view" @click="$router.push({ name: 'stack' })">
-      <p class="limit-line">共收录{{ total }}件藏品,查看书库</p>
+      <div class="home-search">
+        <input
+          v-model="keyword"
+          class="home-search__input"
+          type="text"
+          placeholder="请输入关键词..."
+          @keyup.enter="handleSearch"
+        />
+        <button class="home-search__btn" @click="handleSearch">搜索</button>
+      </div>
+
+      <div class="home-view" @click="$router.push({ name: 'stack' })">
+        <p class="limit-line">共收录{{ total }}件藏品,查看书库</p>
+      </div>
     </div>
-  </div>
 
-  <router-link class="home-more" :to="{ name: 'home2' }">
-    <img draggable="false" src="@/assets/images/icon_click.png" />
-    <p>查看更多</p>
-  </router-link>
+    <router-link class="home-more" :to="{ name: 'home2' }">
+      <p>查看更多</p>
+    </router-link>
+  </div>
 </template>
 
 <script setup>
 import { onMounted, ref } from "vue";
+import { compressImages } from "@dage/utils";
 import { getRecommendListApi, getBookCountApi } from "@/api";
 import { useRouter } from "vue-router";
 import { getBaseUrl } from "@/utils";
@@ -57,18 +59,18 @@ const keyword = ref("");
 onMounted(() => {
   // 点击图片
   window.clickObject = (val) => {
-    const item = list.value.find((i) => i.thumb.indexOf(val) > -1);
+    const item = list.value.find((i) => i._thumb.indexOf(val) > -1);
     router.push({
       name: "detail",
       params: {
-        id: item.id,
+        id: item.bookId,
         type: "reader",
       },
     });
   };
   // 鼠标移入
   window.hoverObject = (val) => {
-    const item = list.value.find((i) => i.thumb.indexOf(val.imgName) > -1);
+    const item = list.value.find((i) => i._thumb.indexOf(val.imgName) > -1);
     if (!item) return;
 
     txt.value = {
@@ -103,19 +105,29 @@ onMounted(() => {
 });
 
 const getRecommendList = async () => {
-  const data = await getRecommendListApi({
+  let data = await getRecommendListApi({
     pageNum: 1,
     pageSize: 20,
     type: "index",
   });
 
+  data = await compressImages({
+    list: data.map((i) => ({
+      ...i,
+      thumb: `${baseUrl}${i.thumb}`,
+    })),
+    maxWidth: 1000,
+    qualityRatio: 0.8,
+  });
+
+  const imgList = data.map((i) => i._thumb);
   const iframe = document.getElementById("iframe");
   const iframeDoc = iframe?.contentDocument || iframe?.contentWindow.document;
   if (iframeDoc?.readyState === "complete") {
-    iframe.contentWindow.initViewer(data.map((i) => `${baseUrl}${i.thumb}`));
+    iframe.contentWindow.initViewer(imgList);
   } else {
     iframe.onload = () => {
-      iframe.contentWindow.initViewer(data.map((i) => `${baseUrl}${i.thumb}`));
+      iframe.contentWindow.initViewer(imgList);
     };
   }
   list.value = data;

+ 15 - 3
packages/pc/src/views/Home2/index.vue

@@ -15,12 +15,20 @@
             </p>
           </div>
 
-          <el-button class="home2-top__pick" type="primary">去挑书 +</el-button>
+          <el-button
+            class="home2-top__pick"
+            type="primary"
+            @click="$router.push({ name: 'stack' })"
+            >去挑书 +</el-button
+          >
         </div>
 
         <div class="home2-top-main">
+          <!-- isCollect = 2 移出书架 -->
           <book-card
-            v-for="item in bookList.slice(0, 4)"
+            v-for="item in bookList
+              .filter((i) => i.isCollect !== 2)
+              .slice(0, 4)"
             :key="item.id"
             :item="item"
             type="column"
@@ -127,7 +135,11 @@ const getRecommendList = async () => {
       pageSize: 10,
       type: "rank",
     });
-    recommendList.value = data;
+    recommendList.value = data.map((i) => ({
+      ...i,
+      _id: i.id,
+      id: i.bookId,
+    }));
   } finally {
     recommendLoading.value = false;
   }

+ 1 - 2
packages/pc/src/views/Stack/components/Sidebar/index.vue

@@ -78,8 +78,6 @@ const getStorageTree = async () => {
     bookLoading.value = true;
     const data = await getStorageTreeApi();
     bookList.value = bookList.value.concat(data);
-
-    emits("select", [bookList.value[0]]);
   } finally {
     bookLoading.value = false;
   }
@@ -135,6 +133,7 @@ const findItemById = (data, id) => {
 
 const handleTab = async () => {
   const isCollection = activeTab.value === "collection";
+  // 初始化数据
   if (isCollection) {
     collectionList.value.length === 1 && (await getExhibitTypeList());
   } else {

+ 8 - 0
packages/pc/src/views/Stack/index.scss

@@ -1,7 +1,13 @@
 .stack {
   display: flex;
   gap: calc(70px - 12.5px);
+  height: 100%;
 
+  &-page {
+    :deep(.el-scrollbar__view) {
+      height: 100%;
+    }
+  }
   &-left {
     flex: 0 0 360px;
     padding-right: 20px;
@@ -10,9 +16,11 @@
   &-main {
     flex: 1;
     padding-top: 15px;
+    height: 100%;
     display: flex;
     flex-direction: column;
     align-items: center;
+    box-sizing: border-box;
   }
 
   &-breadcrumb {

+ 29 - 15
packages/pc/src/views/Stack/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <page-pane>
+  <page-pane class="stack-page">
     <div class="stack">
       <div class="stack-left">
         <sidebar @select="handleSelect" />
@@ -34,6 +34,7 @@
           background
           layout="prev, pager, next"
           :total="total"
+          :page-size="DEFAULT_PAGE_SIZE"
           @change="handlePage"
         />
       </div>
@@ -42,29 +43,47 @@
 </template>
 
 <script setup>
-import { ref, watch } from "vue";
+import { ref } from "vue";
 import { useRouteQuery } from "@vueuse/router";
 import { ArrowRight } from "@element-plus/icons-vue";
-import { usePagination } from "@lsq/base";
-import { getBookListApi } from "@/api";
+import { DEFAULT_PAGE_SIZE, usePagination } from "@lsq/base";
+import { getBookListApi, getStorageSonListApi } from "@/api";
 import Sidebar from "./components/Sidebar/index.vue";
 import BookCard from "@/components/BookCard/index.vue";
 
 const breadcrumb = ref([]);
-const activeTab = useRouteQuery("active");
+const activeTab = useRouteQuery("active", "book");
 const searchKey = useRouteQuery("keyword");
 
 const { pageNum, total, loading, list, noData, getList } = usePagination(
   async (params) => {
+    const id = breadcrumb.value[breadcrumb.value.length - 1].id;
+
+    // 查找书籍分类指定分类
+    if (activeTab.value === "book" && id > -1) {
+      const data = await getStorageSonListApi(id);
+
+      // 模拟分页
+      return {
+        pages: 1,
+        total: data.length,
+        records: data.slice(
+          (params.pageNum - 1) * params.pageSize,
+          params.pageNum * params.pageSize
+        ),
+      };
+    }
+
     const p = {
       ...params,
       searchKey: searchKey.value,
     };
-    const id = breadcrumb.value[breadcrumb.value.length - 1].id;
-    if (activeTab.value === "collection" && id > -1) {
-      p.exhibitTypeId = id;
-    } else if (id > -1) {
-      p.storageId = id;
+    if (breadcrumb.value.length) {
+      if (activeTab.value === "collection" && id > -1) {
+        p.exhibitTypeId = id;
+      } else if (id > -1) {
+        p.storageId = id;
+      }
     }
     return getBookListApi(p);
   }
@@ -87,11 +106,6 @@ const handleSelect = (type, data) => {
   }
   resetSearch();
 };
-
-watch(breadcrumb, (v) => {
-  pageNum.value = 1;
-  v.length && getList();
-});
 </script>
 
 <style lang="scss" scoped>

+ 15 - 3
pnpm-lock.yaml

@@ -100,8 +100,8 @@ importers:
         specifier: ^1.0.3
         version: 1.0.3(lodash@4.17.21)
       '@dage/utils':
-        specifier: ^1.0.2
-        version: 1.0.2(lodash@4.17.21)
+        specifier: ^1.1.0
+        version: 1.1.0(lodash@4.17.21)
       '@element-plus/icons-vue':
         specifier: ^2.3.1
         version: 2.3.1(vue@3.5.11)
@@ -463,7 +463,7 @@ packages:
   /@dage/service@1.0.3(lodash@4.17.21):
     resolution: {integrity: sha512-rZKn9NUQWpZFtzreZyXIl1zK/4+3SsvosuNoq2LWFZl2DakniDdGuLOkkggb1hnRkmscTNQrZQOMpepYrglVWw==}
     dependencies:
-      '@dage/utils': 1.0.2(lodash@4.17.21)
+      '@dage/utils': 1.1.0(lodash@4.17.21)
     transitivePeerDependencies:
       - lodash
     dev: false
@@ -480,6 +480,18 @@ packages:
       query-string: 8.2.0
     dev: false
 
+  /@dage/utils@1.1.0(lodash@4.17.21):
+    resolution: {integrity: sha512-jdP28YBCXddoHxTuFaldKWrSu3m2K6nuJaAbjrQ21o0b8f1y04fesVy0G041Wacrt3ugEawUVKzhU2N+rdiGjw==}
+    peerDependencies:
+      lodash: 4.*
+    dependencies:
+      '@dage/events': 1.0.1
+      dayjs: 1.11.13
+      js-base64: 3.7.7
+      lodash: 4.17.21
+      query-string: 8.2.0
+    dev: false
+
   /@element-plus/icons-vue@2.3.1(vue@3.5.11):
     resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
     peerDependencies: