Просмотр исходного кода

经典医书支持二级目录

cc12458 1 год назад
Родитель
Сommit
0f2b0c1ec2
1 измененных файлов с 95 добавлено и 41 удалено
  1. 95 41
      src/views/knowledge/BookD.vue

+ 95 - 41
src/views/knowledge/BookD.vue

@@ -1,6 +1,6 @@
 <script>
 import {getBook, getBookContent, getBookListOfRecommend, setBookStatus} from '@/api/knowledge.js';
-import {numberToChinese} from '@/utils/format';
+
 
 export default {
   name: 'KnowledgeBookDetail',
@@ -11,6 +11,8 @@ export default {
       recommend: [],
 
       containerHeight: 0,
+
+      selected: {},
     };
   },
   computed: {
@@ -28,34 +30,45 @@ export default {
     }, 200);
   },
   methods: {
-    async getBook() {
-      this.book = await getBook(this.book.id);
-      let index = 1;
-      const section = [];
-      for (const {catalogueName: name, catalogueId: id} of this.book.catalogueList) {
-        section.push({
-          id,
-          name: name === '序' ? name : `第${numberToChinese(index)}章 ${name}`,
-          content: '',
-          dirty: false,
-        });
-        if (name !== '序') { index += 1;}
-      }
-      this.section = section;
-    },
     async getRecommend() {
       try {
         this.recommend = await getBookListOfRecommend(this.book.id);
       } catch (e) {}
     },
-    async getContent(data, index, section) {
-      try {
-        let content = await getBookContent({bookId: this.book.id, catalogueId: data.id});
-        content = `&emsp;&emsp;${(content || '').replace(/\n|\r/g, '<br>&emsp;&emsp;')}`;
-        this.$set(this.section, index, Object.assign(data, {dirty: true, error: '', content}));
-      } catch (e) {
-        this.$set(this.section, index, Object.assign(data, {dirty: true, error: `出错了(${e})`}));
+    async getBook() {
+      this.book = await getBook(this.book.id);
+      let selected;
+      const get = (list, level = 1) => list.map(item => {
+        const children = Array.isArray(item.children) ? get(item.children, level + 1) : [];
+        const isLeaf = !children.length;
+        const section = {
+          id: item.catalogueId,
+          name: item.catalogueName,
+          children, isLeaf, level,
+          content: isLeaf ? '' : void 0,
+          dirty: !isLeaf,
+        }
+        if (section.isLeaf && !selected) selected = section;
+        return section;
+      });
+      this.section = get(this.book.catalogueList);
+      await this.load(selected);
+    },
+    async load(section) {
+      if (!section.isLeaf) return;
+
+      this.selected = {...section};
+      if (!section.dirty) {
+        try {
+          section.content = await getBookContent({bookId: this.book.id, catalogueId: section.id});
+          section.dirty = true;
+          this.$set(this.selected, 'content', section.content);
+          this.$set(this.selected, 'dirty', section.dirty);
+        } catch (e) {}
       }
+      this.$nextTick(() => {
+        document.querySelector(`#scrolling`).scrollTo({top: 0, behavior: 'smooth'});
+      });
     },
     async collected() {
       try {
@@ -67,16 +80,6 @@ export default {
 
       }
     },
-    load(ids) {
-      if (ids && !Array.isArray(ids)) {ids = [ids];}
-      for (const id of ids) {
-        const index = this.section.findIndex(item => item.id === id);
-        const data = this.section[index];
-        if (data.dirty || data.content) continue;
-        this.$set(this.section, index, Object.assign(data, {dirty: true, error: ''}));
-        this.getContent(data, index, this.section);
-      }
-    },
     async onPreview(book) {
       if (book) {
         await this.$router.push({path: `/index/knowledge/book/detail?id=${book.id}`});
@@ -118,14 +121,22 @@ export default {
       </div>
       <el-divider content-position="left">阅读<span v-if="book.readProgress">:{{ book.readProgress }}%</span>
       </el-divider>
-      <el-collapse style="margin: 0 12px;" @change="load">
-        <el-collapse-item v-for="item in section" :key="item.id" :title="item.name" :name="item.id">
-          <div class="book-content-container" v-loading="!item.content">
-            <div v-html="item.content"></div>
+      <div class="book-content-container" :style="{maxHeight: containerHeight}" :class="{scrollable: true}">
+        <div class="catalogue">
+          <div v-for="item in section" :key="item.id"
+               :class="['level-' + item.level, item.isLeaf ? 'section' : 'title']">
+            <span :class="{active: item.id === selected.id}" @click="load(item)">{{ item.name }}</span>
+            <div v-for="item in item.children" :key="item.id"
+                 :class="['level-' + item.level, item.isLeaf ? 'section' : 'title']">
+              <span :class="{active: item.id === selected.id}" @click="load(item)">{{ item.name }}</span>
+            </div>
           </div>
-        </el-collapse-item>
-      </el-collapse>
-      <el-backtop style="right: 300px;" target=".detail-wrapper"></el-backtop>
+        </div>
+        <div class="content" id="scrolling" :style="{height: containerHeight}" v-loading="!selected.dirty">
+          <div class="text" v-html="selected.content"></div>
+          <el-backtop style="right: 300px;" target="#scrolling"></el-backtop>
+        </div>
+      </div>
     </div>
     <el-card header="相关医书">
       <div class="book-wrapper" style="margin-bottom: 24px;" v-for="book in recommend" :key="book.id"
@@ -151,6 +162,7 @@ export default {
   .detail-wrapper {
     flex: auto;
     overflow-y: auto;
+    height: 100%;
     padding: 0 24px;
 
     .header {
@@ -214,7 +226,49 @@ export default {
 }
 
 .book-content-container {
-  min-height: 80px;
+  display: flex;
+
+  &.scrollable > div {
+    overflow-y: auto;
+  }
+
+  .catalogue {
+    flex: 1 0;
+    user-select: none;
+
+
+    .title {
+      margin: 24px 0;
+      color: #213547;
+      > span {
+        pointer-events: none;
+      }
+    }
+
+    .section {
+      margin: 6px 0;
+      color: #3c3c3cb3;
+
+      &:hover, .active {
+        cursor: pointer;
+        color: #5386f6;
+      }
+    }
+  }
+
+  .content {
+    flex: 4 4;
+
+    > .text {
+      padding: 24px;
+      line-height: 2.5;
+      white-space: pre-wrap;
+    }
+  }
+
+  ::v-deep.el-loading-mask {
+    background-color: transparent;
+  }
 }
 
 .book-wrapper {