Quellcode durchsuchen

Merge remote-tracking branch 'origin/develop' into feature/codeHW

Unknown vor 7 Jahren
Ursprung
Commit
c42122b11a
47 geänderte Dateien mit 1485 neuen und 691 gelöschten Zeilen
  1. 24 2
      mock/modules/assignment/index.js
  2. 1 1
      mock/serializers/assignment/DocumentResultSerializer.js
  3. 1 1
      mock/serializers/assignment/DocumentSerializer.js
  4. 63 46
      src/api/assignment.js
  5. 1 1
      src/api/group.js
  6. 40 0
      src/assets/scss/_loading.scss
  7. 17 0
      src/assets/scss/_variables.scss
  8. 13 16
      src/assets/scss/app.scss
  9. 381 0
      src/components/AssignmentCRUD/CreateAssignment/index.vue
  10. 57 0
      src/components/AssignmentCRUD/DeleteAssignment/index.vue
  11. 195 0
      src/components/AssignmentCRUD/UpdateAssignment/index.vue
  12. 21 2
      src/components/Card/index.vue
  13. 21 0
      src/components/DocumentStateTag/index.vue
  14. 37 0
      src/components/PageBody/PageSectionLoading/index.vue
  15. 19 3
      src/components/PageHeader/index.jsx
  16. 14 0
      src/components/PageHeader/index.scss
  17. 23 0
      src/data/documentState.js
  18. 24 0
      src/data/questionDifficulty.js
  19. 2 2
      src/layouts/courseLayouts/LayoutStructure.vue
  20. 7 3
      src/router/routes.js
  21. 9 2
      src/util/request.js
  22. 3 0
      src/views/login/Login.vue
  23. 3 0
      src/views/login/Register.vue
  24. 3 0
      src/views/setting/Password.vue
  25. 3 0
      src/views/setting/Profile.vue
  26. 24 13
      src/views/student/Code/ProjectDetail/Test/FunctionalTestDetail.vue
  27. 25 11
      src/views/student/Code/ProjectDetail/Test/UnitTestDetail.vue
  28. 10 3
      src/views/student/Code/ProjectDetail/TestDetail.vue
  29. 10 2
      src/views/student/Code/ProjectDetail/index.vue
  30. 0 49
      src/views/student/Code/detail.vue
  31. 64 28
      src/views/student/Document/detail.vue
  32. 31 56
      src/views/student/Document/index.vue
  33. 20 5
      src/views/student/Group/index.vue
  34. 7 0
      src/views/student/Review/ReviewDetail/ReviewDocs/index.vue
  35. 21 12
      src/views/student/Review/ReviewDetail/ReviewSelfDetail/index.vue
  36. 11 3
      src/views/student/Review/ReviewDetail/index.vue
  37. 27 17
      src/views/student/Review/ReviewDocDetail/index.vue
  38. 0 0
      src/views/student/Review/ReviewSelf/index.vue
  39. 20 6
      src/views/student/Review/index.vue
  40. 22 18
      src/views/teacher/Code/detail.vue
  41. 58 130
      src/views/teacher/Code/index.vue
  42. 4 68
      src/views/teacher/Code/test.vue
  43. 42 46
      src/views/teacher/Document/detail.vue
  44. 39 0
      src/views/teacher/Document/docContent.vue
  45. 64 145
      src/views/teacher/Document/index.vue
  46. 2 0
      src/views/teacher/Question/index.vue
  47. 2 0
      src/views/teacher/Review/index.vue

+ 24 - 2
mock/modules/assignment/index.js

@@ -11,6 +11,7 @@ const groupSerializer = require("../../serializers/course/GroupSerializer");
 //新建代码作业
 router.route("/course/:courseId/code").post((req, res) => {
   res.send({
+    code: 0,
     data: CodeSerializer()
   });
 });
@@ -20,12 +21,14 @@ router
   //更新代码作业信息
   .put((req, res) => {
     res.send({
+      code: 0,
       data: CodeSerializer()
     });
   })
   //删除代码作业
   .delete((req, res) => {
     res.send({
+      code: 0,
       data: {}
     });
   });
@@ -33,6 +36,7 @@ router
 //老师查看代码作业列表
 router.route("/teacher/course/:courseId/code").get((req, res) => {
   res.send({
+    code: 0,
     data: [CodeSerializer(), CodeSerializer()]
   });
 });
