chenlei vor 9 Monaten
Ursprung
Commit
d4f5a12c55
37 geänderte Dateien mit 395 neuen und 113 gelöschten Zeilen
  1. 1 2
      packages/base/package.json
  2. 0 1
      packages/base/src/index.js
  3. 8 0
      packages/base/src/stores/epub.js
  4. 0 19
      packages/base/src/utils/index.js
  5. 2 1
      packages/mobile/src/main.js
  6. 2 1
      packages/mobile/src/views/Home/index.vue
  7. 10 0
      packages/base/src/api/index.js
  8. 1 1
      packages/pc/src/components/BookCard/index.vue
  9. 10 2
      packages/pc/src/components/PagePane.vue
  10. 1 1
      packages/pc/src/components/TopNav/components/LoginDialog.vue
  11. 60 0
      packages/pc/src/components/TopNav/components/MobileDialog.vue
  12. 6 2
      packages/pc/src/components/TopNav/index.vue
  13. 2 1
      packages/pc/src/main.js
  14. 1 1
      packages/pc/src/stores/base.js
  15. 10 1
      packages/pc/src/stores/detail.js
  16. 18 0
      packages/pc/src/utils/index.js
  17. 2 2
      packages/pc/src/views/Bookshelf/components/UploadDialog.vue
  18. 2 1
      packages/pc/src/views/Bookshelf/components/UploadList.vue
  19. 2 6
      packages/pc/src/views/Bookshelf/index.vue
  20. 1 1
      packages/pc/src/views/Detail/components/Bookmark.vue
  21. 27 2
      packages/pc/src/views/Detail/components/Comment.vue
  22. 6 2
      packages/pc/src/views/Detail/components/Directory.vue
  23. 1 1
      packages/pc/src/views/Detail/components/IntroductionDialog.vue
  24. 3 1
      packages/pc/src/views/Detail/components/Note.vue
  25. 3 0
      packages/pc/src/views/Detail/components/Photocopy/index.scss
  26. 63 9
      packages/pc/src/views/Detail/components/Photocopy/index.vue
  27. 2 1
      packages/pc/src/views/Detail/components/Reader/index.scss
  28. 7 7
      packages/pc/src/views/Detail/components/Reader/index.vue
  29. 83 5
      packages/pc/src/views/Detail/components/Video/index.vue
  30. 7 2
      packages/pc/src/views/Detail/index.vue
  31. 2 1
      packages/pc/src/views/Home/index.vue
  32. 2 1
      packages/pc/src/views/Home2/components/News.vue
  33. 9 2
      packages/pc/src/views/Home2/components/NewsDialog.vue
  34. 1 1
      packages/pc/src/views/Home2/index.vue
  35. 1 1
      packages/pc/src/views/Stack/components/Sidebar/index.vue
  36. 3 1
      packages/pc/src/views/Stack/index.scss
  37. 36 33
      packages/pc/src/views/Stack/index.vue

+ 1 - 2
packages/base/package.json

@@ -13,8 +13,7 @@
   },
   "peerDependencies": {
     "pinia": "2.*",
-    "vue": "3.*",
-    "@dage/service": "*"
+    "vue": "3.*"
   },
   "keywords": [],
   "author": "",

+ 0 - 1
packages/base/src/index.js

@@ -1,4 +1,3 @@
 export * from "./stores";
 export * from "./constants";
-export * from "./api";
 export * from "./utils";

+ 8 - 0
packages/base/src/stores/epub.js

