BookD.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <script>
  2. import {getBook, getBookContent, getBookListOfRecommend, setBookStatus} from '@/api/knowledge.js';
  3. import {getDataByKey} from '@/api/system';
  4. import {mapGetters} from 'vuex';
  5. import printJS from 'print-js';
  6. export default {
  7. name: 'KnowledgeBookDetail',
  8. data() {
  9. return {
  10. showPrintHTML: false,
  11. book: {id: '', catalogueList: []},
  12. section: [],
  13. recommend: [],
  14. containerHeight: 0,
  15. selected: {},
  16. };
  17. },
  18. computed: {
  19. collection() {
  20. return +this.book['isBookshelf'] === 1;
  21. },
  22. ...mapGetters(["getuserinfo"]),
  23. },
  24. created() {
  25. this.book.id = this.$route.query.id;
  26. this.onPreview();
  27. getDataByKey({
  28. key: "showPrintHTML",
  29. organizationid: this.getuserinfo.organizationid,
  30. }).then(data => { this.showPrintHTML = data.enabled; });
  31. },
  32. mounted() {
  33. setTimeout(() => {
  34. try { this.containerHeight = `${this.$el.parentElement.getBoundingClientRect().height - 10}px`; } catch (e) { }
  35. }, 200);
  36. },
  37. methods: {
  38. async getRecommend() {
  39. try {
  40. this.recommend = await getBookListOfRecommend(this.book.id);
  41. } catch (e) {}
  42. },
  43. async getBook() {
  44. this.book = await getBook(this.book.id);
  45. let selected;
  46. const get = (list, level = 1) => list.map(item => {
  47. const children = Array.isArray(item.children) ? get(item.children, level + 1) : [];
  48. const isLeaf = !children.length;
  49. const section = {
  50. id: item.catalogueId,
  51. name: item.catalogueName,
  52. children, isLeaf, level,
  53. content: isLeaf ? '' : void 0,
  54. dirty: !isLeaf,
  55. }
  56. if (section.isLeaf && !selected) selected = section;
  57. return section;
  58. });
  59. this.section = get(this.book.catalogueList);
  60. await this.load(selected);
  61. },
  62. async load(section) {
  63. if (!section.isLeaf) return;
  64. this.selected = {...section};
  65. if (!section.dirty) {
  66. try {
  67. section.content = await getBookContent({bookId: this.book.id, catalogueId: section.id});
  68. section.dirty = true;
  69. this.$set(this.selected, 'content', section.content);
  70. this.$set(this.selected, 'dirty', section.dirty);
  71. } catch (e) {}
  72. }
  73. this.$nextTick(() => {
  74. document.querySelector(`#scrolling`).scrollTo({top: 0, behavior: 'smooth'});
  75. });
  76. },
  77. async collected() {
  78. try {
  79. const isBookshelf = this.collection ? '0' : '1';
  80. await setBookStatus(this.book.id, {isBookshelf});
  81. this.$set(this.book, 'isBookshelf', isBookshelf);
  82. this.$message.success('操作成功');
  83. } catch (e) {
  84. }
  85. },
  86. async onPreview(book) {
  87. if (book) {
  88. await this.$router.push({path: `/index/knowledge/book/detail?id=${book.id}`});
  89. this.book = book;
  90. }
  91. try {
  92. await this.getBook();
  93. await this.getRecommend();
  94. } catch (e) {
  95. }
  96. },
  97. async print(object) {
  98. console.log(object);
  99. printJS({
  100. printable: this.$refs.print,
  101. type: 'html',
  102. header: object.name,
  103. documentTitle: `名医名方 - 经典医书`,
  104. scanStyles: false,
  105. css: ['print/containers.css'],
  106. style: `.text { line-height: 2.5; }`
  107. })
  108. }
  109. },
  110. };
  111. </script>
  112. <template>
  113. <div class="knowledge-book-detail" :style="{height: containerHeight}">
  114. <div class="detail-wrapper">
  115. <div class="header">
  116. <div class="book-wrapper">
  117. <div class="cover">
  118. <div class="name">{{ book.bookName }}</div>
  119. </div>
  120. </div>
  121. <div class="description-wrapper">
  122. <div>
  123. <div style="margin-bottom: 6px;font-size: 18px;font-weight: 700;">{{ book.bookName }}</div>
  124. <div class="content">
  125. <span v-if="book.dynasty">{{ book.dynasty }} · </span>
  126. <span>{{ book.author || '佚名' }}</span>
  127. </div>
  128. </div>
  129. <div style="text-indent: 2em;">{{ book.briefIntroduction }}</div>
  130. <el-button type="primary" plain round size="small" :icon="collection ? 'el-icon-remove-outline' : 'el-icon-circle-plus-outline'" @click="collected()">
  131. {{ collection ? '移出' : '加入' }}书架
  132. </el-button>
  133. </div>
  134. </div>
  135. <el-divider content-position="left">阅读<span v-if="book.readProgress">:{{ book.readProgress }}%</span>
  136. </el-divider>
  137. <div class="book-content-container" :style="{maxHeight: containerHeight}" :class="{scrollable: true}">
  138. <div class="catalogue">
  139. <div v-for="item in section" :key="item.id"
  140. :class="['level-' + item.level, item.isLeaf ? 'section' : 'title']">
  141. <span :class="{active: item.id === selected.id}" @click="load(item)">{{ item.name }}</span>
  142. <div v-for="item in item.children" :key="item.id"
  143. :class="['level-' + item.level, item.isLeaf ? 'section' : 'title']">
  144. <span :class="{active: item.id === selected.id}" @click="load(item)">{{ item.name }}</span>
  145. </div>
  146. </div>
  147. </div>
  148. <div class="content" id="scrolling" :style="{height: containerHeight}" v-loading="!selected.dirty">
  149. <el-tooltip content="下载打印">
  150. <el-button class="print-btn" size="small" v-if="showPrintHTML" type="primary" circle icon="el-icon-download" :disabled="!selected.dirty" @click="print(selected)"></el-button>
  151. </el-tooltip>
  152. <div class="text print-containers" ref="print" v-html="selected.content"></div>
  153. <el-backtop style="right: 300px;" target="#scrolling"></el-backtop>
  154. </div>
  155. </div>
  156. </div>
  157. <el-card header="相关医书">
  158. <div class="book-wrapper" style="margin-bottom: 24px;" v-for="book in recommend" :key="book.id"
  159. @click="onPreview(book)">
  160. <div class="cover">
  161. <div class="name">{{ book.bookName }}</div>
  162. </div>
  163. <div class="content" style="font-weight: 700;">{{ book.bookName }}</div>
  164. <div class="content">
  165. <span v-if="book.dynasty">{{ book.dynasty }}</span>
  166. <span>{{ book.author || '佚名' }}</span>
  167. </div>
  168. </div>
  169. </el-card>
  170. </div>
  171. </template>
  172. <style lang="scss" scoped>
  173. .knowledge-book-detail {
  174. display: flex;
  175. overflow: hidden;
  176. .detail-wrapper {
  177. flex: auto;
  178. overflow-y: auto;
  179. height: 100%;
  180. padding: 0 24px;
  181. .header {
  182. display: flex;
  183. }
  184. .description-wrapper {
  185. display: flex;
  186. flex-direction: column;
  187. justify-content: space-evenly;
  188. margin-left: 24px;
  189. .el-button {
  190. max-width: 120px;
  191. }
  192. }
  193. }
  194. .el-card {
  195. flex: none;
  196. display: flex;
  197. flex-direction: column;
  198. width: 262px;
  199. overflow: hidden;
  200. &::v-deep {
  201. .el-card__header {
  202. flex: none;
  203. }
  204. .el-card__body {
  205. flex: auto;
  206. overflow-y: auto;
  207. }
  208. }
  209. }
  210. .el-divider {
  211. &::v-deep {
  212. .el-divider__text {
  213. background-color: #f5f7f9;
  214. }
  215. }
  216. }
  217. }
  218. .knowledge-book-detail {
  219. &::v-deep {
  220. .el-collapse-item__header {
  221. padding-left: 12px;
  222. }
  223. .el-collapse-item__content {
  224. padding-bottom: 6px;
  225. }
  226. .el-collapse-item__wrap {
  227. padding: 6px 12px;
  228. }
  229. }
  230. }
  231. .book-content-container {
  232. display: flex;
  233. &.scrollable > div {
  234. overflow-y: auto;
  235. }
  236. .catalogue {
  237. flex: 1 0;
  238. user-select: none;
  239. .title {
  240. margin: 24px 0;
  241. color: #213547;
  242. > span {
  243. pointer-events: none;
  244. }
  245. }
  246. .section {
  247. margin: 6px 0;
  248. color: #3c3c3cb3;
  249. &:hover, .active {
  250. cursor: pointer;
  251. color: #5386f6;
  252. }
  253. }
  254. }
  255. .content {
  256. position: relative;
  257. flex: 4 4;
  258. > .text {
  259. padding: 24px;
  260. line-height: 2.5;
  261. white-space: pre-wrap;
  262. }
  263. .print-btn {
  264. position: absolute;
  265. top: 0;
  266. right: 0;
  267. }
  268. }
  269. ::v-deep.el-loading-mask {
  270. background-color: transparent;
  271. }
  272. }
  273. .book-wrapper {
  274. $zoom: 0.6;
  275. cursor: pointer;
  276. .cover {
  277. position: relative;
  278. width: 272px * $zoom;
  279. height: 400px * $zoom;
  280. background: url("../../assets/book-cover.png") no-repeat center / 100%;
  281. }
  282. .name {
  283. position: absolute;
  284. top: 30px * $zoom;
  285. right: 40px * $zoom;
  286. display: flex;
  287. justify-content: center;
  288. align-items: center;
  289. width: 48px * $zoom;
  290. height: 234px * $zoom;
  291. font-size: max(24px * $zoom, 12px);
  292. font-weight: 700;
  293. letter-spacing: 0.3em;
  294. -ms-writing-mode: tb-rl;
  295. writing-mode: vertical-rl;
  296. }
  297. .content {
  298. margin: 8px 0;
  299. font-size: 16px;
  300. span + span::before {
  301. content: '·';
  302. margin: 0 3px;
  303. }
  304. }
  305. }
  306. </style>