BookEep.vue 15 KB

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