Practicechs.vue 43 KB

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