Matching.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <template>
  2. <ModuleBase :type="data.type">
  3. <template #content>
  4. <ul class="option-list">
  5. <li v-for="(li, i) in data.option_list" :key="i" class="option-item">
  6. <div v-for="(item, j) in li" :key="item.mark" class="option">
  7. <span class="serial-number">{{ computeOptionMethods[data.property.serial_number_type_list[j]](i) }}</span>
  8. <span class="option-content">
  9. <RichText
  10. ref="richText"
  11. v-model="item.content"
  12. placeholder="请输入"
  13. :inline="true"
  14. :height="32"
  15. :font-size="data?.unified_attrib?.font_size"
  16. :font-family="data?.unified_attrib?.font"
  17. :is-view-pinyin="isEnable(data.property.view_pinyin)"
  18. @createParsedTextInfoPinyin="handleParsedTextPinyin($event, i, j)"
  19. />
  20. </span>
  21. <span class="multilingual" @click="openMultilingual(i, j)">
  22. <SvgIcon icon-class="multilingual" class-name="multilingual" width="12" height="12" />
  23. </span>
  24. </div>
  25. </li>
  26. </ul>
  27. <div class="answer-tips">答案:</div>
  28. <ul class="answer-list">
  29. <li v-for="(li, i) in data.answer.answer_list" :key="i" class="answer-item">
  30. <template v-for="(item, j) in li">
  31. <span v-if="j === 0" :key="`zero-${j}-${item.mark}`">{{ answerList[j][i].label }}</span>
  32. <el-select v-if="j === 1 || j === 2" :key="`select-${j}`" v-model="item.mark" multiple placeholder="请选择">
  33. <el-option
  34. v-for="answer in answerList[j]"
  35. :key="answer.value"
  36. :label="answer.label"
  37. :value="answer.value"
  38. />
  39. </el-select>
  40. <span v-if="j === 1 && data.property.column_num === 3" :key="`one-${j}-${item.mark}`">
  41. {{ answerList[j][i].label }}
  42. </span>
  43. </template>
  44. </li>
  45. </ul>
  46. <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">拼音效果</el-divider>
  47. <template v-if="isEnable(data.property.view_pinyin)">
  48. <div v-for="(item, i) in data.option_list" :key="i" class="pinyin-text-list">
  49. <PinyinText
  50. v-for="(li, j) in item"
  51. :key="`${i}-${j}`"
  52. ref="PinyinText"
  53. :paragraph-list="li.paragraph_list"
  54. :pinyin-position="data.property.pinyin_position"
  55. @fillCorrectPinyin="fillCorrectPinyin($event, i, j)"
  56. />
  57. </div>
  58. </template>
  59. <MultilingualFill
  60. v-if="curSelectRow !== -1 && curSelectColumn !== -1"
  61. :visible.sync="multilingualVisible"
  62. :text="data.option_list[curSelectRow][curSelectColumn].content"
  63. :translations="data.option_list[curSelectRow][curSelectColumn].multilingual"
  64. @SubmitTranslation="handleMultilingualTranslation"
  65. />
  66. </template>
  67. </ModuleBase>
  68. </template>
  69. <script>
  70. import ModuleMixin from '../../common/ModuleMixin';
  71. import PinyinText from '@/components/PinyinText.vue';
  72. import { getMatchingData, getOption, getOptionItem } from '@/views/book/courseware/data/matching';
  73. import { computeOptionMethods, serialNumberTypeList } from '@/views/book/courseware/data/common';
  74. export default {
  75. name: 'MatchingPage',
  76. components: {
  77. PinyinText,
  78. },
  79. mixins: [ModuleMixin],
  80. data() {
  81. return {
  82. data: getMatchingData(),
  83. computeOptionMethods,
  84. serialNumberTypeList,
  85. curSelectRow: -1,
  86. curSelectColumn: -1,
  87. // 用于记录 data.option_list 中 content 的签名
  88. optionContentSignature: '',
  89. };
  90. },
  91. computed: {
  92. answerList() {
  93. let list = Array.from({ length: this.data.property.column_num }, () => []);
  94. for (let i = 0; i < this.data.option_list.length; i++) {
  95. for (let j = 0; j < this.data.option_list[i].length; j++) {
  96. list[j].push({
  97. label: computeOptionMethods[this.data.property.serial_number_type_list[j]](i),
  98. value: this.data.option_list[i][j].mark,
  99. });
  100. }
  101. }
  102. return list;
  103. },
  104. },
  105. watch: {
  106. 'data.property.column_num': [
  107. {
  108. handler(val) {
  109. let optionNum = this.data.option_list[0].length;
  110. if (val > optionNum) {
  111. // 修改序号类型列表
  112. this.data.property.serial_number_type_list.push(
  113. serialNumberTypeList[val]?.value || serialNumberTypeList[0].value,
  114. );
  115. // 增加选项
  116. for (let i = 0; i < this.data.option_list.length; i++) {
  117. this.data.option_list[i].push(getOptionItem());
  118. }
  119. // 修改答案列表
  120. this.data.answer.answer_list.forEach((li) => {
  121. li.push({ mark: [] });
  122. });
  123. return;
  124. }
  125. if (val < optionNum) {
  126. this.data.property.serial_number_type_list.splice(-1, 1);
  127. this.data.option_list.forEach((li) => {
  128. li.splice(-1, 1);
  129. });
  130. // 修改答案列表
  131. this.data.answer.answer_list.forEach((li) => {
  132. li.splice(-1, 1);
  133. });
  134. }
  135. },
  136. },
  137. 'handlerMindMap',
  138. ],
  139. 'data.property.row_num': [
  140. {
  141. handler(val) {
  142. let optionNum = this.data.option_list.length;
  143. if (val > optionNum) {
  144. for (let i = 0; i < val - optionNum; i++) {
  145. this.data.option_list.push(getOption(this.data.property.column_num));
  146. }
  147. // 增加答案列表
  148. this.data.answer.answer_list.push(
  149. Array.from({ length: this.data.property.column_num }, () => ({ mark: [] })),
  150. );
  151. // 将答案列表最后一项的第一个元素设置进答案列表第一项
  152. this.data.answer.answer_list[this.data.answer.answer_list.length - 1][0] = {
  153. mark: this.data.option_list[this.data.option_list.length - 1][0].mark,
  154. };
  155. return;
  156. }
  157. if (val < optionNum) {
  158. this.data.option_list.splice(val);
  159. this.data.answer.answer_list.splice(val);
  160. }
  161. },
  162. },
  163. 'handlerMindMap',
  164. ],
  165. 'data.property.view_pinyin': {
  166. handler(val) {
  167. if (!this.isEnable(val)) {
  168. this.data.option_list.forEach((item) => {
  169. item.paragraph_list = [];
  170. item.paragraph_list_parameter = {
  171. text: '',
  172. pinyin_proofread_word_list: [],
  173. };
  174. });
  175. return;
  176. }
  177. if (this.data.option_list?.length > 0 && this.data.option_list[0].paragraph_list?.length > 0) return;
  178. this.data.option_list.forEach((item, i) => {
  179. item.forEach((option, j) => {
  180. const text = option.content.replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ');
  181. if (!text) return;
  182. option.paragraph_list_parameter.text = text;
  183. this.createParsedTextInfoPinyin(text, i, j);
  184. });
  185. });
  186. },
  187. },
  188. },
  189. methods: {
  190. /**
  191. * @description 处理思维导图
  192. */
  193. handlerMindMap() {
  194. this.data.mind_map.node_list = [
  195. { name: `${this.data.property.row_num}行${this.data.property.column_num}列连线组件` },
  196. ];
  197. },
  198. openMultilingual(i, j) {
  199. this.curSelectRow = i;
  200. this.curSelectColumn = j;
  201. this.$nextTick(() => {
  202. this.multilingualVisible = true;
  203. });
  204. },
  205. handleMultilingualTranslation(translations) {
  206. this.$set(this.data.option_list[this.curSelectRow][this.curSelectColumn], 'multilingual', translations);
  207. this.curSelectColumn = -1;
  208. this.curSelectRow = -1;
  209. },
  210. handleParsedTextPinyin(text, i, j) {
  211. if (!this.isEnable(this.data.property.view_pinyin)) return;
  212. if (this.data.option_list?.length > 0 && this.data.option_list[0].paragraph_list?.length > 0) return;
  213. if (!text) return;
  214. this.data.option_list[i][j].paragraph_list_parameter.text = text.replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ');
  215. this.createParsedTextInfoPinyin(text, i, j);
  216. },
  217. },
  218. };
  219. </script>
  220. <style lang="scss" scoped>
  221. @use '@/styles/mixin.scss' as *;
  222. .option-list {
  223. display: flex;
  224. flex-direction: column;
  225. row-gap: 16px;
  226. .option-item {
  227. display: flex;
  228. column-gap: 16px;
  229. .option {
  230. display: flex;
  231. flex: 1;
  232. column-gap: 4px;
  233. align-items: center;
  234. .serial-number {
  235. @include serial-number(32px);
  236. }
  237. .option-content {
  238. flex: 1;
  239. padding: 0 4px;
  240. margin-left: 4px;
  241. background-color: $fill-color;
  242. }
  243. .multilingual {
  244. margin-left: 2px;
  245. line-height: 12px;
  246. cursor: pointer;
  247. }
  248. }
  249. }
  250. }
  251. .answer-tips {
  252. margin: 16px 0;
  253. }
  254. .answer-list {
  255. display: flex;
  256. flex-direction: column;
  257. row-gap: 16px;
  258. .answer-item {
  259. display: flex;
  260. column-gap: 16px;
  261. align-items: center;
  262. span {
  263. display: inline-block;
  264. height: 32px;
  265. padding: 4px 8px;
  266. color: #c9cdd4;
  267. background-color: $fill-color;
  268. }
  269. .el-select {
  270. flex: 1;
  271. }
  272. }
  273. }
  274. .pinyin-text-list {
  275. display: flex;
  276. column-gap: 24px;
  277. align-items: center;
  278. + .pinyin-text-list {
  279. margin-top: 4px;
  280. }
  281. }
  282. </style>