Ver código fonte

适应性培训课程详情

空白格 2 anos atrás
pai
commit
83195edf15

+ 62 - 20
src/api/AdaptiveTraining/index.js

@@ -3,11 +3,11 @@
  * @Author: 空白格
  * @Date: 2022-08-12 17:34:57
  * @LastEditors: 空白格
- * @LastEditTime: 2022-08-24 13:41:03
+ * @LastEditTime: 2022-08-25 11:39:24
  * @FilePath: \veterans_client_web\src\api\AdaptiveTraining\index.js
  * @Copyright: Copyright (c) 2016~2022 by 空白格, All Rights Reserved.
  */
-import request from '@/utils/request'
+import request from "@/utils/request";
 /**
  * 获取适应性培训列表
  * @param {*} params
@@ -15,13 +15,13 @@ import request from '@/utils/request'
  */
 export function getTrainingList(params) {
   return request({
-    url: '/app/adaptTrainOnline/queryList',
-    method: 'get',
+    url: "/app/adaptTrainOnline/queryList",
+    method: "get",
     headers: {
-      noLoginFlag: true
+      noLoginFlag: true,
     },
-    params
-  })
+    params,
+  });
 }
 
 /**
@@ -31,10 +31,52 @@ export function getTrainingList(params) {
  */
 export function getOnlineCourseDetails(params) {
   return request({
-    url: '/app/adaptTrainOnline/' + params.id,
-    method: 'get',
-    params
-  })
+    url: "/app/adaptTrainOnline/" + params.id,
+    method: "get",
+    params,
+  });
+}
+
+/**
+ * 获取课程评论
+ * @param {*} params
+ * @returns
+ */
+export function getCommentList(params) {
+  return request({
+    url: "/app/coursecomment",
+    method: "get",
+    headers: {
+      noLoginFlag: true,
+    },
+    params,
+  });
+}
+
+/**
+ * 添加课程评论
+ * @param {*} data
+ * @returns
+ */
+export function addComment(data) {
+  return request({
+    url: "/app/coursecomment",
+    method: "post",
+    data,
+  });
+}
+
+/**
+ * 视频播放时长记录
+ * @param {*} data
+ * @returns
+ */
+export function submitClassesDuration(data) {
+  return request({
+    url: "/app/record/duration/",
+    method: "post",
+    data,
+  });
 }
 
 /**
@@ -44,10 +86,10 @@ export function getOnlineCourseDetails(params) {
  */
 export function getTrainingTotal(params) {
   return request({
-    url: '/app/adaptTrainOnline/queryStatus',
-    method: 'get',
-    params
-  })
+    url: "/app/adaptTrainOnline/queryStatus",
+    method: "get",
+    params,
+  });
 }
 
 /**
@@ -57,11 +99,11 @@ export function getTrainingTotal(params) {
  */
 export function getOfflineTrainingList(params) {
   return request({
-    url: '/app/adaptTrainOffline/queryList',
-    method: 'get',
+    url: "/app/adaptTrainOffline/queryList",
+    method: "get",
     headers: {
-      noLoginFlag: true
+      noLoginFlag: true,
     },
-    params
-  })
+    params,
+  });
 }

+ 144 - 5
src/components/CourseVideo/index.vue

@@ -3,7 +3,7 @@
  * @Author: 空白格
  * @Date: 2022-08-24 13:48:47
  * @LastEditors: 空白格
- * @LastEditTime: 2022-08-24 15:12:30
+ * @LastEditTime: 2022-08-25 13:26:29
  * @FilePath: \veterans_client_web\src\components\CourseVideo\index.vue
  * @Copyright: Copyright (c) 2016~2022 by 空白格, All Rights Reserved.
 -->
@@ -25,8 +25,20 @@
     <div class="course-video-box">
       <el-row>
         <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
