BookEep.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <template>
  2. <div class="common-preview">
  3. <div class="audit-content">
  4. <div ref="previewMain" class="main-container">
  5. <main :class="['preview-main', 'no-audit']" :style="computedCommonPreviewStyle">
  6. <div class="preview-left" :style="{ backgroundColor: background.background?.is_global ? '' : '#fff' }"></div>
  7. <CoursewarePreview
  8. ref="courseware"
  9. :is-show-group="false"
  10. :group-show-all="true"
  11. :group-row-list="groupRowList"
  12. :data="data"
  13. :courseware-id="curSelectId"
  14. :component-list="cList"
  15. :background="background"
  16. :can-remark="false"
  17. :show-remark="false"
  18. :component-remark-obj="{}"
  19. :project="project"
  20. />
  21. <div class="preview-right" :style="{ backgroundColor: background.background?.is_global ? '' : '#fff' }"></div>
  22. </main>
  23. </div>
  24. <div v-if="!sidebarShow" class="back-top" @click="backTop">
  25. <img :src="require(`@/assets/icon/back-top.png`)" alt="返回顶部" />
  26. </div>
  27. </div>
  28. </div>
  29. </template>
  30. <script>
  31. import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
  32. import { isTrue } from '@/utils/validate';
  33. import { buildCoursewareStyle } from '@/views/book/courseware/preview/common/utils/coursewareStyle';
  34. import { updateBaseURL } from '@/utils/http';
  35. export default {
  36. name: 'BookEep',
  37. components: {
  38. CoursewarePreview,
  39. },
  40. provide() {
  41. return {
  42. getLang: () => this.lang,
  43. getChinese: () => this.chinese,
  44. getShowPinYin: () => this.showPinYin,
  45. getLangList: () => this.langList,
  46. convertText: this.convertText,
  47. getTitleList: () => this.title_list,
  48. getPermissionControl: () => this.permissionControl,
  49. };
  50. },
  51. props: {
  52. id: {
  53. type: String,
  54. default: '',
  55. },
  56. // 组件内容
  57. content: {
  58. type: String,
  59. default: '',
  60. },
  61. // 内容分组行列表
  62. contentGroupRowList: {
  63. type: String,
  64. default: '',
  65. },
  66. // 组件列表
  67. componentList: {
  68. type: Array,
  69. default: () => [],
  70. },
  71. // 教材背景
  72. bookBackground: {
  73. type: String,
  74. default: '',
  75. },
  76. },
  77. data() {
  78. return {
  79. background: this.bookBackground.length > 0 ? JSON.parse(this.bookBackground) : {},
  80. groupRowList: this.contentGroupRowList.length > 0 ? JSON.parse(this.contentGroupRowList) : [],
  81. data: this.content.length > 0 ? JSON.parse(this.content) : { row_list: [] },
  82. cList: this.handleComponentList(),
  83. curSelectId: this.id,
  84. isTrue,
  85. sidebarShow: true,
  86. project: {
  87. editor: '', // 作者
  88. cover_image_file_id: null, // 封面图片ID
  89. cover_image_file_url: '', // 封面图片URL
  90. },
  91. title_list: [],
  92. node_list: [],
  93. permissionControl: {
  94. can_answer: true, // 可作答
  95. can_judge_correct: false, // 可判断对错(客观题)
  96. can_show_answer: false, // 可查看答案
  97. can_correct: false, // 可批改
  98. can_check_correct: false, // 可查看批改
  99. },
  100. popoverShow: false,
  101. is_can_edit: 'false', // 是否可以编辑
  102. };
  103. },
  104. computed: {
  105. computedCommonPreviewStyle() {
  106. return buildCoursewareStyle(this.background, 'commonPreview');
  107. },
  108. },
  109. watch: {
  110. componentList: {
  111. handler(newVal) {
  112. if (newVal && newVal.length > 0) {
  113. this.cList = this.handleComponentList();
  114. }
  115. },
  116. deep: true,
  117. },
  118. bookBackground: {
  119. handler(newVal) {
  120. if (newVal && newVal.length > 0) {
  121. this.background = JSON.parse(newVal);
  122. }
  123. },
  124. },
  125. content: {
  126. handler(newVal) {
  127. if (newVal && newVal.length > 0) {
  128. this.data = JSON.parse(newVal);
  129. }
  130. },
  131. },
  132. contentGroupRowList: {
  133. handler(newVal) {
  134. if (newVal && newVal.length > 0) {
  135. this.groupRowList = JSON.parse(newVal);
  136. }
  137. },
  138. },
  139. id: {
  140. handler(newVal) {
  141. if (newVal && newVal.length > 0) {
  142. this.curSelectId = newVal;
  143. }
  144. },
  145. },
  146. },
  147. created() {
  148. const origin = window.location.origin;
  149. if (origin && origin.indexOf('localhost') === -1) {
  150. updateBaseURL(`${origin}/`);
  151. }
  152. },
  153. methods: {
  154. backTop() {
  155. this.$refs.previewMain.scrollTo({
  156. top: 0,
  157. left: 0,
  158. behavior: 'smooth',
  159. });
  160. },
  161. convertText(text) {
  162. return text;
  163. },
  164. handleComponentList() {
  165. const processHtmlString = this.processHtmlString;
  166. const cList = this.componentList || [];
  167. cList.forEach((x) => {
  168. if (x.component_type === 'audio') {
  169. let _c = JSON.parse(x.content);
  170. let p = _c.property || {};
  171. if (!p.file_name_display_mode) p.file_name_display_mode = 'true';
  172. if (p.view_method === 'independent' && !p.style_mode) {
  173. p.style_mode = 'middle';
  174. }
  175. if (!p.style_mode) p.style_mode = 'big';
  176. if (p.view_method === 'icon') {
  177. p.file_name_display_mode = 'false';
  178. p.view_method = 'independent';
  179. p.style_mode = 'small';
  180. }
  181. x.content = JSON.stringify(_c);
  182. } else if (x.component_type === 'richtext') {
  183. if (!processHtmlString) return;
  184. let _c = JSON.parse(x.content);
  185. let p = _c.property || {};
  186. let lev = Number(p.title_style_level);
  187. if (p.is_title !== 'true' || lev < 1 || !_c.content) return;
  188. let style = this.title_list.find((y) => y.level === lev) || {};
  189. if (style && style.style) {
  190. style = JSON.parse(style.style);
  191. let c_text = _c.content;
  192. const parser = new DOMParser();
  193. const doc = parser.parseFromString(c_text, 'text/html');
  194. const body = doc.body;
  195. const pElements = body.querySelectorAll('p');
  196. processHtmlString(pElements, style);
  197. _c.content = body.innerHTML;
  198. x.content = JSON.stringify(_c);
  199. }
  200. }
  201. });
  202. return cList;
  203. },
  204. processHtmlString(pElements, styleConfig, isClear = false) {
  205. const _pElements = pElements || [];
  206. _pElements.forEach((pElement) => {
  207. if (pElement.innerHTML !== '<br data-mce-bogus="1">') {
  208. pElement.innerHTML = pElement.innerText;
  209. }
  210. if (isClear) {
  211. pElement.removeAttribute('style');
  212. pElement.removeAttribute('data-mce-style');
  213. } else {
  214. const style = pElement.style;
  215. if (styleConfig.font) style.fontFamily = styleConfig.font;
  216. if (styleConfig.font_size) style.fontSize = styleConfig.font_size;
  217. if (styleConfig.text_color) style.color = styleConfig.text_color;
  218. style.fontWeight = styleConfig.bold === 'true' ? 'bold' : 'normal';
  219. style.fontStyle = styleConfig.italic === 'true' ? 'italic' : 'normal';
  220. if (styleConfig.indent || styleConfig.indent === 0) style.marginLeft = `${styleConfig.indent}px`;
  221. }
  222. });
  223. },
  224. },
  225. };
  226. </script>
  227. <style lang="scss" scoped>
  228. @use '@/styles/mixin.scss' as *;
  229. $total-width: $courseware-width + $courseware-left-margin + $courseware-right-margin;
  230. .common-preview {
  231. min-width: calc($total-width);
  232. .main-container {
  233. position: relative;
  234. flex: 1;
  235. min-width: 1110px;
  236. padding: 15px;
  237. overflow: auto;
  238. background-color: #ececec;
  239. .catalogue-bar {
  240. position: absolute;
  241. top: 15px;
  242. left: 0;
  243. display: flex;
  244. align-items: center;
  245. justify-content: center;
  246. width: 54px;
  247. height: 54px;
  248. margin: -9px 6px 0 240px;
  249. cursor: pointer;
  250. background-color: #fff;
  251. border-radius: 2px;
  252. box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 40%);
  253. }
  254. .sidebar-bar {
  255. position: absolute;
  256. top: 0;
  257. right: 240px;
  258. display: flex;
  259. width: 60px;
  260. height: calc(100vh - 166px);
  261. .toolbar {
  262. display: flex;
  263. flex-direction: column;
  264. align-items: center;
  265. width: 60px;
  266. height: 100%;
  267. img {
  268. cursor: pointer;
  269. }
  270. &-special {
  271. display: flex;
  272. flex-direction: column;
  273. row-gap: 16px;
  274. align-items: center;
  275. width: 100%;
  276. margin-bottom: 24px;
  277. background-color: #fff;
  278. img {
  279. width: 36px;
  280. height: 36px;
  281. }
  282. }
  283. }
  284. }
  285. }
  286. .back-top {
  287. position: absolute;
  288. right: 240px;
  289. bottom: 0;
  290. display: flex;
  291. place-content: center center;
  292. align-items: center;
  293. width: 60px;
  294. height: 60px;
  295. cursor: pointer;
  296. background-color: #fff;
  297. }
  298. main.preview-main {
  299. display: flex;
  300. flex: 1;
  301. width: calc($total-width);
  302. min-width: calc($total-width);
  303. max-width: calc($total-width);
  304. min-height: 100%;
  305. margin: 0 auto;
  306. background-color: #fff;
  307. border-radius: 4px;
  308. box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 40%);
  309. .preview-left {
  310. width: $courseware-left-margin;
  311. min-width: $courseware-left-margin;
  312. max-width: $courseware-left-margin;
  313. background-color: $courseware-bgColor;
  314. }
  315. .preview-right {
  316. width: $courseware-right-margin;
  317. min-width: $courseware-right-margin;
  318. max-width: $courseware-right-margin;
  319. background-color: $courseware-bgColor;
  320. }
  321. &.no-audit {
  322. margin: 0 auto;
  323. }
  324. }
  325. .audit-content {
  326. display: flex;
  327. min-width: calc($total-width);
  328. .left-menu {
  329. display: flex;
  330. flex-direction: column;
  331. width: $catalogue-width;
  332. font-family: 'Microsoft YaHei', 'Arial', sans-serif;
  333. background-color: #fff;
  334. .courseware-info {
  335. display: flex;
  336. column-gap: 18px;
  337. width: 100%;
  338. height: 186px;
  339. padding: 6px 6px 24px;
  340. border-bottom: $border;
  341. .cover-image {
  342. display: flex;
  343. align-items: center;
  344. justify-content: center;
  345. width: 111px;
  346. height: 157px;
  347. background-color: rgba(229, 229, 229, 100%);
  348. img {
  349. max-width: 111px;
  350. max-height: 157px;
  351. }
  352. }
  353. .info-content {
  354. display: flex;
  355. flex-direction: column;
  356. justify-content: space-between;
  357. .catalogue-icon {
  358. text-align: right;
  359. .svg-icon {
  360. cursor: pointer;
  361. }
  362. }
  363. .courseware {
  364. width: 159px;
  365. height: 64px;
  366. font-size: 16px;
  367. .name {
  368. font-weight: bold;
  369. }
  370. .editor {
  371. display: -webkit-box;
  372. overflow: hidden;
  373. text-overflow: ellipsis;
  374. word-break: break-word;
  375. white-space: normal;
  376. -webkit-line-clamp: 2; /* 多行省略行数,按需调整 */
  377. -webkit-box-orient: vertical;
  378. }
  379. }
  380. }
  381. }
  382. }
  383. .sidebar {
  384. position: relative;
  385. display: flex;
  386. width: $sidebar-width;
  387. .toolbar {
  388. display: flex;
  389. flex-direction: column;
  390. align-items: center;
  391. width: 60px;
  392. height: 100%;
  393. background-color: rgba(247, 248, 250, 100%);
  394. img {
  395. cursor: pointer;
  396. }
  397. &-special {
  398. display: flex;
  399. flex-direction: column;
  400. row-gap: 16px;
  401. margin-bottom: 24px;
  402. }
  403. &-list {
  404. display: flex;
  405. flex-direction: column;
  406. row-gap: 16px;
  407. align-items: center;
  408. width: 100%;
  409. .sidebar-item {
  410. width: 100%;
  411. text-align: center;
  412. .sidebar-icon {
  413. width: 36px;
  414. height: 36px;
  415. cursor: pointer;
  416. }
  417. &.active {
  418. background-color: #4095e5;
  419. }
  420. }
  421. }
  422. }
  423. .content {
  424. flex: 1;
  425. background-color: #fff;
  426. .resource_box {
  427. height: 100%;
  428. overflow-y: auto;
  429. border: 1px solid #e5e5e5;
  430. h5 {
  431. position: fixed;
  432. z-index: 999;
  433. width: 240px;
  434. padding: 0 5px;
  435. margin: 0;
  436. font-size: 18px;
  437. line-height: 40px;
  438. background: #f2f3f5;
  439. }
  440. .scroll-container {
  441. display: flex;
  442. flex-direction: column;
  443. row-gap: 8px;
  444. margin: 6px;
  445. .list-item {
  446. display: flex;
  447. align-items: center;
  448. cursor: pointer;
  449. border: 1px solid #ccc;
  450. border-radius: 8px;
  451. :deep .el-slider {
  452. .el-slider__runway {
  453. background-color: #eee;
  454. }
  455. }
  456. .el-image {
  457. display: flex;
  458. width: 100%;
  459. min-width: 100%;
  460. height: 90px;
  461. background-color: #ccc;
  462. border-radius: 8px;
  463. }
  464. .video-play {
  465. width: 100%;
  466. min-width: 100%;
  467. }
  468. .text-box {
  469. word-break: break-word;
  470. }
  471. }
  472. }
  473. p {
  474. color: #999;
  475. text-align: center;
  476. }
  477. .card-box li {
  478. padding: 10px;
  479. border-bottom: 1px solid #ccc;
  480. .el-icon-notebook-2 {
  481. display: block;
  482. margin-bottom: 4px;
  483. font-size: 12px;
  484. color: grey;
  485. }
  486. }
  487. }
  488. }
  489. .back-top {
  490. position: absolute;
  491. bottom: 0;
  492. left: 0;
  493. display: flex;
  494. place-content: center center;
  495. align-items: center;
  496. width: 60px;
  497. height: 60px;
  498. cursor: pointer;
  499. }
  500. }
  501. }
  502. }
  503. :deep .scroll-container .audio-wrapper {
  504. width: 100% !important;
  505. .audio-middle {
  506. width: 100% !important;
  507. padding: 6px 8px !important;
  508. border: none;
  509. border-radius: 8px;
  510. .audio-name {
  511. text-align: left;
  512. }
  513. .slider-area {
  514. column-gap: 8px !important;
  515. }
  516. :deep .remark-dialog {
  517. .el-dialog__body {
  518. padding: 5px 20px;
  519. }
  520. }
  521. :deep .audit-dialog {
  522. .el-dialog__body {
  523. height: calc(100vh - 260px);
  524. padding: 5px 20px;
  525. }
  526. .mind-map-container .mind-map {
  527. height: calc(100vh - 310px);
  528. }
  529. }
  530. }
  531. }
  532. .mt10 {
  533. margin: 10px 0 0 !important;
  534. background-color: #eee;
  535. }
  536. .courseware-tree {
  537. display: flex;
  538. flex: 1;
  539. flex-direction: column;
  540. row-gap: 8px;
  541. max-height: calc(100vh - 200px);
  542. overflow: auto;
  543. .menu-item {
  544. display: flex;
  545. align-items: center;
  546. &:not(.courseware) {
  547. font-weight: bold;
  548. }
  549. &.courseware {
  550. &:hover {
  551. .name {
  552. background-color: #f3f3f3;
  553. }
  554. }
  555. }
  556. .svg-icon {
  557. margin-left: 4px;
  558. &.my-edit-task {
  559. color: $right-color;
  560. }
  561. }
  562. .name {
  563. flex: 1;
  564. padding: 4px 8px 4px 4px;
  565. border-radius: 4px;
  566. }
  567. &.active {
  568. .name {
  569. font-weight: bold;
  570. color: #4095e5;
  571. }
  572. }
  573. }
  574. }
  575. </style>
  576. <style lang="scss">
  577. .tox-tinymce-aux {
  578. z-index: 9999 !important;
  579. }
  580. </style>