PhraseModel.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. <!-- -->
  2. <template>
  3. <div class="NNPE-ArticleView" v-if="articleInfo">
  4. <template v-if="resArr[0]&&resArr[0].wordsList">
  5. <h2>
  6. <span v-for="(itemR,indexR) in resArr[0].wordsList" :key="indexR" :style="{color:colorObj.titleColor,fontSize:(wordFontsize+30)+'px',lineHeight:(wordFontsize+38)+'px',marginRight:'10px',fontWeight:'700',cursor:'pointer'}"
  7. :class="[
  8. itemR.tokens[9]===' '?'marginRight':'',
  9. itemR.isExplain||itemR.explainNumber?'hasExplain':''
  10. ]">
  11. <template v-if="itemR.isShow">
  12. <template v-if="itemR.isExplain">
  13. <span class="explain-sub" @click="showItem(itemR)">
  14. <img :src="require('../../../assets/explainBg-'+itemR.explainNumber+'.png')"/>
  15. </span>
  16. </template>
  17. <template v-else>
  18. <span
  19. class="NNPE-chs"
  20. :class="[
  21. itemR.type,itemR.tokens[9]===' '?'marginRight':'',itemR.highIndex?'fontWeight':''
  22. ]"
  23. @click="showItem(itemR)"
  24. >{{ itemR.tokens[2] }}</span
  25. >
  26. <template v-if="itemR.explainNumber">
  27. <span class="explain-sub" @click="showItem(itemR)">
  28. <img :src="require('../../../assets/explainBg-'+itemR.explainNumber+'.png')"/>
  29. </span>
  30. </template>
  31. <span
  32. class="NNPE-chs NNPE-chs-both"
  33. v-if="resArr[0].wordsList[indexR + 1] &&
  34. resArr[0].wordsList[indexR + 1].tokens[2] &&
  35. enFhList.indexOf(resArr[0].wordsList[indexR + 1].tokens[2]) > -1"
  36. :class="[
  37. resArr[0].wordsList[indexR + 1].type,resArr[0].wordsList[indexR + 1].tokens[9]===' '?'marginRight':'',resArr[0].wordsList[indexR + 1].highIndex?'fontWeight':''
  38. ]"
  39. @click="showItem(resArr[0].wordsList[indexR + 1])"
  40. >{{ resArr[0].wordsList[indexR + 1].tokens[2] }}</span
  41. >
  42. </template>
  43. </template>
  44. <!-- {{itemR.tokens[2]}} -->
  45. </span>
  46. </h2>
  47. </template>
  48. <h6 class="nnpe-article-author" :style="{color:colorObj.sourceColor,fontSize:(wordFontsize-4)+'px',lineHeight:(wordFontsize+4)+'px',fontWeight:'400'}">
  49. {{articleInfo.art_author+' · '+articleInfo.study_phase_name+'版 · 第 '+articleInfo.iss_no+' 期 · '+articleInfo.release_date+' · '+articleInfo.chn_item+(articleInfo.page_no_in_pub?' · P'+articleInfo.page_no_in_pub:'')}}
  50. </h6>
  51. <div class="audio-box">
  52. <div
  53. class="aduioLine-content aduioLine-box"
  54. v-if="
  55. articleInfo.art_sound_url
  56. " :style="{background:colorObj.audiobg,borderColor:colorObj.audioBorder}"
  57. >
  58. <AudioLine
  59. audioId="artNormalAudio"
  60. :mp3="articleInfo.art_sound_url"
  61. :getCurTime="getCurTime"
  62. ref="audioLine"
  63. :mp3Source="'mp3'"
  64. />
  65. <svg-icon icon-class="icon-wrapper" class="wrapper"></svg-icon>
  66. </div>
  67. </div>
  68. <template v-if="resArr.length > 0">
  69. <div class="table-box">
  70. <div
  71. :class="['NNPE-detail']"
  72. v-for="(item, index) in resArr"
  73. :key="'detail' + index"
  74. >
  75. <div class="wordsList-box">
  76. <template v-if="index!==0">
  77. <div class="nnpe-sentence-box">
  78. <div v-for="(pItem, pIndex) in item.wordsList" :key="'wordsList' + pIndex" class="word-box" :class="[pItem.isExplain||pItem.explainNumber?'hasExplain':'']">
  79. <template v-if="pItem.isShow">
  80. <template v-if="pItem.isExplain">
  81. <span class="explain-sub" @click="showItem(pItem)">
  82. <img :src="require('../../../assets/explainBg-'+pItem.explainNumber+'.png')"/>
  83. </span>
  84. </template>
  85. <template v-else>
  86. <div
  87. :class="[
  88. 'NNPE-words',
  89. ]"
  90. >
  91. <span
  92. class="NNPE-chs"
  93. :class="[
  94. pItem.type,pItem.tokens[9]===' '?'marginRight':'',pItem.highIndex?'fontWeight':''
  95. ]"
  96. :style="{fontSize:wordFontsize + 'px',color: colorObj.contentColor}"
  97. @click="showItem(pItem)"
  98. >{{ pItem.tokens[2] }}</span
  99. >
  100. <template v-if="pItem.explainNumber">
  101. <span class="explain-sub" @click="showItem(pItem)">
  102. <img :src="require('../../../assets/explainBg-'+pItem.explainNumber+'.png')"/>
  103. </span>
  104. </template>
  105. <span
  106. class="NNPE-chs NNPE-chs-both"
  107. v-if="item.wordsList[pIndex + 1] &&
  108. item.wordsList[pIndex + 1].tokens[2] &&
  109. enFhList.indexOf(item.wordsList[pIndex + 1].tokens[2]) > -1"
  110. :class="[
  111. item.wordsList[pIndex + 1].type,item.wordsList[pIndex + 1].tokens[9]===' '?'marginRight':'',item.wordsList[pIndex + 1].highIndex?'fontWeight':''
  112. ]"
  113. :style="{fontSize:wordFontsize + 'px',color: colorObj.contentColor}"
  114. @click="showItem(item.wordsList[pIndex + 1])"
  115. >{{ item.wordsList[pIndex + 1].tokens[2] }}</span
  116. >
  117. </div>
  118. </template>
  119. </template>
  120. </div>
  121. </div>
  122. </template>
  123. </div>
  124. </div>
  125. </div>
  126. </template>
  127. <div class="explain-box" v-if="showExplainFlag&&showObj">
  128. <div class="explain-box-top">
  129. <p>注释</p>
  130. <i class="el-icon-close" @click="closeExplain"></i>
  131. </div>
  132. <h3>{{showObj.exp_title}}</h3>
  133. <span>{{showObj.exp_content}}</span>
  134. </div>
  135. <el-dialog
  136. :visible.sync="showPhraseFlag"
  137. :show-close="false"
  138. :close-on-click-modal="false"
  139. width="580px"
  140. :modal="false"
  141. class="login-dialog phrase-box"
  142. v-if="showPhraseFlag&&showObj">
  143. <phrase-card :dataObj="showObj" @closeWord="closeExplain" @changeLike="changeLike" :likePhrase="likePhraseList"></phrase-card>
  144. </el-dialog>
  145. </div>
  146. </template>
  147. <script>
  148. import AudioLine from "@/components/common/AudioLine.vue"
  149. import PhraseCard from "./PhraseCard.vue"
  150. export default {
  151. name: "ArticleView",
  152. props: [ "titleFontsize", "wordFontsize", "colorObj","articleType","articleInfo","likePhraseList","likeWord"],
  153. components: {
  154. AudioLine,
  155. PhraseCard
  156. },
  157. data() {
  158. return {
  159. resArr: [],
  160. curTime: 0, //单位s
  161. enFhList: [
  162. ",",
  163. ".",
  164. ";",
  165. "?",
  166. "!",
  167. ":",
  168. ">",
  169. "<",
  170. "'",
  171. "’",
  172. "n't",
  173. "n’t",
  174. "n’ts",
  175. "n‘t",
  176. "'t",
  177. "’t",
  178. "‘t",
  179. "'s",
  180. "’s",
  181. "‘s",
  182. "'m",
  183. "’m",
  184. "‘m",
  185. "'re",
  186. "’re",
  187. "‘re",
  188. "'d",
  189. "’d",
  190. "‘d",
  191. "'ve",
  192. "’ve",
  193. "‘ve",
  194. ")",
  195. "'ll",
  196. "’ll",
  197. "‘ll",
  198. "”",
  199. ],
  200. articleImg: {}, // 文章图片
  201. allWordList: [], // 生词短语注释总列表
  202. tokensArr: [],
  203. sentenceList: [],
  204. wordLit:[],
  205. annotationList: [],
  206. phraseList: [],
  207. showObj:null,
  208. activeObjIndex: null,
  209. showWordFlag: false,
  210. showPhraseFlag: false,
  211. showExplainFlag: false,
  212. };
  213. },
  214. computed: {
  215. isPlaying: function () {
  216. let playing = false;
  217. if (this.$refs.audioLine) {
  218. playing = this.$refs.audioLine.audio.isPlaying;
  219. }
  220. return playing;
  221. },
  222. },
  223. watch: {},
  224. //方法集合
  225. methods: {
  226. getCurTime(curTime) {
  227. this.curTime = curTime * 1000;
  228. },
  229. handleData() {
  230. let explainNumber = 1
  231. let resArr = [];
  232. let articleInfo = JSON.parse(JSON.stringify(this.articleInfo));
  233. this.sentenceList = articleInfo.art_corpus_data?articleInfo.art_corpus_data.sentList:[]
  234. this.wordLit = articleInfo.art_voc_data?articleInfo.art_voc_data:[]
  235. this.annotationList = articleInfo.art_phrase_data?articleInfo.art_explain_data:[]
  236. this.phraseList = articleInfo.art_explain_data?articleInfo.art_phrase_data:[]
  237. this.wordLit.forEach(item=>{
  238. item.type='newWord'
  239. item.exp_title = item.word_name
  240. let paraStr = ''
  241. if(item.word_explain&&item.word_explain.word_para_list){
  242. item.word_explain.word_para_list.forEach(items=>{
  243. paraStr += items.para
  244. })
  245. }
  246. item.exp_content = paraStr
  247. item.collect = this.likeWord.indexOf(item.word_name)>-1?true:false
  248. })
  249. this.phraseList.forEach(item=>{
  250. item.type='phrase'
  251. item.collect = this.likePhraseList.indexOf(item.exp_title)>-1?true:false
  252. })
  253. this.annotationList.forEach(item=>{
  254. item.type='explain'
  255. })
  256. this.allWordList = this.wordLit.concat(this.phraseList).concat(this.annotationList)
  257. let leg = articleInfo.art_corpus_data.sentList[articleInfo.art_corpus_data.sentList.length-1].pno
  258. this.sentenceList.forEach((item,index) => {
  259. let flag = ''
  260. item.StyleTokens = []
  261. item.tokens.forEach((items,indexs)=>{
  262. let obj = {
  263. tokens: items
  264. }
  265. this.allWordList.forEach((itema,indexa)=>{
  266. itema.bind_sent_data.bind_sents.forEach((itemb,indexb)=>{
  267. if(itemb.sent_id===item.id){
  268. if(itema.type==='explain'){
  269. if(indexs===itemb.sel_token_idxes[itemb.sel_token_idxes.length-1]){
  270. obj.highIndex = true
  271. obj.type = itema.type
  272. obj.word_id = itema.id // 生词注释短语的id
  273. obj.explainNumber = explainNumber
  274. explainNumber ++
  275. }
  276. }else{
  277. itemb.sel_token_idxes.forEach(itemi=>{
  278. if(indexs===itemi){
  279. obj.highIndex = true
  280. obj.type = itema.type
  281. obj.word_id = itema.id // 生词注释短语的id
  282. }
  283. })
  284. }
  285. if(itemb.sel_token_idxes.length===0&&itema.type==='explain'&&indexs===item.tokens.length-1){
  286. flag = itema.id
  287. }
  288. }
  289. })
  290. })
  291. item.StyleTokens.push(obj)
  292. if(flag){
  293. item.StyleTokens.push({
  294. tokens: [
  295. 0, 8, "Students", "", "", "", "", "", "", " ", 0, "", "", ""
  296. ],
  297. type: 'explain',
  298. word_id: flag,
  299. isExplain: true,
  300. explainNumber: explainNumber
  301. })
  302. explainNumber ++
  303. }
  304. })
  305. });
  306. for(let i=0;i<leg+1;i++){
  307. let obj = {
  308. wordsList: []
  309. }
  310. resArr.push(obj)
  311. }
  312. this.sentenceList.forEach((item,index) => {
  313. item.StyleTokens.forEach((items,indexs)=>{
  314. items.isShow = this.enFhList.indexOf(items.tokens[2])==-1
  315. resArr[item.pno].wordsList.push(items)
  316. })
  317. });
  318. this.resArr = resArr;
  319. },
  320. showItem(item){
  321. if(!item.isShow){
  322. return
  323. }else{
  324. if(item.type==='explain'){
  325. this.annotationList.forEach(itemi=>{
  326. if(item.word_id===itemi.id){
  327. this.showObj = itemi
  328. }
  329. })
  330. this.showExplainFlag = true
  331. }else if(item.type==='phrase'){
  332. this.phraseList.forEach((itemi,indexi)=>{
  333. if(item.word_id===itemi.id){
  334. this.showObj = itemi
  335. this.activeObjIndex = indexi
  336. }
  337. })
  338. this.showPhraseFlag = true
  339. }else if(item.type==='newWord'){
  340. this.showWordFlag = true
  341. }else{
  342. this.showSearchWordFlag = true
  343. }
  344. }
  345. console.log(item)
  346. },
  347. closeExplain(){
  348. this.showExplainFlag = false
  349. this.showPhraseFlag = false
  350. this.showWordFlag = false
  351. this.showSearchWordFlag = false
  352. this.showObj = null
  353. },
  354. changeLike(obj,list){
  355. this.$emit('changeLike',obj,list)
  356. }
  357. },
  358. //生命周期 - 创建完成(可以访问当前this实例)
  359. created() {},
  360. //生命周期 - 挂载完成(可以访问DOM元素)
  361. mounted() {
  362. if (this.articleInfo) {
  363. this.handleData();
  364. }
  365. },
  366. beforeCreate() {}, //生命周期 - 创建之前
  367. beforeMount() {}, //生命周期 - 挂载之前
  368. beforeUpdate() {}, //生命周期 - 更新之前
  369. updated() {}, //生命周期 - 更新之后
  370. beforeDestroy() {}, //生命周期 - 销毁之前
  371. destroyed() {}, //生命周期 - 销毁完成
  372. activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
  373. };
  374. </script>
  375. <style lang='scss' scoped>
  376. //@import url(); 引入公共css类
  377. .NNPE-ArticleView {
  378. width: 100%;
  379. .nnpe-article-author{
  380. margin: 24px 0;
  381. }
  382. h2{
  383. display: flex;
  384. flex-flow: wrap;
  385. &.sentActive {
  386. background: rgba(24, 144, 255, 0.1);
  387. }
  388. &.overActive {
  389. background: rgba(0, 0, 0, 0.06);
  390. }
  391. .wordActive {
  392. color: #175DFF !important;
  393. }
  394. .hasExplain{
  395. min-width: 3px;
  396. position: relative;
  397. }
  398. .explain-sub{
  399. position: absolute;
  400. bottom: -25px;
  401. right: -10px;
  402. z-index: 1;
  403. font-size: 0;
  404. cursor: pointer;
  405. img{
  406. width: 14px;
  407. height: 12px;
  408. }
  409. }
  410. }
  411. .table-box {
  412. // background: #f7f7f7;
  413. // border-top: 1px solid rgba(0, 0, 0, 0.1);
  414. :last-child {
  415. :last-child.wordsList-box {
  416. padding-bottom: 40px;
  417. }
  418. }
  419. .wordsList-box {
  420. flex: 1;
  421. padding: 6px 0 12px 0;
  422. .nnpe-sentence-box {
  423. display: flex;
  424. flex-flow: wrap;
  425. .word-box{
  426. position: relative;
  427. &.hasExplain{
  428. min-width: 3px;
  429. }
  430. .explain-sub{
  431. position: absolute;
  432. bottom: -5px;
  433. right: -10px;
  434. z-index: 1;
  435. font-size: 0;
  436. cursor: pointer;
  437. img{
  438. width: 14px;
  439. height: 12px;
  440. }
  441. }
  442. }
  443. }
  444. > img {
  445. max-width: 50%;
  446. display: block;
  447. padding: 16px 0;
  448. margin: 0 auto;
  449. }
  450. }
  451. }
  452. .NNPE-detail {
  453. clear: both;
  454. overflow: hidden;
  455. display: flex;
  456. .NNPE-words {
  457. float: left;
  458. padding: 0;
  459. &.noPadding{
  460. padding:0;
  461. }
  462. &.sentActive {
  463. background: rgba(24, 144, 255, 0.1);
  464. }
  465. &.overActive {
  466. background: rgba(0, 0, 0, 0.06);
  467. }
  468. > span {
  469. float: left;
  470. cursor: pointer;
  471. &.NNPE-chs {
  472. // font-size: 24px;
  473. font-family: 'Smartisan';
  474. line-height: 150%;
  475. color: #000000;
  476. &.wordActive {
  477. color: #175DFF !important;
  478. }
  479. &.marginRight{
  480. padding: 0 6px 0 0;
  481. }
  482. &.fontWeight{
  483. font-weight: bold;
  484. }
  485. &.newWord{
  486. color: #3459D2 !important;
  487. }
  488. &.phrase{
  489. color: #FF802B !important;
  490. }
  491. &.explain{
  492. // color: #23C847 !important;
  493. font-weight: 400;
  494. }
  495. }
  496. &.padding {
  497. padding: 0 3px;
  498. cursor: pointer;
  499. }
  500. }
  501. }
  502. }
  503. }
  504. .audio-box{
  505. display: flex;
  506. align-items: center;
  507. justify-content: space-between;
  508. .explain-video{
  509. background: #FFB224;
  510. border-color: #FFB224;
  511. color: #FFFFFF;
  512. width: 136px;
  513. height: 48px;
  514. padding: 0;
  515. font-weight: 500;
  516. font-size: 16px;
  517. border-radius: 30px;
  518. .svg-icon{
  519. margin-right: 8px;
  520. }
  521. }
  522. }
  523. .aduioLine-box{
  524. width: 516px;
  525. height: 48px;
  526. background: #FFFFFF;
  527. border: 1px solid #EBEBEB;
  528. border-radius: 30px;
  529. display: flex;
  530. align-items: center;
  531. padding: 8px 24px;
  532. .wrapper{
  533. width: 24px;
  534. height: 24px;
  535. flex-shrink: 0;
  536. color: #175DFF;
  537. margin-left: 8px;
  538. }
  539. .Audio{
  540. width: 430px;
  541. }
  542. }
  543. .explain-box{
  544. width: 451px;
  545. position: fixed;
  546. z-index: 1;
  547. top: 50%;
  548. left: 50%;
  549. margin-left: -225px;
  550. margin-top: -90px;
  551. border-radius: 4px;
  552. background: #FFF;
  553. box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08), 0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 6px 30px 5px rgba(0, 0, 0, 0.05);
  554. padding: 16px;
  555. &-top{
  556. display: flex;
  557. justify-content: space-between;
  558. align-items: center;
  559. p{
  560. margin: 0;
  561. color: #000;
  562. font-size: 14px;
  563. font-weight: 400;
  564. line-height: 22px;
  565. }
  566. .el-icon-close{
  567. cursor: pointer;
  568. }
  569. }
  570. h3{
  571. color:#2F3742;
  572. font-size: 20px;
  573. font-weight: 700;
  574. line-height: 28px;
  575. margin: 8px 0 0 0;
  576. }
  577. >span{
  578. display: block;
  579. margin: 8px 0 0 0;
  580. color:#667180;
  581. font-size: 14px;
  582. font-weight: 400;
  583. line-height: 22px;
  584. }
  585. }
  586. .phrase-box{
  587. // border-radius: 8px;
  588. // border: 1px solid #EBEBEB;
  589. // background: #FFF;
  590. // box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08), 0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 6px 30px 5px rgba(0, 0, 0, 0.05);
  591. // width: 580px;
  592. // height: 200px;
  593. // position: fixed;
  594. // left: 50%;
  595. // margin-left: -290px;
  596. // top: 200px;
  597. // z-index: 1;
  598. }
  599. </style>