xsc před 1 rokem
rodič
revize
9f83a33db1

+ 13 - 2
src/router/index.js

@@ -387,7 +387,7 @@ export const asyncRoutes = [
       {
         path: "videoDetail",
         name: "videoDetail",
-        component: () => import("@/views/video/video_detail"),
+        component: () => import("@/views/video/videoDetail"),
         meta: {
           title: "视频详情",
           icon: "message",
@@ -395,6 +395,17 @@ export const asyncRoutes = [
         },
         hidden: true,
       },
+      {
+        path: "watchRecord",
+        name: "watchRecord",
+        component: () => import("@/views/video/watchRecord"),
+        meta: {
+          title: "观看记录",
+          icon: "message",
+          roles: ["Super admin", "Admin", "Student"],
+        },
+        hidden: true,
+      },
     ],
     // Super admin
   },
@@ -432,7 +443,7 @@ export const asyncRoutes = [
       {
         path: "videoDetail",
         name: "videoDetail",
-        component: () => import("@/views/xuexi/video_detail"),
+        component: () => import("@/views/xuexi/videoDetail"),
         meta: {
           title: "视频详情",
           icon: "message",

+ 10 - 0
src/views/video/api/video.js

@@ -56,3 +56,13 @@ export function lookRecord(data) {
     data,
   })
 }
+
+
+// 视频记录
+export function getSetting(data) {
+  return request({
+    url: '/setting/keys',
+    method: 'post',
+    data,
+  })
+}

+ 18 - 0
src/views/video/api/watch.js

@@ -0,0 +1,18 @@
+/*
+ * @Description:
+ * @version:
+ * @Author: King
+ * @Date: 2022-02-14 14:46:14
+ * @LastEditors: king
+ * @LastEditTime: 2023-03-25 20:23:16
+ */
+import request from '@/utils/request'
+//视频模块接口--------------------------------------------------------------
+export function getList(params) {
+  return request({
+    url: '/course/user-record',
+    method: 'get',
+    params,
+  })
+}
+

+ 2 - 2
src/views/video/components/CourseManage.vue

@@ -84,8 +84,8 @@
           :tiny-height="300"
           @childByValue="getTnyMac"
         /> -->
-        <!-- <tinymce v-model="form.description" :height="300" width="95%" /> -->
-        <el-input type="textarea" v-model="form.description"></el-input>
+        <tinymce v-model="form.description" :height="300" width="95%" />
+        <!-- <el-input type="textarea" v-model="form.description"></el-input> -->
       </el-form-item>
       <el-form-item label="发布时间" prop="published_at">
         <!-- <el-input v-model="form.published_at" /> -->

+ 4 - 3
src/views/video/course_detail.vue

@@ -4,7 +4,7 @@
  * @Author: 小六
  * @Date: 2022-01-21 10:44:04
  * @LastEditors: king
- * @LastEditTime: 2023-03-25 21:48:17
+ * @LastEditTime: 2023-03-26 15:36:24
 -->
 <template>
   <div class="class_container">
@@ -58,6 +58,7 @@
                 :key="index3"
                 effect="dark"
                 :type="colorList[index3]"
+                style="margin-left: 5px;"
               >
                 {{ self }}
               </el-tag>
@@ -112,10 +113,10 @@
             <h3>简介</h3>
             <div class="jianjie_wrap">
               <div v-html="detail.description"></div>
-              <span>
+              <!-- <span>
                 学时:{{ detail.video_nums }}
                 学时
-              </span>
+              </span> -->
             </div>
           </div>
           <div :class="['mulu', current_sec_nav == 2 ? 'isShow' : '']">

+ 6 - 3
src/views/video/editvideo.vue

@@ -45,11 +45,11 @@
           <el-button size="small" type="primary"> 上传视频 </el-button>
           <div slot="tip" class="el-upload__tip">只能上传mp4格式</div>
         </el-upload>
-        <!-- <div v-if="title == '编辑视频'">
+        <div v-if="title == '编辑视频'">
           <a :href="form.url_resource" target="_blank">
             {{ form.url_resource }}
           </a>
-        </div> -->
+        </div>
       </el-form-item>
 
       <el-form-item label="视频简介:" prop="short_description">
@@ -286,7 +286,10 @@ export default {
       console.log(data, "视频详情");
       this.form = data
       this.form.course_id = this.courseId
-      this.form.fileList = []
+      this.form.url_resource = data.url_resource
+      var arr = []
+      arr.push(data.fileList)
+      this.form.fileList = arr
       // this.chapterData = data.data;
     },
     //视频上传成功

+ 12 - 0
src/views/video/index.vue

@@ -173,6 +173,9 @@
           <el-button type="text" @click="courseZhang(row)">
             章节管理
           </el-button>
+          <el-button type="text" @click="courseGuankan(row)">
+            观看记录
+          </el-button>
           <el-button type="text" @click="handleEdit(row)"> 编辑 </el-button>
           <el-button type="text" @click="courseDetail(row)"> 详情 </el-button>
           <el-button type="text" @click="handleDelete(row)"> 删除 </el-button>
@@ -273,6 +276,15 @@ export default {
         },
       });
     },
