Practicechs.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <!-- -->
  2. <template>
  3. <div class="NNPE-ArticleView" v-if="curQue">
  4. <AudioLine
  5. :mp3="curQue.mp3_list[0].url"
  6. v-if="curQue.mp3_list[0].url"
  7. class="aduioLine-box"
  8. :getCurTime="getCurTime"
  9. ref="audioLine"
  10. :stopAudio="stopAudio"
  11. @handleChangeStopAudio="handleChangeStopAudio"
  12. />
  13. <template v-if="resObj">
  14. <!-- -->
  15. <div class="NPC-sentences-list">
  16. <div v-for="(item, index) in resObj.sentList" :key="'detail' + index">
  17. <div
  18. class="NNPE-detail"
  19. @click="handleChangeTime(curQue.wordTime[index].bg)"
  20. >
  21. <div
  22. class="NNPE-words"
  23. v-for="(pItem, pIndex) in item"
  24. :key="'wordsList' + pIndex"
  25. :class="[pItem.wordIndex == 0 ? 'textLeft' : 'textCenter']"
  26. >
  27. <template v-if="!pItem.width">
  28. <template v-if="pItem.isShow">
  29. <template
  30. v-if="
  31. item[pIndex + 1].chs &&
  32. chsFhList.indexOf(item[pIndex + 1].chs) > -1
  33. "
  34. >
  35. <span class="NNPE-words-box">
  36. <span
  37. class="NNPE-pinyin"
  38. :class="[pItem.className ? pItem.className : '']"
  39. >{{ pItem.pinyin }}</span
  40. >
  41. <span
  42. class="NNPE-chs"
  43. :class="[pItem.padding ? 'padding' : '']"
  44. >
  45. <template
  46. v-if="pItem.timeList && pItem.timeList.length > 0"
  47. >
  48. <span
  49. v-for="(wItem, wIndex) in pItem.leg"
  50. :key="'ci' + wIndex + pIndex + index"
  51. :class="[
  52. pItem.timeList[wIndex] &&
  53. curTime >= pItem.timeList[wIndex].wordBg &&
  54. curTime <= curQue.wordTime[index].ed
  55. ? 'active'
  56. : '',
  57. ]"
  58. >{{ pItem.chs[wIndex] }}</span
  59. >
  60. </template>
  61. </span>
  62. </span>
  63. <span class="NNPE-words-box">
  64. <span class="NNPE-pinyin" style="text-align: left">{{
  65. item[pIndex + 1].pinyin
  66. }}</span>
  67. <span class="NNPE-chs" style="text-align: left">
  68. <span
  69. :class="[
  70. pItem.timeList[pItem.leg - 1] &&
  71. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  72. curTime <= curQue.wordTime[index].ed
  73. ? 'active'
  74. : '',
  75. ]"
  76. >
  77. {{ item[pIndex + 1].chs }}</span
  78. >
  79. </span>
  80. </span>
  81. </template>
  82. <template v-else>
  83. <span
  84. class="NNPE-pinyin"
  85. :class="[
  86. pItem.padding ? 'padding' : '',
  87. pItem.className ? pItem.className : '',
  88. ]"
  89. >{{ pItem.pinyin }}</span
  90. >
  91. <span
  92. class="NNPE-chs"
  93. :class="[pItem.padding ? 'padding' : '']"
  94. >
  95. <template
  96. v-if="pItem.timeList && pItem.timeList.length > 0"
  97. >
  98. <span
  99. v-for="(wItem, wIndex) in pItem.leg"
  100. :key="'ci' + wIndex + pIndex + index"
  101. :class="[
  102. pItem.timeList[wIndex] &&
  103. curTime >= pItem.timeList[wIndex].wordBg &&
  104. curTime <= curQue.wordTime[index].ed
  105. ? 'active'
  106. : '',
  107. ]"
  108. >{{ pItem.chs[wIndex] }}</span
  109. >
  110. </template>
  111. </span>
  112. </template>
  113. </template>
  114. </template>
  115. <template v-else>
  116. <span
  117. :style="{
  118. height: pItem.height + 'px',
  119. width: pItem.width + 'px',
  120. }"
  121. ></span>
  122. </template>
  123. </div>
  124. </div>
  125. <div
  126. v-show="
  127. curTime >= curQue.wordTime[index].bg &&
  128. curTime <= curQue.wordTime[index].ed
  129. "
  130. class="Soundrecord-content"
  131. >
  132. <Soundrecord
  133. @handleWav="handleWav"
  134. type="pro"
  135. class="luyin-box"
  136. @handleParentPlay="handleParentPlay"
  137. />
  138. </div>
  139. </div>
  140. </div>
  141. </template>
  142. </div>
  143. </template>
  144. <script>
  145. import { timeStrToSen } from "@/utils/index";
  146. import AudioLine from "../AudioLine.vue";
  147. import Soundrecord from "../../preview/Soundrecord.vue"; // 录音模板
  148. export default {
  149. name: "ArticleView",
  150. props: ["curQue"],
  151. components: {
  152. AudioLine,
  153. Soundrecord,
  154. },
  155. data() {
  156. return {
  157. resObj: null,
  158. curTime: 0, //单位s
  159. chsFhList: [",", "。", "“", ":", "》", "《", "?", "!", ";"],
  160. enFhList: [",", ".", ";", "?", "!", ":", ">", "<"],
  161. stopAudio: false,
  162. };
  163. },
  164. computed: {},
  165. watch: {},
  166. //方法集合
  167. methods: {
  168. getCurTime(curTime) {
  169. this.curTime = curTime * 1000;
  170. },
  171. handleData() {
  172. let resArr = [],
  173. timeArr = [];
  174. let curQue = JSON.parse(JSON.stringify(this.curQue));
  175. let wordTimeList = curQue.wordTime;
  176. curQue.detail.forEach((dItem, dIndex) => {
  177. dItem.wordsList.forEach((sItem, sIndex) => {
  178. let sentArr = [];
  179. sItem.forEach((wItem, wIndex) => {
  180. let startIndex =
  181. wIndex == 0
  182. ? 0
  183. : sentArr[wIndex - 1].startIndex +
  184. sentArr[wIndex - 1].chs.length;
  185. let endIndex =
  186. wIndex == 0
  187. ? wItem.chs.length
  188. : sentArr[wIndex - 1].endIndex + wItem.chs.length;
  189. // this.judgePad(sItem, wItem, wIndex);
  190. this.mergeWordSymbol(sItem, wItem, wIndex);
  191. let obj = {
  192. paraIndex: dIndex, //段落索引
  193. sentIndex: sIndex, //在段落中句子索引
  194. wordIndex: wIndex, //单词的索引
  195. pinyin: wItem.pinyin,
  196. chs: wItem.chs,
  197. padding: true,
  198. className: wItem.className,
  199. isShow: wItem.isShow,
  200. startIndex: startIndex,
  201. endIndex: endIndex,
  202. leg: wItem.chs.length,
  203. timeList: [],
  204. };
  205. sentArr.push(obj);
  206. });
  207. resArr.push(sentArr);
  208. });
  209. timeArr.push(dItem.timeList);
  210. });
  211. this.mergeWordTime(resArr, wordTimeList);
  212. let timeList = [];
  213. timeArr.forEach((item) => {
  214. item.forEach((aItem) => {
  215. if (timeList.indexOf(aItem) < 0) {
  216. timeList.push(aItem);
  217. }
  218. });
  219. });
  220. this.resObj = { sentList: resArr, timeList: timeList };
  221. console.log(this.resObj);
  222. },
  223. mergeWordTime(resArr, wordTimeList) {
  224. resArr.forEach((item, index) => {
  225. let wordsResultList = wordTimeList[index].wordsResultList;
  226. item.forEach((wItem) => {
  227. let startIndex = wItem.startIndex;
  228. let endIndex = wItem.endIndex;
  229. wItem.timeList = wordsResultList.slice(startIndex, endIndex);
  230. });
  231. });
  232. },
  233. //词和标点合一起
  234. mergeWordSymbol(sItem, wItem, curIndex) {
  235. let leg = sItem.length;
  236. if (curIndex < leg - 1) {
  237. let nextIndex = curIndex + 1;
  238. if (this.chsFhList.indexOf(wItem.chs) > -1) {
  239. wItem.isShow = false;
  240. } else {
  241. wItem.isShow = true;
  242. }
  243. }
  244. },
  245. handleData2() {
  246. let resArr = [],
  247. timeArr = [];
  248. let leg = this.curQue.detail.length;
  249. let curQue = JSON.parse(JSON.stringify(this.curQue));
  250. curQue.detail.forEach((dItem, dIndex) => {
  251. let endTime = 0;
  252. if (dIndex < leg - 1) {
  253. endTime = curQue.detail[dIndex + 1].timeList[0];
  254. dItem.timeList.push(endTime);
  255. dItem.timeList = this.handleTimeList(dItem.timeList);
  256. } else {
  257. dItem.timeList = this.handleTimeList(dItem.timeList);
  258. endTime = curQue.mp3_list[0].duration;
  259. dItem.timeList.push(endTime);
  260. }
  261. dItem.wordsList.forEach((sItem, sIndex) => {
  262. let time_diff = dItem.timeList[sIndex + 1] - dItem.timeList[sIndex];
  263. let wordsLeg = dItem.sentences[sIndex].length;
  264. let wordTime = time_diff / wordsLeg;
  265. // console.log(time_diff, wordsLeg, wordTime);
  266. let sentArr = [];
  267. sItem.forEach((wItem, wIndex) => {
  268. let preTotal = wIndex > 0 ? sentArr[wIndex - 1].endTime : 0;
  269. let endTime =
  270. wIndex > 0
  271. ? wordTime * wItem.chs.length + preTotal
  272. : dItem.timeList[sIndex] + wordTime * wItem.chs.length;
  273. //console.log("endTime" + endTime);
  274. let startTime =
  275. wIndex > 0 ? sentArr[wIndex - 1].endTime : dItem.timeList[sIndex];
  276. this.judgePad(sItem, wItem, wIndex);
  277. let obj = {
  278. paraIndex: dIndex, //段落索引
  279. sentIndex: sIndex, //在段落中句子索引
  280. wordIndex: wIndex, //单词的索引
  281. pinyin: wItem.pinyin,
  282. chs: wItem.chs,
  283. padding: wItem.padding,
  284. className: wItem.className,
  285. startTime: startTime,
  286. endTime: endTime,
  287. };
  288. sentArr.push(obj);
  289. });
  290. resArr.push(sentArr);
  291. });
  292. timeArr.push(dItem.timeList);
  293. });
  294. let timeList = [];
  295. timeArr.forEach((item) => {
  296. item.forEach((aItem) => {
  297. if (timeList.indexOf(aItem) < 0) {
  298. timeList.push(aItem);
  299. }
  300. });
  301. });
  302. this.resObj = { sentList: resArr, timeList: timeList };
  303. console.log(this.resObj);
  304. },
  305. //判断是否有padding
  306. judgePad(sItem, wItem, curIndex) {
  307. let leg = sItem.length;
  308. if (curIndex < leg - 1) {
  309. let nextIndex = curIndex + 1;
  310. let chs = sItem[nextIndex].chs;
  311. if (
  312. this.chsFhList.indexOf(chs) > -1 ||
  313. this.chsFhList.indexOf(wItem.chs) > -1
  314. ) {
  315. wItem.padding = false;
  316. } else {
  317. wItem.padding = true;
  318. }
  319. if (this.enFhList.indexOf(wItem.pinyin) > -1) {
  320. wItem.className = "textLeft";
  321. }
  322. }
  323. },
  324. //转化时间
  325. handleTimeList(list) {
  326. let listRes = [];
  327. list.forEach((item) => {
  328. let res = timeStrToSen(item);
  329. listRes.push(res);
  330. });
  331. return listRes;
  332. },
  333. //计算总时间
  334. countWordTime(sentArr) {
  335. let total = 0;
  336. sentArr.forEach((item) => {
  337. total += item.endTime;
  338. });
  339. return total;
  340. },
  341. //点击播放某个句子
  342. handleChangeTime(time) {
  343. this.curTime = time;
  344. this.$refs.audioLine.onTimeupdateTime(time / 1000);
  345. },
  346. handleWav(data) {},
  347. // 录音时暂停音频播放
  348. handleParentPlay() {
  349. this.stopAudio = true;
  350. },
  351. // 音频播放时改变布尔值
  352. handleChangeStopAudio() {
  353. this.stopAudio = false;
  354. },
  355. },
  356. //生命周期 - 创建完成(可以访问当前this实例)
  357. created() {},
  358. //生命周期 - 挂载完成(可以访问DOM元素)
  359. mounted() {
  360. console.log(this.curQue);
  361. if (this.curQue) {
  362. this.handleData();
  363. }
  364. },
  365. beforeCreate() {}, //生命周期 - 创建之前
  366. beforeMount() {}, //生命周期 - 挂载之前
  367. beforeUpdate() {}, //生命周期 - 更新之前
  368. updated() {}, //生命周期 - 更新之后
  369. beforeDestroy() {}, //生命周期 - 销毁之前
  370. destroyed() {}, //生命周期 - 销毁完成
  371. activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
  372. };
  373. </script>
  374. <style lang='scss' scoped>
  375. //@import url(); 引入公共css类
  376. .NNPE-ArticleView {
  377. width: 100%;
  378. .NPC-sentences-list {
  379. padding: 24px;
  380. }
  381. .Soundrecord-content {
  382. }
  383. .NNPE-detail {
  384. clear: both;
  385. overflow: hidden;
  386. width: 100%;
  387. background: #ffffff;
  388. border: 1px solid rgba(0, 0, 0, 0.1);
  389. box-sizing: border-box;
  390. border-radius: 8px;
  391. margin-bottom: 8px;
  392. box-sizing: border-box;
  393. padding: 8px 16px 0px;
  394. .NNPE-words {
  395. float: left;
  396. padding: 0 0px 8px 0px;
  397. &-box {
  398. float: left;
  399. > span {
  400. display: block;
  401. &.NNPE-pinyin {
  402. font-family: "GB-PINYINOK-B";
  403. font-weight: normal;
  404. font-size: 14px;
  405. line-height: 20px;
  406. color: #000000;
  407. height: 20px;
  408. &.textLeft {
  409. text-align: left;
  410. }
  411. }
  412. &.NNPE-chs {
  413. font-family: "FZJCGFKTK";
  414. font-size: 20px;
  415. line-height: 150%;
  416. color: #000000;
  417. .active {
  418. color: #32a5d8;
  419. }
  420. }
  421. // &.padding {
  422. // padding-right: 6px;
  423. // }
  424. }
  425. }
  426. &.textLeft {
  427. text-align: left;
  428. }
  429. &.textCenter {
  430. text-align: center;
  431. }
  432. > span {
  433. display: block;
  434. &.NNPE-pinyin {
  435. font-family: "GB-PINYINOK-B";
  436. font-weight: normal;
  437. font-size: 14px;
  438. line-height: 20px;
  439. color: #000000;
  440. height: 20px;
  441. &.textLeft {
  442. text-align: left;
  443. }
  444. }
  445. &.NNPE-chs {
  446. font-family: "FZJCGFKTK";
  447. font-size: 20px;
  448. line-height: 150%;
  449. color: #000000;
  450. .active {
  451. color: #32a5d8;
  452. }
  453. }
  454. &.padding {
  455. padding: 0 3px;
  456. }
  457. }
  458. }
  459. }
  460. .luyin-box {
  461. width: 200px;
  462. margin: 8px 0 8px 0px;
  463. padding: 0 12px;
  464. border: 1px solid rgba(0, 0, 0, 0.1);
  465. border-radius: 8px;
  466. background: #ffffff;
  467. }
  468. }
  469. </style>