guanchunjie 3 anos atrás
pai
commit
4188adeccf

+ 1 - 0
package.json

@@ -44,6 +44,7 @@
     "connect": "3.6.6",
     "eslint": "6.7.2",
     "eslint-plugin-vue": "6.2.2",
+    "hanzi-writer": "^3.1.0",
     "html-webpack-plugin": "3.2.0",
     "mockjs": "1.0.1-beta3",
     "runjs": "4.3.2",

BIN
src/assets/Cross-16-normal-Black.png


BIN
src/assets/Left-16-normal-Black.png


BIN
src/assets/Right-16-normal-Black.png


BIN
src/assets/chinaTianGreen.png


BIN
src/assets/chinaTianRed.png


BIN
src/assets/chinaTianYellow.png


BIN
src/assets/down-black.png


BIN
src/assets/down.png


BIN
src/assets/icon-voice-play-red.png


BIN
src/assets/play-red.png


BIN
src/assets/starfill-16-normal-red.png


BIN
src/assets/strock-play-green-click.png


BIN
src/assets/strock-play-red-click.png


BIN
src/assets/strock-play-yellow-click.png


BIN
src/assets/up-black.png


BIN
src/assets/up.png


BIN
src/assets/word-close.png


+ 69 - 56
src/components/Personalcenter/Mycollect.vue

@@ -18,19 +18,55 @@
         mode="horizontal"
         @select="handleSelect"
       >
-        <el-menu-item index="all">全部</el-menu-item>
+        <!-- <el-menu-item index="all">全部</el-menu-item> -->
         <el-menu-item index="book">教材</el-menu-item>
         <el-menu-item index="teaching">教学课程</el-menu-item>
         <el-menu-item index="video">视频</el-menu-item>
         <el-menu-item index="live">直播</el-menu-item>
         <el-menu-item index="jiaoyan">教研资料</el-menu-item>
-        <!-- <el-menu-item index="hanzi">字词</el-menu-item> -->
+        <el-menu-item index="hanzi">字词</el-menu-item>
+        <el-menu-item index="sentence">句子</el-menu-item>
       </el-menu>
     </div>
     <div class="list" v-loading="loading">
       <template v-if="!isEmpty">
         <div class="main" v-for="(item, index) in list" :key="'order' + index">
-          <div class="main-content" v-if="item.goods_type != 'hanzi'">
+          <div class="main-content" v-if="item.goods_type == 501">
+            <div
+              v-if="isShowCheckBox"
+              class="checkBox"
+              @click.stop="selecedGoods(item, index)"
+            >
+              <img
+                :src="item.checked ? checked_img : check_img"
+                class="check-img"
+              />
+            </div>
+            <Hanzi
+              :item="item"
+              :index="index"
+              @getMyCollectionList="getMyCollectionList"
+            />
+          </div>
+          <div class="main-content" v-else-if="item.goods_type == 502">
+            <div
+              v-if="isShowCheckBox"
+              class="checkBox"
+              @click.stop="selecedGoods(item, index)"
+            >
+              <img
+                :src="item.checked ? checked_img : check_img"
+                class="check-img"
+              />
+            </div>
+            <Sentence
+              :key="item.id"
+              :sItem="item"
+              :index="index"
+              @getMyCollectionList="getMyCollectionList"
+            />
+          </div>
+          <div class="main-content" v-else>
             <div class="main-content-left" @click.stop="jump(item)">
               <div
                 v-if="isShowCheckBox"
@@ -71,44 +107,6 @@
               </div>
             </div>
           </div>
-          <div class="main-content" v-else>
-            <div class="main-content-left">
-              <div class="checkBox">
-                <img
-                  :src="
-                    item.checked
-                      ? require('../../assets/Personalcenter/checked.png')
-                      : require('../../assets/Personalcenter/check.png')
-                  "
-                  class="check-img"
-                />
-              </div>
-              <div class="hanzi">春</div>
-              <div class="order-infor">
-                <p class="pinyin">{{ item.detail.pinyin }}</p>
-                <p class="en">{{ item.detail.en }}</p>
-                <div class="hanzi-btn">
-                  <div class="hanzi-btn-img">
-                    <img
-                      src="../../assets/Personalcenter/hanzi-play.png"
-                      class=""
-                    />
-                  </div>
-                  <div class="hanzi-btn-img">
-                    <img
-                      src="../../assets/Personalcenter/hanzi-write.png"
-                      class=""
-                    />
-                  </div>
-                  <span class="more-intp">更多释义</span>
-                </div>
-              </div>
-            </div>
-            <div class="main-content-right hanzi-right">
-              <span class="resource">{{ item.detail.resource }}</span>
-              <p class="collectTime">{{ item.collectTime }}</p>
-            </div>
-          </div>
         </div>
         <div class="paging">
           <el-pagination
@@ -166,11 +164,21 @@ import Confirmorder from "../common/Confirmorder.vue";
 import Payment from "../common/Payment.vue";
 
 import { jumpPath } from "@/utils/jumpPath";
