PhraseModelChs.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. <!-- -->
  2. <template>
  3. <div class="NNPE-ArticleView" v-if="curQue">
  4. <div class="aduioLine-box aduioLine-practice-npc" v-if="
  5. curQue.mp3_list &&
  6. curQue.mp3_list.length > 0 &&
  7. curQue.mp3_list[0].id
  8. ">
  9. <div class="aduioLine-content">
  10. <template
  11. v-if="
  12. curQue.mp3_list &&
  13. curQue.mp3_list.length > 0 &&
  14. curQue.mp3_list[0].id
  15. "
  16. >
  17. <AudioLine
  18. audioId="artPhraseAudio"
  19. :mp3="curQue.mp3_list[0].id"
  20. :getCurTime="getCurTime"
  21. :mp3Source="curQue.mp3_list[0].source"
  22. :width="590"
  23. ref="audioLine"
  24. />
  25. </template>
  26. </div>
  27. <div class="aduioLine-right">
  28. <span
  29. :class="['pinyin-16', config.isShowPY ? '' : 'disabled']"
  30. @click="changePinyin"
  31. ></span>
  32. <span
  33. :class="['EN-16', config.isShowEN ? '' : 'disabled']"
  34. @click="changeEN"
  35. ></span>
  36. </div>
  37. </div>
  38. <template v-if="resArr.length > 0">
  39. <div class="NPC-sentences-list">
  40. <div
  41. :class="['NNPE-detail', item.isTitle ? 'NNPE-detail-title' : '']"
  42. v-for="(item, index) in resArr"
  43. :key="'detail' + index"
  44. >
  45. <div class="wordsList-box">
  46. <img :src="articleImg[index]" v-if="articleImg[0] && index == 0" />
  47. <div>
  48. <div
  49. class="NNPE-words"
  50. v-for="(pItem, pIndex) in item.wordsList"
  51. :key="'wordsList' + pIndex"
  52. :class="[
  53. pItem.chs != '“' && pItem.wordIndex == 0
  54. ? 'textLeft'
  55. : 'textCenter',
  56. pItem.chs == '“' ? 'textRight' : '',
  57. ]"
  58. @click="showWordDetail($event, pItem.chs)"
  59. >
  60. <template v-if="!pItem.width">
  61. <template v-if="pItem.isShow">
  62. <template
  63. v-if="
  64. item.wordsList[pIndex + 1] &&
  65. item.wordsList[pIndex + 1].chs &&
  66. chsFhList.indexOf(item.wordsList[pIndex + 1].chs) > -1
  67. "
  68. >
  69. <span class="NNPE-words-box">
  70. <span
  71. v-if="curQue.pyPosition == 'top' && config.isShowPY && item.dhaspinyin"
  72. :class="[
  73. 'NNPE-pinyin',
  74. pItem.className ? pItem.className : '',
  75. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  76. ]"
  77. >{{ pItem.pinyin }}</span
  78. >
  79. <span
  80. :class="[
  81. 'NNPE-chs',
  82. newWordList.indexOf(pItem.chs) > -1 ? 'active' : '',
  83. ]"
  84. >{{ pItem.chs }}</span
  85. >
  86. <span
  87. v-if="
  88. curQue.pyPosition == 'bottom' && config.isShowPY && item.dhaspinyin
  89. "
  90. :class="[
  91. 'NNPE-pinyin',
  92. pItem.className ? pItem.className : '',
  93. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  94. ]"
  95. >{{ pItem.pinyin }}</span
  96. >
  97. </span>
  98. <span class="NNPE-words-box">
  99. <span
  100. v-if="curQue.pyPosition == 'top' && config.isShowPY && item.dhaspinyin"
  101. :class="[
  102. 'NNPE-pinyin',
  103. noFont.indexOf(item.wordsList[pIndex + 1].pinyin) >
  104. -1
  105. ? 'noFont'
  106. : '',
  107. ]"
  108. style="text-align: left"
  109. >{{ item.wordsList[pIndex + 1].pinyin }}</span
  110. >
  111. <span class="NNPE-chs" style="text-align: left">{{
  112. item.wordsList[pIndex + 1].chs
  113. }}</span>
  114. <span
  115. v-if="
  116. curQue.pyPosition == 'bottom' && config.isShowPY && item.dhaspinyin
  117. "
  118. :class="[
  119. 'NNPE-pinyin',
  120. noFont.indexOf(item.wordsList[pIndex + 1].pinyin) >
  121. -1
  122. ? 'noFont'
  123. : '',
  124. ]"
  125. style="text-align: left"
  126. >{{ item.wordsList[pIndex + 1].pinyin }}</span
  127. >
  128. </span>
  129. <span
  130. class="NNPE-words-box"
  131. v-if="
  132. item.wordsList[pIndex + 2] &&
  133. item.wordsList[pIndex + 2].chs &&
  134. chsFhList.indexOf(item.wordsList[pIndex + 2].chs) > -1
  135. "
  136. >
  137. <span
  138. v-if="curQue.pyPosition == 'top' && config.isShowPY && item.dhaspinyin"
  139. :class="[
  140. 'NNPE-pinyin',
  141. noFont.indexOf(item.wordsList[pIndex + 1].pinyin) >
  142. -1
  143. ? 'noFont'
  144. : '',
  145. ]"
  146. style="text-align: left"
  147. >{{ item.wordsList[pIndex + 2].pinyin }}</span
  148. >
  149. <span class="NNPE-chs" style="text-align: left">{{
  150. item.wordsList[pIndex + 2].chs
  151. }}</span>
  152. <span
  153. v-if="
  154. curQue.pyPosition == 'bottom' && config.isShowPY && item.dhaspinyin
  155. "
  156. :class="[
  157. 'NNPE-pinyin',
  158. noFont.indexOf(item.wordsList[pIndex + 2].pinyin) >
  159. -1
  160. ? 'noFont'
  161. : '',
  162. ]"
  163. style="text-align: left"
  164. >{{ item.wordsList[pIndex + 2].pinyin }}</span
  165. >
  166. </span>
  167. </template>
  168. <template v-else>
  169. <span
  170. v-if="curQue.pyPosition == 'top' && config.isShowPY && item.dhaspinyin"
  171. class="NNPE-pinyin"
  172. :class="[
  173. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  174. pItem.className ? pItem.className : '',
  175. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  176. ]"
  177. >{{ pItem.pinyin }}</span
  178. >
  179. <span
  180. class="NNPE-chs"
  181. :class="[
  182. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  183. newWordList.indexOf(pItem.chs) > -1 ? 'active' : '',
  184. ]"
  185. >{{ pItem.chs }}</span
  186. >
  187. <span
  188. v-if="curQue.pyPosition == 'bottom' && config.isShowPY && item.dhaspinyin"
  189. class="NNPE-pinyin"
  190. :class="[
  191. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  192. pItem.className ? pItem.className : '',
  193. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  194. ]"
  195. >{{ pItem.pinyin }}</span
  196. >
  197. </template>
  198. </template>
  199. </template>
  200. <template v-else>
  201. <span
  202. :style="{
  203. height: pItem.height + 'px',
  204. width: pItem.width + 'px',
  205. }"
  206. ></span>
  207. </template>
  208. </div>
  209. </div>
  210. <img :src="articleImg[index + 1]" v-if="articleImg[index + 1]" />
  211. </div>
  212. </div>
  213. </div>
  214. </template>
  215. <template v-if="isShow">
  216. <div
  217. ref="wordcard"
  218. class="NNPE-wordDetail"
  219. :style="{ top: top + 'px', left: left + 'px' }"
  220. >
  221. <Wordcard
  222. :word="word"
  223. :changeWordCard="changeWordCard"
  224. :themeColor="themeColor"
  225. :currentTreeID="currentTreeID"
  226. :TaskModel="TaskModel"
  227. :writeList="curQue.Bookanswer.writeModel"
  228. @changeCurQue="changeCurQue"
  229. />
  230. </div>
  231. </template>
  232. </div>
  233. </template>
  234. <script>
  235. import { timeStrToSen } from "../../../../utils/index";
  236. import AudioLine from "../AudioLine.vue";
  237. import Wordcard from "../components/Wordcard.vue"; // 卡片
  238. export default {
  239. name: "PhraseModelChs",
  240. props: [
  241. "curQue",
  242. "bodyLeft",
  243. "NNPENewWordList",
  244. "themeColor",
  245. "noFont",
  246. "currentTreeID",
  247. "config",
  248. "TaskModel",
  249. ],
  250. components: {
  251. AudioLine,
  252. Wordcard,
  253. },
  254. data() {
  255. return {
  256. resArr: [],
  257. curTime: 0, //单位s
  258. chsFhList: [",", "。", "”", ":", "》", "《", "?", "!", ";"],
  259. enFhList: [",", ".", ";", "?", "!", ":", ">", "<"],
  260. newWords: ["鱼", "辩礼义"],
  261. oldHz: "",
  262. hz: "",
  263. clientY: 0,
  264. top: 0,
  265. left: 0,
  266. articleImg: {}, // 文章图片
  267. newWordList: [],
  268. word: null,
  269. isShow: false,
  270. screenHeight: 0,
  271. cardHeight: 0,
  272. contentWidth: 732,
  273. };
  274. },
  275. computed: {},
  276. watch: {
  277. hz: {
  278. handler: function (val, oldVal) {
  279. let _this = this;
  280. if (val) {
  281. _this.handleNewWords(val);
  282. }
  283. },
  284. // 深度观察监听
  285. deep: true,
  286. },
  287. isShow: {
  288. handler: function (val, oldVal) {
  289. let _this = this;
  290. if (val) {
  291. setTimeout(() => {
  292. _this.cardHeight = _this.$refs.wordcard.offsetHeight;
  293. if (_this.screenHeight - _this.clientY > _this.cardHeight) {
  294. _this.top = _this.clientY + 20;
  295. } else {
  296. _this.top = _this.clientY - _this.cardHeight - 30;
  297. }
  298. }, 0);
  299. }
  300. },
  301. // 深度观察监听
  302. deep: true,
  303. },
  304. },
  305. //方法集合
  306. methods: {
  307. //拼音的显示和隐藏
  308. changePinyin() {
  309. if (this.config.isHasPY) {
  310. this.$emit("changeConfig", "isShowPY");
  311. }
  312. },
  313. // 英文的显示和隐藏
  314. changeEN() {
  315. if (this.config.isHasEN) {
  316. this.$emit("changeConfig", "isShowEN");
  317. }
  318. },
  319. getCurTime(curTime) {
  320. this.curTime = curTime;
  321. },
  322. handleData() {
  323. let resArr = [];
  324. let leg = this.curQue.detail.length;
  325. let curQue = JSON.parse(JSON.stringify(this.curQue));
  326. let dhaspinyin = false; // 每段是否有拼音
  327. curQue.detail.forEach((dItem, dIndex) => {
  328. dhaspinyin = false
  329. let paraArr = [
  330. {
  331. pinyin: "",
  332. chs: "",
  333. width: 20,
  334. height: 20,
  335. },
  336. {
  337. width: 20,
  338. height: 20,
  339. pinyin: "",
  340. chs: "",
  341. },
  342. ];
  343. dItem.wordsList.forEach((sItem, sIndex) => {
  344. sItem.forEach((wItem, wIndex) => {
  345. //this.judgePad(sItem, wItem, wIndex);
  346. this.mergeWordSymbol(sItem, wItem, wIndex);
  347. let obj = {
  348. paraIndex: dIndex, //段落索引
  349. sentIndex: sIndex, //在段落中句子索引
  350. wordIndex: wIndex, //单词的索引
  351. pinyin: wItem.pinyin,
  352. chs: wItem.chs,
  353. padding: true, //wItem.padding,
  354. className: wItem.className,
  355. isShow: wItem.isShow,
  356. isNewWord: this.newWords.indexOf(wItem.chs) > -1 ? true : false,
  357. };
  358. paraArr.push(obj);
  359. if(wItem.pinyin) dhaspinyin=true
  360. });
  361. });
  362. let paraObj = {
  363. wordsList: paraArr,
  364. dhaspinyin:dhaspinyin
  365. };
  366. resArr.push(paraObj);
  367. });
  368. this.resArr = resArr;
  369. // 循环文章图片
  370. if (curQue.img_list) {
  371. curQue.img_list.forEach((item) => {
  372. this.articleImg[item.imgNumber] = item.id;
  373. });
  374. }
  375. },
  376. //词和标点合一起
  377. mergeWordSymbol(sItem, wItem, curIndex) {
  378. let leg = sItem.length;
  379. if (curIndex < leg - 1) {
  380. let nextIndex = curIndex + 1;
  381. let chs = sItem[nextIndex].chs;
  382. let pinyin = sItem[nextIndex].pinyin;
  383. // if (this.chsFhList.indexOf(chs) > -1) {
  384. // wItem.chs = "<a>" + wItem.chs + "</a><a>" + chs + "</a>";
  385. // wItem.pinyin = "<a>" + wItem.pinyin + "</a><a>" + pinyin + "</a>";
  386. // }
  387. if (this.chsFhList.indexOf(wItem.chs) > -1) {
  388. wItem.isShow = false;
  389. } else {
  390. wItem.isShow = true;
  391. }
  392. }
  393. // if (this.enFhList.indexOf(wItem.pinyin) > -1) {
  394. // wItem.className = "textLeft";
  395. // }
  396. },
  397. //判断是否有padding
  398. judgePad(sItem, wItem, curIndex) {
  399. let leg = sItem.length;
  400. if (curIndex < leg - 1) {
  401. let nextIndex = curIndex + 1;
  402. let chs = sItem[nextIndex].chs;
  403. if (
  404. this.chsFhList.indexOf(chs) > -1 ||
  405. this.chsFhList.indexOf(wItem.chs) > -1
  406. ) {
  407. wItem.padding = false;
  408. } else {
  409. wItem.padding = true;
  410. }
  411. if (this.enFhList.indexOf(wItem.pinyin) > -1) {
  412. wItem.className = "textLeft";
  413. }
  414. }
  415. },
  416. //转化时间
  417. handleTimeList(list) {
  418. let listRes = [];
  419. list.forEach((item) => {
  420. let res = timeStrToSen(item);
  421. listRes.push(res);
  422. });
  423. return listRes;
  424. },
  425. //点击播放某个句子
  426. handleChangeTime(time) {
  427. this.curTime = time;
  428. this.$refs.audioLine.onTimeupdateTime(time);
  429. },
  430. handleNewword() {
  431. let NewWordList = [];
  432. this.NNPENewWordList.forEach((item) => {
  433. item.forEach((wItem) => {
  434. NewWordList.push(wItem.new_word);
  435. });
  436. });
  437. this.newWordList = JSON.parse(JSON.stringify(NewWordList));
  438. },
  439. showWordDetail(e, word) {
  440. let _this = this;
  441. if (this.newWordList.indexOf(word) > -1) {
  442. if (_this.oldHz != word) {
  443. this.isShow = false;
  444. setTimeout(() => {
  445. _this.hz = word;
  446. }, 50);
  447. }
  448. _this.clientY = e.clientY;
  449. let left = e.clientX;
  450. let width = 0;
  451. if (word.length == 1 || word.length == 2) {
  452. width = 304;
  453. } else if (word.length == 3 || word.length == 4) {
  454. width = 432;
  455. } else if (word.length > 3) {
  456. width = 560;
  457. }
  458. if (left - this.bodyLeft > this.contentWidth / 2) {
  459. _this.left = left - width + 10;
  460. } else {
  461. _this.left = left;
  462. }
  463. }
  464. },
  465. hideWordDetail() {
  466. this.isShow = false;
  467. },
  468. changeWordCard(isShow) {
  469. this.isShow = isShow;
  470. this.oldHz = "";
  471. this.hz = "";
  472. },
  473. // 处理分词数据
  474. handleNewWords(val, top, left) {
  475. this.isShow = true;
  476. this.word = null;
  477. for (let i = 0; i < this.NNPENewWordList.length; i++) {
  478. let pItem = this.NNPENewWordList[i];
  479. for (let j = 0; j < pItem.length; j++) {
  480. let item = pItem[j];
  481. if (item.new_word.trim() == val.trim()) {
  482. let wordlist = val.split("");
  483. this.word = { list: wordlist, detail: item, top: top, left: left };
  484. break;
  485. }
  486. }
  487. }
  488. this.oldHz = val;
  489. },
  490. getScreenHeight() {
  491. this.screenHeight = window.innerHeight;
  492. },
  493. changeCurQue(answer) {
  494. if (answer) {
  495. let writeModel = this.curQue.Bookanswer.writeModel;
  496. let hz = answer.hz;
  497. if (writeModel.hasOwnProperty(hz)) {
  498. writeModel[hz].push(answer);
  499. } else {
  500. writeModel[hz] = [answer];
  501. }
  502. console.log(this.curQue.Bookanswer);
  503. }
  504. },
  505. },
  506. //生命周期 - 创建完成(可以访问当前this实例)
  507. created() {},
  508. //生命周期 - 挂载完成(可以访问DOM元素)
  509. mounted() {
  510. if (this.curQue) {
  511. this.handleData();
  512. }
  513. if (this.NNPENewWordList && this.NNPENewWordList.length > 0) {
  514. this.handleNewword();
  515. }
  516. $(window).resize(() => {
  517. this.getScreenHeight();
  518. });
  519. this.getScreenHeight();
  520. },
  521. beforeCreate() {}, //生命周期 - 创建之前
  522. beforeMount() {}, //生命周期 - 挂载之前
  523. beforeUpdate() {}, //生命周期 - 更新之前
  524. updated() {}, //生命周期 - 更新之后
  525. beforeDestroy() {}, //生命周期 - 销毁之前
  526. destroyed() {}, //生命周期 - 销毁完成
  527. activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
  528. };
  529. </script>
  530. <style lang='scss' scoped>
  531. //@import url(); 引入公共css类
  532. .NNPE-ArticleView {
  533. width: 100%;
  534. .aduioLine-practice-npc {
  535. display: flex;
  536. justify-content: flex-start;
  537. align-items: center;
  538. .aduioLine-content {
  539. flex: 1;
  540. }
  541. .aduioLine-right {
  542. display: flex;
  543. justify-content: space-between;
  544. align-items: center;
  545. width: 69px;
  546. height: 40px;
  547. box-sizing: border-box;
  548. padding: 0 12px;
  549. border-left: 1px solid rgba(0, 0, 0, 0.1);
  550. > span {
  551. width: 16px;
  552. height: 16px;
  553. cursor: pointer;
  554. }
  555. }
  556. }
  557. .NPC-sentences-list {
  558. padding: 24px 0;
  559. }
  560. .NNPE-detail {
  561. clear: both;
  562. overflow: hidden;
  563. color: rgba(0, 0, 0, 0.65);
  564. .NNPE-words {
  565. float: left;
  566. &-box {
  567. float: left;
  568. > span {
  569. display: block;
  570. &.NNPE-pinyin {
  571. font-family: "GB-PINYINOK-B";
  572. font-weight: normal;
  573. font-size: 14px;
  574. line-height: 22px;
  575. &.noFont {
  576. font-family: initial;
  577. }
  578. height: 21px;
  579. &.textLeft {
  580. text-align: left;
  581. }
  582. }
  583. &.NNPE-chs {
  584. font-family: "FZJCGFKTK";
  585. font-size: 20px;
  586. line-height: 28px;
  587. &.active {
  588. color: #de4444;
  589. }
  590. }
  591. &.padding {
  592. padding: 0 3px;
  593. }
  594. }
  595. }
  596. &.textLeft {
  597. text-align: left;
  598. }
  599. &.textCenter {
  600. text-align: center;
  601. }
  602. &.textRight {
  603. text-align: right;
  604. }
  605. > span {
  606. display: block;
  607. &.NNPE-pinyin {
  608. font-family: "GB-PINYINOK-B";
  609. font-weight: normal;
  610. font-size: 14px;
  611. line-height: 22px;
  612. height: 21px;
  613. &.noFont {
  614. font-family: initial;
  615. }
  616. &.textLeft {
  617. text-align: left;
  618. }
  619. }
  620. &.NNPE-chs {
  621. font-family: "FZJCGFKTK";
  622. font-size: 20px;
  623. line-height: 28px;
  624. &.active {
  625. color: #de4444;
  626. }
  627. }
  628. &.padding {
  629. padding: 0 3px;
  630. }
  631. }
  632. }
  633. &.NNPE-detail-title {
  634. .wordsList-box {
  635. > div {
  636. display: flex;
  637. justify-content: center;
  638. }
  639. }
  640. }
  641. .index {
  642. width: 48px;
  643. box-sizing: border-box;
  644. padding: 8px;
  645. text-align: right;
  646. border-right: 1px solid rgba(0, 0, 0, 0.1);
  647. b {
  648. font-weight: 400;
  649. color: #000000;
  650. line-height: 1.5;
  651. }
  652. }
  653. .wordsList-box {
  654. width: 100%;
  655. padding: 6px 24px 12px 24px;
  656. > div {
  657. overflow: hidden;
  658. clear: both;
  659. }
  660. > img {
  661. max-width: 100%;
  662. display: block;
  663. margin: 0 auto;
  664. }
  665. }
  666. }
  667. .NNPE-wordDetail {
  668. position: fixed;
  669. z-index: 9999;
  670. // width: 260px;
  671. // height: 200px;
  672. // background: #cc0;
  673. }
  674. }
  675. </style>