Prechádzať zdrojové kódy

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

Unknown 7 rokov pred
rodič
commit
1e91659c07
47 zmenil súbory, kde vykonal 3439 pridanie a 472 odobranie
  1. 6 0
      mock/modules/group/index.js
  2. 16 6
      mock/modules/question/index.js
  3. 2 2
      mock/serializers/course/GroupSerializer.js
  4. 6 5
      mock/serializers/question/QuestionSerializer.js
  5. 3 1
      mock/serializers/review/CheckListResultItemSerializer.js
  6. 2 0
      package.json
  7. 9 0
      src/api/group.js
  8. 5 5
      src/api/review.js
  9. 1 1
      src/assets/scss/_variables.scss
  10. 10 3
      src/assets/scss/app.scss
  11. 0 0
      src/assets/undraw_having_fun.svg
  12. 22 2
      src/components/CardJudgeItem/index.vue
  13. 143 0
      src/components/CheckList/ResultCheckListItem.vue
  14. 118 6
      src/components/CheckList/index.vue
  15. 5 0
      src/components/PageHeader/index.scss
  16. 31 0
      src/data/checkListItemState.js
  17. 2 2
      src/data/reviewState.js
  18. 1 0
      src/layouts/HomeLayout/HomeHeader/index.vue
  19. 0 2
      src/layouts/TeacherMainLayout.vue
  20. 3 2
      src/layouts/courseLayouts/LayoutStructure.vue
  21. 1 0
      src/mixins/time.js
  22. 77 2
      src/router/routes.js
  23. 49 0
      src/views/student/Code/detail.vue
  24. 124 1
      src/views/student/Code/index.vue
  25. 247 1
      src/views/student/Group/index.vue
  26. 25 0
      src/views/student/Review/ReviewDetail/ReviewDocs/index.vue
  27. 90 0
      src/views/student/Review/ReviewDetail/ReviewSelfDetail/index.vue
  28. 6 18
      src/views/student/Review/ReviewDetail/index.vue
  29. 36 4
      src/views/student/Review/ReviewDocDetail/index.vue
  30. 0 0
      src/views/student/Review/ReviewSelf/index.vue
  31. 252 0
      src/views/teacher/Code/detail.vue
  32. 250 0
      src/views/teacher/Code/index.vue
  33. 116 0
      src/views/teacher/Code/test.vue
  34. 249 0
      src/views/teacher/Document/detail.vue
  35. 242 0
      src/views/teacher/Document/index.vue
  36. 35 2
      src/views/teacher/Home/index.vue
  37. 160 138
      src/views/teacher/Question/index.vue
  38. 123 0
      src/views/teacher/Question/questionDetail.vue
  39. 239 127
      src/views/teacher/Result/index.vue
  40. 114 0
      src/views/teacher/Review/ReviewDetail/Detail/index.vue
  41. 121 0
      src/views/teacher/Review/ReviewDetail/GroupArgueDetail/index.vue
  42. 100 0
      src/views/teacher/Review/ReviewDetail/GroupDocDetail/index.vue
  43. 51 0
      src/views/teacher/Review/ReviewDetail/Groups/index.vue
  44. 142 0
      src/views/teacher/Review/ReviewDetail/index.vue
  45. 142 0
      src/views/teacher/Review/assignment.vue
  46. 63 0
      src/views/teacher/Review/index.vue
  47. 0 142
      yarn.lock

+ 6 - 0
mock/modules/group/index.js

@@ -20,4 +20,10 @@ router
     })
   );
 
+router.route("/:groupId").get((req, res) =>
+  res.send({
+    data: groupSerializer(req.params.groupId)
+  })
+);
+
 module.exports = router;

+ 16 - 6
mock/modules/question/index.js

