ModuleMixin.js 8.3 KB

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