PinyinBasePreview.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="pinyin-preview" :style="getAreaStyle()">
  4. <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
  5. <div class="main">
  6. <AudioFill v-if="data.audio_file_id" :file-id="data.audio_file_id" />
  7. <div
  8. class="option-content"
  9. :class="[isJudgingRightWrong ? (con_preview[0].all_right ? 'all-right' : 'has-error') : '']"
  10. >
  11. <span v-if="data.content_hz" class="items-hz">{{ data.content_hz }}</span>
  12. <template v-if="data.property.answer_mode === 'select'">
  13. <span
  14. v-for="(itemc, indexc) in con_preview[0].item_con"
  15. :key="indexc"
  16. :class="[
  17. 'item-con',
  18. active_index_str === 0 + '-' + indexc ? 'active' : '',
  19. isJudgingRightWrong && !con_preview[0].user_answer[indexc].is_right ? 'error' : '',
  20. ]"
  21. @click="
  22. con_preview[0].item_active_index = indexc;
  23. active_index_str = 0 + '-' + indexc;
  24. "
  25. >
  26. {{ itemc }}
  27. </span>
  28. </template>
  29. <template v-else>
  30. <span v-for="(itemc, indexc) in con_preview[0].item_con" :key="indexc" class="items-box">
  31. <span
  32. v-for="(itemi, indexi) in itemc"
  33. :key="indexi"
  34. :class="[
  35. 'items-con',
  36. active_index_str === 0 + '-' + indexc + '-' + indexi ? 'active' : '',
  37. isJudgingRightWrong &&
  38. !con_preview[0].user_answer[indexc].is_right &&
  39. con_preview[0].user_answer[indexc].select_index_submit === indexi
  40. ? 'error'
  41. : '',
  42. isJudgingRightWrong &&
  43. !con_preview[0].user_answer[indexc].is_right &&
  44. con_preview[0].user_answer[indexc].right_index === indexi
  45. ? 'right'
  46. : '',
  47. ]"
  48. @click="handleSelectItemTone(0, indexc, indexi, con_preview[0].item_con_yuan[indexc][indexi])"
  49. >{{ itemi }}</span
  50. >
  51. </span>
  52. </template>
  53. </div>
  54. <span
  55. v-for="({ img, value }, j) in toneList"
  56. :key="j"
  57. :class="[
  58. 'tone',
  59. data.property.answer_mode === 'select' &&
  60. con_preview[0].user_answer[con_preview[0].item_active_index] &&
  61. con_preview[0].user_answer[con_preview[0].item_active_index].select_tone === value
  62. ? 'active'
  63. : data.property.answer_mode === 'label' &&
  64. con_preview[0].user_answer[con_preview[0].item_active_index] &&
  65. con_preview[0].user_answer[con_preview[0].item_active_index].select_tone === value &&
  66. con_preview[0].user_answer[con_preview[0].item_active_index].select_letter === active_letter &&
  67. select_item_index === 0
  68. ? 'active'
  69. : '',
  70. (isJudgingRightWrong &&
  71. con_preview[0].user_answer[con_preview[0].item_active_index].right_answer === value &&
  72. con_preview[0].user_answer[con_preview[0].item_active_index].select_index_submit !==
  73. con_preview[0].user_answer[con_preview[0].item_active_index].right_answer &&
  74. data.property.answer_mode === 'select') ||
  75. (isJudgingRightWrong &&
  76. data.property.answer_mode === 'label' &&
  77. con_preview[0].user_answer[con_preview[0].item_active_index].right_answer === value &&
  78. con_preview[0].user_answer[con_preview[0].item_active_index].right_index ===
  79. con_preview[0].user_answer[con_preview[0].item_active_index].select_index &&
  80. select_item_index === 0)
  81. ? 'right'
  82. : '',
  83. ]"
  84. @click="chooseTone(con_preview[0], value)"
  85. >
  86. <SvgIcon :icon-class="img" />
  87. </span>
  88. </div>
  89. </div>
  90. </template>
  91. <script>
  92. import { getPinyinBaseData, arrangeTypeList, audioPositionList } from '@/views/book/courseware/data/pinyinBase';
  93. import PreviewMixin from '../common/PreviewMixin';
  94. import AudioFill from '../fill/components/AudioFillPlay.vue';
  95. import SoundRecord from '../../common/SoundRecord.vue';
  96. export default {
  97. name: 'PinyinBasePreview',
  98. components: {
  99. AudioFill,
  100. SoundRecord,
  101. },
  102. mixins: [PreviewMixin],
  103. watch: {
  104. data: {
  105. handler(val) {
  106. // if (!val || this.data.type !== 'choose_tone') return;
  107. this.handleData();
  108. },
  109. deep: true,
  110. immediate: true,
  111. },
  112. isJudgingRightWrong: {
  113. handler(val) {
  114. if (!val) return;
  115. this.judgeRight();
  116. },
  117. immediate: true,
  118. },
  119. },
  120. data() {
  121. return {
  122. data: getPinyinBaseData(),
  123. arrangeTypeList,
  124. audioPositionList,
  125. toneList: [
  126. { value: '1', label: '一声', img: 'first-tone' },
  127. { value: '2', label: '二声', img: 'second-tone' },
  128. { value: '3', label: '三声', img: 'third-tone' },
  129. { value: '4', label: '四声', img: 'fourth-tone' },
  130. // { value: '0', label: '轻声', img: 'neutral-tone' },
  131. ],
  132. con_preview: [],
  133. tone_data: [
  134. ['ā', 'á', 'ǎ', 'à', 'a'],
  135. ['ō', 'ó', 'ǒ', 'ò', 'o'],
  136. ['ē', 'é', 'ě', 'è', 'e'],
  137. ['ī', 'í', 'ǐ', 'ì', 'i'],
  138. ['ū', 'ú', 'ǔ', 'ù', 'u'],
  139. ['ǖ', 'ǘ', 'ǚ', 'ǜ', 'ü'],
  140. ['ǖ', 'ǘ', 'ǚ', 'ǜ', 'ü'],
  141. ['Ā', 'Á', 'Â', 'À', 'A'],
  142. ['Ō', 'Ó', 'Ô', 'Ò', 'O'],
  143. ['Ē', 'É', 'Ê', 'È', 'E'],
  144. ['Ī', 'Í', 'Î', 'Ì', 'I'],
  145. ['Ū', 'Ú', 'Û', 'Ù', 'U'],
  146. ],
  147. final_con: '',
  148. active_index_str: '', // 高亮索引的字符串
  149. active_letter: '', // 选中字母的值
  150. active_letter_index: 0, // 选择字母索引
  151. select_item_index: 0, // 小题索引
  152. };
  153. },
  154. created() {
  155. // console.log(this.data);
  156. },
  157. methods: {
  158. chooseTone(item, value) {
  159. if (this.disabled) return;
  160. if (!this.active_letter && this.data.property.answer_mode === 'label') return;
  161. if (
  162. item.user_answer[item.item_active_index].select_tone &&
  163. item.user_answer[item.item_active_index].select_tone === value &&
  164. this.data.property.answer_mode === 'label' &&
  165. item.user_answer[item.item_active_index].select_letter === this.active_letter
  166. ) {
  167. item.user_answer[item.item_active_index].select_tone = '';
  168. this.handleReplaceTone(this.active_letter + 0);
  169. setTimeout(() => {
  170. let new_con = item.item_con_yuan[item.item_active_index].split(this.active_letter);
  171. item.item_con[item.item_active_index] = new_con[0] + this.final_con + new_con[1];
  172. this.$forceUpdate();
  173. this.answer.answer_list[0].value[item.item_active_index] = new_con[0] + this.active_letter + new_con[1];
  174. }, 100);
  175. } else if (
  176. item.user_answer[item.item_active_index].select_tone &&
  177. item.user_answer[item.item_active_index].select_tone === value &&
  178. this.data.property.answer_mode === 'select'
  179. ) {
  180. item.user_answer[item.item_active_index].select_tone = '';
  181. this.handleReplaceTone(item.item_con_yuan[item.item_active_index] + 0);
  182. setTimeout(() => {
  183. item.item_con[item.item_active_index] = this.final_con;
  184. this.$forceUpdate();
  185. }, 100);
  186. this.answer.answer_list[0].value[item.item_active_index] = '';
  187. } else {
  188. item.user_answer[item.item_active_index].select_tone = value;
  189. if (this.data.property.answer_mode === 'label') {
  190. item.user_answer[item.item_active_index].select_letter = this.active_letter;
  191. this.active_index_str = `${0}-${item.item_active_index}-${this.active_letter_index}`;
  192. this.handleReplaceTone(this.active_letter + value);
  193. setTimeout(() => {
  194. let new_con = item.item_con_yuan[item.item_active_index].split(this.active_letter);
  195. item.item_con[item.item_active_index] = new_con[0] + this.final_con + new_con[1];
  196. this.$forceUpdate();
  197. this.answer.answer_list[0].value[item.item_active_index] =
  198. new_con[0] + this.active_letter + value + new_con[1];
  199. }, 100);
  200. } else {
  201. this.active_index_str = `${0}-${item.item_active_index}`;
  202. this.handleReplaceTone(item.item_con_yuan[item.item_active_index] + value);
  203. setTimeout(() => {
  204. item.item_con[item.item_active_index] = this.final_con;
  205. this.$forceUpdate();
  206. }, 100);
  207. this.answer.answer_list[0].value[item.item_active_index] = value;
  208. }
  209. }
  210. },
  211. // 处理数据
  212. handleData() {
  213. this.con_preview = [];
  214. this.show_preview = false;
  215. if (!this.isJudgingRightWrong) {
  216. this.answer.answer_list = [];
  217. }
  218. // this.data.option_list.forEach((item) => {
  219. let con_arr = JSON.parse(JSON.stringify(this.data.content_view));
  220. let user_answer = [];
  221. let user_submit = []; // 用户提交答案
  222. con_arr.forEach((items) => {
  223. user_answer.push({
  224. select_tone: null,
  225. select_letter: '',
  226. select_index: '',
  227. });
  228. user_submit.push(this.data.property.answer_mode === 'label' ? items : '');
  229. });
  230. let obj = {
  231. item_con: con_arr,
  232. item_con_yuan: JSON.parse(JSON.stringify(con_arr)),
  233. mark: this.data.mark,
  234. user_answer,
  235. item_active_index: 0,
  236. active_letter: '',
  237. };
  238. if (!this.isJudgingRightWrong) {
  239. let obj = {
  240. mark: this.data.mark,
  241. value: user_submit,
  242. };
  243. this.answer.answer_list.push(obj);
  244. }
  245. this.con_preview.push(obj);
  246. // });
  247. this.show_preview = true;
  248. },
  249. handleReplaceTone(e, arr, index, resArr) {
  250. this.$nextTick(() => {
  251. let value = e;
  252. this.resArr = [];
  253. if (value) {
  254. let reg = /\s+/g;
  255. let valueArr = value.split(reg);
  256. valueArr.forEach((item) => {
  257. this.handleValue(item, resArr);
  258. });
  259. let str = '';
  260. setTimeout(() => {
  261. if (resArr) {
  262. resArr.forEach((item) => {
  263. str += ' ';
  264. item.forEach((sItem) => {
  265. if (sItem.number && sItem.con) {
  266. let number = Number(sItem.number);
  267. let con = sItem.con;
  268. let word = this.addTone(number, con);
  269. str += word;
  270. } else if (sItem.number) {
  271. str += sItem.number;
  272. } else if (sItem.con) {
  273. str += ` ${sItem.con} `;
  274. }
  275. });
  276. });
  277. if (this.data.property.answer_mode === 'label') {
  278. let number_index = e.search(/0|1|2|3|4/) + 1;
  279. arr[index] = str.trim() + (number_index === 0 ? '' : e.substring(number_index));
  280. } else {
  281. arr[index] = str.trim();
  282. }
  283. } else {
  284. this.resArr.forEach((item) => {
  285. str += ' ';
  286. item.forEach((sItem) => {
  287. if (sItem.number && sItem.con) {
  288. let number = Number(sItem.number);
  289. let con = sItem.con;
  290. let word = this.addTone(number, con);
  291. str += word;
  292. } else if (sItem.number) {
  293. str += sItem.number;
  294. } else if (sItem.con) {
  295. str += ` ${sItem.con} `;
  296. }
  297. });
  298. });
  299. this.final_con = str.trim();
  300. }
  301. }, 10);
  302. }
  303. });
  304. },
  305. handleValue(valItem, resArr) {
  306. let reg = /\d/;
  307. let reg2 = /[A-Za-zü]+\d/g;
  308. let numList = [];
  309. let valArr = valItem.split('');
  310. if (reg2.test(valItem)) {
  311. for (let i = 0; i < valArr.length; i++) {
  312. let item = valItem[i];
  313. if (reg.test(item)) {
  314. let numIndex = numList.length === 0 ? 0 : numList[numList.length - 1].index;
  315. let con = valItem.substring(numIndex, i);
  316. con = con.replace(/\d/g, '');
  317. let obj = {
  318. index: i,
  319. number: item,
  320. con,
  321. isTran: true,
  322. };
  323. numList.push(obj);
  324. }
  325. }
  326. } else {
  327. numList = [];
  328. }
  329. if (resArr) {
  330. if (numList.length === 0) {
  331. resArr.push([{ con: valItem }]);
  332. } else {
  333. resArr.push(numList);
  334. }
  335. } else if (numList.length === 0) {
  336. this.resArr.push([{ con: valItem }]);
  337. } else {
  338. this.resArr.push(numList);
  339. }
  340. },
  341. addTone(number, con) {
  342. let zmList = ['a', 'o', 'e', 'i', 'u', 'v', 'ü', 'A', 'O', 'E', 'I', 'U'];
  343. let cons = con;
  344. if (number) {
  345. for (let i = 0; i < zmList.length; i++) {
  346. let zm = zmList[i];
  347. if (con.indexOf(zm) > -1) {
  348. let zm2 = this.tone_data[i][number - 1];
  349. if (con.indexOf('iu') > -1) {
  350. zm2 = this.tone_data[4][number - 1];
  351. cons = con.replace('u', zm2);
  352. } else if (con.indexOf('ui') > -1) {
  353. zm2 = this.tone_data[3][number - 1];
  354. cons = con.replace('i', zm2);
  355. } else if (
  356. con.indexOf('yv') > -1 ||
  357. con.indexOf('jv') > -1 ||
  358. con.indexOf('qv') > -1 ||
  359. con.indexOf('xv') > -1
  360. ) {
  361. zm2 = this.tone_data[4][number - 1];
  362. cons = con.replace('v', zm2);
  363. } else {
  364. cons = con.replace(zm, zm2);
  365. }
  366. break;
  367. }
  368. }
  369. }
  370. return cons;
  371. },
  372. handleSelectItemTone(i, indexc, indexi, itemi) {
  373. this.con_preview[0].item_active_index = indexc;
  374. this.con_preview[0].user_answer[indexc].select_index = indexi;
  375. this.active_index_str = `${i}-${indexc}-${indexi}`;
  376. this.active_letter = itemi;
  377. this.active_letter_index = indexi;
  378. this.select_item_index = i;
  379. },
  380. // 判断对错
  381. judgeRight() {
  382. this.con_preview = [];
  383. this.show_preview = false;
  384. // this.data.option_list.forEach((item, index) => {
  385. let con_arr = JSON.parse(JSON.stringify(this.data.content_view));
  386. let user_answer = [];
  387. let user_select = [];
  388. let user_res_arr = [];
  389. let answer_list_item = this.answer.answer_list.filter((items) => this.data.mark === items.mark);
  390. con_arr.forEach((items, indexs) => {
  391. user_answer.push({
  392. select_tone: answer_list_item[0].value[indexs],
  393. select_letter: '',
  394. select_index: '',
  395. is_right: answer_list_item[0].value[indexs] === this.data.answer.answer_list[0].value[indexs],
  396. right_answer: this.data.answer.answer_list[0].value[indexs],
  397. });
  398. user_res_arr.push([]);
  399. user_select.push('');
  400. if (this.data.property.answer_mode === 'label') {
  401. this.handleReplaceTone(answer_list_item[0].value[indexs], user_select, indexs, user_res_arr[indexs]);
  402. if (
  403. answer_list_item[0].value[indexs].match(/\d+/g) &&
  404. answer_list_item[0].value[indexs].match(/\d+/g).length > 0
  405. ) {
  406. user_answer[indexs].select_tone = answer_list_item[0].value[indexs].match(/\d+/g)[0];
  407. let letter_number = answer_list_item[0].value[indexs].match(/\d+/g)[0];
  408. let letter_index = answer_list_item[0].value[indexs].indexOf(letter_number) - 1;
  409. user_answer[indexs].select_letter = answer_list_item[0].value[indexs].substring(
  410. letter_index,
  411. letter_index + 1,
  412. );
  413. user_answer[indexs].select_index_submit = letter_index;
  414. } else {
  415. user_select[indexs] = items;
  416. }
  417. user_answer[indexs].right_answer = this.data.answer.answer_list[0].value[indexs].match(/0|1|2|3|4/)
  418. ? this.data.answer.answer_list[index].value[indexs].match(/0|1|2|3|4/)[0]
  419. : '';
  420. user_answer[indexs].right_index = this.data.answer.answer_list[0].value[indexs].search(/0|1|2|3|4/) - 1;
  421. } else {
  422. this.handleReplaceTone(items + answer_list_item[0].value[indexs], user_select, indexs, user_res_arr[indexs]);
  423. }
  424. });
  425. let obj = {
  426. item_con: user_select,
  427. item_con_yuan: JSON.parse(JSON.stringify(con_arr)),
  428. mark: this.data.mark,
  429. user_answer,
  430. item_active_index: 0,
  431. active_letter: '',
  432. user_res_arr,
  433. all_right: JSON.stringify(answer_list_item[0].value) === JSON.stringify(this.data.answer.answer_list[0].value),
  434. };
  435. this.con_preview.push(obj);
  436. // });
  437. setTimeout(() => {
  438. this.show_preview = true;
  439. }, 100);
  440. },
  441. },
  442. };
  443. </script>
  444. <style lang="scss" scoped>
  445. @use '@/styles/mixin.scss' as *;
  446. .pinyin-preview {
  447. @include preview-base;
  448. .main {
  449. display: grid;
  450. align-items: center;
  451. }
  452. .fill-wrapper {
  453. grid-area: fill;
  454. font-size: 16pt;
  455. p {
  456. margin: 0;
  457. }
  458. .el-input {
  459. display: inline-flex;
  460. align-items: center;
  461. width: 120px;
  462. margin: 0 2px;
  463. &.pinyin :deep input.el-input__inner {
  464. font-family: 'PINYIN-B', sans-serif;
  465. }
  466. &.chinese :deep input.el-input__inner {
  467. font-family: 'arial', sans-serif;
  468. }
  469. &.english :deep input.el-input__inner {
  470. font-family: 'arial', sans-serif;
  471. }
  472. :deep input.el-input__inner {
  473. padding: 0;
  474. font-size: 16pt;
  475. color: $font-color;
  476. text-align: center;
  477. background-color: #fff;
  478. border-width: 0;
  479. border-bottom: 1px solid $font-color;
  480. border-radius: 0;
  481. }
  482. }
  483. }
  484. .record-box {
  485. padding: 6px 12px;
  486. background-color: $fill-color;
  487. :deep .record-time {
  488. width: 100px;
  489. }
  490. }
  491. }
  492. </style>