+import Hanzi from "../common/Hanzi.vue";
+import Sentence from "../common/Sentence.vue";
 
 export default {
   name: "MyCollect",
   //import引入的组件需要注入到对象中才能使用
-  components: { EditTitle, Empty, Goodstype, Confirmorder, Payment },
+  components: {
+    EditTitle,
+    Empty,
+    Goodstype,
+    Confirmorder,
+    Payment,
+    Hanzi,
+    Sentence,
+  },
   props: {},
   data() {
     //这里存放数据
@@ -181,7 +189,7 @@ export default {
       list: [],
       goods_name: "", // 商品名称,模糊查询,空表示查询所有
       goods_id_list: [], // 商品 ID 列表,空表示查询所有商品
-      goods_type_list: [], // 商品类型列表,具体参看数据字典 6.9,商品类型。空表示查询所有类型
+      goods_type_list: [101], // 商品类型列表,具体参看数据字典 6.9,商品类型。空表示查询所有类型
       total: 0,
       pageSize: 20,
       pageNum: 1,
@@ -283,6 +291,7 @@ export default {
             this.list = [];
             this.listCount = 0;
           }
+          console.log(this.list);
         })
         .catch((error) => {
           _this.loading = false;
@@ -345,7 +354,10 @@ export default {
         _this.goods_type_list = [301];
       } else if (_this.activeIndex == "jiaoyan") {
         _this.goods_type_list = [401];
-      } else {
+      } else if (_this.activeIndex == "hanzi") {
+        _this.goods_type_list = [501];
+      } else if (_this.activeIndex == "sentence") {
+        _this.goods_type_list = [502];
       }
       _this.getMyCollectionList();
     },
@@ -467,6 +479,18 @@ export default {
       padding: 16px 16px 16px 16px;
       background: #ffffff;
       box-shadow: inset 0px -1px 0px rgba(0, 0, 0, 0.15);
+      .checkBox {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        height: 88px;
+        margin-right: 16px;
+        cursor: pointer;
+        .check-img {
+          width: 16px;
+          height: 16px;
+        }
+      }
       &-top {
         width: 100%;
         display: flex;
@@ -505,18 +529,7 @@ export default {
         &-left {
           display: flex;
           justify-content: flex-start;
-          .checkBox {
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            height: 88px;
-            margin-right: 16px;
-            cursor: pointer;
-            .check-img {
-              width: 16px;
-              height: 16px;
-            }
-          }
+
           .coverUrl {
             width: 88px;
             height: 88px;

+ 473 - 0
src/components/common/AudioLineSentence.vue

@@ -0,0 +1,473 @@
+<template>
+  <!-- mp3Source && mp3Source == 'tts' ? 'Audio-tts' : '', -->
+  <div :class="['Audio', 'AudioFull']">
+    <div
+      :class="['audioLine3', bgIndex == 1 ? 'audioLine3-green' : '']"
+      @click="PlayAudio"
+    >
+      <div
+        class="play"
+        :class="[
+          audio.loading ? 'loadBtn' : audio.playing ? 'playBtn' : 'pauseBtn',
+        ]"
+      />
+    </div>
+    <input
+      style="position: absolute; top: -1000px; left: -100px; width: 5px"
+      autocomplete="false"
+      @keyup.space="PlayAudio"
+    />
+    <!-- <div :class="['audioLine', bgIndex == 1 ? 'audioLine-green' : '']">
+      <div
+        class="play"
+        :class="[
+          audio.loading ? 'loadBtn' : audio.playing ? 'playBtn' : 'pauseBtn',
+        ]"
+        @click="PlayAudio"
+      />
+      <template >
+        <el-slider
+          v-model="playValue"
+          :style="{ width: sliderWidth + 'px', height: '6px' }"
+          :format-tooltip="formatProcessToolTip"
+          @change="changeCurrentTime"
+        />
+        <span :class="bgIndex == 1 ? 'color-white' : ''">
+          {{ realFormatSecond(audio.maxTime) }}
+        
+        </span>
+      </template>
+    </div> -->
+    <!-- <div v-else class="audioLine2">
+      <div
+        class="play-icon"
+        :class="
+          audio.loading
+            ? 'loadBtn'
+            : audio.playing
+            ? 'playBtn-icon'
+            : 'pauseBtn-icon'
+        "
+        @click="PlayAudio"
+      />
+    </div> -->
+    <audio
+      :id="audioId"
+      :ref="audioId"
+      :src="mp3"
+      preload="meta"
+      @loadedmetadata="onLoadedmetadata"
+      @timeupdate="onTimeupdate"
+      @canplaythrough="oncanplaythrough"
+    />
+  </div>
+</template>
+
+<script>
+// 这里可以导入其它文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》from ‘《组件路径》';
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {},
+  props: [
+    "mp3",
+    "mp3Source",
+    "getCurTime",
+    "stopAudio",
+    "width",
+    "isRepeat",
+    "themeColor",
+    "ed",
+    "bg",
+    "audioId",
+    "maxTime",
+    "hideSlider",
+    "bgIndex",
+    "wordPlay",
+  ],
+  data() {
+    // 这里存放数据
+    return {
+      playValue: 0,
+      audio: {
+        // 该字段是音频是否处于播放状态的属性
+        playing: false,
+        // 音频当前播放时长
+        currentTime: 0,
+        // 音频最大播放时长
+        maxTime: 0,
+        isPlaying: false,
+        loading: false,
+      },
+      playTime: parseInt(this.maxTime),
+      audioAllTime: null, // 展示总时间
+      duioCurrentTime: null, // 剩余时间
+      count: 0,
+      loading: null,
+    };
+  },
+  // 计算属性 类似于data概念
+  computed: {
+    sliderWidth() {
+      let width = 0;
+      if (this.width) {
+        width = this.width;
+      } else {
+        width = 662;
+      }
+      return width;
+    },
+  },
+  // 监控data中数据变化
+  watch: {
+    stopAudio: {
+      handler(val, oldVal) {
+        const _this = this;
+        if (val) {
+          _this.$refs[_this.audioId].pause();
+          _this.audio.playing = false;
+        }
+      },
+      // 深度观察监听
+      deep: true,
+    },
+    "audio.playing": {
+      handler(val) {
+        this.$emit("playChange", val);
+        if (val) this.$emit("handleChangeStopAudio");
+      },
+    },
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    let _this = this;
+    _this.$nextTick(() => {
+      if (_this.wordPlay) {
+        _this.PlayAudio();
+      }
+    });
+
+    let audioId = _this.audioId;
+    _this.$refs[audioId].addEventListener("loadstart", function () {});
+    _this.$refs[audioId].addEventListener("play", function () {
+      _this.audio.playing = true;
+      _this.audio.loading = false;
+    });
+    _this.$refs[audioId].addEventListener("pause", function () {
+      _this.audio.playing = false;
+      if (_this.audio.currentTime * 1000 + 500 > _this.ed) {
+        //_this.$emit("sentPause", true);
+        _this.playValue = 0;
+        _this.audio.isPlaying = false;
+      }
+      if (_this.wordPlay) {
+        _this.$emit("changePlayStatus");
+        _this.audio.isPlaying = false;
+      }
+    });
+    _this.$refs[audioId].addEventListener("ended", function () {
+      _this.audio.playing = false;
+      _this.audio.isPlaying = false;
+      _this.$emit("handleListenRead", false);
+    });
+
+    // this.$nextTick(() => {
+    //   document
+    //     .getElementsByClassName("el-slider__button-wrapper")[0]
+    //     .addEventListener("mousedown", function () {
+    //       _this.$refs[audioId].pause();
+    //       _this.audio.playing = false;
+    //     });
+    // });
+  },
+  // 生命周期-挂载之前
+  beforeMount() {},
+  // 生命周期-更新之后
+  updated() {},
+  // 如果页面有keep-alive缓存功能,这个函数会触发
+  activated() {},
+  // 方法集合
+  methods: {
+    PlayAudio() {
+      let audioId = this.audioId;
+      let audio = document.getElementsByTagName("audio");
+      audio.forEach((item) => {
+        if (item.src == this.mp3) {
+          if (item.id !== audioId) {
+            item.pause();
+          }
+        } else {
+          item.pause();
+        }
+      });
+      let video = document.getElementsByTagName("video");
+      video.forEach((vItem) => {
+        vItem.pause();
+      });
+      this.$set(this.audio, "isPlaying", true);
+
+      if (this.audio.playing) {
+        this.$refs[audioId].pause();
+        this.audio.playing = false;
+        this.$emit("handleListenRead", false);
+      } else {
+        if (this.count == 0) {
+          this.audio.loading = true;
+          this.count++;
+        }
+
+        this.$refs[audioId].play();
+        this.onTimeupdateTime(this.bg / 1000);
+
+        this.$emit("handleChangeStopAudio");
+        this.$emit("handleListenRead", true);
+      }
+    },
+    oncanplaythrough() {
+      let _this = this;
+      // setTimeout(() => {
+      _this.audio.loading = false;
+
+      // }, 10000);
+    },
+    // 点击 拖拽播放音频
+    changeCurrentTime(value) {
+      let audioId = this.audioId;
+      this.$refs[audioId].play();
+      this.audio.playing = true;
+      this.$refs[audioId].currentTime = parseInt(
+        (value / 100) * this.audio.maxTime
+      );
+    },
+    mousedown() {
+      let audioId = this.audioId;
+      this.$refs[audioId].pause();
+      this.audio.playing = false;
+    },
+    // 进度条格式化toolTip
+    formatProcessToolTip(index) {
+      index = parseInt((this.audio.maxTime / 100) * index);
+      return this.realFormatSecond(index);
+    },
+    // 音频加载完之后
+    onLoadedmetadata(res) {
+      this.audio.maxTime = parseInt(this.maxTime);
+      //this.audio.maxTime = parseInt(res.target.duration);
+      //this.playTime = parseInt(this.maxTime);
+      //this.audioAllTime = this.realFormatSecond(this.audio.maxTime);
+    },
+    // 当音频当前时间改变后,进度条也要改变
+    onTimeupdate(res) {
+      let _this = this;
+      let audioId = _this.audioId;
+
+      _this.audio.currentTime = res.target.currentTime;
+      _this.getCurTime(res.target.currentTime);
+      let time = _this.audio.currentTime - _this.bg / 1000;
+      _this.playValue = (time / _this.audio.maxTime) * 100;
+      setTimeout(() => {
+        if (_this.audio.currentTime * 1000 > _this.ed) {
+          if (_this.$refs[audioId]) {
+            _this.$refs[audioId].pause();
+          }
+        }
+      }, 50);
+    },
+    onTimeupdateTime(res, playFlag) {
+      if (!res) return;
+      let audioId = this.audioId;
+      this.$refs[audioId].currentTime = res;
+      let time = res - this.bg / 1000;
+      this.playValue = (time / this.audio.maxTime) * 100;
+      if (playFlag) {
+        let audio = document.getElementsByTagName("audio");
+        audio.forEach((item) => {
+          if (item.id !== audioId) {
+            item.pause();
+          }
+        });
+        this.$refs[audioId].play();
+      }
+    },
+    // 将整数转换成 时:分:秒的格式
+    realFormatSecond(value) {
+      let theTime = parseInt(value); // 秒
+      let theTime1 = 0; // 分
+      let theTime2 = 0; // 小时
+      if (theTime > 60) {
+        theTime1 = parseInt(theTime / 60);
+        theTime = parseInt(theTime % 60);
+        if (theTime1 > 60) {
+          theTime2 = parseInt(theTime1 / 60);
+          theTime1 = parseInt(theTime1 % 60);
+        }
+      }
+      let result = String(parseInt(theTime));
+      if (result < 10) {
+        result = "0" + result;
+      }
+      if (theTime1 > 0) {
+        result = String(parseInt(theTime1)) + ":" + result;
+        if (theTime1 < 10) {
+          result = "0" + result;
+        }
+      } else {
+        result = "00:" + result;
+      }
+      if (theTime2 > 0) {
+        result = String(parseInt(theTime2)) + ":" + result;
+        if (theTime2 < 10) {
+          result = "0" + result;
+        }
+      } else {
+        // result = "00:" + result;
+      }
+      return result;
+    },
+  },
+  // 生命周期-创建之前
+  beforeCreated() {},
+  // 生命周期-更新之前
+  beforUpdate() {},
+  // 生命周期-销毁之前
+  beforeDestory() {},
+  // 生命周期-销毁完成
+  destoryed() {},
+};
+</script>
+<style lang="scss" scoped>
+/* @import url(); 引入css类 */
+.AudioFull {
+  .audioLine {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    height: 40px;
+    background: #ffffff;
+    // border: 1px solid rgba(0, 0, 0, 0.1);
+    // box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1);
+    box-sizing: border-box;
+    border-radius: 4px;
+    &-green {
+      background: 0 0;
+    }
+    .play {
+      margin-right: 12px;
+      margin-left: 8px;
+      width: 16px;
+      min-width: 16px;
+      height: 16px;
+      cursor: pointer;
+      display: block;
+    }
+
+    span {
+      font-size: 16px;
+      line-height: 19px;
+      color: #000;
+      margin-left: 8px;
+      margin-right: 12px;
+      font-weight: bold;
+      font-size: 16px;
+      line-height: 24px;
+      text-align: right;
+      &.color-white {
+        color: #fff;
+      }
+    }
+  }
+
+  // .loadBtn {
+  //   background: url("../../../assets/NPC/loading-red.png") no-repeat left top;
+  //   background-size: 100% 100%;
+  // }
+  .audioLine3 {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    .play {
+      width: 16px;
+      height: 16px;
+      cursor: pointer;
+      &.playBtn {
+        background: url("../../assets/icon-voice-play-red.png") no-repeat left
+          top;
+        background-size: 100% 100%;
+      }
+      &.pauseBtn {
+        background: url("../../assets/play-red.png") no-repeat left top;
+        background-size: 100% 100%;
+      }
+    }
+  }
+}
+</style>
+<style lang="scss">
+.AudioFull {
+  .el-slider__button-wrapper {
+    position: relative;
+    z-index: 0;
+  }
+  .el-slider__button {
+    width: 6px;
+    height: 12px;
+    border-radius: 6px;
+    top: 12px;
+    position: absolute;
+  }
+  .el-slider__runway {
+    margin: 0;
+    padding: 0;
+    background: #e5e5e5;
+    border-radius: 6px;
+    height: 6px;
+  }
+  .el-slider {
+    position: relative;
+  }
+  .el-slider__bar {
+    height: 6px;
+    border-radius: 6px 0 0 6px;
+    background: #de4444;
+  }
+  .el-slider__button {
+    background: #de4444;
+    border: none;
+  }
+  .el-slider__button-wrapper {
+    width: 25px;
+  }
+  .audioLine-green {
+    .el-slider__bar {
+      background: #ffc600 !important;
+    }
+    .el-slider__button {
+      background: #ffc600 !important;
+    }
+  }
+}
+.NPC-Book-Sty {
+  .AudioFull {
+    .el-slider__bar {
+      height: 6px;
+      border-radius: 6px;
+      background: #de4444;
+    }
+    .el-slider__button {
+      background: #de4444;
+      border: none;
+    }
+    .audioLine-green {
+      .el-slider__bar {
+        background: #ffc600 !important;
+      }
+      .el-slider__button {
+        background: #ffc600 !important;
+      }
+    }
+  }
+}
+</style>

+ 103 - 0
src/components/common/AudioRed.vue

@@ -0,0 +1,103 @@
+<!--  -->
+<template>
+  <div v-if="mp3" class="content-voices" @click="handlePlayVoice">
+    <img :src="voiceSrc" />
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: ["seconds", "mp3"],
+  data() {
+    return {
+      audio: new Audio(),
+      voiceSrc: "",
+      voicePauseSrc: require("../../assets/play-red.png"),
+      voicePlaySrc: require("../../assets/icon-voice-play-red.png"),
+    };
+  },
+  computed: {},
+  watch: {},
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created() {
+    let that = this;
+    window.stopAudioVoice = function () {
+      if (that.audio) {
+        that.audio.pause();
+      }
+    };
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    let _this = this;
+    _this.voiceSrc = _this.isCompare
+      ? _this.comparePlaySrc
+      : _this.voicePauseSrc;
+
+    _this.audio.addEventListener("play", function () {
+      _this.voiceSrc = _this.voicePauseSrc;
+    });
+    _this.audio.addEventListener("pause", function () {
+      _this.voiceSrc = _this.voicePauseSrc;
+    });
+    _this.audio.addEventListener("ended", function () {
+      _this.voiceSrc = _this.voicePauseSrc;
+    });
+  },
+  // 方法集合
+  methods: {
+    handlePlayVoice() {
+      let _this = this;
+      console.log(this.audio.paused);
+      if (!_this.audio.paused) {
+        _this.audio.pause();
+      } else if (_this.isCompare) {
+        _this.audio.pause();
+        _this.audio.play();
+      } else {
+        _this.audio.pause();
+        _this.audio.load();
+        _this.audio.src = _this.mp3;
+        _this.audio.loop = false;
+        _this.audio.play();
+      }
+    },
+    stopAudio() {
+      if (this.audio) {
+        this.audio.pause();
+      }
+    },
+  },
+};
+</script>
+<style lang='scss' scoped>
+//@import url(); 引入公共css类
+.content-voices {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  font-size: 0;
+  cursor: pointer;
+  span {
+    color: #2c2c2c;
+    font-size: 24px;
+    line-height: 30px;
+    float: left;
+    font-family: sourceR;
+    &.noMp3 {
+      margin-left: 0px;
+    }
+  }
+  img {
+    width: 16px;
+    height: 16px;
+    float: left;
+  }
+  .icon-big {
+    width: 24px;
+    height: 24px;
+  }
+}
+</style>

+ 66 - 0
src/components/common/Enword.vue

@@ -0,0 +1,66 @@
+<!--  -->
+<template>
+  <div class="enword">
+    <div class="enword-top">
+      <span class="word">{{ item.new_word.new_word }}</span>
+      <div class="cancle-coll" @click="cancleColl">
+        <span class="coll-icon"></span>
+        <span class="coll-text">取消收藏</span>
+      </div>
+    </div>
+    <div class="enword-main">
+      <span class="pron">{{ item.new_word.pinyin }}</span>
+      <Audio :mp3="item.new_word.audio_file_url" :themeColor="themeColor" />
+    </div>
+    <div class="enword-inpt">
+      <span class="cixing">n.</span>
+      <span class="intp">时间表,进度表</span>
+    </div>
+    <div class="resoure">
+      来自:新航标英语 / 第一单元 / 第一课 家庭、生活/课文一
+    </div>
+    <div class="create_time">
+      {{ create_time }}
+    </div>
+  </div>
+</template>
+
+<script>
+import Audio from "./AudioRed.vue";
+export default {
+  components: { Audio },
+  props: ["item", "themeColor"],
+  data() {
+    return {};
+  },
+  computed: {},
+  watch: {},
+  //方法集合
+  methods: {
+    cancleColl() {
+      let data = {
+        id_list: [this.item.id],
+      };
+      let MethodName = "order-collection_manager-DeleteMyCollection";
+      getLearnWebContent(MethodName, data).then((res) => {
+        this.$message.success("取消成功!");
+        this.$emit("getMyCollectionList");
+      });
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {},
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang='scss' scoped>
+//@import url(); 引入公共css类
+</style>

+ 270 - 0
src/components/common/Hanzi.vue

@@ -0,0 +1,270 @@
+<!--  -->
+<template>
+  <div class="hanzi" v-if="item">
+    <div class="left-part">
+      <div class="strockplay-list">
+        <div
+          :class="[
+            'strockplay',
+            conindex < item.new_word.new_word.length - 1 ? 'borderRight' : '',
+          ]"
+          v-for="(conItem, conindex) in item.new_word.new_word"
+          :key="'new_word_' + index + conindex"
+        >
+          <Strockplayredline
+            :key="conItem + index + conindex"
+            :Book_text="conItem"
+            :playStorkes="true"
+            :targetDiv="'bwcHanziIntp' + conItem + index + conindex"
+            :wordNum="item.new_word.new_word.length"
+            :themeColor="themeColor"
+          />
+          <div
+            :class="[
+              'bwc-line',
+              themeColor == 'green'
+                ? 'green-bg'
+                : themeColor == 'red'
+                ? 'red-bg'
+                : 'brown-bg',
+            ]"
+            v-if="conindex < item.new_word.new_word.length - 1"
+          ></div>
+        </div>
+      </div>
+    </div>
+    <div class="main">
+      <div class="first-part">
+        <div class="left">
+          <span class="pinyin" v-if="item.new_word.pinyin">{{
+            item.new_word.pinyin
+          }}</span>
+          <template>
+            <Audio
+              :mp3="item.new_word.audio_file_url"
+              :themeColor="themeColor"
+            />
+          </template>
+        </div>
+      </div>
+      <div
+        class="def-chs"
+        v-for="(defItem, defIndex) in item.new_word.definition_list"
+        :key="'defIndex' + defIndex"
+      >
+        {{ defItem }}
+      </div>
+      <div class="resource" v-if="item.resource">{{ item.resource }}</div>
+      <div class="create_time" v-if="item.create_time">
+        {{ item.create_time }}
+      </div>
+    </div>
+    <div class="right">
+      <div class="cancle-coll" @click="cancleColl">
+        <span class="coll-icon"></span>
+        <span class="coll-text">取消收藏</span>
+      </div>
+      <span class="look-more" @click="lookMore">查看更多</span>
+    </div>
+    <div class="practiceBox" v-if="isIntpShow">
+      <WordPhraseDetail
+        :currentTreeID="currentTreeID"
+        :closeWord="changeIntpShow"
+        :data="item"
+        :themeColor="themeColor"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import Strockplayredline from "./Strockplayredline.vue";
+import Audio from "./AudioRed.vue";
+import { getLearnWebContent } from "@/api/ajax";
+import WordPhraseDetail from "./WordPhraseDetail.vue";
+export default {
+  name: "Hanzi",
+  components: { Strockplayredline, Audio, WordPhraseDetail },
+  props: ["item", "index"],
+  data() {
+    return {
+      themeColor: "red",
+      isIntpShow: false,
+    };
+  },
+  computed: {},
+  watch: {},
+  //方法集合
+  methods: {
+    cancleColl() {
+      let data = {
+        id_list: [this.item.id],
+      };
+      let MethodName = "order-collection_manager-DeleteMyCollection";
+      getLearnWebContent(MethodName, data).then((res) => {
+        this.$message.success("取消成功!");
+        this.$emit("getMyCollectionList");
+      });
+    },
+    viewIntp() {
+      this.isIntpShow = true;
+    },
+    changeIntpShow() {
+      this.isIntpShow = false;
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {},
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang='scss' scoped>
+//@import url(); 引入公共css类
+.hanzi {
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  .practiceBox {
+    position: fixed;
+    left: 0;
+    top: 0px;
+    z-index: 999;
+    width: 100%;
+    height: 100vh;
+    background: rgba(0, 0, 0, 0.19);
+    box-sizing: border-box;
+    overflow: hidden;
+    overflow-y: auto;
+    &.practiceBoxStrock {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      padding-top: 0px;
+    }
+  }
+  .left-part {
+    min-height: 92px;
+    max-height: 152px;
+    background: #f0f0f0;
+    border-radius: 8px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-right: 16px;
+    padding: 0 8px;
+  }
+  .strockplay-list {
+    display: flex;
+    justify-content: flex-start;
+    align-items: flex-start;
+    border: 2px solid #de4444;
+    box-sizing: border-box;
+    border-radius: 8px;
+    overflow: hidden;
+  }
+  .strockplay {
+    &.borderRight {
+      border-right: 2px #de4444 solid;
+    }
+  }
+  .main {
+    flex: 1;
+    .first-part {
+      clear: both;
+      overflow: hidden;
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      margin-bottom: 8px;
+      .left {
+        float: left;
+        display: flex;
+        justify-content: flex-start;
+        align-items: center;
+        .pinyin {
+          font-size: 24px;
+          line-height: 32px;
+          color: rgba(0, 0, 0, 0.85);
+          margin-right: 4px;
+        }
+      }
+    }
+    .def-chs {
+      font-size: 14px;
+      line-height: 22px;
+      color: rgba(0, 0, 0, 0.85);
+      margin-bottom: 8px;
+      opacity: 0.5;
+    }
+    .def-en {
+      font-size: 14px;
+      line-height: 22px;
+      color: rgba(0, 0, 0, 0.85);
+      margin-bottom: 8px;
+      opacity: 0.5;
+    }
+    .resource {
+      font-size: 14px;
+      line-height: 22px;
+      color: #2c2c2c;
+      opacity: 0.5;
+      margin-bottom: 8px;
+    }
+    .create_time {
+      font-size: 14px;
+      line-height: 22px;
+      color: #2c2c2c;
+      opacity: 0.5;
+    }
+  }
+  .right {
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    align-items: center;
+    height: 100%;
+    .cancle-coll {
+      float: right;
+      padding: 0 12px;
+      height: 32px;
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      cursor: pointer;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      box-sizing: border-box;
+      border-radius: 4px;
+      margin-bottom: 38px;
+      > .coll-icon {
+        width: 16px;
+        height: 16px;
+        background: url("../../assets/starfill-16-normal-red.png") no-repeat
+          left top;
+        background-size: 100% 100%;
+        margin-right: 4px;
+      }
+      > .coll-text {
+        font-size: 14px;
+        line-height: 22px;
+        color: #000000;
+      }
+    }
+    .look-more {
+      font-size: 14px;
+      line-height: 22px;
+      color: rgba(0, 0, 0, 0.85);
+      opacity: 0.5;
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 561 - 0
src/components/common/Intp.vue

@@ -0,0 +1,561 @@
+<!--  -->
+<template>
+  <div class="wordIntp" v-if="flag">
+    <p class="from">数据来自百度汉语</p>
+    <el-menu
+      :default-active="activeIndex"
+      class="el-menu-demo"
+      mode="horizontal"
+      @select="handleSelect"
+    >
+      <el-menu-item index="1">词典释义</el-menu-item>
+      <el-menu-item index="2">近/反义词</el-menu-item>
+      <el-menu-item index="3">组词</el-menu-item>
+    </el-menu>
+    <template v-if="activeIndex == '1'">
+      <div class="bwc-intp">
+        <!-- 基本释义 -->
+        <h1>基本释义</h1>
+        <span v-if="word.pinyin" class="pinyin">{{ word.pinyin }}</span>
+        <template v-if="isHasValue2=='definitionWithSpell'">
+            <div
+                v-for="(itemss, indexss) in paraphrase"
+                :key="indexss"
+                class="paraphrase"
+                >
+                <div
+                    v-for="(vItems, key, vIndexs) in itemss['@definitionWithSpell']"
+                    :key="vIndexs"
+                    class="para"
+                    >
+                    <span class="para-key">{{
+                        key.replace("[", "").replace("]", "")
+                    }}</span>
+                    <ul
+                        v-for="(pItems, pIndexs) in vItems"
+                        :key="pIndexs"
+                        class="para-value"
+                    >
+                        <li>{{ pItems }}</li>
+                    </ul>
+                </div>
+            </div>
+        </template>
+        <template v-else-if="isHasValue2=='definitions'">
+            <div
+                v-for="(itemss, indexss) in paraphrase"
+                :key="indexss"
+                class="paraphrase"
+                >
+                <ul
+                    v-for="(vItems, key, vIndexs) in itemss['@definitions']"
+                    :key="vIndexs"
+                    class="para"
+                    >
+                    <li>{{ vItems }}</li>
+                </ul>
+            </div>
+        </template>
+        <template v-else>
+            <ul
+                v-for="(itemss, indexss) in paraphrase"
+                :key="indexss"
+                class="paraphrase"
+                >
+                <li>{{ itemss['@value'] }}</li>
+            </ul>
+        </template>
+        <hr />
+      </div>
+    </template>
+    <template v-if="activeIndex == '2'">
+      <div class="bwc-intp">
+        <h1 v-if="synonymList.length > 0">近义词</h1>
+        <ul class="synonym">
+          <li
+            v-for="(itemss, indexss) in synonymList"
+            :key="indexss"
+            class="paraphrase"
+          >
+            {{ itemss["@value"] }}
+          </li>
+        </ul>
+        <h1 v-if="antonymList.length > 0">反义词</h1>
+        <ul class="synonym">
+          <li
+            v-for="(itemss, indexss) in antonymList"
+            :key="indexss"
+            class="paraphrase"
+          >
+            {{ itemss["@value"] }}
+          </li>
+        </ul>
+      </div>
+    </template>
+    <template v-if="activeIndex == '3'">
+      <div class="bwc-intp">
+        <ul class="synonym">
+          <li
+            v-for="(itemss, indexss) in termsList"
+            :key="indexss"
+            class="paraphrase"
+          >
+            {{ itemss["@value"] }}
+          </li>
+        </ul>
+      </div>
+    </template>
+  </div>
+</template>
+
+<script>
+import Audio from "./AudioRed.vue";
+import Strockplayredline from "./Strockplayredline.vue";
+import { getHZChineseInfo } from "@/api/ajax";
+
+export default {
+  name: "WordIntp",
+  components: {
+    Strockplayredline,
+    Audio,
+  },
+  props: ["word", "changeIntpShow", "themeColor", "show", "type"],
+  data() {
+    return {
+      isPraShow: false,
+      curData: null,
+      activeIndex: "1",
+      mp3Url: "", // 音频
+      paraphrase: [], // 释义
+      synonymList: [], // 近义词
+      antonymList: [], // 反义词
+      termsList: [], // 组词
+      dataDetail: [],
+      isHasValue2: '', // 释义里是否含有value2
+    };
+  },
+  computed: {
+    flag() {
+      let _this = this;
+      let flag = false;
+      if (_this.word) {
+        if (_this.paraphrase && _this.paraphrase.length > 0) {
+          flag = true;
+        } else if (_this.synonymList && _this.synonymList.length > 0) {
+          flag = true;
+        } else if (_this.antonymList && _this.antonymList.length > 0) {
+          flag = true;
+        } else if (_this.termsList && _this.termsList.length > 0) {
+          flag = true;
+        }
+      }
+      return flag;
+    },
+  },
+  watch: {
+    word: {
+      handler: function (val, oldVal) {
+        let _this = this;
+        if (_this.type == "newWordDetail") {
+          _this.getChineseInfo();
+        }
+      },
+      // 深度观察监听
+      deep: true,
+    },
+  },
+  //方法集合
+  methods: {
+    writeWord() {
+      this.isPraShow = true;
+    },
+    changePraShow() {
+      this.isPraShow = false;
+    },
+    handleSelect(val) {
+      this.activeIndex = val;
+    },
+    getChineseInfo() {
+      let _this = this;
+
+      let data = {
+        query: this.word.new_word,
+      };
+      getHZChineseInfo(data).then((res) => {
+        _this.dataDetail = res.data.result;
+        _this.handleChineseDetail();
+      });
+    },
+    // 处理数据
+    handleChineseDetail() {
+      let _this = this;
+      _this.paraphrase = [];
+      _this.synonymList = [];
+      _this.antonymList = [];
+      _this.termsList = [];
+      _this.isHasValue2 = ''
+      _this.dataDetail.forEach((item) => {
+        if (item.request.queryType == "entity") {
+          // 读音
+          item.response.entity.forEach((items) => {
+            items.attrs.forEach((itemss) => {
+              if (itemss.key == "pronunciation") {
+                // 音频
+
+                _this.word.mp3Url = itemss.objects[0]["@value"]
+                  ? itemss.objects[0]["@value"]
+                  : "";
+              } else if (itemss.key == "definition") {
+                // 释义
+                _this.paraphrase = itemss.objects;
+                for(let i = 0; i< _this.paraphrase.length; i++){
+                    if(_this.paraphrase[i]['@definitionWithSpell']){
+                        _this.isHasValue2 = 'definitionWithSpell'
+                        return false
+                    }else if(_this.paraphrase[i]['@definitions']){
+                        _this.isHasValue2 = 'definitions'
+                    }
+                }
+              } else if (itemss.key == "synonym") {
+                // 近义词
+                _this.synonymList = itemss.objects;
+              } else if (itemss.key == "antonym") {
+                // 反义词
+                _this.antonymList = itemss.objects;
+              } else if (itemss.key == "terms") {
+                // 组词
+                _this.termsList = itemss.objects;
+              }
+            });
+          });
+        }
+      });
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    let _this = this;
+    if (_this.type == "newWordDetail") {
+      _this.getChineseInfo();
+    } else {
+      if (_this.word) {
+        _this.isHasValue2 = ''
+        _this.mp3Url = _this.word.mp3Url ? _this.word.mp3Url : "";
+        _this.paraphrase = _this.word.paraphrase
+          ? JSON.parse(JSON.stringify(_this.word.paraphrase))
+          : [];
+        for(let i = 0; i< _this.paraphrase.length; i++){
+            if(_this.paraphrase[i]['@definitionWithSpell']){
+                _this.isHasValue2 = 'definitionWithSpell'
+                return false
+            }else if(_this.paraphrase[i]['@definitions']){
+                _this.isHasValue2 = 'definitions'
+            }
+        }
+        _this.synonymList = _this.word.synonymList
+          ? JSON.parse(JSON.stringify(_this.word.synonymList))
+          : [];
+        _this.antonymList = _this.word.antonymList
+          ? JSON.parse(JSON.stringify(_this.word.antonymList))
+          : [];
+        _this.termsList = _this.word.termsList
+          ? JSON.parse(JSON.stringify(_this.word.termsList))
+          : [];
+      }
+    }
+  },
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang='scss' scoped>
+//@import url(); 引入公共css类
+.wordIntp {
+  width: 100%;
+  height: 600px;
+  overflow: hidden;
+  overflow-y: auto;
+  margin: 0 auto;
+  position: relative;
+  margin-top: 12px !important;
+  .from {
+    color: rgba(0, 0, 0, 0.85);
+    opacity: 0.2;
+    margin: 0;
+    font-size: 12px;
+    line-height: 20px;
+  }
+  .bwc-intp {
+    padding: 16px 0;
+    h1 {
+      color: #000000;
+      font-size: 20px;
+      line-height: 150%;
+      font-weight: normal;
+      margin: 8px 0 8px 0;
+    }
+    .pinyin {
+      color: #de4444;
+      font-size: 24px;
+      line-height: 36px;
+      margin-bottom: 8px;
+      font-family: "GB-PINYINOK-B";
+      display: block;
+    }
+    .paraphrase {
+      margin-bottom: 8px;
+      margin-top: 0;
+      > .para {
+        font-weight: normal;
+        font-size: 14px;
+        line-height: 22px;
+        color: #000000;
+        margin-bottom: 8px;
+        .para-key {
+          font-family: "GB-PINYINOK-B";
+        }
+      }
+    }
+    hr {
+      margin: 16px 0 0 0;
+      background: rgba($color: #000000, $alpha: 0.15);
+      height: 1px;
+      border: none;
+    }
+    ul.synonym {
+      display: flex;
+      flex-flow: wrap;
+      margin-left: -4px;
+      padding: 4px 0;
+      li {
+        padding: 4px 8px;
+        background: #ffffff;
+        border: 1px solid rgba(0, 0, 0, 0.15);
+        border-radius: 4px;
+        margin: 4px;
+        font-size: 16px;
+        line-height: 150%;
+        color: #000000;
+        text-align: center;
+        min-width: 127px;
+      }
+    }
+  }
+  .closeBox {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    position: absolute;
+    left: 0;
+    top: 0;
+    padding: 12px;
+    > i {
+      font-size: 16px;
+      color: #000000;
+      cursor: pointer;
+    }
+    span {
+      color: #000000;
+      opacity: 0.2;
+      font-size: 14px;
+      line-height: 130%;
+    }
+  }
+  min-width: 312px;
+  min-height: 360px;
+  // background: #ffffff;
+  // box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.15);
+  border-radius: 8px;
+  // padding: 52px 32px 32px;
+  box-sizing: border-box;
+  .bwc-top {
+    margin-bottom: 16px;
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    .content-voices {
+      width: 24px;
+    }
+    > span {
+      font-family: "GB-PINYINOK-B";
+      color: #2c2c2c;
+      font-size: 20px;
+      line-height: 24px;
+      margin-right: 4px;
+    }
+  }
+  .bwc-Strockplay {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    min-width: 130px;
+    height: 130px;
+    margin: 0 auto;
+    margin-bottom: 6px;
+    border: 2px solid #de4444;
+    border-radius: 8px;
+    box-sizing: border-box;
+    overflow: hidden;
+    .strockplay {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      position: relative;
+      .collect-icon {
+        width: 16px;
+        position: absolute;
+        right: 4px;
+        bottom: 4px;
+        cursor: pointer;
+      }
+    }
+    .bwc-line {
+      width: 2px;
+      height: 126px;
+      background: #de4444;
+    }
+  }
+  .bwc-tolength {
+    color: #404040;
+    font-size: 30px;
+    line-height: 1.5;
+    font-family: FZJCGFKTK;
+    text-align: center;
+    border: 2px solid #de4444;
+    border-radius: 8px;
+    padding: 40px 0;
+    margin: 0 0 16px 0;
+  }
+  .bwc-word-en {
+    font-style: normal;
+    font-weight: 600;
+    font-size: 20px;
+    line-height: 150%;
+    text-align: center;
+    color: #2c2c2c;
+    margin-bottom: 8px;
+  }
+  .bwc-more-intp {
+    font-weight: normal;
+    font-size: 14px;
+    line-height: 20px;
+    color: #2c2c2c;
+    opacity: 0.5;
+    text-align: center;
+    margin-bottom: 24px;
+    cursor: pointer;
+  }
+  .bwc-footer {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    > button {
+      width: 128px;
+      height: 40px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: #ff5757;
+      background: rgba(255, 87, 87, 0.1);
+      border-radius: 4px;
+      outline: 0;
+      border: 0;
+      cursor: pointer;
+      > img {
+        width: 24px;
+        height: 24px;
+        margin-right: 8px;
+      }
+    }
+  }
+}
+.NPC-Big-Book-preview-green {
+  .wordIntp {
+    .bwc-intp {
+      .pinyin {
+        color: #24b99e;
+      }
+    }
+    .bwc-Strockplay {
+      border: 2px solid #24b99e;
+      .bwc-line {
+        background: #24b99e;
+      }
+    }
+    .bwc-tolength {
+      border: 2px solid #24b99e;
+    }
+    .bwc-footer {
+      > button {
+        color: #24b99e;
+      }
+    }
+  }
+}
+.NPC-Big-Book-preview-brown {
+  .wordIntp {
+    .bwc-intp {
+      .pinyin {
+        color: #bd8865;
+      }
+    }
+    .bwc-Strockplay {
+      border: 2px solid #bd8865;
+      .bwc-line {
+        background: #bd8865;
+      }
+    }
+    .bwc-tolength {
+      border: 2px solid #bd8865;
+    }
+    .bwc-footer {
+      > button {
+        color: #bd8865;
+      }
+    }
+  }
+}
+</style>
+<style lang="scss">
+.wordIntp {
+  .el-menu--horizontal > .el-menu-item {
+    width: 33.33%;
+    text-align: center;
+    color: #000000;
+    font-size: 18px;
+  }
+  .el-menu.el-menu--horizontal {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+  }
+  .el-menu--horizontal > .el-menu-item.is-active {
+    color: #de4444;
+    font-weight: bold;
+    border-bottom: 2px solid #de4444;
+  }
+}
+.NPC-Big-Book-preview-green {
+  .wordIntp {
+    .el-menu--horizontal > .el-menu-item.is-active {
+      color: #24b99e;
+      border-bottom: 2px solid #24b99e;
+    }
+  }
+}
+.NPC-Big-Book-preview-brown {
+  .wordIntp {
+    .el-menu--horizontal > .el-menu-item.is-active {
+      color: #bd8865;
+      border-bottom: 2px solid #bd8865;
+    }
+  }
+}
+</style>

+ 478 - 0
src/components/common/Sentence.vue

@@ -0,0 +1,478 @@
+<!--  -->
+<template>
+  <div class="sentence">
+    <div class="sent-top">
+      <div class="sent-top-left">
+        <span class="num">{{ index + 1 }}.</span>
+        <template v-if="mp3">
+          <AudioLineSentence
+            :key="'sent' + index"
+            :mp3="mp3"
+            :getCurTime="getCurTime"
+            ref="audioLineSent"
+            :audioId="'artPraAudioId' + index"
+            :stopAudio="stopAudio"
+            :width="120"
+            :hideSlider="true"
+            :bg="bg"
+            :ed="ed"
+          />
+        </template>
+      </div>
+      <div class="cancle-coll" @click="cancleColl">
+        <span class="coll-icon"></span>
+        <span class="coll-text">取消收藏</span>
+      </div>
+    </div>
+    <div class="vc-main" v-if="item">
+      <div class="NNPE-words-box">
+        <div
+          class="NNPE-words"
+          v-for="(pItem, pIndex) in item"
+          :key="'wordsList' + pIndex"
+          :class="[pItem.wordIndex == 0 ? 'textLeft' : 'textCenter']"
+        >
+          <template v-if="!pItem.width">
+            <template v-if="pItem.isShow">
+              <template
+                v-if="
+                  item[pIndex + 1] &&
+                  item[pIndex + 1].chs &&
+                  chsFhList.indexOf(item[pIndex + 1].chs) > -1
+                "
+              >
+                <span class="NNPE-words-box">
+                  <template v-if="pyPosition == 'top'">
+                    <span
+                      class="NNPE-pinyin"
+                      :class="[
+                        pItem.className ? pItem.className : '',
+                        noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
+                      ]"
+                      >{{ pItem.pinyin }}</span
+                    >
+                  </template>
+                  <span
+                    class="NNPE-chs"
+                    :class="[pItem.padding ? 'padding' : '']"
+                  >
+                    <template>
+                      <span
+                        v-for="(wItem, wIndex) in pItem.leg"
+                        :key="'ci' + wIndex + pIndex"
+                        :class="[
+                          pItem.timeList &&
+                          pItem.timeList[wIndex] &&
+                          curTime >= pItem.timeList[wIndex].wordBg &&
+                          curTime <= ed
+                            ? 'active'
+                            : '',
+                        ]"
+                        >{{ pItem.chs[wIndex] }}</span
+                      >
+                    </template>
+                  </span>
+                  <template v-if="pyPosition == 'bottom'">
+                    <span
+                      class="NNPE-pinyin"
+                      :class="[
+                        pItem.className ? pItem.className : '',
+                        noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
+                      ]"
+                      :style="'font-size:' + pySize + 'px'"
+                      >{{ pItem.pinyin }}</span
+                    >
+                  </template>
+                </span>
+                <span class="NNPE-words-box">
+                  <template v-if="pyPosition == 'top'">
+                    <span
+                      :class="[
+                        'NNPE-pinyin',
+                        noFont.indexOf(item[pIndex + 1].pinyin) > -1
+                          ? 'noFont'
+                          : '',
+                      ]"
+                      >{{ item[pIndex + 1].pinyin }}</span
+                    >
+                  </template>
+                  <span class="NNPE-chs">
+                    <span
+                      :class="[
+                        pItem.timeList[pItem.leg - 1] &&
+                        curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
+                        curTime <= ed
+                          ? 'active'
+                          : '',
+                      ]"
+                      >{{ item[pIndex + 1].chs }}</span
+                    >
+                  </span>
+                  <template v-if="pyPosition == 'bottom'">
+                    <span
+                      :class="[
+                        'NNPE-pinyin',
+                        noFont.indexOf(item[pIndex + 1].pinyin) > -1
+                          ? 'noFont'
+                          : '',
+                      ]"
+                      >{{ item[pIndex + 1].pinyin }}</span
+                    >
+                  </template>
+                </span>
+              </template>
+              <template v-else>
+                <template v-if="pyPosition == 'top'">
+                  <template v-if="NumberList.indexOf(pItem.pinyin) < 0">
+                    <span
+                      class="NNPE-pinyin"
+                      :class="[
+                        pItem.padding ? 'padding' : '',
+                        pItem.className ? pItem.className : '',
+                        noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
+                      ]"
+                      >{{ pItem.pinyin }}</span
+                    >
+                  </template>
+                </template>
+                <span
+                  v-if="pItem.chs != '#'"
+                  class="NNPE-chs"
+                  :class="[pItem.padding ? 'padding' : '']"
+                >
+                  <template>
+                    <span
+                      v-for="(wItem, wIndex) in pItem.leg"
+                      :key="'ci' + wIndex + pIndex + index"
+                      :class="[
+                        pItem.timeList &&
+                        pItem.timeList[wIndex] &&
+                        curTime >= pItem.timeList[wIndex].wordBg &&
+                        curTime <= ed
+                          ? 'active'
+                          : '',
+                      ]"
+                      >{{ pItem.chs[wIndex] }}</span
+                    >
+                  </template>
+                </span>
+                <template v-if="pyPosition == 'bottom'">
+                  <template v-if="NumberList.indexOf(pItem.pinyin) < 0">
+                    <span
+                      class="NNPE-pinyin"
+                      :class="[
+                        pItem.padding ? 'padding' : '',
+                        pItem.className ? pItem.className : '',
+                      ]"
+                      >{{ pItem.pinyin }}</span
+                    >
+                  </template>
+                </template>
+              </template>
+            </template>
+          </template>
+          <template v-else>
+            <span
+              :style="{
+                height: pItem.height + 'px',
+                width: pItem.width + 'px',
+              }"
+            ></span>
+          </template>
+        </div>
+      </div>
+      <div style="clear: both; overflow: hidden"></div>
+      <div v-if="item.enwords" class="enwords">
+        {{ item.enwords }}
+      </div>
+    </div>
+    <div class="sent-bottom">
+      <span class="create_time">{{ sItem.create_time }}</span>
+      <span class="resource" v-if="sItem.resource"
+        >来自:{{ sItem.resource }}</span
+      >
+    </div>
+  </div>
+</template>
+
+<script>
+import AudioLineSentence from "./AudioLineSentence.vue";
+import { getLearnWebContent } from "@/api/ajax";
+export default {
+  name: "sentence",
+  components: {
+    AudioLineSentence,
+  },
+  props: ["sItem", "index"],
+  data() {
+    return {
+      item: null,
+      bg: 0,
+      ed: 0,
+      mp3: "",
+      stopAudio: false,
+      chsFhList: [",", "。", "“", ":", "》", "《", "?", "!", ";"],
+      enFhList: [",", ".", ";", "?", "!", ":", ">", "<"],
+      noFont: ["~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "/"],
+      NumberList: [
+        "①",
+        "②",
+        "③",
+        "④",
+        "⑤",
+        "⑥",
+        "⑦",
+        "⑧",
+        "⑨",
+        "⑩",
+        "⑪",
+        "⑫",
+        "⑬",
+        "⑭",
+        "⑮",
+        "⑯",
+        "⑰",
+        "⑱",
+        "⑲",
+        "⑳",
+      ],
+      pyPosition: "top",
+      curTime: 0,
+    };
+  },
+  computed: {},
+  watch: {},
+  //方法集合
+  methods: {
+    handleData() {
+      let sentence_json = JSON.parse(this.sItem.sentence.sentence_json);
+      if (sentence_json) {
+        this.item = JSON.parse(sentence_json.item);
+        this.bg = sentence_json.bg;
+        this.ed = sentence_json.ed;
+        this.pyPosition = sentence_json.pyPosition;
+        this.mp3 = sentence_json.mp3;
+      }
+    },
+    getCurTime(curTime) {
+      let _this = this;
+      _this.curTime = curTime * 1000;
+    },
+    pauseAudio() {
+      let audio = document.getElementsByTagName("audio");
+      audio.forEach((item) => {
+        item.pause();
+      });
+    },
+    cancleColl() {
+      let data = {
+        id_list: [this.sItem.id],
+      };
+      let MethodName = "order-collection_manager-DeleteMyCollection";
+      getLearnWebContent(MethodName, data).then((res) => {
+        this.$message.success("取消成功!");
+        this.$emit("getMyCollectionList");
+      });
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    this.handleData();
+  },
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang='scss' scoped>
+//@import url(); 引入公共css类
+.sentence {
+  width: 100%;
+  .sent-top {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    margin-bottom: 8px;
+    &-left {
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      .num {
+        font-size: 14px;
+        line-height: 22px;
+        color: #000000;
+        min-width: 22px;
+        margin-right: 8px;
+      }
+    }
+    .cancle-coll {
+      float: right;
+      padding: 0 12px;
+      height: 32px;
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      cursor: pointer;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      box-sizing: border-box;
+      border-radius: 4px;
+
+      > .coll-icon {
+        width: 16px;
+        height: 16px;
+        background: url("../../assets/starfill-16-normal-red.png") no-repeat
+          left top;
+        background-size: 100% 100%;
+        margin-right: 4px;
+      }
+      > .coll-text {
+        font-size: 14px;
+        line-height: 22px;
+        color: #000000;
+      }
+    }
+  }
+  .sent-bottom {
+    > span {
+      font-size: 14px;
+      line-height: 22px;
+      color: #2c2c2c;
+      opacity: 0.5;
+      margin-right: 8px;
+    }
+  }
+}
+.vc-main {
+  width: 100%;
+  margin-bottom: 8px;
+  .enwords {
+    font-size: 12px;
+    line-height: 20px;
+    color: rgba(0, 0, 0, 0.85);
+    font-family: "robot";
+  }
+}
+.NNPE-words {
+  float: left;
+  user-select: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  &-box {
+    float: left;
+    > span {
+      display: block;
+      &.NNPE-pinyin {
+        font-family: "GB-PINYINOK-B";
+        font-weight: normal;
+        font-size: 14px;
+        line-height: 22px;
+        box-sizing: border-box;
+        color: rgba(0, 0, 0, 0.85);
+
+        &.noFont {
+          font-family: initial;
+        }
+        &.textLeft {
+          text-align: left;
+        }
+        &.font-white {
+          color: #fff;
+        }
+        &.wordBlank {
+          color: rgba(0, 0, 0, 0.85);
+        }
+      }
+      &.NNPE-chs {
+        font-family: "FZJCGFKTK";
+        font-size: 20px;
+        line-height: 24px;
+        color: rgba(0, 0, 0, 0.85);
+
+        .font-white {
+          color: #fff;
+        }
+        .active {
+          color: #de4444;
+          &-yellow {
+            color: #ffc600;
+          }
+        }
+
+        .wordActive {
+          color: #de4444;
+        }
+        .wordActive-blue {
+          color: #ffc600;
+        }
+      }
+      // &.padding {
+      //   padding-right: 6px;
+      // }
+    }
+  }
+  &.textLeft {
+    text-align: left;
+  }
+  &.textCenter {
+    text-align: center;
+  }
+  > span {
+    display: block;
+    &.NNPE-pinyin {
+      font-family: "GB-PINYINOK-B";
+      font-weight: normal;
+      font-size: 14px;
+      line-height: 22px;
+      box-sizing: border-box;
+      color: rgba(0, 0, 0, 0.85);
+
+      &.font-white {
+        color: #fff;
+      }
+      &.noFont {
+        font-family: initial;
+      }
+      &.textLeft {
+        text-align: left;
+      }
+      &.wordBlank {
+        color: rgba(0, 0, 0, 0.85);
+      }
+    }
+    &.NNPE-chs {
+      font-family: "FZJCGFKTK";
+      font-size: 20px;
+      line-height: 24px;
+      color: rgba(0, 0, 0, 0.85);
+
+      .font-white {
+        color: #fff;
+      }
+      .active {
+        color: #de4444;
+        &-yellow {
+          color: #ffc600;
+        }
+      }
+      .wordActive {
+        color: #de4444;
+      }
+      .wordActive-blue {
+        color: #ffc600;
+      }
+    }
+    &.padding {
+      padding-left: 3px;
+      padding-right: 3px;
+    }
+  }
+}
+</style>

+ 165 - 0
src/components/common/Strockplayredline.vue

@@ -0,0 +1,165 @@
+<!--  -->
+<template>
+  <div class="strockplayRedInner">
+    <div
+      @click="playHanzi"
+      :class="[
+        'strock-play-box',
+        themeColor == 'green'
+          ? 'green-border'
+          : themeColor == 'red'
+          ? 'red-border'
+          : themeColor == 'brown'
+          ? 'brown-border'
+          : 'red-border',
+      ]"
+      v-if="playStorkes"
+    ></div>
+
+    <div :id="targetDiv" class="character-target-div"></div>
+  </div>
+</template>
+
+<script>
+const HanziWriter = require("hanzi-writer");
+export default {
+  components: {},
+  props: [
+    "targetDiv",
+    "Book_text",
+    "playStorkes",
+    "strokeColor",
+    "wordNum",
+    "themeColor",
+  ],
+  data() {
+    return {
+      writer: null,
+    };
+  },
+  computed: {},
+  watch: {
+    targetDiv: {
+      handler: function (val, oldVal) {
+        if (val != oldVal) {
+          let _this = this;
+          _this.$nextTick(() => {
+            _this.initHanziwrite();
+          });
+        }
+      },
+      // 深度观察监听
+      deep: true,
+    },
+  },
+  //方法集合
+  methods: {
+    initHanziwrite() {
+      let _this = this;
+      _this.writer = null;
+      //var ren = require("hanzi-writer-data/国");
+      _this.writer = HanziWriter.default.create(
+        _this.targetDiv,
+        _this.Book_text,
+        {
+          padding: 5,
+          showOutline: true,
+          strokeColor: _this.strokeColor ? _this.strokeColor : "#000",
+        }
+      );
+    },
+    playHanzi(event) {
+      let _this = this;
+      _this.writer.animateCharacter();
+      event.stopPropagation();
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    let _this = this;
+    _this.$nextTick(() => {
+      _this.initHanziwrite();
+    });
+  },
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang='scss' scoped>
+//@import url(); 引入公共css类
+.strockplayRedInner {
+  position: relative;
+
+  width: 60px; //444px
+  height: 60px; //480px
+}
+
+.character-target-div {
+  width: 100%;
+  height: 100%;
+  background: #fff url("../../assets/chinaTianRed.png") center no-repeat;
+  background-size: 100% 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 99999;
+  font-family: "FZJCGFKTK";
+}
+.strock-play-box {
+  position: absolute;
+  right: -2px;
+  top: 0px;
+  z-index: 999;
+  width: 16px !important;
+  height: 16px !important;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+}
+.red-border {
+  background: url("../../assets/strock-play-red-click.png");
+  background-size: 100%;
+}
+.green-border {
+  background: url("../../assets/strock-play-green-click.png");
+  background-size: 100%;
+}
+.brown-border {
+  background: url("../../assets/strock-play-yellow-click.png");
+  background-size: 100%;
+}
+
+.animate-butto {
+  width: 240px;
+  height: 160px;
+  font-size: 28px;
+}
+.NPC-Big-Book-preview-green {
+  .character-target-div {
+    background: #fff url("../../assets/chinaTianGreen.png") center no-repeat;
+    background-size: cover;
+  }
+  .strock-play-box {
+    background: url("../../assets/strock-play-green-click.png");
+    background-size: 100%;
+  }
+}
+.NPC-Big-Book-preview-brown {
+  .character-target-div {
+    background: #fff url("../../assets/chinaTianYellow.png") center no-repeat;
+    background-size: cover;
+  }
+  .strock-play-box {
+    background: url("../../assets/strock-play-yellow-click.png");
+    background-size: 100%;
+  }
+}
+</style>

+ 717 - 0
src/components/common/WordPhraseDetail.vue

@@ -0,0 +1,717 @@
+<template>
+  <div class="wordDetailModule wordDetailChs">
+    <div class="module-inner">
+      <div class="top" v-if="data">
+        <div class="operation">
+          <div>
+            <template v-if="optionRes && optionRes.length > 0">
+              <!-- <img
+                src="../../../../assets/icon/starline-16-normal-Black.png"
+                alt=""
+                v-if="!notshowNext"
+              /> -->
+              <img
+                style="margin-right: 8px"
+                src="../../assets/Left-16-normal-Black.png"
+                alt=""
+                @click="lastDetail"
+                v-if="!notshowNext"
+              />
+              <img
+                src="../../assets/Right-16-normal-Black.png"
+                alt=""
+                @click="nextDetail"
+                v-if="!notshowNext"
+              />
+            </template>
+            <img
+              style="margin-right: 0px"
+              @click="closeWordShow"
+              src="../../assets/Cross-16-normal-Black.png"
+              alt=""
+            />
+          </div>
+        </div>
+        <div class="wordDetail">
+          <div
+            :class="[
+              'bwc-Strockplay',
+              themeColor == 'green'
+                ? 'green-border'
+                : themeColor == 'red'
+                ? 'red-border'
+                : 'brown-border',
+            ]"
+          >
+            <div
+              class="strockplay"
+              v-for="(conItem, conindex) in data.new_word"
+              :key="'new_word_' + conindex"
+            >
+              <Strockplayredline
+                :key="conItem + detailIndex + conindex"
+                :Book_text="conItem"
+                :playStorkes="true"
+                :targetDiv="'bwcHanziIntp' + conItem + detailIndex + conindex"
+                :wordNum="data.new_word.length"
+                :themeColor="themeColor"
+              />
+              <div
+                :class="[
+                  'bwc-line',
+                  themeColor == 'green'
+                    ? 'green-bg'
+                    : themeColor == 'red'
+                    ? 'red-bg'
+                    : 'brown-bg',
+                ]"
+                v-if="conindex < data.new_word.length - 1"
+              ></div>
+            </div>
+          </div>
+          <div class="wordInfor">
+            <div class="yinpin">
+              <span class="pinyintext"> {{ data.pinyin.toLowerCase() }}</span>
+              <template
+                v-if="data.mp3_list && data.mp3_list[0] && data.mp3_list[0].id"
+              >
+                <Audio :mp3="data.mp3_list[0].id" :themeColor="themeColor" />
+              </template>
+              <template v-else-if="data.mp3Url">
+                <Audio :mp3="data.mp3Url" :themeColor="themeColor" />
+              </template>
+            </div>
+            <p
+              class="jieshu"
+              v-for="(fy, i) in data.definition_list"
+              :key="i"
+              v-html="fy"
+            ></p>
+          </div>
+        </div>
+        <div class="zhedie-white">
+          <div v-if="list1 && list1.length > 0" v-loading="loading1">
+            <div class="topTitle">
+              <span>本课例句({{ list1.length }})</span>
+              <span @click="handleChangeTab('wordShow')"
+                >{{ wordShow ? "收起" : "展开" }}
+                <img v-if="wordShow" src="../../assets/down-black.png" alt="" />
+                <img v-else src="../../assets/up-black.png" alt="" />
+              </span>
+            </div>
+            <el-collapse-transition>
+              <div class="liju" v-show="wordShow">
+                <div v-for="(item, i) in list1" :key="i">
+                  <div>{{ i + 1 }}.</div>
+                  <div>
+                    <p v-html="item.res"></p>
+                    <p class="p2">
+                      来源:{{ item.source_courseware_name_path }}
+                    </p>
+                  </div>
+                </div>
+              </div>
+            </el-collapse-transition>
+          </div>
+          <div v-if="list2 && list2.length > 0" v-loading="loading2">
+            <div class="topTitle">
+              <span>本书例句({{ list2.length }})</span>
+              <span @click="handleChangeTab('wordShow2')"
+                >{{ wordShow2 ? "收起" : "展开" }}
+                <img
+                  v-if="wordShow2"
+                  src="../../assets/down-black.png"
+                  alt=""
+                />
+                <img v-else src="../../assets/up-black.png" alt="" />
+              </span>
+            </div>
+            <el-collapse-transition>
+              <div class="liju" v-show="wordShow2">
+                <div v-for="(item, i) in list2" :key="i">
+                  <div>{{ list1.length + i + 1 }}.</div>
+                  <div>
+                    <p v-html="item.res"></p>
+                    <p class="p2">
+                      来源:{{ item.source_courseware_name_path }}
+                    </p>
+                  </div>
+                </div>
+              </div>
+            </el-collapse-transition>
+          </div>
+          <div v-if="list3 && list3.length > 0" v-loading="loading3">
+            <div class="topTitle">
+              <span>本套教材例句({{ list3.length }})</span>
+              <span @click="handleChangeTab('wordShow3')"
+                >{{ wordShow3 ? "收起" : "展开" }}
+                <img v-if="wordShow3" src="../../assets/down-black.png" />
+                <img v-else src="../../assets/up-black.png" alt="" />
+              </span>
+            </div>
+            <el-collapse-transition>
+              <div class="liju" v-if="wordShow3">
+                <div v-for="(item, i) in list3" :key="i">
+                  <div>{{ list1.length + list2.length + i + 1 }}.</div>
+                  <div>
+                    <p>{{ item.sentence }}</p>
+                    <p class="p2">
+                      来源:{{ item.source_courseware_name_path }}
+                    </p>
+                  </div>
+                </div>
+              </div>
+            </el-collapse-transition>
+          </div>
+        </div>
+      </div>
+      <Intp :word="data" :themeColor="themeColor" :type="type" />
+    </div>
+  </div>
+</template>
+
+<script>
+import Strockplayredline from "./Strockplayredline.vue";
+import Audio from "./AudioRed.vue";
+import Intp from "./Intp.vue";
+import { getContent } from "@/api/ajax";
+
+export default {
+  //import引入的组件需要注入到对象中才能使用
+  components: {
+    Strockplayredline,
+    Audio,
+    Intp,
+  },
+  props: [
+    "data",
+    "changeDetailIndex",
+    "closeWord",
+    "detailIndex",
+    "notshowNext",
+    "getWordLiju",
+    "optionRes",
+    "themeColor",
+    "isSuccess",
+    "currentTreeID",
+    "type",
+  ],
+  data() {
+    //这里存放数据
+    return {
+      height: "",
+      margintop: "",
+      wordShow: true,
+      wordShow2: true,
+      wordShow3: true,
+      list1: [],
+      list2: [],
+      list3: [],
+      isShow: false,
+      old_word: "",
+      loading1: false,
+      loading2: false,
+      loading3: false,
+    };
+  },
+  //计算属性 类似于data概念
+  computed: {},
+  //监控data中数据变化
+  watch: {
+    data: {
+      handler: function (val, oldVal) {
+        const _this = this;
+        if (val) {
+          _this.initData();
+        }
+      },
+      // 深度观察监听
+      deep: true,
+    },
+  },
+  //方法集合
+  methods: {
+    playAudio() {},
+    // 关闭单词详情
+    closeWordShow() {
+      this.closeWord(false);
+    },
+    // 上一个单词详情
+    lastDetail() {
+      if (this.detailIndex == 0) {
+        this.$message.warning("当前已经是第一个");
+        return;
+      }
+      this.changeDetailIndex("last");
+    },
+    // 下一个单词详情
+    nextDetail() {
+      let _this = this;
+      if (_this.detailIndex == _this.optionRes.length - 1) {
+        this.$message.warning("当前已经是最后一个了 ");
+        return;
+      }
+      _this.changeDetailIndex("next");
+    },
+    viewIntp() {
+      this.loading1 = true;
+      this.loading2 = true;
+      this.loading3 = true;
+      let Mname =
+        "book-courseware_manager-GetCoursewareWordExampleSentenceList";
+      // 获取本课的 本教材的 本套的 的例句
+      getContent(Mname, {
+        courseware_id: this.currentTreeID, // 课件id
+        word: this.data.new_word, //生词
+        search_scope: 0, //检索范围0 本课件  1本教材 2本套
+        is_contain_word_variants: false,
+      })
+        .then((res) => {
+          this.loading1 = false;
+          this.list1 = this.handleExample(res.sentence_list);
+          console.log(this.list1);
+          getContent(Mname, {
+            courseware_id: this.currentTreeID, // 课件id
+            word: this.data.new_word, //生词
+            search_scope: 1, //检索范围0 本课件  1本教材 2本套
+            is_contain_word_variants: false,
+          })
+            .then((res) => {
+              this.loading2 = false;
+              this.list2 = this.handleExample(res.sentence_list);
+              getContent(Mname, {
+                courseware_id: this.currentTreeID, // 课件id
+                word: this.data.new_word, //生词
+                search_scope: 2, //检索范围0 本课件  1本教材 2本套
+                is_contain_word_variants: false,
+              })
+                .then((res) => {
+                  this.loading3 = false;
+                  this.list3 = this.handleExample(res.sentence_list);
+                })
+                .catch((err) => {
+                  this.loading3 = false;
+                });
+            })
+            .catch((err) => {
+              this.loading2 = false;
+            });
+        })
+        .catch((err) => {
+          this.loading1 = false;
+        });
+    },
+    handleExample(list) {
+      list = list.map((item, index) => {
+        let wordIndex = null;
+        for (let i = 0; i < item.sentence.length; i++) {
+          if (item.sentence[i] == item.word) {
+            wordIndex = i;
+          }
+        }
+        // let b = item.begin_position;
+        // let e = item.end_position;
+        // let sent = item.sentence;
+        // let part1 = sent.substring(0, b);
+        // let part2 = sent.substring(b, e);
+        // let part3 = sent.substring(e);
+        let sent = item.sentence;
+        let part1 = "";
+        let part2 = "";
+        let part3 = "";
+        if (wordIndex === 0) {
+          part1 = "";
+          part2 = sent.substring(0, 1);
+          part3 = sent.substring(1);
+        } else if (wordIndex === item.sentence.length - 1) {
+          part1 = sent.substring(0, wordIndex);
+          part2 = sent.substring(wordIndex);
+          part3 = "";
+        } else {
+          part1 = sent.substring(0, wordIndex);
+          part2 = sent.substring(wordIndex, wordIndex + 1);
+          part3 = sent.substring(wordIndex + 1);
+        }
+        // let reg = new RegExp(`${item.word}`, "g");
+        // let result = sent.replace(
+        //   reg,
+        //   `<span style="color:#DE4444;">${item.word}</span>`
+        // );
+        let res =
+          part1 + '<span style="color:#DE4444;">' + part2 + "</span>" + part3;
+        item.res = res;
+        return item;
+      });
+      return list;
+    },
+
+    initData() {
+      console.log(this.data);
+      this.viewIntp();
+      // let Fathernode = document.getElementsByClassName(
+      //   "NPC-Big-Book-preview"
+      // )[0];
+      // if (Fathernode) {
+      //   //   this.height = Fathernode.clientHeight + "px";
+      //   this.height = window.innerHeight + "px";
+      //   this.margintop = "-" + window.innerHeight / 2 + "px";
+      // }
+    },
+    handleChangeTab(flag) {
+      this[flag] = !this[flag];
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    this.initData();
+  },
+  //生命周期-创建之前
+  beforeCreated() {},
+  //生命周期-挂载之前
+  beforeMount() {},
+  //生命周期-更新之前
+  beforUpdate() {},
+  //生命周期-更新之后
+  updated() {},
+  //生命周期-销毁之前
+  beforeDestory() {},
+  //生命周期-销毁完成
+  destoryed() {},
+  //如果页面有keep-alive缓存功能,这个函数会触发
+  activated() {},
+};
+</script>
+<style lang="scss" scoped>
+/* @import url(); 引入css类 */
+.wordDetailModule {
+  width: 100%;
+  z-index: 999;
+  overflow-y: scroll;
+
+  .module-inner {
+    padding: 30px 0;
+    > div {
+      width: 788px;
+      margin: 0 auto;
+      background: #ffffff;
+      box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.15);
+      border-radius: 8px;
+      padding: 16px 32px 40px 32px;
+      .operation {
+        height: 16px;
+        margin-bottom: 8px;
+        div {
+          display: flex;
+          justify-content: flex-end;
+          align-items: center;
+          > :nth-child(1) {
+            margin-right: 24px;
+          }
+          > :nth-child(2) {
+            margin-right: 8px;
+          }
+          > :nth-child(3) {
+            margin-right: 24px;
+          }
+        }
+
+        img {
+          width: 16px;
+          height: 16px;
+          cursor: pointer;
+        }
+      }
+    }
+  }
+  .module-bottom {
+    width: 788px;
+    margin-top: 16px;
+  }
+  .top {
+    padding-top: 16px;
+    .wordDetail {
+      width: 100%;
+      margin-bottom: 16px;
+      display: flex;
+      justify-content: flex-start;
+      align-items: flex-start;
+      .bwc-Strockplay {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        min-width: 130px;
+        height: 130px;
+        margin-bottom: 16px;
+        border-radius: 8px;
+        box-sizing: border-box;
+        overflow: hidden;
+        margin-right: 37px;
+        .strockplay {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          position: relative;
+          .collect-icon {
+            width: 16px;
+            position: absolute;
+            right: 4px;
+            bottom: 4px;
+            cursor: pointer;
+          }
+        }
+        .bwc-line {
+          width: 2px;
+          height: 128px;
+        }
+        .red-bg {
+          background: #ff5757;
+        }
+        .green-bg {
+          background: #24b99e;
+        }
+        .brown-bg {
+          background: #bd8865;
+        }
+      }
+      .red-border {
+        border: 2px solid #ff5757;
+      }
+      .green-border {
+        border: 2px solid #24b99e;
+      }
+      .brown-border {
+        border: 2px solid #bd8865;
+      }
+      .wordInfor {
+        .yinpin {
+          display: flex;
+          justify-content: flex-start;
+          align-items: center;
+          margin-bottom: 16px;
+          .pinyintext {
+            font-size: 20px;
+            line-height: 30px;
+            color: #2c2c2c;
+            display: flex;
+            align-items: center;
+            margin-right: 8px;
+            word-break: normal;
+            font-family: "GB-PINYINOK-B";
+          }
+          .content-voices {
+            width: 16px;
+          }
+        }
+      }
+
+      p {
+        margin: 0;
+      }
+      .word {
+        font-weight: bold;
+        font-size: 24px;
+        line-height: 28px;
+        color: #000000;
+      }
+
+      .jieshu {
+        margin-top: 16px;
+        font-size: 16px;
+        line-height: 19px;
+        color: #000000;
+      }
+    }
+    .zhedie-white {
+      width: 100%;
+      margin: 0 auto;
+      > div {
+        margin-bottom: 24px;
+      }
+      .topTitle {
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        padding: 0 12px;
+        align-items: center;
+        background: #fff !important;
+        > :nth-child(1) {
+          font-weight: 500;
+          font-size: 16px;
+          line-height: 150%;
+          color: rgba(0, 0, 0, 0.85);
+        }
+        > :nth-child(2) {
+          display: flex;
+          align-items: center;
+          font-weight: normal;
+          font-size: 14px;
+          line-height: 22px;
+          color: rgba(0, 0, 0, 0.85);
+          cursor: pointer;
+        }
+        img {
+          width: 16px;
+          height: 16px;
+          margin-left: 4px;
+        }
+        .rotate {
+          animation-name: firstrotate;
+          animation-direction: 2s;
+          animation-fill-mode: both;
+          animation-timing-function: linear;
+        }
+      }
+      .liju {
+        padding-bottom: 16px;
+        padding-right: 24px;
+        background: #f7f7f7;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        border-top: none;
+        border-radius: 0 0 4px 4px;
+        > div {
+          padding-top: 16px;
+          margin-left: 8px;
+          display: flex;
+
+          > :nth-child(1) {
+            text-align: right;
+            margin-right: 6px;
+            line-height: 24px;
+            font-family: "FZJCGFKTK";
+          }
+          p {
+            margin: 0;
+            line-height: 24px;
+            font-size: 16px;
+            color: rgba(0, 0, 0, 0.85);
+            font-family: "FZJCGFKTK";
+          }
+          .p2 {
+            font-size: 12px;
+            line-height: 20px;
+            color: rgba(0, 0, 0, 0.85);
+            opacity: 0.3;
+          }
+        }
+      }
+    }
+  }
+  .bottom {
+    margin-top: 16px;
+    padding-bottom: 23px;
+    .from {
+      //   text-align: right;
+      margin-right: 16px;
+      font-size: 14px;
+      line-height: 16px;
+      color: #000000;
+      opacity: 0.2;
+      display: flex;
+      justify-content: flex-end;
+      align-items: center;
+      img {
+        width: 24px;
+        margin-left: 24px;
+      }
+    }
+    .wordDetail {
+      width: 538px;
+      margin-left: 40px;
+      padding-bottom: 23px;
+      border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+      p {
+        margin: 0;
+      }
+      .word {
+        font-weight: bold;
+        font-size: 24px;
+        line-height: 28px;
+        color: #000000;
+      }
+      .yinpin {
+        font-size: 16px;
+        line-height: 150%;
+        color: #000000;
+        margin-top: 16px;
+        display: flex;
+        > div {
+          height: 24px;
+          display: flex;
+          align-items: center;
+          > :nth-child(1) {
+            margin-right: 5px;
+          }
+        }
+        > :nth-child(2) {
+          margin-left: 27px;
+        }
+        img {
+          margin-left: 10px;
+          width: 24px;
+          height: 24px;
+          cursor: pointer;
+        }
+      }
+      .jieshu {
+        margin-top: 16px;
+        font-size: 16px;
+        line-height: 150%;
+        color: #000000;
+        display: flex;
+        > :nth-child(1) {
+          width: 30px;
+        }
+        :nth-child(2) {
+          width: 524px;
+        }
+      }
+    }
+  }
+}
+</style>
+<style lang="scss">
+.NPC-Big-Book-preview-red {
+  .wordDetailChs {
+    .zhedie-white {
+      .topTitle {
+        height: 40px;
+        background: #fff !important;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        -webkit-box-sizing: border-box;
+        box-sizing: border-box;
+        border-radius: 8px 8px 0px 0px;
+      }
+    }
+  }
+}
+.NPC-Big-Book-preview-green {
+  .wordDetailChs {
+    .zhedie-white {
+      .topTitle {
+        height: 40px;
+        background: #fff !important;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        -webkit-box-sizing: border-box;
+        box-sizing: border-box;
+        border-radius: 8px 8px 0px 0px;
+      }
+    }
+  }
+}
+.NPC-Big-Book-preview-brown {
+  .wordDetailChs {
+    .zhedie-white {
+      .topTitle {
+        height: 40px;
+        background: #fff !important;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        -webkit-box-sizing: border-box;
+        box-sizing: border-box;
+        border-radius: 8px 8px 0px 0px;
+      }
+    }
+  }
+}
+</style>

+ 2 - 1
vue.config.js

@@ -42,7 +42,8 @@ module.exports = {
       // change xxx-api/login => mock/login
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
-        target: `http://gcls.helxsoft.cn`,
+        target: `http://gcls.utschool.cn/`,
+        // target: `http://gcls.helxsoft.cn/`,
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''