WordPhrase.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <template>
  2. <div class="NPC-zhedie">
  3. <div class="topTitle">
  4. <div class="NPC-top-left">
  5. <span class="NPC-topTitle-text">{{ curQue.title }}</span>
  6. <span
  7. :class="['NPC-play-all', playClass]"
  8. @click.stop="playNewwords"
  9. ></span>
  10. </div>
  11. <div class="NPC-top-right">
  12. <span class="NPC-top-right-text" @click="handleChangeTab">{{
  13. wordShow ? "收起" : "展开"
  14. }}</span>
  15. <img v-if="wordShow" src="../../../assets/NPC/down.png" alt="" />
  16. <img v-else src="../../../assets/NPC/up.png" alt="" />
  17. </div>
  18. </div>
  19. <el-collapse-transition>
  20. <div
  21. class="NPC-word-list"
  22. v-if="curQue.option && curQue.option.length > 0"
  23. v-show="wordShow"
  24. >
  25. <ul class="NPC-word-table" cellspacing="0" border="0" cellpadding="0">
  26. <li
  27. class="NPC-word-tr"
  28. v-for="(item, index) in curQue.option"
  29. :key="'curQue.option' + index"
  30. >
  31. <div
  32. :class="[
  33. 'NPC-word-row',
  34. mp3_index == sItem.sIndex ? 'active' : '',
  35. ]"
  36. v-for="(sItem, sIndex) in item"
  37. :key="'curQue.option.child' + sIndex"
  38. >
  39. <template
  40. v-if="
  41. sItem.mp3_list &&
  42. sItem.mp3_list.length > 0 &&
  43. sItem.mp3_list[0].id
  44. "
  45. >
  46. <span
  47. :class="[
  48. themeColor == 'green'
  49. ? 'NPC-play-btn-green'
  50. : themeColor == 'red'
  51. ? 'NPC-play-btn-red'
  52. : 'NPC-play-btn-brown',
  53. mp3_index == sItem.sIndex ? 'active' : '',
  54. ]"
  55. @click="palyAudio(sItem.sIndex)"
  56. ></span>
  57. <audio
  58. :id="'word' + indexs + indexss + sItem.sIndex"
  59. :src="sItem.mp3_list[0].id"
  60. ></audio>
  61. </template>
  62. <template v-else>
  63. <span style="width: 16px; height: 16px"></span>
  64. </template>
  65. <template v-if="sItem.mIndex == 0">
  66. <b class="tabNum">{{ index + 1 }}</b>
  67. </template>
  68. <div
  69. v-else
  70. style="width: 16px; height: 16px; margin-left: 8px"
  71. ></div>
  72. <span class="NPC-word-tab-common NPC-word-tab-pinyin">
  73. {{ sItem.pinyin }}
  74. </span>
  75. <span class="NPC-word-tab-common NPC-word-tab-word">
  76. {{ sItem.new_word }}
  77. </span>
  78. <span
  79. class="NPC-word-tab-common NPC-word-tab-cixing"
  80. v-html="sItem.cixing"
  81. ></span>
  82. <span
  83. class="NPC-word-tab-common NPC-word-tab-def"
  84. v-html="sItem.def_str"
  85. ></span>
  86. <span v-if="curQue.isInfor">
  87. <img
  88. src="../../../assets/NPC/detail-icon.png"
  89. class="detail-icon"
  90. @click="showDetail(sItem)"
  91. />
  92. </span>
  93. </div>
  94. </li>
  95. </ul>
  96. </div>
  97. </el-collapse-transition>
  98. <div v-if="detailShow">
  99. <WordPhraseDetail
  100. v-if="isSuccess"
  101. :data="data"
  102. :curQue="curQue"
  103. :changeDetailIndex="changeDetailIndex"
  104. :closeWord="closeWordShow"
  105. :detailIndex="detailIndex"
  106. :getWordLiju="getWordLiju"
  107. :optionRes="optionRes"
  108. :themeColor="themeColor"
  109. />
  110. </div>
  111. </div>
  112. </template>
  113. <script>
  114. //这里可以导入其它文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
  115. //例如:import 《组件名称》from ‘《组件路径》';
  116. import WordPhraseDetail from "./components/WordPhraseDetail.vue";
  117. import { getContent } from "@/api/ajax";
  118. export default {
  119. //import引入的组件需要注入到对象中才能使用
  120. components: {
  121. WordPhraseDetail,
  122. },
  123. props: ["curQue", "themeColor", "currentTreeID", "indexs", "indexss"],
  124. data() {
  125. //这里存放数据
  126. return {
  127. wordShow: true,
  128. data: null,
  129. detailShow: false,
  130. detailIndex: 0,
  131. audio: new Audio(),
  132. playClass: "",
  133. mp3_index: -1,
  134. playWord: null,
  135. optionRes: [],
  136. mp3List: [],
  137. isSuccess: false,
  138. };
  139. },
  140. //计算属性 类似于data概念
  141. computed: {},
  142. //监控data中数据变化
  143. watch: {
  144. currentTreeID: {
  145. handler: function () {
  146. this.mp3_index = -1; // 全文预览
  147. this.optionRes = []; // 显示单词和短语
  148. this.mp3List = []; // 语音练习
  149. this.initData();
  150. },
  151. deep: true,
  152. },
  153. },
  154. //方法集合
  155. methods: {
  156. handleChange(val) {},
  157. palyAudio(sIndex) {
  158. let _this = this;
  159. _this.stopAudio();
  160. let node = document.getElementById(
  161. "word" + _this.indexs + _this.indexss + sIndex
  162. );
  163. _this.playWord = node;
  164. if (node) {
  165. this.mp3_index = sIndex;
  166. node.play();
  167. }
  168. this.handleListenPlay(sIndex);
  169. },
  170. handleListenPlay(sIndex) {
  171. let _this = this;
  172. if (_this.playWord) {
  173. let _this = this;
  174. _this.playWord.addEventListener("play", function () {});
  175. _this.playWord.addEventListener("pause", function () {
  176. _this.mp3_index = -1;
  177. });
  178. _this.playWord.addEventListener("ended", function () {
  179. _this.mp3_index = -1;
  180. });
  181. }
  182. },
  183. // 打开单词详情
  184. showDetail(item) {
  185. this.data = null;
  186. this.data = item;
  187. console.log(this.data);
  188. this.detailShow = true;
  189. this.detailIndex = item.sIndex;
  190. this.getWordLiju(item.new_word);
  191. },
  192. // 获取生词的例句
  193. getWordLiju(val) {
  194. this.isSuccess = false;
  195. console.log(this.data);
  196. this.data.list1 = [];
  197. this.data.list2 = [];
  198. this.data.list3 = [];
  199. let Mname =
  200. "book-courseware_manager-GetCoursewareWordExampleSentenceList";
  201. // 获取本课的 本教材的 本套的 的例句
  202. getContent(Mname, {
  203. courseware_id: this.currentTreeID, // 课件id
  204. word: val, //生词
  205. search_scope: 2, //检索范围0 本课件 1本教材 2本套
  206. is_contain_word_variants: false,
  207. }).then((res) => {
  208. this.data.list3 = res.sentence_list;
  209. getContent(Mname, {
  210. courseware_id: this.currentTreeID, // 课件id
  211. word: val, //生词
  212. search_scope: 1, //检索范围0 本课件 1本教材 2本套
  213. is_contain_word_variants: false,
  214. }).then((res) => {
  215. this.data.list2 = res.sentence_list;
  216. getContent(Mname, {
  217. courseware_id: this.currentTreeID, // 课件id
  218. word: val, //生词
  219. search_scope: 0, //检索范围0 本课件 1本教材 2本套
  220. is_contain_word_variants: false,
  221. }).then((res) => {
  222. this.$set(this.data, "list1", res.sentence_list);
  223. this.isSuccess = true;
  224. });
  225. });
  226. });
  227. },
  228. // 关闭单词详情
  229. closeWordShow(val) {
  230. this.detailShow = val;
  231. },
  232. // 上一个单词详情
  233. lasspanetail(val) {
  234. this.detailIndex = val;
  235. this.data = null;
  236. this.data = this.optionRes[this.detailIndex];
  237. this.getWordLiju(this.data.new_word);
  238. },
  239. // 下一个单词详情
  240. nexspanetail(val) {
  241. this.detailIndex = val;
  242. this.data = null;
  243. this.data = this.optionRes[this.detailIndex];
  244. this.getWordLiju(this.data.new_word);
  245. },
  246. changeDetailIndex(val) {
  247. if (val == "last") {
  248. this.detailIndex--;
  249. } else {
  250. this.detailIndex++;
  251. }
  252. this.data = null;
  253. this.data = this.optionRes[this.detailIndex];
  254. },
  255. playNewwords() {
  256. let _this = this;
  257. if (_this.playWord) {
  258. _this.playWord.pause();
  259. }
  260. let mp3_index = 0;
  261. let leg = _this.mp3List.length;
  262. let mp3 = _this.mp3List[mp3_index].mp3_list[0].id;
  263. _this.mp3_index = _this.mp3List[mp3_index].sIndex;
  264. _this.handlePlayVoice(mp3);
  265. _this.audio.addEventListener("ended", function () {
  266. if (mp3_index < leg - 1) {
  267. mp3_index = mp3_index + 1;
  268. _this.mp3_index = _this.mp3List[mp3_index].sIndex;
  269. mp3 =
  270. _this.mp3List[mp3_index].mp3_list.length > 0 &&
  271. _this.mp3List[mp3_index].mp3_list[0].id;
  272. if (mp3) {
  273. _this.handlePlayVoice(mp3);
  274. }
  275. } else {
  276. _this.mp3_index = -1;
  277. }
  278. });
  279. },
  280. handlePlayVoice(mp3) {
  281. let _this = this;
  282. if (!mp3) {
  283. return;
  284. }
  285. if (!this.audio.paused) {
  286. this.audio.pause();
  287. } else {
  288. _this.audio.pause();
  289. _this.audio.load();
  290. _this.audio.src = mp3;
  291. _this.audio.loop = false;
  292. _this.audio.play();
  293. }
  294. },
  295. stopAudio() {
  296. if (this.audio) {
  297. this.audio.pause();
  298. }
  299. },
  300. initData() {
  301. if (this.curQue.type == "NewWord_chs") {
  302. let resIndex = 0;
  303. let optionRes = [];
  304. let mp3List = [];
  305. this.curQue.option.forEach((item, index) => {
  306. optionRes = optionRes.concat(item);
  307. item.forEach((sItem, sIndex) => {
  308. sItem.mIndex = sIndex;
  309. sItem.sIndex = resIndex;
  310. resIndex++;
  311. sItem.def_str =
  312. sItem.definition_list.length > 0
  313. ? sItem.definition_list.join(" ")
  314. : "";
  315. if (sItem.mp3_list[0]) {
  316. mp3List.push(sItem);
  317. }
  318. });
  319. });
  320. this.optionRes = JSON.parse(JSON.stringify(optionRes));
  321. this.mp3List = mp3List;
  322. }
  323. },
  324. handleChangeTab() {
  325. this.wordShow = !this.wordShow;
  326. },
  327. },
  328. //生命周期 - 创建完成(可以访问当前this实例)
  329. created() {
  330. this.initData();
  331. },
  332. //生命周期 - 挂载完成(可以访问DOM元素)
  333. mounted() {
  334. let _this = this;
  335. _this.audio.addEventListener("play", function () {
  336. _this.playClass = "active";
  337. });
  338. _this.audio.addEventListener("pause", function () {
  339. _this.playClass = "";
  340. });
  341. _this.audio.addEventListener("ended", function () {
  342. _this.playClass = "";
  343. });
  344. },
  345. //生命周期-创建之前
  346. beforeCreated() {},
  347. //生命周期-挂载之前
  348. beforeMount() {},
  349. //生命周期-更新之前
  350. beforUpdate() {},
  351. //生命周期-更新之后
  352. updated() {},
  353. //生命周期-销毁之前
  354. beforeDestory() {},
  355. //生命周期-销毁完成
  356. destoryed() {},
  357. //如果页面有keep-alive缓存功能,这个函数会触发
  358. activated() {},
  359. };
  360. </script>
  361. <style lang="scss" scoped>
  362. /* @import url(); 引入css类 */
  363. .NPC-zhedie {
  364. width: 780px;
  365. margin-bottom: 16px;
  366. .NPC-word-list {
  367. background: #f7f7f7;
  368. }
  369. .NPC-word-table {
  370. width: 100%;
  371. > .NPC-word-tr {
  372. background: #fff;
  373. border-radius: 8px;
  374. margin-bottom: 8px;
  375. .NPC-word-row {
  376. cursor: pointer;
  377. display: flex;
  378. justify-content: flex-start;
  379. padding: 8px 13px 8px 12px;
  380. border-radius: 8px;
  381. &.active {
  382. background: linear-gradient(
  383. 0deg,
  384. rgba(0, 0, 0, 0.08),
  385. rgba(0, 0, 0, 0.08)
  386. ),
  387. #ffffff;
  388. }
  389. > span {
  390. font-size: 16px;
  391. line-height: 150%;
  392. color: #000000;
  393. }
  394. }
  395. .tabNum {
  396. background: #de4444;
  397. text-align: center;
  398. width: 16px;
  399. height: 16px;
  400. color: #ffffff;
  401. border-radius: 50%;
  402. font-size: 12px;
  403. font-family: "robot";
  404. line-height: 16px;
  405. margin-top: 4px;
  406. margin-left: 8px;
  407. }
  408. .NPC-word-tab-common {
  409. padding-left: 8px;
  410. width: 120px;
  411. box-sizing: border-box;
  412. }
  413. .NPC-word-tab-pinyin {
  414. font-family: "GB-PINYINOK-B";
  415. white-space: nowrap;
  416. }
  417. .NPC-word-tab-word {
  418. font-family: "sourceR";
  419. white-space: nowrap;
  420. }
  421. .NPC-word-tab-cixing {
  422. font-family: "robot";
  423. width: 48px;
  424. box-sizing: border-box;
  425. text-align: right;
  426. }
  427. .NPC-word-tab-def {
  428. flex: 1;
  429. font-family: "robot";
  430. word-break: break-word;
  431. box-sizing: border-box;
  432. white-space: pre-wrap;
  433. }
  434. }
  435. }
  436. .NPC-play-btn-brown {
  437. margin-top: 4px;
  438. width: 16px;
  439. height: 16px;
  440. display: block;
  441. background: url("../../../assets/NPC/play-brown.png") no-repeat left top;
  442. background-size: 100% 100%;
  443. &.active {
  444. background: url("../../../assets/NPC/icon-voice-play-brown.png") no-repeat
  445. left top;
  446. background-size: 100% 100%;
  447. }
  448. }
  449. .NPC-play-btn-green {
  450. margin-top: 4px;
  451. width: 16px;
  452. height: 16px;
  453. display: block;
  454. background: url("../../../assets/NPC/play-green.png") no-repeat left top;
  455. background-size: 100% 100%;
  456. &.active {
  457. background: url("../../../assets/NPC/icon-voice-play-green.png") no-repeat
  458. left top;
  459. background-size: 100% 100%;
  460. }
  461. }
  462. .NPC-play-btn-red {
  463. margin-top: 4px;
  464. width: 16px;
  465. height: 16px;
  466. display: block;
  467. background: url("../../../assets/NPC/play-red.png") no-repeat left top;
  468. background-size: 100% 100%;
  469. &.active {
  470. background: url("../../../assets/NPC/icon-voice-play-red.png") no-repeat
  471. left top;
  472. background-size: 100% 100%;
  473. }
  474. }
  475. .NPC-word-list {
  476. padding: 20px 24px;
  477. border: 1px solid rgba(0, 0, 0, 0.1);
  478. border-top: none;
  479. border-radius: 0 0 8px 8px;
  480. }
  481. .detail-icon {
  482. width: 56px;
  483. height: 24px;
  484. display: block;
  485. cursor: pointer;
  486. }
  487. }
  488. @keyframes firstrotate {
  489. 0% {
  490. transform: rotateZ(0deg);
  491. }
  492. 100% {
  493. transform: rotateZ(180deg);
  494. }
  495. }
  496. @keyframes huifuRotate {
  497. 0% {
  498. transform: rotateZ(180deg);
  499. }
  500. 100% {
  501. transform: rotateZ(0deg);
  502. }
  503. }
  504. </style>
  505. <style lang="scss">
  506. .NPC-zhedie {
  507. .topTitle {
  508. width: 100%;
  509. display: flex;
  510. justify-content: space-between;
  511. padding-left: 24px;
  512. padding-right: 16px;
  513. height: 48px;
  514. background: #e35454;
  515. border: 1px solid rgba(0, 0, 0, 0.1);
  516. overflow: hidden;
  517. border-radius: 8px 8px 0 0;
  518. &.topTitleWhite {
  519. background: #fff;
  520. }
  521. .NPC-top-left {
  522. display: flex;
  523. justify-content: flex-start;
  524. align-items: center;
  525. .NPC-topTitle-text {
  526. font-family: "sourceR";
  527. font-size: 16px;
  528. color: #fff;
  529. font-weight: bold;
  530. margin-right: 8px;
  531. }
  532. .NPC-play-all {
  533. width: 16px;
  534. height: 16px;
  535. background: url("../../../assets/NPC/play-white.png") no-repeat left top;
  536. background-size: 100% 100%;
  537. cursor: pointer;
  538. &.active {
  539. width: 16px;
  540. height: 16px;
  541. background: url("../../../assets/NPC/icon-voice-play-white.png")
  542. no-repeat left top;
  543. background-size: 100% 100%;
  544. }
  545. }
  546. }
  547. .NPC-top-right {
  548. display: flex;
  549. justify-content: flex-start;
  550. align-items: center;
  551. cursor: pointer;
  552. &-text {
  553. font-weight: normal;
  554. font-size: 14px;
  555. line-height: 16px;
  556. color: #ffffff;
  557. }
  558. img {
  559. width: 16px;
  560. height: 16px;
  561. margin-left: 4px;
  562. }
  563. }
  564. img {
  565. width: 24px;
  566. height: 24px;
  567. }
  568. .rotate {
  569. animation-name: firstrotate;
  570. animation-direction: 2s;
  571. animation-fill-mode: both;
  572. animation-timing-function: linear;
  573. }
  574. }
  575. .el-collapse-item__content {
  576. padding-bottom: 0;
  577. }
  578. .el-slider__button {
  579. width: 8px;
  580. height: 8px;
  581. }
  582. .el-slider__runway {
  583. margin: 0;
  584. padding: 0;
  585. }
  586. .el-slider {
  587. position: relative;
  588. top: -3px;
  589. }
  590. .el-collapse {
  591. background: #f7f7f7;
  592. box-sizing: border-box;
  593. border-radius: 8px;
  594. }
  595. .el-collapse-item__wrap {
  596. border: 1px solid rgba(0, 0, 0, 0.1);
  597. border-top: 0;
  598. background: #f7f7f7;
  599. border-radius: 0px 0px 8px 8px;
  600. }
  601. .el-collapse-item__arrow {
  602. display: none;
  603. }
  604. .el-table__row {
  605. padding: 4px 0;
  606. }
  607. }
  608. .NPC-Big-Book-preview-green {
  609. .NPC-zhedie {
  610. .topTitle {
  611. background: #24b99e !important;
  612. }
  613. .tabNum {
  614. background: #24b99e;
  615. }
  616. }
  617. }
  618. .NPC-Big-Book-preview-brown {
  619. .NPC-zhedie {
  620. .topTitle {
  621. background: #bd8865 !important;
  622. }
  623. .tabNum {
  624. background: #bd8865;
  625. }
  626. }
  627. }
  628. </style>