@@ -40,6 +44,7 @@ router.route("/teacher/course/:courseId/code").get((req, res) => {
 //学生查看代码作业列表
 router.route("/student/course/:courseId/code").get((req, res) => {
   res.send({
+    code: 0,
     data: [CodeSerializer(), CodeSerializer()]
   });
 });
@@ -47,6 +52,7 @@ router.route("/student/course/:courseId/code").get((req, res) => {
 //学生查看单个代码作业
 router.route("/student/code/:codeId").get((req, res) => {
   res.send({
+    code: 0,
     data: CodeSerializer()
   });
 });
@@ -54,6 +60,7 @@ router.route("/student/code/:codeId").get((req, res) => {
 //学生查看单个代码作业下的项目信息列表
 router.route("/student/code/:codeId/project").get((req, res) => {
   res.send({
+    code: 0,
     data: [ProjectSerializer(), ProjectSerializer()]
   });
 });
@@ -61,6 +68,7 @@ router.route("/student/code/:codeId/project").get((req, res) => {
 //老师查看单个代码作业下所有已提交组的过程统计信息
 router.route("/teacher/code/:codeId/submitted/procedure").get((req, res) => {
   res.send({
+    code: 0,
     data: [codeProcedureSerializer(), codeProcedureSerializer()]
   });
 });
@@ -68,6 +76,7 @@ router.route("/teacher/code/:codeId/submitted/procedure").get((req, res) => {
 //老师查看单个代码作业下所有未提交组的信息
 router.route("/teacher/code/:codeId/unsubmitted/groups").get((req, res) => {
   res.send({
+    code: 0,
     data: [groupSerializer(), groupSerializer()]
   });
 });
@@ -75,6 +84,7 @@ router.route("/teacher/code/:codeId/unsubmitted/groups").get((req, res) => {
 //老师查看单个代码作业的成绩列表(所有组的)
 router.route("/teacher/code/:codeId/result").get((req, res) => {
   res.send({
+    code: 0,
     data: [CodeResultSerializer, CodeResultSerializer]
   });
 });
@@ -82,6 +92,7 @@ router.route("/teacher/code/:codeId/result").get((req, res) => {
 //学生查看单个代码作业的成绩
 router.route("/student/code/:codeId/result").get((req, res) => {
   res.send({
+    code: 0,
     data: CodeResultSerializer
   });
 });
@@ -90,6 +101,7 @@ router.route("/student/code/:codeId/result").get((req, res) => {
 //新建文档作业
 router.route("/course/:courseId/document").post((req, res) => {
   res.send({
+    code: 0,
     data: DocumentSerializer()
   });
 });
@@ -99,12 +111,14 @@ router
   //更新文档作业信息
   .put((req, res) => {
     res.send({
+      code: 0,
       data: DocumentSerializer()
     });
   })
   //删除文档作业
   .delete((req, res) => {
     res.send({
+      code: 0,
       data: {}
     });
   });
@@ -112,6 +126,7 @@ router
 //老师查看文档作业列表
 router.route("/teacher/course/:courseId/document").get((req, res) => {
   res.send({
+    code: 0,
     data: [DocumentSerializer(), DocumentSerializer()]
   });
 });
@@ -119,6 +134,7 @@ router.route("/teacher/course/:courseId/document").get((req, res) => {
 //学生查看文档作业列表
 router.route("/student/course/:courseId/document").get((req, res) => {
   res.send({
+    code: 0,
     data: [DocumentSerializer(), DocumentSerializer()]
   });
 });
@@ -126,6 +142,7 @@ router.route("/student/course/:courseId/document").get((req, res) => {
 //学生查看单个文档作业信息
 router.route("/student/document/:documentId").get((req, res) => {
   res.send({
+    code: 0,
     data: DocumentSerializer()
   });
 });
@@ -135,8 +152,11 @@ router
   .route("/teacher/document/:documentId/submitted/result")
   .get((req, res) => {
     res.send({
-      status: "正在互评",
-      data: [DocumentResultSerializer(), DocumentResultSerializer()]
+      code: 0,
+      data: {
+        status: "正在互评",
+        result: [DocumentResultSerializer(), DocumentResultSerializer()]
+      }
     });
   });
 
@@ -145,6 +165,7 @@ router
   .route("/teacher/document/:documentId/unsubmitted/groups")
   .get((req, res) => {
     res.send({
+      code: 0,
       data: [groupSerializer(), groupSerializer()]
     });
   });
@@ -152,6 +173,7 @@ router
 //学生查看文档作业的成绩
 router.route("/student/document/:documentId/result").get((req, res) => {
   res.send({
+    code: 0,
     data: DocumentResultSerializer()
   });
 });

+ 1 - 1
mock/serializers/assignment/DocumentResultSerializer.js

@@ -21,7 +21,7 @@ module.exports = () => {
     document: makeId(),
     group: groupSerializer(),
     state: "互评中",
-    result: "B",
+    result: "A",
     url: "https://github.com",
     reviewGroup: [1, 2, 3]
   };

+ 1 - 1
mock/serializers/assignment/DocumentSerializer.js

@@ -25,7 +25,7 @@ module.exports = () => {
     id: makeId(),
     name: "需求规格说明文档",
     description: "请给该项目的需求规格说明文档",
-    status: "Underway",
+    status: "AVAILABLE",
     createAt: dayjs("2018-10-21").unix(),
     startAt: dayjs("2019-01-01").unix(),
     endAt: dayjs("2019-01-20").unix(),

+ 63 - 46
src/api/assignment.js

@@ -10,17 +10,14 @@ import { ASSIGNMENT_MODULE } from "./_prefix";
  * @param startAt
  * @param endAt
  * @param problem
+ * @param courseId
  * @returns {Promise<{data:CodeSerializerType}>}
  */
-export const postCodeAssignment = ({
-  type,
-  name,
-  description,
-  startAt,
-  endAt,
-  problem
-}) => {
-  return request(`${ASSIGNMENT_MODULE}/course/:courseId/code`, {
+export const postCodeAssignment = (
+  { type, name, description, startAt, endAt, problem },
+  courseId
+) => {
+  return request(`${ASSIGNMENT_MODULE}/course/${courseId}/code`, {
     method: "POST",
     body: {
       type: type || null,
@@ -38,10 +35,14 @@ export const postCodeAssignment = ({
  * @param description
  * @param startAt
  * @param endAt
+ * @param codeId
  * @returns {Promise<{data:CodeSerializerType}>}
  */
-export const putCodeAssignment = ({ name, description, startAt, endAt }) => {
-  return request(`${ASSIGNMENT_MODULE}/code/:codeId`, {
+export const putCodeAssignment = (
+  { name, description, startAt, endAt },
+  codeId
+) => {
+  return request(`${ASSIGNMENT_MODULE}/code/${codeId}`, {
     method: "PUT",
     body: {
       name: name || null,
@@ -54,50 +55,56 @@ export const putCodeAssignment = ({ name, description, startAt, endAt }) => {
 
 /**
  * 老师删除代码作业
+ * @param codeId
  * @returns {Promise<{}>}
  */
-export const delCodeAssignment = () => {
-  return request(`${ASSIGNMENT_MODULE}/code/:codeId`);
+export const delCodeAssignment = codeId => {
+  return request(`${ASSIGNMENT_MODULE}/code/${codeId}`, { method: "DELETE" });
 };
 
 /**
  * 老师查看代码作业列表
+ * @param courseId
  * @returns {Promise<{data:CodeSerializerType[]}>}
  */
-export const getCodeAssignmentListByTeacher = (courseId = 123) => {
+export const getCodeAssignmentListByTeacher = courseId => {
   return request(`${ASSIGNMENT_MODULE}/teacher/course/${courseId}/code`);
 };
 
 /**
  * 学生查看代码作业列表
+ * @param courseId
  * @returns {Promise<{data:CodeSerializerType[]}>}
  */
-export const getCodeAssignmentListByStu = () => {
-  return request(`${ASSIGNMENT_MODULE}/student/course/:courseId/code`);
+export const getCodeAssignmentListByStu = courseId => {
+  return request(`${ASSIGNMENT_MODULE}/student/course/${courseId}}/code`);
 };
 
 /**
  * 学生查看单个代码作业
+ * @param codeId
  * @returns {Promise<{data:CodeSerializerType}>}
  */
-export const getCodeAssignmentByStu = () => {
-  return request(`${ASSIGNMENT_MODULE}/student/code/:codeId`);
+export const getCodeAssignmentByStu = codeId => {
+  return request(`${ASSIGNMENT_MODULE}/student/code/${codeId}`);
 };
 
 /**
  * 学生查看单个代码作业下的项目信息列表
+ * @param codeId
  * @returns {Promise<{data:ProjectSerializerType}>}
  */
-export const getProjectListByCode = () => {
-  return request(`${ASSIGNMENT_MODULE}/student/code/:codeId/project`);
+export const getProjectListByCode = codeId => {
+  return request(`${ASSIGNMENT_MODULE}/student/code/${codeId}/project`);
 };
 
 /**
  * 学生查看单个代码作业下的成绩
+ * @param codeId
  * @returns {Promise<{data:CodeResultSerializerType}>}
  */
-export const getCodeResultByCode = () => {
-  return request(`${ASSIGNMENT_MODULE}/student/code/:codeId/result`);
+export const getCodeResultByCode = codeId => {
+  return request(`${ASSIGNMENT_MODULE}/student/code/${codeId}/result`);
 };
 
 /**
@@ -105,7 +112,7 @@ export const getCodeResultByCode = () => {
  * @param codeId
  * @returns {Promise<{data:CodeProcedureSerializerType[]}>}
  */
-export const getCodeProcedureListByCodeId = (codeId = 123) => {
+export const getCodeProcedureListByCodeId = codeId => {
   return request(
     `${ASSIGNMENT_MODULE}/teacher/code/${codeId}/submitted/procedure`
   );
@@ -116,7 +123,7 @@ export const getCodeProcedureListByCodeId = (codeId = 123) => {
  * @param codeId
  * @returns {Promise<{data:GroupSerializerType[]}>}
  */
-export const getUnsubmittedCodeGroupsList = (codeId = 123) => {
+export const getUnsubmittedCodeGroupsList = codeId => {
   return request(
     `${ASSIGNMENT_MODULE}/teacher/code/${codeId}/unsubmitted/groups`
   );
@@ -124,10 +131,11 @@ export const getUnsubmittedCodeGroupsList = (codeId = 123) => {
 
 /**
  * 老师查看单个代码作业的成绩列表(所有组的)
+ * @param codeId
  * @returns {Promise<{data:CodeResultSerializerType}>}
  */
-export const getCodeResultListByCode = () => {
-  return request(`${ASSIGNMENT_MODULE}/teacher/code/:codeId/result`);
+export const getCodeResultListByCode = codeId => {
+  return request(`${ASSIGNMENT_MODULE}/teacher/code/${codeId}/result`);
 };
 
 /* 文档作业*/
@@ -139,17 +147,14 @@ export const getCodeResultListByCode = () => {
  * @param startAt
  * @param endAt
  * @param problem
+ * @param courseId
  * @returns {Promise<{data:DocumentSerializerType}>}
  */
-export const postDocAssignment = ({
-  type,
-  name,
-  description,
-  startAt,
-  endAt,
-  problem
-}) => {
-  return request(`${ASSIGNMENT_MODULE}/course/:courseId/document`, {
+export const postDocAssignment = (
+  { type, name, description, startAt, endAt, problem },
+  courseId
+) => {
+  return request(`${ASSIGNMENT_MODULE}/course/${courseId}/document`, {
     method: "POST",
     body: {
       type: type || null,
@@ -167,10 +172,14 @@ export const postDocAssignment = ({
  * @param description
  * @param startAt
  * @param endAt
+ * @param documentId
  * @returns {Promise<{data:DocumentSerializerType}>}
  */
-export const putDocAssignment = ({ name, description, startAt, endAt }) => {
-  return request(`${ASSIGNMENT_MODULE}/document/:documentId`, {
+export const putDocAssignment = (
+  { name, description, startAt, endAt },
+  documentId
+) => {
+  return request(`${ASSIGNMENT_MODULE}/document/${documentId}`, {
     method: "PUT",
     body: {
       name: name || null,
@@ -183,49 +192,57 @@ export const putDocAssignment = ({ name, description, startAt, endAt }) => {
 
 /**
  * 老师删除文档作业
+ * @param documentId
  * @returns {Promise<{}>}
  */
-export const delDocAssignment = () => {
-  return request(`${ASSIGNMENT_MODULE}/document/:documentId`);
+export const delDocAssignment = documentId => {
+  return request(`${ASSIGNMENT_MODULE}/document/${documentId}`, {
+    method: "DELETE"
+  });
 };
 
 /**
  * 老师查看文档作业列表
+ * @param courseId
  * @returns {Promise<{data:DocumentSerializerType[]}>}
  */
-export const getDocAssignmentListTeacherByCrs = (courseId = 123) => {
+export const getDocAssignmentListTeacherByCrs = courseId => {
   return request(`${ASSIGNMENT_MODULE}/teacher/course/${courseId}/document`);
 };
 
 /**
  * 学生查看文档作业列表
+ * @param courseId
  * @returns {Promise<{data:DocumentSerializerType[]}>}
  */
-export const getDocAssignmentListByCrs = (courseId = 123) => {
+export const getDocAssignmentListByCrs = courseId => {
   return request(`${ASSIGNMENT_MODULE}/student/course/${courseId}/document`);
 };
 
 /**
  * 学生查看单个文档作业信息
+ * @param documentId
  * @returns {Promise<{data:DocumentSerializerType}>}
  */
-export const getDocAssignmentDetailByDocuId = (documentId = 123) => {
+export const getDocAssignmentDetailByDocuId = documentId => {
   return request(`${ASSIGNMENT_MODULE}/student/document/${documentId}`);
 };
 
 /**
  * 学生查看单个文档作业下的成绩
+ * @param documentId
  * @returns {Promise<{data:DocumentResultSerializerType}>}
  */
-export const getDocResultById = (documentId = 123) => {
+export const getDocResultById = documentId => {
   return request(`${ASSIGNMENT_MODULE}/student/document/${documentId}/result`);
 };
 
 /**
  * 老师查看文档作业的成绩列表(已提交小组)
- * @returns {Promise<{data:DocumentResultSerializerType}>}
+ * @param documentId
+ * @returns {Promise<{data:DocumentResultSerializerType[]}>}
  */
-export const getSubmittedDocResultListByDocId = (documentId = 123) => {
+export const getSubmittedDocResultListByDocId = documentId => {
   return request(
     `${ASSIGNMENT_MODULE}/teacher/document/${documentId}/submitted/result`
   );
@@ -236,7 +253,7 @@ export const getSubmittedDocResultListByDocId = (documentId = 123) => {
  * @param documentId
  * @returns {Promise<{data:GroupSerializerType[]}>}
  */
-export const getUnsubmittedDocGroupsByDocId = (documentId = 123) => {
+export const getUnsubmittedDocGroupsByDocId = documentId => {
   return request(
     `${ASSIGNMENT_MODULE}/teacher/document/${documentId}/unsubmitted/groups`
   );

+ 1 - 1
src/api/group.js

@@ -33,7 +33,7 @@ export const getStudentGroup = (courseId = -1) => {
  */
 export const putStudentGroup = code => {
   return request(`${GROUP_MODULE}`, {
-    method: "POST",
+    method: "PUT",
     body: {
       code: code || null
     }

+ 40 - 0
src/assets/scss/_loading.scss

@@ -0,0 +1,40 @@
+@import "variables";
+
+$skeleton-color: #f2f2f2;
+$skeleton-to-color: darken($skeleton-color, 5%);
+
+.skeleton-title{
+  width: 100%;
+  max-width: 400px;
+  height: $size-3;
+  @extend %skeleton-loading;
+}
+
+.skeleton-normal{
+  width: 100%;
+  max-width: 700px;
+  height: $size-normal;
+  @extend %skeleton-loading;
+}
+
+
+%skeleton-loading {
+  border-radius: $default-radius;
+  background: linear-gradient(
+                  90deg,
+                  $skeleton-color 25%,
+                  $skeleton-to-color 37%,
+                  $skeleton-color 63%
+  );
+  animation: skeleton-loading 1.4s ease infinite;
+  background-size: 400% 100%;
+}
+
+@keyframes skeleton-loading {
+  0% {
+    background-position: 100% 50%;
+  }
+  100% {
+    background-position: 0 50%;
+  }
+}

+ 17 - 0
src/assets/scss/_variables.scss

@@ -152,3 +152,20 @@ $size-small: $size-7 !default;
 $size-normal: $size-6 !default;
 $size-medium: $size-5 !default;
 $size-large: $size-4 !default;
+
+
+
+// basic
+
+$default-shadow:0 3px 8px rgba(211, 215, 222, 0.4);
+$default-shadow-lighter:0 3px 8px rgba(211, 215, 222, 0.2);
+
+$default-radius:4px;
+
+$default-background:#f4f7fc;
+
+$default-border-color:rgba(84, 66, 251, 0.56);
+$default-border:2px solid $default-border-color;
+
+$default-margin:0.75rem;
+$default-margin-between:1.5rem;

+ 13 - 16
src/assets/scss/app.scss

@@ -1,26 +1,12 @@
 @import "~bulma/sass/utilities/initial-variables";
 @import "~bulma/sass/utilities/functions";
 @import "variables";
-
+@import "loading";
 @import "~bulma/sass/utilities/derived-variables";
 
 @import "~bulma";
 @import "~buefy/src/scss/buefy";
 
-
-$default-shadow:0 3px 8px rgba(211, 215, 222, 0.4);
-$default-shadow-lighter:0 3px 8px rgba(211, 215, 222, 0.2);
-
-$default-radius:4px;
-
-$default-background:#f4f7fc;
-
-$default-border-color:rgba(84, 66, 251, 0.56);
-$default-border:2px solid $default-border-color;
-
-$default-margin:0.75rem;
-$default-margin-between:1.5rem;
-
 .page-section{
   padding: $default-margin-between;
   border-radius: $default-radius;
@@ -65,7 +51,9 @@ $default-margin-between:1.5rem;
     }
   }
   .is-active {
-    background-color: rgba(255, 255, 255, 0.18) !important;
+    //background-color: rgba(255, 255, 255, 0.18) !important;
+    background-color: rgb(94, 87, 220) !important;
+    box-shadow: 0 2px 5px rgba(17, 15, 53, 0.12);
   }
 }
 
@@ -87,3 +75,12 @@ $default-margin-between:1.5rem;
   padding: 1em;
 }
 
+.table thead td, .table thead th{
+  border-bottom: 1px solid rgba(112, 96, 255, 0.22);
+}
+
+.table td, .table th{
+  border-width: 0 0 1px;
+  padding: 1em 0.75em;
+  border-bottom: 1px solid rgba(112, 96, 255, 0.22);
+}

+ 381 - 0
src/components/AssignmentCRUD/CreateAssignment/index.vue

@@ -0,0 +1,381 @@
+<template>
+  <div>
+    <a class="button is-link" @click="startCreation()"
+      >新建{{ createTitle }}作业</a
+    >
+
+    <!--新建文档作业模态框-->
+    <b-modal :active.sync="isCreateModalActive" :width="640" scroll="keep">
+      <div class="card">
+        <div class="title">新建{{ createTitle }}作业</div>
+        <div class="content">
+          <b-field label="题目名称">
+            <b-input v-model="problemName"></b-input>
+          </b-field>
+          <b-field
+            label="作业名称"
+            :type="$v.params.name.$error ? 'is-danger' : ''"
+            :message="$v.params.name.$error ? '请填写作业名称' : ''"
+          >
+            <b-input
+              placeholder="请填写作业名称"
+              v-model="$v.params.name.$model"
+            ></b-input>
+          </b-field>
+          <b-field
+            label="作业描述"
+            :type="$v.params.description.$error ? 'is-danger' : ''"
+            :message="$v.params.description.$error ? '请填写作业描述' : ''"
+          >
+            <b-input
+              type="textarea"
+              placeholder="请填写作业描述"
+              v-model="$v.params.description.$model"
+            ></b-input>
+          </b-field>
+          <b-field
+            label="开始时间"
+            :type="$v.params.startAt.$error ? 'is-danger' : ''"
+            :message="$v.params.startAt.$error ? '请填写作业开始时间' : ''"
+          >
+            <flat-pickr
+              placeholder="Click to select..."
+              class="input"
+              v-model="$v.params.startAt.$model"
+              :config="startAtConfig"
+            ></flat-pickr>
+          </b-field>
+          <b-field
+            label="结束时间"
+            :type="$v.params.endAt.$error ? 'is-danger' : ''"
+            :message="$v.params.endAt.$error ? '请填写作业结束时间' : ''"
+          >
+            <flat-pickr
+              placeholder="Click to select..."
+              class="input"
+              v-model="$v.params.endAt.$model"
+              :config="endAtConfig"
+            ></flat-pickr>
+          </b-field>
+
+          <a
+            class="button is-link"
+            :class="{ 'is-loading': submitting }"
+            @click="createAssignment()"
+          >
+            确认新建
+          </a>
+        </div>
+      </div>
+    </b-modal>
+
+    <!--搜索题目模态框-->
+    <b-modal :active.sync="isSearchModalActive">
+      <div class="card">
+        <div class="title">请先查找题目</div>
+        <div class="content">
+          <b-field
+            :type="$v.searchContent.$error ? 'is-danger' : ''"
+            :message="$v.params.endAt.$error ? '' : ''"
+          >
+            <b-input
+              placeholder="输入关键字模糊查找"
+              type="search"
+              v-model="$v.searchContent.$model"
+              expanded
+            >
+            </b-input>
+            <p class="control">
+              <button
+                class="button is-link"
+                :class="{ 'is-loading': submitting }"
+                @click="search(searchContent)"
+              >
+                查找
+              </button>
+            </p>
+          </b-field>
+        </div>
+      </div>
+    </b-modal>
+
+    <!--题目列表展示框-->
+    <b-modal :active.sync="isProblemListModalActive">
+      <b-notification type="is-danger" :active.sync="isServerError">
+        {{ errorMassage }}
+      </b-notification>
+      <div class="card">
+        <div class="title">选择题目</div>
+        <div class="content">
+          <div class="question-list">
+            <b-table :data="questions" detailed detail-key="id">
+              <template slot-scope="props">
+                <b-table-column
+                  field="id"
+                  label="ID"
+                  width="40"
+                  numeric
+                  sortable
+                >
+                  {{ props.row.id }}
+                </b-table-column>
+                <b-table-column label="题目名称">
+                  <a @click="selectAndCreate(props.row)">{{
+                    props.row.name
+                  }}</a>
+                </b-table-column>
+                <b-table-column label="题目描述" width="100">
+                  <div class="description">{{ props.row.description }}</div>
+                </b-table-column>
+                <b-table-column
+                  field="difficulty"
+                  label="题目难度"
+                  sortable
+                  centered
+                >
+                  <span
+                    :class="[
+                      'tag',
+                      `${getDifficultyClass(props.row.difficulty)}`
+                    ]"
+                  >
+                    {{ getDifficultyType(props.row.difficulty) }}
+                  </span>
+                </b-table-column>
+
+                <b-table-column
+                  label="平均时长"
+                  field="expectTime"
+                  sortable
+                  centered
+                >
+                  {{ props.row.expectTime }}分钟
+                </b-table-column>
+              </template>
+              <template slot="detail" slot-scope="props">
+                <div>{{ props.row.description }}</div>
+              </template>
+            </b-table>
+          </div>
+        </div>
+      </div>
+    </b-modal>
+  </div>
+</template>
+
+<script>
+import { getTeacherKeywordQuestions } from "@/api/question";
+import { postDocAssignment } from "@/api/assignment";
+import { postCodeAssignment } from "@/api/assignment";
+import timeMixins from "@/mixins/time";
+import { required } from "vuelidate/lib/validators";
+import pathMixins from "@/mixins/path";
+import { difficultyType } from "@/data/questionDifficulty";
+import { difficultyClass } from "@/data/questionDifficulty";
+export default {
+  props: {
+    courseId: {
+      type: String
+    },
+    assignType: {
+      type: Number
+    }
+  },
+  mounted() {
+    if (this.assignType === 1) {
+      this.createTitle = "文档";
+      this.postParam = "DOCUMENT";
+    } else {
+      this.createTitle = "代码";
+      this.postParam = "CODE";
+    }
+  },
+  data() {
+    return {
+      params: {
+        name: null,
+        description: null,
+        startAt: null,
+        endAt: null,
+        problem: null //题目编号
+      },
+      createTitle: "",
+      postParam: "",
+      myPromise: null,
+      problemName: "",
+      searchContent: "",
+      questions: [],
+      isCreateModalActive: false,
+      isSearchModalActive: false,
+      isProblemListModalActive: false,
+      startAtConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      endAtConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      errorMassage: null,
+      isServerError: false,
+      submitting: false
+    };
+  },
+  methods: {
+    /**
+     * 准备新建
+     */
+    startCreation() {
+      if (this.submitting) return;
+      this.isSearchModalActive = true;
+    },
+    /**
+     * 搜索题目
+     */
+    async search(content) {
+      if (this.submitting) return;
+      this.isServerError = false;
+      this.$v.searchContent.$touch();
+      if (!this.$v.searchContent.$invalid) {
+        this.submitting = true;
+        try {
+          await getTeacherKeywordQuestions(content).then(res => {
+            this.questions = res.data;
+          });
+          this.isSearchModalActive = false;
+          this.isProblemListModalActive = true;
+        } catch (e) {
+          this.errorMassage = e.message;
+          this.isServerError = true;
+        } finally {
+          this.submitting = false;
+        }
+      }
+    },
+    selectAndCreate(problem) {
+      this.params.problem = problem.id;
+      this.problemName = problem.name;
+      this.isProblemListModalActive = false;
+      this.isCreateModalActive = true;
+    },
+    /**
+     * 确认新建
+     */
+    createAssignment() {
+      if (this.submitting) return;
+      this.$v.params.$touch();
+      if (!this.$v.params.$invalid) {
+        this.submitting = true;
+        this.isCreateModalActive = false;
+        this.params["type"] = this.postParam;
+        this.parseParams();
+        if (this.assignType === 1) {
+          this.myPromise = postDocAssignment(
+            this.params,
+            parseInt(this.courseId)
+          );
+        } else {
+          this.myPromise = postCodeAssignment(
+            this.params,
+            parseInt(this.courseId)
+          );
+        }
+        this.myPromise.then(res => {
+          if (res.code === 0) {
+            //创建成功
+            this.$emit("sendCreatedAssignment", res.data);
+            this.isCreateModalActive = false;
+            if (this.assignType === 1) {
+              this.$router.push({
+                path: `${this.prefix}/review?documentHwId=${res.data.id}`
+              });
+            }
+          } else {
+            //创建失败
+            this.$dialog.alert({
+              title: "创建失败",
+              message: res.message,
+              type: "is-danger",
+              hasIcon: true
+            });
+          }
+        });
+        this.submitting = false;
+      }
+    },
+    getDifficultyType(level) {
+      return difficultyType(level);
+    },
+    getDifficultyClass(level) {
+      return difficultyClass(level);
+    },
+    parseParams() {
+      this.params.startAt = parseInt(this.params.startAt);
+      this.params.endAt = parseInt(this.params.endAt);
+    }
+  },
+  mixins: [timeMixins, pathMixins],
+  validations: {
+    params: {
+      name: {
+        required
+      },
+      description: {
+        required
+      },
+      startAt: {
+        required
+      },
+      endAt: {
+        required
+      }
+    },
+    searchContent: {
+      required
+    }
+  },
+  watch: {
+    isCreateModalActive(newValue) {
+      if (newValue === false) {
+        this.$v.params.$reset();
+      }
+    },
+    isSearchModalActive(newValue) {
+      if (newValue === false) {
+        this.$v.searchContent.$reset();
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.card {
+  text-align: left;
+  padding: 30px;
+  .title {
+    font-size: 1.25rem;
+    small {
+      color: gray;
+      font-size: 1rem;
+    }
+  }
+  .content {
+    a {
+      margin-top: 30px;
+      width: 100%;
+    }
+  }
+}
+.description {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 1;
+  word-wrap: break-word;
+  word-break: break-all;
+  display: -webkit-box;
+}
+</style>

+ 57 - 0
src/components/AssignmentCRUD/DeleteAssignment/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <div><a style="color:#b34141" @click="confirmDelete()">删除</a></div>
+</template>
+
+<script>
+import { delDocAssignment } from "@/api/assignment";
+import { delCodeAssignment } from "@/api/assignment";
+
+export default {
+  props: {
+    delId: {
+      type: Number
+    },
+    assignType: {
+      type: Number
+    }
+  },
+  data() {
+    return {
+      myPromise: null,
+      submitting: false
+    };
+  },
+  methods: {
+    confirmDelete() {
+      if (this.submitting) return;
+      this.submitting = true;
+      this.$dialog.confirm({
+        message: "确认删除?",
+        confirmText: "删除",
+        type: "is-danger",
+        onConfirm: () => {
+          if (this.assignType === 1) {
+            this.myPromise = delDocAssignment(this.delId);
+          } else {
+            this.myPromise = delCodeAssignment(this.delId);
+          }
+          this.myPromise.then(res => {
+            if (res.code === 0) {
+              this.$emit("del");
+              this.$toast.open("删除成功");
+            } else {
+              this.$dialog.alert({
+                title: "删除失败",
+                message: `${res.message}`,
+                type: "is-danger",
+                hasIcon: true
+              });
+            }
+          });
+        }
+      });
+      this.submitting = false;
+    }
+  }
+};
+</script>

+ 195 - 0
src/components/AssignmentCRUD/UpdateAssignment/index.vue

@@ -0,0 +1,195 @@
+<template>
+  <div>
+    <a @click="updateInfo()">修改</a>
+    <!--新建文档作业模态框-->
+    <b-modal :active.sync="isUpdateModalActive" :width="640" scroll="keep">
+      <div class="card">
+        <div class="title">修改{{ updateTitle }}作业</div>
+        <div class="content">
+          <b-field
+            label="作业名称"
+            :type="$v.params.name.$error ? 'is-danger' : ''"
+            :message="$v.params.name.$error ? '请填写作业名称' : ''"
+          >
+            <b-input
+              placeholder="请填写作业名称"
+              v-model="$v.params.name.$model"
+            ></b-input>
+          </b-field>
+          <b-field
+            label="作业描述"
+            :type="$v.params.description.$error ? 'is-danger' : ''"
+            :message="$v.params.description.$error ? '请填写作业描述' : ''"
+          >
+            <b-input
+              type="textarea"
+              placeholder="请填写作业描述"
+              v-model="$v.params.description.$model"
+            ></b-input>
+          </b-field>
+          <b-field
+            label="开始时间"
+            :type="$v.params.startAt.$error ? 'is-danger' : ''"
+            :message="$v.params.startAt.$error ? '请填写作业开始时间' : ''"
+          >
+            <flat-pickr
+              placeholder="Click to select..."
+              class="input"
+              v-model="$v.params.startAt.$model"
+              :config="startAtConfig"
+            ></flat-pickr>
+          </b-field>
+          <b-field
+            label="结束时间"
+            :type="$v.params.endAt.$error ? 'is-danger' : ''"
+            :message="$v.params.endAt.$error ? '请填写作业结束时间' : ''"
+          >
+            <flat-pickr
+              placeholder="Click to select..."
+              class="input"
+              v-model="$v.params.endAt.$model"
+              :config="endAtConfig"
+            ></flat-pickr>
+          </b-field>
+          <a class="button is-link" @click="updateAssignment()"> 确认修改 </a>
+        </div>
+      </div>
+    </b-modal>
+  </div>
+</template>
+
+<script>
+import timeMixins from "@/mixins/time";
+import { putDocAssignment } from "@/api/assignment";
+import { putCodeAssignment } from "@/api/assignment";
+import { required } from "vuelidate/lib/validators";
+export default {
+  props: {
+    updateItem: {
+      type: Object
+    },
+    assignType: {
+      type: Number
+    }
+  },
+  mixins: [timeMixins],
+  mounted() {
+    if (this.assignType === 1) {
+      this.updateTitle = "文档";
+    } else {
+      this.updateTitle = "代码";
+    }
+  },
+  data() {
+    return {
+      isUpdateModalActive: false,
+      params: {
+        name: null,
+        description: null,
+        startAt: null,
+        endAt: null
+      },
+      updateTitle: "",
+      myPromise: null,
+      startAtConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      endAtConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      submitting: false
+    };
+  },
+  methods: {
+    /**
+     * 准备修改
+     */
+    updateInfo() {
+      this.isUpdateModalActive = true;
+      this.params.name = this.updateItem.name;
+      this.params.description = this.updateItem.description;
+    },
+    /**
+     * 确认修改
+     */
+    updateAssignment() {
+      if (this.submitting) return;
+      this.$v.params.$touch();
+      if (!this.$v.params.$invalid) {
+        this.submitting = true;
+        this.isUpdateModalActive = false;
+        this.parseParams();
+        if (this.assignType === 1) {
+          this.myPromise = putDocAssignment(this.params, this.updateItem.id);
+        } else {
+          this.myPromise = putCodeAssignment(this.params, this.updateItem.id);
+        }
+        this.myPromise.then(res => {
+          if (res.code === 0) {
+            this.$emit("updateDocData", res.data);
+          } else {
+            this.$dialog.alert({
+              title: "修改失败",
+              message: res.message,
+              type: "is-danger",
+              hasIcon: true
+            });
+          }
+        });
+        this.submitting = true;
+      }
+    },
+    parseParams() {
+      this.params.startAt = parseInt(this.params.startAt);
+      this.params.endAt = parseInt(this.params.endAt);
+    }
+  },
+  validations: {
+    params: {
+      name: {
+        required
+      },
+      description: {
+        required
+      },
+      startAt: {
+        required
+      },
+      endAt: {
+        required
+      }
+    }
+  },
+  watch: {
+    isUpdateModalActive(newValue) {
+      if (newValue === false) {
+        this.$v.params.$reset();
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.card {
+  text-align: left;
+  padding: 30px;
+  .title {
+    font-size: 1.25rem;
+    small {
+      color: gray;
+      font-size: 1rem;
+    }
+  }
+  .content {
+    a {
+      margin-top: 30px;
+      width: 100%;
+    }
+  }
+}
+</style>

+ 21 - 2
src/components/Card/index.vue

@@ -1,10 +1,14 @@
 <template>
   <div
     class="custom-card"
-    :class="{ hoverable: hoverable }"
+    :class="{ hoverable: hoverable, 'custom-card__loading': loading }"
     :style="{ padding: hasPadding ? '20px' : 0 }"
   >
-    <slot></slot>
+    <div v-if="loading">
+      <div class="loading" style="width: 50%;margin-bottom: 1rem"></div>
+      <div class="loading"></div>
+    </div>
+    <slot v-else></slot>
   </div>
 </template>
 
@@ -18,6 +22,10 @@ export default {
     hasPadding: {
       type: Boolean,
       default: true
+    },
+    loading: {
+      type: Boolean,
+      default: false
     }
   }
 };
@@ -25,6 +33,13 @@ export default {
 
 <style lang="scss" scoped>
 @import "../../assets/scss/app";
+
+.loading {
+  width: 100%;
+  height: $size-normal;
+  @extend %skeleton-loading;
+}
+
 .custom-card {
   width: 100%;
   height: 100%;
@@ -34,6 +49,10 @@ export default {
   border-radius: $default-radius;
   color: #152935;
   border: $default-border;
+
+  &__loading {
+    border-color: lighten($default-border-color, 40%);
+  }
 }
 .hoverable {
   cursor: pointer;

+ 21 - 0
src/components/DocumentStateTag/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <div class="tag" :class="tagDetail.styledClass">{{ tagDetail.name }}</div>
+</template>
+
+<script>
+import getDocumentState from "../../data/documentState";
+export default {
+  name: "DocumentStateTag",
+  props: {
+    state: {
+      type: String,
+      default: "已失效"
+    }
+  },
+  computed: {
+    tagDetail() {
+      return getDocumentState(this.state);
+    }
+  }
+};
+</script>

+ 37 - 0
src/components/PageBody/PageSectionLoading/index.vue

@@ -0,0 +1,37 @@
+<template>
+  <div v-if="show" class="page-section">
+    <h1 class="loading-title title" />
+    <p class="loading-line" />
+    <p class="loading-line" />
+    <p class="loading-line" style="width: 60%" />
+  </div>
+</template>
+
+<script>
+export default {
+  name: "PageSectionLoading",
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../assets/scss/app";
+.loading-title {
+  @extend %skeleton-loading;
+  height: $size-large;
+  width: 100%;
+  max-width: 300px;
+}
+
+.loading-line {
+  @extend %skeleton-loading;
+  height: $size-normal;
+  width: 100%;
+  margin-top: $size-normal;
+}
+</style>

+ 19 - 3
src/components/PageHeader/index.jsx

@@ -12,6 +12,10 @@ export default {
     tabList: {
       type: Array,
       default: () => []
+    },
+    loading: {
+      type: Boolean,
+      default: false
     }
   },
   computed: {
@@ -21,7 +25,7 @@ export default {
   },
   mixins: [pathMixin],
   render() {
-    const { prefix, course, routes = [], tabList = [] } = this;
+    const { prefix, course, routes = [], tabList = [], loading } = this;
     const { logo, title, action, content, extraContent } = this.$slots;
     const showTabs = tabList && tabList.length > 0;
     return (
@@ -53,11 +57,23 @@ export default {
           {logo && <div class="logo">{logo}</div>}
           <div class="main">
             <div class="row">
-              {title && <h1 class="title">{title}</h1>}
+              {loading ? (
+                <h1 class="title">
+                  <div class="skeleton-title" />
+                </h1>
+              ) : (
+                title && <h1 class="title">{title}</h1>
+              )}
               {action && <div class="action">{action}</div>}
             </div>
             <div class="row">
-              {content && <div class="content">{content}</div>}
+              {loading ? (
+                <div class="content">
+                  <div class="skeleton-normal" />
+                </div>
+              ) : (
+                content && <div class="content">{content}</div>
+              )}
               {extraContent && <div class="extra-content">{extraContent}</div>}
             </div>
           </div>

+ 14 - 0
src/components/PageHeader/index.scss

@@ -22,6 +22,13 @@
     width: 100%;
   }
 
+  .skeleton-basic{
+    @extend %skeleton-loading;
+    height: $size-normal;
+    width: 100%;
+    max-width: 800px;
+  }
+
   .breadcrumb {
     margin-bottom: $default-margin-between;
   }
@@ -51,10 +58,17 @@
   }
 
   .title {
+    font-size: $size-3;
     font-weight: 500;
+    line-height: $size-3;
     //color: $default;
   }
 
+  .content{
+    font-size: $size-normal;
+    line-height: $size-normal;
+  }
+
   .action {
     margin-left: 56px;
     min-width: 266px;

+ 23 - 0
src/data/documentState.js

@@ -0,0 +1,23 @@
+export const DEFAULT = "DEFAULT";
+export const PREPARING = "PREPARING";
+export const UNAVAILABLE = "UNAVAILABLE";
+export const READY = "READY";
+export const AVAILABLE = "AVAILABLE";
+export const FINISHED = "FINISHED";
+
+export default function(state) {
+  switch (state) {
+    case PREPARING:
+      return { name: "准备中", styledClass: "is-info" };
+    case UNAVAILABLE:
+      return { name: "创建失败", styledClass: "is-warning" };
+    case READY:
+      return { name: "未开始", styledClass: "is-light" };
+    case AVAILABLE:
+      return { name: "正在进行", styledClass: "is-success" };
+    case FINISHED:
+      return { name: "已结束", styledClass: "is-danger" };
+    default:
+      return { name: "无状态", styledClass: "is-dark" };
+  }
+}

+ 24 - 0
src/data/questionDifficulty.js

@@ -0,0 +1,24 @@
+export const difficultyType = level => {
+  switch (level) {
+    case 1:
+      return "简单";
+    case 2:
+      return "中等";
+    case 3:
+      return "困难";
+    default:
+      return "--";
+  }
+};
+export const difficultyClass = level => {
+  switch (level) {
+    case 1:
+      return "is-success";
+    case 2:
+      return "is-warning";
+    case 3:
+      return "is-danger";
+    default:
+      return "is-link";
+  }
+};

+ 2 - 2
src/layouts/courseLayouts/LayoutStructure.vue

@@ -95,10 +95,10 @@ $top-section-height: 4.25rem;
   z-index: 31;
   /*TODO: 寻找更好的图片或背景颜色*/
   /*background-image: url("../../assets/sideBar.png");*/
-  /*background-image: linear-gradient(to top, #493a9e 0%, #4f54e9 100%);*/
+  background-image: linear-gradient(to top, #493a9e 0%, #4f54e9 100%);
   /*background: linear-gradient(to top, #7c71bd 0%, #4f62e9 100%);*/
   /*background: linear-gradient(to top, #7c71bdc7 0%, #6172ec 100%);*/
-  background: linear-gradient(to top, #20328ec7 0%, #0d0f23 100%);
+  /*background: linear-gradient(to top, #20328ec7 0%, #0d0f23 100%);*/
   background-size: cover;
   position: sticky;
   overflow-y: auto;

+ 7 - 3
src/router/routes.js

@@ -81,11 +81,11 @@ export default [
         component: () => import("@/views/teacher/Code/index")
       },
       {
-        path: "code/detail",
+        path: "code/:codeId",
         component: () => import("@/views/teacher/Code/detail")
       },
       {
-        path: "code/detail/test",
+        path: "code/:codeId/test",
         component: () => import("@/views/teacher/Code/test")
       },
 
@@ -94,9 +94,13 @@ export default [
         component: () => import("@/views/teacher/Document/index")
       },
       {
-        path: "document/detail",
+        path: "document/:documentId",
         component: () => import("@/views/teacher/Document/detail")
       },
+      {
+        path: "document/:documentId/content",
+        component: () => import("@/views/teacher/Document/docContent")
+      },
       {
         path: "review",
         component: () => import("@/views/teacher/Review")

+ 9 - 2
src/util/request.js

@@ -2,6 +2,7 @@
 import "whatwg-fetch";
 import router from "@/router";
 import { LOGIN } from "@/router/name";
+import { getToken } from "@/util/tokenStorage";
 
 /**
  * @description error
@@ -68,7 +69,13 @@ export default async function request(url, options) {
       };
     }
   }
-
+  const token = getToken();
+  if (token) {
+    newOptions.headers = {
+      ...newOptions.headers,
+      Authorization: token
+    };
+  }
   const response = await fetch(url, newOptions);
   try {
     await checkStatus(response);
@@ -80,7 +87,7 @@ export default async function request(url, options) {
     }
   }
 
-  if (newOptions.method === "DELETE" || response.status === 204) {
+  if (response.status === 204) {
     return response.text();
   }
   const Authorization = await response.headers.get("Authorization");

+ 3 - 0
src/views/login/Login.vue

@@ -83,6 +83,9 @@ export default {
   },
   methods: {
     async loginAction() {
+      if (this.submitting) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.$invalid) {
         const { username = "", password = "" } = this;

+ 3 - 0
src/views/login/Register.vue

@@ -133,6 +133,9 @@ export default {
   },
   methods: {
     async registerAction() {
+      if (this.submitting) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.$invalid) {
         this.submitting = true;

+ 3 - 0
src/views/setting/Password.vue

@@ -94,6 +94,9 @@ export default {
   },
   methods: {
     async registerAction() {
+      if (this.submitting) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.$invalid) {
         this.submitting = true;

+ 3 - 0
src/views/setting/Profile.vue

@@ -55,6 +55,9 @@ export default {
   },
   methods: {
     async updateProfileAction() {
+      if (this.submitting) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.$invalid) {
         this.submitting = true;

+ 24 - 13
src/views/student/Code/ProjectDetail/Test/FunctionalTestDetail.vue

@@ -1,6 +1,7 @@
 <template>
   <div>
     <page-header
+      :loading="testDetailLoading"
       :routes="[
         { name: '代码作业', path: 'code' },
         { name: '项目列表', path: `code/${$route.params.homeworkId}` },
@@ -24,15 +25,22 @@
       </template>
     </page-header>
     <page-body>
-      <div v-for="item in testDetail.detail" :key="item.id" class="gap-section">
-        <div class="page-section">
-          <div>
-            <pass-tag :pass="item.isPass" />
-            <strong style="padding-left: 10px">
-              页面测试用例 : #{{ item.id }}
-            </strong>
+      <div v-if="testDetailLoading"><page-section-loading show /></div>
+      <div v-else>
+        <div
+          v-for="item in testDetail.detail"
+          :key="item.id"
+          class="gap-section"
+        >
+          <div class="page-section">
+            <div>
+              <pass-tag :pass="item.isPass" />
+              <strong style="padding-left: 10px">
+                页面测试用例 : #{{ item.id }}
+              </strong>
+            </div>
+            <div class="code-section">{{ item.message }}</div>
           </div>
-          <div class="code-section">{{ item.message }}</div>
         </div>
       </div>
     </page-body>
@@ -45,20 +53,23 @@ import PageBody from "@/components/PageBody/index";
 import { getFunctionalDetail } from "@/api/codehw";
 import timeMixins from "@/mixins/time";
 import PassTag from "@/views/student/Code/ProjectDetail/components/PassTag";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
-  components: { PassTag, PageBody, PageHeader },
+  components: { PageSectionLoading, PassTag, PageBody, PageHeader },
   data() {
     return {
       testDetail: { detail: [] },
-      showModal: false
+      testDetailLoading: false
     };
   },
   mixins: [timeMixins],
   mounted() {
     const { functionalTestId } = this.$route.params;
-    getFunctionalDetail(functionalTestId).then(
-      value => (this.testDetail = value.data)
-    );
+    this.testDetailLoading = true;
+    getFunctionalDetail(functionalTestId).then(value => {
+      this.testDetail = value.data;
+      this.testDetailLoading = false;
+    });
   }
 };
 </script>

+ 25 - 11
src/views/student/Code/ProjectDetail/Test/UnitTestDetail.vue

@@ -1,6 +1,7 @@
 <template>
   <div>
     <page-header
+      :loading="testDetailLoading"
       :routes="[
         { name: '代码作业', path: 'code' },
         { name: '项目列表', path: `code/${$route.params.homeworkId}` },
@@ -24,15 +25,22 @@
       </template>
     </page-header>
     <page-body>
-      <div v-for="item in testDetail.detail" :key="item.id" class="gap-section">
-        <div class="page-section">
-          <div>
-            <pass-tag :pass="item.isPass" />
-            <strong style="padding-left: 10px">
-              页面测试用例 : #{{ item.id }}
-            </strong>
+      <div v-if="testDetailLoading"><page-section-loading show /></div>
+      <div v-else>
+        <div
+          v-for="item in testDetail.detail"
+          :key="item.id"
+          class="gap-section"
+        >
+          <div class="page-section">
+            <div>
+              <pass-tag :pass="item.isPass" />
+              <strong style="padding-left: 10px">
+                页面测试用例 : #{{ item.id }}
+              </strong>
+            </div>
+            <div class="code-section">{{ item.message }}</div>
           </div>
-          <div class="code-section">{{ item.message }}</div>
         </div>
       </div>
     </page-body>
@@ -45,17 +53,23 @@ import PageBody from "@/components/PageBody/index";
 import { getUnitTestDetail } from "@/api/codehw";
 import timeMixins from "@/mixins/time";
 import PassTag from "@/views/student/Code/ProjectDetail/components/PassTag";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
-  components: { PassTag, PageBody, PageHeader },
+  components: { PageSectionLoading, PassTag, PageBody, PageHeader },
   data() {
     return {
-      testDetail: { detail: [] }
+      testDetail: { detail: [] },
+      testDetailLoading: false
     };
   },
   mixins: [timeMixins],
   mounted() {
     const { unitTestId } = this.$route.params;
-    getUnitTestDetail(unitTestId).then(value => (this.testDetail = value.data));
+    this.testDetailLoading = true;
+    getUnitTestDetail(unitTestId).then(value => {
+      this.testDetail = value.data;
+      this.testDetailLoading = false;
+    });
   }
 };
 </script>

+ 10 - 3
src/views/student/Code/ProjectDetail/TestDetail.vue

@@ -1,7 +1,8 @@
 <template>
   <div class="columns is-desktop">
     <div class="column">
-      <div class="page-section">
+      <div v-if="detailLoading"><page-section-loading show /></div>
+      <div v-else class="page-section">
         <h5 class="title is-5">单元测试:</h5>
         <router-link
           class="test-item"
@@ -19,7 +20,8 @@
       </div>
     </div>
     <div class="column">
-      <div class="page-section">
+      <div v-if="detailLoading"><page-section-loading show /></div>
+      <div v-else class="page-section">
         <h5 class="test-title title is-5">
           页面测试:
           <div
@@ -53,8 +55,9 @@
 import PassTag from "@/views/student/Code/ProjectDetail/components/PassTag";
 import timeMixins from "@/mixins/time";
 import { makeFunctionalTest } from "@/api/codehw";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
-  components: { PassTag },
+  components: { PageSectionLoading, PassTag },
   props: {
     projectDetail: {
       type: Object,
@@ -62,6 +65,10 @@ export default {
         unitTestList: [],
         functionTestList: []
       })
+    },
+    detailLoading: {
+      type: Boolean,
+      default: false
     }
   },
   computed: {

+ 10 - 2
src/views/student/Code/ProjectDetail/index.vue

@@ -34,7 +34,12 @@
         <div>项目详情</div>
       </template>
     </page-header>
-    <page-body> <router-view :projectDetail="projectDetail" /> </page-body>
+    <page-body>
+      <router-view
+        :project-detail="projectDetail"
+        :detail-loading="detailLoading"
+      />
+    </page-body>
   </div>
 </template>
 
@@ -49,13 +54,16 @@ export default {
   },
   data() {
     return {
-      projectDetail: {}
+      projectDetail: {},
+      detailLoading: false
     };
   },
   async mounted() {
     const { projectId } = this.$route.params;
+    this.detailLoading = true;
     const detail = await getProjectDetail(projectId);
     this.projectDetail = detail.data;
+    this.detailLoading = false;
   }
 };
 </script>

+ 0 - 49
src/views/student/Code/detail.vue

@@ -1,49 +0,0 @@
-<template>
-  <div>
-    <page-header
-      :routes="[
-        { name: '代码作业', path: 'code' },
-        { name: '购物车web项目', path: 'code/detail' }
-      ]"
-    >
-      <template slot="title">
-        Here might be a page title
-      </template>
-      <template slot="content">
-        Here might be a page title
-      </template>
-    </page-header>
-    <page-body> </page-body>
-  </div>
-</template>
-
-<script>
-import PageHeader from "@/components/PageHeader";
-import PageBody from "@/components/PageBody/index";
-const codeAssignmentDetail = {
-  id: 1,
-  name: "软件需求规格说明书",
-  description: "这里是描述这里是描述这里是描述这里是描述这里是描述",
-  state: "未开始",
-  startAt: "2019-1-1",
-  endAt: "2019-6-1"
-};
-const codeAssignmentProjects = {
-  codeId: 1,
-  url: "https://www.github.com/adsf/adsfa",
-  name: "电影票web项目",
-  buildList: []
-};
-export default {
-  components: {
-    PageBody,
-    PageHeader
-  },
-  data() {
-    return {
-      codeAssignmentDetail,
-      codeAssignmentProjects
-    };
-  }
-};
-</script>

+ 64 - 28
src/views/student/Document/detail.vue

@@ -3,42 +3,64 @@
     <page-header
       :routes="[
         { name: '文档作业', path: 'document' },
-        { name: `${assignmentInfo.name}`, path: 'document/123' }
+        { name: `作业 `, path: $route.path }
       ]"
     >
       <template slot="title">
         {{ assignmentInfo.name }}
       </template>
       <template slot="content">
-        Here might be a page title
+        {{ assignmentInfo.description }}
+      </template>
+      <template slot="action" v-if="resultDisplay">
+        <a class="button is-primary" v-if="resultDisplay"
+          >成绩:{{ docResult.result }}</a
+        >
       </template>
     </page-header>
     <page-body>
-      <div class="data-table">
-        <div class="detail-description">
-          <div class="subtitle">题目名称</div>
-          <div class="detail-content">
-            {{ detail.problem ? detail.problem.name : "" }}
+      <div class="page-section">
+        <!--第一行-->
+        <div class="columns is-desktop">
+          <div class="column">
+            <h5 class="title is-5">题目名称</h5>
+            <div class="subtitle is-6">
+              {{ detail.problem ? detail.problem.name : "" }}
+            </div>
           </div>
-        </div>
-        <div class="detail-description">
-          <div class="subtitle">作业详情</div>
-          <div class="detail-content">
-            {{ detail.problem ? detail.problem.description : "" }}
+          <div class="column">
+            <h5 class="title is-5">状态:</h5>
+            <div class="subtitle is-6">
+              <document-state-tag :state="detail.status"></document-state-tag>
+            </div>
           </div>
         </div>
-        <div class="detail-description">
-          <div class="subtitle">git地址</div>
-          <div class="detail-content">{{ detail.url }}</div>
+        <!--第二行-->
+        <div class="columns is-desktop">
+          <div class="column">
+            <h5 class="title is-5">开始时间</h5>
+            <div class="subtitle is-6">
+              {{ displayUnixTime(detail.startAt) }}
+            </div>
+          </div>
+          <div class="column">
+            <h5 class="title is-5">结束时间</h5>
+            <div class="subtitle is-6">{{ displayUnixTime(detail.endAt) }}</div>
+          </div>
         </div>
-
-        <div class="detail-description">
-          <div class="subtitle">所处阶段</div>
-          <div class="detail-content">{{ detail.status }}</div>
+        <div class="columns is-desktop">
+          <div class="column">
+            <h5 class="title is-5">Git地址</h5>
+            <div class="subtitle is-6">{{ docResult.url }}</div>
+          </div>
         </div>
-        <div class="detail-description">
-          <div class="subtitle">成绩</div>
-          <div class="detail-content">{{ result.result }}</div>
+        <div class="columns is-desktop">
+          <div class="column">
+            <h5 class="title is-5">题目描述</h5>
+            <div class="subtitle is-6">
+              {{ detail.problem ? detail.problem.description : "" }}
+            </div>
+          </div>
         </div>
       </div>
     </page-body>
@@ -46,10 +68,13 @@
 </template>
 
 <script>
+import { mapState } from "vuex";
 import PageHeader from "../../../components/PageHeader";
 import PageBody from "../../../components/PageBody";
 import { getDocAssignmentDetailByDocuId } from "@/api/assignment";
 import { getDocResultById } from "@/api/assignment";
+import DocumentStateTag from "@/components/DocumentStateTag";
+import timeMixins from "@/mixins/time";
 
 const assignmentInfo = {
   id: 0,
@@ -62,25 +87,36 @@ const assignmentInfo = {
 export default {
   components: {
     PageBody,
-    PageHeader
+    PageHeader,
+    DocumentStateTag
   },
+  mixins: [timeMixins],
   mounted() {
     this.assignmentInfo = this.$route.query.assignmentInfo;
-    getDocAssignmentDetailByDocuId(123).then(res => {
+    getDocAssignmentDetailByDocuId(this.assignmentInfo.id).then(res => {
       console.log(res.data);
       this.detail = res.data;
     });
-    getDocResultById(123).then(res => {
-      console.log(res.data);
-      this.result = res.data;
+    getDocResultById(this.assignmentInfo.id).then(res => {
+      this.docResult = res.data;
+      console.log("doc.result" + this.docResult.result);
+      if (parseInt(this.docResult.result) !== -1) {
+        this.resultDisplay = true;
+      }
     });
   },
   data() {
     return {
       assignmentInfo,
       detail: {},
-      result: {}
+      docResult: {},
+      resultDisplay: false
     };
+  },
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
   }
 };
 </script>

+ 31 - 56
src/views/student/Document/index.vue

@@ -5,83 +5,58 @@
         文档作业列表
       </template>
       <template slot="content">
-        共有{n}份作业
+        共有{{ docList.length }}份作业
       </template>
     </page-header>
     <page-body>
-      <div>
-        <b-table
-          :data="docList"
-          paginated
-          per-page="5"
-          detailed
-          detail-key="id"
-        >
-          <template slot-scope="props">
-            <b-table-column field="id" label="题目ID" numeric sortable centered>
-              {{ props.row.id }}
-            </b-table-column>
-
-            <b-table-column label="作业名称" centered>
-              <router-link
-                :to="{
-                  path: `/course-student/:courseId/document/detail`,
-                  query: { assignmentInfo: props.row }
-                }"
-              >
-                {{ props.row.name }}</router-link
-              >
-            </b-table-column>
-            <b-table-column label="状态" centered>
-              {{ props.row.status }}
-            </b-table-column>
-
-            <b-table-column label="开始时间" centered>
-              {{ new Date(props.row.startAt * 1000).getFullYear() }}-{{
-                new Date(props.row.startAt * 1000).getMonth() + 1
-              }}-{{ new Date(props.row.startAt * 1000).getDate() }}
-            </b-table-column>
-
-            <b-table-column label="结束时间" centered>
-              {{ new Date(props.row.endAt * 1000).getFullYear() }}-{{
-                new Date(props.row.endAt * 1000).getMonth() + 1
-              }}-{{ new Date(props.row.endAt * 1000).getDate() }}
-            </b-table-column>
-          </template>
-
-          <template slot="detail" slot-scope="props">
-            <article class="media">
-              <div class="media-content">
-                <div class="content">
-                  <p>
-                    <strong>作业详情:</strong>
-                    <small>{{ props.row.description }}</small> <br />
-                  </p>
-                </div>
+      <div class="columns is-multiline is-desktop">
+        <div :key="item.id" v-for="item in docList" class="column is-4">
+          <router-link
+            :to="{
+              path: `${$route.path}/${item.id}`,
+              query: { assignmentInfo: item }
+            }"
+          >
+            <card hoverable>
+              {{ item.name }}
+              <div>
+                <document-state-tag :state="item.status"></document-state-tag>
               </div>
-            </article>
-          </template>
-        </b-table>
+            </card>
+          </router-link>
+        </div>
       </div>
     </page-body>
   </div>
 </template>
 
 <script>
+import { mapState } from "vuex";
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
 import { getDocAssignmentListByCrs } from "@/api/assignment";
+import timeMixins from "@/mixins/time";
+import Card from "@/components/Card";
+import DocumentStateTag from "@/components/DocumentStateTag";
 export default {
   components: {
     PageBody,
-    PageHeader
+    PageHeader,
+    Card,
+    DocumentStateTag
   },
+  mixins: [timeMixins],
   mounted() {
-    getDocAssignmentListByCrs(123).then(res => {
-      console.log(typeof res.data);
+    console.log(this.$route.path);
+    getDocAssignmentListByCrs(this.course.id).then(res => {
       this.docList = res.data;
     });
   },
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
+  },
   data() {
     return {
       docList: [],

+ 20 - 5
src/views/student/Group/index.vue

@@ -1,12 +1,17 @@
 <template>
   <div>
-    <page-header :routes="[{ name: '我的小组' }]">
+    <page-header :loading="isLoadingGroup" :routes="[{ name: '我的小组' }]">
       <template slot="title">
-        我的小组 {{ this.groups.length > 0 && `「${this.groups[0].name}」` }}
+        我的小组
+        <span v-if="groups.length > 0">
+          {{ `「${this.groups[0].name}」` }}
+        </span>
       </template>
     </page-header>
     <page-body>
-      <div v-if="groups.length > 0">
+      <page-section-loading :show="isLoadingGroup" />
+
+      <div v-if="!isLoadingGroup && groups.length > 0">
         <div class="page-section">
           <div class="columns is-desktop">
             <div class="column">
@@ -139,8 +144,9 @@ import {
   postStudentGroup,
   putStudentGroup
 } from "@/api/group";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
-  components: { PageBody, PageHeader, Card },
+  components: { PageSectionLoading, PageBody, PageHeader, Card },
   data() {
     return {
       showCreateModal: false,
@@ -180,12 +186,18 @@ export default {
   },
   methods: {
     async createGroup() {
+      if (this.submitLoading) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.name.$invalid) {
         try {
           this.submitLoading = true;
           this.isServerError = false;
-          await postStudentGroup(this.name);
+          await postStudentGroup({
+            name: this.name,
+            courseId: this.$route.params.courseId
+          });
           this.$toast.open({
             message: `小组「${this.name}」创建成功`,
             type: "is-success"
@@ -201,6 +213,9 @@ export default {
       }
     },
     async joinGroup() {
+      if (this.submitLoading) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.code.$invalid) {
         try {

+ 7 - 0
src/views/student/Review/ReviewDetail/ReviewDocs/index.vue

@@ -1,6 +1,9 @@
 <template>
   <div class="columns is-multiline is-desktop">
+    <div v-if="detailLoading" class="column is-4-desktop"><card loading /></div>
+
     <div
+      v-else
       :key="item.docReviewId"
       v-for="item in docs"
       class="column is-4-desktop"
@@ -23,6 +26,10 @@ export default {
     docs: {
       type: Array,
       default: () => []
+    },
+    detailLoading: {
+      type: Boolean,
+      default: false
     }
   }
 };

+ 21 - 12
src/views/student/Review/ReviewDetail/ReviewSelfDetail/index.vue

@@ -2,7 +2,8 @@
   <div>
     <div class="columns">
       <div class="column is-8">
-        <div class="page-section">
+        <page-section-loading :show="selfDetailLoading" />
+        <div v-if="!selfDetailLoading" class="page-section">
           <div v-if="detail.docContent" class="content">
             <vue-markdown>{{ detail.docContent }}</vue-markdown>
           </div>
@@ -10,13 +11,16 @@
       </div>
       <div class="column is-4">
         <div class="right-part">
-          <result-check-list-item
-            v-for="item in detail.checkListResult"
-            :arguable="true"
-            :key="item.id"
-            :check-result-item="item"
-            @argue="argue"
-          />
+          <page-section-loading :show="selfDetailLoading" />
+          <div v-if="!selfDetailLoading">
+            <result-check-list-item
+              v-for="item in detail.checkListResult"
+              :arguable="true"
+              :key="item.id"
+              :check-result-item="item"
+              @argue="argue"
+            />
+          </div>
         </div>
       </div>
     </div>
@@ -30,8 +34,10 @@ import {
 } from "@/api/review";
 import timeMixins from "@/mixins/time";
 import ResultCheckListItem from "@/components/CheckList/ResultCheckListItem";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
   components: {
+    PageSectionLoading,
     ResultCheckListItem,
     VueMarkdown
   },
@@ -41,15 +47,18 @@ export default {
         review: {},
         checkListResult: []
       },
-      submitLoading: false
+      submitLoading: false,
+      selfDetailLoading: false
     };
   },
   mixins: [timeMixins],
   mounted() {
     const { reviewId } = this.$route.params;
-    getStudentReviewSelfDetail(reviewId).then(
-      resp => (this.detail = resp.data)
-    );
+    this.selfDetailLoading = true;
+    getStudentReviewSelfDetail(reviewId).then(resp => {
+      this.detail = resp.data;
+      this.selfDetailLoading = false;
+    });
   },
   methods: {
     async argue(item) {

+ 11 - 3
src/views/student/Review/ReviewDetail/index.vue

@@ -1,6 +1,7 @@
 <template>
   <div>
     <page-header
+      :loading="detailLoading"
       :routes="[{ name: '互评作业', path: 'review' }, { name: '文档详情' }]"
       :tab-list="[
         { name: '待评文档', path: `review/${$route.params.reviewId}` },
@@ -30,7 +31,9 @@
         </div>
       </template>
     </page-header>
-    <page-body> <router-view :docs="detail.documents" /> </page-body>
+    <page-body>
+      <router-view :docs="detail.documents" :detail-loading="detailLoading" />
+    </page-body>
   </div>
 </template>
 <script>
@@ -43,13 +46,18 @@ export default {
   components: { ReviewStateTag, PageBody, PageHeader },
   data() {
     return {
-      detail: {}
+      detail: {},
+      detailLoading: false
     };
   },
   mixins: [timeMixins],
   mounted() {
     const { reviewId } = this.$route.params;
-    getStudentReviewDetail(reviewId).then(resp => (this.detail = resp.data));
+    this.detailLoading = true;
+    getStudentReviewDetail(reviewId).then(resp => {
+      this.detail = resp.data;
+      this.detailLoading = false;
+    });
   }
 };
 </script>

+ 27 - 17
src/views/student/Review/ReviewDocDetail/index.vue

@@ -1,6 +1,7 @@
 <template>
   <div>
     <page-header
+      :loading="fetchLoading"
       :routes="[
         { name: '互评作业', path: 'review' },
         { name: '文档详情', path: `review/${$route.params.reviewId}` },
@@ -19,7 +20,8 @@
     <page-body>
       <div class="columns">
         <div class="column is-8">
-          <div class="page-section">
+          <div v-if="fetchLoading"><page-section-loading show /></div>
+          <div v-else class="page-section">
             <div v-if="detail.docContent" class="content">
               <vue-markdown>{{ detail.docContent }}</vue-markdown>
             </div>
@@ -27,19 +29,22 @@
         </div>
         <div class="column is-4">
           <div class="right-part">
-            <div
-              class="button  is-medium is-primary action-button"
-              :class="{ 'is-loading': submitLoading }"
-              @click="submitCheckList"
-            >
-              提 交
+            <div v-if="fetchLoading"><page-section-loading show /></div>
+            <div v-else>
+              <div
+                class="button  is-medium is-primary action-button"
+                :class="{ 'is-loading': submitLoading }"
+                @click="submitCheckList"
+              >
+                提 交
+              </div>
+              <check-list
+                v-model="detail.checkList"
+                @submit-solution="submitSolution"
+                @accept-argue="acceptArgue"
+                @refuse-argue="refuseArgue"
+              />
             </div>
-            <check-list
-              v-model="detail.checkList"
-              @submit-solution="submitSolution"
-              @accept-argue="acceptArgue"
-              @refuse-argue="refuseArgue"
-            />
           </div>
         </div>
       </div>
@@ -59,8 +64,10 @@ import {
 import timeMixins from "@/mixins/time";
 import ReviewStateTag from "@/components/ReviewStateTag/index";
 import CheckList from "@/components/CheckList/index";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
   components: {
+    PageSectionLoading,
     CheckList,
     ReviewStateTag,
     PageBody,
@@ -73,15 +80,18 @@ export default {
         review: {},
         checkList: []
       },
-      submitLoading: false
+      submitLoading: false,
+      fetchLoading: false
     };
   },
   mixins: [timeMixins],
   mounted() {
     const { reviewId, docReviewId } = this.$route.params;
-    getStudentReviewDocDetail(reviewId, docReviewId).then(
-      resp => (this.detail = resp.data)
-    );
+    this.fetchLoading = true;
+    getStudentReviewDocDetail(reviewId, docReviewId).then(resp => {
+      this.detail = resp.data;
+      this.fetchLoading = false;
+    });
   },
   methods: {
     submitCheckList() {

+ 0 - 0
src/views/student/Review/ReviewSelf/index.vue


+ 20 - 6
src/views/student/Review/index.vue

@@ -1,6 +1,9 @@
 <template>
   <div>
-    <page-header :routes="[{ name: '互评作业', path: 'review' }]">
+    <page-header
+      :loading="loadingReviewList"
+      :routes="[{ name: '互评作业', path: 'review' }]"
+    >
       <template slot="title">
         互评作业
       </template>
@@ -10,7 +13,15 @@
     </page-header>
     <page-body>
       <div class="columns is-multiline is-desktop">
-        <div :key="item.id" v-for="item in review" class="column is-4-desktop">
+        <div v-if="loadingReviewList" class="column is-4-desktop">
+          <card loading />
+        </div>
+        <div
+          v-else
+          :key="item.id"
+          v-for="item in review"
+          class="column is-4-desktop"
+        >
           <router-link :to="`${$route.path}/${item.reviewId}`">
             <card hoverable>
               {{ item.name }}
@@ -45,13 +56,16 @@ export default {
   data() {
     return {
       courses: [],
-      review: []
+      review: [],
+      loadingReviewList: false
     };
   },
   mounted() {
-    getStudentCourseReviewList(this.courses.id).then(
-      value => (this.review = value.data)
-    );
+    this.loadingReviewList = true;
+    getStudentCourseReviewList(this.courses.id).then(value => {
+      this.review = value.data;
+      this.loadingReviewList = false;
+    });
   }
 };
 </script>

+ 22 - 18
src/views/teacher/Code/detail.vue

@@ -3,7 +3,7 @@
     <page-header
       :routes="[
         { name: '代码作业', path: 'code' },
-        { name: `${assignmentInfo.name}`, path: 'code/detail' }
+        { name: '作业详情', path: 'code/${assignmentInfo.id}' }
       ]"
     >
       <template slot="title">
@@ -16,22 +16,9 @@
 
     <page-body>
       <div class="data-table">
-        <!--下拉框&搜索框-->
-        <div class="form-group">
-          <!--搜索框-->
-          <b-field>
-            <b-input
-              placeholder="输入关键字查找小组"
-              type="search"
-              icon="magnify"
-            >
-            </b-input>
-          </b-field>
-        </div>
-        <!---->
         <b-tabs type="is-boxed">
           <b-tab-item label="已提交">
-            <b-table :data="codeProcedureList" paginated per-page="5">
+            <b-table :data="codeProcedureList" detailed detail-key="id">
               <template slot-scope="props">
                 <b-table-column
                   field="id"
@@ -86,11 +73,28 @@
                   >
                 </b-table-column>
               </template>
+              <template slot="detail" slot-scope="props">
+                <article class="media">
+                  <div class="media-content">
+                    <div class="content">
+                      <p>
+                        <strong>小组成员:</strong>
+                        <small
+                          v-for="member of props.row.group.members"
+                          :key="member.id"
+                        >
+                          {{ member.username }}&emsp;
+                        </small>
+                      </p>
+                    </div>
+                  </div>
+                </article>
+              </template>
             </b-table>
           </b-tab-item>
 
           <b-tab-item :label="submitInfo.no">
-            <b-table :data="unsubmitedGroups" paginated per-page="5">
+            <b-table :data="unsubmitedGroups">
               <template slot-scope="props">
                 <b-table-column
                   field="id"
@@ -150,7 +154,7 @@ export default {
     PageBody,
     PageHeader
   },
-  async mounted() {
+  mounted() {
     this.assignmentInfo = this.$route.query.assignmentInfo;
     getCodeProcedureListByCodeId(123).then(res => {
       this.codeProcedureList = res.data;
@@ -181,7 +185,7 @@ export default {
         title: "",
         tag: ""
       };
-      if (state === 2) {
+      if (state === -1) {
         data.title = status.failed;
         data.tag = tag.failed;
       } else if (state === 0) {

+ 58 - 130
src/views/teacher/Code/index.vue

@@ -5,61 +5,61 @@
         代码作业列表
       </template>
       <template slot="content">
-        Here might be a page title
+        共有 {{ codeData.length }} 份作业
       </template>
       <template slot="action">
-        <b-field>
-          <a class="button is-link" @click="updateInfo(-1)">新建代码作业</a>
-        </b-field>
+        <create-assignment
+          @sendCreatedAssignment="pushNewAssignment($event)"
+          :course-id="course.id"
+          :assign-type="assignType"
+        ></create-assignment>
       </template>
     </page-header>
+
     <page-body>
       <div class="data-table">
-        <b-table
-          :data="codeData"
-          paginated
-          per-page="5"
-          detailed
-          detail-key="id"
-        >
+        <b-table :data="codeData" detailed detail-key="id">
           <template slot-scope="props">
-            <b-table-column field="id" label="作业ID" numeric sortable centered>
+            <b-table-column field="id" label="ID" numeric sortable centered>
               {{ props.row.id }}
             </b-table-column>
 
             <b-table-column label="作业名称" centered>
               <router-link
                 :to="{
-                  path: `/course-teacher/:courseId/code/detail`,
-                  query: { assignmentInfo: props.row }
+                  path: `code/${props.row.id}`,
+                  query: {
+                    assignmentInfo: props.row.problem
+                  }
                 }"
               >
                 {{ props.row.name }}
               </router-link>
             </b-table-column>
-            <b-table-column label="作业状态" centered>
+            <b-table-column label="状态" centered>
               {{ props.row.status }}
             </b-table-column>
 
             <b-table-column label="开始时间" centered>
-              {{ new Date(props.row.startAt * 1000).getFullYear() }}-{{
-                new Date(props.row.startAt * 1000).getMonth() + 1
-              }}-{{ new Date(props.row.startAt * 1000).getDate() }}
+              {{ displayUnixTime(props.row.startAt) }}
             </b-table-column>
 
             <b-table-column label="结束时间" centered>
-              {{ new Date(props.row.endAt * 1000).getFullYear() }}-{{
-                new Date(props.row.endAt * 1000).getMonth() + 1
-              }}-{{ new Date(props.row.endAt * 1000).getDate() }}
+              {{ displayUnixTime(props.row.endAt) }}
             </b-table-column>
-
             <b-table-column label="修改" centered>
-              <a @click="updateInfo(props.row.id)">修改</a>
+              <update-assignment
+                :assign-type="assignType"
+                :update-item="props.row"
+                @updateDocData="updateData($event)"
+              ></update-assignment>
             </b-table-column>
             <b-table-column label="删除" centered>
-              <a style="color:#b34141" @click="confirmDelete(props.row.id)"
-                >删除</a
-              >
+              <delete-assignment
+                :del-id="props.row.id"
+                :assign-type="assignType"
+                @del="deleteAssignment(props.row.id)"
+              ></delete-assignment>
             </b-table-column>
           </template>
 
@@ -78,40 +78,6 @@
         </b-table>
       </div>
     </page-body>
-
-    <!--新建文档作业模态框-->
-    <b-modal :active.sync="isCardModalActive" :width="640" scroll="keep">
-      <div class="card">
-        <div class="title">{{ modal.title }}</div>
-        <div class="content">
-          <b-field label="作业名称">
-            <b-input v-model="codeAssignment.name"></b-input>
-          </b-field>
-          <b-field label="作业描述">
-            <b-input v-model="codeAssignment.description"></b-input>
-          </b-field>
-          <b-field label="开始时间">
-            <b-datepicker
-              v-model="codeAssignment.startAt"
-              placeholder="Click to select..."
-              icon="calendar-today"
-            >
-            </b-datepicker>
-          </b-field>
-          <b-field label="结束时间">
-            <b-datepicker
-              v-model="codeAssignment.endAt"
-              placeholder="Click to select..."
-              icon="calendar-today"
-            >
-            </b-datepicker>
-          </b-field>
-          <a class="button is-link" @click="createCodeAssignment">{{
-            modal.submit
-          }}</a>
-        </div>
-      </div>
-    </b-modal>
   </div>
 </template>
 
@@ -119,97 +85,59 @@
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
 import { getCodeAssignmentListByTeacher } from "@/api/assignment";
+import timeMixins from "@/mixins/time";
+import { mapState } from "vuex";
+import DeleteAssignment from "@/components/AssignmentCRUD/DeleteAssignment";
+import UpdateAssignment from "@/components/AssignmentCRUD/UpdateAssignment";
+import CreateAssignment from "@/components/AssignmentCRUD/CreateAssignment";
+
 export default {
   components: {
     PageBody,
-    PageHeader
+    PageHeader,
+    DeleteAssignment,
+    UpdateAssignment,
+    CreateAssignment
   },
+  mixins: [timeMixins],
   mounted() {
-    getCodeAssignmentListByTeacher(123).then(res => {
+    getCodeAssignmentListByTeacher(this.course.id).then(res => {
       this.codeData = res.data;
     });
   },
   data() {
     return {
       codeData: [],
-      codeAssignment: {
-        id: -1,
-        name: "",
-        description: "",
-        startAt: new Date(),
-        endAt: new Date()
-      },
-      isCardModalActive: false,
-      modal: {
-        title: "新建文档作业",
-        submit: "确认创建"
-      }
+      assignType: 0
     };
   },
   methods: {
-    confirmDelete(id) {
-      this.$dialog.confirm({
-        message: "确认删除?",
-        confirmText: "删除",
-        type: "is-danger",
-        onConfirm: () => {
-          for (var item of this.codeData) {
-            if (item.id === id) {
-              this.codeData.pop(item);
-            }
-          }
-          this.$toast.open("删除成功");
-        }
-      });
+    pushNewAssignment(item) {
+      this.codeData.push(item);
     },
-    updateInfo(id) {
-      this.isCardModalActive = true;
-      if (id !== -1) {
-        this.modal.submit = "确认修改";
-        this.modal.title = "修改文档作业";
-        for (let item of this.codeData) {
-          if (item.id === id) {
-            this.codeAssignment.id = id;
-            this.codeAssignment.name = item.name;
-            this.codeAssignment.description = item.description;
-            this.codeAssignment.startAt = new Date(item.startAt);
-            this.codeAssignment.endAt = new Date(item.endAt);
-            break;
-          }
+    updateData(modifiedItem) {
+      let length = this.codeData.length;
+      for (let i = 0; i < length; i++) {
+        if (modifiedItem.id === this.codeData[i].id) {
+          this.codeData.splice(i, 1, modifiedItem);
+          break;
         }
-      } else {
-        this.modal.title = "新建文档作业";
-        this.modal.submit = "确认创建";
-        this.codeAssignment.id = -1;
-        this.codeAssignment.name = "";
-        this.codeAssignment.description = "";
-        this.codeAssignment.startAt = new Date();
-        this.codeAssignment.endAt = new Date();
       }
     },
-    createCodeAssignment() {
-      this.isCardModalActive = false;
-      if (this.codeAssignment.id === -1) {
-        var itemsCount = this.codeData.length;
-        this.codeData.push({
-          id: itemsCount + 1,
-          name: this.codeAssignment.name,
-          description: this.codeAssignment.description,
-          startAt: this.codeAssignment.startAt.getTime(),
-          endAt: this.codeAssignment.endAt.getTime()
-        });
-      } else {
-        for (var item of this.codeData) {
-          if (item.id === this.codeAssignment.id) {
-            item.name = this.codeAssignment.name;
-            item.description = this.codeAssignment.description;
-            item.startAt = this.codeAssignment.startAt.getTime();
-            item.endAt = this.codeAssignment.endAt.getTime();
-            break;
-          }
+    deleteAssignment(delId) {
+      let length = this.codeData.length;
+      for (let i = 0; i < length; i++) {
+        if (this.codeData[i].id === delId) {
+          this.codeData.splice(i, 1);
+          break;
         }
       }
     }
+  },
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
   }
 };
 </script>

+ 4 - 68
src/views/teacher/Code/test.vue

@@ -3,8 +3,8 @@
     <page-header
       :routes="[
         { name: '代码作业', path: 'code' },
-        { name: '购物车web项目', path: 'code/detail' },
-        { name: '测试信息', path: 'code/detail/test' }
+        { name: '作业详情', path: `code/${assignmentInfo.id}` },
+        { name: '测试信息', path: `${$route.path}` }
       ]"
     >
       <template slot="title">
@@ -16,30 +16,7 @@
     </page-header>
 
     <page-body>
-      <div class="data-table">
-        <b-collapse
-          :open="false"
-          class="collapseeeeeee"
-          v-for="item in testResult"
-          :key="item.id"
-        >
-          <button class="button is-primary " slot="trigger">
-            测试时间:2019-1-18 23:02{{ item.id }}
-          </button>
-          <div class="notification card">
-            <div class="content">
-              <h3>Build Info</h3>
-              <p>{{ item.buildResult }}</p>
-              <h3>Deploy Info</h3>
-              <p>{{ item.deployResult }}</p>
-              <h3>Unit Test Info</h3>
-              <p>{{ item.unitTestResult }}</p>
-              <h3>Functional Test Info</h3>
-              <p>{{ item.functionTestResult }}</p>
-            </div>
-          </div>
-        </b-collapse>
-      </div>
+      <div class="data-table">和学生代码作业的测试信息的显示格式和接口相同</div>
     </page-body>
   </div>
 </template>
@@ -48,45 +25,6 @@
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
 
-const testResult = [
-  {
-    id: "1547130528000",
-    buildResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    deployResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    unitTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    functionTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS"
-  },
-  {
-    id: "1547130722000",
-    buildResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    deployResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    unitTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    functionTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS"
-  },
-  {
-    id: "1547123230722000",
-    buildResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    deployResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    unitTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    functionTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS"
-  },
-  {
-    id: "154712320722000",
-    buildResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    deployResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    unitTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    functionTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS"
-  },
-  {
-    id: "1547130434722000",
-    buildResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    deployResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    unitTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS",
-    functionTestResult: "SUCCESSSUCCESSSUCCESSSUCCESSSUCCESSSUCCESS"
-  }
-];
-const showResult = [];
-
 export default {
   components: {
     PageBody,
@@ -97,9 +35,7 @@ export default {
   },
   data() {
     return {
-      assignmentInfo: {},
-      showResult,
-      testResult
+      assignmentInfo: {}
     };
   }
 };

+ 42 - 46
src/views/teacher/Document/detail.vue

@@ -1,44 +1,28 @@
 <template>
   <div>
     <page-header
+      :loading="isLoading"
       :routes="[
         { name: '文档作业', path: 'document' },
-        { name: `${title}`, path: 'document/detail' }
+        { name: `作业详情`, path: `document` }
       ]"
     >
       <template slot="title">
-        {{ assignmentInfo.name }}
+        {{ question.name }}
       </template>
       <template slot="content">
-        题目描述:{{ assignmentInfo.description }} <br />
+        题目描述:{{ question.description }}
+      </template>
+      <template slot="extraContent">
         题目进行阶段:{{ status }}
       </template>
     </page-header>
 
     <page-body>
       <div class="data-table">
-        <!--下拉框&搜索框-->
-        <div class="form-group">
-          <!--搜索框-->
-          <b-field>
-            <b-input
-              placeholder="输入关键字查找小组"
-              type="search"
-              icon="magnify"
-            >
-            </b-input>
-          </b-field>
-        </div>
-
         <b-tabs>
           <b-tab-item :label="submitInfo.yes">
-            <b-table
-              :data="submittedData"
-              paginated
-              per-page="5"
-              detailed
-              detail-key="id"
-            >
+            <b-table :data="submittedData" detailed detail-key="id">
               <template slot-scope="props">
                 <b-table-column
                   field="id"
@@ -64,8 +48,12 @@
                   {{ props.row.result }}
                 </b-table-column>
                 <b-table-column field="difficulty" label="详情" centered
-                  ><a :href="props.row.url" target="_blank"
-                    >查看详情-加提示框</a
+                  ><router-link
+                    :to="{
+                      path: `${$route.path}/content`,
+                      query: { assignId: docId }
+                    }"
+                    >查看详情</router-link
                   >
                 </b-table-column>
               </template>
@@ -75,10 +63,13 @@
                   <div class="media-content">
                     <div class="content">
                       <p>
-                        <strong>小组成员:</strong
-                        ><small>{{
-                          props.row.group ? props.row.group.members : ""
-                        }}</small>
+                        <strong>小组成员:</strong>
+                        <small
+                          v-for="member of props.row.group.members"
+                          :key="member.id"
+                        >
+                          {{ member.username }}&emsp;
+                        </small>
                         <br />
                         <strong>git地址:</strong
                         ><small>{{ props.row.url }}</small> <br />
@@ -91,7 +82,7 @@
           </b-tab-item>
 
           <b-tab-item :label="submitInfo.no">
-            <b-table :data="unsubmitedGroups" paginated per-page="5">
+            <b-table :data="unsubmitedGroups">
               <template slot-scope="props">
                 <b-table-column
                   field="id"
@@ -125,43 +116,48 @@ import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
 import { getSubmittedDocResultListByDocId } from "@/api/assignment";
 import { getUnsubmittedDocGroupsByDocId } from "@/api/assignment";
-
-const assignmentInfo = {
-  id: 0,
-  name: "",
-  description: ""
-};
+import { mapState } from "vuex";
 
 export default {
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
+  },
   components: {
     PageBody,
     PageHeader
   },
+  created() {
+    this.docId = this.$route.query.docId;
+    this.question = this.$route.query.question;
+  },
   mounted() {
-    this.assignmentInfo = this.$route.query.assignmentInfo;
-    this.title = this.$route.query.title;
-    getSubmittedDocResultListByDocId(123).then(res => {
-      this.status = res.status;
-      this.submittedData = res.data;
+    this.isLoading = true;
+    getSubmittedDocResultListByDocId(this.docId).then(res => {
+      this.status = res.data.status;
+      this.submittedData = res.data.result;
       this.submitInfo.yes = "已提交:" + this.submittedData.length;
+      this.isLoading = false;
     });
-    getUnsubmittedDocGroupsByDocId(123).then(res => {
+    getUnsubmittedDocGroupsByDocId(this.docId).then(res => {
       this.unsubmitedGroups = res.data;
       this.submitInfo.no = "未提交:" + this.unsubmitedGroups.length;
+      this.isLoading = false;
     });
   },
-
   data() {
     return {
-      title: "",
       submittedData: [],
       unsubmitedGroups: [],
       status: "",
-      assignmentInfo,
+      question: null,
+      docId: null,
       submitInfo: {
         yes: "",
         no: ""
-      }
+      },
+      isLoading: false
     };
   }
 };

+ 39 - 0
src/views/teacher/Document/docContent.vue

@@ -0,0 +1,39 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '文档作业', path: 'document' },
+        {
+          name: `作业详情`,
+          path: `document/${docId}`
+        },
+        { name: `文档内容`, path: `${$route.path}` }
+      ]"
+    >
+      <template slot="title">
+        文档内容
+      </template>
+    </page-header>
+    <page-body> <div class="data-table">文档内容</div> </page-body>
+  </div>
+</template>
+
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  data() {
+    return {
+      docId: null
+    };
+  },
+  created() {
+    this.docId = this.$route.query.assignId;
+  },
+  mounted() {}
+};
+</script>

+ 64 - 145
src/views/teacher/Document/index.vue

@@ -5,24 +5,20 @@
         文档作业列表
       </template>
       <template slot="content">
-        Here might be a page title
+        共有 {{ docData.length }} 份作业
       </template>
       <template slot="action">
-        <b-field>
-          <a class="button is-link" @click="updateInfo(-1)">新建文档作业</a>
-        </b-field>
+        <create-assignment
+          @sendCreatedAssignment="pushNewAssignment($event)"
+          :course-id="course.id"
+          :assign-type="assignType"
+        ></create-assignment>
       </template>
     </page-header>
 
     <page-body>
       <div class="data-table">
-        <b-table
-          :data="docData"
-          paginated
-          per-page="5"
-          detailed
-          detail-key="id"
-        >
+        <b-table :data="docData" detailed detail-key="id">
           <template slot-scope="props">
             <b-table-column field="id" label="ID" numeric sortable centered>
               {{ props.row.id }}
@@ -31,11 +27,8 @@
             <b-table-column label="作业名称" centered>
               <router-link
                 :to="{
-                  path: `/course-teacher/:courseId/document/detail`,
-                  query: {
-                    assignmentInfo: props.row.problem,
-                    title: props.row.name
-                  }
+                  path: `${$route.path}/${props.row.id}`,
+                  query: { docId: props.row.id, question: props.row.problem }
                 }"
               >
                 {{ props.row.name }}
@@ -46,23 +39,25 @@
             </b-table-column>
 
             <b-table-column label="开始时间" centered>
-              {{ new Date(props.row.startAt).getFullYear() }}-{{
-                new Date(props.row.startAt).getMonth() + 1
-              }}-{{ new Date(props.row.startAt).getDate() }}
+              {{ displayUnixTime(props.row.startAt) }}
             </b-table-column>
 
             <b-table-column label="结束时间" centered>
-              {{ new Date(props.row.endAt).getFullYear() }}-{{
-                new Date(props.row.endAt).getMonth() + 1
-              }}-{{ new Date(props.row.endAt).getDate() }}
+              {{ displayUnixTime(props.row.endAt) }}
             </b-table-column>
             <b-table-column label="修改" centered>
-              <a @click="updateInfo(props.row.id)">修改</a>
+              <update-assignment
+                :assign-type="assignType"
+                :update-item="props.row"
+                @updateDocData="updateData($event)"
+              ></update-assignment>
             </b-table-column>
             <b-table-column label="删除" centered>
-              <a style="color:#b34141" @click="confirmDelete(props.row.id)"
-                >删除</a
-              >
+              <delete-assignment
+                :del-id="props.row.id"
+                :assign-type="assignType"
+                @del="deleteAssignment(props.row.id)"
+              ></delete-assignment>
             </b-table-column>
           </template>
 
@@ -71,7 +66,7 @@
               <div class="media-content">
                 <div class="content">
                   <p>
-                    <strong>作业描述:</strong><br />
+                    <strong>作业描述:</strong>
                     <small>{{ props.row.description }}</small> <br />
                   </p>
                 </div>
@@ -81,148 +76,63 @@
         </b-table>
       </div>
     </page-body>
-
-    <!--新建文档作业模态框-->
-    <b-modal :active.sync="isCardModalActive" :width="640" scroll="keep">
-      <div class="card">
-        <div class="title">{{ modal.title }}</div>
-        <div class="content">
-          <b-field label="作业名称">
-            <b-input v-model="docAssignment.name"></b-input>
-          </b-field>
-          <b-field label="作业描述">
-            <b-input v-model="docAssignment.description"></b-input>
-          </b-field>
-          <b-field label="开始时间">
-            <b-datepicker
-              v-model="docAssignment.startAt"
-              placeholder="Click to select..."
-              icon="calendar-today"
-            >
-            </b-datepicker>
-          </b-field>
-          <b-field label="结束时间">
-            <b-datepicker
-              v-model="docAssignment.endAt"
-              placeholder="Click to select..."
-              icon="calendar-today"
-            >
-            </b-datepicker>
-          </b-field>
-          <a class="button is-link" @click="createDocAssignment()">{{
-            modal.submit
-          }}</a>
-        </div>
-      </div>
-    </b-modal>
   </div>
 </template>
 
 <script>
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
-import { required } from "vuelidate/lib/validators";
 import { getDocAssignmentListTeacherByCrs } from "@/api/assignment";
+import timeMixins from "@/mixins/time";
+import { mapState } from "vuex";
+import DeleteAssignment from "@/components/AssignmentCRUD/DeleteAssignment";
+import UpdateAssignment from "@/components/AssignmentCRUD/UpdateAssignment";
+import CreateAssignment from "@/components/AssignmentCRUD/CreateAssignment";
+
 export default {
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
+  },
   components: {
     PageBody,
-    PageHeader
+    PageHeader,
+    DeleteAssignment,
+    UpdateAssignment,
+    CreateAssignment
   },
+  mixins: [timeMixins],
   mounted() {
-    getDocAssignmentListTeacherByCrs(123).then(res => {
+    getDocAssignmentListTeacherByCrs(this.course.id).then(res => {
       this.docData = res.data;
     });
   },
   data() {
     return {
-      docData: [],
-      docAssignment: {
-        id: -1,
-        name: "",
-        description: "",
-        startAt: new Date(),
-        endAt: new Date()
-      },
-      isCardModalActive: false,
-      modal: {
-        title: "新建文档作业",
-        submit: "确认创建"
-      }
+      assignType: 1,
+      docData: []
     };
   },
-  validations: {
-    docAssignment: {
-      name: {
-        required
-      },
-      description: {
-        required
-      }
-    }
-  },
   methods: {
-    confirmDelete(id) {
-      this.$dialog.confirm({
-        message: "确认删除?",
-        confirmText: "删除",
-        type: "is-danger",
-        onConfirm: () => {
-          for (var item of this.data) {
-            if (item.id === id) {
-              this.data.pop(item);
-            }
-          }
-          this.$toast.open("删除成功");
-        }
-      });
+    pushNewAssignment(item) {
+      this.docData.push(item);
     },
-    updateInfo(id) {
-      this.isCardModalActive = true;
-      if (id !== -1) {
-        this.modal.title = "修改文档作业";
-        this.modal.submit = "确认修改";
-        for (let item of this.data) {
-          if (item.id === id) {
-            this.docAssignment.id = id;
-            this.docAssignment.name = item.name;
-            this.docAssignment.description = item.description;
-            this.docAssignment.startAt = new Date(item.startAt);
-            this.docAssignment.endAt = new Date(item.endAt);
-            break;
-          }
+    updateData(modifiedItem) {
+      let length = this.docData.length;
+      for (let i = 0; i < length; i++) {
+        if (modifiedItem.id === this.docData[i].id) {
+          this.docData.splice(i, 1, modifiedItem);
+          break;
         }
-      } else {
-        this.modal.title = "新建文档作业";
-        this.modal.submit = "确认创建";
-        this.docAssignment.id = -1;
-        this.docAssignment.name = "";
-        this.docAssignment.description = "";
-        this.docAssignment.startAt = new Date();
-        this.docAssignment.endAt = new Date();
       }
     },
-    createDocAssignment() {
-      this.isCardModalActive = false;
-      if (this.docAssignment.id === -1) {
-        // 新建作业
-        var itemsCount = this.data.length;
-        this.data.push({
-          id: itemsCount + 1,
-          name: this.docAssignment.name,
-          description: this.docAssignment.description,
-          startAt: this.docAssignment.startAt.getTime(),
-          endAt: this.docAssignment.endAt.getTime()
-        });
-      } else {
-        // 修改作业
-        for (var item of this.data) {
-          if (item.id === this.docAssignment.id) {
-            item.name = this.docAssignment.name;
-            item.description = this.docAssignment.description;
-            item.startAt = this.docAssignment.startAt.getTime();
-            item.endAt = this.docAssignment.endAt.getTime();
-            break;
-          }
+    deleteAssignment(delId) {
+      let length = this.docData.length;
+      for (let i = 0; i < length; i++) {
+        if (this.docData[i].id === delId) {
+          this.docData.splice(i, 1);
+          break;
         }
       }
     }
@@ -247,4 +157,13 @@ export default {
     }
   }
 }
+.description {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 1;
+  word-wrap: break-word;
+  word-break: break-all;
+  display: -webkit-box;
+}
 </style>

+ 2 - 0
src/views/teacher/Question/index.vue

@@ -23,6 +23,7 @@
             </b-select>
           </b-field>
           <!--搜索框-->
+
           <b-field position="is-centered">
             <b-input
               placeholder="输入关键字查找题目"
@@ -32,6 +33,7 @@
             </b-input>
             <p class="control"><button class="button is-link">查找</button></p>
           </b-field>
+
           <!--<b-field>-->
           <!--<b-input-->
           <!--placeholder="输入关键字查找题目"-->

+ 2 - 0
src/views/teacher/Review/index.vue

@@ -268,6 +268,8 @@ export default {
       }
     },
     async submit() {
+      if (this.submitting) return;
+
       this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.$invalid) {