Practicechs.vue 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811
  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) ||
  8. config.isHasPY ||
  9. config.isHasEN) &&
  10. curQue.property.mp3_position === 'top'
  11. "
  12. class="aduioLine-box aduioLine-practice-npc"
  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. ref="audioLine"
  18. audio-id="diaPraAudio"
  19. :mp3="curQue.mp3_list[0].url"
  20. :get-cur-time="getCurTime"
  21. :stop-audio="stopAudio"
  22. :width="colLength == 2 ? 175 : isPhone ? 200 : 750"
  23. :is-repeat="isRepeat"
  24. :mp3-source="curQue.mp3_list[0].source"
  25. :ed="ed"
  26. type="audioLine"
  27. :attrib="attrib"
  28. @handleChangeStopAudio="handleChangeStopAudio"
  29. @emptyEd="emptyEd"
  30. />
  31. </template>
  32. </div>
  33. <div class="aduioLine-right">
  34. <!-- <span :class="['Repeat-16', isRepeat ? '' : 'disabled']" @click="changeRepeat"></span> -->
  35. <SvgIcon
  36. v-if="config.isHasPY"
  37. icon-class="repeat-1"
  38. size="16"
  39. :class="['Repeat-16', isRepeat ? '' : 'disabled']"
  40. :style="{ color: isRepeat ? (attrib ? attrib.topic_color : '') : '#DCDFE6', cursor: 'pointer' }"
  41. @click="changeRepeat"
  42. />
  43. <!-- <span
  44. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  45. @click="changePinyin"
  46. v-if="config.isHasPY"
  47. ></span>
  48. <span :class="['EN-16', config.isShowEN ? '' : 'disabled']" @click="changeEN" v-if="config.isHasEN"></span> -->
  49. <SvgIcon
  50. v-if="config.isHasPY"
  51. icon-class="pin-btn"
  52. size="16"
  53. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  54. :style="{ color: config.isShowPY ? (attrib ? attrib.topic_color : '') : '#DCDFE6' }"
  55. @click="changePinyin"
  56. />
  57. <!-- <span :class="['EN-16', config.isShowEN ? '' : 'disabled']" @click="changeEN" v-if="config.isHasEN"></span> -->
  58. <SvgIcon
  59. v-if="config.isHasEN"
  60. icon-class="en-btn"
  61. size="16"
  62. :class="['EN-16', config.isShowEN ? '' : 'disabled']"
  63. :style="{ color: config.isShowEN ? (attrib ? attrib.topic_color : '') : '#DCDFE6' }"
  64. @click="changeEN"
  65. />
  66. </div>
  67. </div>
  68. <div
  69. :style="{
  70. height: curQue.property.content_height ? curQue.property.content_height + 'px' : '',
  71. overflow: 'auto',
  72. }"
  73. >
  74. <template v-if="resObj">
  75. <p
  76. v-if="curQue.notice"
  77. class="notice"
  78. style="padding-top: 24px"
  79. :style="{
  80. fontSize: curQue.property.notice_size ? curQue.property.notice_size + 'px' : '',
  81. color: curQue.property.notice_color ? curQue.property.notice_color : '',
  82. fontFamily: curQue.property.noitce_family ? curQue.property.noitce_family : '',
  83. }"
  84. >
  85. {{ convertText(curQue.notice) }}
  86. </p>
  87. <div class="NPC-sentences-list">
  88. <div
  89. v-for="(item, index) in resObj.sentList"
  90. :key="'detail' + index"
  91. :class="['NNPE-detail-box', sentIndex == index ? 'active' : '']"
  92. :style="{
  93. backgroundColor: sentIndex == index && attrib ? attrib.assist_color : '',
  94. }"
  95. >
  96. <div
  97. class="NNPE-detail"
  98. @click="
  99. handleChangeTime(
  100. curQue.wordTime && curQue.wordTime[index] && curQue.wordTime[index].bg,
  101. index,
  102. curQue.wordTime && curQue.wordTime[index] && curQue.wordTime[index].ed,
  103. )
  104. "
  105. >
  106. <template v-if="item.sentArr[0].sentIndex == 0">
  107. <RoleChs
  108. :cur-role="item.roleDetail"
  109. :color="
  110. (curQue.wordTime &&
  111. curQue.wordTime[index] &&
  112. curTime >= curQue.wordTime[index].bg &&
  113. curTime <= curQue.wordTime[index].ed) ||
  114. sentIndex == index
  115. ? 'rgba(0,0,0,0.85)'
  116. : 'rgba(0,0,0,0.45)'
  117. "
  118. :type="curQue.property.role_img_type"
  119. />
  120. </template>
  121. <div v-else style="width: 44px; height: 44px"></div>
  122. <div class="sentence-box">
  123. <template v-if="item.sentArr[0].sentIndex == 0">
  124. <!-- <div class="roleDetail" v-if="item.roleDetail.detail && item.roleDetail.detail.wordsList.length > 0">
  125. <span
  126. :class="[
  127. 'pinyin',
  128. (curQue.wordTime &&
  129. curQue.wordTime[index] &&
  130. curTime >= curQue.wordTime[index].bg &&
  131. curTime <= curQue.wordTime[index].ed) ||
  132. sentIndex == index
  133. ? 'color85'
  134. : 'color45',
  135. ]"
  136. >{{ item.roleDetail.detail.wordsList | handlePinyin }}</span
  137. >
  138. <span
  139. :class="[
  140. 'chs',
  141. (curQue.wordTime &&
  142. curQue.wordTime[index] &&
  143. curTime >= curQue.wordTime[index].bg &&
  144. curTime <= curQue.wordTime[index].ed) ||
  145. sentIndex == index
  146. ? 'color85'
  147. : 'color45',
  148. ]"
  149. >{{ item.roleDetail.detail.wordsList | handleChs }}</span
  150. >
  151. </div> -->
  152. <div v-if="item.roleDetail.fullName || item.roleDetail.fullPinyin" class="roleDetail">
  153. <span
  154. :class="[
  155. 'chs',
  156. (curQue.wordTime &&
  157. curQue.wordTime[index] &&
  158. curTime >= curQue.wordTime[index].bg &&
  159. curTime <= curQue.wordTime[index].ed) ||
  160. sentIndex == index
  161. ? 'color85'
  162. : 'color45',
  163. ]"
  164. >{{ convertText(item.roleDetail.fullName) }}</span
  165. >
  166. <span
  167. :class="[
  168. 'pinyin',
  169. (curQue.wordTime &&
  170. curQue.wordTime[index] &&
  171. curTime >= curQue.wordTime[index].bg &&
  172. curTime <= curQue.wordTime[index].ed) ||
  173. sentIndex == index
  174. ? 'color85'
  175. : 'color45',
  176. ]"
  177. >{{ item.roleDetail.fullPinyin }}</span
  178. >
  179. </div>
  180. </template>
  181. <div class="sentence-box-inner" :style="{ background: item.roleDetail.color.bg }">
  182. <div
  183. v-if="item.enwords && config.isShowEN && curQue.enPosition && curQue.enPosition == 'top'"
  184. :class="['enwords', sentIndex == index ? 'wordBlank' : '']"
  185. >
  186. {{ convertText(item.enwords) }}
  187. </div>
  188. <div style="overflow: hidden; clear: both"></div>
  189. <div class="NNPE-words-box">
  190. <div
  191. v-for="(pItem, pIndex) in item.sentArr"
  192. :key="'wordsList' + pIndex"
  193. class="NNPE-words"
  194. :class="[
  195. pItem.chs != '“' && pItem.wordIndex == 0 ? 'textLeft' : 'textCenter',
  196. pItem.chs == '“' ? 'textRight' : '',
  197. ]"
  198. >
  199. <template v-if="!pItem.width">
  200. <template v-if="pItem.isShow">
  201. <span
  202. v-if="
  203. item.sentArr[pIndex - 1] &&
  204. item.sentArr[pIndex - 1].chs &&
  205. firstFhlist.indexOf(item.sentArr[pIndex - 1].chs) > -1 &&
  206. item.sentArr[pIndex - 2] &&
  207. item.sentArr[pIndex - 2].chs &&
  208. firstFhlist.indexOf(item.sentArr[pIndex - 2].chs) > -1
  209. "
  210. class="NNPE-words-box"
  211. >
  212. <template v-if="curQue.property.pinyin_position == 'top'">
  213. <span
  214. v-if="config.isShowPY && item.dhaspinyin"
  215. :class="[
  216. 'NNPE-pinyin',
  217. sentIndex == index ? 'wordBlank' : '',
  218. noFont.indexOf(item.sentArr[pIndex - 2].pinyin) > -1 ? 'noFont' : '',
  219. ]"
  220. style="text-align: left"
  221. :style="{
  222. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  223. height:
  224. attrib && attrib.pinyin_size
  225. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  226. : '22px',
  227. }"
  228. >{{
  229. NumberList.indexOf(item.sentArr[pIndex - 2].pinyin) == -1
  230. ? item.sentArr[pIndex - 2].pinyin
  231. : ''
  232. }}</span
  233. >
  234. </template>
  235. <span class="NNPE-chs" style="text-align: left">
  236. <span
  237. :class="[
  238. isPlaying &&
  239. pItem.timeList[0] &&
  240. curTime >= pItem.timeList[0].wordBg &&
  241. curQue.wordTime &&
  242. curQue.wordTime[index] &&
  243. curTime <= curQue.wordTime[index].ed
  244. ? 'active'
  245. : '',
  246. sentIndex == index ? 'wordBlank' : '',
  247. ]"
  248. :style="{
  249. fontFamily: item.sentArr[pIndex - 2].config.fontFamily,
  250. textDecoration: item.sentArr[pIndex - 2].config.textDecoration,
  251. borderBottom: item.sentArr[pIndex - 2].config.border === 'dotted' ? '1px dotted' : '',
  252. fontWeight: item.sentArr[pIndex - 2].config.fontWeight,
  253. height:
  254. attrib && attrib.font_size
  255. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  256. : '28px',
  257. fontSize: attrib && attrib.font_size ? attrib.font_size : '20px',
  258. lineHeight:
  259. attrib && attrib.font_size
  260. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  261. : '28px',
  262. display: 'inline-block',
  263. color:
  264. isPlaying &&
  265. pItem.timeList[0] &&
  266. curTime >= pItem.timeList[0].wordBg &&
  267. curQue.wordTime &&
  268. curQue.wordTime[index] &&
  269. curTime <= curQue.wordTime[index].ed &&
  270. attrib
  271. ? attrib.topic_color
  272. : item.sentArr[pIndex - 2].config.color,
  273. whiteSpace: item.sentArr[pIndex - 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
  274. }"
  275. >
  276. {{
  277. NumberList.indexOf(item.sentArr[pIndex - 2].pinyin) == -1
  278. ? convertText(item.sentArr[pIndex - 2].chs)
  279. : ''
  280. }}</span
  281. >
  282. </span>
  283. <template v-if="curQue.property.pinyin_position == 'bottom'">
  284. <span
  285. v-if="config.isShowPY && item.dhaspinyin"
  286. :class="[
  287. 'NNPE-pinyin',
  288. sentIndex == index ? 'wordBlank' : '',
  289. noFont.indexOf(item.sentArr[pIndex - 2].pinyin) > -1 ? 'noFont' : '',
  290. ]"
  291. style="text-align: left"
  292. :style="{
  293. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  294. height:
  295. attrib && attrib.pinyin_size
  296. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  297. : '22px',
  298. }"
  299. >{{
  300. NumberList.indexOf(item.sentArr[pIndex - 2].pinyin) == -1
  301. ? item.sentArr[pIndex - 2].pinyin
  302. : ''
  303. }}</span
  304. >
  305. </template>
  306. </span>
  307. <span
  308. v-if="
  309. item.sentArr[pIndex - 1] &&
  310. item.sentArr[pIndex - 1].chs &&
  311. firstFhlist.indexOf(item.sentArr[pIndex - 1].chs) > -1
  312. "
  313. class="NNPE-words-box"
  314. >
  315. <template v-if="curQue.property.pinyin_position == 'top'">
  316. <span
  317. v-if="config.isShowPY && item.dhaspinyin"
  318. :class="[
  319. 'NNPE-pinyin',
  320. sentIndex == index ? 'wordBlank' : '',
  321. noFont.indexOf(item.sentArr[pIndex - 1].pinyin) > -1 ? 'noFont' : '',
  322. ]"
  323. style="text-align: left"
  324. :style="{
  325. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  326. height:
  327. attrib && attrib.pinyin_size
  328. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  329. : '22px',
  330. }"
  331. >{{
  332. NumberList.indexOf(item.sentArr[pIndex - 1].pinyin) == -1
  333. ? item.sentArr[pIndex - 1].pinyin
  334. : ''
  335. }}</span
  336. >
  337. </template>
  338. <span class="NNPE-chs" style="text-align: left">
  339. <span
  340. :class="[
  341. isPlaying &&
  342. pItem.timeList[0] &&
  343. curTime >= pItem.timeList[0].wordBg &&
  344. curQue.wordTime &&
  345. curQue.wordTime[index] &&
  346. curTime <= curQue.wordTime[index].ed
  347. ? 'active'
  348. : '',
  349. sentIndex == index ? 'wordBlank' : '',
  350. ]"
  351. :style="{
  352. fontFamily: item.sentArr[pIndex - 1].config.fontFamily,
  353. textDecoration: item.sentArr[pIndex - 1].config.textDecoration,
  354. borderBottom: item.sentArr[pIndex - 1].config.border === 'dotted' ? '1px dotted' : '',
  355. fontWeight: item.sentArr[pIndex - 1].config.fontWeight,
  356. height:
  357. attrib && attrib.font_size
  358. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  359. : '28px',
  360. fontSize: attrib && attrib.font_size ? attrib.font_size : '20px',
  361. lineHeight:
  362. attrib && attrib.font_size
  363. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  364. : '28px',
  365. display: 'inline-block',
  366. color:
  367. isPlaying &&
  368. pItem.timeList[0] &&
  369. curTime >= pItem.timeList[0].wordBg &&
  370. curQue.wordTime &&
  371. curQue.wordTime[index] &&
  372. curTime <= curQue.wordTime[index].ed &&
  373. attrib
  374. ? attrib.topic_color
  375. : item.sentArr[pIndex - 1].config.color,
  376. whiteSpace: item.sentArr[pIndex - 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
  377. }"
  378. >
  379. {{
  380. NumberList.indexOf(item.sentArr[pIndex - 1].pinyin) == -1
  381. ? convertText(item.sentArr[pIndex - 1].chs)
  382. : ''
  383. }}</span
  384. >
  385. </span>
  386. <template v-if="curQue.property.pinyin_position == 'bottom'">
  387. <span
  388. v-if="config.isShowPY && item.dhaspinyin"
  389. :class="[
  390. 'NNPE-pinyin',
  391. sentIndex == index ? 'wordBlank' : '',
  392. noFont.indexOf(item.sentArr[pIndex - 1].pinyin) > -1 ? 'noFont' : '',
  393. ]"
  394. style="text-align: left"
  395. :style="{
  396. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  397. height:
  398. attrib && attrib.pinyin_size
  399. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  400. : '22px',
  401. }"
  402. >{{
  403. NumberList.indexOf(item.sentArr[pIndex - 1].pinyin) == -1
  404. ? item.sentArr[pIndex - 1].pinyin
  405. : ''
  406. }}</span
  407. >
  408. </template>
  409. </span>
  410. <template
  411. v-if="
  412. (item.sentArr[pIndex + 1] &&
  413. item.sentArr[pIndex + 1].chs &&
  414. chsFhList.indexOf(item.sentArr[pIndex + 1].chs) > -1) ||
  415. (item.sentArr[pIndex + 1] &&
  416. item.sentArr[pIndex + 1].chs &&
  417. item.sentArr[pIndex + 1].chs == '#')
  418. "
  419. >
  420. <span class="NNPE-words-box">
  421. <template v-if="curQue.property.pinyin_position == 'top'">
  422. <span
  423. v-if="config.isShowPY && item.dhaspinyin"
  424. class="NNPE-pinyin"
  425. :class="[
  426. pItem.className ? pItem.className : '',
  427. sentIndex == index ? 'wordBlank' : '',
  428. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  429. ]"
  430. :style="{
  431. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  432. height:
  433. attrib && attrib.pinyin_size
  434. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  435. : '22px',
  436. }"
  437. >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
  438. >
  439. </template>
  440. <span
  441. class="NNPE-chs"
  442. :class="[
  443. pItem.padding && config.isShowPY && item.dhaspinyin ? 'padding' : '',
  444. sentIndex == index ? 'wordBlank' : '',
  445. ]"
  446. >
  447. <template>
  448. <span
  449. v-for="(wItem, wIndex) in pItem.leg"
  450. :key="'ci' + wIndex + pIndex + index"
  451. :class="[
  452. isPlaying &&
  453. pItem.timeList &&
  454. pItem.timeList[wIndex] &&
  455. curTime >= pItem.timeList[wIndex].wordBg &&
  456. curQue.wordTime &&
  457. curQue.wordTime[index] &&
  458. curTime <= curQue.wordTime[index].ed
  459. ? 'active'
  460. : '',
  461. sentIndex == index ? 'wordBlank' : '',
  462. ]"
  463. :style="{
  464. fontFamily: pItem.config.fontFamily,
  465. textDecoration: pItem.config.textDecoration,
  466. borderBottom: pItem.config.border === 'dotted' ? '1px dotted' : '',
  467. fontWeight: pItem.config.fontWeight,
  468. height:
  469. attrib && attrib.font_size
  470. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  471. : '28px',
  472. fontSize: attrib && attrib.font_size ? attrib.font_size : '20px',
  473. lineHeight:
  474. attrib && attrib.font_size
  475. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  476. : '28px',
  477. display: 'inline-block',
  478. color:
  479. isPlaying &&
  480. pItem.timeList &&
  481. pItem.timeList[wIndex] &&
  482. curTime >= pItem.timeList[wIndex].wordBg &&
  483. curQue.wordTime &&
  484. curQue.wordTime[index] &&
  485. curTime <= curQue.wordTime[index].ed &&
  486. attrib
  487. ? attrib.topic_color
  488. : pItem.config.color,
  489. whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
  490. }"
  491. >{{
  492. NumberList.indexOf(pItem.pinyin) == -1 ? convertText(pItem.chs[wIndex]) : ''
  493. }}</span
  494. >
  495. </template>
  496. </span>
  497. <template v-if="curQue.property.pinyin_position == 'bottom'">
  498. <span
  499. v-if="config.isShowPY && item.dhaspinyin"
  500. class="NNPE-pinyin"
  501. :class="[
  502. pItem.className ? pItem.className : '',
  503. sentIndex == index ? 'wordBlank' : '',
  504. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  505. ]"
  506. :style="{
  507. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  508. height:
  509. attrib && attrib.pinyin_size
  510. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  511. : '22px',
  512. }"
  513. >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
  514. >
  515. </template>
  516. </span>
  517. <span class="NNPE-words-box">
  518. <template v-if="curQue.property.pinyin_position == 'top'">
  519. <span
  520. v-if="config.isShowPY && item.dhaspinyin"
  521. :class="[
  522. 'NNPE-pinyin',
  523. sentIndex == index ? 'wordBlank' : '',
  524. noFont.indexOf(item.sentArr[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
  525. ]"
  526. style="text-align: left"
  527. :style="{
  528. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  529. height:
  530. attrib && attrib.pinyin_size
  531. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  532. : '22px',
  533. }"
  534. >{{
  535. NumberList.indexOf(item.sentArr[pIndex + 1].pinyin) == -1
  536. ? item.sentArr[pIndex + 1].pinyin
  537. : ''
  538. }}</span
  539. >
  540. </template>
  541. <span class="NNPE-chs" style="text-align: left">
  542. <span
  543. :class="[
  544. isPlaying &&
  545. pItem.timeList[pItem.leg - 1] &&
  546. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  547. curQue.wordTime &&
  548. curQue.wordTime[index] &&
  549. curTime <= curQue.wordTime[index].ed
  550. ? 'active'
  551. : '',
  552. sentIndex == index ? 'wordBlank' : '',
  553. ]"
  554. :style="{
  555. fontFamily: item.sentArr[pIndex + 1].config.fontFamily,
  556. textDecoration: item.sentArr[pIndex + 1].config.textDecoration,
  557. borderBottom:
  558. item.sentArr[pIndex + 1].config.border === 'dotted' ? '1px dotted' : '',
  559. fontWeight: item.sentArr[pIndex + 1].config.fontWeight,
  560. height:
  561. attrib && attrib.font_size
  562. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  563. : '28px',
  564. fontSize: attrib && attrib.font_size ? attrib.font_size : '20px',
  565. lineHeight:
  566. attrib && attrib.font_size
  567. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  568. : '28px',
  569. display: 'inline-block',
  570. color:
  571. isPlaying &&
  572. pItem.timeList[pItem.leg - 1] &&
  573. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  574. curQue.wordTime &&
  575. curQue.wordTime[index] &&
  576. curTime <= curQue.wordTime[index].ed &&
  577. attrib
  578. ? attrib.topic_color
  579. : item.sentArr[pIndex + 1].config.color,
  580. whiteSpace: item.sentArr[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
  581. }"
  582. >
  583. {{
  584. NumberList.indexOf(item.sentArr[pIndex + 1].pinyin) == -1
  585. ? convertText(item.sentArr[pIndex + 1].chs)
  586. : ''
  587. }}</span
  588. >
  589. </span>
  590. <template v-if="curQue.property.pinyin_position == 'bottom'">
  591. <span
  592. v-if="config.isShowPY && item.dhaspinyin"
  593. :class="[
  594. 'NNPE-pinyin',
  595. sentIndex == index ? 'wordBlank' : '',
  596. noFont.indexOf(item.sentArr[pIndex + 1].pinyin) > -1 ? 'noFont' : '',
  597. ]"
  598. style="text-align: left"
  599. :style="{
  600. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  601. height:
  602. attrib && attrib.pinyin_size
  603. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  604. : '22px',
  605. }"
  606. >{{
  607. NumberList.indexOf(item.sentArr[pIndex + 1].pinyin) == -1
  608. ? item.sentArr[pIndex + 1].pinyin
  609. : ''
  610. }}</span
  611. >
  612. </template>
  613. </span>
  614. <span
  615. v-if="
  616. item.sentArr[pIndex + 2] &&
  617. item.sentArr[pIndex + 2].chs &&
  618. chsFhList.indexOf(item.sentArr[pIndex + 2].chs) > -1
  619. "
  620. class="NNPE-words-box"
  621. >
  622. <template v-if="curQue.property.pinyin_position == 'top'">
  623. <span
  624. v-if="config.isShowPY && item.dhaspinyin"
  625. :class="[
  626. 'NNPE-pinyin',
  627. sentIndex == index ? 'wordBlank' : '',
  628. noFont.indexOf(item.sentArr[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
  629. ]"
  630. style="text-align: left"
  631. :style="{
  632. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  633. height:
  634. attrib && attrib.pinyin_size
  635. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  636. : '22px',
  637. }"
  638. >{{
  639. NumberList.indexOf(item.sentArr[pIndex + 2].pinyin) == -1
  640. ? item.sentArr[pIndex + 2].pinyin
  641. : ''
  642. }}</span
  643. >
  644. </template>
  645. <span class="NNPE-chs" style="text-align: left">
  646. <span
  647. :class="[
  648. isPlaying &&
  649. pItem.timeList[pItem.leg - 1] &&
  650. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  651. curQue.wordTime &&
  652. curQue.wordTime[index] &&
  653. curTime <= curQue.wordTime[index].ed
  654. ? 'active'
  655. : '',
  656. sentIndex == index ? 'wordBlank' : '',
  657. ]"
  658. :style="{
  659. fontFamily: item.sentArr[pIndex + 2].config.fontFamily,
  660. textDecoration: item.sentArr[pIndex + 2].config.textDecoration,
  661. borderBottom:
  662. item.sentArr[pIndex + 2].config.border === 'dotted' ? '1px dotted' : '',
  663. fontWeight: item.sentArr[pIndex + 2].config.fontWeight,
  664. height:
  665. attrib && attrib.font_size
  666. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  667. : '28px',
  668. fontSize: attrib && attrib.font_size ? attrib.font_size : '20px',
  669. lineHeight:
  670. attrib && attrib.font_size
  671. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  672. : '28px',
  673. display: 'inline-block',
  674. color:
  675. isPlaying &&
  676. pItem.timeList[pItem.leg - 1] &&
  677. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  678. curQue.wordTime &&
  679. curQue.wordTime[index] &&
  680. curTime <= curQue.wordTime[index].ed &&
  681. attrib
  682. ? attrib.topic_color
  683. : item.sentArr[pIndex + 2].config.color,
  684. whiteSpace: item.sentArr[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
  685. }"
  686. >
  687. {{
  688. NumberList.indexOf(item.sentArr[pIndex + 2].pinyin) == -1
  689. ? convertText(item.sentArr[pIndex + 2].chs)
  690. : ''
  691. }}</span
  692. >
  693. </span>
  694. <template v-if="curQue.property.pinyin_position == 'bottom'">
  695. <span
  696. v-if="config.isShowPY && item.dhaspinyin"
  697. :class="[
  698. 'NNPE-pinyin',
  699. sentIndex == index ? 'wordBlank' : '',
  700. noFont.indexOf(item.sentArr[pIndex + 2].pinyin) > -1 ? 'noFont' : '',
  701. ]"
  702. style="text-align: left"
  703. :style="{
  704. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  705. height:
  706. attrib && attrib.pinyin_size
  707. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  708. : '22px',
  709. }"
  710. >{{
  711. NumberList.indexOf(item.sentArr[pIndex + 2].pinyin) == -1
  712. ? item.sentArr[pIndex + 2].pinyin
  713. : ''
  714. }}</span
  715. >
  716. </template>
  717. </span>
  718. </template>
  719. <template v-else>
  720. <span class="NNPE-words-box">
  721. <template v-if="curQue.property.pinyin_position == 'top'">
  722. <span
  723. v-if="config.isShowPY && item.dhaspinyin"
  724. class="NNPE-pinyin"
  725. :class="[
  726. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  727. pItem.className ? pItem.className : '',
  728. sentIndex == index ? 'wordBlank' : '',
  729. noFont.indexOf(item.pinyin) > -1 ? 'noFont' : '',
  730. ]"
  731. :style="{
  732. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  733. height:
  734. attrib && attrib.pinyin_size
  735. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  736. : '22px',
  737. }"
  738. >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
  739. >
  740. </template>
  741. <span
  742. v-if="pItem.chs != '#'"
  743. class="NNPE-chs"
  744. :class="[
  745. pItem.chs != '“' && pItem.padding && config.isShowPY && item.dhaspinyin
  746. ? 'padding'
  747. : '',
  748. sentIndex == index ? 'wordBlank' : '',
  749. ]"
  750. >
  751. <template>
  752. <span
  753. v-for="(wItem, wIndex) in pItem.leg"
  754. :key="'ci' + wIndex + pIndex + index"
  755. :class="[
  756. isPlaying &&
  757. pItem.timeList &&
  758. pItem.timeList[wIndex] &&
  759. curTime >= pItem.timeList[wIndex].wordBg &&
  760. curQue.wordTime &&
  761. curQue.wordTime[index] &&
  762. curTime <= curQue.wordTime[index].ed
  763. ? 'active'
  764. : '',
  765. ]"
  766. :style="{
  767. fontFamily: pItem.config.fontFamily,
  768. textDecoration: pItem.config.textDecoration,
  769. borderBottom: pItem.config.border === 'dotted' ? '1px dotted' : '',
  770. fontWeight: pItem.config.fontWeight,
  771. height:
  772. attrib && attrib.font_size
  773. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  774. : '28px',
  775. fontSize: attrib && attrib.font_size ? attrib.font_size : '20px',
  776. lineHeight:
  777. attrib && attrib.font_size
  778. ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
  779. : '28px',
  780. display: 'inline-block',
  781. color:
  782. isPlaying &&
  783. pItem.timeList &&
  784. pItem.timeList[wIndex] &&
  785. curTime >= pItem.timeList[wIndex].wordBg &&
  786. curQue.wordTime &&
  787. curQue.wordTime[index] &&
  788. curTime <= curQue.wordTime[index].ed &&
  789. attrib
  790. ? attrib.topic_color
  791. : pItem.config.color,
  792. whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
  793. }"
  794. >{{
  795. NumberList.indexOf(pItem.chs[wIndex]) == -1 ? convertText(pItem.chs[wIndex]) : ''
  796. }}</span
  797. >
  798. </template>
  799. </span>
  800. <template v-if="curQue.property.pinyin_position == 'bottom'">
  801. <span
  802. v-if="config.isShowPY && item.dhaspinyin"
  803. class="NNPE-pinyin"
  804. :class="[
  805. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  806. pItem.className ? pItem.className : '',
  807. sentIndex == index ? 'wordBlank' : '',
  808. ]"
  809. :style="{
  810. fontSize: attrib && attrib.pinyin_size ? attrib.pinyin_size : '14px',
  811. height:
  812. attrib && attrib.pinyin_size
  813. ? attrib.pinyin_size.replace('pt', '') * 1.5 + 'pt'
  814. : '22px',
  815. }"
  816. >{{ NumberList.indexOf(pItem.pinyin) == -1 ? pItem.pinyin : '' }}</span
  817. >
  818. </template>
  819. </span>
  820. </template>
  821. </template>
  822. </template>
  823. <template v-else>
  824. <span
  825. :style="{
  826. height: pItem.height + 'px',
  827. width: pItem.width + 'px',
  828. }"
  829. ></span>
  830. </template>
  831. </div>
  832. </div>
  833. <div style="overflow: hidden; clear: both"></div>
  834. <div
  835. v-if="
  836. item.enwords &&
  837. config.isShowEN &&
  838. (!curQue.enPosition || (curQue.enPosition && curQue.enPosition == 'bottom'))
  839. "
  840. :class="['enwords', sentIndex == index ? 'wordBlank' : '']"
  841. >
  842. {{ convertText(item.enwords) }}
  843. </div>
  844. <div
  845. v-if="curQue.property.multilingual_position === 'para'"
  846. class="multilingual-para"
  847. :class="[item.isTitle ? 'multilingual-para-center' : '']"
  848. >
  849. {{
  850. multilingualTextList[multilingual] && multilingualTextList[multilingual][index]
  851. ? multilingualTextList[multilingual][index]
  852. : ''
  853. }}
  854. </div>
  855. </div>
  856. </div>
  857. </div>
  858. <div v-show="sentIndex == index" class="Soundrecord-content">
  859. <div class="Soundrecord-content-inner">
  860. <Soundrecord
  861. v-if="refresh"
  862. type="promax"
  863. class="luyin-box"
  864. :TaskModel="TaskModel"
  865. :answer-record-list="
  866. curQue.Bookanswer.practiceModel[index] && curQue.Bookanswer.practiceModel[index].recordList
  867. "
  868. :tm-index="index"
  869. :sent-index="sentIndex"
  870. :attrib="attrib"
  871. @getWavblob="getWavblob"
  872. @handleParentPlay="handleParentPlay"
  873. @sentPause="sentPause"
  874. @handleWav="handleWav"
  875. />
  876. <div v-if="curQue.mp3_list && curQue.mp3_list.length > 0" class="compare-box">
  877. <Audio-compare
  878. :theme-color="attrib ? attrib.topic_color : '#e35454'"
  879. :index="index"
  880. :sent-index="sentIndex"
  881. :url="curQue.mp3_list[0].url"
  882. :bg="curQue.wordTime[index].bg"
  883. :ed="curQue.wordTime[index].ed"
  884. :wavblob="wavblob"
  885. :get-cur-time="getCurTime"
  886. :sent-pause="sentPause"
  887. :is-record="isRecord"
  888. :handle-change-stop-audio="handleChangeStopAudio"
  889. :get-play-status="getPlayStatus"
  890. :attrib="attrib"
  891. />
  892. </div>
  893. </div>
  894. <span class="full-screen-icon" @click="fullScreen">
  895. <svg-icon
  896. icon-class="icon-full"
  897. size="24"
  898. :style="{
  899. color: attrib && attrib.topic_color ? attrib.topic_color : '',
  900. }"
  901. />
  902. </span>
  903. </div>
  904. </div>
  905. <!-- <div class="multilingual" v-for="(items, indexs) in multilingualTextList" :key="indexs">
  906. {{ items }}
  907. </div> -->
  908. </div>
  909. </template>
  910. <template v-for="(items, indexs) in curQue.detail">
  911. <div
  912. v-if="
  913. curQue.property.multilingual_position === 'all' &&
  914. items.multilingualTextList &&
  915. items.multilingualTextList[multilingual] &&
  916. items.multilingualTextList[multilingual].length > 0
  917. "
  918. :key="indexs"
  919. class="multilingual"
  920. >
  921. <div class="multilingual-para" :class="[items.isTitle ? 'multilingual-para-center' : '']">
  922. {{
  923. items.multilingualTextList && items.multilingualTextList[multilingual]
  924. ? items.multilingualTextList[multilingual].join(' ')
  925. : ''
  926. }}
  927. </div>
  928. </div>
  929. </template>
  930. </div>
  931. <div
  932. v-if="
  933. ((curQue.mp3_list && curQue.mp3_list.length > 0 && curQue.mp3_list[0].url) ||
  934. config.isHasPY ||
  935. config.isHasEN) &&
  936. curQue.property.mp3_position === 'bottom'
  937. "
  938. class="aduioLine-box aduioLine-practice-npc aduioLine-box-bottom"
  939. >
  940. <div class="aduioLine-content">
  941. <template v-if="curQue.mp3_list && curQue.mp3_list.length > 0 && curQue.mp3_list[0].url">
  942. <AudioLine
  943. ref="audioLine"
  944. audio-id="diaPraAudio"
  945. :mp3="curQue.mp3_list[0].url"
  946. :get-cur-time="getCurTime"
  947. :stop-audio="stopAudio"
  948. :width="colLength == 2 ? 175 : isPhone ? 200 : 750"
  949. :is-repeat="isRepeat"
  950. :mp3-source="curQue.mp3_list[0].source"
  951. :ed="ed"
  952. type="audioLine"
  953. :attrib="attrib"
  954. @handleChangeStopAudio="handleChangeStopAudio"
  955. @emptyEd="emptyEd"
  956. />
  957. </template>
  958. </div>
  959. <div class="aduioLine-right">
  960. <SvgIcon
  961. v-if="config.isHasPY"
  962. icon-class="repeat-1"
  963. size="16"
  964. :class="['Repeat-16', isRepeat ? '' : 'disabled']"
  965. :style="{ color: isRepeat ? (attrib ? attrib.topic_color : '') : '#DCDFE6', cursor: 'pointer' }"
  966. @click="changeRepeat"
  967. />
  968. <!-- <span
  969. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  970. @click="changePinyin"
  971. v-if="config.isHasPY"
  972. ></span>
  973. <span :class="['EN-16', config.isShowEN ? '' : 'disabled']" @click="changeEN" v-if="config.isHasEN"></span> -->
  974. <SvgIcon
  975. v-if="config.isHasPY"
  976. icon-class="pin-btn"
  977. size="16"
  978. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  979. :style="{ color: config.isShowPY ? (attrib ? attrib.topic_color : '') : '#DCDFE6' }"
  980. @click="changePinyin"
  981. />
  982. <!-- <span :class="['EN-16', config.isShowEN ? '' : 'disabled']" @click="changeEN" v-if="config.isHasEN"></span> -->
  983. <SvgIcon
  984. v-if="config.isHasEN"
  985. icon-class="en-btn"
  986. size="16"
  987. :class="['EN-16', config.isShowEN ? '' : 'disabled']"
  988. :style="{ color: config.isShowEN ? (attrib ? attrib.topic_color : '') : '#DCDFE6' }"
  989. @click="changeEN"
  990. />
  991. </div>
  992. </div>
  993. <div :id="'screen-' + mathNum" class="voice-full-screen">
  994. <Voicefullscreen
  995. v-if="isFull && resObj"
  996. :theme-color="attrib ? attrib.topic_color : '#e35454'"
  997. :cur-que="curQue"
  998. :sent-list="resObj.sentList"
  999. :sent-index="sentIndex"
  1000. :mp3="curQue.mp3_list && curQue.mp3_list[0] ? curQue.mp3_list[0].id : ''"
  1001. :no-font="noFont"
  1002. :NNPENewWordList="NNPENewWordList"
  1003. :NNPEAnnotationList="NNPEAnnotationList"
  1004. :current-tree-i-d="currentTreeID"
  1005. :is-full="isFull"
  1006. :config="config"
  1007. :TaskModel="TaskModel"
  1008. :NpcNewWordMp3="NpcNewWordMp3"
  1009. :attrib="attrib"
  1010. @handleWav="handleWav"
  1011. @changePinyin="changePinyin"
  1012. @changeEN="changeEN"
  1013. @exitFullscreen="exitFullscreen"
  1014. @changeIsFull="changeIsFull"
  1015. />
  1016. </div>
  1017. </div>
  1018. </template>
  1019. <script>
  1020. import { timeStrToSen } from '@/utils/transform';
  1021. import AudioLine from '../voice_matrix/components/AudioLine.vue';
  1022. import Soundrecord from '../../common/SoundRecord.vue'; // 录音模板
  1023. import RoleChs from './RoleChs.vue';
  1024. import AudioCompare from '../article/components/AudioCompare.vue';
  1025. import Voicefullscreen from '../article/Voicefullscreen.vue';
  1026. export default {
  1027. name: 'ArticleView',
  1028. components: {
  1029. AudioLine,
  1030. Soundrecord,
  1031. RoleChs,
  1032. AudioCompare,
  1033. Voicefullscreen,
  1034. },
  1035. filters: {
  1036. handlePinyin(wordsList) {
  1037. let str = '';
  1038. wordsList.forEach((item, index) => {
  1039. if (index < wordsList.length - 1) {
  1040. str += `${item.pinyin} `;
  1041. } else {
  1042. str += item.pinyin;
  1043. }
  1044. });
  1045. return str;
  1046. },
  1047. handleChs(wordsList) {
  1048. let str = '';
  1049. wordsList.forEach((item, index) => {
  1050. if (index < wordsList.length - 1) {
  1051. str += `${item.chs} `;
  1052. } else {
  1053. str += item.chs;
  1054. }
  1055. });
  1056. return str;
  1057. },
  1058. },
  1059. inject: ['convertText'],
  1060. props: [
  1061. 'curQue',
  1062. 'colorBox',
  1063. 'noFont',
  1064. 'attrib',
  1065. 'config',
  1066. 'NNPENewWordList',
  1067. 'NNPEAnnotationList',
  1068. 'currentTreeID',
  1069. 'TaskModel',
  1070. 'colLength',
  1071. 'NpcNewWordMp3',
  1072. 'isPhone',
  1073. 'multilingual',
  1074. ],
  1075. data() {
  1076. return {
  1077. wavblob: null,
  1078. resObj: null,
  1079. curTime: 0, // 单位s
  1080. chsFhList: [',', '。', '”', ':', '》', '?', '!', ';', '#', '、', '’', ')', '〕', '〕〕', '〉'],
  1081. enFhList: [',', '.', ';', '?', '!', ':', '>', '<'],
  1082. firstFhlist: ['(', '“', '‘', '〔', '〔〔', '〈', '《'],
  1083. NumberList: ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳'],
  1084. stopAudio: false,
  1085. sentIndex: 0,
  1086. isRepeat: false,
  1087. currSent: null, // 当前句子的时间
  1088. isRecord: false,
  1089. isFull: false,
  1090. mathNum: Math.random().toString(36).substr(2),
  1091. ed: undefined,
  1092. refresh: true,
  1093. highWords: null,
  1094. highWordsArr: [],
  1095. highIndex: 0,
  1096. newWordList: [],
  1097. multilingualTextList: {},
  1098. };
  1099. },
  1100. computed: {
  1101. isPlaying() {
  1102. let playing = false;
  1103. if (this.$refs.audioLine) {
  1104. playing = this.$refs.audioLine.audio.isPlaying;
  1105. }
  1106. return playing;
  1107. },
  1108. },
  1109. watch: {
  1110. sentIndex: {
  1111. handler(newVal, oldVal) {
  1112. let _this = this;
  1113. if (newVal !== oldVal) {
  1114. let Bookanswer = _this.curQue.Bookanswer;
  1115. if (
  1116. Bookanswer &&
  1117. Bookanswer.practiceModel &&
  1118. Bookanswer.practiceModel[newVal] &&
  1119. Bookanswer.practiceModel[newVal].recordList &&
  1120. Bookanswer.practiceModel[newVal].recordList.length > 0
  1121. ) {
  1122. _this.wavblob = Bookanswer.practiceModel[newVal].recordList[0].wavData;
  1123. } else {
  1124. _this.wavblob = '';
  1125. }
  1126. }
  1127. },
  1128. deep: true,
  1129. },
  1130. isFull: {
  1131. handler(newVal, oldVal) {
  1132. let _this = this;
  1133. _this.refresh = false;
  1134. if (!newVal) {
  1135. _this.$nextTick(() => {
  1136. // 重新渲染组件
  1137. _this.refresh = true;
  1138. });
  1139. }
  1140. },
  1141. deep: true,
  1142. },
  1143. },
  1144. // 生命周期 - 创建完成(可以访问当前this实例)
  1145. created() {},
  1146. // 生命周期 - 挂载完成(可以访问DOM元素)
  1147. mounted() {
  1148. if (this.curQue) {
  1149. this.handleData();
  1150. }
  1151. },
  1152. beforeCreate() {}, // 生命周期 - 创建之前
  1153. beforeMount() {}, // 生命周期 - 挂载之前
  1154. beforeUpdate() {}, // 生命周期 - 更新之前
  1155. updated() {}, // 生命周期 - 更新之后
  1156. beforeDestroy() {}, // 生命周期 - 销毁之前
  1157. destroyed() {}, // 生命周期 - 销毁完成
  1158. activated() {},
  1159. // 方法集合
  1160. methods: {
  1161. getPlayStatus(val) {
  1162. // this.isPlaying = val;
  1163. },
  1164. pauseAudio() {
  1165. let audio = document.getElementsByTagName('audio');
  1166. if (audio && audio.length > 0 && window.location.href.indexOf('GCLS-Learn') === -1 && audio.forEach) {
  1167. audio.forEach((item) => {
  1168. item.pause();
  1169. });
  1170. }
  1171. },
  1172. pauseVideo() {
  1173. let video = document.getElementsByTagName('video');
  1174. if (video && video.length > 0 && window.location.href.indexOf('GCLS-Learn') === -1 && video.forEach) {
  1175. video.forEach((item) => {
  1176. item.pause();
  1177. });
  1178. }
  1179. },
  1180. // 语音全屏
  1181. fullScreen() {
  1182. this.pauseAudio();
  1183. this.pauseVideo();
  1184. this.isFull = true;
  1185. // this.goFullscreen();
  1186. },
  1187. goFullscreen() {
  1188. let id = `screen-${this.mathNum}`;
  1189. let element = document.getElementById(id);
  1190. if (element.requestFullscreen) {
  1191. element.requestFullscreen();
  1192. } else if (element.msRequestFullscreen) {
  1193. element.msRequestFullscreen();
  1194. } else if (element.mozRequestFullScreen) {
  1195. element.mozRequestFullScreen();
  1196. } else if (element.webkitRequestFullscreen) {
  1197. element.webkitRequestFullscreen();
  1198. }
  1199. },
  1200. exitFullscreen() {
  1201. this.isFull = false;
  1202. // if (document.exitFullscreen) {
  1203. // document.exitFullscreen();
  1204. // } else if (document.msExitFullscreen) {
  1205. // document.msExitFullscreen();
  1206. // } else if (document.mozCancelFullScreen) {
  1207. // document.mozCancelFullScreen();
  1208. // } else if (document.webkitExitFullscreen) {
  1209. // document.webkitExitFullscreen();
  1210. // }
  1211. },
  1212. changeIsFull() {
  1213. this.isFull = false;
  1214. },
  1215. getWavblob(wavblob) {
  1216. this.wavblob = wavblob;
  1217. },
  1218. sentPause(isRecord) {
  1219. this.isRecord = isRecord;
  1220. },
  1221. getCurTime(curTime) {
  1222. let _this = this;
  1223. if (_this.isRepeat) {
  1224. let time = curTime * 1000;
  1225. if (time >= _this.currSent.ed || time <= _this.currSent.bg) {
  1226. _this.curTime = _this.currSent.bg;
  1227. this.$refs.audioLine.onTimeupdateTime(_this.currSent.bg / 1000, true);
  1228. } else {
  1229. _this.curTime = curTime * 1000;
  1230. }
  1231. } else {
  1232. _this.curTime = curTime * 1000;
  1233. _this.getSentIndex(_this.curTime);
  1234. }
  1235. },
  1236. getSentIndex(curTime) {
  1237. for (let i = 0; i < this.curQue.wordTime.length; i++) {
  1238. let bg = this.curQue.wordTime[i].bg;
  1239. let ed = this.curQue.wordTime[i].ed;
  1240. if (curTime >= bg && curTime <= ed) {
  1241. this.sentIndex = i;
  1242. break;
  1243. }
  1244. }
  1245. },
  1246. handleData() {
  1247. this.curQue.multilingual.forEach((item) => {
  1248. let trans_arr = item.translation.split('\n');
  1249. this.$set(this.multilingualTextList, item.type, trans_arr);
  1250. });
  1251. let resArr = [];
  1252. let sentArrTotal = [];
  1253. let curQue = JSON.parse(JSON.stringify(this.curQue));
  1254. let wordTimeList = curQue.wordTime;
  1255. let dhaspinyin = false; // 每段是否有拼音
  1256. this.handleNewword();
  1257. curQue.detail.forEach((dItem, dIndex) => {
  1258. dhaspinyin = false;
  1259. let roleDetail = this.getRole(dItem);
  1260. dItem.wordsList.forEach((sItem, sIndex) => {
  1261. let sentArr = [];
  1262. let sentence = dItem.sentences[sIndex];
  1263. sItem.forEach((wItem, wIndex) => {
  1264. let startIndex = wIndex === 0 ? 0 : sentArr[wIndex - 1].startIndex + sentArr[wIndex - 1].chs.length;
  1265. let endIndex = wIndex === 0 ? wItem.chs.length : sentArr[wIndex - 1].endIndex + wItem.chs.length;
  1266. // this.judgePad(sItem, wItem, wIndex);
  1267. this.mergeWordSymbol(wItem);
  1268. let words = '';
  1269. if (this.newWordList.length > 0) {
  1270. if (!this.highWords) {
  1271. this.findLightWord(wItem, wIndex, sentence, sItem);
  1272. words = this.highWords ? this.highWords.words : '';
  1273. } else if (wIndex > this.highWords.endIndex - 1) {
  1274. this.highWords = null;
  1275. this.findLightWord(wItem, wIndex, sentence, sItem);
  1276. words = this.highWords ? this.highWords.words : '';
  1277. } else {
  1278. words = this.highWords ? this.highWords.words : '';
  1279. }
  1280. }
  1281. let obj = {
  1282. paraIndex: dIndex, // 段落索引
  1283. sentIndex: sIndex, // 在段落中句子索引
  1284. wordIndex: wIndex, // 单词的索引
  1285. pinyin:
  1286. curQue.pinyin_type === 'pinyin'
  1287. ? curQue.property.is_first_sentence_first_hz_pinyin_first_char_upper_case === 'true' && wIndex === 0
  1288. ? wItem.pinyin_up
  1289. : wItem.pinyin
  1290. : wItem.pinyin_tone,
  1291. chs: wItem.chs,
  1292. padding: true,
  1293. className: wItem.className,
  1294. isShow: wItem.isShow,
  1295. startIndex,
  1296. endIndex,
  1297. leg: wItem.chs.length,
  1298. timeList: [],
  1299. words,
  1300. config: {
  1301. fontFamily: wItem.fontFamily,
  1302. color: wItem.color,
  1303. textDecoration: wItem.textDecoration,
  1304. border: wItem.border,
  1305. fontWeight: wItem.fontWeight,
  1306. },
  1307. matchWords: wItem.matchWords,
  1308. matchNotes: wItem.matchNotes,
  1309. notesColor: wItem.notesColor,
  1310. matchNotesObj: wItem.matchNotesObj,
  1311. img: wItem.img,
  1312. imgPosition: wItem.imgPosition,
  1313. };
  1314. sentArr.push(obj);
  1315. if (wItem.pinyin) dhaspinyin = true;
  1316. });
  1317. let objs = {
  1318. roleDetail,
  1319. sentArr,
  1320. enwords: dItem.sentencesEn && dItem.sentencesEn[sIndex] && dItem.sentencesEn[sIndex].replace(/'/g, '’'),
  1321. dhaspinyin,
  1322. type: dItem.type,
  1323. notice: dItem.notice,
  1324. noticeWordList: dItem.noticeWordList,
  1325. notice_size: dItem.notice_size,
  1326. noitce_family: dItem.noitce_family,
  1327. notice_color: dItem.notice_color,
  1328. file_url_open: dItem.file_url_open,
  1329. };
  1330. sentArrTotal.push(sentArr);
  1331. resArr.push(objs);
  1332. });
  1333. });
  1334. if (wordTimeList && wordTimeList.length > 0) {
  1335. this.mergeWordTime(sentArrTotal, wordTimeList);
  1336. }
  1337. this.resObj = { sentList: resArr };
  1338. },
  1339. // 获取角色
  1340. getRole(dItem) {
  1341. let roleIndex = dItem.roleIndex;
  1342. let resObj = null;
  1343. let roleList = JSON.parse(JSON.stringify(this.curQue.property.role_list));
  1344. for (let i = 0; i < roleList.length; i++) {
  1345. let item = roleList[i];
  1346. if (item.id === roleIndex) {
  1347. resObj = item;
  1348. // resObj.color = this.colorBox[i];
  1349. break;
  1350. }
  1351. }
  1352. return resObj;
  1353. },
  1354. mergeWordTime(resArr, wordTimeList) {
  1355. resArr.forEach((item, index) => {
  1356. let wordsResultList = wordTimeList[index].wordsResultList;
  1357. item.forEach((wItem) => {
  1358. let startIndex = wItem.startIndex;
  1359. let endIndex = wItem.endIndex;
  1360. wItem.timeList = wordsResultList.slice(startIndex, endIndex);
  1361. });
  1362. });
  1363. },
  1364. // 词和标点合一起
  1365. mergeWordSymbol(wItem) {
  1366. if (this.chsFhList.indexOf(wItem.chs) > -1 || this.firstFhlist.indexOf(wItem.chs) > -1) {
  1367. wItem.isShow = false;
  1368. } else {
  1369. wItem.isShow = true;
  1370. }
  1371. },
  1372. findLightWord(wItem, startIndex, sentence, sItem) {
  1373. let endIndex = 0;
  1374. let words = '';
  1375. this.newWordList.forEach((item) => {
  1376. if (item.length === 1) {
  1377. if (item === wItem.chs && !wItem.banLight) {
  1378. words = wItem.chs;
  1379. endIndex = startIndex + 1;
  1380. }
  1381. } else if (item.substring(0, wItem.chs.length) === wItem.chs && sentence.indexOf(item) > -1) {
  1382. let index = null;
  1383. let chsStr = '';
  1384. for (let i = startIndex; i < sItem.length + 1; i++) {
  1385. index = i;
  1386. if (chsStr.length === item.length) {
  1387. break;
  1388. } else {
  1389. chsStr += sItem[i] ? sItem[i].chs : '';
  1390. }
  1391. }
  1392. if (chsStr === item && !wItem.banLight) {
  1393. words = item;
  1394. endIndex = index;
  1395. }
  1396. } else if (wItem.new_word && wItem.new_word === item && !wItem.banLight) {
  1397. words = item;
  1398. endIndex = startIndex + 1;
  1399. }
  1400. });
  1401. if (words) {
  1402. this.highWords = { words, endIndex };
  1403. } else {
  1404. this.highWords = null;
  1405. }
  1406. },
  1407. // 判断是否有padding
  1408. judgePad(sItem, wItem, curIndex) {
  1409. let leg = sItem.length;
  1410. if (curIndex < leg - 1) {
  1411. let nextIndex = curIndex + 1;
  1412. let chs = sItem[nextIndex].chs;
  1413. if (this.chsFhList.indexOf(chs) > -1 || this.chsFhList.indexOf(wItem.chs) > -1) {
  1414. wItem.padding = false;
  1415. } else {
  1416. wItem.padding = true;
  1417. }
  1418. if (this.enFhList.indexOf(wItem.pinyin) > -1) {
  1419. wItem.className = 'textLeft';
  1420. }
  1421. }
  1422. },
  1423. // 转化时间
  1424. handleTimeList(list) {
  1425. let listRes = [];
  1426. list.forEach((item) => {
  1427. let res = timeStrToSen(item);
  1428. listRes.push(res);
  1429. });
  1430. return listRes;
  1431. },
  1432. // 计算总时间
  1433. countWordTime(sentArr) {
  1434. let total = 0;
  1435. sentArr.forEach((item) => {
  1436. total += item.endTime;
  1437. });
  1438. return total;
  1439. },
  1440. // 点击播放某个句子
  1441. handleChangeTime(time, index, ed) {
  1442. let _this = this;
  1443. if (_this.isRepeat) {
  1444. _this.currSent = _this.curQue.wordTime[index];
  1445. }
  1446. _this.sentIndex = index;
  1447. _this.ed = ed;
  1448. if (time !== null && time !== undefined) {
  1449. _this.curTime = time;
  1450. _this.$refs.audioLine.onTimeupdateTime(time / 1000, true);
  1451. }
  1452. },
  1453. emptyEd() {
  1454. this.ed = undefined;
  1455. },
  1456. handleWav(list, tmIndex) {
  1457. let _tmIndex = tmIndex || 0;
  1458. this.curQue.Bookanswer.practiceModel[_tmIndex] = {
  1459. recordList: [],
  1460. };
  1461. this.$set(this.curQue.Bookanswer.practiceModel[_tmIndex], 'recordList', list);
  1462. },
  1463. // 录音时暂停音频播放
  1464. handleParentPlay() {
  1465. this.stopAudio = true;
  1466. },
  1467. // 音频播放时改变布尔值
  1468. handleChangeStopAudio() {
  1469. this.stopAudio = false;
  1470. },
  1471. // 拼音的显示和隐藏
  1472. changePinyin() {
  1473. if (this.config.isHasPY) {
  1474. this.$emit('changeConfig', 'isShowPY');
  1475. }
  1476. },
  1477. // 英文的显示和隐藏
  1478. changeEN() {
  1479. if (this.config.isHasEN) {
  1480. this.$emit('changeConfig', 'isShowEN');
  1481. }
  1482. },
  1483. // 单句是否重复播放
  1484. changeRepeat() {
  1485. let _this = this;
  1486. _this.isRepeat = !_this.isRepeat;
  1487. this.currSent = _this.curQue.wordTime[_this.sentIndex];
  1488. },
  1489. handleNewword() {
  1490. let NewWordList = [];
  1491. this.NNPENewWordList.forEach((wItem) => {
  1492. // item.forEach((wItem) => {
  1493. if (wItem.new_word) {
  1494. NewWordList.push(wItem.new_word);
  1495. } else if (wItem.detail && wItem.detail.sentence) {
  1496. NewWordList.push(wItem.detail.sentence);
  1497. }
  1498. // });
  1499. });
  1500. this.newWordList = JSON.parse(JSON.stringify(NewWordList));
  1501. },
  1502. }, // 如果页面有keep-alive缓存功能,这个函数会触发
  1503. };
  1504. </script>
  1505. <style lang="scss" scoped>
  1506. //@import url(); 引入公共css类
  1507. .NNPE-ArticleView {
  1508. position: relative;
  1509. width: 100%;
  1510. .ArticleView-full {
  1511. position: absolute;
  1512. top: -44px;
  1513. left: 0;
  1514. z-index: 99999;
  1515. padding-left: 24px;
  1516. font-size: 14px;
  1517. font-weight: bold;
  1518. line-height: 24px;
  1519. color: #000;
  1520. background: url('@/assets/full-screen-red.png') left center no-repeat;
  1521. background-size: 16px 16px;
  1522. }
  1523. .NPC-sentences-list {
  1524. padding: 0 0 24px;
  1525. overflow: auto;
  1526. }
  1527. .multilingual {
  1528. padding: 6px 24px 12px;
  1529. word-break: break-word;
  1530. }
  1531. .aduioLine-content {
  1532. flex: 1;
  1533. }
  1534. .aduioLine-practice-npc {
  1535. display: flex;
  1536. align-items: center;
  1537. justify-content: flex-start;
  1538. .aduioLine-right {
  1539. box-sizing: border-box;
  1540. display: flex;
  1541. align-items: center;
  1542. justify-content: space-between;
  1543. width: 92px;
  1544. height: 40px;
  1545. padding: 0 12px;
  1546. border-left: 1px solid rgba(0, 0, 0, 10%);
  1547. > span {
  1548. width: 16px;
  1549. height: 16px;
  1550. cursor: pointer;
  1551. }
  1552. }
  1553. }
  1554. .NNPE-detail-box {
  1555. &.active {
  1556. background: rgba(0, 0, 0, 6%);
  1557. }
  1558. }
  1559. .enwords {
  1560. padding-left: 3px;
  1561. font-family: 'Helvetica';
  1562. font-size: 14px;
  1563. font-weight: normal;
  1564. line-height: 22px;
  1565. color: rgba(0, 0, 0, 45%);
  1566. word-break: break-word;
  1567. &.wordBlank {
  1568. color: rgba(0, 0, 0, 85%);
  1569. }
  1570. }
  1571. .NNPE-detail {
  1572. box-sizing: border-box;
  1573. display: flex;
  1574. align-items: flex-start;
  1575. justify-content: flex-start;
  1576. width: 100%;
  1577. padding: 8px 24px;
  1578. overflow: hidden;
  1579. clear: both;
  1580. color: rgba(0, 0, 0, 45%);
  1581. .sentence-box {
  1582. padding-left: 8px;
  1583. overflow: hidden;
  1584. clear: both;
  1585. &-inner {
  1586. box-sizing: border-box;
  1587. float: left;
  1588. padding: 8px 12px;
  1589. border: 1px solid rgba(0, 0, 0, 10%);
  1590. border-radius: 8px;
  1591. }
  1592. .roleDetail {
  1593. display: flex;
  1594. align-items: center;
  1595. justify-content: flex-start;
  1596. height: 36px;
  1597. .pinyin {
  1598. margin-left: 4px;
  1599. font-family: 'League';
  1600. font-size: 14px;
  1601. line-height: 22px;
  1602. color: rgba(0, 0, 0, 85%);
  1603. &.noFont {
  1604. font-family: initial;
  1605. }
  1606. }
  1607. .chs {
  1608. font-family: '楷体';
  1609. font-size: 16px;
  1610. line-height: 24px;
  1611. color: rgba(0, 0, 0, 85%);
  1612. }
  1613. .color85 {
  1614. color: rgba(0, 0, 0, 85%);
  1615. }
  1616. .color45 {
  1617. color: rgba(0, 0, 0, 45%);
  1618. }
  1619. }
  1620. }
  1621. .NNPE-words {
  1622. float: left;
  1623. padding-bottom: 5px;
  1624. &-box {
  1625. float: left;
  1626. > span {
  1627. display: block;
  1628. &.NNPE-pinyin {
  1629. height: 20px;
  1630. font-family: 'League';
  1631. font-size: 14px;
  1632. font-weight: normal;
  1633. line-height: 1.5;
  1634. &.noFont {
  1635. font-family: initial;
  1636. }
  1637. &.textLeft {
  1638. text-align: left;
  1639. }
  1640. &.wordBlank {
  1641. color: rgba(0, 0, 0, 85%);
  1642. }
  1643. }
  1644. &.NNPE-chs {
  1645. font-family: '楷体';
  1646. font-size: 20px;
  1647. line-height: 1.4;
  1648. .active {
  1649. color: #de4444;
  1650. }
  1651. &.wordBlank {
  1652. color: rgba(0, 0, 0, 85%);
  1653. }
  1654. }
  1655. // &.padding {
  1656. // padding-right: 6px;
  1657. // }
  1658. }
  1659. }
  1660. &.textLeft {
  1661. text-align: left;
  1662. }
  1663. &.textCenter {
  1664. text-align: center;
  1665. }
  1666. &.textRight {
  1667. text-align: right;
  1668. }
  1669. > span {
  1670. display: block;
  1671. &.NNPE-pinyin {
  1672. height: 20px;
  1673. font-family: 'League';
  1674. font-size: 14px;
  1675. font-weight: normal;
  1676. line-height: 1.5;
  1677. &.noFont {
  1678. font-family: initial;
  1679. }
  1680. &.textLeft {
  1681. text-align: left;
  1682. }
  1683. &.wordBlank {
  1684. color: rgba(0, 0, 0, 85%);
  1685. }
  1686. }
  1687. &.NNPE-chs {
  1688. font-family: '楷体';
  1689. font-size: 20px;
  1690. line-height: 1.4;
  1691. .active {
  1692. color: #de4444;
  1693. }
  1694. &.wordBlank {
  1695. color: rgba(0, 0, 0, 85%);
  1696. }
  1697. }
  1698. &.padding {
  1699. padding: 0 3px;
  1700. }
  1701. }
  1702. }
  1703. }
  1704. .Soundrecord-content {
  1705. display: flex;
  1706. align-items: center;
  1707. justify-content: space-between;
  1708. padding: 0 10px 8px 76px;
  1709. &-inner {
  1710. display: flex;
  1711. align-items: center;
  1712. justify-content: flex-start;
  1713. // width: 304px;
  1714. padding: 4px 12px;
  1715. background: #fff;
  1716. border: 1px solid rgba(0, 0, 0, 10%);
  1717. border-radius: 8px;
  1718. .luyin-box {
  1719. width: 280px;
  1720. }
  1721. .compare-box {
  1722. display: flex;
  1723. align-items: center;
  1724. justify-content: center;
  1725. height: 32px;
  1726. }
  1727. }
  1728. .full-screen-icon {
  1729. width: 24px;
  1730. height: 24px;
  1731. cursor: pointer;
  1732. // background: url('@/assets/full-screen-red.png') no-repeat left top;
  1733. // background-size: 100% 100%;
  1734. }
  1735. }
  1736. .voice-full-screen {
  1737. // position: fixed;
  1738. // top: 0;
  1739. // left: 0;
  1740. // z-index: 9999;
  1741. }
  1742. .notice {
  1743. margin: 0;
  1744. font-size: 14px;
  1745. font-weight: 400;
  1746. line-height: 150%;
  1747. color: #000;
  1748. word-break: break-word;
  1749. }
  1750. img {
  1751. max-width: 100%;
  1752. }
  1753. }
  1754. </style>