dusenyao 3 سال پیش
والد
کامیت
da5f2a2266

+ 62 - 2
src/api/course.js

@@ -351,8 +351,68 @@ export function DeleteLearningMaterialFromCSItem(data) {
  * 填写任务执行信息(学员填写)
  * @param {Object} data
  */
-export function FillTaskExecuteInfo_Student(data) {
-  let params = getRequestParams('teaching-task_manager-FillTaskExecuteInfo_Student');
+export function FillMyTaskExecuteInfo_Student(data) {
+  let params = getRequestParams('teaching-task_manager-FillMyTaskExecuteInfo_Student');
+
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_LearnWebSI,
+    params,
+    data
+  });
+}
+
+/**
+ * 完成任务资料(学员填写)
+ * @param {Object} data
+ */
+export function FinishMyTaskMaterial_Student(data) {
+  let params = getRequestParams('teaching-task_manager-FinishMyTaskMaterial_Student');
+
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_LearnWebSI,
+    params,
+    data
+  });
+}
+
+/**
+ * 得到任务的学员执行情况
+ * @param {Object} data {task_id 任务ID, student_id 学员ID}
+ */
+export function GetTaskStudentExecuteInfo(data) {
+  let params = getRequestParams('teaching-task_manager-GetTaskStudentExecuteInfo');
+
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_LearnWebSI,
+    params,
+    data
+  });
+}
+
+/**
+ * 得到任务资料的学员完成答案
+ * @param {Object} data
+ */
+export function GetTaskMaterialStudentExamAnswer(data) {
+  let params = getRequestParams('teaching-task_manager-GetTaskMaterialStudentExamAnswer');
+
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_LearnWebSI,
+    params,
+    data
+  });
+}
+
+/**
+ * 点评任务的学员执行情况(教师点评)
+ * @param {Object} data
+ */
+export function RemarkTaskStudentExecuteInfo_Teacher(data) {
+  let params = getRequestParams('teaching-task_manager-RemarkTaskStudentExecuteInfo_Teacher');
 
   return request({
     method: 'post',

BIN
src/assets/course_details/courseware.png


+ 141 - 0
src/components/course/CompletionView.vue

@@ -0,0 +1,141 @@
+<template>
+  <el-dialog :visible="dialogVisible" width="820px" @close="dialogClose">
+    <div class="completion">
+      <img src="../../assets/course_details/courseware.png" />
+      <div class="completion-info">
+        <div>{{ material_name }}</div>
+        <div>
+          {{ student_name }} {{ finish_time_view_txt }} 用时:{{ duration }}秒 | 正确
+          {{ count_right }} | 错误 {{ count_error }}
+        </div>
+      </div>
+    </div>
+
+    <bookreport
+      v-if="dialogVisible"
+      :context="context"
+      :book-client-width="800"
+      :book-answer-content="bookAnswerContent"
+    />
+
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import { GetTaskMaterialStudentExamAnswer, GetCoursewareContent_View } from '@/api/course';
+
+export default {
+  props: {
+    dialogVisible: {
+      default: false,
+      type: Boolean
+    },
+    curCoursewareId: {
+      default: '',
+      type: String
+    },
+    taskId: {
+      default: '',
+      type: String
+    },
+    curStudentId: {
+      default: '',
+      type: String
+    }
+  },
+  data() {
+    return {
+      student_id: '',
+      student_name: '',
+      material_name: '',
+      finish_time_view_txt: '',
+      duration: 0,
+      count_not_done: 0,
+      count_right: 0,
+      count_error: 0,
+      context: null,
+      bookAnswerContent: ''
+    };
+  },
+  watch: {
+    curCoursewareId(newVal) {
+      if (!newVal) {
+        this.context = null;
+        this.bookAnswerContent = '';
+        return;
+      }
+      this.getTaskMaterialStudentExamAnswer().then(() => {
+        GetCoursewareContent_View({ id: this.curCoursewareId }).then(({ content }) => {
+          if (content) {
+            this.context = {
+              id: this.curCoursewareId,
+              ui_type: JSON.parse(content).question.ui_type,
+              content: JSON.parse(content)
+            };
+          } else {
+            this.context = null;
+          }
+        });
+      });
+    }
+  },
+  methods: {
+    getTaskMaterialStudentExamAnswer() {
+      return GetTaskMaterialStudentExamAnswer({
+        material_id: this.curCoursewareId,
+        task_id: this.taskId,
+        material_type: 'COURSEWARE',
+        student_id: this.curStudentId
+      }).then(
+        ({
+          duration,
+          count_not_done,
+          count_right,
+          count_error,
+          content,
+          student_id,
+          student_name,
+          material_name,
+          finish_time_view_txt
+        }) => {
+          this.material_name = material_name;
+          this.finish_time_view_txt = finish_time_view_txt;
+          this.student_id = student_id;
+          this.student_name = student_name;
+          this.duration = duration;
+          this.count_not_done = count_not_done;
+          this.count_right = count_right;
+          this.count_error = count_error;
+          this.bookAnswerContent = content;
+        }
+      );
+    },
+
+    dialogClose() {
+      this.$emit('dialogClose');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.completion {
+  display: flex;
+  padding: 0 32px 24px;
+
+  &-info {
+    margin-left: 24px;
+
+    > div {
+      height: 20px;
+      line-height: 20px;
+
+      &:first-child {
+        font-size: 18px;
+        font-weight: bold;
+      }
+    }
+  }
+}
+</style>

+ 100 - 0
src/components/course/FinishCourseware.vue

@@ -0,0 +1,100 @@
+<template>
+  <el-dialog
+    class="finish-courseware"
+    :visible="dialogVisible"
+    width="860px"
+    title="完成课件"
+    :close-on-click-modal="false"
+    @close="dialogClose"
+  >
+    <bookquestion
+      ref="courseware"
+      :context="context"
+      @handleBookUserAnswer="handleBookUserAnswer"
+    />
+
+    <div slot="footer">
+      <el-button type="primary" @click="finishTaskMaterial">完成</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { GetCoursewareContent_View, FinishMyTaskMaterial_Student } from '@/api/course';
+
+export default {
+  props: {
+    dialogVisible: {
+      default: false,
+      type: Boolean
+    },
+    id: {
+      default: '',
+      type: String
+    },
+    coursewareId: {
+      default: '',
+      type: String
+    }
+  },
+  data() {
+    return {
+      context: null,
+      exam_answer: ''
+    };
+  },
+  watch: {
+    coursewareId(val) {
+      if (!val) {
+        this.context = null;
+        this.exam_answer = '';
+        return;
+      }
+
+      GetCoursewareContent_View({ id: this.coursewareId }).then(({ content }) => {
+        if (content) {
+          this.context = {
+            id: this.coursewareId,
+            ui_type: JSON.parse(content).question.ui_type,
+            content: JSON.parse(content)
+          };
+          this.$nextTick(() => {
+            this.$refs.courseware.handleAnswerTimeStart();
+          });
+        } else {
+          this.context = null;
+        }
+      });
+    }
+  },
+  methods: {
+    finishTaskMaterial() {
+      FinishMyTaskMaterial_Student({
+        task_id: this.id,
+        material_type: 'COURSEWARE',
+        material_id: this.coursewareId,
+        exam_answer: this.exam_answer
+      }).then(() => {
+        this.$message.success('完成课件成功');
+        this.dialogClose();
+      });
+    },
+
+    handleBookUserAnswer(data) {
+      this.exam_answer = data;
+    },
+
+    dialogClose() {
+      this.$emit('dialogClose');
+    }
+  }
+};
+</script>
+
+<style lang="scss">
+@import '~@/styles/mixin.scss';
+
+.finish-courseware {
+  @include dialog;
+}
+</style>

+ 1 - 1
src/components/live/CurMaterial.vue

@@ -123,7 +123,7 @@ export default {
       GetCoursewareContent_View({ id: this.materialId }).then(({ content }) => {
         if (content) {
           this.context = {
-            id: this.currentCourse,
+            id: this.materialId,
             ui_type: JSON.parse(content).question.ui_type,
             content: JSON.parse(content)
           };

+ 5 - 0
src/icons/svg/check-mark-circle.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 0H0V24H24V0Z" fill="white" fill-opacity="0.01"/>
+<path d="M12 22C14.7614 22 17.2614 20.8807 19.0711 19.0711C20.8807 17.2614 22 14.7614 22 12C22 9.2386 20.8807 6.7386 19.0711 4.92893C17.2614 3.11929 14.7614 2 12 2C9.2386 2 6.7386 3.11929 4.92893 4.92893C3.11929 6.7386 2 9.2386 2 12C2 14.7614 3.11929 17.2614 4.92893 19.0711C6.7386 20.8807 9.2386 22 12 22Z" stroke="#2ECE5B" stroke-width="1.5" stroke-linejoin="round"/>
+<path d="M8 12L11 15L17 9" stroke="#2ECE5B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 4 - 0
src/icons/svg/check-mark.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 0H0V24H24V0Z" fill="white" fill-opacity="0.01"/>
+<path d="M5 12L10 17L20 7" stroke="#3ACB85" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 5 - 0
src/icons/svg/courseware.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 0H0V24H24V0Z" fill="white" fill-opacity="0.01"/>
+<path d="M14 6V2L4 7V21L10 18" stroke="#FFAC2F" stroke-width="1.5" stroke-linejoin="round"/>
+<path d="M10 8L20 3V17L10 22V8Z" stroke="#FFAC2F" stroke-width="1.5" stroke-linejoin="round"/>
+</svg>

+ 4 - 0
src/router/index.js

@@ -107,6 +107,10 @@ const routes = [
       {
         path: '/task_detail/student/:id',
         component: () => import('@/views/task_details/student')
+      },
+      {
+        path: '/task_detail/teacher/:id',
+        component: () => import('@/views/task_details/teacher')
       }
     ]
   },

+ 4 - 0
src/styles/index.scss

@@ -29,6 +29,10 @@ body {
 ul {
   padding-inline-start: 0;
   margin: 0;
+
+  li {
+    list-style: none;
+  }
 }
 
 #app {

+ 1 - 1
src/styles/mixin.scss

@@ -61,7 +61,7 @@
 
     &__body {
       padding: 15px 20px 0;
-      height: 55vh;
+      max-height: 80vh;
       color: $color;
       overflow: auto;
     }

+ 18 - 5
src/views/live/common.js

@@ -72,21 +72,21 @@ export function publishStream(streamName) {
 /**
  * 创建本地流
  */
-export function createLocalStream() {
+export function createLocalStream(streamName) {
   const createData = {
     video: true,
     audio: true
   };
   rtc.createLocalStream({
-    streamName: 'main',
+    streamName,
     createData,
-    success: function (stream) {
+    success(stream) {
       console.log('创建本地流成功', stream);
       // 创建本地流成功,将流展示到id为 live 的dom元素盒子中
       stream.show('live');
-      publishStream('main'); // 如果需要立即推流,执行 publish 方法
+      publishStream(streamName); // 如果需要立即推流,执行 publish 方法
     },
-    fail: function (data) {
+    fail(data) {
       // 创建本地流失败,应用层处理
       Message({
         type: 'error',
@@ -111,6 +111,19 @@ export function closeVideo(streamName) {
   });
 }
 
+// 重连
+export function reconnection() {
+  rtc.closeVideo({
+    streamName: 'main',
+    success() {
+      createLocalStream('main');
+    },
+    fail(str) {
+      console.log(str);
+    }
+  });
+}
+
 /**
  * 关闭本地流声音
  */

+ 5 - 1
src/views/live/teacher/index.vue

@@ -9,7 +9,7 @@
           <el-button v-if="liveStat" icon="el-icon-switch-button" @click="closeLiveRoom">
             结束直播
           </el-button>
-          <!-- <el-button v-if="liveStat" @click="publishStream">推流</el-button> -->
+          <el-button v-if="liveStat" @click="reconnection">重连</el-button>
         </div>
       </div>
       <div class="live-course-name">{{ roomInfo.course_name }}</div>
@@ -404,6 +404,10 @@ export default {
       common.unPubShareStream();
     },
 
+    reconnection() {
+      common.reconnection();
+    },
+
     // 老师邀请学生上麦
     invite(student, mode) {
       if (this.connect || this.callLoading) {

+ 6 - 5
src/views/live/teacher/live.js

@@ -14,7 +14,8 @@ export {
   sendPublishMessage,
   closeVideo,
   chatRoll,
-  updateMcResult
+  updateMcResult,
+  reconnection
 } from '@/views/live/common';
 
 /**
@@ -47,13 +48,13 @@ function createLocalStream() {
   rtc.createLocalStream({
     streamName: 'main',
     createData,
-    success: function (stream) {
+    success(stream) {
       console.log('创建本地流成功', stream);
       // 创建本地流成功,将流展示到id为 live 的 dom 元素盒子中
       stream.show('live');
       publishStream('main'); // 如果需要立即推流,执行 publish 方法
     },
-    fail: function (data) {
+    fail(data) {
       // 创建本地流失败,应用层处理
       Message({
         type: 'error',
@@ -306,10 +307,10 @@ export function stopLive() {
  */
 export function publishShareStream() {
   rtc.publishShareStream({
-    success: function (stream) {
+    success: stream => {
       console.log('推送桌面共享成功', stream);
     },
-    fail: function (str) {
+    fail: str => {
       console.log(str);
     }
   });

+ 3 - 3
src/views/main/TaskList.vue

@@ -116,10 +116,10 @@ export default {
             query: { live_room_sys_user_id, room_id, session_id, task_id, room_user_id }
           });
         });
-      } else if (userType === 'STUDENT') {
-        this.$router.push(`/task_detail/student/${task_id}`);
       } else {
-        this.$message.warning('暂未实现');
+        this.$router.push(
+          `/task_detail/${userType === 'STUDENT' ? 'student' : 'teacher'}/${task_id}`
+        );
       }
     },
     dateSkip(num) {

+ 44 - 13
src/views/task_details/student/index.vue

@@ -2,10 +2,10 @@
   <div class="task-detail">
     <div class="task-detail-top">
       <div class="title">
-        <span class="title-name">{{ name }}</span>
+        <span class="title-name">{{ course_name }}</span>
         <span class="title-time">{{ time_space_view_txt }}</span>
       </div>
-      <div class="course_name">{{ course_name }}</div>
+      <div class="cs_item_name">{{ cs_item_name }}</div>
       <div class="learning-material">
         <div class="learning-material-title">学习资料</div>
         <el-tag
@@ -29,8 +29,9 @@
           :key="item.courseware_id"
           color="#fff"
           :title="item.courseware_name"
+          @click="finishTask(item.courseware_id)"
         >
-          <span>{{ item.courseware_name }}</span>
+          <svg-icon icon-class="courseware" /> <span>{{ item.courseware_name }}</span>
         </el-tag>
       </div>
       <div class="accessory-list">
@@ -70,31 +71,44 @@
         <el-button type="primary" @click="fillTaskExecuteInfo_Student">完成任务</el-button>
       </div>
     </div>
+
+    <finish-courseware
+      :id="id"
+      :courseware-id="coursewareId"
+      :dialog-visible="dialogVisible"
+      @dialogClose="dialogClose"
+    />
   </div>
 </template>
 
 <script>
+import FinishCourseware from '@/components/course/FinishCourseware.vue';
 import { fileUpload, FileDownload } from '@/api/app';
-import { GetTaskInfo, FillTaskExecuteInfo_Student } from '@/api/course';
+import { GetTaskInfo, FillMyTaskExecuteInfo_Student } from '@/api/course';
 
 export default {
+  components: {
+    FinishCourseware
+  },
   data() {
     return {
+      // 任务 ID
       id: this.$route.params.id,
       student_message: '',
       name: '',
       teaching_type: '',
       time_type: '',
       course_name: '',
+      cs_item_name: '',
       content: '',
-      begin_time: '',
-      end_time: '',
       task_mode: '',
       time_space_view_txt: '',
       courseware_list: [],
       accessory_list: [],
       cs_item_learning_material_list: [],
-      file_list: []
+      file_list: [],
+      dialogVisible: false,
+      coursewareId: ''
     };
   },
   computed: {
@@ -121,8 +135,7 @@ export default {
         teaching_type,
         time_type,
         course_name,
-        begin_time,
-        end_time,
+        cs_item_name,
         courseware_list,
         accessory_list,
         cs_item_learning_material_list,
@@ -132,11 +145,10 @@ export default {
       }) => {
         this.name = name;
         this.course_name = course_name;
+        this.cs_item_name = cs_item_name;
         this.content = content;
         this.teaching_type = teaching_type;
         this.time_type = time_type;
-        this.begin_time = begin_time;
-        this.end_time = end_time;
         this.courseware_list = courseware_list;
         this.accessory_list = accessory_list;
         this.task_mode = task_mode;
@@ -170,7 +182,7 @@ export default {
       this.file_list.forEach(item => {
         homework_file_id_list.push(item.file_id);
       });
-      FillTaskExecuteInfo_Student({
+      FillMyTaskExecuteInfo_Student({
         task_id: this.id,
         homework_file_id_list,
         student_message: this.student_message,
@@ -178,6 +190,16 @@ export default {
       }).then(() => {
         this.$message.success('任务完成成功');
       });
+    },
+
+    // 完成任务
+    finishTask(id) {
+      this.coursewareId = id;
+      this.dialogVisible = true;
+    },
+    dialogClose() {
+      this.dialogVisible = false;
+      this.coursewareId = '';
     }
   }
 };
@@ -218,7 +240,7 @@ $bor-color: #d9d9d9;
       }
     }
 
-    .course_name {
+    .cs_item_name {
       margin: 8px 0 24px;
     }
 
@@ -281,6 +303,15 @@ $bor-color: #d9d9d9;
       }
     }
 
+    .task-courseware .el-tag {
+      cursor: pointer;
+
+      .svg-icon {
+        font-size: 18px;
+        margin-right: 8px;
+      }
+    }
+
     .task-require,
     .task-courseware,
     .accessory-list,

+ 426 - 0
src/views/task_details/teacher/index.vue

@@ -0,0 +1,426 @@
+<template>
+  <div class="teacher-task-detail">
+    <div class="teacher-task-detail-top">
+      <div class="title">
+        <span class="title-name">{{ course_name }}</span>
+        <span class="title-time">{{ time_space_view_txt }}</span>
+      </div>
+      <div class="course_name">{{ cs_item_name }}</div>
+      <div class="learning-material">
+        <div class="learning-material-title">学习资料</div>
+        <el-tag
+          v-for="item in cs_item_learning_material_list"
+          :key="item.file_id"
+          color="#fff"
+          :title="item.file_name"
+        >
+          <a :href="item.file_url" target="_blank">{{ item.file_name }}</a>
+        </el-tag>
+      </div>
+    </div>
+    <div class="teacher-task-detail-main">
+      <div class="student-finish-situation">
+        <div>学员完成情况</div>
+        <div class="student-list">
+          <ul>
+            <li
+              v-for="item in student_list"
+              :key="item.student_id"
+              :class="['student-item', { active: item.student_id === curStudentId }]"
+              @click="getTaskStudentExecuteInfo(item.student_id)"
+            >
+              <span>{{ item.student_name }}</span>
+              <svg-icon v-if="item.is_finished === 'true'" icon-class="check-mark" />
+            </li>
+          </ul>
+        </div>
+      </div>
+      <div class="finish-detail">
+        <div class="student-info">
+          <div>
+            <el-avatar
+              :src="curFinishDetail.student_image_url"
+              :size="32"
+              icon="el-icon-user-solid"
+            />
+            <span class="student-info-name">{{ curFinishDetail.student_name }}</span>
+          </div>
+          <span class="finish-time">{{ curFinishDetail.finish_time_view_txt }}</span>
+        </div>
+        <div class="finish-situation">
+          <div class="title">课件任务</div>
+          <div class="courseware-list">
+            <el-tag
+              v-for="item in curFinishDetail.courseware_list"
+              :key="item.courseware_id"
+              color="#fff"
+              :title="item.courseware_name"
+              @click="showCompletionView(item.courseware_id, item.is_finished)"
+            >
+              <div class="courseware">
+                <svg-icon icon-class="courseware" />
+                <span class="courseware_name">{{ item.courseware_name }}</span>
+                <svg-icon
+                  v-if="item.is_finished === 'true'"
+                  class="check-mark"
+                  icon-class="check-mark-circle"
+                />
+              </div>
+            </el-tag>
+          </div>
+          <div class="title">文档列表</div>
+          <div>
+            <el-tag
+              v-for="item in curFinishDetail.homework_list"
+              :key="item.file_id"
+              color="#fff"
+              :title="item.file_name"
+            >
+              <a :href="item.file_url" target="_blank">{{ item.file_name }}</a>
+            </el-tag>
+          </div>
+          <div class="title">学生留言</div>
+          <div>{{ curFinishDetail.student_message }}</div>
+          <div class="title">
+            <span>教师点评</span>
+            <el-rate v-model="teacher_score"></el-rate>
+          </div>
+          <div>
+            <el-input v-model="teacher_remark" type="textarea" resize="none" :rows="6"></el-input>
+          </div>
+          <div class="confirm">
+            <el-button type="primary" @click="remarkTaskStudentExecuteInfo_Teacher">提交</el-button>
+            <el-button v-if="student_list.length > 1" @click="next">
+              {{ buttonName() }} <i class="el-icon-right" />
+            </el-button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <completion-view
+      :task-id="id"
+      :cur-student-id="curStudentId"
+      :cur-courseware-id="curCoursewareId"
+      :dialog-visible="dialogVisible"
+      @dialogClose="dialogClose"
+    />
+  </div>
+</template>
+
+<script>
+import CompletionView from '@/components/course/CompletionView.vue';
+import {
+  GetTaskInfo,
+  GetTaskStudentExecuteInfo,
+  RemarkTaskStudentExecuteInfo_Teacher
+} from '@/api/course';
+
+export default {
+  components: { CompletionView },
+  data() {
+    return {
+      id: this.$route.params.id,
+      name: '',
+      teaching_type: '',
+      time_type: '',
+      course_name: '',
+      cs_item_name: '',
+      task_mode: '',
+      time_space_view_txt: '',
+      courseware_list: [],
+      accessory_list: [],
+      cs_item_learning_material_list: [],
+      file_list: [],
+      student_list: [],
+      curStudentId: '',
+      // 当前学生完成详情
+      curFinishDetail: {
+        student_name: '',
+        student_image_url: '',
+        courseware_list: [],
+        homework_list: [],
+        student_message: '',
+        finish_time_view_txt: ''
+      },
+      teacher_remark: '',
+      teacher_score: 0,
+      dialogVisible: false,
+      curCoursewareId: ''
+    };
+  },
+  created() {
+    GetTaskInfo({
+      id: this.id,
+      is_contain_cs_item_learning_material: true,
+      is_contain_student: true
+    }).then(
+      ({
+        name,
+        teaching_type,
+        time_type,
+        course_name,
+        courseware_list,
+        cs_item_name,
+        accessory_list,
+        cs_item_learning_material_list,
+        task_mode,
+        time_space_view_txt,
+        student_list
+      }) => {
+        this.name = name;
+        this.course_name = course_name;
+        this.cs_item_name = cs_item_name;
+        this.teaching_type = teaching_type;
+        this.time_type = time_type;
+        this.courseware_list = courseware_list;
+        this.accessory_list = accessory_list;
+        this.task_mode = task_mode;
+        this.cs_item_learning_material_list = cs_item_learning_material_list;
+        this.time_space_view_txt = time_space_view_txt;
+        this.student_list = student_list;
+        if (student_list.length > 0) this.getTaskStudentExecuteInfo(student_list[0].student_id);
+      }
+    );
+  },
+  methods: {
+    getTaskStudentExecuteInfo(student_id) {
+      GetTaskStudentExecuteInfo({
+        task_id: this.id,
+        student_id
+      }).then(
+        ({
+          courseware_list,
+          homework_list,
+          student_message,
+          student_name,
+          student_image_url,
+          finish_time_view_txt
+        }) => {
+          this.curStudentId = student_id;
+          this.teacher_remark = '';
+          this.teacher_score = 0;
+          this.curFinishDetail = {
+            courseware_list,
+            homework_list,
+            student_message,
+            student_name,
+            student_image_url,
+            finish_time_view_txt
+          };
+        }
+      );
+    },
+
+    showCompletionView(id, is_finished) {
+      if (is_finished === 'false') return this.$message.warning('该课件没有被完成');
+      this.curCoursewareId = id;
+      this.dialogVisible = true;
+    },
+
+    dialogClose() {
+      this.curCoursewareId = '';
+      this.dialogVisible = false;
+    },
+
+    buttonName() {
+      let list = this.student_list;
+      if (list.length <= 0) return '';
+      return list[list.length - 1].student_id === this.curStudentId ? '上一个' : '下一个';
+    },
+
+    remarkTaskStudentExecuteInfo_Teacher() {
+      RemarkTaskStudentExecuteInfo_Teacher({
+        task_id: this.id,
+        student_id: this.curStudentId,
+        teacher_score: this.teacher_score,
+        teacher_remark: this.teacher_remark
+      }).then(() => {
+        this.$message.success('点评成功');
+      });
+    },
+
+    next() {
+      let list = this.student_list;
+      if (list.length <= 0) {
+        return this.$message.warning('当前学生列表为空');
+      }
+      let curIndex = list.findIndex(item => item.student_id === this.curStudentId);
+      let nextList = list.length - 1 === curIndex ? list[curIndex - 1] : list[curIndex + 1];
+      if (nextList) this.getTaskStudentExecuteInfo(nextList.student_id);
+    }
+  }
+};
+</script>
+
+<style lang="scss">
+@import '~@/styles/mixin.scss';
+$bor-color: #d9d9d9;
+
+.teacher-task-detail {
+  @include container;
+  @include dialog;
+
+  .el-tag {
+    @include el-tag;
+
+    border-radius: 4px;
+    margin-right: 8px;
+    border-color: $bor-color;
+  }
+  // 课程信息
+  &-top {
+    padding: 24px 32px;
+    background-color: #fff;
+    border-radius: 8px;
+
+    .title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      &-name {
+        font-size: 24px;
+        font-weight: 700;
+      }
+
+      &-time {
+        color: #737373;
+      }
+    }
+
+    .course_name {
+      margin: 8px 0 24px;
+    }
+
+    .learning-material {
+      border-top: 1px solid $bor-color;
+      padding-top: 24px;
+      margin-bottom: 16px;
+
+      &-title {
+        font-size: 18px;
+        margin-bottom: 16px;
+      }
+    }
+  }
+
+  &-main {
+    display: flex;
+    min-height: calc(100vh - 390px);
+    background-color: #fff;
+    border-radius: 8px;
+    margin-top: 16px;
+
+    .student-finish-situation {
+      padding: 24px 0;
+      flex: 3;
+
+      > div:first-child {
+        font-weight: 700;
+        padding: 0 32px;
+        margin-bottom: 24px;
+      }
+      // 学员列表
+      .student-list {
+        ul {
+          cursor: pointer;
+
+          .student-item {
+            display: flex;
+            justify-content: space-between;
+            padding: 8px 32px;
+
+            &.active {
+              background-color: #f2f2f2;
+            }
+          }
+        }
+      }
+    }
+
+    // 完成详情
+    .finish-detail {
+      flex: 7;
+      border-left: 1px solid #dbdbdb;
+
+      .student-info {
+        display: flex;
+        justify-content: space-between;
+        padding: 28px 32px;
+        border-bottom: 1px solid #dbdbdb;
+        height: 89px;
+        line-height: 32px;
+
+        &-name {
+          display: inline-block;
+          vertical-align: text-bottom;
+          height: 32px;
+          line-height: 32px;
+          margin-left: 24px;
+        }
+
+        .finish-time {
+          color: #999;
+        }
+      }
+
+      // 完成情况
+      .finish-situation {
+        width: 100%;
+        padding: 24px 32px;
+
+        .el-rate {
+          display: inline-block;
+          margin-left: 42px;
+        }
+
+        .title {
+          font-weight: bold;
+          font-size: 18px;
+
+          & + div {
+            padding: 16px 0;
+          }
+        }
+
+        .courseware-list {
+          .el-tag {
+            cursor: pointer;
+
+            .courseware {
+              overflow: hidden;
+
+              .svg-icon {
+                font-size: 18px;
+                margin-right: 12px;
+              }
+
+              .check-mark {
+                position: relative;
+                top: 2px;
+                margin: 0 0 0 12px;
+              }
+
+              &_name {
+                display: inline-block;
+                max-width: 120px;
+              }
+            }
+          }
+        }
+
+        // 学员详情按钮
+        .confirm {
+          display: flex;
+          justify-content: space-between;
+
+          .el-button {
+            width: 110px;
+          }
+        }
+      }
+    }
+  }
+}
+</style>