@@ -110,6 +110,13 @@ export const useEpubStore = defineStore("epub", () => {
     rendition.value = _rendition;
   };
 
+  const clear = () => {
+    rendition.value.clear();
+    rendition.value = null;
+    book.value = null;
+    fileName.value = null;
+  };
+
   const initTheme = (themes) => {
     (themes || THEMES).forEach((theme) => {
       rendition.value.themes.register(theme.key, {
@@ -253,6 +260,7 @@ export const useEpubStore = defineStore("epub", () => {
     isSimplified,
     navigation,
     init,
+    clear,
     convertText,
     toggleText,
     toggleTheme,

+ 0 - 19
packages/base/src/utils/index.js

@@ -1,20 +1 @@
-import { getBaseURL } from "@dage/service";
-
-const baseUrl = getBaseURL();
-
-export const isDevelopment = import.meta.env.MODE === "development";
-
-export const getBaseUrl = () => {
-  return `${baseUrl}${isDevelopment ? "/api" : ""}`;
-};
-
-export const getFixUrl = (path) => {
-  const isHttp = path.startsWith("http");
-  if (isHttp) {
-    return path;
-  }
-  const base = getBaseUrl();
-  return base + path;
-};
-
 export * from "./usePagination";

+ 2 - 1
packages/mobile/src/main.js

@@ -1,9 +1,10 @@
+import "./configure";
+
 import "@lsq/base/src/theme.scss";
 import "swiper/css";
 import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
 import "./assets/main.css";
 import "virtual:svg-icons-register";
-import "./configure";
 
 import { createApp } from "vue";
 import { createPinia } from "pinia";

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

@@ -13,8 +13,9 @@
 
 <script setup>
 import { onMounted, ref } from "vue";
-import { getRecommendListApi, getBaseUrl } from "@lsq/base";
+import { getRecommendListApi } from "@lsq/base";
 import { useRouter } from "vue-router";
+import { getBaseUrl } from "@/utils";
 import SearchPane from "./components/SearchPane.vue";
 
 const router = useRouter();

+ 10 - 0
packages/base/src/api/index.js

@@ -144,3 +144,13 @@ export const getMessageListApi = (params) => {
 export const saveMessageApi = (params) => {
   return requestByPost("/api/wx/message/save", params);
 };
+
+// 留言-删除
+export const deleteMessageApi = (id) => {
+  return requestByGet(`/api/wx/message/remove/${id}`);
+};
+
+// 影印&视频-列表-分页
+export const getMediaListApi = (params) => {
+  return requestByPost("/api/show/video/pageList", params);
+};

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

@@ -27,7 +27,7 @@
 
 <script setup>
 import { computed } from "vue";
-import { getBaseUrl } from "@lsq/base";
+import { getBaseUrl } from "@/utils";
 
 const props = defineProps({
   // 模式 row-横版 column-竖版

+ 10 - 2
packages/pc/src/components/PagePane.vue

@@ -1,12 +1,12 @@
 <template>
   <div class="page-pane">
-    <div
+    <el-scrollbar
       v-if="!simple"
       class="page-pane-container"
       :style="containerColor ? { background: containerColor } : ''"
     >
       <slot />
-    </div>
+    </el-scrollbar>
 
     <slot v-else />
   </div>
@@ -45,6 +45,14 @@ defineProps({
     background: var(--pane-bg-color);
     box-shadow: 0px 0px 21px 0px rgba(0, 0, 0, 0.1);
     box-sizing: border-box;
+
+    :deep(.el-scrollbar__wrap) {
+      width: 100%;
+    }
+    :deep(.el-scrollbar__view) {
+      width: 100%;
+      min-height: 100%;
+    }
   }
 }
 </style>

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

@@ -12,7 +12,7 @@
 <script setup>
 import { computed, ref, watch, nextTick } from "vue";
 import { useRoute, useRouter } from "vue-router";
-import { wxLoginApi } from "@lsq/base";
+import { wxLoginApi } from "@/api";
 import { useBaseStore } from "@/stores";
 
 const props = defineProps({

+ 60 - 0
packages/pc/src/components/TopNav/components/MobileDialog.vue

@@ -0,0 +1,60 @@
+<template>
+  <el-dialog v-model="show" class="mobile-dialog" width="425px">
+    <div class="mobile-dialog__scan" />
+    <span>手机扫描二维码</span>
+  </el-dialog>
+</template>
+
+<script setup>
+import { computed, ref, watch, nextTick } from "vue";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+  },
+});
+const emits = defineEmits(["update:visible"]);
+const loading = ref(false);
+
+const show = computed({
+  get() {
+    return props.visible;
+  },
+  set(v) {
+    emits("update:visible", v);
+  },
+});
+
+watch(show, (v) => {
+  v &&
+    nextTick(() => {
+      login();
+    });
+});
+</script>
+
+<style lang="scss">
+.mobile-dialog {
+  height: 517px;
+  background: rgba(255, 255, 255, 0.9);
+
+  .el-dialog__body {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 32px;
+    height: 100%;
+
+    span {
+      font-size: 24px;
+      font-family: "Source Han Serif CN-Bold";
+    }
+  }
+  &__scan {
+    width: 300px;
+    height: 300px;
+  }
+}
+</style>

+ 6 - 2
packages/pc/src/components/TopNav/index.vue

@@ -37,7 +37,7 @@
 
         <div class="top-nav-rg">
           <ul class="top-nav-rg-first">
-            <li>手机版</li>
+            <li @click="mobileVisible = true">手机版</li>
             <li v-if="!isDetail">
               <router-link :to="{ name: 'stack' }">书库</router-link>
             </li>
@@ -124,6 +124,7 @@
   </div>
 
   <login-dialog v-model:visible="loginVisible" />
+  <mobile-dialog v-model:visible="mobileVisible" />
 </template>
 
 <script setup>
@@ -131,9 +132,11 @@ import { watch, ref } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { useDark } from "@vueuse/core";
 import { storeToRefs } from "pinia";
-import { getFixUrl, updateBookCollectApi, fakeLoginApi } from "@lsq/base";
+import { getFixUrl } from "@/utils";
+import { updateBookCollectApi, fakeLoginApi } from "@/api";
 import { useBaseStore, useEpubStore, useDetailStore } from "@/stores";
 import LoginDialog from "./components/LoginDialog.vue";
+import MobileDialog from "./components/MobileDialog.vue";
 
 import LogoIcon from "@/assets/images/logo_03-min.png";
 import LogoWhiteIcon from "@/assets/images/logo-min.png";
@@ -185,6 +188,7 @@ const showLogo = ref(false);
 const hideBgColor = ref(false);
 const bgColor = ref("");
 const joinLoading = ref(false);
+const mobileVisible = ref(false);
 
 // 加入书架
 const handleLike = async () => {

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

@@ -1,9 +1,10 @@
+import "./configure";
+
 import "./assets/main.css";
 import "@lsq/base/src/theme.scss";
 import "element-plus/theme-chalk/dark/css-vars.css";
 import "element-plus/theme-chalk/el-notification.css";
 import "virtual:svg-icons-register";
-import "./configure";
 
 import { createApp } from "vue";
 import { createPinia } from "pinia";

+ 1 - 1
packages/pc/src/stores/base.js

@@ -1,6 +1,6 @@
 import { ref } from "vue";
 import { defineStore } from "pinia";
-import { getUserInfoApi, logoutApi } from "@lsq/base";
+import { getUserInfoApi, logoutApi } from "@/api";
 import { getUserInfo, USERINFO_KEY } from "@/utils";
 
 const userInfoStorage = getUserInfo();

+ 10 - 1
packages/pc/src/stores/detail.js

@@ -1,6 +1,6 @@
 import { ref } from "vue";
 import { defineStore } from "pinia";
-import { getLabelListApi } from "@lsq/base";
+import { getLabelListApi } from "@/api";
 
 export const useDetailStore = defineStore("detail", () => {
   const detail = ref(null);
@@ -32,12 +32,21 @@ export const useDetailStore = defineStore("detail", () => {
     );
   };
 
+  const clear = () => {
+    detail.value = null;
+    searchKey.value = "";
+    noteList.value = [];
+    searchVisible.value = false;
+    introductionVisible.value = false;
+  };
+
   return {
     detail,
     searchVisible,
     searchKey,
     noteList,
     introductionVisible,
+    clear,
     openSearchDrawer,
     getLabelList,
   };

+ 18 - 0
packages/pc/src/utils/index.js

@@ -1,3 +1,5 @@
+import { getBaseURL } from "@dage/service";
+
 export const USERINFO_KEY = "userinfo";
 
 export const getUserInfo = () => {
@@ -8,3 +10,19 @@ export const getUserInfo = () => {
 
   return userInfoStorage;
 };
+
+export const isDevelopment = import.meta.env.MODE === "development";
+
+export const getBaseUrl = () => {
+  const baseUrl = getBaseURL();
+  return `${baseUrl}${isDevelopment ? "/api" : ""}`;
+};
+
+export const getFixUrl = (path) => {
+  const isHttp = path.startsWith("http");
+  if (isHttp) {
+    return path;
+  }
+  const base = getBaseUrl();
+  return base + path;
+};

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

@@ -177,9 +177,9 @@ import {
   getStorageTreeApi,
   saveBookApi,
   getExhibitTypeListApi,
-  getBaseUrl,
-} from "@lsq/base";
+} from "@/api";
 import { getBaseURL } from "@dage/service";
+import { getBaseUrl } from "@/utils";
 
 const DEFAULT_FORM = {
   name: "",

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

@@ -48,8 +48,9 @@
 
 <script setup>
 import { ref, onMounted } from "vue";
-import { getBaseUrl, getUploadBookListApi, deleteBookApi } from "@lsq/base";
+import { getUploadBookListApi, deleteBookApi } from "@/api";
 import { formatDate } from "@dage/utils";
+import { getBaseUrl } from "@/utils";
 
 const STATUS = [
   {

+ 2 - 6
packages/pc/src/views/Bookshelf/index.vue

@@ -96,12 +96,8 @@
 import { ref, onMounted } from "vue";
 import { useBaseStore } from "@/stores";
 import { storeToRefs } from "pinia";
-import {
-  updateUserInfoApi,
-  getMyBookListApi,
-  uploadApi,
-  getFixUrl,
-} from "@lsq/base";
+import { updateUserInfoApi, getMyBookListApi, uploadApi } from "@/api";
+import { getFixUrl } from "@/utils";
 import { useRouter } from "vue-router";
 import BookCard from "@/components/BookCard/index.vue";
 import UploadDialog from "./components/UploadDialog.vue";

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

@@ -31,7 +31,7 @@
 <script setup>
 import { computed, ref, watch } from "vue";
 import { Plus } from "@element-plus/icons-vue";
-import { saveLabelApi, getLabelListApi, deleteLabelApi } from "@lsq/base";
+import { saveLabelApi, getLabelListApi, deleteLabelApi } from "@/api";
 import { useEpubStore, useDetailStore } from "@/stores";
 import Drawer from "./Drawer.vue";
 

+ 27 - 2
packages/pc/src/views/Detail/components/Comment.vue

@@ -20,8 +20,9 @@
         >
       </template>
 
+      <el-empty v-if="noData" description="暂无数据" />
       <div class="comment-list" v-infinite-scroll="load">
-        <div v-for="item in list" :key="item.id" class="comment-item">
+        <div v-for="(item, idx) in list" :key="item.id" class="comment-item">
           <div class="comment-item-header">
             <p>{{ item.createBy }}</p>
             <p class="comment-item-header__date">{{ item.createTime }}</p>
@@ -32,6 +33,7 @@
               width="16px"
               height="16px"
               color="var(--el-color-primary)"
+              @click="handleDelete(item, idx)"
             />
           </div>
           <p class="comment-item__inner">
@@ -45,7 +47,8 @@
 
 <script setup>
 import { computed, ref } from "vue";
-import { usePagination, getMessageListApi, saveMessageApi } from "@lsq/base";
+import { usePagination } from "@lsq/base";
+import { getMessageListApi, saveMessageApi, deleteMessageApi } from "@/api";
 import Drawer from "./Drawer.vue";
 import { useBaseStore, useDetailStore } from "@/stores";
 import { storeToRefs } from "pinia";
@@ -97,6 +100,28 @@ const handleSubmit = async () => {
     btnLoading.value = false;
   }
 };
+
+const handleDelete = async (item, idx) => {
+  ElMessageBox.confirm("确认是否删除?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+    beforeClose: async (action, instance, done) => {
+      if (action === "confirm") {
+        try {
+          instance.confirmButtonLoading = true;
+          await deleteMessageApi(item.id);
+          list.value.splice(idx, 1);
+          done();
+        } finally {
+          instance.confirmButtonLoading = false;
+        }
+      } else {
+        done();
+      }
+    },
+  });
+};
 </script>
 
 <style lang="scss" scoped>

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

@@ -5,7 +5,7 @@
         <el-image :src="baseUrl + detail?.thumb" fit="cover" />
 
         <div class="directory-info-inner">
-          <h1>{{ detail?.name }}</h1>
+          <h1 class="limit-line">{{ detail?.name }}</h1>
           <p>{{ detail?.author }}</p>
           <p>{{ detail?.press }} 编</p>
         </div>
@@ -27,7 +27,7 @@
 <script setup>
 import { computed } from "vue";
 import { storeToRefs } from "pinia";
-import { getBaseUrl } from "@lsq/base";
+import { getBaseUrl } from "@/utils";
 import { useEpubStore, useDetailStore } from "@/stores";
 import Drawer from "./Drawer.vue";
 
@@ -76,6 +76,10 @@ const goToChapter = (href) => {
     p {
       color: var(--text-color-secondary);
     }
+    &-inner {
+      flex: 1;
+      width: 0;
+    }
   }
 
   &-list {

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

@@ -32,7 +32,7 @@
 </template>
 
 <script setup>
-import { getBaseUrl } from "@lsq/base";
+import { getBaseUrl } from "@/utils";
 import { computed } from "vue";
 
 const props = defineProps({

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

@@ -3,6 +3,8 @@
     <div class="note">
       <p style="margin-bottom: 3px">总数:{{ noteList.length }}</p>
 
+      <el-empty v-if="!noteList.length" description="暂无数据" />
+
       <div
         v-for="item in noteList"
         :key="item.id"
@@ -55,7 +57,7 @@
 import { computed, ref } from "vue";
 import { storeToRefs } from "pinia";
 import { formatDate } from "@dage/utils";
-import { saveLabelApi } from "@lsq/base";
+import { saveLabelApi } from "@/api";
 import { useDetailStore } from "@/stores";
 import Drawer from "./Drawer.vue";
 

+ 3 - 0
packages/pc/src/views/Detail/components/Photocopy/index.scss

@@ -70,4 +70,7 @@
       background: rgba(169, 146, 113, 0.3);
     }
   }
+  &-right {
+    padding: 20px;
+  }
 }

+ 63 - 9
packages/pc/src/views/Detail/components/Photocopy/index.vue

@@ -1,20 +1,74 @@
 <template>
-  <div class="photocopy">
-    <div class="photocopy-left">
-      <el-image src="" fit="contain" />
+  <div v-loading="loading" class="photocopy">
+    <template v-if="!noData">
+      <div class="photocopy-left">
+        <el-image :src="baseUrl + list[curIndex]?.thumb" fit="contain" />
 
-      <div class="photocopy-left__prev" />
-      <div class="photocopy-left__next" />
+        <div class="photocopy-left__prev" @click="handlePrev" />
+        <div class="photocopy-left__next" @click="handleNext" />
 
-      <div class="photocopy-left__magnify" />
+        <div class="photocopy-left__magnify" @click="viewerVisible = true" />
 
-      <div class="photocopy-left__pagination">12/24</div>
-    </div>
+        <div class="photocopy-left__pagination">
+          {{ curIndex + 1 }}/{{ total }}
+        </div>
+      </div>
 
-    <div class="photocopy-right"></div>
+      <div class="photocopy-right" v-html="list[curIndex]?.description" />
+    </template>
+
+    <el-empty v-else description="暂无数据" style="width: 100%" />
   </div>
+
+  <el-image-viewer
+    v-if="viewerVisible"
+    :url-list="list.map((i) => `${baseUrl}${i.thumb}`)"
+    :initial-index="curIndex"
+    @close="viewerVisible = false"
+  />
 </template>
 
+<script setup>
+import { watch, ref } from "vue";
+import { usePagination } from "@lsq/base";
+import { getMediaListApi } from "@/api";
+import { getBaseUrl } from "@/utils";
+
+const props = defineProps(["detail", "isLogin"]);
+const curIndex = ref(0);
+const viewerVisible = ref(false);
+const baseUrl = getBaseUrl();
+
+const { total, loading, list, noData, getList } = usePagination(
+  async (params) => {
+    return getMediaListApi({
+      ...params,
+      pageSize: 999,
+      bookId: props.detail.id,
+      type: "img",
+    });
+  }
+);
+
+const handlePrev = () => {
+  curIndex.value = Math.max(curIndex.value - 1, 0);
+};
+
+const handleNext = () => {
+  curIndex.value = Math.min(curIndex.value + 1, list.value.length - 1);
+};
+
+watch(
+  () => props.detail,
+  () => {
+    props.detail && getList();
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+
 <style lang="scss" scoped>
 @import "./index.scss";
 </style>

+ 2 - 1
packages/pc/src/views/Detail/components/Reader/index.scss

@@ -2,7 +2,8 @@
   display: flex;
   flex-direction: column;
   width: 100%;
-  height: 100%;
+  height: calc(100vh - var(--topnav-height) - 54px - 52px);
+  overflow: hidden;
 
   #reader {
     flex: 1;

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

@@ -77,19 +77,15 @@
 </template>
 
 <script setup>
-import { onMounted, ref, watch } from "vue";
+import { onMounted, onBeforeUnmount, ref, watch } from "vue";
 import { ElNotification } from "element-plus";
 import useClipboard from "vue-clipboard3";
 import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
 import { useEpubStore, useDetailStore, useBaseStore } from "@/stores";
 import { COLOR_MAP } from "./constants";
-import {
-  getBaseUrl,
-  updateBookCollectApi,
-  saveLabelApi,
-  deleteLabelApi,
-} from "@lsq/base";
+import { updateBookCollectApi, saveLabelApi, deleteLabelApi } from "@/api";
 import { storeToRefs } from "pinia";
+import { getBaseUrl } from "@/utils";
 
 const props = defineProps(["detail", "isLogin"]);
 
@@ -130,6 +126,10 @@ onMounted(() => {
   });
 });
 
+onBeforeUnmount(() => {
+  epubStore.clear();
+});
+
 const getLabelList = () => {
   detailStore.getLabelList().then(() => {
     detailStore.noteList.forEach((item) => {

+ 83 - 5
packages/pc/src/views/Detail/components/Video/index.vue

@@ -1,25 +1,103 @@
 <template>
-  <div class="detail-video">
-    <div class="detail-video-item">
-      <el-image src="" fit="cover" />
+  <div v-loading="loading" class="detail-video">
+    <el-empty v-if="noData" description="暂无数据" />
+
+    <div
+      v-for="item in list"
+      :key="item.id"
+      class="detail-video-item"
+      @click="handleClick(item)"
+    >
+      <el-image :src="baseUrl + item.thumb" fit="cover" />
 
       <div class="detail-video-item-inner">
-        <p>学习求索</p>
+        <p>{{ item.name }}</p>
         <p>
-          刘少奇(1898年11月24日-1969年11月12日),生于湖南省宁乡县,伟大的马克思主义者,伟大的无产阶级革命家、政治家、理论家,党和国家主要领导人之一,中华人民共和国开国元勋,是以毛泽东同志为核心的党的第一代中央领导集体的重要成员
+          {{ item.description }}
         </p>
       </div>
     </div>
+
+    <div style="display: flex; justify-content: center">
+      <el-pagination
+        hide-on-single-page
+        layout="prev, pager, next"
+        :total="total"
+        prev-text="上一页"
+        next-text="下一页"
+        @change="handlePage"
+      />
+    </div>
   </div>
+
+  <el-dialog
+    v-model="dialogVisible"
+    destroy-on-close
+    width="80vw"
+    :title="checkedItem?.name"
+  >
+    <video
+      v-if="checkedItem"
+      :src="baseUrl + checkedItem.filePath"
+      style="width: 100%; height: 400px"
+      controls
+    />
+  </el-dialog>
 </template>
 
+<script setup>
+import { watch, ref } from "vue";
+import { usePagination } from "@lsq/base";
+import { getMediaListApi } from "@/api";
+import { getBaseUrl } from "@/utils";
+
+const props = defineProps(["detail", "isLogin"]);
+const baseUrl = getBaseUrl();
+const checkedItem = ref(null);
+const dialogVisible = ref(false);
+
+const { pageNum, total, loading, list, noData, getList } = usePagination(
+  async (params) => {
+    return getMediaListApi({
+      ...params,
+      bookId: props.detail.id,
+      type: "video",
+    });
+  }
+);
+
+const handlePage = (page) => {
+  pageNum.value = page;
+  getList();
+};
+
+const handleClick = (item) => {
+  checkedItem.value = item;
+  dialogVisible.value = true;
+};
+
+watch(
+  () => props.detail,
+  () => {
+    props.detail && getList();
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+
 <style lang="scss" scoped>
 .detail-video {
+  overflow: hidden;
+  min-height: calc(100% - 52px);
+
   &-item {
     margin: 20px 0;
     display: flex;
     align-items: center;
     gap: 80px;
+    cursor: pointer;
 
     .el-image {
       flex-shrink: 0;

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

@@ -12,9 +12,10 @@
 </template>
 
 <script setup>
-import { computed, ref, watch } from "vue";
+import { computed, ref, watch, onUnmounted } from "vue";
 import { useRoute } from "vue-router";
-import { THEMES, getBookDetailApi, getBookDetail2Api } from "@lsq/base";
+import { THEMES } from "@lsq/base";
+import { getBookDetailApi, getBookDetail2Api } from "@/api";
 import { storeToRefs } from "pinia";
 import { useEpubStore, useDetailStore, useBaseStore } from "@/stores";
 import Toolbar from "./components/Toolbar/index.vue";
@@ -68,6 +69,10 @@ watch(
     immediate: true,
   }
 );
+
+onUnmounted(() => {
+  detailStore.clear();
+});
 </script>
 
 <style lang="scss" scoped>

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

@@ -42,8 +42,9 @@
 
 <script setup>
 import { onMounted, ref } from "vue";
-import { getRecommendListApi, getBookCountApi, getBaseUrl } from "@lsq/base";
+import { getRecommendListApi, getBookCountApi } from "@/api";
 import { useRouter } from "vue-router";
+import { getBaseUrl } from "@/utils";
 
 const router = useRouter();
 const txt = ref({ show: false });

+ 2 - 1
packages/pc/src/views/Home2/components/News.vue

@@ -46,7 +46,8 @@
 <script setup>
 import { ref, onMounted } from "vue";
 import { formatDate } from "@dage/utils";
-import { getNoticeListApi, usePagination } from "@lsq/base";
+import { usePagination } from "@lsq/base";
+import { getNoticeListApi } from "@/api";
 import NewsDialog from "./NewsDialog.vue";
 
 const dialogVisible = ref(false);

+ 9 - 2
packages/pc/src/views/Home2/components/NewsDialog.vue

@@ -25,7 +25,7 @@
     </template>
 
     <el-scrollbar max-height="600px">
-      <div class="news-dialog-inner" v-html="item?.description" />
+      <div v-for="c in content" class="news-dialog-inner" v-html="c.txt" />
     </el-scrollbar>
   </el-dialog>
 </template>
@@ -33,7 +33,7 @@
 <script setup>
 import { computed, watch } from "vue";
 import { formatDate } from "@dage/utils";
-import { addNoticeVisitApi } from "@lsq/base";
+import { addNoticeVisitApi } from "@/api";
 
 const props = defineProps({
   visible: {
@@ -60,6 +60,13 @@ const date = computed(() =>
   formatDate(props.item?.createTime, "YYYY年MM月DD日 HH:MM")
 );
 
+const content = computed(() => {
+  if (!props.item || !props.item.description) return [];
+
+  const res = JSON.parse(props.item.description);
+  return res.txtArr;
+});
+
 watch(show, (v) => {
   if (v && props.item) {
     addNoticeVisitApi(props.item.id);

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

@@ -73,7 +73,7 @@ import {
   getReadListApi,
   getMyBookListApi,
   getUserCollectedBookCountApi,
-} from "@lsq/base";
+} from "@/api";
 import BookCard from "@/components/BookCard/index.vue";
 import RankPanel from "@/components/RankPanel/index.vue";
 import News from "./components/News.vue";

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

@@ -37,7 +37,7 @@
 <script setup>
 import { ref, onMounted } from "vue";
 import { useRouteQuery } from "@vueuse/router";
-import { getStorageTreeApi, getExhibitTypeListApi } from "@lsq/base";
+import { getStorageTreeApi, getExhibitTypeListApi } from "@/api";
 import TreeMenu from "@/components/TreeMenu.vue";
 import SearchInput from "../SearchInput/index.vue";
 

+ 3 - 1
packages/pc/src/views/Stack/index.scss

@@ -1,4 +1,7 @@
 .stack {
+  display: flex;
+  gap: calc(70px - 12.5px);
+
   &-left {
     flex: 0 0 360px;
     padding-right: 20px;
@@ -7,7 +10,6 @@
   &-main {
     flex: 1;
     padding-top: 15px;
-    margin: 0 -12.5px;
     display: flex;
     flex-direction: column;
     align-items: center;

+ 36 - 33
packages/pc/src/views/Stack/index.vue

@@ -1,40 +1,42 @@
 <template>
-  <page-pane class="stack">
-    <div class="stack-left">
-      <sidebar @select="handleSelect" />
-    </div>
+  <page-pane>
+    <div class="stack">
+      <div class="stack-left">
+        <sidebar @select="handleSelect" />
+      </div>
 
-    <div class="stack-main">
-      <el-breadcrumb
-        class="stack-breadcrumb"
-        :separator-icon="ArrowRight"
-        style="width: 100%"
-      >
-        <el-breadcrumb-item v-for="item in breadcrumb" :key="item.id">{{
-          item.name
-        }}</el-breadcrumb-item>
-      </el-breadcrumb>
+      <div class="stack-main">
+        <el-breadcrumb
+          class="stack-breadcrumb"
+          :separator-icon="ArrowRight"
+          style="width: 100%"
+        >
+          <el-breadcrumb-item v-for="item in breadcrumb" :key="item.id">{{
+            item.name
+          }}</el-breadcrumb-item>
+        </el-breadcrumb>
 
-      <el-scrollbar
-        v-loading="loading"
-        style="flex: 1; width: 100%; height: 0; margin: 18px 0"
-      >
-        <ul class="stack-list">
-          <li v-for="item in list" :key="item.id">
-            <book-card type="row" size="large" :item="item" />
-          </li>
-        </ul>
+        <el-scrollbar
+          v-loading="loading"
+          style="flex: 1; width: 100%; height: 0; margin: 18px 0"
+        >
+          <ul class="stack-list">
+            <li v-for="item in list" :key="item.id">
+              <book-card type="row" size="large" :item="item" />
+            </li>
+          </ul>
 
-        <el-empty v-if="noData" description="暂无数据" />
-      </el-scrollbar>
+          <el-empty v-if="noData" description="暂无数据" />
+        </el-scrollbar>
 
-      <el-pagination
-        hide-on-single-page
-        background
-        layout="prev, pager, next"
-        :total="total"
-        @change="handlePage"
-      />
+        <el-pagination
+          hide-on-single-page
+          background
+          layout="prev, pager, next"
+          :total="total"
+          @change="handlePage"
+        />
+      </div>
     </div>
   </page-pane>
 </template>
@@ -43,7 +45,8 @@
 import { ref, watch } from "vue";
 import { useRouteQuery } from "@vueuse/router";
 import { ArrowRight } from "@element-plus/icons-vue";
-import { getBookListApi, usePagination } from "@lsq/base";
+import { usePagination } from "@lsq/base";
+import { getBookListApi } from "@/api";
 import Sidebar from "./components/Sidebar/index.vue";
 import BookCard from "@/components/BookCard/index.vue";