PhraseModelChs.vue 20 KB

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