WordPhrase.vue 16 KB

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