Practicechs.vue 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. <!-- -->
  2. <template>
  3. <div v-if="curQue" class="NNPE-ArticleView">
  4. <!-- <a class="ArticleView-full" @click="fullScreen">全屏模式</a> -->
  5. <div
  6. v-if="
  7. (curQue.mp3_list && curQue.mp3_list.length > 0 && curQue.mp3_list[0].url) || config.isHasPY || config.isHasEN
  8. "
  9. class="aduioLine-box aduioLine-practice-npc"
  10. >
  11. <div v-if="curQue.mp3_list && curQue.mp3_list.length > 0 && curQue.mp3_list[0].url" class="aduioLine-content">
  12. <AudioLine
  13. ref="audioLine"
  14. audio-id="artPraAudio"
  15. :mp3="curQue.mp3_list[0].url"
  16. :get-cur-time="getCurTime"
  17. :stop-audio="stopAudio"
  18. :width="colLength == 2 ? 175 : 700"
  19. :mp3-source="curQue.mp3_list[0].source"
  20. :ed="ed"
  21. type="audioLine"
  22. @handleChangeStopAudio="handleChangeStopAudio"
  23. @emptyEd="emptyEd"
  24. />
  25. </div>
  26. <div class="aduioLine-right">
  27. <!-- <span :class="['Repeat-16', isRepeat ? '' : 'disabled']" @click="changeRepeat"></span>
  28. <span
  29. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  30. @click="changePinyin"
  31. v-if="config.isHasPY"
  32. ></span>
  33. <span :class="['EN-16', config.isShowEN ? '' : 'disabled']" @click="changeEN" v-if="config.isHasEN"></span> -->
  34. <SvgIcon
  35. icon-class="repeat"
  36. size="16"
  37. :class="['Repeat-16', isRepeat ? '' : 'disabled']"
  38. :style="{ color: isRepeat ? themeColor : 'rgba(0,0,0,0.1)' }"
  39. @click="changeRepeat"
  40. />
  41. <SvgIcon
  42. v-if="config.isHasPY"
  43. icon-class="PinYin"
  44. size="16"
  45. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  46. :style="{ color: themeColor }"
  47. @click="changePinyin"
  48. />
  49. <SvgIcon
  50. v-if="config.isHasEN"
  51. icon-class="PinYin"
  52. size="16"
  53. :class="['EN-16', config.isShowEN ? '' : 'disabled']"
  54. :style="{ color: themeColor }"
  55. @click="changeEN"
  56. />
  57. </div>
  58. </div>
  59. <template v-if="resObj">
  60. <!-- -->
  61. <div class="NPC-sentences-list">
  62. <div
  63. v-for="(item, index) in resObj.sentList"
  64. :key="'detail' + index"
  65. :class="['NNPE-detail-box', sentIndex == index ? 'active' : '']"
  66. >
  67. <div
  68. :class="['NNPE-detail']"
  69. @click="
  70. handleChangeTime(
  71. curQue.wordTime && curQue.wordTime[index] && curQue.wordTime[index].bg,
  72. index,
  73. curQue.wordTime && curQue.wordTime[index] && curQue.wordTime[index].ed,
  74. )
  75. "
  76. >
  77. <div
  78. v-if="item.enwords && config.isShowEN && curQue.enPosition && curQue.enPosition == 'top'"
  79. :class="['enwords', sentIndex == index ? 'wordBlank' : '']"
  80. >
  81. {{ item.enwords }}
  82. </div>
  83. <div style="overflow: hidden; clear: both"></div>
  84. <div
  85. v-for="(pItem, pIndex) in item.sentArr"
  86. :key="'wordsList' + pIndex"
  87. class="NNPE-words"
  88. :class="[
  89. pItem.chs != '“' && pItem.wordIndex == 0 ? 'textLeft' : 'textCenter',
  90. pItem.chs == '“' ? 'textRight' : '',
  91. ]"
  92. >
  93. <template v-if="!pItem.width">
  94. <template v-if="pItem.isShow">
  95. <template
  96. v-if="
  97. item.sentArr[pIndex + 1] &&
  98. item.sentArr[pIndex + 1].chs &&
  99. chsFhList.indexOf(item.sentArr[pIndex + 1].chs) > -1
  100. "
  101. >
  102. <span class="NNPE-words-box">
  103. <template v-if="curQue.property.pinyin_position == 'top'">
  104. <span
  105. v-if="config.isShowPY"
  106. class="NNPE-pinyin"
  107. :class="[
  108. pItem.className ? pItem.className : '',
  109. sentIndex == index ? 'wordBlank' : '',
  110. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  111. ]"
  112. >{{ pItem.pinyin }}</span
  113. >
  114. </template>
  115. <span
  116. class="NNPE-chs"
  117. :class="[
  118. pItem.padding && config.isShowPY ? 'padding' : '',
  119. sentIndex == index ? 'wordBlank' : '',
  120. ]"
  121. >
  122. <template>
  123. <span
  124. v-for="(wItem, wIndex) in pItem.leg"
  125. :key="'ci' + wIndex + pIndex + index"
  126. :class="[
  127. pItem.timeList[wIndex] &&
  128. curTime >= pItem.timeList[wIndex].wordBg &&
  129. curTime <= curQue.wordTime[index].ed
  130. ? 'active'
  131. : '',
  132. sentIndex == index ? 'wordBlank' : '',
  133. ]"
  134. :style="{
  135. fontFamily: pItem.config.fontFamily,
  136. height: '28px',
  137. display: 'inline-block',
  138. width: pItem.chs[wIndex].trim() === '' ? '6px' : '',
  139. }"
  140. >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.chs[wIndex] : '' }}</span
  141. >
  142. </template>
  143. </span>
  144. <template v-if="curQue.property.pinyin_position == 'bottom'">
  145. <span
  146. v-if="config.isShowPY"
  147. class="NNPE-pinyin"
  148. :class="[
  149. pItem.className ? pItem.className : '',
  150. sentIndex == index ? 'wordBlank' : '',
  151. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  152. ]"
  153. >{{ pItem.pinyin }}</span
  154. >
  155. </template>
  156. </span>
  157. <span class="NNPE-words-box">
  158. <template v-if="curQue.property.pinyin_position == 'top'">
  159. <span
  160. v-if="config.isShowPY"
  161. :class="[
  162. 'NNPE-pinyin',
  163. sentIndex == index ? 'wordBlank' : '',
  164. noFont.indexOf(item.sentArr[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
  165. ]"
  166. style="text-align: left"
  167. >{{ item.sentArr[pIndex + 1].pinyin }}</span
  168. >
  169. </template>
  170. <span class="NNPE-chs" style="text-align: left" :class="[sentIndex == index ? 'wordBlank' : '']">
  171. <span
  172. :class="[
  173. pItem.timeList[pItem.leg - 1] &&
  174. curQue.wordTime &&
  175. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  176. curTime <= curQue.wordTime[index].ed
  177. ? 'active'
  178. : '',
  179. sentIndex == index ? 'wordBlank' : '',
  180. ]"
  181. :style="{
  182. fontFamily: item.sentArr[pIndex + 1].config.fontFamily,
  183. height: '28px',
  184. display: 'inline-block',
  185. width: item.sentArr[pIndex + 1].chs.trim() === '' ? '6px' : '',
  186. }"
  187. >
  188. {{
  189. NumberList.indexOf(item.sentArr[pIndex + 1].pinyin) == -1
  190. ? item.sentArr[pIndex + 1].chs
  191. : ''
  192. }}</span
  193. >
  194. </span>
  195. <template v-if="curQue.property.pinyin_position == 'bottom'">
  196. <span
  197. v-if="config.isShowPY"
  198. :class="[
  199. 'NNPE-pinyin',
  200. sentIndex == index ? 'wordBlank' : '',
  201. noFont.indexOf(item.sentArr[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
  202. ]"
  203. style="text-align: left"
  204. >{{ item.sentArr[pIndex + 1].pinyin }}</span
  205. >
  206. </template>
  207. </span>
  208. <span
  209. v-if="
  210. item.sentArr[pIndex + 2] &&
  211. item.sentArr[pIndex + 2].chs &&
  212. chsFhList.indexOf(item.sentArr[pIndex + 2].chs) > -1
  213. "
  214. class="NNPE-words-box"
  215. >
  216. <template v-if="curQue.property.pinyin_position == 'top'">
  217. <span
  218. v-if="config.isShowPY"
  219. :class="[
  220. 'NNPE-pinyin',
  221. sentIndex == index ? 'wordBlank' : '',
  222. noFont.indexOf(item.sentArr[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
  223. ]"
  224. style="text-align: left"
  225. >{{ item.sentArr[pIndex + 2].pinyin }}</span
  226. >
  227. </template>
  228. <span class="NNPE-chs" style="text-align: left" :class="[sentIndex == index ? 'wordBlank' : '']">
  229. <span
  230. :class="[
  231. pItem.timeList[pItem.leg - 1] &&
  232. curQue.wordTime &&
  233. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  234. curTime <= curQue.wordTime[index].ed
  235. ? 'active'
  236. : '',
  237. sentIndex == index ? 'wordBlank' : '',
  238. ]"
  239. :style="{
  240. fontFamily: item.sentArr[pIndex + 2].config.fontFamily,
  241. height: '28px',
  242. display: 'inline-block',
  243. width: item.sentArr[pIndex + 2].chs.trim() === '' ? '6px' : '',
  244. }"
  245. >
  246. {{
  247. NumberList.indexOf(item.sentArr[pIndex + 2].pinyin) == -1
  248. ? item.sentArr[pIndex + 2].chs
  249. : ''
  250. }}</span
  251. >
  252. </span>
  253. <template v-if="curQue.property.pinyin_position == 'bottom'">
  254. <span
  255. v-if="config.isShowPY"
  256. :class="[
  257. 'NNPE-pinyin',
  258. sentIndex == index ? 'wordBlank' : '',
  259. noFont.indexOf(item.sentArr[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
  260. ]"
  261. style="text-align: left"
  262. >{{ item.sentArr[pIndex + 2].pinyin }}</span
  263. >
  264. </template>
  265. </span>
  266. </template>
  267. <template v-else>
  268. <template v-if="curQue.property.pinyin_position == 'top'">
  269. <span
  270. v-if="config.isShowPY"
  271. class="NNPE-pinyin"
  272. :class="[
  273. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  274. pItem.className ? pItem.className : '',
  275. sentIndex == index ? 'wordBlank' : '',
  276. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  277. ]"
  278. >{{ pItem.pinyin }}</span
  279. >
  280. </template>
  281. <span
  282. class="NNPE-chs"
  283. :class="[
  284. pItem.chs != '“' && pItem.padding && config.isShowPY ? 'padding' : '',
  285. sentIndex == index ? 'wordBlank' : '',
  286. ]"
  287. >
  288. <template>
  289. <span
  290. v-for="(wItem, wIndex) in pItem.leg"
  291. :key="'ci' + wIndex + pIndex + index"
  292. :class="[
  293. pItem.timeList[wIndex] &&
  294. curQue.wordTime &&
  295. curQue.wordTime[index] &&
  296. curTime >= pItem.timeList[wIndex].wordBg &&
  297. curTime <= curQue.wordTime[index].ed
  298. ? 'active'
  299. : '',
  300. sentIndex == index ? 'wordBlank' : '',
  301. ]"
  302. :style="{
  303. fontFamily: pItem.config.fontFamily,
  304. height: '28px',
  305. display: 'inline-block',
  306. width: pItem.chs[wIndex].trim() === '' ? '6px' : '',
  307. }"
  308. >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.chs[wIndex] : '' }}</span
  309. >
  310. </template>
  311. </span>
  312. <template v-if="curQue.property.pinyin_position == 'bottom'">
  313. <span
  314. v-if="config.isShowPY"
  315. class="NNPE-pinyin"
  316. :class="[
  317. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  318. pItem.className ? pItem.className : '',
  319. sentIndex == index ? 'wordBlank' : '',
  320. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  321. ]"
  322. >{{ pItem.pinyin }}</span
  323. >
  324. </template>
  325. </template>
  326. </template>
  327. </template>
  328. <template v-else>
  329. <span
  330. :style="{
  331. height: pItem.height + 'px',
  332. width: pItem.width + 'px',
  333. }"
  334. ></span>
  335. </template>
  336. </div>
  337. <div style="overflow: hidden; clear: both"></div>
  338. <div
  339. v-if="
  340. item.enwords &&
  341. config.isShowEN &&
  342. (!curQue.enPosition || (curQue.enPosition && curQue.enPosition == 'bottom'))
  343. "
  344. :class="['enwords', sentIndex == index ? 'wordBlank' : '']"
  345. >
  346. {{ item.enwords }}
  347. </div>
  348. </div>
  349. <div
  350. v-show="
  351. (curQue.wordTime &&
  352. curQue.wordTime[index] &&
  353. curTime >= curQue.wordTime[index].bg &&
  354. curTime <= curQue.wordTime[index].ed) ||
  355. sentIndex == index
  356. "
  357. class="Soundrecord-content"
  358. >
  359. <div class="Soundrecord-content-inner">
  360. <Soundrecord
  361. type="promax"
  362. class="luyin-box"
  363. :TaskModel="TaskModel"
  364. v-if="refresh"
  365. :answer-record-list="
  366. curQue.Bookanswer.practiceModel[index] && curQue.Bookanswer.practiceModel[index].recordList
  367. "
  368. :tm-index="index"
  369. :sent-index="sentIndex"
  370. @getWavblob="getWavblob"
  371. @handleParentPlay="handleParentPlay"
  372. @sentPause="sentPause"
  373. @handleWav="handleWav"
  374. />
  375. <div v-if="curQue.mp3_list && curQue.mp3_list.length > 0" class="compare-box">
  376. <Audio-compare
  377. :theme-color="themeColor"
  378. :index="index"
  379. :sent-index="sentIndex"
  380. :url="curQue.mp3_list[0].id"
  381. :bg="curQue.wordTime[index].bg"
  382. :ed="curQue.wordTime[index].ed"
  383. :wavblob="wavblob"
  384. :get-cur-time="getCurTime"
  385. :sent-pause="sentPause"
  386. :is-record="isRecord"
  387. :handle-change-stop-audio="handleChangeStopAudio"
  388. :get-play-status="getPlayStatus"
  389. />
  390. </div>
  391. </div>
  392. <span class="full-screen-icon" @click="fullScreen"> </span>
  393. </div>
  394. </div>
  395. <div class="multilingual" v-for="(items, indexs) in multilingualTextList" :key="indexs">
  396. {{ items }}
  397. </div>
  398. </div>
  399. </template>
  400. <div :id="'screen-' + mathNum" class="voice-full-screen">
  401. <Voicefullscreen
  402. v-if="isFull && resObj"
  403. :theme-color="themeColor"
  404. :cur-que="curQue"
  405. :sent-list="resObj.sentList"
  406. :sent-index="sentIndex"
  407. :mp3="curQue.mp3_list && curQue.mp3_list[0] ? curQue.mp3_list[0].url : ''"
  408. :no-font="noFont"
  409. :NNPENewWordList="NNPENewWordList"
  410. :current-tree-i-d="currentTreeID"
  411. :config="config"
  412. :TaskModel="TaskModel"
  413. @handleWav="handleWav"
  414. @changePinyin="changePinyin"
  415. @changeEN="changeEN"
  416. @exitFullscreen="exitFullscreen"
  417. @changeIsFull="changeIsFull"
  418. />
  419. </div>
  420. </div>
  421. </template>
  422. <script>
  423. import AudioLine from '../voice_matrix/components/AudioLine.vue';
  424. import Soundrecord from '../../common/SoundRecord.vue';
  425. import AudioCompare from './components/AudioCompare.vue';
  426. import Voicefullscreen from './Voicefullscreen.vue';
  427. export default {
  428. name: 'ArticleView',
  429. components: {
  430. AudioLine,
  431. Soundrecord,
  432. AudioCompare,
  433. Voicefullscreen,
  434. },
  435. props: [
  436. 'curQue',
  437. 'noFont',
  438. 'themeColor',
  439. 'NNPENewWordList',
  440. 'currentTreeID',
  441. 'config',
  442. 'TaskModel',
  443. 'colLength',
  444. 'isFull',
  445. 'multilingualTextList',
  446. ],
  447. data() {
  448. return {
  449. resObj: null,
  450. curTime: 0, // 单位s
  451. chsFhList: [',', '。', '”', ':', '》', '?', '!', ';', '、'],
  452. enFhList: [',', '.', ';', '?', '!', ':', '>', '<'],
  453. NumberList: ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳'],
  454. stopAudio: false,
  455. sentIndex: 0,
  456. isRepeat: false,
  457. currSent: null, // 当前句子的时间
  458. isRecord: false,
  459. wavblob: null,
  460. mathNum: Math.random().toString(36).substr(2),
  461. ed: undefined,
  462. refresh: true,
  463. };
  464. },
  465. computed: {
  466. isPlaying() {
  467. let playing = false;
  468. if (this.$refs.audioLine) {
  469. playing = this.$refs.audioLine.audio.isPlaying;
  470. }
  471. return playing;
  472. },
  473. },
  474. watch: {
  475. sentIndex: {
  476. handler(newVal, oldVal) {
  477. let _this = this;
  478. if (newVal != oldVal) {
  479. let Bookanswer = _this.curQue.Bookanswer;
  480. if (
  481. Bookanswer &&
  482. Bookanswer.practiceModel &&
  483. Bookanswer.practiceModel[newVal] &&
  484. Bookanswer.practiceModel[newVal].recordList &&
  485. Bookanswer.practiceModel[newVal].recordList.length > 0
  486. ) {
  487. _this.wavblob = Bookanswer.practiceModel[newVal].recordList[0].wavData;
  488. } else {
  489. _this.wavblob = '';
  490. }
  491. }
  492. },
  493. deep: true,
  494. },
  495. isFull: {
  496. handler(newVal, oldVal) {
  497. let _this = this;
  498. _this.refresh = false;
  499. if (!newVal) {
  500. _this.$nextTick(() => {
  501. // 重新渲染组件
  502. _this.refresh = true;
  503. });
  504. }
  505. },
  506. deep: true,
  507. },
  508. },
  509. // 生命周期 - 创建完成(可以访问当前this实例)
  510. created() {},
  511. // 生命周期 - 挂载完成(可以访问DOM元素)
  512. mounted() {
  513. if (this.curQue) {
  514. this.handleData();
  515. }
  516. },
  517. beforeCreate() {}, // 生命周期 - 创建之前
  518. beforeMount() {}, // 生命周期 - 挂载之前
  519. beforeUpdate() {}, // 生命周期 - 更新之前
  520. updated() {}, // 生命周期 - 更新之后
  521. beforeDestroy() {}, // 生命周期 - 销毁之前
  522. destroyed() {}, // 生命周期 - 销毁完成
  523. activated() {},
  524. // 方法集合
  525. methods: {
  526. getPlayStatus(val) {
  527. // this.isPlaying = val;
  528. },
  529. // 拼音的显示和隐藏
  530. changePinyin() {
  531. if (this.config.isHasPY) {
  532. this.$emit('changeConfig', 'isShowPY');
  533. }
  534. },
  535. // 英文的显示和隐藏
  536. changeEN() {
  537. if (this.config.isHasEN) {
  538. this.$emit('changeConfig', 'isShowEN');
  539. }
  540. },
  541. pauseAudio() {
  542. let audio = document.getElementsByTagName('audio');
  543. if (audio && audio.length > 0 && window.location.href.indexOf('GCLS-Learn') == -1 && audio.forEach) {
  544. audio.forEach((item) => {
  545. item.pause();
  546. });
  547. }
  548. },
  549. pauseVideo() {
  550. let video = document.getElementsByTagName('video');
  551. if (video && video.length > 0 && window.location.href.indexOf('GCLS-Learn') == -1 && video.forEach) {
  552. video.forEach((item) => {
  553. item.pause();
  554. });
  555. }
  556. },
  557. // 语音全屏
  558. fullScreen() {
  559. this.pauseAudio();
  560. this.pauseVideo();
  561. this.isFull = true;
  562. this.goFullscreen();
  563. },
  564. goFullscreen() {
  565. let id = `screen-${this.mathNum}`;
  566. let element = document.getElementById(id);
  567. if (element.requestFullscreen) {
  568. element.requestFullscreen();
  569. } else if (element.msRequestFullscreen) {
  570. element.msRequestFullscreen();
  571. } else if (element.mozRequestFullScreen) {
  572. element.mozRequestFullScreen();
  573. } else if (element.webkitRequestFullscreen) {
  574. element.webkitRequestFullscreen();
  575. }
  576. },
  577. exitFullscreen() {
  578. this.isFull = false;
  579. if (document.exitFullscreen) {
  580. document.exitFullscreen();
  581. } else if (document.msExitFullscreen) {
  582. document.msExitFullscreen();
  583. } else if (document.mozCancelFullScreen) {
  584. document.mozCancelFullScreen();
  585. } else if (document.webkitExitFullscreen) {
  586. document.webkitExitFullscreen();
  587. }
  588. },
  589. changeIsFull() {
  590. this.isFull = false;
  591. },
  592. getWavblob(wavblob) {
  593. this.wavblob = wavblob;
  594. },
  595. sentPause(isRecord) {
  596. this.isRecord = isRecord;
  597. },
  598. getCurTime(curTime) {
  599. let _this = this;
  600. if (_this.isRepeat) {
  601. let time = curTime * 1000;
  602. if (time >= _this.currSent.ed || time <= _this.currSent.bg) {
  603. _this.curTime = _this.currSent.bg;
  604. this.$refs.audioLine.onTimeupdateTime(_this.currSent.bg / 1000, true);
  605. } else {
  606. _this.curTime = curTime * 1000;
  607. }
  608. } else {
  609. _this.curTime = curTime * 1000;
  610. _this.getSentIndex(_this.curTime);
  611. }
  612. },
  613. getSentIndex(curTime) {
  614. for (let i = 0; i < this.curQue.wordTime.length; i++) {
  615. let bg = this.curQue.wordTime[i].bg;
  616. let ed = this.curQue.wordTime[i].ed;
  617. if (curTime >= bg && curTime <= ed) {
  618. this.sentIndex = i;
  619. break;
  620. }
  621. }
  622. },
  623. handleData() {
  624. let resArr = [];
  625. let sentArrTotal = [];
  626. let timeArr = [];
  627. let curQue = JSON.parse(JSON.stringify(this.curQue));
  628. let wordTimeList = curQue.wordTime;
  629. let dhaspinyin = false; // 每段是否有拼音
  630. let dhaspinyinArr = [];
  631. curQue.detail.forEach((dItem, dIndex) => {
  632. dhaspinyin = false;
  633. dItem.wordsList.forEach((sItem, sIndex) => {
  634. let sentArr = [];
  635. sItem.forEach((wItem, wIndex) => {
  636. let startIndex = wIndex == 0 ? 0 : sentArr[wIndex - 1].startIndex + sentArr[wIndex - 1].chs.length;
  637. let endIndex = wIndex == 0 ? wItem.chs.length : sentArr[wIndex - 1].endIndex + wItem.chs.length;
  638. // this.judgePad(sItem, wItem, wIndex);
  639. this.mergeWordSymbol(wItem);
  640. let obj = {
  641. paraIndex: dIndex, // 段落索引
  642. sentIndex: sIndex, // 在段落中句子索引
  643. wordIndex: wIndex, // 单词的索引
  644. pinyin:
  645. curQue.pinyin_type === 'pinyin'
  646. ? curQue.property.is_enable_sentence_case && wIndex === 0
  647. ? wItem.pinyin_up
  648. : wItem.pinyin
  649. : wItem.pinyin_tone,
  650. chs: wItem.chs,
  651. padding: true,
  652. className: wItem.className,
  653. isShow: wItem.isShow,
  654. startIndex,
  655. endIndex,
  656. leg: wItem.chs.length,
  657. timeList: [],
  658. config: {
  659. fontFamily: wItem.fontFamily,
  660. },
  661. };
  662. sentArr.push(obj);
  663. if (wItem.pinyin) dhaspinyin = true;
  664. });
  665. let objs = {
  666. sentArr,
  667. enwords: dItem.sentencesEn && dItem.sentencesEn[sIndex] && dItem.sentencesEn[sIndex].replace(/\'/g, '’'),
  668. };
  669. sentArrTotal.push(sentArr);
  670. resArr.push(objs);
  671. });
  672. timeArr.push(dItem.timeList);
  673. dhaspinyinArr.push(dhaspinyin);
  674. });
  675. if (wordTimeList && wordTimeList.length > 0) {
  676. this.mergeWordTime(sentArrTotal, wordTimeList);
  677. }
  678. let timeList = [];
  679. timeArr.forEach((item) => {
  680. item.forEach((aItem) => {
  681. if (timeList.indexOf(aItem) < 0) {
  682. timeList.push(aItem);
  683. }
  684. });
  685. });
  686. this.resObj = {
  687. sentList: resArr,
  688. timeList,
  689. dhaspinyinArr,
  690. };
  691. },
  692. mergeWordTime(resArr, wordTimeList) {
  693. resArr.forEach((item, index) => {
  694. let wordsResultList = wordTimeList[index].wordsResultList;
  695. item.forEach((wItem) => {
  696. let startIndex = wItem.startIndex;
  697. let endIndex = wItem.endIndex;
  698. wItem.timeList = wordsResultList.slice(startIndex, endIndex);
  699. });
  700. });
  701. },
  702. // 词和标点合一起
  703. mergeWordSymbol(wItem) {
  704. if (this.chsFhList.indexOf(wItem.chs) > -1) {
  705. wItem.isShow = false;
  706. } else {
  707. wItem.isShow = true;
  708. }
  709. },
  710. // 判断是否有padding
  711. judgePad(sItem, wItem, curIndex) {
  712. let leg = sItem.length;
  713. if (curIndex < leg - 1) {
  714. let nextIndex = curIndex + 1;
  715. let chs = sItem[nextIndex].chs;
  716. if (this.chsFhList.indexOf(chs) > -1 || this.chsFhList.indexOf(wItem.chs) > -1) {
  717. wItem.padding = false;
  718. } else {
  719. wItem.padding = true;
  720. }
  721. if (this.enFhList.indexOf(wItem.pinyin) > -1) {
  722. wItem.className = 'textLeft';
  723. }
  724. }
  725. },
  726. // 转化时间
  727. handleTimeList(list) {
  728. let listRes = [];
  729. list.forEach((item) => {
  730. let res = timeStrToSen(item);
  731. listRes.push(res);
  732. });
  733. return listRes;
  734. },
  735. // 分:秒转秒
  736. timeStrToSen(time) {
  737. if (!time) {
  738. return -1;
  739. }
  740. let pos = time.indexOf(':');
  741. let min = 0;
  742. var sec = 0;
  743. if (pos > 0) {
  744. min = parseInt(time.substring(0, pos));
  745. sec = parseFloat(time.substring(pos + 1));
  746. }
  747. return min * 60 + sec;
  748. },
  749. // 计算总时间
  750. countWordTime(sentArr) {
  751. let total = 0;
  752. sentArr.forEach((item) => {
  753. total += item.endTime;
  754. });
  755. return total;
  756. },
  757. // 点击播放某个句子
  758. handleChangeTime(time, index, ed) {
  759. let _this = this;
  760. if (this.isRepeat) {
  761. _this.currSent = _this.curQue.wordTime[index];
  762. }
  763. _this.sentIndex = index;
  764. _this.ed = ed;
  765. if (time) {
  766. _this.curTime = time;
  767. _this.$refs.audioLine.onTimeupdateTime(time / 1000, true);
  768. }
  769. },
  770. emptyEd() {
  771. this.ed = undefined;
  772. },
  773. handleWav(list, tmIndex) {
  774. tmIndex = tmIndex || 0;
  775. this.curQue.Bookanswer.practiceModel[tmIndex] = {
  776. recordList: [],
  777. };
  778. this.$set(this.curQue.Bookanswer.practiceModel[tmIndex], 'recordList', list);
  779. },
  780. // 录音时暂停音频播放
  781. handleParentPlay() {
  782. this.stopAudio = true;
  783. },
  784. // 音频播放时改变布尔值
  785. handleChangeStopAudio() {
  786. this.stopAudio = false;
  787. },
  788. // 单句是否重复播放
  789. changeRepeat() {
  790. let _this = this;
  791. _this.isRepeat = !_this.isRepeat;
  792. this.currSent = _this.curQue.wordTime[_this.sentIndex];
  793. },
  794. }, // 如果页面有keep-alive缓存功能,这个函数会触发
  795. };
  796. </script>
  797. <style lang="scss" scoped>
  798. //@import url(); 引入公共css类
  799. .NNPE-ArticleView {
  800. position: relative;
  801. width: 100%;
  802. .ArticleView-full {
  803. position: absolute;
  804. top: -33px;
  805. left: 0;
  806. z-index: 99999;
  807. padding-left: 24px;
  808. font-size: 14px;
  809. font-weight: bold;
  810. line-height: 24px;
  811. color: #000;
  812. background: url('@/assets/full-screen-red.png') left center no-repeat;
  813. background-size: 16px 16px;
  814. }
  815. .NPC-sentences-list {
  816. padding: 16px 0;
  817. }
  818. .multilingual {
  819. padding: 6px 24px 12px;
  820. word-break: break-word;
  821. }
  822. .aduioLine-content {
  823. flex: 1;
  824. }
  825. .NNPE-detail-box {
  826. box-sizing: border-box;
  827. width: 100%;
  828. padding: 8px 24px;
  829. margin-bottom: 8px;
  830. &.active {
  831. background: rgba(222, 68, 68, 15%);
  832. }
  833. }
  834. .aduioLine-practice-npc {
  835. display: flex;
  836. align-items: center;
  837. justify-content: flex-start;
  838. .aduioLine-right {
  839. box-sizing: border-box;
  840. display: flex;
  841. align-items: center;
  842. justify-content: space-between;
  843. width: 92px;
  844. height: 40px;
  845. padding: 0 12px;
  846. border-left: 1px solid rgba(0, 0, 0, 10%);
  847. .svg-icon {
  848. width: 16px;
  849. height: 16px;
  850. cursor: pointer;
  851. }
  852. }
  853. }
  854. .NNPE-detail {
  855. overflow: hidden;
  856. clear: both;
  857. .NNPE-words {
  858. float: left;
  859. &-box {
  860. float: left;
  861. > span {
  862. display: block;
  863. &.NNPE-pinyin {
  864. height: 20px;
  865. font-family: 'GB-PINYINOK-B';
  866. font-size: 14px;
  867. font-weight: normal;
  868. line-height: 20px;
  869. color: rgba(0, 0, 0, 45%);
  870. &.noFont {
  871. font-family: initial;
  872. }
  873. &.textLeft {
  874. text-align: left;
  875. }
  876. &.wordBlank {
  877. color: rgba(0, 0, 0, 85%);
  878. }
  879. }
  880. &.NNPE-chs {
  881. display: flex;
  882. flex-flow: wrap;
  883. font-family: '楷体';
  884. font-size: 20px;
  885. line-height: 28px;
  886. color: rgba(0, 0, 0, 45%);
  887. .active {
  888. color: #de4444;
  889. }
  890. &.wordBlank {
  891. color: rgba(0, 0, 0, 85%);
  892. }
  893. }
  894. // &.padding {
  895. // padding-right: 6px;
  896. // }
  897. }
  898. }
  899. &.textLeft {
  900. text-align: left;
  901. }
  902. &.textCenter {
  903. text-align: center;
  904. .NNPE-chs {
  905. justify-content: center;
  906. }
  907. }
  908. &.textRight {
  909. text-align: right;
  910. }
  911. > span {
  912. display: block;
  913. &.NNPE-pinyin {
  914. height: 20px;
  915. font-family: 'GB-PINYINOK-B';
  916. font-size: 14px;
  917. font-weight: normal;
  918. line-height: 20px;
  919. color: rgba(0, 0, 0, 45%);
  920. &.noFont {
  921. font-family: initial;
  922. }
  923. &.textLeft {
  924. text-align: left;
  925. }
  926. &.wordBlank {
  927. color: rgba(0, 0, 0, 85%);
  928. }
  929. }
  930. &.NNPE-chs {
  931. display: flex;
  932. flex-flow: wrap;
  933. font-family: '楷体';
  934. font-size: 20px;
  935. line-height: 28px;
  936. color: rgba(0, 0, 0, 45%);
  937. .active {
  938. color: #de4444;
  939. }
  940. &.wordBlank {
  941. color: rgba(0, 0, 0, 85%);
  942. }
  943. }
  944. &.padding {
  945. padding: 0 3px;
  946. }
  947. }
  948. }
  949. }
  950. .Soundrecord-content {
  951. display: flex;
  952. align-items: center;
  953. justify-content: space-between;
  954. margin-top: 8px;
  955. &-inner {
  956. display: flex;
  957. align-items: center;
  958. justify-content: flex-start;
  959. width: 304px;
  960. padding: 4px 12px;
  961. background: #fff;
  962. border: 1px solid rgba(0, 0, 0, 10%);
  963. border-radius: 8px;
  964. .luyin-box {
  965. width: 280px;
  966. max-width: 280px;
  967. }
  968. .compare-box {
  969. display: flex;
  970. align-items: center;
  971. justify-content: center;
  972. height: 32px;
  973. }
  974. }
  975. .full-screen-icon {
  976. width: 16px;
  977. height: 16px;
  978. cursor: pointer;
  979. background: url('@/assets/full-screen-red.png') no-repeat left top;
  980. background-size: 100% 100%;
  981. }
  982. }
  983. .enwords {
  984. padding-left: 3px;
  985. font-family: 'Helvetica';
  986. font-size: 14px;
  987. font-weight: normal;
  988. line-height: 22px;
  989. color: rgba(0, 0, 0, 45%);
  990. word-break: break-word;
  991. &.wordBlank {
  992. color: rgba(0, 0, 0, 85%);
  993. }
  994. }
  995. }
  996. </style>