WordPhrase.vue 16 KB

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