ModuleMixin.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // 组件混入
  2. import Vue from 'vue';
  3. import ModuleBase from './ModuleBase.vue';
  4. import RichText from '@/components/RichText.vue';
  5. import MultilingualFill from '@/views/book/components/MultilingualFill.vue';
  6. import { displayList, viewMethodList, isEnable } from '@/views/book/courseware/data/common';
  7. import {
  8. ContentSaveCoursewareComponentContent,
  9. ContentGetCoursewareComponentContent,
  10. CrateParsedTextInfo_Pinyin,
  11. } from '@/api/book';
  12. const mixin = {
  13. data() {
  14. return {
  15. displayList,
  16. viewMethodList,
  17. typeList: ['interaction'],
  18. isEnable,
  19. property: {
  20. isGetContent: false, // 是否已获取内容
  21. },
  22. borderColorObj: Vue.observable({ value: this.borderColor }), // 边框颜色
  23. multilingualVisible: false, // 多语言弹窗
  24. };
  25. },
  26. props: {
  27. id: {
  28. type: String,
  29. required: true,
  30. },
  31. componentMove: {
  32. type: Function,
  33. required: true,
  34. },
  35. borderColor: {
  36. type: String,
  37. required: true,
  38. },
  39. content: {
  40. type: String,
  41. default: '{}',
  42. },
  43. type: {
  44. type: String,
  45. default: '',
  46. },
  47. },
  48. components: {
  49. ModuleBase,
  50. RichText,
  51. MultilingualFill,
  52. },
  53. provide() {
  54. return {
  55. showSetting: this.showSetting,
  56. id: this.id,
  57. deleteComponent: this.deleteComponent,
  58. handleComponentMove: this.handleComponentMove,
  59. property: this.property,
  60. borderColor: this.borderColorObj,
  61. };
  62. },
  63. watch: {
  64. borderColor(newVal) {
  65. this.borderColorObj.value = newVal;
  66. },
  67. content: {
  68. handler(newVal) {
  69. if (this.type !== 'interaction') return;
  70. if (newVal) this.data = JSON.parse(newVal);
  71. },
  72. immediate: true,
  73. },
  74. },
  75. inject: ['courseware_id', 'project_id', 'getBookUnifiedAttr', 'getTemporaryCoursewareID'],
  76. created() {
  77. if (this.type !== 'interaction') {
  78. this.GetCoursewareComponentContent();
  79. }
  80. // 初始化 mind_map.node_list[0].id
  81. if (!this.data?.mind_map?.node_list?.[0]?.id) {
  82. this.data.mind_map = this.data.mind_map ?? {};
  83. this.data.mind_map.node_list = this.data.mind_map.node_list ?? [{}];
  84. if (this.data.mind_map.node_list.length > 0) {
  85. this.data.mind_map.node_list[0].id = this.id;
  86. }
  87. }
  88. },
  89. methods: {
  90. GetCoursewareComponentContent() {
  91. if (this.courseware_id === '' || this.id === '') return;
  92. let temporaryCoursewareID = this.getTemporaryCoursewareID();
  93. let coursewareID = temporaryCoursewareID || this.courseware_id;
  94. ContentGetCoursewareComponentContent({ courseware_id: coursewareID, component_id: this.id }).then(
  95. ({ content }) => {
  96. if (content) {
  97. this.data = JSON.parse(content);
  98. } else {
  99. const bookUnifiedAttr = this.getBookUnifiedAttr();
  100. this.$set(this.data, 'unified_attrib', bookUnifiedAttr);
  101. // 初始化时,如果有 view_pinyin 则根据全文设置配置设 view_pinyin 和 pinyin_position
  102. if ('view_pinyin' in this.data.property) {
  103. this.data.property.view_pinyin = bookUnifiedAttr?.view_pinyin ?? 'false';
  104. this.data.property.pinyin_position = bookUnifiedAttr?.pinyin_position ?? 'top';
  105. }
  106. if ('frame_color' in this.data.property) {
  107. // 框颜色初始化为主题色
  108. this.data.property.frame_color = bookUnifiedAttr?.topic_color ?? '#F13232';
  109. }
  110. if ('sn_background_color' in this.data.property) {
  111. // 序号背景色
  112. this.data.property.sn_background_color = bookUnifiedAttr?.topic_color ?? '#EA3232';
  113. }
  114. }
  115. this.property.isGetContent = true;
  116. this.$watch(
  117. 'data',
  118. () => {
  119. this.$emit('changeData');
  120. },
  121. { deep: true },
  122. );
  123. },
  124. );
  125. },
  126. /**
  127. * @description 显示设置
  128. */
  129. showSetting() {
  130. this.$emit('showSetting', this.data.property, this.data.type, this.id);
  131. },
  132. /**
  133. * @description 删除组件
  134. */
  135. deleteComponent() {
  136. this.$emit('deleteComponent', this.id);
  137. },
  138. /**
  139. * @description 更新property属性
  140. * @param {String} type 类型
  141. * @param {Object} attr 属性
  142. */
  143. updateProperty(type, attr) {
  144. this.$set(this.data.property, type, attr);
  145. },
  146. /**
  147. * 批量设置富文本样式
  148. * @param {Object} type 类型
  149. * @param {Object} attr 属性
  150. */
  151. updateRichTextProperty(type, attr) {
  152. let richTextRefs = this.$refs.richText;
  153. if (!richTextRefs) return;
  154. if (!Array.isArray(richTextRefs)) {
  155. richTextRefs = [richTextRefs];
  156. }
  157. richTextRefs?.forEach((richText) => {
  158. richText.setRichFormat(type, attr);
  159. });
  160. },
  161. setUnifiedAttr(data) {
  162. if (!data) return;
  163. this.data.unified_attrib = data;
  164. },
  165. handleComponentMove(data) {
  166. this.componentMove({ ...data, id: this.id });
  167. },
  168. /**
  169. * 保存课节组件内容
  170. * @returns {Promise} 返回 Promise,便于父组件 await
  171. */
  172. saveCoursewareComponentContent() {
  173. return ContentSaveCoursewareComponentContent({
  174. courseware_id: this.courseware_id,
  175. component_id: this.id,
  176. component_type: this.data.type,
  177. content: JSON.stringify(this.data),
  178. });
  179. },
  180. findComponentWithRefAndMethod(children, refName, methodName) {
  181. for (const child of children) {
  182. // 检查当前组件是否符合条件
  183. if (child.$refs[refName] && typeof child[methodName] === 'function') {
  184. return child;
  185. }
  186. // 递归检查子组件
  187. if (child.$children?.length) {
  188. const found = this.findComponentWithRefAndMethod(child.$children, refName, methodName);
  189. if (found) return found;
  190. }
  191. }
  192. return null;
  193. },
  194. openMultilingual() {
  195. this.multilingualVisible = true;
  196. },
  197. /**
  198. * @description 提交多语言翻译
  199. * @param {Array} multilingual
  200. */
  201. handleMultilingualTranslation(multilingual) {
  202. this.$set(this.data, 'multilingual', multilingual);
  203. },
  204. /**
  205. * 获取并设置拼音解析文本
  206. * @param {String} text 文本
  207. * @param {Number} i 行索引
  208. * @param {Number} j 列索引
  209. * @param {String} attr 属性名
  210. */
  211. createParsedTextInfoPinyin(text, i = -1, j = -1, attr = 'option_list') {
  212. let data = null;
  213. if (i === -1) {
  214. data = this.data;
  215. } else if (i >= 0 && j === -1) {
  216. data = this.data[attr][i];
  217. } else if (i >= 0 && j >= 0) {
  218. data = this.data[attr][i][j];
  219. }
  220. if (text === '') {
  221. data.pinyin_proofread_word_list = [];
  222. return;
  223. }
  224. data.paragraph_list_parameter.text = text;
  225. CrateParsedTextInfo_Pinyin({
  226. ...data.paragraph_list_parameter,
  227. is_first_sentence_first_hz_pinyin_first_char_upper_case:
  228. this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case,
  229. }).then(({ parsed_text }) => {
  230. if (parsed_text) {
  231. // 合并 activeTextStyle
  232. const mergedData = parsed_text.paragraph_list.map((outerArr, i) =>
  233. outerArr.map((innerArr, j) =>
  234. innerArr.map((newItem, k) => {
  235. if (!newItem.pinyin) newItem.pinyin = newItem.text;
  236. return {
  237. ...newItem,
  238. // 如果 originalItem 有 activeTextStyle,就合并到 newItem
  239. ...(data.paragraph_list?.[i]?.[j]?.[k]?.activeTextStyle && {
  240. activeTextStyle: data.paragraph_list[i][j][k].activeTextStyle,
  241. }),
  242. };
  243. }),
  244. ),
  245. );
  246. data.paragraph_list = mergedData;
  247. }
  248. });
  249. },
  250. /**
  251. * 填充校对后的拼音
  252. * @param {Object} param0 拼音参数
  253. * @param {Number} col 列索引
  254. * @param {Number} row 行索引
  255. * @param {String} attr 属性名
  256. */
  257. fillCorrectPinyin(
  258. { selectContent: { text, pinyin, activeTextStyle }, i, j, k },
  259. row = -1,
  260. col = -1,
  261. attr = 'option_list',
  262. ) {
  263. let data = null;
  264. if (row === -1) {
  265. data = this.data.paragraph_list_parameter;
  266. } else if (row >= 0 && col === -1) {
  267. data = this.data[attr][row].paragraph_list_parameter;
  268. } else if (row >= 0 && col >= 0) {
  269. data = this.data[attr][row][col].paragraph_list_parameter;
  270. }
  271. if (!Array.isArray(data.pinyin_proofread_word_list)) {
  272. data.pinyin_proofread_word_list = [];
  273. }
  274. data.pinyin_proofread_word_list.push({
  275. paragraph_index: i,
  276. sentence_index: j,
  277. word_index: k,
  278. word: text,
  279. pinyin,
  280. });
  281. let listItem = null;
  282. if (row === -1) {
  283. listItem = this.data.paragraph_list?.[i]?.[j]?.[k];
  284. } else if (row >= 0 && col === -1) {
  285. listItem = this.data[attr]?.[row]?.paragraph_list?.[i]?.[j]?.[k];
  286. } else if (row >= 0 && col >= 0) {
  287. listItem = this.data[attr]?.[row]?.[col]?.paragraph_list?.[i]?.[j]?.[k];
  288. }
  289. if (listItem) {
  290. if (pinyin) listItem.pinyin = pinyin;
  291. if (activeTextStyle) listItem.activeTextStyle = activeTextStyle;
  292. }
  293. },
  294. },
  295. };
  296. export default mixin;