BookD.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <script>
  2. import {getBook, getBookContent, getBookListOfRecommend, setBookStatus} from '@/api/knowledge.js';
  3. import {numberToChinese} from '@/utils/format';
  4. export default {
  5. name: 'KnowledgeBookDetail',
  6. data() {
  7. return {
  8. book: {id: '', catalogueList: []},
  9. section: [],
  10. recommend: [],
  11. containerHeight: 0,
  12. };
  13. },
  14. computed: {
  15. collection() {
  16. return +this.book['isBookshelf'] === 1;
  17. },
  18. },
  19. created() {
  20. this.book.id = this.$route.query.id;
  21. this.onPreview();
  22. },
  23. mounted() {
  24. setTimeout(() => {
  25. try { this.containerHeight = `${this.$el.parentElement.getBoundingClientRect().height - 10}px`; } catch (e) { }
  26. }, 200);
  27. },
  28. methods: {
  29. async getBook() {
  30. this.book = await getBook(this.book.id);
  31. let index = 1;
  32. const section = [];
  33. for (const {catalogueName: name, catalogueId: id} of this.book.catalogueList) {
  34. section.push({
  35. id,
  36. name: name === '序' ? name : `第${numberToChinese(index)}章 ${name}`,
  37. content: '',
  38. dirty: false,
  39. });
  40. if (name !== '序') { index += 1;}
  41. }
  42. this.section = section;
  43. },
  44. async getRecommend() {
  45. try {
  46. this.recommend = await getBookListOfRecommend(this.book.id);
  47. } catch (e) {}
  48. },
  49. async getContent(data, index, section) {
  50. try {
  51. let content = await getBookContent({bookId: this.book.id, catalogueId: data.id});
  52. content = `&emsp;&emsp;${(content || '').replace(/\n|\r/g, '<br>&emsp;&emsp;')}`;
  53. this.$set(this.section, index, Object.assign(data, {dirty: true, error: '', content}));
  54. } catch (e) {
  55. this.$set(this.section, index, Object.assign(data, {dirty: true, error: `出错了(${e})`}));
  56. }
  57. },
  58. async collected() {
  59. try {
  60. const isBookshelf = this.collection ? '0' : '1';
  61. await setBookStatus(this.book.id, {isBookshelf});
  62. this.$set(this.book, 'isBookshelf', isBookshelf);
  63. this.$message.success('操作成功');
  64. } catch (e) {
  65. }
  66. },
  67. load(ids) {
  68. if (ids && !Array.isArray(ids)) {ids = [ids];}
  69. for (const id of ids) {
  70. const index = this.section.findIndex(item => item.id === id);
  71. const data = this.section[index];
  72. if (data.dirty || data.content) continue;
  73. this.$set(this.section, index, Object.assign(data, {dirty: true, error: ''}));
  74. this.getContent(data, index, this.section);
  75. }
  76. },
  77. async onPreview(book) {
  78. if (book) {
  79. await this.$router.push({path: `/index/knowledge/book/detail?id=${book.id}`});
  80. this.book = book;
  81. }
  82. try {
  83. await this.getBook();
  84. await this.getRecommend();
  85. } catch (e) {
  86. }
  87. },
  88. },
  89. };
  90. </script>
  91. <template>
  92. <div class="knowledge-book-detail" :style="{height: containerHeight}">
  93. <div class="detail-wrapper">
  94. <div class="header">
  95. <div class="book-wrapper">
  96. <div class="cover">
  97. <div class="name">{{ book.bookName }}</div>
  98. </div>
  99. </div>
  100. <div class="description-wrapper">
  101. <div>
  102. <div style="margin-bottom: 6px;font-size: 18px;font-weight: 700;">{{ book.bookName }}</div>
  103. <div class="content">
  104. <span v-if="book.dynasty">{{ book.dynasty }} · </span>
  105. <span>{{ book.author || '佚名' }}</span>
  106. </div>
  107. </div>
  108. <div style="text-indent: 2em;">{{ book.briefIntroduction }}</div>
  109. <el-button type="primary" plain round size="small" :icon="collection ? 'el-icon-remove-outline' : 'el-icon-circle-plus-outline'" @click="collected()">
  110. {{ collection ? '移出' : '加入' }}书架
  111. </el-button>
  112. </div>
  113. </div>
  114. <el-divider content-position="left">阅读<span v-if="book.readProgress">:{{ book.readProgress }}%</span>
  115. </el-divider>
  116. <el-collapse style="margin: 0 12px;" @change="load">
  117. <el-collapse-item v-for="item in section" :key="item.id" :title="item.name" :name="item.id">
  118. <div class="book-content-container" v-loading="!item.content">
  119. <div v-html="item.content"></div>
  120. </div>
  121. </el-collapse-item>
  122. </el-collapse>
  123. <el-backtop style="right: 300px;" target=".detail-wrapper"></el-backtop>
  124. </div>
  125. <el-card header="相关医书">
  126. <div class="book-wrapper" style="margin-bottom: 24px;" v-for="book in recommend" :key="book.id"
  127. @click="onPreview(book)">
  128. <div class="cover">
  129. <div class="name">{{ book.bookName }}</div>
  130. </div>
  131. <div class="content" style="font-weight: 700;">{{ book.bookName }}</div>
  132. <div class="content">
  133. <span v-if="book.dynasty">{{ book.dynasty }}</span>
  134. <span>{{ book.author || '佚名' }}</span>
  135. </div>
  136. </div>
  137. </el-card>
  138. </div>
  139. </template>
  140. <style lang="scss" scoped>
  141. .knowledge-book-detail {
  142. display: flex;
  143. overflow: hidden;
  144. .detail-wrapper {
  145. flex: auto;
  146. overflow-y: auto;
  147. padding: 0 24px;
  148. .header {
  149. display: flex;
  150. }
  151. .description-wrapper {
  152. display: flex;
  153. flex-direction: column;
  154. justify-content: space-evenly;
  155. margin-left: 24px;
  156. .el-button {
  157. max-width: 120px;
  158. }
  159. }
  160. }
  161. .el-card {
  162. flex: none;
  163. display: flex;
  164. flex-direction: column;
  165. width: 262px;
  166. overflow: hidden;
  167. &::v-deep {
  168. .el-card__header {
  169. flex: none;
  170. }
  171. .el-card__body {
  172. flex: auto;
  173. overflow-y: auto;
  174. }
  175. }
  176. }
  177. .el-divider {
  178. &::v-deep {
  179. .el-divider__text {
  180. background-color: #f5f7f9;
  181. }
  182. }
  183. }
  184. }
  185. .knowledge-book-detail {
  186. &::v-deep {
  187. .el-collapse-item__header {
  188. padding-left: 12px;
  189. }
  190. .el-collapse-item__content {
  191. padding-bottom: 6px;
  192. }
  193. .el-collapse-item__wrap {
  194. padding: 6px 12px;
  195. }
  196. }
  197. }
  198. .book-content-container {
  199. min-height: 80px;
  200. }
  201. .book-wrapper {
  202. $zoom: 0.6;
  203. cursor: pointer;
  204. .cover {
  205. position: relative;
  206. width: 272px * $zoom;
  207. height: 400px * $zoom;
  208. background: url("../../assets/book-cover.png") no-repeat center / 100%;
  209. }
  210. .name {
  211. position: absolute;
  212. top: 30px * $zoom;
  213. right: 40px * $zoom;
  214. display: flex;
  215. justify-content: center;
  216. align-items: center;
  217. width: 48px * $zoom;
  218. height: 234px * $zoom;
  219. font-size: max(24px * $zoom, 12px);
  220. font-weight: 700;
  221. letter-spacing: 0.3em;
  222. -ms-writing-mode: tb-rl;
  223. writing-mode: vertical-rl;
  224. }
  225. .content {
  226. margin: 8px 0;
  227. font-size: 16px;
  228. span + span::before {
  229. content: '·';
  230. margin: 0 3px;
  231. }
  232. }
  233. }
  234. </style>