-          <div class="course-video-box-left">
-            <video :src="currentClasses.videoUrl"></video>
+          <div class="course-video-box-left" v-if="isPay">
+            <video
+              ref="video"
+              id="myVideo"
+              @timeupdate="timeUpdate"
+              :src="currentClasses.videoUrl"
+              controls
+              :initial-time="videoInfo.initial_time"
+              object-fit="fill"
+              play-btn-position="center"
+              @ended="ended"
+              @click="videoClick"
+              @loadedmetadata="loadedmetadata"
+            ></video>
           </div>
         </el-col>
         <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
@@ -45,9 +57,11 @@
                   v-for="(course, index) in courseDetails.chapterList"
                   :key="index"
                   timestamp=""
+                  :color="course.id === currentClasses.id ? '#EF651F' : ''"
                 >
                   <div
                     class="cvbr-bottom-list-item"
+                    :class="{ current: course.id === currentClasses.id }"
                     @click="chooseClasses(course)"
                   >
                     第{{ index + 1 }}课({{ course.finishPercent }}%)
@@ -63,6 +77,7 @@
 </template>
 
 <script>
+import { submitClassesDuration } from "@/api/AdaptiveTraining";
 export default {
   name: "CourseVideoIndex",
   props: {
@@ -74,11 +89,27 @@ export default {
   data() {
     return {
       currentClasses: {},
+      videoInfo: {
+        initial_time: 0,
+        duration: 0,
+        playedTime: 0,
+        video_real_time: 0,
+      },
+      isPay: false,
     };
   },
   watch: {
     "courseDetails.id"(val) {
       this.currentClasses = this.courseDetails.chapterList[0];
+      this.videoInfo.playedTime =
+        this.courseDetails.chapterList[0].playDuration;
+      this.isPay = true;
+      this.$nextTick(() => {
+        let video = document.getElementById("myVideo");
+        video.onpause = () => {
+          this.submitVideoDuration();
+        };
+      });
     },
   },
   methods: {
@@ -89,7 +120,100 @@ export default {
      * @returns {any}
      */
     chooseClasses(item) {
-      this.currentClasses = item;
+      this.isPay = false;
+      this.submitVideoDuration(item, true);
+    },
+    /**
+     * 视频播放实时触发
+     * @date 2022-08-24
+     * @param {any} e
+     * @returns {any}
+     */
+    timeUpdate(e) {
+      let detail = e.target;
+      //播放的总时长
+      let duration = detail.duration;
+      //实时播放进度 秒数
+      let jumpTime = parseInt(detail.currentTime);
+      //当前视频进度
+      if (jumpTime - this.videoInfo.playedTime > 3) {
+        // 差别过大,调用seek方法跳转到实际观看时间
+        this.$refs.video.currentTime = this.videoInfo.playedTime;
+        this.$message.warning("未完整看完该视频,不能快进!");
+      } else {
+        this.videoInfo.video_real_time = parseInt(detail.currentTime);
+        if (this.videoInfo.video_real_time > this.videoInfo.playedTime) {
+          this.videoInfo.playedTime = this.videoInfo.video_real_time;
+        }
+      }
+    },
+    /**
+     * 视频结束触发
+     * @date 2022-08-24
+     * @returns {any}
+     */
+    ended() {
+      // this.submitVideoDuration();
+    },
+    /**
+     * 视频点击触发
+     * @date 2022-08-24
+     * @param {any} e
+     * @returns {any}
+     */
+    videoClick(e) {
+      console.log(e);
+    },
+    /**
+     * 视频加载完成触发
+     * @date 2022-08-24
+     * @param {any} details
+     * @returns {any}
+     */
+    loadedmetadata(details) {
+      this.videoInfo.duration = details.target.duration;
+      this.videoInfo.initial_time = this.currentClasses.currentDuration;
+      this.$nextTick(() => {
+        this.$refs.video.currentTime = this.videoInfo.initial_time;
+      });
+    },
+    /**
+     * 提交视频播放进度
+     * @date 2022-08-24
+     * @returns {any}
+     */
+    submitVideoDuration(item, isNeed) {
+      let playDuration = this.videoInfo.video_real_time,
+        currentDuration = this.videoInfo.video_real_time;
+      if (this.videoInfo.playDuration > this.videoInfo.video_real_time) {
+        playDuration = this.videoInfo.playDuration;
+      }
+      // 防止视频未加载完成就提交
+      if (Number(this.videoInfo.duration) > 0 && Number(playDuration) > 0) {
+        submitClassesDuration({
+          tabId: this.currentClasses.id,
+          playDuration: playDuration,
+          currentDuration: currentDuration,
+          duration: this.videoInfo.duration,
+        }).then((res) => {
+          if (res.code === 200) {
+            console.log("已记录播放时长" + JSON.stringify(res.data));
+            if (isNeed) {
+              this.$emit("changeClasses", item);
+              this.currentClasses = item;
+              this.isPay = true;
+              this.videoInfo.playedTime = item.playDuration;
+              this.videoInfo.initial_time = item.currentDuration;
+              this.$nextTick(() => {
+                let video = document.getElementById("myVideo");
+                video.onpause = () => {
+                  this.submitVideoDuration();
+                };
+              });
+            }
+          }
+        });
+      }
     },
   },
 };
@@ -97,7 +221,7 @@ export default {
 
 <style lang="scss" scoped>
 .course-video {
-  padding: 10px 0;
+  padding: 10px 0 40px;
   &-header {
     display: flex;
     justify-content: space-between;
@@ -141,9 +265,24 @@ export default {
         color: #fff;
         &-teacher {
           font-size: 20px;
+          margin-bottom: 10px;
         }
         &-des {
           font-size: 14px;
+          line-height: 25px;
+        }
+      }
+      .cvbr-bottom {
+        padding: 20px 30px;
+        &-list {
+          &-item {
+            color: #fff;
+            cursor: pointer;
+            font-size: 14px;
+          }
+          .current {
+            color: #ef651f;
+          }
         }
       }
     }

+ 1 - 1
src/views/AdaptiveTraining/AdaptiveTrainingIndex.vue

@@ -3,7 +3,7 @@
  * @Author: 空白格
  * @Date: 2022-08-12 15:23:44
  * @LastEditors: 空白格
- * @LastEditTime: 2022-08-24 10:56:08
+ * @LastEditTime: 2022-08-25 09:40:14
  * @FilePath: \veterans_client_web\src\views\AdaptiveTraining\AdaptiveTrainingIndex.vue
  * @Copyright: Copyright (c) 2016~2022 by 空白格, All Rights Reserved.
 -->

+ 282 - 3
src/views/AdaptiveTraining/OnlineCourseDetails/OnlineCourseDetailsIndex.vue

@@ -3,7 +3,7 @@
  * @Author: 空白格
  * @Date: 2022-08-24 10:44:26
  * @LastEditors: 空白格
- * @LastEditTime: 2022-08-24 14:01:39
+ * @LastEditTime: 2022-08-25 11:45:37
  * @FilePath: \veterans_client_web\src\views\AdaptiveTraining\OnlineCourseDetails\OnlineCourseDetailsIndex.vue
  * @Copyright: Copyright (c) 2016~2022 by 空白格, All Rights Reserved.
 -->
@@ -25,15 +25,113 @@
     </div>
     <div class="course-details-video">
       <div class="course-details-video-box">
-        <course-video :courseDetails="detailsInfo"/>
+        <course-video
+          ref="courseVideo"
+          :courseDetails="detailsInfo"
+          @changeClasses="changeClasses"
+        />
       </div>
     </div>
+    <div class="course-details-comment" v-loading="commentObj.loading">
+      <el-row :gutter="11">
+        <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
+          <div class="course-details-comment-content">
+            <div class="cdcc-header">
+              <div class="cdcc-header-title">课程评论</div>
+              <div class="cdcc-header-total">
+                共{{ commentObj.total || 0 }}条评论
+              </div>
+            </div>
+            <div class="cdcc-list" v-if="commentObj.list.length">
+              <div
+                class="cdcc-list-item"
+                v-for="(item, index) in commentObj.list"
+                :key="index"
+              >
+                <div class="cdcc-list-item-left">
+                  <el-avatar
+                    shape="square"
+                    :size="77"
+                    :src="item.createByAvatar"
+                  ></el-avatar>
+                </div>
+                <div class="cdcc-list-item-right">
+                  <div class="clir-item">{{ item.createBy }}</div>
+                  <div class="clir-item">
+                    <div>
+                      <el-rate
+                        v-model="item.starLevel"
+                        :disabled="true"
+                        :colors="['#C4C4C4', '#C4C4C4', '#C4C4C4']"
+                      ></el-rate>
+                    </div>
+                    <div class="date">{{ item.createTime }}</div>
+                  </div>
+                  <div class="clir-item">{{ item.content }}</div>
+                </div>
+              </div>
+              <div class="pagination" v-if="commentObj.total">
+                <el-pagination
+                  background
+                  layout="prev, pager, next"
+                  :page-size="commentObj.queryParams.pageSize"
+                  :total="commentObj.total"
+                  @current-change="currentChange"
+                />
+              </div>
+            </div>
+            <div class="empty" v-else>
+              <el-empty description="评论数据为空"></el-empty>
+            </div>
+          </div>
+        </el-col>
+        <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
+          <div class="course-details-comment-submit">
+            <div class="cdcs-title">我的评论</div>
+            <div class="cdcs-form">
+              <el-form
+                ref="form"
+                :model="commentObj.form"
+                :rules="commentObj.rules"
+              >
+                <el-form-item prop="starLevel">
+                  <el-rate v-model="commentObj.form.starLevel"></el-rate>
+                </el-form-item>
+                <el-form-item prop="content">
+                  <el-input
+                    type="textarea"
+                    :rows="6"
+                    placeholder="请输入您的评价"
+                    v-model="commentObj.form.content"
+                  >
+                  </el-input>
+                </el-form-item>
+                <el-form-item>
+                  <div class="cdcs-form-btn">
+                    <el-button
+                      class="cdcs-form-btn-sub"
+                      @click="submitComment"
+                      :loading="commentObj.subLoading"
+                      >提交</el-button
+                    >
+                  </div>
+                </el-form-item>
+              </el-form>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
   </div>
 </template>
 
 <script>
 import CourseVideo from "@/components/CourseVideo";
-import { getOnlineCourseDetails } from "@/api/AdaptiveTraining";
+import {
+  getOnlineCourseDetails,
+  getCommentList,
+  addComment,
+} from "@/api/AdaptiveTraining";
 export default {
   name: "OnlineCourseDetailsIndex",
   components: {
@@ -45,6 +143,28 @@ export default {
       classesId: null,
       detailsInfo: {},
       currentClasses: {},
+      commentObj: {
+        total: 0,
+        list: [],
+        loading: false,
+        subLoading: false,
+        queryParams: {
+          pageNum: 1,
+          pageSize: 10,
+          tabId: undefined,
+        },
+        form: {
+          tabId: "",
+          starLevel: 0,
+          content: "",
+        },
+        rules: {
+          starLevel: [{ required: true, message: "请评分", trigger: "change" }],
+          content: [
+            { required: true, message: "请输入您的评价", trigger: "blur" },
+          ],
+        },
+      },
     };
   },
   created() {
@@ -52,7 +172,10 @@ export default {
     const { id } = this.$route.query;
     if (id) {
       this.classesId = id;
+      this.commentObj.queryParams.tabId = id;
+      this.commentObj.form.tabId = id;
       this.getDetails();
+      this.getCommentList();
     }
   },
   methods: {
@@ -69,6 +192,66 @@ export default {
         }
       });
     },
+    /**
+     * 切换章节触发
+     * @date 2022-08-25
+     * @returns {any}
+     */
+    changeClasses() {
+      this.getDetails();
+    },
+    /**
+     * 获取课程评论
+     * @date 2022-08-25
+     * @returns {any}
+     */
+    getCommentList() {
+      this.commentObj.loading = true
+      getCommentList(this.commentObj.queryParams).then((res) => {
+        if (res.code === 200) {
+          this.commentObj.list = res?.rows;
+          this.commentObj.total = Number(res?.total);
+        }
+        this.commentObj.loading = false
+      }).catch(() => {
+        this.commentObj.loading = false
+      })
+    },
+    /**
+     * 分页切换触发
+     * @date 2022-08-25
+     * @param {any} page
+     * @returns {any}
+     */
+    currentChange(page) {
+      this.commentObj.queryParams.pageNum = page;
+      this.getCommentList();
+    },
+    /**
+     * 提交评论
+     * @date 2022-08-25
+     * @returns {any}
+     */
+    submitComment() {
+      this.$refs["form"].validate((valid) => {
+        if (valid) {
+          this.commentObj.subLoading = true
+          addComment(this.commentObj.form).then((res) => {
+            if (res.code === 200) {
+              this.$message.success("评论成功!");
+              this.$refs["form"].resetFields();
+              this.commentObj.queryParams.pageNum = 1;
+              this.getCommentList();
+            } else {
+              this.$message.error(res.msg || "评论失败!");
+            }
+            this.commentObj.subLoading = false
+          }).catch(() => {
+            this.commentObj.subLoading = false
+          })
+        }
+      });
+    },
     /**
      * 根据路由获取面包屑
      * @date 2022-08-24
@@ -96,6 +279,9 @@ export default {
       }
     },
   },
+  beforeDestroy() {
+    this.$refs["courseVideo"].submitVideoDuration();
+  },
 };
 </script>
 
@@ -125,5 +311,98 @@ export default {
       margin: 0 auto;
     }
   }
+  &-comment {
+    width: 70%;
+    min-width: 600px;
+    margin: 16px auto 0;
+    &-content {
+      background: #fff;
+      padding: 30px 20px;
+      .cdcc-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        border-bottom: solid 1px #cbcbcb;
+        padding-bottom: 10px;
+        &-title {
+          color: #1a1a1a;
+          font-size: 20px;
+          font-weight: 500;
+        }
+        &-total {
+          color: #8d8d8d;
+          font-size: 14px;
+        }
+      }
+      .cdcc-list {
+        padding: 30px 0 0;
+        &-item {
+          display: flex;
+          margin-bottom: 22px;
+          &-left {
+            margin-right: 14px;
+          }
+          &-right {
+            .clir-item {
+              display: flex;
+              font-size: 14px;
+              margin-bottom: 5px;
+              :deep(.el-rate__icon) {
+                margin-right: 2px;
+              }
+              .date {
+                color: #a5a5a5;
+                margin-left: 30px;
+                font-size: 13px;
+              }
+              &:first-child {
+                color: #3d3d3d;
+                font-size: 16px;
+              }
+              &:last-child {
+                color: #6c6c6c;
+              }
+            }
+          }
+        }
+        .pagination {
+          text-align: center;
+          margin-top: 20px;
+          :deep(.el-pager .active) {
+            background-color: #ff3939;
+          }
+          :deep(.el-pagination.is-background
+              .el-pager
+              li:not(.disabled).active) {
+            background-color: #ff3939;
+          }
+        }
+      }
+      .empty {
+        padding: 30px 0;
+      }
+    }
+    &-submit {
+      background: #fff;
+      padding: 30px 20px;
+      .cdcs-title {
+        font-size: 20px;
+        color: #1a1a1a;
+        margin-bottom: 14px;
+      }
+      .cdcs-form {
+        &-btn {
+          width: 100%;
+          text-align: center;
+          &-sub {
+            width: 80%;
+            background-color: #709078;
+            color: #fff;
+            border-radius: 30px;
+          }
+        }
+      }
+    }
+  }
 }
 </style>