ChooseTonePreview.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="choosetone-preview">
  4. <div class="stem">
  5. <span class="question-number">{{ data.property.question_number }}.</span>
  6. <span v-html="sanitizeHTML(data.stem)"></span>
  7. </div>
  8. <div v-if="data.property.is_enable_description" class="description">{{ data.description }}</div>
  9. <div class="option-list">
  10. <li v-for="(item, i) in data.option_list" :key="i" :class="['option-item', { active: isAnswer(item.mark) }]">
  11. <span>{{ computeOptionMethods[data.option_number_show_mode](i) }}. </span>
  12. <AudioPlay v-if="item.audio_file_id" :file-id="item.audio_file_id" />
  13. <div class="option-content">
  14. <template v-if="data.property.tone_type === 'select'">
  15. <span
  16. v-for="(itemc, indexc) in con_preview[i].item_con"
  17. :key="indexc"
  18. :class="['item-con']"
  19. @click="con_preview[i].item_active_index = indexc"
  20. >
  21. {{ itemc }}
  22. </span>
  23. </template>
  24. </div>
  25. <span
  26. v-for="({ img, value }, j) in toneList"
  27. :key="j"
  28. :class="[
  29. 'tone',
  30. con_preview[i].user_answer[con_preview[i].item_active_index].select_tone === value ? 'active' : '',
  31. ]"
  32. @click="chooseTone(con_preview[i], value)"
  33. >
  34. <SvgIcon :icon-class="img" />
  35. </span>
  36. </li>
  37. </div>
  38. </div>
  39. </template>
  40. <script>
  41. import { computeOptionMethods } from '@/views/exercise_questions/data/common';
  42. import PreviewMixin from './components/PreviewMixin';
  43. export default {
  44. name: 'ChooseTonePreview',
  45. mixins: [PreviewMixin],
  46. data() {
  47. return {
  48. computeOptionMethods,
  49. toneList: [
  50. { value: '1', label: '一声', img: 'first-tone' },
  51. { value: '2', label: '二声', img: 'second-tone' },
  52. { value: '3', label: '三声', img: 'third-tone' },
  53. { value: '4', label: '四声', img: 'fourth-tone' },
  54. { value: '0', label: '轻声', img: 'neutral-tone' },
  55. ],
  56. con_preview: [],
  57. tone_data: [
  58. ['ā', 'á', 'ǎ', 'à', 'a'],
  59. ['ō', 'ó', 'ǒ', 'ò', 'o'],
  60. ['ē', 'é', 'ě', 'è', 'e'],
  61. ['ī', 'í', 'ǐ', 'ì', 'i'],
  62. ['ū', 'ú', 'ǔ', 'ù', 'u'],
  63. ['ǖ', 'ǘ', 'ǚ', 'ǜ', 'ü'],
  64. ['Ā', 'Á', 'Â', 'À', 'A'],
  65. ['Ō', 'Ó', 'Ô', 'Ò', 'O'],
  66. ['Ē', 'É', 'Ê', 'È', 'E'],
  67. ['Ī', 'Í', 'Î', 'Ì', 'I'],
  68. ['Ū', 'Ú', 'Û', 'Ù', 'U'],
  69. ],
  70. final_con: '',
  71. };
  72. },
  73. created() {
  74. this.handleData();
  75. },
  76. methods: {
  77. isAnswer(mark, option_type) {
  78. return this.answer.select_list.some((li) => li.mark === mark && li.option_type === option_type);
  79. },
  80. chooseTone(item, value) {
  81. item.user_answer[item.item_active_index].select_tone = value;
  82. if (this.data.property.tone_type === 'dimension') {
  83. item.user_answer[item.item_active_index].select_letter = item.user_answer[item.item_active_index].active_letter;
  84. }
  85. this.handleReplaceTone(item.item_con_yuan[item.item_active_index] + value);
  86. setTimeout(() => {
  87. item.item_con[item.item_active_index] = this.final_con;
  88. this.$forceUpdate();
  89. }, 100);
  90. },
  91. // 处理数据
  92. handleData() {
  93. this.con_preview = [];
  94. this.data.option_list.forEach((item) => {
  95. let con_arr = item.content_preview.split(' ');
  96. let user_answer = [];
  97. con_arr.forEach(() => {
  98. user_answer.push({
  99. select_tone: null,
  100. select_letter: '',
  101. });
  102. });
  103. let obj = {
  104. item_con: con_arr,
  105. item_con_yuan: JSON.parse(JSON.stringify(con_arr)),
  106. mark: item.mark,
  107. user_answer,
  108. item_active_index: 0,
  109. active_letter: '',
  110. };
  111. this.con_preview.push(obj);
  112. });
  113. console.log(this.con_preview);
  114. },
  115. handleReplaceTone(e) {
  116. this.$nextTick(() => {
  117. let value = e;
  118. this.resArr = [];
  119. if (value) {
  120. let reg = /\s+/g;
  121. let valueArr = value.split(reg);
  122. valueArr.forEach((item) => {
  123. this.handleValue(item);
  124. });
  125. let str = '';
  126. setTimeout(() => {
  127. this.resArr.forEach((item) => {
  128. str += ' ';
  129. item.forEach((sItem) => {
  130. if (sItem.number && sItem.con) {
  131. let number = Number(sItem.number);
  132. let con = sItem.con;
  133. let word = this.addTone(number, con);
  134. str += word;
  135. } else if (sItem.number) {
  136. str += sItem.number;
  137. } else if (sItem.con) {
  138. str += ` ${sItem.con} `;
  139. }
  140. });
  141. });
  142. this.final_con = str.trim();
  143. }, 10);
  144. }
  145. });
  146. },
  147. handleValue(valItem) {
  148. let reg = /\d/;
  149. let reg2 = /[A-Za-z]+\d/g;
  150. let numList = [];
  151. let valArr = valItem.split('');
  152. if (reg2.test(valItem)) {
  153. for (let i = 0; i < valArr.length; i++) {
  154. let item = valItem[i];
  155. if (reg.test(item)) {
  156. let numIndex = numList.length === 0 ? 0 : numList[numList.length - 1].index;
  157. let con = valItem.substring(numIndex, i);
  158. con = con.replace(/\d/g, '');
  159. let obj = {
  160. index: i,
  161. number: item,
  162. con,
  163. isTran: true,
  164. };
  165. numList.push(obj);
  166. }
  167. }
  168. } else {
  169. numList = [];
  170. }
  171. if (numList.length === 0) {
  172. this.resArr.push([{ con: valItem }]);
  173. } else {
  174. this.resArr.push(numList);
  175. }
  176. },
  177. addTone(number, con) {
  178. let zmList = ['a', 'o', 'e', 'i', 'u', 'v', 'A', 'O', 'E', 'I', 'U'];
  179. let cons = con;
  180. if (number) {
  181. for (let i = 0; i < zmList.length; i++) {
  182. let zm = zmList[i];
  183. if (con.indexOf(zm) > -1) {
  184. let zm2 = this.tone_data[i][number - 1];
  185. if (con.indexOf('iu') > -1) {
  186. zm2 = this.tone_data[4][number - 1];
  187. cons = con.replace('u', zm2);
  188. } else if (con.indexOf('ui') > -1) {
  189. zm2 = this.tone_data[3][number - 1];
  190. cons = con.replace('i', zm2);
  191. } else if (
  192. con.indexOf('yv') > -1 ||
  193. con.indexOf('jv') > -1 ||
  194. con.indexOf('qv') > -1 ||
  195. con.indexOf('xv') > -1
  196. ) {
  197. zm2 = this.tone_data[4][number - 1];
  198. cons = con.replace('v', zm2);
  199. } else {
  200. cons = con.replace(zm, zm2);
  201. }
  202. break;
  203. }
  204. }
  205. }
  206. return cons;
  207. },
  208. },
  209. };
  210. </script>
  211. <style lang="scss" scoped>
  212. @use '@/styles/mixin.scss' as *;
  213. .choosetone-preview {
  214. @include preview;
  215. .option-list {
  216. display: flex;
  217. flex-direction: column;
  218. flex-flow: wrap;
  219. row-gap: 16px;
  220. .option-item {
  221. display: flex;
  222. column-gap: 16px;
  223. align-items: center;
  224. width: 45%;
  225. margin-right: 5%;
  226. .option-content {
  227. padding: 12px 24px;
  228. color: #706f78;
  229. background-color: #f9f8f9;
  230. border-radius: 40px;
  231. }
  232. .item-con {
  233. cursor: pointer;
  234. }
  235. .tone {
  236. width: 32px;
  237. height: 32px;
  238. padding: 8px;
  239. font-size: 0;
  240. text-align: center;
  241. cursor: pointer;
  242. &.active {
  243. background: #dfe9fd;
  244. border-radius: 16px;
  245. }
  246. }
  247. }
  248. }
  249. }
  250. </style>