SentenceInput.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. <!-- -->
  2. <template>
  3. <div class="Big-Book-prev-Textdes" v-if="curQue">
  4. <h2 v-if="curQue.title">{{ curQue.title }}</h2>
  5. <ul>
  6. <li v-for="(items, indexs) in curQue.option" :key="indexs">
  7. <b class="xuhao" v-if="items.number">{{ items.number }}</b>
  8. <p
  9. :class="[oldsrc == items.mp3_list[0].id ? palyClass : '']"
  10. @click="handlePlayVoice(items.mp3_list[0].id)"
  11. v-if="items.mp3_list && items.mp3_list.length > 0"
  12. ></p>
  13. <!-- 句子内容 -->
  14. <div class="item-content">
  15. <!-- 如果不是填空题 不替换 -->
  16. <template v-if="dataType.indexOf('sentence_input_chs') == -1">
  17. <template v-if="items.detail.wordsList.length == 0">
  18. <p :class="['content-con']" v-if="items.detail.sentence">
  19. {{ items.detail.sentence }}
  20. </p>
  21. </template>
  22. <template v-else>
  23. <div class="con-box">
  24. <div
  25. :class="['con-item', indexCon === 0 ? 'con-item-0' : '']"
  26. v-for="(itemCon, indexCon) in items.detail.resArr"
  27. :key="indexCon"
  28. v-show="itemCon.isShow"
  29. >
  30. <template
  31. v-if="
  32. items.detail.wordsList[indexCon + 1] &&
  33. items.detail.wordsList[indexCon + 1].chs &&
  34. chsFhList.indexOf(
  35. items.detail.wordsList[indexCon + 1].chs
  36. ) > -1
  37. "
  38. >
  39. <div class="synthesis-box">
  40. <div>
  41. <span
  42. class="pinyin"
  43. :class="[
  44. noFont.indexOf(itemCon.pinyin) > -1 ? 'noFont' : '',
  45. ]"
  46. >{{ itemCon.pinyin }}</span
  47. >
  48. <span class="hanzi content-con">{{ itemCon.chs }}</span>
  49. </div>
  50. <div style="text-align: left">
  51. <span
  52. class="pinyin"
  53. :class="[
  54. noFont.indexOf(
  55. items.detail.wordsList[indexCon + 1].pinyin
  56. ) > -1
  57. ? 'noFont'
  58. : '',
  59. ]"
  60. >{{
  61. items.detail.wordsList[indexCon + 1].pinyin
  62. }}</span
  63. >
  64. <span class="hanzi content-con">{{
  65. items.detail.wordsList[indexCon + 1].chs
  66. }}</span>
  67. </div>
  68. </div>
  69. </template>
  70. <template v-else>
  71. <span
  72. class="pinyin"
  73. :class="[
  74. noFont.indexOf(itemCon.pinyin) > -1 ? 'noFont' : '',
  75. ]"
  76. >{{ itemCon.pinyin }}</span
  77. >
  78. <span class="hanzi content-con">{{ itemCon.chs }}</span>
  79. </template>
  80. </div>
  81. </div>
  82. </template>
  83. <b class="content-en" v-if="items.en">{{ items.en }}</b>
  84. </template>
  85. <template v-else>
  86. <div class="chs_pinyin">
  87. <div
  88. v-for="(conent, conIndex) in items.detail.wordsList"
  89. :key="conIndex + 'con'"
  90. >
  91. <template v-if="items.detail.pyPosition == 'top'">
  92. <div
  93. v-if="conent.pinyin == '__'"
  94. contenteditable="true"
  95. class="input pinyin"
  96. v-html="answerList[indexs].pinyin"
  97. @change="changeAnswerList($event, indexs, 'pinyin')"
  98. ></div>
  99. <!--
  100. v-model="answerList[indexs].pinyin"
  101. -->
  102. <p class="pinyin" :class="[conent.pinyin.indexOf('__')>-1?'pinyin-opacity':'']" v-if="conent.pinyin">
  103. {{ conent.pinyin }}
  104. </p>
  105. </template>
  106. <template v-if="conent.chs.indexOf('__')>-1">
  107. <!-- {{items.detail.resArr[conIndex].inputNumber}} -->
  108. <EditDiv class="input chs" v-model='curQue.Bookanswer[indexs].answerList[items.detail.resArr[conIndex].inputNumber].chs' :canEdit="TaskModel=='ANSWER'?false:true"></EditDiv>
  109. <!-- <el-input
  110. class="input chs"
  111. type="textarea"
  112. autosize
  113. placeholder="输入"
  114. v-model="curQue.Bookanswer[indexs].answerList[items.detail.resArr[conIndex].inputNumber].chs"
  115. :readonly="TaskModel=='ANSWER'"
  116. maxlength="200">
  117. </el-input> -->
  118. </template>
  119. <template v-else>
  120. <p class="chs">
  121. {{ conent.chs }}
  122. </p>
  123. </template>
  124. <template
  125. v-if="items.detail.pyPosition == 'bottom' && conent.pinyin"
  126. >
  127. <!-- <div
  128. v-if="conent.pinyin == '__'"
  129. contenteditable="true"
  130. class="input pinyin"
  131. v-html="answerList[indexs].pinyin"
  132. @change="changeAnswerList($event, indexs, 'pinyin')"
  133. ></div> -->
  134. <p class="pinyin" :class="[conent.pinyin.indexOf('__')>-1?'pinyin-opacity':'']" v-if="conent.pinyin">
  135. {{ conent.pinyin }}
  136. </p>
  137. </template>
  138. </div>
  139. </div>
  140. </template>
  141. </div>
  142. <template v-if="dataType.indexOf('sentence_answer_chs') > -1 || dataType.indexOf('sentence_input_chs') > -1">
  143. <input
  144. @input="handleInput"
  145. :class="['item-input']"
  146. v-model="curQue.Bookanswer[indexs].answer"
  147. placeholder="输入"
  148. />
  149. </template>
  150. <template v-if="dataType.indexOf('sentence_judge_chs') > -1">
  151. <div class="judge-box" :class="[TaskModel == 'ANSWER'?'notAllow':'']">
  152. <a
  153. :class="[
  154. 'right-btn',
  155. curQue.Bookanswer[indexs].answer == 'right' ? 'active' : '',
  156. ]"
  157. @click="handleSelectJudge('right', indexs)"
  158. ></a>
  159. <a
  160. :class="[
  161. 'error-btn',
  162. curQue.Bookanswer[indexs].answer == 'error' ? 'active' : '',
  163. ]"
  164. @click="handleSelectJudge('error', indexs)"
  165. ></a>
  166. </div>
  167. </template>
  168. <template v-if="dataType.indexOf('sentence_record_chs') > -1">
  169. <template v-if="dataType.indexOf('sentence_input_chs') != -1">
  170. <Soundrecord
  171. @handleWav="handleWav"
  172. :answerRecordList="curQue.Bookanswer[indexs].recordList"
  173. :tmIndex="indexs"
  174. :TaskModel="TaskModel"
  175. type="normal"
  176. v-if="items.IsRecord"
  177. :style="{marginLeft:'8px'}"
  178. />
  179. </template>
  180. <template v-else>
  181. <Soundrecord
  182. @handleWav="handleWav"
  183. :answerRecordList="curQue.Bookanswer[indexs].recordList"
  184. :tmIndex="indexs"
  185. :TaskModel="TaskModel"
  186. type="mini"
  187. class="luyin-box"
  188. v-if="items.IsRecord"
  189. :style="{marginLeft:'8px'}"
  190. />
  191. </template>
  192. </template>
  193. </li>
  194. </ul>
  195. </div>
  196. </template>
  197. <script>
  198. import Soundrecord from "../preview/Soundrecord.vue"; // 录音模板
  199. import EditDiv from "../preview/EditDiv.vue"
  200. export default {
  201. components: { Soundrecord, EditDiv },
  202. props: ["curQue","TaskModel"],
  203. data() {
  204. return {
  205. audio: new Audio(),
  206. oldsrc: "", // 存储播放音频的src 用来比对是否点击的是同一个音频
  207. palyClass: "",
  208. dataType: [], // 题型
  209. chsFhList: [",", "。", "“", ":", "》", "《", "?", "!", ";"],
  210. noFont: ["~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")"], // 对应不要拼音字体
  211. answerList: [],
  212. };
  213. },
  214. computed: {},
  215. watch: {},
  216. //方法集合
  217. methods: {
  218. // input 输入时
  219. handleInput(e) {
  220. e.target.value = e.target.value ? e.target.value.trim() : "";
  221. },
  222. handlePlayVoice(url) {
  223. let _this = this;
  224. if (!url) {
  225. return;
  226. }
  227. if (!this.audio.paused && this.oldsrc == url) {
  228. this.audio.pause();
  229. } else if (this.audio.paused && this.oldsrc == url) {
  230. this.audio.play();
  231. } else {
  232. _this.audio.pause();
  233. _this.audio.load();
  234. _this.audio.src = url;
  235. _this.oldsrc = url;
  236. _this.audio.loop = false;
  237. _this.audio.play();
  238. }
  239. },
  240. // 处理数据
  241. handleData() {
  242. let _this = this;
  243. this.dataType = [];
  244. this.curQue.fn_list.forEach((item) => {
  245. if (item.isFn) {
  246. _this.dataType.push(item.type);
  247. }
  248. });
  249. if (!this.curQue.Bookanswer) {
  250. let answerList = []
  251. let curCorrect = [];
  252. this.curQue.option.forEach((item) => {
  253. if (this.dataType.indexOf("sentence_input_chs") != -1) {
  254. answerList = []
  255. item.answer.forEach((it) => {
  256. answerList.push({
  257. chs: "",
  258. pinyin: "",
  259. });
  260. });
  261. }
  262. curCorrect.push({
  263. answer: "",
  264. recordList: [],
  265. answerList: answerList
  266. });
  267. });
  268. this.$set(this.curQue, "Bookanswer", curCorrect);
  269. }
  270. let curQue = JSON.parse(JSON.stringify(this.curQue));
  271. curQue.option.forEach((dItem, dIndex) => {
  272. let paraArr = [];
  273. let inputNumber = 0
  274. dItem.detail.wordsList.forEach((sItem, sIndex) => {
  275. this.mergeWordSymbol(dItem.detail.wordsList, sItem, sIndex);
  276. let obj = {
  277. pinyin: sItem.pinyin,
  278. chs: sItem.chs,
  279. isShow: sItem.isShow,
  280. inputNumber: sItem.chs.indexOf('__')>-1?inputNumber:''
  281. };
  282. paraArr.push(obj);
  283. if(sItem.chs.indexOf('__')>-1){
  284. inputNumber++
  285. }
  286. });
  287. this.$set(_this.curQue.option[dIndex].detail, "resArr", paraArr);
  288. });
  289. console.log(_this.curQue.option)
  290. },
  291. //词和标点合一起
  292. mergeWordSymbol(sItem, wItem, curIndex) {
  293. let leg = sItem.length;
  294. if (curIndex < leg - 1) {
  295. if (this.chsFhList.indexOf(wItem.chs) > -1) {
  296. wItem.isShow = false;
  297. } else {
  298. wItem.isShow = true;
  299. }
  300. }
  301. },
  302. // 判断题选择
  303. handleSelectJudge(obj, index) {
  304. if(!this.TaskModel||this.TaskModel!='ANSWER'){
  305. this.curQue.Bookanswer[index].answer = obj
  306. }
  307. },
  308. handleWav(list, tmIndex) {
  309. tmIndex = tmIndex ? tmIndex : 0;
  310. this.$set(this.curQue.Bookanswer[tmIndex], "recordList", list);
  311. },
  312. handleDatas(str, type) {
  313. str = str.trim();
  314. str = str.replace(/_{2,}/g, "^ ");
  315. str =
  316. type == "hanzi" ? str.replace(/\s+/g, "") : str.replace(/\s+/g, " ");
  317. let resArr = type == "hanzi" ? str.split("") : str.split(/\s+/);
  318. return resArr;
  319. },
  320. changeAnswerList(e, index, type, indexs) {
  321. if (type == "pinyin") {
  322. this.curQue.Bookanswer[index].answerList[indexs].pinyin += e.target.innerHTML;
  323. } else {
  324. this.curQue.Bookanswer[index].answerList[indexs].chs += e.target.innerHTML;
  325. }
  326. },
  327. handleFalse(){
  328. return false;
  329. }
  330. },
  331. //生命周期 - 创建完成(可以访问当前this实例)
  332. created() {
  333. this.handleData();
  334. },
  335. //生命周期 - 挂载完成(可以访问DOM元素)
  336. mounted() {
  337. let _this = this;
  338. _this.audio.addEventListener("play", function () {
  339. _this.palyClass = "active";
  340. });
  341. _this.audio.addEventListener("pause", function () {
  342. _this.palyClass = "";
  343. });
  344. _this.audio.addEventListener("ended", function () {
  345. _this.palyClass = "";
  346. });
  347. },
  348. beforeCreate() {}, //生命周期 - 创建之前
  349. beforeMount() {}, //生命周期 - 挂载之前
  350. beforeUpdate() {}, //生命周期 - 更新之前
  351. updated() {}, //生命周期 - 更新之后
  352. beforeDestroy() {}, //生命周期 - 销毁之前
  353. destroyed() {}, //生命周期 - 销毁完成
  354. activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
  355. };
  356. </script>
  357. <style lang='scss' scoped>
  358. //@import url(); 引入公共css类
  359. .Big-Book-prev-Textdes {
  360. width: 100%;
  361. h2 {
  362. font-weight: normal;
  363. font-size: 16px;
  364. line-height: 150%;
  365. color: #000000;
  366. margin: 0 2px 8px 2px;
  367. }
  368. ul {
  369. display: flex;
  370. flex-flow: wrap;
  371. justify-content: start;
  372. list-style: none;
  373. li {
  374. width: 100%;
  375. background: #ffffff;
  376. border: 1px solid rgba(0, 0, 0, 0.1);
  377. box-sizing: border-box;
  378. border-radius: 8px;
  379. display: flex;
  380. align-items: center;
  381. padding: 8px 12px;
  382. margin-bottom: 8px;
  383. > b {
  384. width: 24px;
  385. line-height: 24px;
  386. font-size: 16px;
  387. text-align: center;
  388. margin-right: 8px;
  389. font-weight: 400;
  390. color: rgba(0, 0, 0, 1);
  391. // margin-top: 4px;
  392. font-family: "FZJCGFKTK";
  393. border-radius: 50%;
  394. }
  395. > p {
  396. width: 16px;
  397. height: 16px;
  398. cursor: pointer;
  399. background: url("../../../assets/NPC/play-red.png") left
  400. center no-repeat;
  401. background-size: 16px;
  402. margin: 0px 8px 0 0;
  403. &.active {
  404. background: url("../../../assets/NPC/icon-voice-play-red.png")
  405. left center no-repeat;
  406. background-size: 16px;
  407. }
  408. }
  409. .item-content {
  410. flex: 1;
  411. .chs_pinyin {
  412. display: flex;
  413. flex-flow: wrap;
  414. align-items: flex-end;
  415. .input {
  416. // margin-left: 16px;
  417. margin-right: 8px;
  418. min-width: 50px;
  419. border-bottom: 1px solid black;
  420. min-height: 28px;
  421. height: auto;
  422. }
  423. .pinyin {
  424. font-family: "GB-PINYINOK-B";
  425. font-size: 14px;
  426. line-height: 130%;
  427. color: rgba(0, 0, 0, 0.6);
  428. margin-top: 8px;
  429. &-opacity{
  430. opacity: 0;
  431. }
  432. }
  433. .chs {
  434. font-family: "FZJCGFKTK";
  435. font-style: normal;
  436. font-weight: normal;
  437. font-size: 20px;
  438. line-height: 150%;
  439. color: #000000;
  440. }
  441. p {
  442. margin: 0;
  443. }
  444. }
  445. .content-con {
  446. font-size: 20px;
  447. line-height: 150%;
  448. color: #000000;
  449. font-family: "FZJCGFKTK";
  450. margin: 0;
  451. &.content-con-small {
  452. font-size: 16px;
  453. }
  454. }
  455. .content-en {
  456. font-weight: normal;
  457. font-size: 16px;
  458. line-height: 150%;
  459. color: #000000;
  460. font-family: "robot";
  461. }
  462. .con-box {
  463. display: flex;
  464. flex-flow: wrap;
  465. .con-item {
  466. text-align: center;
  467. padding: 0 3px;
  468. &.con-item-0 {
  469. text-align: left;
  470. padding-left: 0;
  471. }
  472. }
  473. span {
  474. display: block;
  475. }
  476. .pinyin {
  477. font-family: "GB-PINYINOK-B";
  478. font-size: 14px;
  479. line-height: 130%;
  480. color: #000000;
  481. height: 18px;
  482. &.noFont {
  483. font-family: initial;
  484. }
  485. }
  486. .synthesis-box {
  487. display: flex;
  488. }
  489. }
  490. }
  491. input {
  492. width: 68px;
  493. border: 1px solid rgba(0, 0, 0, 0.15);
  494. box-sizing: border-box;
  495. border-radius: 4px;
  496. outline: none;
  497. height: 32px;
  498. padding: 4px 8px;
  499. color: #000000;
  500. text-align: center;
  501. font-size: 16px;
  502. line-height: 150%;
  503. }
  504. .judge-box {
  505. display: flex;
  506. justify-content: center;
  507. &.notAllow{
  508. a,a.error-btn{
  509. cursor: not-allowed;
  510. }
  511. }
  512. a {
  513. width: 32px;
  514. height: 32px;
  515. background: #fff url("../../../assets/newImage/common/right-btn.png")
  516. center no-repeat;
  517. background-size: 24px;
  518. border-radius: 8px;
  519. border: 1px solid rgba(0, 0, 0, 0.1);
  520. &:hover,
  521. &.active {
  522. background-color: #e5fff0;
  523. border-color: #00c850;
  524. }
  525. }
  526. a.error-btn {
  527. background: #fff url("../../../assets/newImage/common/error-btn.png")
  528. center no-repeat;
  529. background-size: 24px;
  530. margin-left: 4px;
  531. &:hover,
  532. &.active {
  533. background-color: #ffe5e5;
  534. border-color: #de4444;
  535. }
  536. }
  537. }
  538. .luyin-box {
  539. border: 1px solid rgba(0, 0, 0, 0.1);
  540. border-radius: 8px;
  541. width: 64px;
  542. }
  543. }
  544. }
  545. }
  546. .NPC-Big-Book-preview-green {
  547. .Big-Book-prev-Textdes {
  548. ul{
  549. li{
  550. >p{
  551. background: url("../../../assets/NPC/play-green.png") center no-repeat;
  552. background-size: cover;
  553. &.active {
  554. background-image: url("../../../assets/NPC/icon-voice-play-green.png");
  555. background-size: cover;
  556. }
  557. }
  558. }
  559. }
  560. }
  561. }
  562. .NPC-Big-Book-preview-brown {
  563. .Big-Book-prev-Textdes {
  564. ul{
  565. li{
  566. >p{
  567. background: url("../../../assets/NPC/play-brown.png") center no-repeat;
  568. background-size: cover;
  569. &.active {
  570. background-image: url("../../../assets/NPC/icon-voice-play-brown.png");
  571. background-size: cover;
  572. }
  573. }
  574. }
  575. }
  576. }
  577. }
  578. </style>