+    //观看记录
+    courseGuankan(row){
+      this.$router.push({
+        path: "/video/watchRecord",
+        query: {
+          id: row.id,
+        },
+      });
+    },
     // 跳转到课程详情页面
     courseVideo(row) {
       console.log(row.id, "课程列表页跳转到课程详情页row.id就是课程id");

+ 687 - 0
src/views/video/videoDetail.vue

@@ -0,0 +1,687 @@
+<template>
+  <div class="video_container">
+    <div :class="['left_video', isNav ? 'all' : '']">
+      <div class="video_header">
+        <span class="back" @click="back">
+          <i class="el-icon-arrow-left"></i>
+          返回课程
+        </span>
+        <span>{{ videoTitle }}</span>
+      </div>
+      <div class="video_body">
+        <video id="myVideo" class="video-js">
+          <!--<source src="" type="video/mp4" />-->
+          <!-- ../../assets/video/ce.mp4 -->
+        </video>
+      </div>
+      <div class="video_footer"></div>
+    </div>
+    <div :class="['right_nav', isNav ? 'chuxian' : '']">
+      <div class="btn">
+        <i class="el-icon-d-arrow-right"></i>
+      </div>
+      <div class="nav">
+        <ul>
+          <li class="active">目录</li>
+        </ul>
+      </div>
+      <div v-if="index == 1" class="nav_item">
+        <div class="item_wrap">
+          <ul>
+            <li
+              v-for="(item, key) in course_zhangjie"
+              :key="key"
+              class="out_wrap"
+              @click="zhedie(item.id)"
+            >
+              <div class="zhangjie">
+                <div class="left">
+                  <i class="el-icon-s-unfold"></i>
+                  <span class="title">
+                    {{ item.title }}
+                  </span>
+                </div>
+                <div class="right">
+                  <i
+                    v-if="!current_zhedie.includes(item.id)"
+                    class="el-icon-minus"
+                  ></i>
+                  <i v-else class="el-icon-plus"></i>
+                </div>
+              </div>
+              <ul
+                :class="[
+                  'keshi',
+                  current_zhedie.includes(item.id) ? 'is_zhedie' : '',
+                ]"
+              >
+                <li
+                  v-for="(item1, index1) in item.children"
+                  :key="index1"
+                  :class="isLearn == item1.id ? 'li-active' : ''"
+                  @click.stop="clickVideo(item1, vIndex, cIndex)"
+                >
+                  <div class="left">
+                    <span v-if="item1.progress < 100" class="scrle"></span>
+                    <i
+                      v-else-if="item1.progress == 100"
+                      class="el-icon-success complate"
+                    ></i>
+                    <!-- <img v-else alt="" src="../../assets/index/circular.png" /> -->
+                    <span class="title">{{ item1.title }}</span>
+                  </div>
+                  <div class="right">
+                    <span>{{ item1.duration_text }}</span>
+                    <i class="el-icon-video-play complate"></i>
+                  </div>
+                </li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { videoDetail, lookRecord, getSetting } from "./api/video";
+import { showDetail } from "./api/course_detail";
+
+var myPlayer = "";
+var playTimeInterval = ""; //播放事件定时器
+export default {
+  data() {
+    return {
+      value1: 3.5,
+      index: 1,
+      isNav: true,
+      course_zhangjie: [],
+      video_slug: "",
+      current_zhedie: [],
+
+      ban_drag: 0, //当前播放视频是否禁止拖动
+      time: 3, //倒计时
+      isFull: false, //是否全屏
+      loading: true,
+      vIndex: 0, //当前播放视频索引
+      cIndex: 0, //当前播放视频所属章节索引
+      zhUrl: "", //中文字幕地址
+      enUrl: "", //英文字幕地址
+      nextVideo: false,
+      dialogVisible: false,
+      activeName: "zero",
+
+      directory: [], //章节视频列表
+      videoList: [], //当前章节的视频列表
+      isLearn: "", //是否正在学习中
+      courseId: "", //课程id
+      videoId: "", //正在学习的视频id
+      isWatch: 0, //是否完成学习
+      videoTitle: "", //正在播放的视频名称
+      playTime: 0, //视频播放秒数
+      queryForm: {
+        page: 1,
+        per_page: 15,
+      },
+      show: false,
+      form: {
+        danmu: "admin-pro",
+      },
+      activeNames: ["first"],
+      player: null,
+      videoArr: [],
+      times:''
+    };
+  },
+  watch: {
+    $route: "getCourseDetail",
+  },
+  // computed: {
+  //   ...mapGetters({
+  //     token: 'user/token',
+  //   }),
+  // },
+  mounted() {
+    // this.player = this.$refs.player.dp
+    this.nextVideo = false;
+    console.log("测试");
+    this.initVideo();
+    // clearInterval(this.playTimeInterval)
+    // console.log(this.player.video.paused, 'ppppp')
+  },
+  beforeDestroy() {
+    //  组件销毁时,清除播放器
+    myPlayer.dispose(); // 该方法会重置videojs的内部状态并移除dom
+    clearInterval(playTimeInterval);
+
+    if (myPlayer) {
+      console.log("清除视频播放组件");
+      myPlayer.dispose(); // 该方法会重置videojs的内部状态并移除dom
+    }
+  },
+  destroyed() {
+    clearInterval(playTimeInterval);
+
+    console.log("清除视频播放组件");
+  },
+  created() {
+    let route = this.$route;
+    this.courseId = route.query.course_id;
+    this.video_slug = route.query.video_slug;
+    this.course_slug = route.query.slug;
+    this.isLearn = this.videoId = route.query.video_id;
+    console.log(this.videoId, this.courseId, this.video_slug, "123");
+    this.nextVideo = false;
+    console.log(this.vIndex, "YYYYYYYYYYYYYY");
+    clearInterval(playTimeInterval);
+    this.getCourseDetail();
+    this.getSetting();
+    this.show = false;
+    this.$nextTick(() => {
+      this.show = true;
+    });
+  },
+  methods: {
+    //章节折叠
+    zhedie(e) {
+      if (this.current_zhedie.includes(e)) {
+        this.current_zhedie = this.current_zhedie.filter((item) => {
+          return item != e;
+        });
+      } else {
+        this.current_zhedie.push(e);
+      }
+    },
+    async getSetting() {
+      const { data } = await getSetting({ keys: ["SENG_VIDEO_MSG"]});
+      console.log(data);
+      this.times = Number(data.SENG_VIDEO_MSG)
+      console.log(this.times)
+    },
+    //点击重新播放
+    refLook() {
+      this.cIndex = 0;
+      this.vIndex = 0;
+      this.videoList = this.directory[this.cIndex].children;
+      console.log(this.video_list, "list");
+      //let video_list = this.directory[this.cIndex].children
+      this.isLearn = this.videoId = this.videoList[this.vIndex].id;
+      // this.isLearn = this.video_slug = this.videoList[this.vIndex].slug
+      this.lookVideo();
+      //this.videoComments()
+    },
+    initVideo() {
+      let that = this;
+      //初始化视频方法
+      myPlayer = this.$video(document.getElementById("myVideo"), {
+        language: "zh-CN",
+        //确定播放器是否具有用户可以与之交互的控件。没有控件,启动视频播放的唯一方法是使用autoplay属性或通过Player API。
+        controls: true,
+        //自动播放属性,muted:静音播放
+        autoplay: "autoplay",
+        //建议浏览器是否应在<video>加载元素后立即开始下载视频数据。
+        preload: "auto",
+      });
+
+      myPlayer.ready(function () {
+        myPlayer.volume(0.5);
+        myPlayer.play();
+      });
+      console.log(myPlayer, "document.getElementById");
+      //监听视频播放完成
+      myPlayer.on("ended", function () {
+        console.log(this.course_slug, "YYYYYYYYYYYYYY---视频播放结束");
+        clearInterval(playTimeInterval);
+        that.isWatch = 1;
+        that.playTime = parseInt(myPlayer.currentTime());
+        that.lookRecord();
+        that.playTime = 0;
+        myPlayer.exitFullscreen();
+        setTimeout(function () {
+          that.autoPlayNext();
+        }, 3000);
+
+        console.log("视频播放结束");
+      });
+      //监听视频暂停
+      myPlayer.on("pause", function () {
+        console.log("YYYYYYYYYYYYYY---视频播放暂停");
+        clearInterval(playTimeInterval);
+        console.log(parseInt(myPlayer.currentTime()), "视频播放进度暂停");
+        if (this.ban_drag == 0) {
+          let time = parseInt(myPlayer.currentTime());
+          if (time - this.playTime > 10) {
+            myPlayer.currentTime(this.playTime);
+          }
+        }
+        console.log(that.playTime, "视频播放时长");
+        console.log("用户点击了暂停");
+      });
+      //监听视频播放
+      myPlayer.on("play", function () {
+        that.time = 0;
+        that.nextVideo = false;
+        console.log("YYYYYYYYYYYYYY---视频播放");
+        that.getPlayTime();
+        console.log("视频播放");
+      });
+    },
+    //获取视频播放事件每秒加一
+    //自动播放下一章节事件
+    autoPlayNext() {
+      //获取视频判断当前视频是不是本章节的最后一条
+      console.log(this.directory, this.cIndex, "this.directory");
+
+      this.videoList = this.directory[this.cIndex].children;
+      this.vIndex = Number(this.vIndex);
+      console.log(typeof this.vIndex + 1, "vIndex");
+      let vIndexnum = this.vIndex + 1;
+      console.log(vIndexnum == this.videoList.length, "length");
+      if (vIndexnum == this.videoList.length) {
+        //是---判断章节是不是最后一张
+        this.cIndex = Number(this.cIndex);
+        let cIndexnum = this.cIndex + 1;
+        if (cIndexnum == this.directory.length) {
+          clearInterval(playTimeInterval);
+          this.nextVideo = true;
+          return;
+          //是最后一张-----提示用户本课程视频已学完
+        } else {
+          //不是最后一张---播放下一章节的第一条视频
+          console.log(" //不是最后一张---播放下一章节的第一条视频");
+          this.cIndex = this.cIndex + 1;
+          this.vIndex = 0;
+          let video_list = this.directory[this.cIndex].children;
+          this.isLearn = this.videoId = video_list[this.vIndex].id;
+          this.video_slug = video_list[this.vIndex].slug;
+
+          this.lookVideo();
+          /// this.videoComments()
+        }
+      } else {
+        //不是---视频索引+1播放下一条
+        console.log(" // //不是---视频索引+1播放下一条");
+        this.vIndex = this.vIndex + 1;
+        let video_list = this.directory[this.cIndex].children;
+        console.log(video_list, "video_list");
+        this.isLearn = this.videoId = video_list[this.vIndex].id;
+        this.video_slug = video_list[this.vIndex].slug;
+        console.log(this.videoId, "this.videoId");
+        this.lookVideo();
+        //  this.videoComments()
+      }
+    },
+    getPlayTime() {
+      clearInterval(playTimeInterval);
+      let that = this;
+      playTimeInterval = setInterval(function () {
+        that.playTime = parseInt(myPlayer.currentTime());
+        if (Math.floor(that.playTime / that.times) > 0 && that.playTime % that.times == 0) {
+          that.lookRecord();
+          console.log(that.playTime, "that.playTime");
+        }
+      }, 1000);
+    },
+    //定时发送视频播放记录
+    async lookRecord() {
+      let data = {
+        video_id: this.videoId,
+        //video_slug: this.video_slug,
+        watch_seconds: this.playTime,
+        is_watched: this.isWatch,
+        course_id: this.courseId,
+      };
+      const { message } = await lookRecord(data);
+      console.log(message, "ooooooooo");
+    },
+    //获取课程详情章节列表和视频列表
+    async getCourseDetail() {
+      let slug = this.$route.query.slug;
+      console.log(slug, "getCourseDetail");
+      const { data } = await showDetail({ slug: slug });
+      console.log(data);
+      this.directory = data.directory;
+      this.course_zhangjie = data.directory;
+      if (this.videoId) {
+        this.isLearn = this.videoId;
+        //this.isLearn = this.video_slug
+        let route = this.$route;
+        this.vIndex = route.query.v_index;
+        this.cIndex = route.query.c_index;
+      } else {
+        console.log(data, "YYYYY YYY");
+        this.isLearn = this.videoId = data.directory[0].children[0].id;
+        //this.isLearn = this.video_slug = data.directory[0].children[0].slug
+        this.videoList = this.directory[0].children;
+      }
+      var videoArr = [];
+      for (var i = 0; i < data.directory.length; i++) {
+        for (var j = 0; j < data.directory[i].children.length; i++) {
+          videoArr = videoArr.concat(data.directory[i].children[j]);
+        }
+      }
+      console.log(videoArr, "视频数组");
+      this.videoArr = videoArr;
+      this.lookVideo();
+      //   this.videoComments()
+      console.log(data.directory, "课程详情");
+    },
+    //选择需要播放的视频
+    async clickVideo(item, vIndex, cIndex) {
+      this.vIndex = vIndex;
+      this.cIndex = cIndex;
+      clearInterval(playTimeInterval);
+      console.log(vIndex, "vIndex, cIndex");
+      console.log(cIndex, "vIndex, cIndex");
+      this.nextVideo = false;
+      this.isWatch = 0;
+      console.log(item, "点击时间点");
+      this.isLearn = item.id;
+      this.videoTitle = item.title;
+      this.videoId = item.id;
+      this.video_slug = item.slug;
+      this.playTime = 0;
+      this.lookVideo();
+      //  this.videoComments()
+    },
+    async lookVideo() {
+      var oldTracks = myPlayer.remoteTextTracks();
+      var i = oldTracks.length;
+      while (i--) {
+        myPlayer.removeRemoteTextTrack(oldTracks[i]);
+      }
+      const { data } = await videoDetail({ id: this.videoId });
+      // const { data } = await videoDetail(this.video_slug)
+      console.log(this.video_slug, "this.video_slug");
+      this.videoTitle = data.title;
+      myPlayer.src(data.url_resource);
+
+      this.attaches = data.attaches;
+      this.videoDescription = data.description;
+      this.loading = false;
+      this.ban_drag = data.ban_drag;
+      let enurl = data.subtitle_en_path_resource;
+      let url = data.subtitle_zh_path_resource;
+      var zhurl = {
+        kind: "subtitles",
+        label: "中文简体",
+        src: url,
+        default: "true",
+      };
+      //https://platform.site.ximengnaikang.com/storage/course_video/hKy5X404wDrRAuAS8gZxfZIDbx6sFkAW.webvtt
+      // this.enUrl = data.video.subtitle_en_path_resource
+      myPlayer.addRemoteTextTrack(zhurl, true);
+      console.log(myPlayer, "myPlayer");
+      console.log(data.subtitle_zh_path_resource, "myPlayer");
+      if (enurl) {
+        var enUrl = {
+          kind: "subtitles",
+          label: "English",
+          src: enurl,
+          default: "true",
+        };
+        myPlayer.addRemoteTextTrack(enUrl, true);
+      }
+
+      console.log(data.subtitle_en_path_resource, "视频详情");
+    },
+
+    back() {
+      // this.$router.go(-1)
+      this.$router.push({
+        path: "./courseDetail",
+        query: {
+          slug: this.course_slug,
+          id: this.courseId,
+        },
+      });
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.video_container {
+  ul,
+  li {
+    margin: 0;
+    padding: 0;
+  }
+  position: fixed;
+  top: 145px;
+  width: 100%;
+  background-color: #e5e5e5;
+  height: 100%;
+  .left_video {
+    position: absolute;
+    left: 26px;
+    right: 35px;
+    top: 0;
+    padding-top: 54px;
+    bottom: 70px;
+    transition: 0.5s;
+    .video_header {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      height: 54px;
+      line-height: 54px;
+      color: #999;
+      font-size: 16px;
+      z-index: 1;
+      overflow: hidden;
+      .back {
+        color: #666;
+        padding-right: 20px;
+        border-right: 1px solid #c1c1c1;
+        margin-right: 20px;
+        cursor: pointer;
+      }
+    }
+    .video_body {
+      position: relative;
+      background-color: #000;
+      height: 88%;
+      overflow: hidden;
+      .my_video {
+        width: 100%;
+        // height: calc(100vh - 300px);
+      }
+      .vjs-error .vjs-error-display:before {
+        content: none !important;
+      }
+      .video-js .vjs-big-play-button {
+        /*对播放按钮的样式进行设置*/
+        width: 100%;
+        // height: calc(100vh - 300px);
+        border-radius: 50%;
+      }
+      .video-js {
+        width: 100% !important;
+        height: 100% !important;
+      }
+      .video-js .vjs-big-play-button {
+        position: absolute !important;
+        top: 50% !important;
+        left: 50% !important;
+        display: block;
+        width: 50px !important;
+        height: 50px !important;
+        padding: 0;
+        margin-top: -25px !important;
+        margin-left: -25px !important;
+        font-size: 3em;
+        line-height: 42px !important;
+        cursor: pointer;
+        background-color: #2b333f;
+        background-color: rgba(43, 51, 63, 0.7);
+        border: 0.06666em solid #fff;
+        border-radius: 50% !important;
+        opacity: 1;
+        -webkit-transition: all 0.4s;
+        transition: all 0.4s;
+      }
+    }
+    .video_footer {
+    }
+  }
+  .all {
+    right: 395px;
+  }
+  .right_nav {
+    position: absolute;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    width: 360px;
+    right: -360px;
+    background-color: #fff;
+    box-shadow: -1px 0 5px rgba(0, 0, 0, 0.1);
+    transition: 0.5s;
+
+    .btn {
+      position: absolute;
+      top: 50%;
+      left: -32px;
+      margin-top: -24px;
+      width: 32px;
+      text-align: center;
+      line-height: 48px;
+      height: 48px;
+      border-radius: 100px 0 0 100px;
+      cursor: pointer;
+      background: #46c37b;
+      color: #fff;
+      font-size: 18px;
+    }
+    .nav {
+      ul {
+        padding: 0;
+        margin: 0;
+        height: 56px;
+        list-style: none;
+        text-align: center;
+        border-bottom: 1px solid #f5f5f5;
+        font-size: 16px;
+        font-weight: 500;
+        color: #666;
+        line-height: 56px;
+        display: flex;
+        justify-content: space-around;
+        align-items: center;
+        position: relative;
+        cursor: pointer;
+        li {
+          position: relative;
+        }
+        .active {
+          &::after {
+            content: "";
+            display: block;
+            position: absolute;
+            bottom: 0;
+            left: 50%;
+            transform: translateX(-50%);
+            width: 36px;
+            height: 2px;
+            border-radius: 1px;
+            background: #46c37b;
+          }
+        }
+      }
+    }
+    .nav_item {
+      position: absolute;
+      top: 57px;
+      bottom: 0;
+      width: 100%;
+      // overflow-y: hidden;
+      overflow-x: hidden;
+      margin-bottom: 20px;
+      overflow-y: scroll;
+      .item_wrap {
+        margin-top: 24px;
+        ul {
+          list-style: none;
+          padding: 0;
+          margin-bottom: 0;
+          .out_wrap {
+            margin-bottom: 10px;
+            cursor: pointer;
+
+            .zhangjie {
+              display: flex;
+              justify-content: space-between;
+              background-color: #f5f5f5;
+              padding-right: 17px;
+
+              align-items: center;
+            }
+            .keshi {
+              li {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                margin-bottom: 10px;
+                cursor: pointer;
+                padding-right: 17px;
+                &:hover {
+                  background-color: #f3fee8;
+                }
+              }
+              .li-active {
+                background-color: #f3fee8;
+              }
+            }
+            .is_zhedie {
+              display: none;
+            }
+            i {
+              font-size: 18px;
+            }
+            .left {
+              padding: 0 0 0 17px;
+              display: flex;
+              align-items: center;
+              width: calc(100% - 60px);
+              .scrle {
+                display: inline-block;
+                width: 16px;
+                height: 16px;
+                border-radius: 50%;
+                border: 2px solid #999;
+                box-sizing: border-box;
+                flex-shrink: 0;
+              }
+              img {
+                width: 16px;
+                height: 16px;
+              }
+              .complate {
+                color: #46c37b;
+              }
+              .title {
+                display: inline-block;
+                padding: 15px 10px 15px 0;
+                margin-left: 20px;
+                // width: calc(100% - 20px);
+              }
+            }
+            .right {
+              display: flex;
+              align-items: center;
+              color: #999;
+              span {
+                margin-right: 5px;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  .chuxian {
+    right: 0px;
+  }
+}
+</style>

+ 260 - 0
src/views/video/watchRecord.vue

@@ -0,0 +1,260 @@
+<template>
+  <div class="department-management-container">
+    <vab-query-form style="display: flex; justify-content: space-between">
+      <vab-query-form-left-panel :span="12">
+        <!-- 添加暂时不要求 -->
+        <!-- <el-button icon="el-icon-plus" type="primary" @click="handleEdit">
+          添加
+        </el-button> -->
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item>
+            <el-input
+              v-model.trim="queryForm.title"
+              clearable
+              placeholder="请输入课程名称"
+              @keyup.enter.native="queryData"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">
+              搜索
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-right-panel>
+    </vab-query-form>
+    <!-- 列表表格 -->
+    <el-table v-loading="listLoading" border :data="list" default-expand-all>
+      <el-table-column
+        align="center"
+        label="序号"
+        show-overflow-tooltip
+        type="index"
+        width="60"
+      />
+      <el-table-column align="center" label="图片">
+        <template #default="{ row }">
+          <el-image
+            v-if="row.thumb_resource != null"
+            :src="row.thumb_resource.url"
+          />
+          <div v-else>--</div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="课程名称"
+        min-width="120"
+        prop="title"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="标签"
+        prop="labels"
+        show-overflow-tooltip
+      >
+        <template #default="{ row }">
+          <span v-if="row.labels.length == 0">--</span>
+          <el-tag
+            v-for="(item, index) in row.labels"
+            :key="index"
+            type="success"
+          >
+            {{ item }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column
+        align="center"
+        label="课程类型"
+        show-overflow-tooltip
+      >
+        <template #default="{ row }">
+          <el-tag v-if="row.category != null">{{ row.category.name }}</el-tag>
+          <span v-else>--</span>
+        </template>
+      </el-table-column> -->
+      <el-table-column
+        align="center"
+        label="视频数量"
+        prop="video_nums"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="创建时间"
+        min-width="100"
+        prop="created_at"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="状态"
+        min-width="80"
+        prop="status"
+        show-overflow-tooltip
+      >
+        <template #default="{ row }">
+          <el-tag v-if="row.status == 1"> 正常 </el-tag>
+          <el-tag v-if="row.status == 0" type="danger"> 禁用 </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        fixed="right"
+        label="操作"
+        min-width="200"
+      >
+        <template #default="{ row }">
+          <el-button type="text" @click="handleEdit(row)"> 编辑 </el-button>
+          <el-button type="text" @click="courseDetail(row)"> 详情 </el-button>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <!-- <el-image
+          class="vab-data-empty"
+          :src="require('@/assets/empty_images/data_empty.png')"
+        /> -->
+      </template>
+    </el-table>
+    <el-pagination
+      background
+      :current-page="queryForm.page"
+      :layout="layout"
+      :page-size="queryForm.per_page"
+      :page-sizes="[15, 20, 30, 40, 50, 100]"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+    <edit ref="edit" @fetch-data="getCourseList" />
+  </div>
+</template>
+
+<script>
+import { getList } from "./api/watch";
+
+export default {
+  name: "CourseManage",
+  components: {  },
+  data() {
+    return {
+      //修改排序
+      editForm: {},
+      oldSort: 0,
+      editStatus: 0, //排序编辑状态 0为正常 1显示输入框
+      //课程列表数组
+      list: [],
+      listLoading: true,
+      layout: "total, sizes, prev, pager, next, jumper",
+      total: 0,
+      queryForm: {
+        page: 1,
+        per_page: 15,
+        title: "",
+        course_id:2
+      },
+    };
+  },
+  created() {
+    this.getCourseList();
+  },
+  methods: {
+    //修改排序事件
+    cancelSort(row) {
+      this.editStatus = 0;
+      row.sort = this.oldSort;
+    },
+    //修改排序事件
+    async changeSort(row) {
+      console.log("1111111111");
+      this.editStatus = 1;
+      this.editForm = row;
+      this.oldSort = row.sort;
+      this.$nextTick(() => {
+        this.$refs["getFocus"].focus();
+      });
+      //await doEdit(this.editForm)
+    },
+    //修改排序
+    inputSort(e) {
+      console.log(e, "000000000");
+      this.editForm.sort = e;
+    },
+    // 修改排序报错
+    async sureChange() {
+      this.editStatus = 0;
+      console.log(this.editForm, "课程列表中修改排序传递参数");
+      if (this.oldSort !== this.editForm.sort) {
+        const { message } = await doEdit(this.editForm);
+        console.log(message, "message");
+        // location.reload()
+        this.$message({ message, type: "success" });
+        this.getList();
+      } else {
+        return;
+      }
+    },
+    courseDetail(row) {
+      console.log(row.id, "课程列表页跳转到课程详情页row.id就是课程id");
+      this.$router.push({
+        path: "/video/courseDetail",
+        query: {
+          id: row.id,
+          slug:row.slug
+        },
+      });
+    },
+    handleSizeChange(val) {
+      this.queryForm.per_page = val;
+      this.getCourseList();
+    },
+    handleCurrentChange(val) {
+      this.queryForm.page = val;
+      this.getCourseList();
+    },
+    queryData() {
+      this.queryForm.page = 1;
+      this.getCourseList();
+    },
+    // 获取课程列表数据
+    async getCourseList() {
+      this.listLoading = true;
+      const { data } = await getList(this.queryForm);
+      console.log(data, "课程列表信息");
+      // const { list, meta } = data;
+      // console.log(list);
+      // this.list = data.data;
+      // this.total = meta.pagination.total;
+      // this.listLoading = false;
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+/* 修改排序样式 */
+.edit {
+  display: inline-block;
+  width: 110px;
+}
+.input-sort {
+  display: inline-block;
+  width: 80px;
+  margin-right: 10px;
+  margin-left: 10px;
+  border: none;
+}
+.sort-num {
+  display: inline-block;
+  margin-right: 10px;
+}
+.el-icon-edit,
+.el-icon-circle-check,
+.el-icon-circle-close {
+  color: #1890ff;
+  cursor: pointer;
+}
+</style>

+ 17 - 1
src/views/xuexi/api/video.js

@@ -4,7 +4,7 @@
  * @Author: King
  * @Date: 2022-02-14 14:46:14
  * @LastEditors: king
- * @LastEditTime: 2023-03-25 21:27:53
+ * @LastEditTime: 2023-03-26 14:24:39
  */
 import request from '@/utils/request'
 //视频模块接口--------------------------------------------------------------
@@ -15,6 +15,14 @@ export function videoList(params) {
     params,
   })
 }
+//视频详情
+export function videoDetail(params) {
+  return request({
+    url: '/course/video',
+    method: 'get',
+    params,
+  })
+}
 // 视频添加 ?????????
 export function videoAdd(data) {
   return request({
@@ -55,3 +63,11 @@ export function showDetail(params) {
     params,
   })
 }
+// 视频记录
+export function getSetting(data) {
+  return request({
+    url: '/setting/keys',
+    method: 'post',
+    data,
+  })
+}

+ 4 - 3
src/views/xuexi/course_detail.vue

@@ -4,7 +4,7 @@
  * @Author: 小六
  * @Date: 2022-01-21 10:44:04
  * @LastEditors: king
- * @LastEditTime: 2023-03-25 21:30:36
+ * @LastEditTime: 2023-03-26 15:35:03
 -->
 <template>
   <div class="class_container">
@@ -58,6 +58,7 @@
                 :key="index3"
                 effect="dark"
                 :type="colorList[index3]"
+                style="margin-left: 5px;"
               >
                 {{ self }}
               </el-tag>
@@ -107,10 +108,10 @@
             <h3>简介</h3>
             <div class="jianjie_wrap">
               <div v-html="detail.description"></div>
-              <span>
+              <!-- <span>
                 学时:{{ detail.video_nums }}
                 学时
-              </span>
+              </span> -->
             </div>
           </div>
           <div :class="['mulu', current_sec_nav == 2 ? 'isShow' : '']">

+ 687 - 0
src/views/xuexi/videoDetail.vue

@@ -0,0 +1,687 @@
+<template>
+  <div class="video_container">
+    <div :class="['left_video', isNav ? 'all' : '']">
+      <div class="video_header">
+        <span class="back" @click="back">
+          <i class="el-icon-arrow-left"></i>
+          返回课程
+        </span>
+        <span>{{ videoTitle }}</span>
+      </div>
+      <div class="video_body">
+        <video id="myVideo" class="video-js">
+          <!--<source src="" type="video/mp4" />-->
+          <!-- ../../assets/video/ce.mp4 -->
+        </video>
+      </div>
+      <div class="video_footer"></div>
+    </div>
+    <div :class="['right_nav', isNav ? 'chuxian' : '']">
+      <div class="btn">
+        <i class="el-icon-d-arrow-right"></i>
+      </div>
+      <div class="nav">
+        <ul>
+          <li class="active">目录</li>
+        </ul>
+      </div>
+      <div v-if="index == 1" class="nav_item">
+        <div class="item_wrap">
+          <ul>
+            <li
+              v-for="(item, key) in course_zhangjie"
+              :key="key"
+              class="out_wrap"
+              @click="zhedie(item.id)"
+            >
+              <div class="zhangjie">
+                <div class="left">
+                  <i class="el-icon-s-unfold"></i>
+                  <span class="title">
+                    {{ item.title }}
+                  </span>
+                </div>
+                <div class="right">
+                  <i
+                    v-if="!current_zhedie.includes(item.id)"
+                    class="el-icon-minus"
+                  ></i>
+                  <i v-else class="el-icon-plus"></i>
+                </div>
+              </div>
+              <ul
+                :class="[
+                  'keshi',
+                  current_zhedie.includes(item.id) ? 'is_zhedie' : '',
+                ]"
+              >
+                <li
+                  v-for="(item1, index1) in item.children"
+                  :key="index1"
+                  :class="isLearn == item1.id ? 'li-active' : ''"
+                  @click.stop="clickVideo(item1, vIndex, cIndex)"
+                >
+                  <div class="left">
+                    <span v-if="item1.progress < 100" class="scrle"></span>
+                    <i
+                      v-else-if="item1.progress == 100"
+                      class="el-icon-success complate"
+                    ></i>
+                    <!-- <img v-else alt="" src="../../assets/index/circular.png" /> -->
+                    <span class="title">{{ item1.title }}</span>
+                  </div>
+                  <div class="right">
+                    <span>{{ item1.duration_text }}</span>
+                    <i class="el-icon-video-play complate"></i>
+                  </div>
+                </li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { videoDetail, lookRecord, getSetting } from "./api/video";
+import { showDetail } from "./api/course_detail";
+
+var myPlayer = "";
+var playTimeInterval = ""; //播放事件定时器
+export default {
+  data() {
+    return {
+      value1: 3.5,
+      index: 1,
+      isNav: true,
+      course_zhangjie: [],
+      video_slug: "",
+      current_zhedie: [],
+
+      ban_drag: 0, //当前播放视频是否禁止拖动
+      time: 3, //倒计时
+      isFull: false, //是否全屏
+      loading: true,
+      vIndex: 0, //当前播放视频索引
+      cIndex: 0, //当前播放视频所属章节索引
+      zhUrl: "", //中文字幕地址
+      enUrl: "", //英文字幕地址
+      nextVideo: false,
+      dialogVisible: false,
+      activeName: "zero",
+
+      directory: [], //章节视频列表
+      videoList: [], //当前章节的视频列表
+      isLearn: "", //是否正在学习中
+      courseId: "", //课程id
+      videoId: "", //正在学习的视频id
+      isWatch: 0, //是否完成学习
+      videoTitle: "", //正在播放的视频名称
+      playTime: 0, //视频播放秒数
+      queryForm: {
+        page: 1,
+        per_page: 15,
+      },
+      show: false,
+      form: {
+        danmu: "admin-pro",
+      },
+      activeNames: ["first"],
+      player: null,
+      videoArr: [],
+      times:''
+    };
+  },
+  watch: {
+    $route: "getCourseDetail",
+  },
+  // computed: {
+  //   ...mapGetters({
+  //     token: 'user/token',
+  //   }),
+  // },
+  mounted() {
+    // this.player = this.$refs.player.dp
+    this.nextVideo = false;
+    console.log("测试");
+    this.initVideo();
+    // clearInterval(this.playTimeInterval)
+    // console.log(this.player.video.paused, 'ppppp')
+  },
+  beforeDestroy() {
+    //  组件销毁时,清除播放器
+    myPlayer.dispose(); // 该方法会重置videojs的内部状态并移除dom
+    clearInterval(playTimeInterval);
+
+    if (myPlayer) {
+      console.log("清除视频播放组件");
+      myPlayer.dispose(); // 该方法会重置videojs的内部状态并移除dom
+    }
+  },
+  destroyed() {
+    clearInterval(playTimeInterval);
+
+    console.log("清除视频播放组件");
+  },
+  created() {
+    let route = this.$route;
+    this.courseId = route.query.course_id;
+    this.video_slug = route.query.video_slug;
+    this.course_slug = route.query.slug;
+    this.isLearn = this.videoId = route.query.video_id;
+    console.log(this.videoId, this.courseId, this.video_slug, "123");
+    this.nextVideo = false;
+    console.log(this.vIndex, "YYYYYYYYYYYYYY");
+    clearInterval(playTimeInterval);
+    this.getCourseDetail();
+    this.getSetting();
+    this.show = false;
+    this.$nextTick(() => {
+      this.show = true;
+    });
+  },
+  methods: {
+    //章节折叠
+    zhedie(e) {
+      if (this.current_zhedie.includes(e)) {
+        this.current_zhedie = this.current_zhedie.filter((item) => {
+          return item != e;
+        });
+      } else {
+        this.current_zhedie.push(e);
+      }
+    },
+    async getSetting() {
+      const { data } = await getSetting({ keys: ["SENG_VIDEO_MSG"]});
+      console.log(data);
+      this.times = Number(data.SENG_VIDEO_MSG)
+      console.log(this.times)
+    },
+    //点击重新播放
+    refLook() {
+      this.cIndex = 0;
+      this.vIndex = 0;
+      this.videoList = this.directory[this.cIndex].children;
+      console.log(this.video_list, "list");
+      //let video_list = this.directory[this.cIndex].children
+      this.isLearn = this.videoId = this.videoList[this.vIndex].id;
+      // this.isLearn = this.video_slug = this.videoList[this.vIndex].slug
+      this.lookVideo();
+      //this.videoComments()
+    },
+    initVideo() {
+      let that = this;
+      //初始化视频方法
+      myPlayer = this.$video(document.getElementById("myVideo"), {
+        language: "zh-CN",
+        //确定播放器是否具有用户可以与之交互的控件。没有控件,启动视频播放的唯一方法是使用autoplay属性或通过Player API。
+        controls: true,
+        //自动播放属性,muted:静音播放
+        autoplay: "autoplay",
+        //建议浏览器是否应在<video>加载元素后立即开始下载视频数据。
+        preload: "auto",
+      });
+
+      myPlayer.ready(function () {
+        myPlayer.volume(0.5);
+        myPlayer.play();
+      });
+      console.log(myPlayer, "document.getElementById");
+      //监听视频播放完成
+      myPlayer.on("ended", function () {
+        console.log(this.course_slug, "YYYYYYYYYYYYYY---视频播放结束");
+        clearInterval(playTimeInterval);
+        that.isWatch = 1;
+        that.playTime = parseInt(myPlayer.currentTime());
+        that.lookRecord();
+        that.playTime = 0;
+        myPlayer.exitFullscreen();
+        setTimeout(function () {
+          that.autoPlayNext();
+        }, 3000);
+
+        console.log("视频播放结束");
+      });
+      //监听视频暂停
+      myPlayer.on("pause", function () {
+        console.log("YYYYYYYYYYYYYY---视频播放暂停");
+        clearInterval(playTimeInterval);
+        console.log(parseInt(myPlayer.currentTime()), "视频播放进度暂停");
+        if (this.ban_drag == 0) {
+          let time = parseInt(myPlayer.currentTime());
+          if (time - this.playTime > 10) {
+            myPlayer.currentTime(this.playTime);
+          }
+        }
+        console.log(that.playTime, "视频播放时长");
+        console.log("用户点击了暂停");
+      });
+      //监听视频播放
+      myPlayer.on("play", function () {
+        that.time = 0;
+        that.nextVideo = false;
+        console.log("YYYYYYYYYYYYYY---视频播放");
+        that.getPlayTime();
+        console.log("视频播放");
+      });
+    },
+    //获取视频播放事件每秒加一
+    //自动播放下一章节事件
+    autoPlayNext() {
+      //获取视频判断当前视频是不是本章节的最后一条
+      console.log(this.directory, this.cIndex, "this.directory");
+
+      this.videoList = this.directory[this.cIndex].children;
+      this.vIndex = Number(this.vIndex);
+      console.log(typeof this.vIndex + 1, "vIndex");
+      let vIndexnum = this.vIndex + 1;
+      console.log(vIndexnum == this.videoList.length, "length");
+      if (vIndexnum == this.videoList.length) {
+        //是---判断章节是不是最后一张
+        this.cIndex = Number(this.cIndex);
+        let cIndexnum = this.cIndex + 1;
+        if (cIndexnum == this.directory.length) {
+          clearInterval(playTimeInterval);
+          this.nextVideo = true;
+          return;
+          //是最后一张-----提示用户本课程视频已学完
+        } else {
+          //不是最后一张---播放下一章节的第一条视频
+          console.log(" //不是最后一张---播放下一章节的第一条视频");
+          this.cIndex = this.cIndex + 1;
+          this.vIndex = 0;
+          let video_list = this.directory[this.cIndex].children;
+          this.isLearn = this.videoId = video_list[this.vIndex].id;
+          this.video_slug = video_list[this.vIndex].slug;
+
+          this.lookVideo();
+          /// this.videoComments()
+        }
+      } else {
+        //不是---视频索引+1播放下一条
+        console.log(" // //不是---视频索引+1播放下一条");
+        this.vIndex = this.vIndex + 1;
+        let video_list = this.directory[this.cIndex].children;
+        console.log(video_list, "video_list");
+        this.isLearn = this.videoId = video_list[this.vIndex].id;
+        this.video_slug = video_list[this.vIndex].slug;
+        console.log(this.videoId, "this.videoId");
+        this.lookVideo();
+        //  this.videoComments()
+      }
+    },
+    getPlayTime() {
+      clearInterval(playTimeInterval);
+      let that = this;
+      playTimeInterval = setInterval(function () {
+        that.playTime = parseInt(myPlayer.currentTime());
+        if (Math.floor(that.playTime / that.times) > 0 && that.playTime % that.times == 0) {
+          that.lookRecord();
+          console.log(that.playTime, "that.playTime");
+        }
+      }, 1000);
+    },
+    //定时发送视频播放记录
+    async lookRecord() {
+      let data = {
+        video_id: this.videoId,
+        //video_slug: this.video_slug,
+        watch_seconds: this.playTime,
+        is_watched: this.isWatch,
+        course_id: this.courseId,
+      };
+      const { message } = await lookRecord(data);
+      console.log(message, "ooooooooo");
+    },
+    //获取课程详情章节列表和视频列表
+    async getCourseDetail() {
+      let slug = this.$route.query.slug;
+      console.log(slug, "getCourseDetail");
+      const { data } = await showDetail({ slug: slug });
+      console.log(data);
+      this.directory = data.directory;
+      this.course_zhangjie = data.directory;
+      if (this.videoId) {
+        this.isLearn = this.videoId;
+        //this.isLearn = this.video_slug
+        let route = this.$route;
+        this.vIndex = route.query.v_index;
+        this.cIndex = route.query.c_index;
+      } else {
+        console.log(data, "YYYYY YYY");
+        this.isLearn = this.videoId = data.directory[0].children[0].id;
+        //this.isLearn = this.video_slug = data.directory[0].children[0].slug
+        this.videoList = this.directory[0].children;
+      }
+      var videoArr = [];
+      for (var i = 0; i < data.directory.length; i++) {
+        for (var j = 0; j < data.directory[i].children.length; i++) {
+          videoArr = videoArr.concat(data.directory[i].children[j]);
+        }
+      }
+      console.log(videoArr, "视频数组");
+      this.videoArr = videoArr;
+      this.lookVideo();
+      //   this.videoComments()
+      console.log(data.directory, "课程详情");
+    },
+    //选择需要播放的视频
+    async clickVideo(item, vIndex, cIndex) {
+      this.vIndex = vIndex;
+      this.cIndex = cIndex;
+      clearInterval(playTimeInterval);
+      console.log(vIndex, "vIndex, cIndex");
+      console.log(cIndex, "vIndex, cIndex");
+      this.nextVideo = false;
+      this.isWatch = 0;
+      console.log(item, "点击时间点");
+      this.isLearn = item.id;
+      this.videoTitle = item.title;
+      this.videoId = item.id;
+      this.video_slug = item.slug;
+      this.playTime = 0;
+      this.lookVideo();
+      //  this.videoComments()
+    },
+    async lookVideo() {
+      var oldTracks = myPlayer.remoteTextTracks();
+      var i = oldTracks.length;
+      while (i--) {
+        myPlayer.removeRemoteTextTrack(oldTracks[i]);
+      }
+      const { data } = await videoDetail({ id: this.videoId });
+      // const { data } = await videoDetail(this.video_slug)
+      console.log(this.video_slug, "this.video_slug");
+      this.videoTitle = data.title;
+      myPlayer.src(data.url_resource);
+
+      this.attaches = data.attaches;
+      this.videoDescription = data.description;
+      this.loading = false;
+      this.ban_drag = data.ban_drag;
+      let enurl = data.subtitle_en_path_resource;
+      let url = data.subtitle_zh_path_resource;
+      var zhurl = {
+        kind: "subtitles",
+        label: "中文简体",
+        src: url,
+        default: "true",
+      };
+      //https://platform.site.ximengnaikang.com/storage/course_video/hKy5X404wDrRAuAS8gZxfZIDbx6sFkAW.webvtt
+      // this.enUrl = data.video.subtitle_en_path_resource
+      myPlayer.addRemoteTextTrack(zhurl, true);
+      console.log(myPlayer, "myPlayer");
+      console.log(data.subtitle_zh_path_resource, "myPlayer");
+      if (enurl) {
+        var enUrl = {
+          kind: "subtitles",
+          label: "English",
+          src: enurl,
+          default: "true",
+        };
+        myPlayer.addRemoteTextTrack(enUrl, true);
+      }
+
+      console.log(data.subtitle_en_path_resource, "视频详情");
+    },
+
+    back() {
+      // this.$router.go(-1)
+      this.$router.push({
+        path: "./courseDetail",
+        query: {
+          slug: this.course_slug,
+          id: this.courseId,
+        },
+      });
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.video_container {
+  ul,
+  li {
+    margin: 0;
+    padding: 0;
+  }
+  position: fixed;
+  top: 145px;
+  width: 100%;
+  background-color: #e5e5e5;
+  height: 100%;
+  .left_video {
+    position: absolute;
+    left: 26px;
+    right: 35px;
+    top: 0;
+    padding-top: 54px;
+    bottom: 70px;
+    transition: 0.5s;
+    .video_header {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      height: 54px;
+      line-height: 54px;
+      color: #999;
+      font-size: 16px;
+      z-index: 1;
+      overflow: hidden;
+      .back {
+        color: #666;
+        padding-right: 20px;
+        border-right: 1px solid #c1c1c1;
+        margin-right: 20px;
+        cursor: pointer;
+      }
+    }
+    .video_body {
+      position: relative;
+      background-color: #000;
+      height: 88%;
+      overflow: hidden;
+      .my_video {
+        width: 100%;
+        // height: calc(100vh - 300px);
+      }
+      .vjs-error .vjs-error-display:before {
+        content: none !important;
+      }
+      .video-js .vjs-big-play-button {
+        /*对播放按钮的样式进行设置*/
+        width: 100%;
+        // height: calc(100vh - 300px);
+        border-radius: 50%;
+      }
+      .video-js {
+        width: 100% !important;
+        height: 100% !important;
+      }
+      .video-js .vjs-big-play-button {
+        position: absolute !important;
+        top: 50% !important;
+        left: 50% !important;
+        display: block;
+        width: 50px !important;
+        height: 50px !important;
+        padding: 0;
+        margin-top: -25px !important;
+        margin-left: -25px !important;
+        font-size: 3em;
+        line-height: 42px !important;
+        cursor: pointer;
+        background-color: #2b333f;
+        background-color: rgba(43, 51, 63, 0.7);
+        border: 0.06666em solid #fff;
+        border-radius: 50% !important;
+        opacity: 1;
+        -webkit-transition: all 0.4s;
+        transition: all 0.4s;
+      }
+    }
+    .video_footer {
+    }
+  }
+  .all {
+    right: 395px;
+  }
+  .right_nav {
+    position: absolute;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    width: 360px;
+    right: -360px;
+    background-color: #fff;
+    box-shadow: -1px 0 5px rgba(0, 0, 0, 0.1);
+    transition: 0.5s;
+
+    .btn {
+      position: absolute;
+      top: 50%;
+      left: -32px;
+      margin-top: -24px;
+      width: 32px;
+      text-align: center;
+      line-height: 48px;
+      height: 48px;
+      border-radius: 100px 0 0 100px;
+      cursor: pointer;
+      background: #46c37b;
+      color: #fff;
+      font-size: 18px;
+    }
+    .nav {
+      ul {
+        padding: 0;
+        margin: 0;
+        height: 56px;
+        list-style: none;
+        text-align: center;
+        border-bottom: 1px solid #f5f5f5;
+        font-size: 16px;
+        font-weight: 500;
+        color: #666;
+        line-height: 56px;
+        display: flex;
+        justify-content: space-around;
+        align-items: center;
+        position: relative;
+        cursor: pointer;
+        li {
+          position: relative;
+        }
+        .active {
+          &::after {
+            content: "";
+            display: block;
+            position: absolute;
+            bottom: 0;
+            left: 50%;
+            transform: translateX(-50%);
+            width: 36px;
+            height: 2px;
+            border-radius: 1px;
+            background: #46c37b;
+          }
+        }
+      }
+    }
+    .nav_item {
+      position: absolute;
+      top: 57px;
+      bottom: 0;
+      width: 100%;
+      // overflow-y: hidden;
+      overflow-x: hidden;
+      margin-bottom: 20px;
+      overflow-y: scroll;
+      .item_wrap {
+        margin-top: 24px;
+        ul {
+          list-style: none;
+          padding: 0;
+          margin-bottom: 0;
+          .out_wrap {
+            margin-bottom: 10px;
+            cursor: pointer;
+
+            .zhangjie {
+              display: flex;
+              justify-content: space-between;
+              background-color: #f5f5f5;
+              padding-right: 17px;
+
+              align-items: center;
+            }
+            .keshi {
+              li {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                margin-bottom: 10px;
+                cursor: pointer;
+                padding-right: 17px;
+                &:hover {
+                  background-color: #f3fee8;
+                }
+              }
+              .li-active {
+                background-color: #f3fee8;
+              }
+            }
+            .is_zhedie {
+              display: none;
+            }
+            i {
+              font-size: 18px;
+            }
+            .left {
+              padding: 0 0 0 17px;
+              display: flex;
+              align-items: center;
+              width: calc(100% - 60px);
+              .scrle {
+                display: inline-block;
+                width: 16px;
+                height: 16px;
+                border-radius: 50%;
+                border: 2px solid #999;
+                box-sizing: border-box;
+                flex-shrink: 0;
+              }
+              img {
+                width: 16px;
+                height: 16px;
+              }
+              .complate {
+                color: #46c37b;
+              }
+              .title {
+                display: inline-block;
+                padding: 15px 10px 15px 0;
+                margin-left: 20px;
+                // width: calc(100% - 20px);
+              }
+            }
+            .right {
+              display: flex;
+              align-items: center;
+              color: #999;
+              span {
+                margin-right: 5px;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  .chuxian {
+    right: 0px;
+  }
+}
+</style>