소스 검색

Merge branch 'develop' into feature/assignment-zhangyasen

Johnson 7 년 전
부모
커밋
b67582641b
10개의 변경된 파일334개의 추가작업 그리고 16개의 파일을 삭제
  1. 1 1
      mock/serializers/user/UserSerializer.js
  2. 1 0
      package.json
  3. 11 1
      src/api/review.js
  4. 4 6
      src/components/PageHeader/index.jsx
  5. 13 0
      src/main.js
  6. 13 0
      src/mixins/path.js
  7. 10 0
      src/mixins/time.js
  8. 3 3
      src/views/login/Register.vue
  9. 266 5
      src/views/teacher/Review/index.vue
  10. 12 0
      yarn.lock

+ 1 - 1
mock/serializers/user/UserSerializer.js

@@ -14,7 +14,7 @@ const makeId = require("../../util/makeId");
  * @param role
  * @param role
  * @returns {UserSerializerType}
  * @returns {UserSerializerType}
  */
  */
-module.exports = (role = "STUDENT") => {
+module.exports = (role = "TEACHER") => {
   return {
   return {
     id: makeId(),
     id: makeId(),
     role: role,
     role: role,

+ 1 - 0
package.json

@@ -17,6 +17,7 @@
     "register-service-worker": "^1.5.2",
     "register-service-worker": "^1.5.2",
     "vue": "^2.5.17",
     "vue": "^2.5.17",
     "vue-chartjs": "^3.4.0",
     "vue-chartjs": "^3.4.0",
+    "vue-flatpickr-component": "^8.1.1",
     "vue-markdown": "^2.2.4",
     "vue-markdown": "^2.2.4",
     "vue-router": "^3.0.1",
     "vue-router": "^3.0.1",
     "vuelidate": "^0.7.4",
     "vuelidate": "^0.7.4",

+ 11 - 1
src/api/review.js

@@ -5,6 +5,7 @@ import { REVIEW_MODULE } from "./_prefix";
 
 
 /**
 /**
  * 新建互评作业
  * 新建互评作业
+ * @param {string} courseId 课程id
  * @param {number} documentHwId 作业id
  * @param {number} documentHwId 作业id
  * @param {number} startAt 开始时间
  * @param {number} startAt 开始时间
  * @param {number} reviewDdl 互评结束时间
  * @param {number} reviewDdl 互评结束时间
@@ -15,6 +16,7 @@ import { REVIEW_MODULE } from "./_prefix";
  * @throws {StatusException} 输入不合法
  * @throws {StatusException} 输入不合法
  */
  */
 export const createReview = ({
 export const createReview = ({
+  courseId,
   documentHwId,
   documentHwId,
   startAt,
   startAt,
   reviewDdl,
   reviewDdl,
@@ -24,7 +26,15 @@ export const createReview = ({
 }) => {
 }) => {
   return request(`${REVIEW_MODULE}/review`, {
   return request(`${REVIEW_MODULE}/review`, {
     method: "POST",
     method: "POST",
-    body: { documentHwId, startAt, reviewDdl, argueDdl, endDdl, taskNumber }
+    body: {
+      courseId,
+      documentHwId,
+      startAt,
+      reviewDdl,
+      argueDdl,
+      endDdl,
+      taskNumber
+    }
   });
   });
 };
 };
 
 

+ 4 - 6
src/components/PageHeader/index.jsx

@@ -1,4 +1,5 @@
 import { mapState } from "vuex";
 import { mapState } from "vuex";
+import pathMixin from "@/mixins/path";
 import "./index.scss";
 import "./index.scss";
 
 
 export default {
 export default {
@@ -15,17 +16,14 @@ export default {
   },
   },
   computed: {
   computed: {
     ...mapState({
     ...mapState({
-      course: state => state.course.currentCourse,
-      profile: state => state.user.profile
+      course: state => state.course.currentCourse
     })
     })
   },
   },
+  mixins: [pathMixin],
   render() {
   render() {
-    const { profile, course = {}, routes = [], tabList = [] } = this;
-    const { role = "" } = profile;
-    const prefix = `/course-${role.toLowerCase()}/${course.id}`;
+    const { prefix, course, routes = [], tabList = [] } = this;
     const { logo, title, action, content, extraContent } = this.$slots;
     const { logo, title, action, content, extraContent } = this.$slots;
     const showTabs = tabList && tabList.length > 0;
     const showTabs = tabList && tabList.length > 0;
-    console.log(prefix);
     return (
     return (
       <div class="page-header">
       <div class="page-header">
         <nav class="breadcrumb" aria-label="breadcrumbs">
         <nav class="breadcrumb" aria-label="breadcrumbs">

+ 13 - 0
src/main.js

@@ -6,11 +6,24 @@ import "./registerServiceWorker";
 import Buefy from "buefy";
 import Buefy from "buefy";
 import "./assets/scss/app.scss";
 import "./assets/scss/app.scss";
 import Vuelidate from "vuelidate";
 import Vuelidate from "vuelidate";
+import VueFlatPickr from "vue-flatpickr-component";
+import "flatpickr/dist/flatpickr.css";
+import flatpickr from "../node_modules/flatpickr";
+import { Mandarin } from "../node_modules/flatpickr/dist/l10n/zh.js";
+// date picker
+flatpickr.setDefaults({
+  enableTime: true,
+  dateFormat: "U",
+  altFormat: "M	j, Y  H:i",
+  altInput: true,
+  locale: Mandarin
+});
 
 
 Vue.config.productionTip = false;
 Vue.config.productionTip = false;
 
 
 Vue.use(Vuelidate);
 Vue.use(Vuelidate);
 Vue.use(Buefy);
 Vue.use(Buefy);
+Vue.use(VueFlatPickr);
 
 
 new Vue({
 new Vue({
   router,
   router,

+ 13 - 0
src/mixins/path.js

@@ -0,0 +1,13 @@
+import { mapState } from "vuex";
+
+export default {
+  computed: {
+    ...mapState({
+      profile: state => state.user.profile
+    }),
+    prefix() {
+      const { role = "" } = this.profile;
+      return `/course-${role.toLowerCase()}/${this.$route.params.courseId}`;
+    }
+  }
+};

+ 10 - 0
src/mixins/time.js

@@ -1,10 +1,20 @@
 import dayjs from "dayjs";
 import dayjs from "dayjs";
+import { Mandarin } from "../../node_modules/flatpickr/dist/l10n/zh.js";
 
 
 export default {
 export default {
   methods: {
   methods: {
     displayUnixTime(time) {
     displayUnixTime(time) {
       if (!time) return;
       if (!time) return;
       return dayjs.unix(time).format("YYYY年 MM月 DD日 HH:mm:ss");
       return dayjs.unix(time).format("YYYY年 MM月 DD日 HH:mm:ss");
+    },
+    getDefaultConfig() {
+      return {
+        enableTime: true,
+        dateFormat: "U",
+        altFormat: "M	j, Y  H:i",
+        altInput: true,
+        locale: Mandarin
+      };
     }
     }
   }
   }
 };
 };

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

@@ -172,19 +172,19 @@ export default {
 }
 }
 .register {
 .register {
   color: #ffffff;
   color: #ffffff;
-  padding: 10px 0px 10px 0px;
+  padding: 10px 0 10px 0;
   display: flex;
   display: flex;
   justify-content: center;
   justify-content: center;
   font-size: 1rem;
   font-size: 1rem;
   width: 100%;
   width: 100%;
   background: linear-gradient(to right, #82b1ed, #b073fd);
   background: linear-gradient(to right, #82b1ed, #b073fd);
   border-radius: 4px;
   border-radius: 4px;
-  border: 0px;
+  border: 0;
 }
 }
 .login {
 .login {
   color: #7396fd;
   color: #7396fd;
   margin-top: 20px;
   margin-top: 20px;
-  padding: 10px 0px 10px 0px;
+  padding: 10px 0 10px 0;
   font-size: 1rem;
   font-size: 1rem;
   width: 100%;
   width: 100%;
   display: flex;
   display: flex;

+ 266 - 5
src/views/teacher/Review/index.vue

@@ -7,6 +7,11 @@
       <template slot="content">
       <template slot="content">
         共有 {{ review.length }} 份互评作业
         共有 {{ review.length }} 份互评作业
       </template>
       </template>
+      <template slot="action">
+        <a @click="() => (this.showModal = true)" class="button is-link">
+          新建互评作业
+        </a>
+      </template>
     </page-header>
     </page-header>
     <page-body>
     <page-body>
       <div class="columns is-multiline is-desktop">
       <div class="columns is-multiline is-desktop">
@@ -20,16 +25,108 @@
         </div>
         </div>
       </div>
       </div>
     </page-body>
     </page-body>
+    <b-modal :active.sync="showModal">
+      <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.documentHwId.$error ? 'is-danger' : ''"
+            :message="$v.documentHwId.$error ? '请选择文档' : ''"
+          >
+            <b-select
+              :loading="docHomeworkLoading"
+              v-model="$v.documentHwId.$model"
+              placeholder="选择一个文档作业"
+              expanded
+            >
+              <option
+                v-for="option in docHomework"
+                :value="option.id"
+                :key="option.id"
+              >
+                {{ option.name }}
+              </option>
+            </b-select>
+          </b-field>
+          <b-field
+            label="互评任务数"
+            :type="$v.taskNumber.$error ? 'is-danger' : ''"
+            :message="$v.taskNumber.$error ? '请输入互评任务数' : ''"
+          >
+            <b-input type="number" v-model="$v.taskNumber.$model"></b-input>
+          </b-field>
+          <b-field
+            label="互评开始时间"
+            :type="$v.startAt.$error ? 'is-danger' : ''"
+            :message="$v.startAt.$error ? '请选择时间' : ''"
+          >
+            <flat-pickr
+              class="input"
+              v-model="$v.startAt.$model"
+              :config="startAtConfig"
+            ></flat-pickr>
+          </b-field>
+          <b-field
+            label="互评结束时间"
+            :type="$v.reviewDdl.$error ? 'is-danger' : ''"
+            :message="$v.reviewDdl.$error ? '请选择时间' : ''"
+          >
+            <flat-pickr
+              class="input"
+              v-model="$v.reviewDdl.$model"
+              :config="reviewDdlConfig"
+            ></flat-pickr>
+          </b-field>
+          <b-field
+            label="申请重审结束时间"
+            :type="$v.argueDdl.$error ? 'is-danger' : ''"
+            :message="$v.argueDdl.$error ? '请选择时间' : ''"
+          >
+            <flat-pickr
+              class="input"
+              v-model="$v.argueDdl.$model"
+              :config="argueDdlConfig"
+            ></flat-pickr>
+          </b-field>
+          <b-field
+            label="重审结束时间"
+            :type="$v.endDdl.$error ? 'is-danger' : ''"
+            :message="$v.endDdl.$error ? '请选择时间' : ''"
+          >
+            <flat-pickr
+              class="input"
+              v-model="$v.endDdl.$model"
+              :config="endDdlConfig"
+            ></flat-pickr>
+          </b-field>
+
+          <a
+            class="button is-link"
+            :class="{ 'is-loading': submitting }"
+            @click="submit()"
+          >
+            创建互评作业
+          </a>
+        </div>
+      </div>
+    </b-modal>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
 import { mapState } from "vuex";
 import { mapState } from "vuex";
-import { getTeacherCourseReviewList } from "@/api/review";
+import { createReview, getTeacherCourseReviewList } from "@/api/review";
 import Card from "@/components/Card";
 import Card from "@/components/Card";
 import PageHeader from "@/components/PageHeader";
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
 import PageBody from "@/components/PageBody/index";
 import ReviewStateTag from "@/components/ReviewStateTag/index";
 import ReviewStateTag from "@/components/ReviewStateTag/index";
+import { getDocAssignmentListByCrs } from "@/api/assignment";
+import timeMixins from "@/mixins/time";
+import { required } from "vuelidate/lib/validators";
 export default {
 export default {
   components: {
   components: {
     ReviewStateTag,
     ReviewStateTag,
@@ -42,21 +139,185 @@ export default {
       course: state => state.course.currentCourse
       course: state => state.course.currentCourse
     })
     })
   },
   },
+  mixins: [timeMixins],
   data() {
   data() {
     return {
     return {
       courses: [],
       courses: [],
-      review: []
+      review: [],
+      showModal: false,
+      submitting: false,
+      docHomework: [],
+      docHomeworkLoading: false,
+      errorMassage: false,
+      isServerError: false,
+      documentHwId: null,
+      taskNumber: null,
+      startAt: null,
+      reviewDdl: null,
+      argueDdl: null,
+      endDdl: null,
+      startAtConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      reviewDdlConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      argueDdlConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      },
+      endDdlConfig: {
+        ...this.getDefaultConfig(),
+        minDate: "today",
+        maxDate: null
+      }
     };
     };
   },
   },
   mounted() {
   mounted() {
-    getTeacherCourseReviewList(this.courses.id).then(
-      value => (this.review = value.data)
-    );
+    const { documentHwId } = this.$route.query;
+    if (documentHwId) {
+      this.documentHwId = documentHwId;
+      this.showModal = true;
+    }
+    this.getReviewList();
+  },
+  validations: {
+    documentHwId: {
+      required
+    },
+    taskNumber: {
+      required
+    },
+    startAt: {
+      required
+    },
+    reviewDdl: {
+      required
+    },
+    argueDdl: {
+      required
+    },
+    endDdl: {
+      required
+    }
+  },
+  watch: {
+    showModal(newValue) {
+      if (newValue) {
+        this.getDocHomework();
+      } else {
+        this.isServerError = false;
+      }
+    },
+    startAt(newValue) {
+      if (newValue !== "") {
+        this.adjustMinDate("reviewDdlConfig", newValue);
+        this.adjustMinDate("argueDdlConfig", newValue);
+        this.adjustMinDate("endDdlConfig", newValue);
+      }
+    },
+    reviewDdl(newValue) {
+      if (newValue !== "") {
+        this.adjustMaxDate("startAtConfig", newValue);
+        this.adjustMinDate("argueDdlConfig", newValue);
+        this.adjustMinDate("endDdlConfig", newValue);
+      }
+    },
+    argueDdl(newValue) {
+      if (newValue !== "") {
+        this.adjustMaxDate("startAtConfig", newValue);
+        this.adjustMaxDate("reviewDdlConfig", newValue);
+        this.adjustMinDate("endDdlConfig", newValue);
+      }
+    },
+    endDdl(newValue) {
+      if (newValue !== "") {
+        this.adjustMaxDate("startAtConfig", newValue);
+        this.adjustMaxDate("reviewDdlConfig", newValue);
+        this.adjustMaxDate("argueDdlConfig", newValue);
+      }
+    }
+  },
+  methods: {
+    async getReviewList() {
+      const { data } = await getTeacherCourseReviewList(this.courses.id);
+      this.review = data;
+    },
+    async getDocHomework() {
+      this.docHomeworkLoading = true;
+      const { courseId } = this.$route.params;
+      const { data } = await getDocAssignmentListByCrs(courseId);
+      this.docHomework = data;
+      this.docHomeworkLoading = false;
+    },
+    adjustMinDate(config, newValue) {
+      const configMinDate = Number(this[config].minDate);
+      if (!configMinDate || configMinDate < Number(newValue)) {
+        this[config].minDate = newValue;
+      }
+    },
+    adjustMaxDate(config, newValue) {
+      const configMinDate = Number(this[config].maxDate);
+      if (!configMinDate || configMinDate > Number(newValue)) {
+        this[config].maxDate = newValue;
+      }
+    },
+    async submit() {
+      this.isServerError = false;
+      this.$v.$touch();
+      if (!this.$v.$invalid) {
+        this.submitting = true;
+        const { courseId } = this.$route.params;
+        const {
+          documentHwId = 0,
+          startAt = 0,
+          argueDdl = 0,
+          reviewDdl = 0,
+          taskNumber = 0,
+          endDdl = 0
+        } = this;
+
+        try {
+          await createReview({
+            courseId,
+            documentHwId,
+            startAt: Number(startAt),
+            argueDdl: Number(argueDdl),
+            reviewDdl: Number(reviewDdl),
+            taskNumber,
+            endDdl: Number(endDdl)
+          });
+          this.$toast.open({
+            message: `互评作业创建成功`,
+            type: "is-success"
+          });
+          this.showModal = false;
+          this.getReviewList();
+        } catch (e) {
+          this.errorMassage = e.message;
+          this.isServerError = true;
+        } finally {
+          this.submitting = false;
+        }
+      }
+    }
   }
   }
 };
 };
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+@import "../../../assets/scss/app";
+.card {
+  padding: $default-margin-between;
+  border-radius: $default-radius;
+  margin: 0 auto;
+  max-width: 600px;
+}
 .top-nav-section {
 .top-nav-section {
   padding: 0 40px;
   padding: 0 40px;
 }
 }

+ 12 - 0
yarn.lock

@@ -3864,6 +3864,11 @@ flat-cache@^1.2.1:
     rimraf "~2.6.2"
     rimraf "~2.6.2"
     write "^0.2.1"
     write "^0.2.1"
 
 
+flatpickr@^4.5.1:
+  version "4.5.2"
+  resolved "http://registry.npm.taobao.org/flatpickr/download/flatpickr-4.5.2.tgz#47c8ad472a096e5fb7e74b809b0703535383f20d"
+  integrity sha1-R8itRyoJbl+350uAmwcDU1OD8g0=
+
 flush-write-stream@^1.0.0:
 flush-write-stream@^1.0.0:
   version "1.0.3"
   version "1.0.3"
   resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd"
   resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd"
@@ -9293,6 +9298,13 @@ vue-eslint-parser@^4.0.2:
     esquery "^1.0.1"
     esquery "^1.0.1"
     lodash "^4.17.11"
     lodash "^4.17.11"
 
 
+vue-flatpickr-component@^8.1.1:
+  version "8.1.1"
+  resolved "http://registry.npm.taobao.org/vue-flatpickr-component/download/vue-flatpickr-component-8.1.1.tgz#f0876e62cc8a6fdbe0e0cc5068f3a314b972a96f"
+  integrity sha1-8IduYsyKb9vg4MxQaPOjFLlyqW8=
+  dependencies:
+    flatpickr "^4.5.1"
+
 vue-hot-reload-api@^2.3.0:
 vue-hot-reload-api@^2.3.0:
   version "2.3.1"
   version "2.3.1"
   resolved "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2"
   resolved "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2"