NotesPreview.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="notes-preview" :style="[getAreaStyle(), getComponentStyle()]">
  4. <SerialNumberPosition
  5. v-if="isEnable(data.property.hasOwnProperty('sn_display_mode') ? data.property.sn_display_mode : 'false')"
  6. :property="data.property"
  7. />
  8. <div class="main">
  9. <div class="EEP-zhedie">
  10. <!-- :style="{
  11. backgroundColor:
  12. data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '',
  13. }" -->
  14. <div v-if="data.title_con" class="topTitle-note">
  15. <div class="NPC-top-left">
  16. <span
  17. class="NPC-topTitle-text"
  18. :style="{
  19. color:
  20. data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#de4444',
  21. fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  22. }"
  23. v-html="convertText(sanitizeHTML(data.title_con))"
  24. ></span>
  25. <span v-if="showLang" class="NPC-topTitle-text">
  26. {{ titleTrans[getLang()] }}
  27. </span>
  28. </div>
  29. <!-- <div class="NPC-top-right" @click="handleChangeTab">
  30. <span class="NPC-top-right-text">{{ wordShow ? '收起' : '展开' }}</span>
  31. <img v-if="wordShow" src="@/assets/down.png" alt="" />
  32. <img v-else class="rotate" src="@/assets/down.png" alt="" />
  33. </div> -->
  34. </div>
  35. <el-collapse-transition>
  36. <div v-show="wordShow" class="NPC-notes-list">
  37. <div v-for="(item, index) in data.option" :key="'NPC-notes' + index" class="NPC-notes">
  38. <div class="NPC-notes-con">
  39. <span
  40. class="NPC-notes-con-number"
  41. :style="{
  42. color: data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '',
  43. fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  44. }"
  45. v-html="convertText(sanitizeHTML(item.number))"
  46. ></span>
  47. <div class="NPC-notes-con-box">
  48. <template v-if="isEnable(data.property.view_pinyin) && !notesData">
  49. <p class="pinyin-text">
  50. <span
  51. v-if="data.property.pinyin_position === 'top'"
  52. class="pinyin"
  53. :style="{
  54. fontSize:
  55. data.unified_attrib && data.unified_attrib.pinyin_size
  56. ? data.unified_attrib.pinyin_size
  57. : '',
  58. }"
  59. v-html="item.pinyin"
  60. >
  61. </span>
  62. <span>
  63. <span
  64. v-if="data.property.pinyin_position === 'front'"
  65. class="pinyin pinyin-front"
  66. :style="{
  67. fontSize:
  68. data.unified_attrib && data.unified_attrib.pinyin_size
  69. ? data.unified_attrib.pinyin_size
  70. : '',
  71. }"
  72. v-html="item.pinyin"
  73. ></span
  74. ><span
  75. class="NPC-notes-con-text"
  76. :class="[
  77. item.match_jump &&
  78. ((item.match_jump.type === 'outer' && item.match_jump.con) ||
  79. (item.match_jump.type === 'inner' && item.match_jump.source_courseware_name))
  80. ? 'NPC-notes-con-text-cursor'
  81. : '',
  82. ]"
  83. :style="{
  84. color:
  85. data.unified_attrib && data.unified_attrib.text_color
  86. ? data.unified_attrib.text_color
  87. : '',
  88. fontSize:
  89. data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  90. }"
  91. @click="handleMatchJump(item)"
  92. v-html="convertText(sanitizeHTML(item.con))"
  93. ></span
  94. ><span
  95. v-if="data.property.pinyin_position === 'back'"
  96. class="pinyin pinyin-back"
  97. :style="{
  98. fontSize:
  99. data.unified_attrib && data.unified_attrib.pinyin_size
  100. ? data.unified_attrib.pinyin_size
  101. : '',
  102. }"
  103. v-html="item.pinyin"
  104. ></span>
  105. </span>
  106. <span
  107. v-if="data.property.pinyin_position === 'bottom'"
  108. class="pinyin"
  109. :style="{
  110. fontSize:
  111. data.unified_attrib && data.unified_attrib.pinyin_size
  112. ? data.unified_attrib.pinyin_size
  113. : '',
  114. }"
  115. v-html="item.pinyin"
  116. ></span>
  117. </p>
  118. </template>
  119. <span
  120. v-else
  121. class="NPC-notes-con-text NPC-notes-con-texts"
  122. :class="[
  123. item.match_jump &&
  124. ((item.match_jump.type === 'outer' && item.match_jump.con) ||
  125. (item.match_jump.type === 'inner' && item.match_jump.source_courseware_name))
  126. ? 'NPC-notes-con-text-cursor'
  127. : '',
  128. ]"
  129. :style="{
  130. color:
  131. data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '',
  132. fontSize:
  133. data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  134. }"
  135. @click="handleMatchJump(item)"
  136. v-html="convertText(sanitizeHTML(item.con))"
  137. ></span>
  138. <span v-if="showLang" class="multilingual">
  139. {{
  140. multilingualTextList[getLang()] &&
  141. multilingualTextList[getLang()][index] &&
  142. multilingualTextList[getLang()][index][0]
  143. ? multilingualTextList[getLang()][index][0]
  144. : ''
  145. }}
  146. </span>
  147. </div>
  148. </div>
  149. <div
  150. v-if="item.interpret"
  151. class="NPC-notes-trans"
  152. :style="{
  153. fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  154. }"
  155. v-html="convertText(sanitizeHTML(item.interpret))"
  156. ></div>
  157. <div
  158. v-if="item.note"
  159. class="NPC-notes-note"
  160. :style="{
  161. fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  162. }"
  163. v-html="convertText(sanitizeHTML(item.note))"
  164. ></div>
  165. <div v-if="item.file_list && item.file_list[0]" class="NPC-notes-note-img">
  166. <el-image
  167. :src="item.file_list[0].file_url"
  168. :preview-src-list="[item.file_list[0].file_url]"
  169. fit="contain"
  170. />
  171. </div>
  172. <div v-if="showLang" class="NPC-notes-note">
  173. {{
  174. multilingualTextList[getLang()] &&
  175. multilingualTextList[getLang()][index] &&
  176. multilingualTextList[getLang()][index][1]
  177. ? multilingualTextList[getLang()][index][1]
  178. : ''
  179. }}
  180. </div>
  181. </div>
  182. </div>
  183. </el-collapse-transition>
  184. </div>
  185. </div>
  186. </div>
  187. </template>
  188. <script>
  189. import { getNotesData, isEnable } from '@/views/book/courseware/data/notes';
  190. import PreviewMixin from '../common/PreviewMixin';
  191. import { GetFileURLMap } from '@/api/app';
  192. export default {
  193. name: 'NotesPreview',
  194. components: {},
  195. mixins: [PreviewMixin],
  196. props: ['notesData', 'isMobile'],
  197. data() {
  198. return {
  199. data: this.notesData ? this.notesData : getNotesData(),
  200. wordShow: true,
  201. multilingualTextList: {}, // 多语言对应的切割后的翻译
  202. titleTrans: {},
  203. };
  204. },
  205. computed: {},
  206. watch: {
  207. 'data.option.length': {
  208. handler(val) {
  209. if (val) {
  210. // this.wordShow = isEnable(this.data.property.is_word_show);
  211. this.handleData();
  212. }
  213. },
  214. deep: true,
  215. immediate: true,
  216. },
  217. },
  218. created() {},
  219. methods: {
  220. handleChangeTab() {
  221. this.wordShow = !this.wordShow;
  222. },
  223. handleData() {
  224. if (this.data.multilingual) {
  225. this.data.multilingual.forEach((item) => {
  226. let trans_arr = item.translation.split('\n');
  227. this.$set(this.titleTrans, item.type, trans_arr[0] ? trans_arr[0] : '');
  228. let chunkSize = 2;
  229. let chunkedArr = trans_arr.splice(1).reduce((acc, curr, index) => {
  230. // 当索引是chunkSize的倍数时,开始一个新的子数组
  231. if (index % chunkSize === 0) {
  232. acc.push([curr]); // 开始新的子数组并添加当前元素
  233. } else {
  234. acc[acc.length - 1].push(curr); // 将当前元素添加到最后一个子数组中
  235. }
  236. return acc;
  237. }, []);
  238. this.$set(this.multilingualTextList, item.type, chunkedArr);
  239. });
  240. }
  241. // this.data.option.forEach((item) => {
  242. // if (item.file_list && item.file_list[0]) {
  243. // GetFileURLMap({ file_id_list: item.file_list }).then(({ url_map }) => {
  244. // this.$set(item, 'pic_url', url_map[item.file_list[0]]);
  245. // });
  246. // }
  247. // });
  248. },
  249. /**
  250. * 获取无文本内容的数据结构,用于保存为个人模板时的样式模板
  251. */
  252. getNoTextContentData() {
  253. let noTextContentData = JSON.parse(JSON.stringify(this.data));
  254. const resetFieldMap = {
  255. title_con: '',
  256. file_id_list: [], // 文件 id
  257. paragraph_list: [],
  258. };
  259. Object.assign(noTextContentData, resetFieldMap);
  260. noTextContentData.option.forEach((item) => {
  261. item.number = '';
  262. item.con = '';
  263. item.pinyin = '';
  264. item.interpret = '';
  265. item.note = '';
  266. item.file_list = [''];
  267. // item.notesColor = '#8206BF';
  268. });
  269. return noTextContentData;
  270. },
  271. handleMatchJump(item) {
  272. if (
  273. item.match_jump &&
  274. ((item.match_jump.type === 'outer' && item.match_jump.con) ||
  275. (item.match_jump.type === 'inner' && item.match_jump.source_courseware_name))
  276. ) {
  277. if (item.match_jump.type === 'outer' && item.match_jump.con) {
  278. // 外部跳转
  279. window.open(`${item.match_jump.con}`);
  280. } else if (item.match_jump.type === 'inner' && item.match_jump.source_courseware_name) {
  281. // 内部跳转
  282. console.log(item.match_jump.source_courseware_id);
  283. }
  284. }
  285. },
  286. },
  287. };
  288. </script>
  289. <style lang="scss" scoped>
  290. @use '@/styles/mixin.scss' as *;
  291. .notes-preview {
  292. @include preview-base;
  293. .EEP-zhedie {
  294. // margin-bottom: 24px;
  295. .topTitle-note {
  296. display: flex;
  297. justify-content: space-between;
  298. width: 100%;
  299. height: 48px;
  300. padding-right: 16px;
  301. padding-left: 24px;
  302. overflow: hidden;
  303. // background: #e35454;
  304. // border: 1px solid rgba(0, 0, 0, 10%);
  305. // border-radius: 8px 8px 0 0;
  306. :deep p {
  307. margin: 0;
  308. }
  309. .NPC-top-left {
  310. display: flex;
  311. align-items: center;
  312. justify-content: flex-start;
  313. .NPC-topTitle-text {
  314. // margin: 0 8px 0 0;
  315. // font-family: 'sourceR';
  316. font-size: 16px;
  317. font-weight: bold;
  318. // color: #fff;
  319. }
  320. }
  321. .NPC-top-right {
  322. display: flex;
  323. align-items: center;
  324. justify-content: flex-start;
  325. cursor: pointer;
  326. &-text {
  327. font-size: 16px;
  328. font-weight: normal;
  329. line-height: 16px;
  330. color: #fff;
  331. }
  332. img {
  333. width: 20px;
  334. height: 20px;
  335. margin-left: 4px;
  336. }
  337. }
  338. .rotate {
  339. animation-name: firstrotate;
  340. animation-timing-function: linear;
  341. animation-direction: 2s;
  342. animation-fill-mode: both;
  343. }
  344. }
  345. .NPC-notes-list {
  346. padding: 24px 24px 5px;
  347. border: 1px solid rgba(0, 0, 0, 10%);
  348. border-radius: 4px;
  349. .NPC-notes {
  350. width: 100%;
  351. margin-bottom: 24px;
  352. :deep p {
  353. margin: 0;
  354. }
  355. .NPC-notes-con {
  356. display: flex;
  357. align-items: center;
  358. justify-content: flex-start;
  359. margin-bottom: 12px;
  360. .NPC-notes-con-box {
  361. flex: 1;
  362. margin-left: 5px;
  363. }
  364. .pinyin-text {
  365. > span {
  366. display: block;
  367. }
  368. }
  369. .NPC-notes-con-number {
  370. font-family: 'robot';
  371. font-size: 14px;
  372. font-style: normal;
  373. font-weight: normal;
  374. line-height: 150%;
  375. color: #e35454;
  376. }
  377. .NPC-notes-con-text {
  378. width: max-content;
  379. margin: 0 5px;
  380. // font-size: 14px;
  381. font-style: normal;
  382. font-weight: normal;
  383. line-height: 1;
  384. color: #e35454;
  385. :deep p {
  386. // line-height: 150%;
  387. display: inline;
  388. }
  389. &.NPC-notes-con-texts {
  390. margin: 0;
  391. :deep p {
  392. display: inline-block;
  393. width: 100%;
  394. }
  395. }
  396. &-cursor {
  397. cursor: pointer;
  398. }
  399. }
  400. }
  401. .pinyin {
  402. font-family: 'League';
  403. font-size: 14px;
  404. &-front,
  405. &-back {
  406. :deep p {
  407. // line-height: 150%;
  408. display: inline;
  409. }
  410. }
  411. }
  412. .NPC-notes-trans {
  413. padding-left: 27px;
  414. margin-bottom: 12px;
  415. font-size: 14px;
  416. font-style: normal;
  417. font-weight: bold;
  418. line-height: 150%;
  419. color: #000;
  420. }
  421. .NPC-notes-note {
  422. font-size: 14px;
  423. font-style: normal;
  424. font-weight: normal;
  425. line-height: 150%;
  426. color: #000;
  427. text-indent: 27px;
  428. word-break: break-word;
  429. }
  430. }
  431. }
  432. .NPC-notes-note-img {
  433. width: 100%;
  434. > div {
  435. max-width: 100%;
  436. > img {
  437. max-width: 100%;
  438. }
  439. }
  440. }
  441. }
  442. @keyframes firstrotate {
  443. 0% {
  444. transform: rotateZ(0deg);
  445. }
  446. 100% {
  447. transform: rotateZ(180deg);
  448. }
  449. }
  450. @keyframes huifuRotate {
  451. 0% {
  452. transform: rotateZ(180deg);
  453. }
  454. 100% {
  455. transform: rotateZ(0deg);
  456. }
  457. }
  458. }
  459. </style>