Browse Source

merge develop again

Johnson 7 năm trước cách đây
mục cha
commit
957f1f06d9

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 12 - 3
src/views/student/Group/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <page-header :routes="[{ name: '我的小组' }]">
+    <page-header :loading="isLoadingGroup" :routes="[{ name: '我的小组' }]">
       <template slot="title">
         我的小组
         <span v-if="groups.length > 0">
@@ -9,7 +9,9 @@
       </template>
     </page-header>
     <page-body>
-      <div v-if="groups.length > 0">
+      <page-section-loading :show="isLoadingGroup" />
+
+      <div v-if="!isLoadingGroup && groups.length > 0">
         <div class="page-section">
           <div class="columns is-desktop">
             <div class="column">
@@ -142,8 +144,9 @@ import {
   postStudentGroup,
   putStudentGroup
 } from "@/api/group";
+import PageSectionLoading from "@/components/PageBody/PageSectionLoading/index";
 export default {
-  components: { PageBody, PageHeader, Card },
+  components: { PageSectionLoading, PageBody, PageHeader, Card },
   data() {
     return {
       showCreateModal: false,
@@ -183,6 +186,9 @@ export default {
   },
   methods: {
     async createGroup() {
+      if (this.submitLoading) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.name.$invalid) {
         try {
@@ -207,6 +213,9 @@ export default {
       }
     },
     async joinGroup() {
+      if (this.submitLoading) return;
+
+      this.isServerError = false;
       this.$v.$touch();
       if (!this.$v.code.$invalid) {
         try {

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

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

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

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

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

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

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

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

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


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

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

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

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

+ 0 - 1
vue.config.js

@@ -3,7 +3,6 @@ module.exports = {
     proxy: {
       "^/api": {
         target: "http://localhost:4000/",
-        // target: "http://10.1.2.3:8080/",
         ws: true,
         changeOrigin: true
       }