WordPhrase.vue 18 KB

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