@@ -6,18 +6,28 @@ const QuestionTypeSerializer = require("../../serializers/question/QuestionTypeS
 //获取全部题目分类
 router.route("/teacher/questionType").get((req, res) => {
   res.send({
-    data: [
-      QuestionTypeSerializer(),
-      QuestionTypeSerializer(),
-      QuestionTypeSerializer()
-    ]
+    data: [QuestionTypeSerializer(), QuestionTypeSerializer()]
   });
 });
 
 //按题目分类获得题目(分页) || 根据作业分类获得题目
 router.route("/teacher/questions").get((req, res) => {
   res.send({
-    data: [QuestionSerializer(), QuestionSerializer(), QuestionSerializer()],
+    data: [
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer(),
+      QuestionSerializer()
+    ],
     pageNum: 1
   });
 });

+ 2 - 2
mock/serializers/course/GroupSerializer.js

@@ -14,9 +14,9 @@ const userSerializer = require("../user/UserSerializer");
  * @description 生成课程信息
  * @returns {GroupSerializerType}
  */
-module.exports = () => {
+module.exports = (id = makeId()) => {
   return {
-    id: makeId(),
+    id,
     name: "tag-maker",
     code: "xxxxx",
     members: [

+ 6 - 5
mock/serializers/question/QuestionSerializer.js

@@ -22,19 +22,20 @@ const fileSerializer = require("../FileSerializer");
 module.exports = () => {
   return {
     id: makeId(),
-    name: "物流系统需求规格说明书",
-    description: "某公司为xxx快递公司,请为该公司设计一个物流管理系统",
+    name: "独立的小易",
+    description:
+      "小易为了向他的父母表现他已经长大独立了,他决定搬出去自己居住一段时间。一个人生活增加了许多花费: 小易每天必须吃一个水果并且需要每天支付x元的房屋租金。当前小易手中已经有f个水果和d元钱,小易也能去商店购买一些水果,商店每个水果售卖p元。小易为了表现他独立生活的能力,希望能独立生活的时间越长越好,小易希望你来帮他计算一下他最多能独立生活多少天。",
     assignmentType: {
       name: "DOCUMENT",
       value: "文档作业"
     },
     type: {
-      name: "DOCUMENT",
-      value: "文档作业"
+      name: "JAVA",
+      value: "JAVA"
     },
     creator: makeId(),
     expectTime: 20,
     difficulty: 1,
-    file: fileSerializer()
+    file: [fileSerializer(), fileSerializer()]
   };
 };

+ 3 - 1
mock/serializers/review/CheckListResultItemSerializer.js

@@ -15,6 +15,7 @@ const makeId = require("../../util/makeId");
  * @property {number|string} groupId 小组 id
  * @property {boolean} level 小组做出的评价
  * @property {string} comment 小组做出的备注
+ * @property {boolean} isArgued 是否已经申请过重评
  */
 
 /**
@@ -28,7 +29,8 @@ module.exports = ({ id = makeId() }) => {
   const groupResult = () => ({
     groupId: makeId(),
     level: true,
-    comment: "小组做出的备注"
+    comment: "小组做出的备注",
+    isArgued: false
   });
 
   return {

+ 2 - 0
package.json

@@ -12,9 +12,11 @@
   },
   "dependencies": {
     "buefy": "^0.7.1",
+    "chart.js": "^2.7.3",
     "dayjs": "^1.7.8",
     "register-service-worker": "^1.5.2",
     "vue": "^2.5.17",
+    "vue-chartjs": "^3.4.0",
     "vue-markdown": "^2.2.4",
     "vue-router": "^3.0.1",
     "vuelidate": "^0.7.4",

+ 9 - 0
src/api/group.js

@@ -39,3 +39,12 @@ export const putStudentGroup = code => {
     }
   });
 };
+
+/**
+ * 获得小组详情
+ * @param groupId
+ * @returns {Promise<{data:GroupSerializerType}>}
+ */
+export const getGroupDetail = groupId => {
+  return request(`${GROUP_MODULE}/${groupId}`);
+};

+ 5 - 5
src/api/review.js

@@ -186,7 +186,7 @@ export const postStudentArgueReview = ({
   groupId,
   remark = ""
 }) => {
-  return request(`${REVIEW_MODULE}/argue`, {
+  return request(`${REVIEW_MODULE}/student/argue`, {
     method: "POST",
     body: { reviewId, id, groupId, remark }
   });
@@ -213,15 +213,15 @@ export const postStudentArgueAcceptation = (reviewId, checkListId, accept) => {
  * 学生(评审方)提交argue [标记为已处理完成]
  * @param {string|number} reviewId
  * @param {string|number} checkListId
- * @param {CheckListItemSerializerType[]} checkList
+ * @param {CheckListItemSerializerType} checkListItem
  * @return {Promise<{data:CheckListItemSerializerType[]}>}
  */
-export const postStudentArgueEnd = (reviewId, checkListId, checkList) => {
+export const postStudentArgueEnd = (reviewId, checkListId, checkListItem) => {
   return request(
-    `${REVIEW_MODULE}/student/review/${reviewId}/checklist/${checkListId}/argue/acceptation`,
+    `${REVIEW_MODULE}/student/review/${reviewId}/checklist/${checkListId}/argue/end`,
     {
       method: "POST",
-      body: { data: checkList }
+      body: { data: checkListItem }
     }
   );
 };

+ 1 - 1
src/assets/scss/_variables.scss

@@ -130,7 +130,7 @@ $pre-background: $background !default;
 
 // Link colors
 
-$link: #6172ec !default;
+$link: $blue !default;
 $link-invert: $blue-invert !default;
 $link-visited: $purple !default;
 

+ 10 - 3
src/assets/scss/app.scss

@@ -48,7 +48,7 @@ $default-margin-between:1.5rem;
   }
   .menu-list {
     font-weight: bold;
-    font-size: 1.1rem;
+    font-size: 1rem;
     li {
       margin-bottom: 10px;
       a {
@@ -65,13 +65,20 @@ $default-margin-between:1.5rem;
     }
   }
   .is-active {
-    background-color: rgba(0, 0, 0, 0.18) !important;
+    background-color: rgba(255, 255, 255, 0.18) !important;
   }
 }
 
 .tabs a,
 .tabs ul{
-  border-bottom-color: white;
+  border-bottom-color: #dbdbdb;
+  //border-bottom-color: white;
+}
+.data-table {
+  width: 100%;
+  padding: 30px;
+  /*min-height: 80vh;*/
+  background-color: #ffffff;
 }
 
 .content blockquote {

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/undraw_having_fun.svg


+ 22 - 2
src/components/CardJudgeItem/index.vue

@@ -1,16 +1,30 @@
 <template>
-  <div :class="{ 'judge-section': true, 'judge-success': success }">
+  <div
+    :class="{
+      'judge-section': true,
+      'judge-success': success,
+      'judge-primary': state === 'SOLVING',
+      'judge-warning': state === 'PENDING',
+      'judge-end': state === 'ARGUED'
+    }"
+  >
     <slot></slot>
   </div>
 </template>
 
 <script>
+import { DEFAULT } from "@/data/checkListItemState";
+
 export default {
   name: "CardJudgeItem",
   props: {
     success: {
       type: Boolean,
       default: false
+    },
+    state: {
+      type: String,
+      default: DEFAULT
     }
   }
 };
@@ -28,6 +42,12 @@ export default {
   border-left: 6px solid $success;
 }
 .judge-primary {
-  border-left: 6px solid $info;
+  border-left: 6px solid $primary;
+}
+.judge-warning {
+  border-left: 6px solid $warning;
+}
+.judge-end {
+  border-left: 6px solid $grey;
 }
 </style>

+ 143 - 0
src/components/CheckList/ResultCheckListItem.vue

@@ -0,0 +1,143 @@
+<template>
+  <card-judge-item :success="pass">
+    <div class="result-section">
+      <strong>{{ checkResultItem.content }}</strong>
+      <div
+        :class="{ 'group-line--hover': !result.isArgued }"
+        class="group-line"
+        v-for="result in checkResultItem.results"
+        :key="result.rid"
+        @click="argue(result)"
+      >
+        <strong style="padding-right:10px;color:grey"
+          >+ id:{{ result.groupId + "\t" }}</strong
+        >
+        <span class="state state-success" v-if="result.level">PASS</span>
+        <span class="state state-error" v-else>ERROR</span>
+        <span class="comments">{{ result.comment }}</span>
+        <b-tag
+          v-if="result.isArgued"
+          type="is-warning"
+          class="state"
+          style="margin-left: 10px;"
+          >已申请
+        </b-tag>
+      </div>
+    </div>
+
+    <b-modal :active.sync="showModal" :width="400">
+      <div class="card">
+        <div class="title">申请重判</div>
+        <div class="content">
+          <b-field label="理由">
+            <b-input maxlength="200" type="textarea" v-model="remark"></b-input>
+          </b-field>
+
+          <a
+            class="button is-link"
+            :class="{ 'is-loading': submitLoading }"
+            @click="submit()"
+          >
+            提交重判申请
+          </a>
+        </div>
+      </div>
+    </b-modal>
+  </card-judge-item>
+</template>
+<script>
+import CardJudgeItem from "@/components/CardJudgeItem/index";
+export default {
+  name: "ResultCheckListItem",
+  components: { CardJudgeItem },
+  props: {
+    checkResultItem: {
+      type: Object,
+      default: () => ({
+        results: []
+      })
+    },
+    arguable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      selectedResult: {},
+      showModal: false,
+      remark: "",
+      submitLoading: false
+    };
+  },
+  computed: {
+    pass() {
+      const { results = [] } = this.checkResultItem;
+      return results.filter(result => result.level === false).length === 0;
+    }
+  },
+  methods: {
+    argue(result) {
+      if (this.arguable && !result.isArgued) {
+        this.selectedResult = result;
+        this.showModal = true;
+      }
+    },
+    async submit() {
+      this.submitLoading = true;
+      await this.$emit("argue", {
+        id: this.checkResultItem.id,
+        groupId: this.selectedResult.groupId,
+        remark: this.remark
+      });
+      this.submitLoading = false;
+      this.showModal = false;
+      this.remark = "";
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../assets/scss/app";
+.result-section {
+  padding: $default-margin;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.card {
+  padding: $default-margin-between;
+  border-radius: $default-radius;
+}
+
+.state {
+  padding: 2px 5px;
+  font-weight: bold;
+  font-size: 12px;
+  border-radius: 3px;
+  color: white;
+}
+
+.state-success {
+  background: $success;
+}
+.state-error {
+  background: $danger;
+}
+.comments {
+  padding-left: 10px;
+  display: inline-block;
+  color: $grey;
+  font-size: 14px;
+}
+
+.group-line {
+  padding: 2px 0;
+  border-radius: $default-radius;
+}
+
+.group-line--hover:hover {
+  cursor: pointer;
+  background-color: #f4f4f4;
+}
+</style>

+ 118 - 6
src/components/CheckList/index.vue

@@ -1,23 +1,75 @@
 <template>
   <div>
-    <card-judge-item v-for="item in value" :key="item.id" :success="item.level">
-      <b-tooltip :label="item.explain" position="is-left">
+    <card-judge-item
+      v-for="item in value"
+      :key="item.id"
+      :success="item.level"
+      :state="item.state"
+    >
+      <div>
         <b-checkbox
           class="checkbox-section"
           v-model="item.level"
-          type="is-success"
+          :type="item.state === 'SOLVING' ? 'is-primary' : 'is-success'"
+          :disabled="item.state === 'PENDING' || item.state === 'ARGUED'"
         >
-          {{ item.content }}
+          <b-tooltip :label="item.explain" position="is-top">
+            {{ item.content }}
+          </b-tooltip>
         </b-checkbox>
-      </b-tooltip>
+        <b-tooltip :label="`申诉备注 : ${item.remark}`" type="is-warning">
+          <b-tag v-if="item.state === 'PENDING'" type="is-warning">
+            学生申诉
+          </b-tag>
+          <b-tag v-if="item.state === 'SOLVING'" type="is-primary">
+            处理申诉
+          </b-tag>
+        </b-tooltip>
+      </div>
+
       <input
         v-model="item.comment"
         class="input-custom"
-        :class="item.level ? 'input-custom--success' : 'input-custom--danger'"
+        :class="{
+          'input-custom--success': item.level,
+          'input-custom--danger': !item.level,
+          'input-custom--primary': item.state === 'SOLVING',
+          'input-custom--warning': item.state === 'PENDING',
+          'input-custom--end': item.state === 'ARGUED'
+        }"
+        :disabled="item.state === 'PENDING' || item.state === 'ARGUED'"
         type="text"
         placeholder="填写备注"
         style="box-shadow:none;border-color:white"
       />
+      <div
+        class="action-section--warning action-section "
+        v-if="item.state === 'PENDING'"
+      >
+        <span
+          @click="acceptArgue(item)"
+          :class="{ 'is-loading': acceptArgueLoading }"
+          class="button is-warning"
+          >接受申诉</span
+        >
+        <span
+          @click="refuseArgue(item)"
+          :class="{ 'is-loading': refuseArgueLoading }"
+          class="button is-danger"
+          >拒绝申诉</span
+        >
+      </div>
+      <div
+        class="action-section--primary action-section "
+        v-if="item.state === 'SOLVING'"
+      >
+        <span
+          @click="submitSolution(item)"
+          :class="{ 'is-loading': submitSolutionLoading }"
+          class="button is-primary"
+          >提交处理好的申诉</span
+        >
+      </div>
     </card-judge-item>
   </div>
 </template>
@@ -33,6 +85,13 @@ export default {
       default: () => []
     }
   },
+  data() {
+    return {
+      acceptArgueLoading: false,
+      refuseArgueLoading: false,
+      submitSolutionLoading: false
+    };
+  },
   watch: {
     value: {
       handler(newValue) {
@@ -40,6 +99,24 @@ export default {
       },
       deep: true
     }
+  },
+  methods: {
+    async acceptArgue(item) {
+      this.acceptArgueLoading = true;
+      await this.$emit("accept-argue", item);
+      this.acceptArgueLoading = false;
+    },
+    async refuseArgue(item) {
+      this.refuseArgueLoading = true;
+      await this.$emit("refuse-argue", item);
+      this.refuseArgueLoading = false;
+    },
+    async submitSolution(item) {
+      this.submitSolutionLoading = true;
+      await this.$emit("submit-solution", item);
+      this.submitSolutionLoading = false;
+    }
+    // getColor(state) {}
   }
 };
 </script>
@@ -50,6 +127,29 @@ export default {
   padding: $default-margin;
 }
 
+.argue-section {
+  padding: $default-margin;
+  background-color: $warning;
+}
+
+.action-section {
+  .button {
+    border-radius: 0;
+  }
+
+  &--warning {
+    .button {
+      width: 50%;
+    }
+  }
+
+  &--primary {
+    .button {
+      width: 100%;
+    }
+  }
+}
+
 .input-custom {
   padding: $default-margin;
   width: 100%;
@@ -66,6 +166,18 @@ export default {
     background-color: #ebfff2;
   }
 
+  &--primary {
+    background-color: lighten($primary, 30%);
+  }
+
+  &--warning {
+    background-color: lighten($warning, 30%);
+  }
+
+  &--end {
+    background-color: lighten($grey, 50%);
+  }
+
   &::placeholder {
     /*color: hsl(0, 0%, 96%);*/
   }

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

@@ -4,6 +4,11 @@
   background: white;
   padding: $default-margin-between $default-margin-between*1.5 0 $default-margin-between*1.5;
   box-shadow: $default-shadow-lighter;
+
+  .tabs a,
+  .tabs ul{
+    border-bottom-color: white;
+  }
   .wide {
     max-width: 1200px;
     margin: auto;

+ 31 - 0
src/data/checkListItemState.js

@@ -5,3 +5,34 @@ export const SOLVING = "SOLVING";
 export const ARGUED = "ARGUED";
 
 export const ALL_STATE = [DEFAULT, SUBMITTED, PENDING, SOLVING, ARGUED];
+
+/**
+ * @typedef CheckListItemStateHelper
+ * @property {string} name
+ * @property {string} styledClass
+ */
+
+class CheckListItemStateHelperFactory {
+  /**
+   * @param state
+   * @return {ReviewStateHelper}
+   */
+  getStateHelper = state => {
+    switch (state) {
+      case DEFAULT:
+        return { name: "未评价", styledClass: "is-light" };
+      case SUBMITTED:
+        return { name: "已评价", styledClass: "is-success" };
+      case PENDING:
+        return { name: "待处理重判", styledClass: "is-warning" };
+      case SOLVING:
+        return { name: "处理重判期间", styledClass: "is-info" };
+      case ARGUED:
+        return { name: "重判已结束", styledClass: "is-danger" };
+      default:
+        console.log("checklist item state not found : ", state);
+    }
+  };
+}
+
+export const checkListItemStateHelperFactory = new CheckListItemStateHelperFactory();

+ 2 - 2
src/data/reviewState.js

@@ -11,7 +11,7 @@ export const ALL_STATE = [DEFAULT, REVIEWING, ARGUING, SOLVING, END];
  * @property {string} styledClass
  */
 
-class ReviewStateHelperFectory {
+class ReviewStateHelperFactory {
   /**
    * @param state
    * @return {ReviewStateHelper}
@@ -34,4 +34,4 @@ class ReviewStateHelperFectory {
   };
 }
 
-export const reviewStateHelperFectory = new ReviewStateHelperFectory();
+export const reviewStateHelperFectory = new ReviewStateHelperFactory();

+ 1 - 0
src/layouts/HomeLayout/HomeHeader/index.vue

@@ -25,6 +25,7 @@ export default {
     DefaultHeader,
     UserDropdown
   },
+
   computed: {
     ...mapState({
       profile: state => state.user.profile

+ 0 - 2
src/layouts/TeacherMainLayout.vue

@@ -4,5 +4,3 @@
     <router-view></router-view>
   </div>
 </template>
-
-<style lang="scss"></style>

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

@@ -87,7 +87,7 @@ $top-section-height: 4.25rem;
 .menu-section {
   //min-width: $menu-width;
   //width: $left-width-calc;
-  width: 300px;
+  max-width: 300px;
   /*background: white;*/
   /*border-right: 3px solid rgb(159, 148, 254);*/
   padding-left: $left-padding-calc;
@@ -97,7 +97,8 @@ $top-section-height: 4.25rem;
   /*background-image: url("../../assets/sideBar.png");*/
   /*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, #7c71bdc7 0%, #6172ec 100%);*/
+  background: linear-gradient(to top, #20328ec7 0%, #0d0f23 100%);
   background-size: cover;
   position: sticky;
   overflow-y: auto;

+ 1 - 0
src/mixins/time.js

@@ -3,6 +3,7 @@ import dayjs from "dayjs";
 export default {
   methods: {
     displayUnixTime(time) {
+      if (!time) return;
       return dayjs.unix(time).format("YYYY年 MM月 DD日 HH:mm:ss");
     }
   }

+ 77 - 2
src/router/routes.js

@@ -68,9 +68,64 @@ export default [
         path: "question",
         component: () => import("@/views/teacher/Question")
       },
+      {
+        path: "question/:id",
+        component: () => import("@/views/teacher/Question/questionDetail.vue")
+      },
       {
         path: "result",
         component: () => import("@/views/teacher/Result")
+      },
+      {
+        path: "code",
+        component: () => import("@/views/teacher/Code/index")
+      },
+      {
+        path: "code/detail",
+        component: () => import("@/views/teacher/Code/detail")
+      },
+      {
+        path: "code/detail/test",
+        component: () => import("@/views/teacher/Code/test")
+      },
+
+      {
+        path: "document",
+        component: () => import("@/views/teacher/Document/index")
+      },
+      {
+        path: "document/detail",
+        component: () => import("@/views/teacher/Document/detail")
+      },
+      {
+        path: "review",
+        component: () => import("@/views/teacher/Review")
+      },
+      {
+        path: "review/:reviewId",
+        component: () => import("@/views/teacher/Review/ReviewDetail"),
+        children: [
+          {
+            path: "",
+            component: () =>
+              import("@/views/teacher/Review/ReviewDetail/Detail")
+          },
+          {
+            path: "groups",
+            component: () =>
+              import("@/views/teacher/Review/ReviewDetail/Groups")
+          }
+        ]
+      },
+      {
+        path: "review/:reviewId/groups/:groupId",
+        component: () =>
+          import("@/views/teacher/Review/ReviewDetail/GroupDocDetail")
+      },
+      {
+        path: "review/:reviewId/groups/:groupId/argue",
+        component: () =>
+          import("@/views/teacher/Review/ReviewDetail/GroupArgueDetail")
       }
     ]
   },
@@ -86,7 +141,11 @@ export default [
       },
       {
         path: "code",
-        component: () => import("@/views/student/Code")
+        component: () => import("@/views/student/Code/index")
+      },
+      {
+        path: "code/detail",
+        component: () => import("@/views/student/Code/detail")
       },
       {
         path: "document",
@@ -98,7 +157,19 @@ export default [
       },
       {
         path: "review/:reviewId",
-        component: () => import("@/views/student/Review/ReviewDetail")
+        component: () => import("@/views/student/Review/ReviewDetail"),
+        children: [
+          {
+            path: "self",
+            component: () =>
+              import("@/views/student/Review/ReviewDetail/ReviewSelfDetail")
+          },
+          {
+            path: "",
+            component: () =>
+              import("@/views/student/Review/ReviewDetail/ReviewDocs")
+          }
+        ]
       },
       {
         path: "review/:reviewId/doc/:docReviewId",
@@ -107,6 +178,10 @@ export default [
       {
         path: "code",
         component: () => import("@/views/student/Code")
+      },
+      {
+        path: "group",
+        component: () => import("@/views/student/Group")
       }
     ]
   },

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

@@ -0,0 +1,49 @@
+<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>

+ 124 - 1
src/views/student/Code/index.vue

@@ -1,3 +1,126 @@
 <template>
-  <div></div>
+  <div>
+    <page-header :routes="[{ name: '代码作业', path: 'code' }]">
+      <template slot="title">
+        代码作业列表
+      </template>
+      <template slot="content">
+        Here might be a page title
+      </template>
+    </page-header>
+    <page-body>
+      <div>
+        <b-table
+          :data="codeData"
+          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="`/course-student/:courseId/code/detail`">
+                {{ props.row.name }}</router-link
+              >
+            </b-table-column>
+            <b-table-column label="状态" centered>
+              {{ props.row.state }}
+            </b-table-column>
+
+            <b-table-column label="开始时间" centered>
+              {{ props.row.startAt }}
+            </b-table-column>
+
+            <b-table-column label="结束时间" centered>
+              {{ props.row.endAt }}
+            </b-table-column>
+          </template>
+
+          <template slot="detail">
+            <article class="media">
+              <div class="media-content">
+                <div class="content">
+                  <p>
+                    <strong>成绩详情</strong>
+                    <small>这里可以放具体的作业成绩,前面可以放总评啥的</small>
+                    <br />
+                  </p>
+                </div>
+              </div>
+            </article>
+          </template>
+        </b-table>
+      </div>
+    </page-body>
+  </div>
 </template>
+
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+const codeData = [
+  {
+    id: 1,
+    name: "购物车web项目",
+    state: "未开始",
+    startAt: "2019-1-1",
+    endAt: "2019-6-1"
+  },
+  {
+    id: 2,
+    name: "购物车web项目",
+    state: "已结束",
+    startAt: "2019-1-1",
+    endAt: "2019-6-1"
+  },
+  {
+    id: 3,
+    name: "购物车web项目",
+    state: "正在进行",
+    startAt: "2019-1-1",
+    endAt: "2019-6-1"
+  }
+];
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  data() {
+    return {
+      codeData,
+      docAssignment: {
+        name: "",
+        description: "",
+        startAt: new Date(),
+        endAt: new Date()
+      },
+      isCardModalActive: false
+    };
+  },
+  methods: {}
+};
+</script>
+
+<style lang="scss" scoped>
+.card {
+  padding: 30px;
+  .title {
+    font-size: 1.25rem;
+    small {
+      color: gray;
+      font-size: 1rem;
+    }
+  }
+  .content {
+    a {
+      margin-top: 30px;
+      width: 100%;
+    }
+  }
+}
+</style>

+ 247 - 1
src/views/student/Group/index.vue

@@ -1,3 +1,249 @@
 <template>
-  <div></div>
+  <div>
+    <page-header :routes="[{ name: '我的小组' }]">
+      <template slot="title">
+        我的小组 {{ this.groups.length > 0 && `「${this.groups[0].name}」` }}
+      </template>
+    </page-header>
+    <page-body>
+      <div v-if="groups.length > 0">
+        <div class="page-section">
+          <div class="columns is-desktop">
+            <div class="column">
+              <h5 class="title is-5">id:</h5>
+              <div class="subtitle is-6">{{ groups[0].id }}</div>
+            </div>
+            <div class="column">
+              <h5 class="title is-5">名称:</h5>
+              <div class="subtitle is-6">{{ groups[0].name }}</div>
+            </div>
+            <div class="column">
+              <h5 class="title is-5">选组码:</h5>
+              <div class="subtitle is-6">{{ groups[0].code }}</div>
+            </div>
+          </div>
+        </div>
+        <div class="divider">
+          <div class="columns is-multiline is-desktop">
+            <div
+              class="column is-3"
+              :key="member.id"
+              v-for="member in groups[0].members"
+            >
+              <card>
+                <h5 class="title is-5">成员:「{{ member.nickname }}」</h5>
+                <div class="subtitle is-6">{{ member.username }}</div>
+              </card>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div
+        v-if="!isLoadingGroup && groups.length === 0"
+        style="padding: 50px"
+        class="columns is-desktop  is-vcentered"
+      >
+        <div class="column is-three-fifths-desktop">
+          <img src="@/assets/undraw_having_fun.svg" alt="" />
+        </div>
+        <div class="column">
+          <div class="title is-spaced">您还尚未加入任何小组</div>
+          <p class="subtitle">加入或创建小组,一起迎接挑战!</p>
+          <div class="buttons">
+            <a
+              @click="() => (this.showCreateModal = true)"
+              class="button is-primary"
+              >创建小组
+            </a>
+            <a
+              @click="() => (this.showJoinModal = true)"
+              class="button is-primary is-outlined"
+              >加入小组
+            </a>
+          </div>
+        </div>
+      </div>
+    </page-body>
+    <b-modal :active.sync="showCreateModal" :width="400">
+      <div class="card">
+        <div class="title">创建小组</div>
+        <b-notification type="is-danger" :active.sync="isServerError">
+          {{ errorMassage }}
+        </b-notification>
+        <div class="content">
+          <b-field
+            label="名称"
+            :type="$v.name.$error ? 'is-danger' : ''"
+            :message="
+              $v.name.$error
+                ? [
+                    !$v.name.basicChar
+                      ? '名称只能由大小写字母,数字,- 与 _ 组成'
+                      : undefined,
+                    !$v.name.required ? '请输入名称' : undefined
+                  ]
+                : ''
+            "
+          >
+            <b-input maxlength="50" v-model="$v.name.$model"></b-input>
+          </b-field>
+
+          <a
+            class="button is-primary"
+            :class="{ 'is-loading': submitLoading }"
+            @click="createGroup()"
+          >
+            创建小组
+          </a>
+        </div>
+      </div>
+    </b-modal>
+    <b-modal :active.sync="showJoinModal" :width="400">
+      <div class="card">
+        <div class="title">加入小组</div>
+        <b-notification type="is-danger" :active.sync="isServerError">
+          {{ errorMassage }}
+        </b-notification>
+        <div class="content">
+          <b-field
+            label="选组码"
+            :type="$v.code.$error ? 'is-danger' : ''"
+            :message="
+              $v.name.$error
+                ? [!$v.code.required ? '请输入选组码' : undefined]
+                : ''
+            "
+          >
+            <b-input v-model="$v.code.$model"></b-input>
+          </b-field>
+          <a
+            class="button is-primary"
+            :class="{ 'is-loading': submitLoading }"
+            @click="joinGroup()"
+          >
+            申请加入小组
+          </a>
+        </div>
+      </div>
+    </b-modal>
+  </div>
 </template>
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import Card from "@/components/Card";
+import { required } from "vuelidate/lib/validators";
+import {
+  getStudentGroup,
+  postStudentGroup,
+  putStudentGroup
+} from "@/api/group";
+export default {
+  components: { PageBody, PageHeader, Card },
+  data() {
+    return {
+      showCreateModal: false,
+      showJoinModal: false,
+      name: "",
+      code: "",
+      isServerError: false,
+      errorMassage: "",
+      submitLoading: false,
+      isLoadingGroup: false,
+      groups: []
+    };
+  },
+  validations: {
+    name: {
+      required,
+      basicChar: name => /^[a-zA-Z0-9_-]*$/.test(name)
+    },
+    code: {
+      required
+    }
+  },
+  watch: {
+    showCreateModal(newValue) {
+      if (newValue === false) {
+        this.onModalClosed();
+      }
+    },
+    showJoinModal(newValue) {
+      if (newValue === false) {
+        this.onModalClosed();
+      }
+    }
+  },
+  mounted() {
+    this.loadGroup();
+  },
+  methods: {
+    async createGroup() {
+      this.$v.$touch();
+      if (!this.$v.name.$invalid) {
+        try {
+          this.submitLoading = true;
+          this.isServerError = false;
+          await postStudentGroup(this.name);
+          this.$toast.open({
+            message: `小组「${this.name}」创建成功`,
+            type: "is-success"
+          });
+          this.showCreateModal = false;
+          this.loadGroup();
+        } catch (e) {
+          this.errorMassage = e.message;
+          this.isServerError = true;
+        } finally {
+          this.submitLoading = false;
+        }
+      }
+    },
+    async joinGroup() {
+      this.$v.$touch();
+      if (!this.$v.code.$invalid) {
+        try {
+          this.submitLoading = true;
+          this.isServerError = false;
+          await putStudentGroup(this.code);
+          this.$toast.open({
+            message: `小组加入成功`,
+            type: "is-success"
+          });
+          this.showJoinModal = false;
+          this.loadGroup();
+        } catch (e) {
+          this.errorMassage = e.message;
+          this.isServerError = true;
+        } finally {
+          this.submitLoading = false;
+        }
+      }
+    },
+    onModalClosed() {
+      this.isServerError = false;
+      this.submitLoading = false;
+    },
+    async loadGroup() {
+      this.isLoadingGroup = true;
+      const { courseId } = this.$route.params;
+      const { data } = await getStudentGroup(courseId);
+      this.groups = data;
+      this.isLoadingGroup = false;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../assets/scss/app";
+.card {
+  padding: $default-margin-between;
+  border-radius: $default-radius;
+}
+
+.divider {
+  padding-top: $default-margin-between;
+}
+</style>

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

@@ -0,0 +1,25 @@
+<template>
+  <div class="columns is-multiline is-desktop">
+    <div :key="item.docReviewId" v-for="item in docs" class="column is-4">
+      <router-link :to="`${$route.path}/doc/${item.docReviewId}`">
+        <card hoverable> {{ item.group.name }} </card>
+      </router-link>
+    </div>
+  </div>
+</template>
+
+<script>
+import Card from "@/components/Card";
+
+export default {
+  components: {
+    Card
+  },
+  props: {
+    docs: {
+      type: Array,
+      default: () => []
+    }
+  }
+};
+</script>

+ 90 - 0
src/views/student/Review/ReviewDetail/ReviewSelfDetail/index.vue

@@ -0,0 +1,90 @@
+<template>
+  <div>
+    <div class="columns">
+      <div class="column is-8">
+        <div class="page-section">
+          <div v-if="detail.docContent" class="content">
+            <vue-markdown>{{ detail.docContent }}</vue-markdown>
+          </div>
+        </div>
+      </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"
+          />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import VueMarkdown from "vue-markdown";
+import {
+  getStudentReviewSelfDetail,
+  postStudentArgueReview
+} from "@/api/review";
+import timeMixins from "@/mixins/time";
+import ResultCheckListItem from "@/components/CheckList/ResultCheckListItem";
+export default {
+  components: {
+    ResultCheckListItem,
+    VueMarkdown
+  },
+  data() {
+    return {
+      detail: {
+        review: {},
+        checkListResult: []
+      },
+      submitLoading: false
+    };
+  },
+  mixins: [timeMixins],
+  mounted() {
+    const { reviewId } = this.$route.params;
+    getStudentReviewSelfDetail(reviewId).then(
+      resp => (this.detail = resp.data)
+    );
+  },
+  methods: {
+    async argue(item) {
+      const { reviewId } = this.$route.params;
+      try {
+        const { data } = await postStudentArgueReview({ reviewId, ...item });
+        this.detail.checkList = data;
+      } catch (e) {
+        this.$toast.open({
+          message: `重复的申请评审是不被允许的`,
+          position: "is-bottom",
+          type: "is-danger"
+        });
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../../assets/scss/app";
+.right-part {
+  margin-bottom: -1.5rem;
+  margin-right: -1.5rem;
+  background: white;
+  border-top: 1px solid #eff2f7;
+  /*position: sticky;*/
+  /*top: 0;*/
+  /*max-height: 100vh;*/
+  /*overflow-y: auto;*/
+  box-shadow: $default-shadow;
+}
+
+.action-button {
+  width: 100%;
+  border-radius: 0;
+}
+</style>

+ 6 - 18
src/views/student/Review/ReviewDetail/index.vue

@@ -1,9 +1,10 @@
 <template>
   <div>
     <page-header
-      :routes="[
-        { name: '互评作业', path: 'review' },
-        { name: '文档详情', path: $route.path }
+      :routes="[{ name: '互评作业', path: 'review' }, { name: '文档详情' }]"
+      :tab-list="[
+        { name: '待评文档', path: `review/${$route.params.reviewId}` },
+        { name: '我的文档', path: `review/${$route.params.reviewId}/self` }
       ]"
     >
       <template slot="title">
@@ -29,30 +30,17 @@
         </div>
       </template>
     </page-header>
-    <page-body>
-      <div class="columns is-multiline is-desktop">
-        <div
-          :key="item.docReviewId"
-          v-for="item in detail.documents"
-          class="column is-4"
-        >
-          <router-link :to="`${$route.path}/doc/${item.docReviewId}`">
-            <card hoverable> {{ item.group.name }} </card>
-          </router-link>
-        </div>
-      </div>
-    </page-body>
+    <page-body> <router-view :docs="detail.documents" /> </page-body>
   </div>
 </template>
 <script>
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
-import Card from "@/components/Card";
 import { getStudentReviewDetail } from "@/api/review";
 import timeMixins from "@/mixins/time";
 import ReviewStateTag from "@/components/ReviewStateTag/index";
 export default {
-  components: { ReviewStateTag, PageBody, PageHeader, Card },
+  components: { ReviewStateTag, PageBody, PageHeader },
   data() {
     return {
       detail: {}

+ 36 - 4
src/views/student/Review/ReviewDocDetail/index.vue

@@ -28,13 +28,18 @@
         <div class="column is-4">
           <div class="right-part">
             <div
-              class="button is-primary action-button"
+              class="button  is-medium is-primary action-button"
               :class="{ 'is-loading': submitLoading }"
               @click="submitCheckList"
             >
               提 交
             </div>
-            <check-list v-model="detail.checkList" />
+            <check-list
+              v-model="detail.checkList"
+              @submit-solution="submitSolution"
+              @accept-argue="acceptArgue"
+              @refuse-argue="refuseArgue"
+            />
           </div>
         </div>
       </div>
@@ -45,7 +50,12 @@
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
 import VueMarkdown from "vue-markdown";
-import { getStudentReviewDocDetail, postStudentReviewWork } from "@/api/review";
+import {
+  getStudentReviewDocDetail,
+  postStudentArgueAcceptation,
+  postStudentArgueEnd,
+  postStudentReviewWork
+} from "@/api/review";
 import timeMixins from "@/mixins/time";
 import ReviewStateTag from "@/components/ReviewStateTag/index";
 import CheckList from "@/components/CheckList/index";
@@ -83,6 +93,29 @@ export default {
           this.detail.checkList = resp.data;
         }
       );
+    },
+    async submitSolution(item) {
+      const { reviewId } = this.$route.params;
+      const { data } = await postStudentArgueEnd(reviewId, item.id, item);
+      this.detail.checkList = data;
+    },
+    async refuseArgue(item) {
+      const { reviewId } = this.$route.params;
+      const { data } = await postStudentArgueAcceptation(
+        reviewId,
+        item.id,
+        false
+      );
+      this.detail.checkList = data;
+    },
+    async acceptArgue(item) {
+      const { reviewId } = this.$route.params;
+      const { data } = await postStudentArgueAcceptation(
+        reviewId,
+        item.id,
+        true
+      );
+      this.detail.checkList = data;
     }
   }
 };
@@ -99,7 +132,6 @@ export default {
   top: 0;
   max-height: 100vh;
   overflow-y: auto;
-  overflow-x: hidden;
   box-shadow: $default-shadow;
 }
 

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


+ 252 - 0
src/views/teacher/Code/detail.vue

@@ -0,0 +1,252 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '代码作业', path: 'code' },
+        { name: `${assignmentInfo.name}`, path: 'code/detail' }
+      ]"
+    >
+      <template slot="title">
+        {{ assignmentInfo.name }}
+      </template>
+      <template slot="content">
+        作业描述信息作业描述信息作业描述信息作业描述信息作业描述信息作业描述信息作业描述信息
+      </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 type="is-boxed">
+          <b-tab-item>
+            <template slot="header">
+              <span> {{ submitInfo.yes }} </span>
+            </template>
+            <div class="submitted-list">
+              <b-table :data="showResult" paginated per-page="5">
+                <template slot-scope="props">
+                  <b-table-column
+                    field="id"
+                    label="小组ID"
+                    width="120"
+                    numeric
+                    sortable
+                    centered
+                  >
+                    {{ props.row.id }}
+                  </b-table-column>
+                  <b-table-column label="构建" centered>
+                    <span :class="props.row.buildTag">{{
+                      props.row.buildStatus
+                    }}</span>
+                  </b-table-column>
+
+                  <b-table-column label="部署" centered>
+                    <span :class="props.row.deployTag">{{
+                      props.row.deployStatus
+                    }}</span>
+                  </b-table-column>
+                  <b-table-column label="测试">
+                    <span :class="props.row.testTag">{{
+                      props.row.testStatus
+                    }}</span>
+                  </b-table-column>
+                  <b-table-column label="详情">
+                    <router-link
+                      :to="{
+                        path: `/course-teacher/:courseId/code/detail/test`,
+                        query: { assignmentInfo: props.row }
+                      }"
+                      >查看详情</router-link
+                    >
+                  </b-table-column>
+                </template>
+              </b-table>
+            </div>
+          </b-tab-item>
+          <b-tab-item>
+            <template slot="header">
+              <span> {{ submitInfo.no }} </span>
+            </template>
+            <b-table
+              :data="unsubmited.data"
+              :columns="unsubmited.columns"
+            ></b-table>
+          </b-tab-item>
+        </b-tabs>
+      </div>
+    </page-body>
+  </div>
+</template>
+
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+
+const unsubmited = {
+  data: [
+    {
+      id: 1,
+      PM: "Jesse",
+      members: "Simmons\tClarence\tTina\tJohn"
+    },
+    {
+      id: 2,
+      PM: "Jesse",
+      members: "Simmons Clarence Tina John"
+    },
+    {
+      id: 3,
+      PM: "Jesse",
+      members: "Simmons Clarence Tina John"
+    }
+  ],
+  columns: [
+    {
+      field: "id",
+      label: "小组ID",
+      numeric: true,
+      centered: true
+    },
+    {
+      field: "PM",
+      label: "组长"
+    },
+    {
+      field: "members",
+      label: "组员"
+    }
+  ]
+};
+const info = [
+  {
+    id: 1,
+    stage: "测试",
+    PM: "Jesse",
+    members: "Simmons\tClarence\tTina\tJohn",
+    build: 0,
+    deploy: 0,
+    test: -1,
+    pass: 0,
+    testNum: 10
+  },
+  {
+    id: 2,
+    stage: "测试",
+    build: 1,
+    deploy: 1,
+    test: 0,
+    pass: 0,
+    testNum: 10
+  },
+  {
+    id: 3,
+    stage: "测试",
+    build: 1,
+    deploy: 1,
+    test: 1,
+    pass: 10,
+    testNum: 10
+  }
+];
+const showResult = [];
+const assignmentInfo = {
+  id: 0,
+  name: "",
+  description: "",
+  startAt: 0,
+  endAt: 0
+};
+
+const tag = {
+  success: "tag is-success",
+  failed: "tag is-danger",
+  blank: "tag is-dark"
+};
+
+const status = {
+  success: "SUCCESS",
+  failed: "FAILED",
+  blank: "BLANK"
+};
+
+const submitCount = {
+  yes: 19,
+  no: 8
+};
+const submitInfo = {
+  yes: "已提交:" + submitCount.yes,
+  no: "未提交:" + submitCount.no
+};
+
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  mounted() {
+    this.assignmentInfo = this.$route.query.assignmentInfo;
+    for (var item of info) {
+      showResult.push({
+        id: item.id,
+        buildStatus: item.build === 1 ? status.success : status.failed,
+        buildTag: item.build === 1 ? tag.success : tag.failed,
+        deployStatus: item.deploy === 1 ? status.success : status.failed,
+        deployTag: item.deploy === 1 ? tag.success : tag.failed,
+        testStatus: item.test === 1 ? status.success : status.failed,
+        testTag: item.test === 1 ? tag.success : tag.failed
+      });
+    }
+  },
+  data() {
+    return {
+      info,
+      unsubmited,
+      assignmentInfo,
+      tag,
+      status,
+      showResult,
+      submitInfo
+    };
+  }
+};
+</script>
+<style scoped>
+.breadcrumb a {
+  /*color: #7957d5;*/
+  font-weight: bold;
+}
+.data-table {
+  width: 100%;
+  padding: 30px;
+  min-height: 80vh;
+  background-color: #ffffff;
+}
+.submitted-list {
+  /*margin-top: 30px;*/
+}
+.form-group {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 1.5rem;
+}
+
+.submit-state {
+  display: flex;
+  margin-top: 20px;
+  align-items: center;
+  margin-bottom: 20px;
+}
+</style>

+ 250 - 0
src/views/teacher/Code/index.vue

@@ -0,0 +1,250 @@
+<template>
+  <div>
+    <page-header :routes="[{ name: '代码作业', path: 'code' }]">
+      <template slot="title">
+        代码作业列表
+      </template>
+      <template slot="content">
+        Here might be a page title
+      </template>
+      <template slot="action">
+        <b-field>
+          <a class="button is-link" @click="updateInfo(-1)">新建代码作业</a>
+        </b-field>
+      </template>
+    </page-header>
+    <page-body>
+      <div class="data-table">
+        <b-table
+          :data="codeData"
+          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-teacher/:courseId/code/detail`,
+                  query: { assignmentInfo: props.row }
+                }"
+              >
+                {{ props.row.name }}
+              </router-link>
+            </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() }}
+            </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() }}
+            </b-table-column>
+
+            <b-table-column label="修改" centered>
+              <a @click="updateInfo(props.row.id)">修改</a>
+            </b-table-column>
+            <b-table-column label="删除" centered>
+              <a style="color:#b34141" @click="confirmDelete(props.row.id)"
+                >删除</a
+              >
+            </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>
+            </article>
+          </template>
+        </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>
+
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+const codeData = [
+  {
+    id: 1,
+    name: "购物车web项目",
+    description: "1111111111111这里是文档描述",
+    startAt: 1547107174000,
+    endAt: 1547971174000
+  },
+  {
+    id: 2,
+    name: "电影票web项目",
+    description: "222222222222这里是文档描述",
+    startAt: 1547107174000,
+    endAt: 1547971174000
+  },
+  {
+    id: 3,
+    name: "SEECweb项目",
+    description: "3333333333333333这里是文档描述",
+    startAt: 1547107174000,
+    endAt: 1547971174000
+  }
+];
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  data() {
+    return {
+      codeData,
+      codeAssignment: {
+        id: -1,
+        name: "",
+        description: "",
+        startAt: new Date(),
+        endAt: new Date()
+      },
+      isCardModalActive: false,
+      modal: {
+        title: "新建文档作业",
+        submit: "确认创建"
+      }
+    };
+  },
+  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("删除成功");
+        }
+      });
+    },
+    updateInfo(id) {
+      this.isCardModalActive = true;
+      if (id !== -1) {
+        this.modal.submit = "确认修改";
+        this.modal.title = "修改文档作业";
+        console.log(this.modal.submit);
+        console.log(this.modal.submit);
+        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;
+          }
+        }
+      } 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;
+          }
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.card {
+  padding: 30px;
+  .title {
+    font-size: 1.25rem;
+    small {
+      color: gray;
+      font-size: 1rem;
+    }
+  }
+  .content {
+    a {
+      margin-top: 30px;
+      width: 100%;
+    }
+  }
+}
+</style>

+ 116 - 0
src/views/teacher/Code/test.vue

@@ -0,0 +1,116 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '代码作业', path: 'code' },
+        { name: '购物车web项目', path: 'code/detail' },
+        { name: '测试信息', path: 'code/detail/test' }
+      ]"
+    >
+      <template slot="title">
+        测试信息
+      </template>
+      <template slot="content">
+        可查看最近五条测试数据
+      </template>
+    </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>
+    </page-body>
+  </div>
+</template>
+
+<script>
+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,
+    PageHeader
+  },
+  mounted() {
+    this.assignmentInfo = this.$route.query.assignmentInfo;
+  },
+  data() {
+    return {
+      assignmentInfo: {},
+      showResult,
+      testResult
+    };
+  }
+};
+</script>
+
+<style scoped>
+.breadcrumb a {
+  /*color: #7957d5;*/
+  font-weight: bold;
+}
+.collapseeeeeee {
+  margin-bottom: 10px;
+}
+</style>

+ 249 - 0
src/views/teacher/Document/detail.vue

@@ -0,0 +1,249 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '文档作业', path: 'document' },
+        { name: `${assignmentInfo.name}`, path: 'document/detail' }
+      ]"
+    >
+      <template slot="title">
+        {{ assignmentInfo.name }}
+      </template>
+      <template slot="content">
+        Here might be a page title
+      </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">
+            <div class="submitted-list">
+              <b-table
+                :data="data"
+                paginated
+                per-page="5"
+                detailed
+                detail-key="id"
+              >
+                <template slot-scope="props">
+                  <b-table-column
+                    field="id"
+                    label="小组ID"
+                    width="120"
+                    numeric
+                    sortable
+                    centered
+                  >
+                    {{ props.row.id }}
+                  </b-table-column>
+
+                  <b-table-column label="阶段" centered>
+                    {{ props.row.stage }}
+                  </b-table-column>
+
+                  <b-table-column label="互评负责组" centered>
+                    {{ props.row.charge }}
+                  </b-table-column>
+
+                  <b-table-column label="分数" centered>
+                    {{ props.row.score }}
+                  </b-table-column>
+                  <b-table-column field="difficulty" label="详情" centered>
+                    {{ props.row.detail }}
+                  </b-table-column>
+                </template>
+
+                <template slot="detail">
+                  <article class="media">
+                    <div class="media-content">
+                      <div class="content">
+                        <p>
+                          <strong>小组成员</strong><br />
+                          <small>组长:小明&emsp;组员:小黄 小兰</small> <br />
+                        </p>
+                      </div>
+                    </div>
+                  </article>
+                </template>
+              </b-table>
+            </div>
+          </b-tab-item>
+
+          <b-tab-item :label="submitInfo.no">
+            <b-table
+              :data="unsubmited.data"
+              :columns="unsubmited.columns"
+            ></b-table>
+          </b-tab-item>
+        </b-tabs>
+      </div>
+    </page-body>
+  </div>
+</template>
+
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+
+const unsubmited = {
+  data: [
+    {
+      id: 1,
+      PM: "Jesse",
+      members: "Simmons\tClarence\tTina\tJohn"
+    },
+    {
+      id: 2,
+      PM: "Jesse",
+      members: "Simmons Clarence Tina John"
+    },
+    {
+      id: 3,
+      PM: "Jesse",
+      members: "Simmons Clarence Tina John"
+    }
+  ],
+  columns: [
+    {
+      field: "id",
+      label: "小组ID",
+      // width: "80",
+      numeric: true,
+      centered: true
+    },
+    {
+      field: "PM",
+      label: "组长"
+    },
+    {
+      field: "members",
+      label: "组员"
+    }
+  ]
+};
+const submitCount = {
+  yes: 19,
+  no: 8
+};
+const submitInfo = {
+  yes: "已提交:" + submitCount.yes,
+  no: "未提交:" + submitCount.no
+};
+const data = [
+  {
+    id: 1,
+    stage: "互评完成",
+    charge: 2,
+    score: 80,
+    detail: "查看详情"
+  },
+  {
+    id: 2,
+    stage: "正在互评",
+    charge: 2,
+    score: 80,
+    detail: "查看详情"
+  },
+  {
+    id: 3,
+    stage: "互评完成",
+    charge: 2,
+    score: 80,
+    detail: "查看详情"
+  },
+  {
+    id: 4,
+    stage: "互评完成",
+    charge: 2,
+    score: 80,
+    detail: "查看详情"
+  }
+];
+
+const assignmentInfo = {
+  id: 0,
+  name: "",
+  description: "",
+  startAt: 0,
+  endAt: 0
+};
+
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  mounted() {
+    this.assignmentInfo = this.$route.query.assignmentInfo;
+    console.log(this.assignmentInfo.name);
+    console.log(this.assignmentInfo.description);
+  },
+  data() {
+    return {
+      courses: [],
+      review: [],
+      questionType: [
+        { id: 1, title: "文档作业题目" },
+        { id: 2, title: "代码作业题目" }
+      ],
+      data,
+      unsubmited,
+      assignmentInfo,
+      submitInfo
+    };
+  }
+};
+</script>
+
+<style scoped>
+.breadcrumb a {
+  /*color: #7957d5;*/
+  font-weight: bold;
+}
+.data-table {
+  width: 100%;
+  padding: 30px;
+  min-height: 80vh;
+  background-color: #ffffff;
+}
+.submitted-list {
+  /*margin-top: 30px;*/
+}
+.form-group {
+  display: flex;
+  justify-content: space-between;
+}
+.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;
+}
+.process-bar {
+  width: 50%;
+  margin-right: 10px;
+}
+.submit-state {
+  display: flex;
+  margin-top: 20px;
+  align-items: center;
+  margin-bottom: 20px;
+}
+</style>

+ 242 - 0
src/views/teacher/Document/index.vue

@@ -0,0 +1,242 @@
+<template>
+  <div>
+    <page-header :routes="[{ name: '文档作业', path: 'document' }]">
+      <template slot="title">
+        文档作业列表
+      </template>
+      <template slot="content">
+        Here might be a page title
+      </template>
+      <template slot="action">
+        <b-field>
+          <a class="button is-link" @click="updateInfo(-1)">新建文档作业</a>
+        </b-field>
+      </template>
+    </page-header>
+
+    <page-body>
+      <div class="data-table">
+        <b-table :data="data" 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-teacher/:courseId/document/detail`,
+                  query: { assignmentInfo: props.row }
+                }"
+              >
+                {{ props.row.name }}
+              </router-link>
+            </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() }}
+            </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() }}
+            </b-table-column>
+            <b-table-column label="修改" centered>
+              <a @click="updateInfo(props.row.id)">修改</a>
+            </b-table-column>
+            <b-table-column label="删除" centered>
+              <a style="color:#b34141" @click="confirmDelete(props.row.id)"
+                >删除</a
+              >
+            </b-table-column>
+          </template>
+
+          <template slot="detail" slot-scope="props">
+            <article class="media">
+              <div class="media-content">
+                <div class="content">
+                  <p>
+                    <strong>作业描述:</strong><br />
+                    <small>{{ props.row.description }}</small> <br />
+                  </p>
+                </div>
+              </div>
+            </article>
+          </template>
+        </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";
+const docData = [
+  {
+    id: 1,
+    name: "软件需求规格说明书",
+    description: "1111111111111这里是文档描述",
+    startAt: 1547107174000,
+    endAt: 1547971174000
+  },
+  {
+    id: 2,
+    name: "用户需求列表",
+    description: "222222222222222222这里是文档描述",
+    startAt: 1547107174000,
+    endAt: 1547971174000
+  },
+  {
+    id: 3,
+    name: "面谈报告",
+    description: "333333333333333这里是文档描述",
+    startAt: 1547107174000,
+    endAt: 1547971174000
+  }
+];
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  data() {
+    return {
+      data: docData,
+      docAssignment: {
+        id: -1,
+        name: "",
+        description: "",
+        startAt: new Date(),
+        endAt: new Date()
+      },
+      isCardModalActive: false,
+      modal: {
+        title: "新建文档作业",
+        submit: "确认创建"
+      }
+    };
+  },
+  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("删除成功");
+        }
+      });
+    },
+    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;
+          }
+        }
+      } 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;
+          }
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.card {
+  padding: 30px;
+  .title {
+    font-size: 1.25rem;
+    small {
+      color: gray;
+      font-size: 1rem;
+    }
+  }
+  .content {
+    a {
+      margin-top: 30px;
+      width: 100%;
+    }
+  }
+}
+</style>

+ 35 - 2
src/views/teacher/Home/index.vue

@@ -5,7 +5,11 @@
         <div class="container hint">
           <h1 class="title">我开设的课程</h1>
           <h2 class="subtitle">请点击下方按钮新建课程</h2>
-          <img class="newSubject" src="../../../assets/white.png" />
+          <img
+            class="newSubject"
+            src="../../../assets/white.png"
+            @click="isCardModalActive = true"
+          />
         </div>
       </div>
     </section>
@@ -18,19 +22,32 @@
         </div>
       </div>
     </div>
+    <!-- 成绩比例调控模态框 -->
+    <b-modal :active.sync="isCardModalActive" :width="320" scroll="keep">
+      <div class="card">
+        <div class="title">新建课程</div>
+        <div class="content">
+          <b-field label="课程名"> <b-input></b-input> </b-field>
+          <b-field label="选课码"> <b-input></b-input> </b-field>
+          <a class="button is-link" @click="isCardModalActive = false">新建</a>
+        </div>
+      </div>
+    </b-modal>
   </div>
 </template>
 
 <script>
 import Card from "@/components/Card";
 import { getTeacherCourses } from "@/api/course";
+
 export default {
   components: {
     Card
   },
   data() {
     return {
-      courses: []
+      courses: [],
+      isCardModalActive: false
     };
   },
   mounted() {
@@ -59,6 +76,22 @@ export default {
     color: #ffffff;
   }
 }
+.card {
+  padding: 30px;
+  .title {
+    font-size: 1.25rem;
+    small {
+      color: gray;
+      font-size: 1rem;
+    }
+  }
+  .content {
+    a {
+      margin-top: 30px;
+      width: 100%;
+    }
+  }
+}
 .newSubject {
   height: 80px;
   width: 80px;

+ 160 - 138
src/views/teacher/Question/index.vue

@@ -1,152 +1,139 @@
 <template>
   <div>
-    <nav class="breadcrumb" aria-label="breadcrumbs">
-      <ul>
-        <li><router-link :to="{ name: 'HOME' }">所有课程</router-link></li>
-        <li>
-          <router-link :to="`/course-teacher/${course.id}/dashboard`">{{
-            course.name
-          }}</router-link>
-        </li>
-        <li class="is-active">
-          <router-link :to="`/student-course/${course.id}/question`"
-            >查看题目</router-link
-          >
-        </li>
-      </ul>
-    </nav>
-    <!-- container面板-->
-    <div class="data-table">
-      <!--下拉框&搜索框-->
-      <div class="form-group">
-        <!--下拉框-->
-        <b-field>
-          <b-select placeholder="选择题目类型">
-            <option
-              v-for="option in questionType"
-              :value="option.title"
-              :key="option.id"
-            >
-              {{ option.title }}
-            </option>
-          </b-select>
-        </b-field>
-        <!--搜索框-->
-        <b-field>
-          <b-input
-            placeholder="输入关键字查找题目"
-            type="search"
-            icon="magnify"
-          >
-          </b-input>
-        </b-field>
-      </div>
-      <!--题目table-->
-      <div class="question-list">
-        <b-table
-          :data="data"
-          paginated
-          per-page="5"
-          detailed
-          detail-key="id"
-          @details-open="
-            (row, index) => $toast.open(`Expanded ${row.user.first_name}`)
-          "
-        >
-          <template slot-scope="props">
-            <b-table-column field="id" label="ID" width="40" numeric sortable>
-              <!--{{ props.row.id }}-->1
-            </b-table-column>
+    <page-header :routes="[{ name: `查看题目`, path: 'question/' }]">
+      <template slot="title">
+        查看题目
+      </template>
+    </page-header>
+    <page-body>
+      <!-- container面板-->
+      <div class="data-table">
+        <!--下拉框&搜索框-->
+        <div class="form-group">
+          <!--下拉框-->
+          <b-field>
+            <b-select placeholder="选择题目类型">
+              <option
+                v-for="option in questionType"
+                :value="option.title"
+                :key="option.id"
+              >
+                {{ option.title }}
+              </option>
+            </b-select>
+          </b-field>
+          <!--搜索框-->
+          <div class="field has-addons">
+            <div class="control">
+              <input
+                class="input"
+                type="text"
+                placeholder="输入关键字查找题目"
+              />
+            </div>
+            <div class="control"><a class="button is-link"> 查找 </a></div>
+          </div>
+          <!--<b-field>-->
+          <!--<b-input-->
+          <!--placeholder="输入关键字查找题目"-->
+          <!--type="search"-->
+          <!--icon="magnify"-->
+          <!--&gt;-->
+          <!--</b-input>-->
+          <!--</b-field>-->
+        </div>
+        <!--题目table-->
+        <div class="question-list">
+          <b-table :data="data" 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 field="user.first_name" label="题目名称">
-              {{ props.row.name }}
-            </b-table-column>
+              <b-table-column label="题目名称">
+                <router-link
+                  :to="{
+                    path: `${props.row.id}`,
+                    query: { questionName: props.row.name }
+                  }"
+                  >{{ props.row.name }}</router-link
+                >
+              </b-table-column>
 
-            <b-table-column field="user.last_name" label="题目描述">
-              <div class="description">{{ props.row.description }}</div>
-            </b-table-column>
+              <b-table-column label="题目描述" width="400">
+                <div class="description">{{ props.row.description }}</div>
+              </b-table-column>
 
-            <b-table-column
-              field="difficulty"
-              label="题目难度"
-              sortable
-              centered
-            >
-              <span class="tag is-success">
-                {{ props.row.difficulty == 1 ? "简单" : "困难" }}
-              </span>
-            </b-table-column>
+              <b-table-column
+                field="difficulty"
+                label="题目难度"
+                sortable
+                centered
+              >
+                <span
+                  :class="['tag', `${difficultyClass(props.row.difficulty)}`]"
+                >
+                  {{ difficultyType(props.row.difficulty) }}
+                </span>
+              </b-table-column>
 
-            <b-table-column
-              label="平均时长"
-              field="expectTime"
-              sortable
-              centered
-            >
-              {{ props.row.expectTime }}分钟
-            </b-table-column>
-            <b-table-column label="题目标签">
-              {{ props.row.assignmentType.value }}
-            </b-table-column>
-          </template>
+              <b-table-column
+                label="平均时长"
+                field="expectTime"
+                sortable
+                centered
+              >
+                {{ props.row.expectTime }}分钟
+              </b-table-column>
+            </template>
 
-          <template slot="detail" slot-scope="props">
-            <article class="media">
-              <!--<figure class="media-left">-->
-              <!--<p class="image is-64x64">-->
-              <!--<img src="/static/img/placeholder-128x128.png">-->
-              <!--</p>-->
-              <!--</figure>-->
-              <div class="media-content">
-                <div class="content">
-                  <p>
-                    <strong>{{ props.row.name }}</strong>
-                    <small>@{{ props.row.expectTime }}分钟</small> <br />
-                    {{ props.row.description }}
-                  </p>
+            <template slot="detail" slot-scope="props">
+              <article class="media">
+                <div class="media-content">
+                  <div class="content">
+                    <p class="detail-content">
+                      <strong>{{ props.row.name }}</strong>
+                      <small
+                        ><b-tag type="is-info"
+                          >{{ props.row.expectTime }}分钟</b-tag
+                        ></small
+                      >
+                      <small
+                        ><b-tag
+                          :type="`${difficultyClass(props.row.difficulty)}`"
+                          >{{ difficultyType(props.row.difficulty) }}</b-tag
+                        ></small
+                      >
+                      <small
+                        ><b-tag type="is-link">{{
+                          props.row.assignmentType.value
+                        }}</b-tag></small
+                      >
+                      <br /><br />
+                      {{ props.row.description }}
+                    </p>
+                  </div>
                 </div>
-              </div>
-            </article>
-          </template>
-        </b-table>
+              </article>
+            </template>
+          </b-table>
+        </div>
       </div>
-    </div>
+    </page-body>
   </div>
 </template>
 <script>
 import { mapState } from "vuex";
 import { getStudentCourseReviewList } from "@/api/review";
 import { getTeacherQuestions } from "@/api/question";
-
-// const data = [
-//   {
-//     id: 1,
-//     name: "第一题",
-//     description: "这题xxxxxxxx",
-//     difficulty: "easy",
-//     expectTime: 20,
-//     assignmentType: "文档作业"
-//   },
-//   {
-//     id: 2,
-//     name: "第一题",
-//     description: "这题xxxxxxxx",
-//     difficulty: "easy",
-//     expectTime: 25,
-//     assignmentType: "文档作业"
-//   },
-//   {
-//     id: 3,
-//     name: "第一题",
-//     description: "这题xxxxxxxx",
-//     difficulty: "easy",
-//     expectTime: 35,
-//     assignmentType: "文档作业"
-//   }
-// ];
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
 
 export default {
-  components: {},
+  components: {
+    PageHeader,
+    PageBody
+  },
   computed: {
     ...mapState({
       course: state => state.course.currentCourse
@@ -168,6 +155,32 @@ export default {
       value => (this.review = value.data)
     );
     getTeacherQuestions().then(value => (this.data = value.data));
+  },
+  methods: {
+    difficultyType: level => {
+      switch (level) {
+        case 1:
+          return "简单";
+        case 2:
+          return "中等";
+        case 3:
+          return "困难";
+        default:
+          return "--";
+      }
+    },
+    difficultyClass: level => {
+      switch (level) {
+        case 1:
+          return "is-success";
+        case 2:
+          return "is-warning";
+        case 3:
+          return "is-danger";
+        default:
+          return "is-link";
+      }
+    }
   }
 };
 </script>
@@ -176,12 +189,12 @@ export default {
   /*color: #7957d5;*/
   font-weight: bold;
 }
-.data-table {
-  width: 100%;
-  padding: 30px;
-  min-height: 80vh;
-  background-color: #ffffff;
-}
+/*.data-table {*/
+/*width: 100%;*/
+/*padding: 30px;*/
+/*!*min-height: 80vh;*!*/
+/*background-color: #ffffff;*/
+/*}*/
 .question-list {
   margin-top: 30px;
 }
@@ -198,4 +211,13 @@ export default {
   word-break: break-all;
   display: -webkit-box;
 }
+.detail-content {
+  strong {
+    font-size: 1.2rem;
+    margin-right: 10px;
+  }
+  small {
+    margin-right: 10px;
+  }
+}
 </style>

+ 123 - 0
src/views/teacher/Question/questionDetail.vue

@@ -0,0 +1,123 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '查看题目', path: 'question/' },
+        { name: `${questionName}`, path: `question/123` }
+      ]"
+    >
+      <template slot="title">
+        {{ questionName }}
+      </template>
+      <template slot="content">
+        <b-tag class="tag" type="is-link" rounded>{{
+          questionInfo.assignmentType ? questionInfo.assignmentType.value : ""
+        }}</b-tag>
+        <b-tag class="tag" type="is-link" rounded>{{
+          questionInfo.type ? questionInfo.type.value : ""
+        }}</b-tag>
+      </template>
+    </page-header>
+    <page-body>
+      <div class="data-table">
+        <div class="question-description">
+          <div class="subtitle">题目描述</div>
+          <div class="question-content">{{ questionInfo.description }}</div>
+        </div>
+        <div class="question-description">
+          <div class="subtitle">预计完成时间</div>
+          <div class="question-content">{{ questionInfo.expectTime }}分钟</div>
+        </div>
+        <div class="question-description">
+          <div class="subtitle">题目难度</div>
+          <div class="question-content">
+            {{ difficultyType(questionInfo.difficulty) }}
+          </div>
+        </div>
+        <div class="question-description">
+          <div class="subtitle">文件信息</div>
+          <div class="question-content">
+            <div
+              class="file-info"
+              v-for="(item, index) in questionInfo.file"
+              :key="index"
+            >
+              <a>{{ item.filename }}</a>
+            </div>
+          </div>
+        </div>
+      </div>
+    </page-body>
+  </div>
+</template>
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import { getQuestionDetail } from "@/api/question";
+
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  mounted() {
+    this.questionName = this.$route.query.questionName;
+    getQuestionDetail(234).then(res => {
+      console.log(res.data);
+      this.questionInfo = res.data;
+    });
+  },
+  data() {
+    return {
+      questionName: "",
+      questionInfo: {}
+    };
+  },
+  methods: {
+    difficultyType: level => {
+      switch (level) {
+        case 1:
+          return "简单";
+        case 2:
+          return "中等";
+        case 3:
+          return "困难";
+        default:
+          return "--";
+      }
+    },
+    difficultyClass: level => {
+      switch (level) {
+        case 1:
+          return "is-success";
+        case 2:
+          return "is-warning";
+        case 3:
+          return "is-danger";
+        default:
+          return "is-link";
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.tag {
+  margin-right: 10px;
+}
+.question-description {
+  margin-bottom: 1.5rem;
+  .subtitle {
+    width: 100%;
+    font-weight: bold;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #dbdbdb;
+  }
+  .file-info {
+    margin-bottom: 0.75rem;
+    span {
+      margin-left: 0.75rem;
+    }
+  }
+}
+</style>

+ 239 - 127
src/views/teacher/Result/index.vue

@@ -1,122 +1,144 @@
 <template>
   <div>
-    <nav class="breadcrumb" aria-label="breadcrumbs">
-      <ul>
-        <li><router-link :to="{ name: 'HOME' }">所有课程</router-link></li>
-        <li>
-          <router-link :to="`/course-teacher/${course.id}/dashboard`">{{
-            course.name
-          }}</router-link>
-        </li>
-        <li class="is-active">
-          <router-link :to="`/student-course/${course.id}/result`"
-            >查看成绩</router-link
-          >
-        </li>
-      </ul>
-    </nav>
-    <!-- container面板-->
-    <div class="data-table">
-      <!--下拉框&搜索框-->
-      <div class="form-group">
-        <!--下拉框-->
-        <b-field> <a class="button is-link">导出作业成绩</a> </b-field>
-        <b-field>
-          <a class="button is-link" @click="isCardModalActive = true"
-            >成绩比例调控</a
-          >
-        </b-field>
-      </div>
-      <!--题目table-->
-      <div class="question-list">
-        <b-table :data="data" paginated per-page="5" 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="文档作业" centered>
-              <div
-                class="result"
-                v-for="item in props.row.documentHomeworkResult"
-                :key="item.id"
-              >
-                {{ item.name }} : {{ item.result }}
-              </div>
-            </b-table-column>
+    <page-header :routes="[{ name: '成绩管理', path: 'result/' }]">
+      <template slot="title">
+        全部作业成绩
+      </template>
+    </page-header>
+    <page-body>
+      <!-- container面板-->
+      <div class="data-table">
+        <!--下拉框&搜索框-->
+        <div class="form-group">
+          <!--下拉框-->
+          <b-field> <a class="button is-link">导出作业成绩</a> </b-field>
+          <b-field>
+            <a class="button is-link" @click="isCardModalActive = true"
+              >成绩比例调控</a
+            >
+          </b-field>
+        </div>
+        <!--成绩图表-->
+        <div class="chart">
+          <canvas id="result-chart" style="width: 100% !important;"></canvas>
+        </div>
+        <!--题目table-->
+        <div class="question-list">
+          <b-table :data="data" paginated per-page="5" 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="学生姓名" width="120" centered>
+                {{ props.row.student }}
+              </b-table-column>
+              <b-table-column label="文档作业" centered> 80 </b-table-column>
 
-            <b-table-column label="代码作业" centered>
-              <div
-                class="result"
-                v-for="item in props.row.codeHomeworkResult"
-                :key="item.id"
-              >
-                {{ item.name }} : {{ item.result }}
-              </div>
-            </b-table-column>
+              <b-table-column label="代码作业" centered> 90 </b-table-column>
 
-            <b-table-column label="互评作业" centered>
-              <div
-                class="result"
-                v-for="item in props.row.reviewHomeworkResult"
-                :key="item.id"
-              >
-                {{ item.name }} : {{ item.result }}
-              </div>
-            </b-table-column>
-            <b-table-column field="difficulty" label="总评" sortable centered>
-              <span
-                :class="[
-                  'tag',
-                  props.row.finalResult > 60 ? 'is-success' : 'is-danger'
-                ]"
+              <b-table-column label="互评作业" centered> 100 </b-table-column>
+              <b-table-column
+                field="finalResult"
+                label="总评"
+                sortable
+                centered
               >
-                {{ props.row.finalResult }}
-              </span>
-            </b-table-column>
-          </template>
+                <span :class="['tag', type(props.row.finalResult)]">
+                  {{ props.row.finalResult }}
+                </span>
+              </b-table-column>
+            </template>
 
-          <template slot="detail">
-            <article class="media">
-              <!--<figure class="media-left">-->
-              <!--<p class="image is-64x64">-->
-              <!--<img src="/static/img/placeholder-128x128.png">-->
-              <!--</p>-->
-              <!--</figure>-->
-              <div class="media-content">
-                <div class="content">
-                  <p>
-                    <strong>成绩详情</strong>
-                    <small>这里可以放具体的作业成绩,前面可以放总评啥的</small>
-                    <br />
-                  </p>
+            <template slot="detail" slot-scope="props">
+              <article class="media">
+                <div class="media-content">
+                  <div class="content">
+                    <!--<strong>成绩详情</strong>-->
+                    <!--<br />-->
+                    <div class="homework-layout">
+                      <div class="document-homework">
+                        <div class="homework-class">文档作业</div>
+                        <div
+                          class="result"
+                          v-for="item in props.row.documentHomeworkResult"
+                          :key="item.id"
+                        >
+                          {{ item.name }} : {{ item.result }}
+                        </div>
+                      </div>
+                      <div class="document-homework">
+                        <div class="homework-class">代码作业</div>
+                        <div
+                          class="result"
+                          v-for="item in props.row.codeHomeworkResult"
+                          :key="item.id"
+                        >
+                          {{ item.name }} : {{ item.result }}
+                        </div>
+                      </div>
+                      <div class="document-homework">
+                        <div class="homework-class">互评作业</div>
+                        <div
+                          class="result"
+                          v-for="item in props.row.reviewHomeworkResult"
+                          :key="item.id"
+                        >
+                          {{ item.name }} : {{ item.result }}
+                        </div>
+                      </div>
+                    </div>
+                  </div>
                 </div>
-              </div>
-            </article>
-          </template>
-        </b-table>
+              </article>
+            </template>
+          </b-table>
+        </div>
       </div>
-    </div>
+    </page-body>
     <!-- 成绩比例调控模态框 -->
-    <b-modal :active.sync="isCardModalActive" :width="320" scroll="keep">
+    <b-modal :active.sync="isCardModalActive" width="420px" scroll="keep">
       <div class="card">
         <div class="title">
           成绩比例调控<small>&nbsp;&nbsp;请按百分比填写</small>
         </div>
         <div class="content">
-          <b-field label="文档作业比例">
-            <b-input v-model="name"></b-input>
-          </b-field>
-          <b-field label="互评作业比例">
-            <b-input v-model="name"></b-input>
-          </b-field>
-          <b-field label="代码作业比例">
-            <b-input v-model="name"></b-input>
-          </b-field>
-          <a class="button is-link" @click="isCardModalActive = false"
-            >确认调整</a
-          >
+          <div class="homework-item">
+            <label>文档作业</label>
+            <div class="homework-percent">
+              <span>第一次作业</span>
+              <b-input></b-input>
+            </div>
+            <div class="homework-percent">
+              <span>第一次作业</span>
+              <b-input></b-input>
+            </div>
+            <div class="homework-percent">
+              <span>第一次作业</span>
+              <b-input></b-input>
+            </div>
+          </div>
+          <div class="homework-item">
+            <label>互评作业</label>
+            <div class="homework-percent">
+              <span>第一次作业</span>
+              <b-input></b-input>
+            </div>
+            <div class="homework-percent">
+              <span>第一次作业</span>
+              <b-input></b-input>
+            </div>
+          </div>
+          <div class="homework-item">
+            <label>代码作业</label>
+            <div class="homework-percent">
+              <span>第一次作业</span>
+              <b-input></b-input>
+            </div>
+          </div>
         </div>
+        <a class="button is-link" @click="isCardModalActive = false"
+          >确认调整</a
+        >
       </div>
     </b-modal>
   </div>
@@ -124,9 +146,15 @@
 <script>
 import { mapState } from "vuex";
 import { getStudentCourseReviewList } from "@/api/review";
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import Chart from "chart.js";
+import BInput from "buefy/src/components/input/Input";
+
 const data = [
   {
     id: 1,
+    student: "小白",
     documentHomeworkResult: [
       {
         id: 1,
@@ -144,14 +172,14 @@ const data = [
         result: 99
       }
     ],
-    reviewHomeworkResult: [
+    charge: [
       {
         id: 1,
         name: "第一次作业",
         result: 80
       }
     ],
-    codeHomeworkResult: [
+    score: [
       {
         id: 1,
         name: "第一次作业",
@@ -167,6 +195,7 @@ const data = [
   },
   {
     id: 2,
+    student: "yasen",
     documentHomeworkResult: [
       {
         id: 1,
@@ -184,14 +213,14 @@ const data = [
         result: 99
       }
     ],
-    reviewHomeworkResult: [
+    charge: [
       {
         id: 1,
         name: "第一次作业",
         result: 80
       }
     ],
-    codeHomeworkResult: [
+    score: [
       {
         id: 1,
         name: "第一次作业",
@@ -203,10 +232,12 @@ const data = [
         result: 85
       }
     ],
-    finalResult: 99
+
+    finalResult: 79
   },
   {
     id: 3,
+    student: "soleil",
     documentHomeworkResult: [
       {
         id: 1,
@@ -224,14 +255,14 @@ const data = [
         result: 99
       }
     ],
-    reviewHomeworkResult: [
+    charge: [
       {
         id: 1,
         name: "第一次作业",
         result: 80
       }
     ],
-    codeHomeworkResult: [
+    score: [
       {
         id: 1,
         name: "第一次作业",
@@ -243,10 +274,11 @@ const data = [
         result: 85
       }
     ],
-    finalResult: 99
+    finalResult: 59
   },
   {
     id: 4,
+    student: "xiaohan",
     documentHomeworkResult: [
       {
         id: 1,
@@ -264,14 +296,14 @@ const data = [
         result: 99
       }
     ],
-    reviewHomeworkResult: [
+    charge: [
       {
         id: 1,
         name: "第一次作业",
         result: 80
       }
     ],
-    codeHomeworkResult: [
+    score: [
       {
         id: 1,
         name: "第一次作业",
@@ -288,7 +320,11 @@ const data = [
 ];
 
 export default {
-  components: {},
+  components: {
+    BInput,
+    PageHeader,
+    PageBody
+  },
   computed: {
     ...mapState({
       course: state => state.course.currentCourse
@@ -296,6 +332,7 @@ export default {
   },
   data() {
     return {
+      name: "",
       courses: [],
       review: [],
       questionType: [
@@ -303,13 +340,57 @@ export default {
         { id: 2, title: "代码作业题目" }
       ],
       data,
-      isCardModalActive: false
+      isCardModalActive: false,
+      myChart2: {}
     };
   },
+  methods: {
+    type(value) {
+      const number = parseFloat(value);
+      if (number < 60) {
+        return "is-danger";
+      } else if (number >= 60 && number < 80) {
+        return "is-warning";
+      } else if (number >= 80) {
+        return "is-success";
+      }
+    }
+  },
   mounted() {
     getStudentCourseReviewList(this.courses.id).then(
       value => (this.review = value.data)
     );
+    //绘制图表
+    var resultChart = document.getElementById("result-chart");
+    var myChart2 = new Chart(resultChart, {
+      type: "bar",
+      data: {
+        labels: [
+          "0-30分段",
+          "30-60分段",
+          "60-70分段",
+          "70-80分段",
+          "80-90分段",
+          "90-100分段"
+        ],
+        datasets: [
+          {
+            label: "人数",
+            backgroundColor: "rgba(54,162,235,0.3)",
+            borderColor: "rgba(54,162,235,1)",
+            borderWidth: 1,
+            pointStrokeColor: "#fff",
+            pointStyle: "crossRot",
+            data: [6, 10, 34, 50, 80, 30],
+            cubicInterpolationMode: "monotone",
+            spanGaps: "false",
+            fill: "false"
+          }
+        ]
+      },
+      options: {}
+    });
+    this.myChart2 = myChart2;
   }
 };
 </script>
@@ -318,14 +399,17 @@ export default {
   /*color: #7957d5;*/
   font-weight: bold;
 }
-.data-table {
-  width: 100%;
-  padding: 30px;
-  min-height: 80vh;
-  background-color: #ffffff;
+/*.data-table {*/
+/*width: 100%;*/
+/*padding: 30px;*/
+/*!*min-height: 80vh;*!*/
+/*background-color: #ffffff;*/
+/*}*/
+.chart {
+  margin-top: 1.5rem;
 }
 .question-list {
-  margin-top: 30px;
+  margin-top: 3rem;
 }
 .form-group {
   display: flex;
@@ -337,20 +421,48 @@ export default {
 .card {
   padding: 30px;
   .title {
+    padding-bottom: 5px;
     font-size: 1.25rem;
+    border-bottom: 1px solid #dbdbdb;
     small {
       color: gray;
       font-size: 1rem;
     }
   }
   .content {
-    a {
-      margin-top: 30px;
-      width: 100%;
+    .homework-item {
+      margin-bottom: 30px;
+      flex: 1;
+      label {
+        width: 100%;
+        font-weight: bold;
+        border-bottom: 2px solid #dbdbdb;
+      }
+      .homework-percent {
+        margin-top: 20px;
+        display: flex;
+        align-items: center;
+        span {
+          margin-right: 0.5rem;
+        }
+      }
     }
   }
+  a {
+    margin-top: 30px;
+    width: 100%;
+  }
 }
-.button {
-  background-color: #6172ec;
+.homework-layout {
+  display: flex;
+  justify-content: space-between;
+  .document-homework {
+    margin-right: 3rem;
+    .homework-class {
+      font-weight: bold;
+      border-bottom: 1px solid #dbdbdb;
+      margin-bottom: 10px;
+    }
+  }
 }
 </style>

+ 114 - 0
src/views/teacher/Review/ReviewDetail/Detail/index.vue

@@ -0,0 +1,114 @@
+<template>
+  <div>
+    <div class="page-section">
+      <div class="columns is-desktop">
+        <div class="column">
+          <h5 class="title is-5">id:</h5>
+          <div class="subtitle is-6">{{ detail.reviewId }}</div>
+        </div>
+        <div class="column">
+          <h5 class="title is-5">名称:</h5>
+          <div class="subtitle is-6">{{ detail.name }}</div>
+        </div>
+        <div class="column">
+          <h5 class="title is-5">状态:</h5>
+          <div class="subtitle is-6">
+            <review-state-tag :state="detail.state" />
+          </div>
+        </div>
+      </div>
+      <div class="columns is-desktop">
+        <div class="column">
+          <h5 class="title is-5">互评结束时间:</h5>
+          <div class="subtitle is-6">
+            {{ displayUnixTime(detail.reviewDdl) }}
+          </div>
+        </div>
+        <div class="column">
+          <h5 class="title is-5">重申申请结束时间:</h5>
+          <div class="subtitle is-6">
+            {{ displayUnixTime(detail.argueDdl) }}
+          </div>
+        </div>
+        <div class="column">
+          <h5 class="title is-5">重申结束时间:</h5>
+          <div class="subtitle is-6">{{ displayUnixTime(detail.endDdl) }}</div>
+        </div>
+      </div>
+    </div>
+    <div class="space">
+      <div class="columns is-desktop">
+        <div class="column">
+          <div class="page-section">
+            <h5 class="title is-5">完成率: {{ sum.completeRate }}%</h5>
+            <progress
+              class="progress is-success"
+              :value="sum.completeRate"
+              max="100"
+              >{sum.completeRate}%</progress
+            >
+          </div>
+        </div>
+        <div class="column">
+          <div class="page-section">
+            <h5 class="title is-5">checkList 通过情况:</h5>
+            <div
+              class="checklist-item"
+              :key="item.id"
+              v-for="item in sum.checklistItems"
+            >
+              <div>id: {{ item.id }}</div>
+              <b-tag type="is-success" style="margin-right: 5px"
+                >通过 : {{ item.passedNum }}</b-tag
+              >
+              <span>{{ item.content }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import ReviewStateTag from "@/components/ReviewStateTag/index";
+import timeMixins from "@/mixins/time";
+import { getTeacherReviewResult } from "@/api/review";
+export default {
+  components: { ReviewStateTag },
+  mixins: [timeMixins],
+  props: {
+    detail: {
+      type: Object,
+      default: () => ({ completeRate: 0 })
+    }
+  },
+  data() {
+    return {
+      sum: { passedCDF: [], checklistItems: [] }
+    };
+  },
+  mounted() {
+    getTeacherReviewResult(this.$route.params.reviewId).then(
+      value => (this.sum = value.data)
+    );
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../../assets/scss/app";
+.divider {
+  margin: $default-margin 0;
+  height: 1px;
+  background-color: #eaeaea;
+}
+
+.space {
+  padding-top: $default-margin-between;
+}
+
+.checklist-item {
+  margin: $default-margin 0;
+}
+</style>

+ 121 - 0
src/views/teacher/Review/ReviewDetail/GroupArgueDetail/index.vue

@@ -0,0 +1,121 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '互评作业', path: 'review' },
+        { name: '文档详情', path: `review/${$route.params.reviewId}` },
+        {
+          name: '小组作业',
+          path: `review/${$route.params.reviewId}/groups/${
+            $route.params.groupId
+          }`
+        },
+        {
+          name: '重评作业'
+        }
+      ]"
+    >
+      <template slot="title">
+        {{ groupDetail.name }}
+      </template>
+      <template slot="action">
+        <router-link
+          :to="$route.path.substring(0, $route.path.length - '/argue'.length)"
+          class="button is-link"
+        >
+          返回小组详情
+        </router-link>
+      </template>
+    </page-header>
+    <page-body>
+      <div class="columns">
+        <div class="column is-8">
+          <div class="page-section">
+            <div v-if="detail.docContent" class="content">
+              <vue-markdown>{{ detail.docContent }}</vue-markdown>
+            </div>
+          </div>
+        </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>
+            <check-list v-model="detail.checkList" />
+          </div>
+        </div>
+      </div>
+    </page-body>
+  </div>
+</template>
+<script>
+import VueMarkdown from "vue-markdown";
+import { getTeacherStartArgue, postTeacherArgueResult } from "@/api/review";
+import timeMixins from "@/mixins/time";
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import { getGroupDetail } from "@/api/group";
+import CheckList from "@/components/CheckList/index";
+export default {
+  components: {
+    PageBody,
+    PageHeader,
+    CheckList,
+    VueMarkdown
+  },
+  data() {
+    return {
+      detail: {
+        review: {},
+        checkList: []
+      },
+      groupDetail: {},
+      submitLoading: false
+    };
+  },
+  mixins: [timeMixins],
+  mounted() {
+    const { reviewId, groupId } = this.$route.params;
+    getGroupDetail(groupId).then(value => (this.groupDetail = value.data));
+    getTeacherStartArgue(reviewId, groupId).then(
+      resp => (this.detail = resp.data)
+    );
+  },
+  methods: {
+    submitCheckList() {
+      const { reviewId, groupId } = this.$route.params;
+      this.submitLoading = true;
+      postTeacherArgueResult(reviewId, groupId, this.detail.checkList).then(
+        resp => {
+          this.submitLoading = false;
+          this.detail.checkList = resp.checkList;
+        }
+      );
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../../assets/scss/app";
+.right-part {
+  margin-bottom: -1.5rem;
+  margin-right: -1.5rem;
+  background: white;
+  border-top: 1px solid #eff2f7;
+  /*position: sticky;*/
+  /*top: 0;*/
+  /*max-height: 100vh;*/
+  /*overflow-y: auto;*/
+  box-shadow: $default-shadow;
+}
+
+.action-button {
+  width: 100%;
+  border-radius: 0;
+}
+</style>

+ 100 - 0
src/views/teacher/Review/ReviewDetail/GroupDocDetail/index.vue

@@ -0,0 +1,100 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '互评作业', path: 'review' },
+        { name: '文档详情', path: `review/${$route.params.reviewId}` },
+        {
+          name: '小组作业',
+          path: `review/${$route.params.reviewId}/groups/${
+            $route.params.groupId
+          }`
+        }
+      ]"
+    >
+      <template slot="title">
+        {{ groupDetail.name }}
+      </template>
+      <template slot="action">
+        <router-link :to="`${$route.path}/argue`" class="button is-primary">
+          开始重评
+        </router-link>
+      </template>
+    </page-header>
+    <page-body>
+      <div class="columns">
+        <div class="column is-8">
+          <div class="page-section">
+            <div v-if="detail.docContent" class="content">
+              <vue-markdown>{{ detail.docContent }}</vue-markdown>
+            </div>
+          </div>
+        </div>
+        <div class="column is-4">
+          <div class="right-part">
+            <result-check-list-item
+              v-for="item in detail.checkListResult"
+              :key="item.id"
+              :check-result-item="item"
+            />
+          </div>
+        </div>
+      </div>
+    </page-body>
+  </div>
+</template>
+<script>
+import VueMarkdown from "vue-markdown";
+import { getTeacherReviewGroupDetail } from "@/api/review";
+import timeMixins from "@/mixins/time";
+import ResultCheckListItem from "@/components/CheckList/ResultCheckListItem";
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import { getGroupDetail } from "@/api/group";
+export default {
+  components: {
+    PageBody,
+    PageHeader,
+    ResultCheckListItem,
+    VueMarkdown
+  },
+  data() {
+    return {
+      detail: {
+        review: {},
+        checkListResult: []
+      },
+      groupDetail: {},
+      submitLoading: false
+    };
+  },
+  mixins: [timeMixins],
+  mounted() {
+    const { reviewId, groupId } = this.$route.params;
+    getGroupDetail(groupId).then(value => (this.groupDetail = value.data));
+    getTeacherReviewGroupDetail(reviewId, groupId).then(
+      resp => (this.detail = resp.data)
+    );
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../../assets/scss/app";
+.right-part {
+  margin-bottom: -1.5rem;
+  margin-right: -1.5rem;
+  background: white;
+  border-top: 1px solid #eff2f7;
+  /*position: sticky;*/
+  /*top: 0;*/
+  /*max-height: 100vh;*/
+  /*overflow-y: auto;*/
+  box-shadow: $default-shadow;
+}
+
+.action-button {
+  width: 100%;
+  border-radius: 0;
+}
+</style>

+ 51 - 0
src/views/teacher/Review/ReviewDetail/Groups/index.vue

@@ -0,0 +1,51 @@
+<template>
+  <div>
+    <div class="input-area">
+      <b-input v-model="filter" size="is-medium" placeholder="输入小组名来搜索">
+      </b-input>
+    </div>
+    <div class="columns is-multiline is-desktop">
+      <div :key="item.id" v-for="item in availableGroups" class="column is-4">
+        <router-link :to="`${$route.path}/${item.id}`">
+          <card hoverable> {{ item.name }} </card>
+        </router-link>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Card from "@/components/Card";
+export default {
+  components: {
+    Card
+  },
+  props: {
+    groups: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      filter: ""
+    };
+  },
+  computed: {
+    availableGroups() {
+      if (this.filter === "") return this.groups;
+
+      return this.groups.filter(group => group.name.indexOf(this.filter) >= 0);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../../assets/scss/app";
+.input-area {
+  margin: 0 auto;
+  max-width: 600px;
+  padding-bottom: $default-margin-between;
+}
+</style>

+ 142 - 0
src/views/teacher/Review/ReviewDetail/index.vue

@@ -0,0 +1,142 @@
+<template>
+  <div>
+    <page-header
+      :routes="[{ name: '互评作业', path: 'review' }, { name: '文档详情' }]"
+      :tab-list="[
+        { name: '作业详情', path: `review/${$route.params.reviewId}` },
+        { name: '小组列表', path: `review/${$route.params.reviewId}/groups` }
+      ]"
+    >
+      <template slot="title">
+        {{ detail.name }}
+      </template>
+      <template slot="content">
+        <div>共有 {{ groups.length }} 组小组参与互评</div>
+      </template>
+      <template slot="action">
+        <div @click="() => (this.showModal = true)" class="button is-primary">
+          制定评分标准
+        </div>
+      </template>
+      <template slot="extraContent">
+        <div class="tag is-light" v-if="detail.state === 'REVIEWING'">
+          互评结束时间 : {{ displayUnixTime(detail.reviewDdl) }}
+        </div>
+        <div class="tag is-light" v-if="detail.state === 'ARGUING'">
+          申诉结束时间 : {{ displayUnixTime(detail.argueDdl) }}
+        </div>
+        <div class="tag is-light" v-if="detail.state === 'SOLVING'">
+          处理申诉结束时间 : {{ displayUnixTime(detail.endDdl) }}
+        </div>
+      </template>
+    </page-header>
+    <page-body> <router-view :detail="detail" :groups="groups" /> </page-body>
+    <b-modal :active.sync="showModal" :width="400">
+      <div class="card">
+        <div class="title">每个等级通过数字</div>
+        <div class="content">
+          <b-field label="A">
+            <b-input type="number" v-model="grade.A"></b-input>
+          </b-field>
+          <b-field label="B">
+            <b-input type="number" v-model="grade.B"></b-input>
+          </b-field>
+          <b-field label="C">
+            <b-input type="number" v-model="grade.C"></b-input>
+          </b-field>
+          <b-field label="D">
+            <b-input type="number" v-model="grade.D"></b-input>
+          </b-field>
+          <b-field label="E">
+            <b-input type="number" v-model="grade.E"></b-input>
+          </b-field>
+          <b-field label="F">
+            <b-input type="number" v-model="grade.F"></b-input>
+          </b-field>
+
+          <a
+            class="button is-link"
+            :class="{ 'is-loading': submitLoading }"
+            @click="submit()"
+          >
+            更新评分标准
+          </a>
+        </div>
+      </div>
+    </b-modal>
+  </div>
+</template>
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import {
+  getTeacherReviewAllGroups,
+  getTeacherReviewDetail,
+  postReviewSumLevel
+} from "@/api/review";
+import timeMixins from "@/mixins/time";
+export default {
+  components: { PageBody, PageHeader },
+  data() {
+    return {
+      detail: {},
+      groups: [],
+      showModal: false,
+      grade: {
+        A: 0,
+        B: 0,
+        C: 0,
+        D: 0,
+        E: 0,
+        F: 0
+      },
+      submitLoading: false
+    };
+  },
+  mixins: [timeMixins],
+  mounted() {
+    const { reviewId } = this.$route.params;
+    getTeacherReviewDetail(reviewId).then(resp => (this.detail = resp.data));
+    getTeacherReviewAllGroups(reviewId).then(resp => (this.groups = resp.data));
+  },
+  methods: {
+    async submit() {
+      const { reviewId } = this.$route.params;
+      this.submitLoading = true;
+      try {
+        await postReviewSumLevel(reviewId, this.grade);
+      } catch (e) {
+        this.$toast.open({
+          message: e.message,
+          type: "is-error"
+        });
+      }
+      this.submitLoading = false;
+      this.showModal = false;
+      this.$toast.open({
+        message: "制定成功",
+        type: "is-success"
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../assets/scss/app";
+.review-body {
+  display: flex;
+
+  &__left {
+    flex-grow: 1;
+  }
+
+  &__right {
+    min-width: 300px;
+  }
+}
+.card {
+  padding: $default-margin-between;
+  border-radius: $default-radius;
+}
+</style>

+ 142 - 0
src/views/teacher/Review/assignment.vue

@@ -0,0 +1,142 @@
+<template>
+  <div>
+    <page-header
+      :routes="[
+        { name: '互评作业', path: 'review' },
+        { name: '物流系统需求规格说明文档', path: `review/123` }
+      ]"
+    >
+      <template slot="title">
+        物流系统需求规格说明文档
+      </template>
+      <template slot="content">
+        <span>checklist:</span>
+        <b-tag class="checklist" type="is-link" rounded>需求明确</b-tag>
+        <b-tag class="checklist" type="is-link" rounded
+          >用例图、架构图、流程图完整</b-tag
+        >
+        <b-tag class="checklist" type="is-link" rounded>描述清晰</b-tag>
+      </template>
+    </page-header>
+    <page-body>
+      <!-- container面板-->
+      <div class="data-table">
+        <section>
+          <b-tabs type="is-boxed">
+            <b-tab-item label="未评价"> <!--题目table--> </b-tab-item>
+            <b-tab-item label="互评中"></b-tab-item>
+            <b-tab-item label="提出异议"> </b-tab-item>
+            <b-tab-item label="互评完成">
+              <!--题目table-->
+              <div class="question-list">
+                <b-table
+                  :data="data"
+                  paginated
+                  per-page="5"
+                  detailed
+                  detail-key="id"
+                >
+                  <template slot-scope="props">
+                    <b-table-column
+                      field="id"
+                      label="被评价小组"
+                      numeric
+                      sortable
+                      centered
+                    >
+                      {{ props.row.id }}
+                    </b-table-column>
+                    <b-table-column label="评价小组" numeric centered>
+                      {{ props.row.reviewId }}
+                    </b-table-column>
+                    <b-table-column label="作业等级" centered>
+                      {{ props.row.level }}
+                    </b-table-column>
+                    <b-table-column
+                      field="score"
+                      label="作业评分"
+                      sortable
+                      centered
+                    >
+                      <span :class="['tag', type(props.row.score)]">
+                        {{ props.row.score }}
+                      </span>
+                    </b-table-column>
+                  </template>
+
+                  <template slot="detail" slot-scope="props">
+                    <article class="media">
+                      <div class="media-content">
+                        <div class="content">
+                          <strong>成绩详情{{ props.row.score }}}</strong>
+                        </div>
+                      </div>
+                    </article>
+                  </template>
+                </b-table>
+              </div>
+            </b-tab-item>
+          </b-tabs>
+        </section>
+      </div>
+    </page-body>
+  </div>
+</template>
+<script>
+import { mapState } from "vuex";
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+
+const data = [
+  {
+    id: 1,
+    reviewId: 2,
+    level: "S",
+    score: 88
+  }
+];
+
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  },
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
+  },
+  data() {
+    return {
+      data
+    };
+  },
+  mounted() {},
+  methods: {
+    type(value) {
+      const number = parseFloat(value);
+      if (number < 60) {
+        return "is-danger";
+      } else if (number >= 60 && number < 80) {
+        return "is-warning";
+      } else if (number >= 80) {
+        return "is-success";
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.checklist {
+  margin-left: 0.75rem;
+}
+span {
+  font-weight: bold;
+}
+.data-table {
+  width: 100%;
+  padding: 30px;
+  /*min-height: 80vh;*/
+  background-color: #ffffff;
+}
+</style>

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

@@ -0,0 +1,63 @@
+<template>
+  <div>
+    <page-header :routes="[{ name: '互评作业', path: 'review' }]">
+      <template slot="title">
+        互评作业
+      </template>
+      <template slot="content">
+        共有 {{ review.length }} 份互评作业
+      </template>
+    </page-header>
+    <page-body>
+      <div class="columns is-multiline is-desktop">
+        <div :key="item.id" v-for="item in review" class="column is-4">
+          <router-link :to="`${$route.path}/${item.reviewId}`">
+            <card hoverable>
+              {{ item.name }}
+              <div><review-state-tag :state="item.state" /></div>
+            </card>
+          </router-link>
+        </div>
+      </div>
+    </page-body>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import { getTeacherCourseReviewList } from "@/api/review";
+import Card from "@/components/Card";
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+import ReviewStateTag from "@/components/ReviewStateTag/index";
+export default {
+  components: {
+    ReviewStateTag,
+    PageBody,
+    PageHeader,
+    Card
+  },
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse
+    })
+  },
+  data() {
+    return {
+      courses: [],
+      review: []
+    };
+  },
+  mounted() {
+    getTeacherCourseReviewList(this.courses.id).then(
+      value => (this.review = value.data)
+    );
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.top-nav-section {
+  padding: 0 40px;
+}
+</style>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 142
yarn.lock


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov