ModuleMixin.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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'],
  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. ContentGetCoursewareComponentContent({ courseware_id: this.courseware_id, component_id: this.id }).then(
  93. ({ content }) => {
  94. if (content) {
  95. this.data = JSON.parse(content);
  96. } else {
  97. const bookUnifiedAttr = this.getBookUnifiedAttr();
  98. this.$set(this.data, 'unified_attrib', bookUnifiedAttr);
  99. // 初始化时,如果有 view_pinyin 则根据全文设置配置设 view_pinyin 和 pinyin_position
  100. if ('view_pinyin' in this.data.property) {
  101. this.data.property.view_pinyin = bookUnifiedAttr?.view_pinyin ?? 'false';
  102. this.data.property.pinyin_position = bookUnifiedAttr?.pinyin_position ?? 'top';
  103. }
  104. if ('frame_color' in this.data.property) {
  105. // 框颜色初始化为主题色
  106. this.data.property.frame_color = bookUnifiedAttr?.topic_color ?? '#F13232';
  107. }
  108. if ('sn_background_color' in this.data.property) {
  109. // 序号背景色
  110. this.data.property.sn_background_color = bookUnifiedAttr?.topic_color ?? '#EA3232';
  111. }
  112. }
  113. this.property.isGetContent = true;
  114. this.$watch(
  115. 'data',
  116. () => {
  117. this.$emit('changeData');
  118. },
  119. { deep: true },
  120. );
  121. },
  122. );
  123. },
  124. /**
  125. * @description 显示设置
  126. */
  127. showSetting() {
  128. this.$emit('showSetting', this.data.property, this.data.type, this.id);
  129. },
  130. /**
  131. * @description 删除组件
  132. */
  133. deleteComponent() {
  134. this.$emit('deleteComponent', this.id);
  135. },
  136. /**
  137. * @description 更新property属性
  138. * @param {String} type 类型
  139. * @param {Object} attr 属性
  140. */
  141. updateProperty(type, attr) {
  142. this.$set(this.data.property, type, attr);
  143. },
  144. /**
  145. * 批量设置富文本样式
  146. * @param {Object} type 类型
  147. * @param {Object} attr 属性
  148. */
  149. updateRichTextProperty(type, attr) {
  150. let richTextRefs = this.$refs.richText;
  151. if (!richTextRefs) return;
  152. if (!Array.isArray(richTextRefs)) {
  153. richTextRefs = [richTextRefs];
  154. }
  155. richTextRefs?.forEach((richText) => {
  156. richText.setRichFormat(type, attr);
  157. });
  158. },
  159. setUnifiedAttr(data) {
  160. if (!data) return;
  161. this.data.unified_attrib = data;
  162. },
  163. handleComponentMove(data) {
  164. this.componentMove({ ...data, id: this.id });
  165. },
  166. /**
  167. * 保存课节组件内容
  168. * @returns {Promise} 返回 Promise,便于父组件 await
  169. */
  170. saveCoursewareComponentContent() {
  171. return ContentSaveCoursewareComponentContent({
  172. courseware_id: this.courseware_id,
  173. component_id: this.id,
  174. component_type: this.data.type,
  175. content: JSON.stringify(this.data),
  176. });
  177. },
  178. findComponentWithRefAndMethod(children, refName, methodName) {
  179. for (const child of children) {
  180. // 检查当前组件是否符合条件
  181. if (child.$refs[refName] && typeof child[methodName] === 'function') {
  182. return child;
  183. }
  184. // 递归检查子组件
  185. if (child.$children?.length) {
  186. const found = this.findComponentWithRefAndMethod(child.$children, refName, methodName);
  187. if (found) return found;
  188. }
  189. }
  190. return null;
  191. },
  192. openMultilingual() {
  193. this.multilingualVisible = true;
  194. },
  195. /**
  196. * @description 提交多语言翻译
  197. * @param {Array} multilingual
  198. */
  199. handleMultilingualTranslation(multilingual) {
  200. this.data.multilingual = multilingual;
  201. },
  202. /**
  203. * 获取并设置拼音解析文本
  204. * @param {String} text 文本
  205. * @param {Number} i 行索引
  206. * @param {Number} j 列索引
  207. * @param {String} attr 属性名
  208. */
  209. createParsedTextInfoPinyin(text, i = -1, j = -1, attr = 'option_list') {
  210. let data = null;
  211. if (i === -1) {
  212. data = this.data;
  213. } else if (i >= 0 && j === -1) {
  214. data = this.data[attr][i];
  215. } else if (i >= 0 && j >= 0) {
  216. data = this.data[attr][i][j];
  217. }
  218. if (text === '') {
  219. data.pinyin_proofread_word_list = [];
  220. return;
  221. }
  222. data.paragraph_list_parameter.text = text;
  223. CrateParsedTextInfo_Pinyin({
  224. ...data.paragraph_list_parameter,
  225. is_first_sentence_first_hz_pinyin_first_char_upper_case:
  226. this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case,
  227. }).then(({ parsed_text }) => {
  228. if (parsed_text) {
  229. // 合并 activeTextStyle
  230. const mergedData = parsed_text.paragraph_list.map((outerArr, i) =>
  231. outerArr.map((innerArr, j) =>
  232. innerArr.map((newItem, k) => ({
  233. ...newItem,
  234. // 如果 originalItem 有 activeTextStyle,就合并到 newItem
  235. ...(data.paragraph_list?.[i]?.[j]?.[k]?.activeTextStyle && {
  236. activeTextStyle: data.paragraph_list[i][j][k].activeTextStyle,
  237. }),
  238. })),
  239. ),
  240. );
  241. data.paragraph_list = mergedData;
  242. }
  243. });
  244. },
  245. /**
  246. * 填充校对后的拼音
  247. * @param {Object} param0 拼音参数
  248. * @param {Number} col 列索引
  249. * @param {Number} row 行索引
  250. * @param {String} attr 属性名
  251. */
  252. fillCorrectPinyin(
  253. { selectContent: { text, pinyin, activeTextStyle }, i, j, k },
  254. row = -1,
  255. col = -1,
  256. attr = 'option_list',
  257. ) {
  258. let data = null;
  259. if (row === -1) {
  260. data = this.data.paragraph_list_parameter;
  261. } else if (row >= 0 && col === -1) {
  262. data = this.data[attr][row].paragraph_list_parameter;
  263. } else if (row >= 0 && col >= 0) {
  264. data = this.data[attr][row][col].paragraph_list_parameter;
  265. }
  266. if (!Array.isArray(data.pinyin_proofread_word_list)) {
  267. data.pinyin_proofread_word_list = [];
  268. }
  269. data.pinyin_proofread_word_list.push({
  270. paragraph_index: i,
  271. sentence_index: j,
  272. word_index: k,
  273. word: text,
  274. pinyin,
  275. });
  276. let listItem = null;
  277. if (row === -1) {
  278. listItem = this.data.paragraph_list?.[i]?.[j]?.[k];
  279. } else if (row >= 0 && col === -1) {
  280. listItem = this.data[attr]?.[row]?.paragraph_list?.[i]?.[j]?.[k];
  281. } else if (row >= 0 && col >= 0) {
  282. listItem = this.data[attr]?.[row]?.[col]?.paragraph_list?.[i]?.[j]?.[k];
  283. }
  284. if (listItem) {
  285. if (pinyin) listItem.pinyin = pinyin;
  286. if (activeTextStyle) listItem.activeTextStyle = activeTextStyle;
  287. }
  288. },
  289. },
  290. };
  291. export default mixin;