PinyinBasePreview.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="pinyin-preview" :style="[getAreaStyle(), getComponentStyle()]">
  4. <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
  5. <div class="main">
  6. <div
  7. class="content-box"
  8. :class="[data.property.arrange_type === 'horizontal' ? 'content-box-flex' : 'content-box-vertical']"
  9. >
  10. <div class="first-con">
  11. <AudioPlay
  12. v-if="data.audio_file_id && data.property.audio_position === 'front'"
  13. :file-id="data.audio_file_url"
  14. :theme-color="data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : ''"
  15. />
  16. <div
  17. class="option-content"
  18. :class="[
  19. isJudgingRightWrong && data.property.fun_type === 'mark'
  20. ? con_preview[0].all_right
  21. ? 'all-right'
  22. : 'has-error'
  23. : '',
  24. ]"
  25. :style="{
  26. fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  27. minWidth: data.property.content_width ? data.property.content_width + 'px' : '',
  28. }"
  29. >
  30. <span v-if="data.content_hz" class="items-hz">{{ convertText(data.content_hz) }}</span>
  31. <!-- 拼音输入 -->
  32. <template v-if="data.property.fun_type === 'input'">
  33. <span
  34. v-for="(itemc, indexc) in con_preview"
  35. :key="indexc"
  36. :class="['item-con', isJudgingRightWrong && itemc.type === 'input' ? itemc.is_right : '']"
  37. :style="{
  38. color:
  39. data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
  40. }"
  41. >
  42. <el-input
  43. v-if="itemc.type === 'input'"
  44. v-model="itemc.con"
  45. :disabled="disabled"
  46. :style="[
  47. {
  48. width: Math.max(20, itemc.con.length * 12) + 'px',
  49. fontSize:
  50. data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  51. '--input-color':
  52. data.unified_attrib && data.unified_attrib.text_color
  53. ? data.unified_attrib.text_color
  54. : '#1d2129',
  55. },
  56. ]"
  57. class="item-input"
  58. @blur="onInputChange(itemc)"
  59. />
  60. <span v-else>{{ convertText(itemc.con) }}</span>
  61. </span>
  62. </template>
  63. <template v-else-if="data.property.answer_mode === 'select'">
  64. <template v-if="data.property.fun_type === 'show'">
  65. <span
  66. v-for="(itemc, indexc) in data.matically_pinyin_str[data.mark]"
  67. :key="indexc"
  68. class="items-box"
  69. :style="{
  70. color:
  71. data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
  72. }"
  73. >
  74. <span v-for="(itemi, indexi) in itemc" :key="indexi" :class="['items-con']">{{ itemi }}</span>
  75. </span>
  76. </template>
  77. <template v-else>
  78. <span
  79. v-for="(itemc, indexc) in con_preview[0].item_con"
  80. :key="indexc"
  81. :class="[
  82. 'item-con',
  83. active_index_str === 0 + '-' + indexc ? 'active' : '',
  84. isJudgingRightWrong &&
  85. con_preview[0].user_answer[indexc].is_right !== '' &&
  86. !con_preview[0].user_answer[indexc].is_right
  87. ? 'error'
  88. : '',
  89. data.property.fun_type === 'show' ? 'item-con-in' : '',
  90. ]"
  91. :style="{
  92. color:
  93. data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
  94. }"
  95. @click="
  96. if (data.property.fun_type === 'show') return;
  97. con_preview[0].item_active_index = indexc;
  98. active_index_str = 0 + '-' + indexc;
  99. "
  100. >
  101. {{ itemc }}
  102. </span>
  103. </template>
  104. </template>
  105. <template v-else>
  106. <span v-for="(itemc, indexc) in con_preview[0].item_con" :key="indexc" class="items-box">
  107. <span
  108. v-for="(itemi, indexi) in itemc"
  109. :key="indexi"
  110. :class="['items-con', active_index_str === 0 + '-' + indexc + '-' + indexi ? 'active' : '']"
  111. :style="{
  112. color:
  113. data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
  114. }"
  115. @click="handleSelectItemTone(0, indexc, indexi, con_preview[0].item_con_yuan[indexc][indexi])"
  116. >{{ itemi }}</span
  117. >
  118. <!-- 拼音之间增加一点间隙 -->
  119. <span v-if="indexc !== con_preview[0].item_con.length - 1" style="opacity: 0">a</span>
  120. </span>
  121. </template>
  122. </div>
  123. <AudioPlay
  124. v-if="data.audio_file_id && data.property.audio_position === 'back'"
  125. :file-id="data.audio_file_url"
  126. />
  127. </div>
  128. <div v-if="data.property.fun_type === 'mark'" class="tone-box">
  129. <span
  130. v-for="({ img, value }, j) in toneList"
  131. :key="j"
  132. :class="[
  133. 'tone',
  134. data.property.answer_mode === 'select' &&
  135. con_preview[0].user_answer[con_preview[0].item_active_index] &&
  136. con_preview[0].user_answer[con_preview[0].item_active_index].select_tone === value
  137. ? 'active'
  138. : data.property.answer_mode === 'label' &&
  139. con_preview[0].user_answer[con_preview[0].item_active_index] &&
  140. con_preview[0].user_answer[con_preview[0].item_active_index].select_tone === value &&
  141. con_preview[0].user_answer[con_preview[0].item_active_index].select_letter === active_letter &&
  142. select_item_index === 0
  143. ? 'active'
  144. : '',
  145. (isJudgingRightWrong &&
  146. con_preview[0].user_answer[con_preview[0].item_active_index].right_answer === value &&
  147. con_preview[0].user_answer[con_preview[0].item_active_index].select_index_submit !==
  148. con_preview[0].user_answer[con_preview[0].item_active_index].right_answer &&
  149. data.property.answer_mode === 'select') ||
  150. (isJudgingRightWrong &&
  151. data.property.answer_mode === 'label' &&
  152. con_preview[0].user_answer[con_preview[0].item_active_index].right_answer === value &&
  153. con_preview[0].user_answer[con_preview[0].item_active_index].right_index ===
  154. con_preview[0].user_answer[con_preview[0].item_active_index].select_index &&
  155. select_item_index === 0)
  156. ? 'right'
  157. : '',
  158. ]"
  159. @click="chooseTone(con_preview[0], value)"
  160. >
  161. <SvgIcon :icon-class="img" size="16" />
  162. </span>
  163. </div>
  164. <template v-else-if="data.property.fun_type !== 'mark' && isEnable(data.property.is_enable_voice_answer)">
  165. <SoundRecord
  166. ref="record"
  167. type="normal"
  168. class="record-box"
  169. :answer-record-list="answer.answer_list[0].record_list"
  170. :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
  171. :attrib="data.unified_attrib"
  172. @handleWav="handleWav"
  173. />
  174. </template>
  175. </div>
  176. <PreviewOperation
  177. v-if="
  178. data.property.fun_type !== 'show' ||
  179. (data.property.fun_type === 'show' && isEnable(data.property.is_enable_voice_answer))
  180. "
  181. :is-show-answer="isShowAnswers"
  182. @showAnswerAnalysis="showAnswerAnalysis"
  183. @judgeCorrect="judgeCorrect"
  184. @retry="retry"
  185. />
  186. <AnswerCorrect
  187. :answer-correct="data?.answer_correct"
  188. :visible.sync="visibleAnswerCorrect"
  189. :is-check-correct="isCheckCorrect"
  190. @closeAnswerCorrect="closeAnswerCorrect"
  191. />
  192. <AnswerAnalysis
  193. :visible.sync="visibleAnswerAnalysis"
  194. :answer-list="data.answer_list"
  195. :analysis-list="data.analysis_list"
  196. @closeAnswerAnalysis="closeAnswerAnalysis"
  197. >
  198. <div v-if="data.property.fun_type !== 'show'" slot="right-answer" class="right-answer">
  199. <div
  200. class="content-box"
  201. :class="[data.property.arrange_type === 'horizontal' ? 'content-box-flex' : 'content-box-vertical']"
  202. >
  203. <div class="first-con">
  204. <AudioPlay
  205. v-if="data.audio_file_id && data.property.audio_position === 'front'"
  206. :file-id="data.audio_file_url"
  207. :theme-color="
  208. data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : ''
  209. "
  210. />
  211. <div
  212. class="option-content"
  213. :style="{
  214. fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  215. color: data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '',
  216. minWidth: data.property.content_width ? data.property.content_width + 'px' : '',
  217. }"
  218. >
  219. <span v-if="data.content_hz" class="items-hz">{{ convertText(data.content_hz) }}</span>
  220. <!-- 拼音输入 -->
  221. <template v-if="data.property.fun_type === 'input'">
  222. <span
  223. v-for="(itemc, indexc) in con_preview_right"
  224. :key="indexc"
  225. :class="['item-con', itemc.type === 'input' ? 'right' : '']"
  226. >
  227. <el-input
  228. v-if="itemc.type === 'input'"
  229. v-model="itemc.answer"
  230. :disabled="true"
  231. :style="[
  232. {
  233. width: Math.max(20, itemc.answer.length * 12) + 'px',
  234. fontSize:
  235. data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
  236. '--input-color':
  237. data.unified_attrib && data.unified_attrib.text_color
  238. ? data.unified_attrib.text_color
  239. : '#1d2129',
  240. },
  241. ]"
  242. class="item-input"
  243. />
  244. <span v-else>{{ itemc.con }}</span>
  245. </span>
  246. </template>
  247. <template v-else-if="data.property.answer_mode === 'select'">
  248. <span
  249. v-for="(itemc, indexc) in data.matically_pinyin_str[data.mark].split(' ')"
  250. :key="indexc"
  251. :class="['item-con', active_index_str_right === 0 + '-' + indexc ? 'active' : '']"
  252. @click="
  253. if (data.property.fun_type === 'show') return;
  254. con_preview_right[0].item_active_index = indexc;
  255. active_index_str_right = 0 + '-' + indexc;
  256. "
  257. >
  258. {{ itemc }}
  259. </span>
  260. <!-- <span
  261. v-for="(itemc, indexc) in con_preview_right[0].item_con"
  262. :key="indexc"
  263. :class="[
  264. 'item-con',
  265. active_index_str === 0 + '-' + indexc ? 'active' : '',
  266. data.property.fun_type === 'show' ? 'item-con-in' : '',
  267. ]"
  268. @click="
  269. if (data.property.fun_type === 'show') return;
  270. con_preview_right[0].item_active_index = indexc;
  271. active_index_str = 0 + '-' + indexc;
  272. "
  273. >
  274. {{ itemc }}
  275. </span> -->
  276. </template>
  277. <template v-else>
  278. <span
  279. v-for="(itemc, indexc) in data.matically_pinyin_str[data.mark].split(' ')"
  280. :key="indexc"
  281. class="items-box"
  282. >
  283. <span
  284. v-for="(itemi, indexi) in itemc"
  285. :key="indexi"
  286. :class="['items-con', active_index_str_right === 0 + '-' + indexc + '-' + indexi ? 'active' : '']"
  287. :style="{
  288. color:
  289. data.unified_attrib && data.unified_attrib.text_color
  290. ? data.unified_attrib.text_color
  291. : '#000',
  292. }"
  293. @click="
  294. handleSelectItemTone(0, indexc, indexi, con_preview_right[0].item_con_yuan[indexc][indexi])
  295. "
  296. >{{ itemi }}</span
  297. >
  298. <!-- 拼音之间增加一点间隙 -->
  299. <span
  300. v-if="indexc !== data.matically_pinyin_str[data.mark].split(' ').length - 1"
  301. style="opacity: 0"
  302. >a</span
  303. >
  304. </span>
  305. </template>
  306. </div>
  307. <AudioPlay
  308. v-if="data.audio_file_id && data.property.audio_position === 'back'"
  309. :file-id="data.audio_file_url"
  310. />
  311. </div>
  312. <div v-if="data.property.fun_type === 'mark'" class="tone-box">
  313. <span
  314. v-for="({ img, value }, j) in toneList"
  315. :key="j"
  316. :class="[
  317. 'tone',
  318. (isJudgingRightWrong &&
  319. con_preview_right[0].user_answer[con_preview_right[0].item_active_index].right_answer === value &&
  320. con_preview_right[0].user_answer[con_preview_right[0].item_active_index].select_index_submit !==
  321. con_preview_right[0].user_answer[con_preview_right[0].item_active_index].right_answer &&
  322. data.property.answer_mode === 'select') ||
  323. (isJudgingRightWrong &&
  324. data.property.answer_mode === 'label' &&
  325. con_preview_right[0].user_answer[con_preview_right[0].item_active_index].right_answer === value &&
  326. con_preview_right[0].user_answer[con_preview_right[0].item_active_index].right_index ===
  327. con_preview_right[0].user_answer[con_preview_right[0].item_active_index].select_index &&
  328. select_item_index === 0)
  329. ? 'right'
  330. : '',
  331. ]"
  332. >
  333. <SvgIcon :icon-class="img" />
  334. </span>
  335. </div>
  336. </div>
  337. </div>
  338. </AnswerAnalysis>
  339. </div>
  340. </div>
  341. </template>
  342. <script>
  343. import { getPinyinBaseData, arrangeTypeList, audioPositionList } from '@/views/book/courseware/data/pinyinBase';
  344. import PreviewMixin from '../common/PreviewMixin';
  345. import AudioPlay from '../character_base/components/AudioPlay.vue';
  346. import SoundRecord from '../../common/SoundRecord.vue';
  347. import { addTone, handleToneValue } from '@/utils/common';
  348. export default {
  349. name: 'PinyinBasePreview',
  350. components: {
  351. AudioPlay,
  352. SoundRecord,
  353. },
  354. mixins: [PreviewMixin],
  355. data() {
  356. return {
  357. data: getPinyinBaseData(),
  358. arrangeTypeList,
  359. audioPositionList,
  360. toneList: [
  361. { value: '1', label: '一声', img: 'first-tone' },
  362. { value: '2', label: '二声', img: 'second-tone' },
  363. { value: '3', label: '三声', img: 'third-tone' },
  364. { value: '4', label: '四声', img: 'fourth-tone' },
  365. // { value: '0', label: '轻声', img: 'neutral-tone' },
  366. ],
  367. con_preview: [],
  368. con_preview_right: [], // 查看答案是显示的
  369. tone_data: [
  370. ['ā', 'á', 'ǎ', 'à', 'a'],
  371. ['ō', 'ó', 'ǒ', 'ò', 'o'],
  372. ['ē', 'é', 'ě', 'è', 'e'],
  373. ['ī', 'í', 'ǐ', 'ì', 'i'],
  374. ['ū', 'ú', 'ǔ', 'ù', 'u'],
  375. ['ǖ', 'ǘ', 'ǚ', 'ǜ', 'ü'],
  376. ['ǖ', 'ǘ', 'ǚ', 'ǜ', 'ü'],
  377. ['Ā', 'Á', 'Â', 'À', 'A'],
  378. ['Ō', 'Ó', 'Ô', 'Ò', 'O'],
  379. ['Ē', 'É', 'Ê', 'È', 'E'],
  380. ['Ī', 'Í', 'Î', 'Ì', 'I'],
  381. ['Ū', 'Ú', 'Û', 'Ù', 'U'],
  382. ['n', 'ń', 'ň', 'ǹ', 'n'],
  383. ['m̄', 'ḿ', 'm', 'm̀', 'm'],
  384. ],
  385. final_con: '',
  386. active_index_str: '0-0', // 高亮索引的字符串
  387. active_index_str_right: '', // 查看答案时高亮索引
  388. active_letter: '', // 选中字母的值
  389. active_letter_index: 0, // 选择字母索引
  390. select_item_index: 0, // 小题索引
  391. isShowAnswers: false,
  392. };
  393. },
  394. watch: {
  395. data: {
  396. handler(val) {
  397. // if (!val || this.data.type !== 'choose_tone') return;
  398. this.handleData();
  399. },
  400. deep: true,
  401. immediate: true,
  402. },
  403. isJudgingRightWrong: {
  404. handler(val) {
  405. if (!val) return;
  406. this.judgeRight();
  407. this.$forceUpdate();
  408. },
  409. immediate: true,
  410. },
  411. 'answer.answer_list'(val) {
  412. this.answer.answer_list = val;
  413. },
  414. },
  415. created() {
  416. if (
  417. this.data.property.fun_type !== 'show' ||
  418. (this.data.property.fun_type === 'show' &&
  419. ((this.data.answer_list && this.data.answer_list.length > 0) ||
  420. (this.data.analysis_list && this.data.analysis_list.length > 0)))
  421. ) {
  422. this.isShowAnswers = true;
  423. } else {
  424. this.isShowAnswers = false;
  425. }
  426. },
  427. methods: {
  428. chooseTone(item, value) {
  429. if (this.disabled) return;
  430. if (!this.active_letter && this.data.property.answer_mode === 'label') return;
  431. if (
  432. item.user_answer[item.item_active_index].select_tone &&
  433. item.user_answer[item.item_active_index].select_tone === value &&
  434. this.data.property.answer_mode === 'label' &&
  435. item.user_answer[item.item_active_index].select_letter === this.active_letter
  436. ) {
  437. item.user_answer[item.item_active_index].select_tone = '';
  438. this.handleReplaceTone(this.active_letter + 0);
  439. setTimeout(() => {
  440. let new_con = item.item_con_yuan[item.item_active_index].split(this.active_letter);
  441. item.item_con[item.item_active_index] = new_con[0] + this.final_con + new_con[1];
  442. this.$forceUpdate();
  443. this.answer.answer_list[0].value[item.item_active_index] = new_con[0] + this.active_letter + new_con[1];
  444. }, 100);
  445. } else if (
  446. item.user_answer[item.item_active_index].select_tone &&
  447. item.user_answer[item.item_active_index].select_tone === value &&
  448. this.data.property.answer_mode === 'select'
  449. ) {
  450. item.user_answer[item.item_active_index].select_tone = '';
  451. this.handleReplaceTone(item.item_con_yuan[item.item_active_index] + 0);
  452. setTimeout(() => {
  453. item.item_con[item.item_active_index] = this.final_con;
  454. this.$forceUpdate();
  455. }, 100);
  456. this.answer.answer_list[0].value[item.item_active_index] = '';
  457. } else {
  458. item.user_answer[item.item_active_index].select_tone = value;
  459. if (this.data.property.answer_mode === 'label') {
  460. item.user_answer[item.item_active_index].select_letter = this.active_letter;
  461. this.active_index_str = `${0}-${item.item_active_index}-${this.active_letter_index}`;
  462. this.handleReplaceTone(this.active_letter + value);
  463. setTimeout(() => {
  464. let new_con = item.item_con_yuan[item.item_active_index].split(this.active_letter);
  465. item.item_con[item.item_active_index] = new_con[0] + this.final_con + new_con[1];
  466. this.$forceUpdate();
  467. this.answer.answer_list[0].value[item.item_active_index] =
  468. new_con[0] + this.active_letter + value + new_con[1];
  469. }, 100);
  470. } else {
  471. this.active_index_str = `${0}-${item.item_active_index}`;
  472. this.handleReplaceTone(item.item_con_yuan[item.item_active_index] + value);
  473. setTimeout(() => {
  474. item.item_con[item.item_active_index] = this.final_con;
  475. this.$forceUpdate();
  476. }, 100);
  477. this.answer.answer_list[0].value[item.item_active_index] = value;
  478. }
  479. }
  480. },
  481. // 处理数据
  482. handleData() {
  483. this.con_preview = [];
  484. this.show_preview = false;
  485. if (!this.isJudgingRightWrong) {
  486. this.answer.answer_list = [];
  487. }
  488. if (this.data.property.fun_type === 'input') {
  489. let arr = this.data.matically_pinyin_str[this.data.mark].split(/_{3,}/g);
  490. let inputIndex = 0;
  491. arr.forEach((item, index) => {
  492. let obj = {
  493. con: item,
  494. type: 'text',
  495. };
  496. this.con_preview.push(obj);
  497. if (index !== arr.length - 1) {
  498. let objs = {
  499. con: '',
  500. type: 'input',
  501. inputIndex,
  502. answer: this.data.answer.answer_list[inputIndex] ? this.data.answer.answer_list[inputIndex].con : '',
  503. };
  504. this.con_preview.push(JSON.parse(JSON.stringify(objs)));
  505. inputIndex++;
  506. }
  507. });
  508. if (!this.isJudgingRightWrong) {
  509. let obj = {
  510. mark: this.data.mark,
  511. value: this.con_preview,
  512. record_list: [],
  513. };
  514. this.answer.answer_list.push(obj);
  515. }
  516. } else {
  517. // this.data.option_list.forEach((item) => {
  518. let con_arr = JSON.parse(JSON.stringify(this.data.content_view));
  519. let user_answer = [];
  520. let user_submit = []; // 用户提交答案
  521. con_arr.forEach((items) => {
  522. user_answer.push({
  523. select_tone: null,
  524. select_letter: '',
  525. select_index: '',
  526. });
  527. user_submit.push(this.data.property.answer_mode === 'label' ? items : '');
  528. });
  529. let obj = {
  530. item_con: con_arr,
  531. item_con_yuan: JSON.parse(JSON.stringify(con_arr)),
  532. mark: this.data.mark,
  533. user_answer,
  534. item_active_index: 0,
  535. active_letter: '',
  536. };
  537. if (!this.isJudgingRightWrong) {
  538. let obj = {
  539. mark: this.data.mark,
  540. value: user_submit,
  541. record_list: [],
  542. };
  543. this.answer.answer_list.push(obj);
  544. }
  545. this.con_preview.push(obj);
  546. // });
  547. }
  548. this.show_preview = true;
  549. },
  550. handleReplaceTone(e, arr, index, resArr) {
  551. this.$nextTick(() => {
  552. let value = e;
  553. this.resArr = [];
  554. if (value) {
  555. let reg = /\s+/g;
  556. let valueArr = value.split(reg);
  557. valueArr.forEach((item) => {
  558. this.handleValue(item, resArr);
  559. });
  560. let str = '';
  561. setTimeout(() => {
  562. if (resArr) {
  563. resArr.forEach((item) => {
  564. str += ' ';
  565. item.forEach((sItem) => {
  566. if (sItem.number && sItem.con) {
  567. let number = Number(sItem.number);
  568. let con = sItem.con;
  569. let word = this.addTone(number, con);
  570. str += word;
  571. } else if (sItem.number) {
  572. str += sItem.number;
  573. } else if (sItem.con) {
  574. str += ` ${sItem.con} `;
  575. }
  576. });
  577. });
  578. if (this.data.property.answer_mode === 'label') {
  579. let number_index = e.search(/0|1|2|3|4/) + 1;
  580. arr[index] = str.trim() + (number_index === 0 ? '' : e.substring(number_index));
  581. } else {
  582. arr[index] = str.trim();
  583. }
  584. } else {
  585. this.resArr.forEach((item) => {
  586. str += ' ';
  587. item.forEach((sItem) => {
  588. if (sItem.number && sItem.con) {
  589. let number = Number(sItem.number);
  590. let con = sItem.con;
  591. let word = this.addTone(number, con);
  592. str += word;
  593. } else if (sItem.number) {
  594. str += sItem.number;
  595. } else if (sItem.con) {
  596. str += ` ${sItem.con} `;
  597. }
  598. });
  599. });
  600. this.final_con = str.trim();
  601. }
  602. }, 10);
  603. }
  604. });
  605. },
  606. handleValue(valItem, resArr) {
  607. let reg = /\d/;
  608. let reg2 = /[A-Za-zü]+\d/g;
  609. let numList = [];
  610. let valArr = valItem.split('');
  611. if (reg2.test(valItem)) {
  612. for (let i = 0; i < valArr.length; i++) {
  613. let item = valItem[i];
  614. if (reg.test(item)) {
  615. let numIndex = numList.length === 0 ? 0 : numList[numList.length - 1].index;
  616. let con = valItem.substring(numIndex, i);
  617. con = con.replace(/\d/g, '');
  618. let obj = {
  619. index: i,
  620. number: item,
  621. con,
  622. isTran: true,
  623. };
  624. numList.push(obj);
  625. }
  626. }
  627. } else {
  628. numList = [];
  629. }
  630. if (resArr) {
  631. if (numList.length === 0) {
  632. resArr.push([{ con: valItem }]);
  633. } else {
  634. resArr.push(numList);
  635. }
  636. } else if (numList.length === 0) {
  637. this.resArr.push([{ con: valItem }]);
  638. } else {
  639. this.resArr.push(numList);
  640. }
  641. },
  642. addTone(number, con) {
  643. let zmList = ['a', 'o', 'e', 'i', 'u', 'v', 'ü', 'A', 'O', 'E', 'I', 'U', 'n', 'm'];
  644. let cons = con;
  645. if (number) {
  646. for (let i = 0; i < zmList.length; i++) {
  647. let zm = zmList[i];
  648. if (con.indexOf(zm) > -1) {
  649. let zm2 = this.tone_data[i][number - 1];
  650. if (con.indexOf('iu') > -1) {
  651. zm2 = this.tone_data[4][number - 1];
  652. cons = con.replace('u', zm2);
  653. } else if (con.indexOf('ui') > -1) {
  654. zm2 = this.tone_data[3][number - 1];
  655. cons = con.replace('i', zm2);
  656. } else if (
  657. con.indexOf('yv') > -1 ||
  658. con.indexOf('jv') > -1 ||
  659. con.indexOf('qv') > -1 ||
  660. con.indexOf('xv') > -1
  661. ) {
  662. zm2 = this.tone_data[4][number - 1];
  663. cons = con.replace('v', zm2);
  664. } else {
  665. cons = con.replace(zm, zm2);
  666. }
  667. break;
  668. }
  669. }
  670. }
  671. return cons;
  672. },
  673. handleSelectItemTone(i, indexc, indexi, itemi) {
  674. if (this.data.property.fun_type === 'show') {
  675. return;
  676. }
  677. if (this.isJudgingRightWrong) {
  678. this.con_preview_right[0].item_active_index = indexc;
  679. this.con_preview_right[0].user_answer[indexc].select_index = indexi;
  680. this.active_index_str_right = `${i}-${indexc}-${indexi}`;
  681. } else {
  682. this.con_preview[0].item_active_index = indexc;
  683. this.con_preview[0].user_answer[indexc].select_index = indexi;
  684. this.active_index_str = `${i}-${indexc}-${indexi}`;
  685. this.select_item_index = i;
  686. this.active_letter = itemi;
  687. this.active_letter_index = indexi;
  688. }
  689. },
  690. // 判断对错
  691. judgeRight() {
  692. let active_index_old = 0;
  693. this.active_index_str_right = '';
  694. if (this.con_preview.length > 0) {
  695. active_index_old = this.con_preview[0].item_active_index;
  696. }
  697. this.show_preview = false;
  698. let answer_list_item = this.answer.answer_list.filter((items) => this.data.mark === items.mark);
  699. if (this.data.property.fun_type === 'input') {
  700. this.con_preview.forEach((items, indexs) => {
  701. if (items.type === 'input') {
  702. let is_right = items.answer
  703. ? answer_list_item[0].value[indexs].con === items.answer
  704. ? 'right'
  705. : 'error'
  706. : '';
  707. this.$set(items, 'is_right', is_right);
  708. }
  709. });
  710. } else {
  711. let old_con_preview = JSON.parse(JSON.stringify(this.con_preview));
  712. this.con_preview = [];
  713. let con_arr = JSON.parse(JSON.stringify(this.data.content_view));
  714. let user_answer = [];
  715. let user_select = [];
  716. let user_res_arr = [];
  717. con_arr.forEach((items, indexs) => {
  718. user_answer.push({
  719. select_tone: answer_list_item[0].value[indexs],
  720. select_letter: '',
  721. select_index: '',
  722. is_right:
  723. this.data.answer.answer_list[0] &&
  724. this.data.answer.answer_list[0].value &&
  725. this.data.answer.answer_list[0].value[indexs] &&
  726. answer_list_item[0].value[indexs] === this.data.answer.answer_list[0].value[indexs],
  727. right_answer:
  728. this.data.answer.answer_list[0] &&
  729. this.data.answer.answer_list[0].value &&
  730. this.data.answer.answer_list[0].value[indexs]
  731. ? this.data.answer.answer_list[0].value[indexs]
  732. : null,
  733. });
  734. user_res_arr.push([]);
  735. user_select.push('');
  736. if (this.data.property.fun_type === 'mark') {
  737. if (this.data.property.answer_mode === 'label') {
  738. this.handleReplaceTone(answer_list_item[0].value[indexs], user_select, indexs, user_res_arr[indexs]);
  739. if (
  740. answer_list_item[0].value[indexs].match(/\d+/g) &&
  741. answer_list_item[0].value[indexs].match(/\d+/g).length > 0
  742. ) {
  743. user_answer[indexs].select_tone = answer_list_item[0].value[indexs].match(/\d+/g)[0];
  744. let letter_number = answer_list_item[0].value[indexs].match(/\d+/g)[0];
  745. let letter_index = answer_list_item[0].value[indexs].indexOf(letter_number) - 1;
  746. user_answer[indexs].select_letter = answer_list_item[0].value[indexs].substring(
  747. letter_index,
  748. letter_index + 1,
  749. );
  750. user_answer[indexs].select_index_submit = letter_index;
  751. } else {
  752. user_select[indexs] = items;
  753. }
  754. user_answer[indexs].right_answer = this.data.answer.answer_list[0].value[indexs].match(/0|1|2|3|4/)
  755. ? this.data.answer.answer_list[0].value[indexs].match(/0|1|2|3|4/)[0]
  756. : '';
  757. user_answer[indexs].right_index = this.data.answer.answer_list[0].value[indexs].search(/0|1|2|3|4/) - 1;
  758. } else {
  759. this.handleReplaceTone(
  760. items + answer_list_item[0].value[indexs],
  761. user_select,
  762. indexs,
  763. user_res_arr[indexs],
  764. );
  765. user_select[indexs] = items;
  766. }
  767. } else if (this.data.property.fun_type === 'input') {
  768. }
  769. });
  770. let obj = {
  771. item_con: old_con_preview[0].item_con,
  772. item_con_yuan: JSON.parse(JSON.stringify(con_arr)),
  773. mark: this.data.mark,
  774. user_answer,
  775. item_active_index: active_index_old,
  776. active_letter: '',
  777. user_res_arr,
  778. all_right:
  779. answer_list_item[0].value.length === this.data.answer.answer_list[0].value.length &&
  780. answer_list_item[0].value.every((value, index) => value === this.data.answer.answer_list[0].value[index]),
  781. };
  782. this.con_preview.push(obj);
  783. }
  784. this.con_preview_right = JSON.parse(JSON.stringify(this.con_preview));
  785. setTimeout(() => {
  786. this.show_preview = true;
  787. }, 100);
  788. },
  789. handleWav(data) {
  790. this.answer.answer_list[0].record_list = data;
  791. },
  792. onInputChange(item) {
  793. let answer = item.con;
  794. answer = answer
  795. .trim()
  796. .split(/\s+/)
  797. .map((item) => {
  798. return handleToneValue(item);
  799. })
  800. .map((item) =>
  801. item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
  802. )
  803. .filter((item) => item.length > 0)
  804. .join(' ');
  805. item.con = answer;
  806. },
  807. // 重做
  808. retry() {
  809. this.isJudgingRightWrong = false;
  810. this.isShowRightAnswer = false;
  811. this.handleData();
  812. if (this.data.property.fun_type !== 'mark' && this.isEnable(this.data.property.is_enable_voice_answer)) {
  813. this.$refs.record.handleReset();
  814. }
  815. },
  816. /**
  817. * 获取无文本内容的数据结构,用于保存为个人模板时的样式模板
  818. */
  819. getNoTextContentData() {
  820. let noTextContentData = JSON.parse(JSON.stringify(this.data));
  821. const resetFieldMap = {
  822. character: '',
  823. content: '',
  824. content_view: [],
  825. content_hz: '',
  826. matically_pinyin_str: {},
  827. audio_file_id: '',
  828. audio_file_url: '',
  829. file_id_list: [], // 文件 id
  830. record_list: [],
  831. answer_list: [], // 答案列表
  832. analysis_list: [], // 解析列表
  833. };
  834. Object.assign(noTextContentData, resetFieldMap);
  835. if (noTextContentData.answer) {
  836. noTextContentData.answer.answer_list = [];
  837. noTextContentData.answer.reference_answer = '';
  838. }
  839. return noTextContentData;
  840. },
  841. },
  842. };
  843. </script>
  844. <style lang="scss" scoped>
  845. @use '@/styles/mixin.scss' as *;
  846. .pinyin-preview {
  847. @include preview-base;
  848. .content-box {
  849. &-flex {
  850. display: flex;
  851. flex-wrap: wrap;
  852. column-gap: 8px;
  853. align-items: center;
  854. }
  855. &-vertical {
  856. .tone-box,
  857. .record-box {
  858. margin-top: 8px;
  859. }
  860. }
  861. }
  862. .first-con {
  863. display: flex;
  864. column-gap: 8px;
  865. align-items: center;
  866. }
  867. .option-content {
  868. padding: 5px 15px;
  869. color: #706f78;
  870. // background-color: $content-color;
  871. border: 1px solid $content-color;
  872. border-radius: 4px;
  873. &.all-right {
  874. background-color: $right-bc-color;
  875. border-color: $right-bc-color;
  876. }
  877. &.has-error {
  878. border-color: $error-color;
  879. }
  880. }
  881. .items-hz {
  882. margin-right: 4px;
  883. font-size: 16px;
  884. // font-weight: 500;
  885. line-height: 24px;
  886. color: #000;
  887. }
  888. .item-con,
  889. .items-con {
  890. font-family: 'League';
  891. // font-weight: 500;
  892. // color: #000;
  893. cursor: pointer;
  894. &.right {
  895. color: $right-color;
  896. :deep .el-input__inner {
  897. color: $right-color;
  898. border-bottom-color: $right-color;
  899. }
  900. }
  901. &.error {
  902. color: $error-color;
  903. :deep .el-input__inner {
  904. color: $error-color;
  905. border-bottom-color: $error-color;
  906. }
  907. }
  908. &.active {
  909. color: #2f6fec !important;
  910. }
  911. }
  912. .item-con-in {
  913. cursor: initial;
  914. }
  915. .tone-box {
  916. display: flex;
  917. column-gap: 8px;
  918. }
  919. .tone {
  920. width: 32px;
  921. height: 32px;
  922. padding: 8px;
  923. font-size: 0;
  924. color: #9f9f9f;
  925. text-align: center;
  926. cursor: pointer;
  927. &.right {
  928. color: $right-color;
  929. background-color: $right-bc-color;
  930. border-radius: 16px;
  931. }
  932. &.active {
  933. color: #2f6fec;
  934. background: #dfe9fd;
  935. border-radius: 16px;
  936. }
  937. }
  938. .record-box {
  939. padding: 7px 12px;
  940. background: #f2f3f5;
  941. border-radius: 2px;
  942. }
  943. .item-input {
  944. :deep .el-input__inner {
  945. padding: 0;
  946. font-family: 'League';
  947. // color: '#000';
  948. color: var(--input-color);
  949. // font-size: 16px;
  950. text-align: center;
  951. background-color: transparent;
  952. border-width: 0;
  953. border-bottom: 1px solid #000;
  954. border-radius: 0;
  955. }
  956. }
  957. }
  958. </style>