Notes.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <template>
  2. <ModuleBase :type="data.type">
  3. <template #content>
  4. <!-- <label>标题:</label>
  5. <RichText
  6. ref="richText"
  7. v-model="data.title_con"
  8. :inline="true"
  9. :placeholder="'输入标题'"
  10. :font-size="data?.unified_attrib?.font_size"
  11. :font-family="data?.unified_attrib?.font"
  12. toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
  13. class="title-box"
  14. /> -->
  15. <el-table :key="refreshKey" :data="data.option" border style="width: 100%">
  16. <el-table-column fixed prop="number" label="序号" width="70">
  17. <template slot-scope="scope">
  18. <el-input v-model="scope.row.number" />
  19. </template>
  20. </el-table-column>
  21. <el-table-column fixed prop="con" label="内容" width="200">
  22. <template slot-scope="scope">
  23. <RichText
  24. ref="richText"
  25. v-model="scope.row.con"
  26. :inline="true"
  27. :item-index="scope.$index"
  28. :font-size="data?.unified_attrib?.font_size"
  29. :font-family="data?.unified_attrib?.font"
  30. toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
  31. @handleRichTextBlur="handleBlurCon"
  32. />
  33. </template>
  34. </el-table-column>
  35. <el-table-column v-if="isEnable(data.property.view_pinyin)" prop="con" label="拼音" width="150">
  36. <template slot-scope="scope">
  37. <RichText
  38. ref="richText"
  39. v-model="scope.row.pinyin"
  40. :inline="true"
  41. :font-size="data?.unified_attrib?.font_size"
  42. :font-family="data?.unified_attrib?.font"
  43. toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
  44. />
  45. </template>
  46. </el-table-column>
  47. <el-table-column prop="interpret" label="翻译" width="200">
  48. <template slot-scope="scope">
  49. <RichText
  50. ref="richText"
  51. v-model="scope.row.interpret"
  52. :inline="true"
  53. :font-size="data?.unified_attrib?.font_size"
  54. :font-family="data?.unified_attrib?.font"
  55. toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
  56. />
  57. </template>
  58. </el-table-column>
  59. <el-table-column prop="note" label="注释" width="200">
  60. <template slot-scope="scope">
  61. <RichText
  62. ref="richText"
  63. v-model="scope.row.note"
  64. :inline="true"
  65. :font-size="data?.unified_attrib?.font_size"
  66. :font-family="data?.unified_attrib?.font"
  67. toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
  68. />
  69. </template>
  70. </el-table-column>
  71. <el-table-column prop="img_list" label="图片" width="300">
  72. <template slot-scope="scope">
  73. <UploadPicture
  74. :file-info="scope.row.file_list[0]"
  75. :item-index="scope.$index"
  76. :show-upload="!scope.row.file_list[0]"
  77. @upload="uploadPic"
  78. @deleteFile="deletePic"
  79. />
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="操作" width="150">
  83. <template slot-scope="scope">
  84. <el-button size="mini" type="text" @click="handleDelete(scope.$index)">删除</el-button>
  85. <el-button size="mini" type="text" @click="moveElement(scope.row, scope.$index, 'up')">上移</el-button>
  86. <el-button size="mini" type="text" @click="moveElement(scope.row, scope.$index, 'down')">下移</el-button>
  87. </template>
  88. </el-table-column>
  89. </el-table>
  90. <el-button icon="el-icon-plus" style="margin: 24px 0" @click="addElement">增加一个</el-button>
  91. <el-button @click="handleMultilingual">多语言</el-button>
  92. <MultilingualFill
  93. :visible.sync="multilingualVisible"
  94. :text="multilingualText"
  95. :translations="data.multilingual"
  96. @SubmitTranslation="handleMultilingualTranslation"
  97. />
  98. </template>
  99. </ModuleBase>
  100. </template>
  101. <script>
  102. import { isEnable } from '@/views/book/courseware/data/common';
  103. import ModuleMixin from '../../common/ModuleMixin';
  104. import { PinyinBuild_OldFormat } from '@/api/book';
  105. import UploadPicture from '../new_word/components/UploadPicture.vue';
  106. import { getNotesData, getOption } from '@/views/book/courseware/data/notes';
  107. import cnchar from 'cnchar';
  108. export default {
  109. name: 'NotesPage',
  110. components: { UploadPicture },
  111. mixins: [ModuleMixin],
  112. data() {
  113. return {
  114. isEnable,
  115. data: getNotesData(),
  116. multilingualText: '',
  117. refreshKey: '',
  118. };
  119. },
  120. watch: {
  121. 'data.option': 'handleMindMap',
  122. 'data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case': {
  123. handler(val, oldVal) {
  124. if (val === oldVal) return;
  125. if (isEnable(this.data.property.view_pinyin)) {
  126. this.handlePinyin();
  127. }
  128. },
  129. deep: true,
  130. },
  131. },
  132. methods: {
  133. // 删除行
  134. handleDelete(index) {
  135. this.data.option.splice(index, 1);
  136. },
  137. // 上移下移
  138. moveElement(dItem, index, type) {
  139. let obj = JSON.parse(JSON.stringify(dItem));
  140. if (type === 'up' && index > 0) {
  141. this.data.option.splice(index - 1, 0, obj);
  142. this.data.option.splice(index + 1, 1);
  143. }
  144. if (type === 'down' && index < this.data.option.length - 1) {
  145. this.data.option[index] = this.data.option.splice(index + 1, 1, this.data.option[index])[0];
  146. }
  147. },
  148. // 增加
  149. addElement() {
  150. this.data.option.push(getOption());
  151. },
  152. handleMindMap() {
  153. // 思维导图数据
  154. let node_list = [];
  155. this.data.option.forEach((item) => {
  156. node_list.push({
  157. name: item.con.replace(/<[^>]*>?/gm, ''),
  158. id: Math.random().toString(36).substring(2, 12),
  159. });
  160. });
  161. this.data.mind_map.node_list = node_list;
  162. },
  163. handleBlurCon(i) {
  164. let text = this.data.option[i].con.replace(/<[^>]+>/g, '');
  165. this.data.option[i].pinyin = cnchar
  166. .spell(
  167. text,
  168. 'array',
  169. this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case === 'true' ? 'high' : 'low',
  170. 'tone',
  171. )
  172. .join(' ');
  173. this.handleMindMap();
  174. },
  175. handlePinyin() {
  176. this.data.option.forEach((item) => {
  177. let text = item.con.replace(/<[^>]+>/g, '');
  178. item.pinyin = cnchar
  179. .spell(
  180. text,
  181. 'array',
  182. this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case === 'true' ? 'high' : 'low',
  183. 'tone',
  184. )
  185. .join(' ');
  186. });
  187. },
  188. handleMultilingual() {
  189. this.multilingualText = this.data.title_con ? this.data.title_con : '<p>&nbsp;</p>';
  190. this.data.option.forEach((item) => {
  191. this.multilingualText += item.con ? item.con : '<p>&nbsp;</p>';
  192. this.multilingualText += item.note ? item.note : '<p>&nbsp;</p>';
  193. });
  194. this.multilingualVisible = true;
  195. },
  196. // 获取拼音解析文本
  197. createParsedTextInfoPinyin(text) {
  198. if (text === '') {
  199. this.data.paragraph_list_parameter.pinyin_proofread_word_list = [];
  200. return;
  201. }
  202. this.data.paragraph_list_parameter.text = text.replace(/<[^>]+>/g, '');
  203. this.data.paragraph_list_parameter.is_first_sentence_first_hz_pinyin_first_char_upper_case =
  204. this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case;
  205. PinyinBuild_OldFormat(this.data.paragraph_list_parameter).then((res) => {
  206. if (res.parsed_text) {
  207. const mergedData = res.parsed_text.paragraph_list.map((outerArr, i) =>
  208. outerArr.map((innerArr, j) =>
  209. innerArr.map((newItem, k) => {
  210. // 从 originalData 中找到对应的项
  211. const originalItem = this.data.paragraph_list[i]?.[j]?.[k];
  212. // 如果 originalItem 有 activeTextStyle,就合并到 newItem
  213. if (originalItem?.activeTextStyle) {
  214. return {
  215. ...newItem,
  216. activeTextStyle: originalItem.activeTextStyle,
  217. };
  218. }
  219. // 否则直接返回 newItem
  220. return newItem;
  221. }),
  222. ),
  223. );
  224. this.data.paragraph_list = mergedData;
  225. let pinyin_index = 0;
  226. this.data.option.forEach((items, index) => {
  227. items.model_pinyin = [];
  228. if (items.content && mergedData[pinyin_index] && mergedData[pinyin_index][0]) {
  229. mergedData[pinyin_index][0].forEach((itemP) => {
  230. items.model_pinyin.push(itemP);
  231. });
  232. pinyin_index++;
  233. }
  234. });
  235. }
  236. });
  237. },
  238. // 填充校对后的拼音
  239. fillCorrectPinyin({ selectContent: { text, pinyin, activeTextStyle }, i, j, k }) {
  240. this.data.paragraph_list_parameter.pinyin_proofread_word_list.push({
  241. paragraph_index: i,
  242. sentence_index: j,
  243. word_index: k,
  244. word: text,
  245. pinyin,
  246. });
  247. if (pinyin) this.data.paragraph_list[i][j][k].pinyin = pinyin;
  248. if (activeTextStyle) this.data.paragraph_list[i][j][k].activeTextStyle = activeTextStyle;
  249. },
  250. uploadPic(file_id, index, file) {
  251. this.data.option[index].file_list[0] = file;
  252. this.data.file_id_list.push(file_id);
  253. this.refreshKey = Math.random();
  254. },
  255. deletePic(file_id, index) {
  256. this.data.option[index].file_list[0] = '';
  257. this.data.file_id_list = this.data.file_id_list.filter((item) => item !== file_id);
  258. },
  259. },
  260. };
  261. </script>
  262. <style lang="scss" scoped>
  263. .upload-file {
  264. display: flex;
  265. column-gap: 12px;
  266. align-items: center;
  267. margin: 8px 0;
  268. .file-name {
  269. display: flex;
  270. column-gap: 14px;
  271. align-items: center;
  272. justify-content: space-between;
  273. max-width: 360px;
  274. padding: 8px 12px;
  275. font-size: 14px;
  276. color: #1d2129;
  277. background-color: #f7f8fa;
  278. span {
  279. display: flex;
  280. column-gap: 14px;
  281. align-items: center;
  282. }
  283. }
  284. .svg-icon {
  285. cursor: pointer;
  286. }
  287. }
  288. .title-box {
  289. padding: 5px;
  290. margin: 5px 0;
  291. background-color: #f2f3f5;
  292. border: 1px solid #f2f3f5;
  293. border-radius: 4px;
  294. :deep p {
  295. margin: 0;
  296. }
  297. }
  298. </style>
  299. <style lang="scss">
  300. .tox .tox-editor-header {
  301. z-index: 3 !important;
  302. }
  303. </style>