Explorar el Código

代码判题,学生考试结果

ChengYuXuan hace 5 años
padre
commit
4a5355b700

+ 7 - 2
src/api/judge.ts

@@ -1,6 +1,11 @@
 import axios from 'axios'
 import { JUDGE_MODULE } from '@/api/prefix'
-import { CodeSubmitSerializer, ID, ReceivedData } from '@/api/types'
+import {
+  CodeJudgeResultSerializer,
+  CodeSubmitSerializer,
+  ID,
+  ReceivedData
+} from '@/api/types'
 
 export const objectiveJudge = (examId: ID, records: object) => {
   return axios.post<ReceivedData>(
@@ -14,7 +19,7 @@ export const codeJudge = (
   questionId: ID,
   codeSubmit: CodeSubmitSerializer
 ) => {
-  return axios.post<ReceivedData>(
+  return axios.post<ReceivedData<CodeJudgeResultSerializer>>(
     `${JUDGE_MODULE}/code?examId=${examId}&questionId=${questionId}`,
     codeSubmit
   )

+ 1 - 1
src/api/record.ts

@@ -3,7 +3,7 @@ import { RECORD_MODULE } from '@/api/prefix'
 import { ExamQuestionSubmitRecord, ID, ReceivedData } from '@/api/types'
 
 const getExamQuestionSubmitRecord = (examId: ID, questionId: ID) => {
-  return axios.get<ReceivedData<ExamQuestionSubmitRecord>>(
+  return axios.get<ReceivedData<Array<ExamQuestionSubmitRecord>>>(
     `${RECORD_MODULE}?examId=${examId}&questionId=${questionId}`
   )
 }

+ 19 - 1
src/api/types.d.ts

@@ -4,6 +4,12 @@ export interface AxiosResponseInfo {}
 // 0 1 2 3
 export type ExamStateText = '所有评测' | '未开始' | '进行中' | '已结束'
 export type ExamState = 0 | 1 | 2 | 3
+export type CodeJudgeState =
+  | 'Accepted'
+  | 'Wrong Answer'
+  | 'Compile Error'
+  | 'Time Limit Exceeded'
+  | 'Memory Limit Exceeded'
 
 export interface ReceivedData<R = {}, E = 0> extends AxiosResponseInfo {
   code: E | 0 | 401
@@ -108,7 +114,7 @@ export interface CodeQuestionSerializer extends QuestionSerializer {
   memoryLimit: number
   millisecondLimit: number
 }
-// TODO 代码题的格式
+
 export interface TeacherQuestionSerializer extends QuestionSerializer {
   answer?: string
   analysis?: string
@@ -172,4 +178,16 @@ export interface ExamQuestionSubmitRecord {
   actualScore: number
   submitAnswer: string
   submitTime: LocalDateTime
+  message: string // 代码题的结果
+}
+
+export interface CodeJudgeResultSerializer {
+  message: string
+  result_list: Array<CodeJudgeResult>
+}
+
+export interface CodeJudgeResult {
+  result: CodeJudgeState
+  timeused: number
+  memoryused: number
 }

+ 1 - 1
src/views/exam/BeforeExam.vue

@@ -62,7 +62,7 @@ export default Vue.extend({
           href: '/student/home'
         },
         {
-          text: '评测列表',
+          text: '我的评测',
           disabled: false,
           href: '/student/exams'
         },

+ 3 - 1
src/views/exam/ExamPane.vue

@@ -140,7 +140,7 @@
       <v-container fluid class="pa-2">
         <v-window
           :value="currentQuestionIndex - codeQuestionStartIndex"
-          v-if="isCodeQuestion"
+          v-if="isCodeQuestion && codeQuestionsReady"
         >
           <v-window-item v-for="i in codeQuestionNum" :key="i">
             <code-pane
@@ -271,6 +271,7 @@ export default Vue.extend({
       codeQuestionStartIndex: 0,
       objectiveQuestionNum: 0,
       codeQuestionNum: 0,
+      codeQuestionsReady: false,
       endTimeChanged: false
     }
   },
@@ -419,6 +420,7 @@ export default Vue.extend({
               this.codeQuestions = reps.map((value) => {
                 return value.data.result as CodeQuestionSerializer
               })
+              this.codeQuestionsReady = true
             })
           })
 

+ 152 - 131
src/views/exam/PersonalResult.vue

@@ -23,8 +23,11 @@
           <v-card style="height: 100%">
             <e-charts-component
               :options="rankOption"
-              v-if="rankOption"
+              v-if="examEnded && rankOption"
             ></e-charts-component>
+            <v-card-title v-else>
+              {{ loadingText }}
+            </v-card-title>
           </v-card>
         </v-col>
         <v-col cols="6">
@@ -62,7 +65,7 @@ import Vue from 'vue'
 import PageHeader from '@/views/components/PageHeader.vue'
 import ExamQuestionsInfo from '@/views/components/ExamQuestionsInfo.vue'
 import { TeacherQuestionSerializer, ReceivedData } from '@/api/types'
-import { getExamQuestions } from '@/api/exam'
+import { getExamById, getExamQuestions } from '@/api/exam'
 import { AxiosResponse } from 'axios'
 import { getQuestionInfo } from '@/api/question'
 import EChartsComponent from '@/views/components/echarts/EChartsComponent.vue'
@@ -72,6 +75,8 @@ import {
   GaugeChartOption
 } from '@/views/components/echarts/echarts.type'
 import { getPersonalReport } from '@/api/report'
+import dayjs from 'dayjs'
+import { TIME_FORMAT } from '@/utils/common'
 
 export default Vue.extend({
   name: 'PersonalResult',
@@ -90,7 +95,7 @@ export default Vue.extend({
           href: '/student'
         },
         {
-          text: '评测列表',
+          text: '我的评测',
           disabled: false,
           href: '/student/exams'
         },
@@ -103,21 +108,32 @@ export default Vue.extend({
       examQuestions: [] as Array<TeacherQuestionSerializer>,
       skillOption: (null as unknown) as BarChartOption,
       gradeOption: (null as unknown) as PieChartOption,
-      rankOption: (null as unknown) as GaugeChartOption
+      rankOption: (null as unknown) as GaugeChartOption,
+      totalScore: 100,
+      examEnded: false,
+      loadingText: ''
     }
   },
   mounted() {
     const examId = this.$route.params.examId
-    let rank = 0
-    let score = 0
-    getPersonalReport(examId).then(({ data }) => {
-      rank = data.result.rank
-      score = data.result.score
+
+    getExamById(examId).then(({ data }) => {
+      const curTime = dayjs()
+      const examEndTime = dayjs(data.result.endTime, TIME_FORMAT)
+      if (curTime.isAfter(examEndTime.add(1, 'minute'))) {
+        this.examEnded = true
+      } else {
+        this.loadingText = '考试进行中,请在考试结束后查看排名信息'
+      }
     })
 
     getExamQuestions(examId).then(({ data }) => {
       this.fetching = true
-
+      this.totalScore = Object.values(data.result.questionAndScore).reduce(
+        (prev, cur) => {
+          return prev + cur
+        }
+      )
       let questionsPromises: Promise<
         AxiosResponse<ReceivedData<TeacherQuestionSerializer, 0>>
       >[] = []
@@ -134,135 +150,140 @@ export default Vue.extend({
       })
     })
 
-    this.skillOption = {
-      title: {
-        text: '知识点掌握情况'
-      },
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      legend: {
-        data: ['个人正确率', '平均正确率']
-      },
-      grid: {
-        left: '3%',
-        right: '4%',
-        bottom: '3%',
-        containLabel: true
-      },
-      xAxis: [
-        {
-          type: 'category',
-          data: ['数据结构', '网络', '操作系统', '测试', '设计模式']
-        }
-      ],
-      yAxis: [
-        {
-          type: 'value'
-        }
-      ],
-      series: [
-        {
-          name: '个人正确率',
-          type: 'bar',
-          data: [50, 33, 31, 34, 39]
-        },
-        {
-          name: '平均正确率',
-          type: 'bar',
-          data: [12, 13, 10, 14, 90]
-        }
-      ]
-    }
+    getPersonalReport(examId).then(({ data }) => {
+      let rank = data.result.rank
+      let score = data.result.score
 
-    this.gradeOption = {
-      tooltip: {
-        trigger: 'item',
-        formatter: '{a}{b}: {c} ({d}%)'
-      },
-      title: {
-        text: '得分   ',
-        left: 'right'
-      },
-      legend: {
-        orient: 'vertical',
-        left: 10,
-        data: ['错误', '正确']
-      },
-      series: [
-        {
-          name: '',
-          type: 'pie',
-          radius: ['50%', '70%'],
-          avoidLabelOverlap: false,
-          label: {
-            show: false
-          },
-          emphasis: {
-            label: {
-              show: true,
-              fontSize: '15',
-              fontWeight: 'bold'
-            }
-          },
-          labelLine: {
-            show: false
+      this.skillOption = {
+        title: {
+          text: '知识点掌握情况'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow'
+          }
+        },
+        legend: {
+          data: ['个人正确率', '平均正确率']
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: ['数据结构', '网络', '操作系统', '测试', '设计模式']
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value'
+          }
+        ],
+        series: [
+          {
+            name: '个人正确率',
+            type: 'bar',
+            data: [50, 33, 31, 34, 39]
           },
-          data: [
-            { value: 100 - score, name: '错误' },
-            { value: score, name: '正确' }
-          ]
-        }
-      ]
-    }
+          {
+            name: '平均正确率',
+            type: 'bar',
+            data: [12, 13, 10, 14, 90]
+          }
+        ]
+      }
 
-    this.rankOption = {
-      series: [
-        {
-          type: 'gauge',
-          startAngle: 180,
-          endAngle: 0,
-          min: 100,
-          max: 0,
-          splitNumber: 5,
-          axisLabel: {
-            distance: 15,
-            fontSize: 10
-          },
+      this.gradeOption = {
+        tooltip: {
+          trigger: 'item',
+          formatter: '{a}{b}: {c} ({d}%)'
+        },
+        title: {
+          text: '得分   ',
+          left: 'right'
+        },
+        legend: {
+          orient: 'vertical',
+          left: 10,
+          data: ['错误', '正确']
+        },
+        series: [
+          {
+            name: '',
+            type: 'pie',
+            radius: ['50%', '70%'],
+            avoidLabelOverlap: false,
+            label: {
+              show: false
+            },
+            emphasis: {
+              label: {
+                show: true,
+                fontSize: '15',
+                fontWeight: 'bold'
+              }
+            },
+            labelLine: {
+              show: false
+            },
+            data: [
+              { value: this.totalScore - score, name: '错误' },
+              { value: score, name: '正确' }
+            ]
+          }
+        ]
+      }
 
-          progress: {
-            show: true,
-            roundCap: true,
-            width: 10
-          },
+      this.rankOption = {
+        series: [
+          {
+            type: 'gauge',
+            startAngle: 180,
+            endAngle: 0,
+            min: 100,
+            max: 0,
+            splitNumber: 5,
+            axisLabel: {
+              distance: 15,
+              fontSize: 10
+            },
 
-          detail: {
-            formatter: function(value) {
-              return '排名前 {value|' + value.toFixed(0) + '}{unit|%}'
+            progress: {
+              show: true,
+              roundCap: true,
+              width: 10
             },
-            rich: {
-              value: {
-                fontSize: 20,
-                fontWeight: 'bolder',
-                color: '#777'
+
+            detail: {
+              formatter: function(value) {
+                return '排名前 {value|' + value.toFixed(0) + '}{unit|%}'
               },
-              unit: {
-                fontSize: 20,
-                color: '#999'
+              rich: {
+                value: {
+                  fontSize: 20,
+                  fontWeight: 'bolder',
+                  color: '#777'
+                },
+                unit: {
+                  fontSize: 20,
+                  color: '#999'
+                }
               }
-            }
-          },
-          data: [
-            {
-              value: rank
-            }
-          ]
-        }
-      ]
-    }
+            },
+            data: [
+              {
+                value: rank
+              }
+            ]
+          }
+        ]
+      }
+    })
   }
 })
 </script>

+ 55 - 96
src/views/exam/components/CodePane.vue

@@ -19,67 +19,36 @@
                 ></markdown-text>
               </div>
             </v-tab-item>
-            <!--            <v-tab-item>-->
-            <!--              <div class="d-flex flex-column justify-space-between pa-2">-->
-            <!--                <v-card-->
-            <!--                  flat-->
-            <!--                  class="d-flex flex-column justify-space-between mb-5"-->
-            <!--                >-->
-            <!--                  <div class="d-inline mb-2">-->
-            <!--                    执行结果:-->
-            <!--                    <v-chip-->
-            <!--                      :color="statusToColor(currentCommit.status)"-->
-            <!--                      outlined-->
-            <!--                      >{{ currentCommit.status }}</v-chip-->
-            <!--                    >-->
-            <!--                  </div>-->
-            <!--                  <v-card-text-->
-            <!--                    style="background-color: darkgray; height: 30vh"-->
-            <!--                    class="rounded ma-0 mb-5 overflow-y-auto pa-2"-->
-            <!--                    v-if="-->
-            <!--                      currentCommit && currentCommit.status === '编译或运行失败'-->
-            <!--                    "-->
-            <!--                  >-->
-            <!--                    TODO-->
-            <!--                  </v-card-text>-->
-            <!--                </v-card>-->
-            <!--                <v-simple-table-->
-            <!--                  fixed-header-->
-            <!--                  :height="-->
-            <!--                    currentCommit && currentCommit.status === '编译或运行失败'-->
-            <!--                      ? 35 + 'vh'-->
-            <!--                      : 70 + 'vh'-->
-            <!--                  "-->
-            <!--                >-->
-            <!--                  <thead>-->
-            <!--                    <tr>-->
-            <!--                      <th>提交时间</th>-->
-            <!--                      <th>状态</th>-->
-            <!--                      <th>成绩</th>-->
-            <!--                      <th>操作</th>-->
-            <!--                    </tr>-->
-            <!--                  </thead>-->
-            <!--                  <tbody>-->
-            <!--                    <tr v-for="item in commits" :key="item.id">-->
-            <!--                      <td>{{ item.submitTime }}</td>-->
-            <!--                      <td>-->
-            <!--                        <v-chip-->
-            <!--                          :color="statusToColor(item.status)"-->
-            <!--                          small-->
-            <!--                          outlined-->
-            <!--                        >-->
-            <!--                          {{ item.status }}-->
-            <!--                        </v-chip>-->
-            <!--                      </td>-->
-            <!--                      <td>{{ item.score }}</td>-->
-            <!--                      <td>-->
-            <!--                        <v-btn small>详情</v-btn>-->
-            <!--                      </td>-->
-            <!--                    </tr>-->
-            <!--                  </tbody>-->
-            <!--                </v-simple-table>-->
-            <!--              </div>-->
-            <!--            </v-tab-item>-->
+            <v-tab-item>
+              <div class="d-flex flex-column justify-space-between pa-2">
+                <v-simple-table
+                  fixed-header
+                  class="overflow-y-auto"
+                  style="height: 84vh"
+                >
+                  <thead>
+                    <tr>
+                      <th>测试用例</th>
+                      <th>状态</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr v-for="item in codeJudgeResults" :key="item.id">
+                      <td>测试 {{ item.id }}</td>
+                      <td>
+                        <v-chip
+                          :color="statusToColor(item.result)"
+                          small
+                          outlined
+                        >
+                          {{ item.result }}
+                        </v-chip>
+                      </td>
+                    </tr>
+                  </tbody>
+                </v-simple-table>
+              </div>
+            </v-tab-item>
           </v-tabs-items>
         </div>
         <div style="height: 32px" class="d-flex justify-space-between px-2">
@@ -163,21 +132,17 @@
 import Vue, { PropType } from 'vue'
 import CodeEditor from '@/views/exam/components/CodeEditor.vue'
 import MarkdownText from '@/views/exam/components/MarkdownText.vue'
-import { clone } from 'ramda'
 import {
+  CodeJudgeResult,
   CodeQuestionSerializer,
   CodeSubmitSerializer,
-  CodeTuple
+  CodeTuple,
+  ID
 } from '@/api/types'
 import { codeJudge } from '@/api/judge'
 
-interface CommitInfo {
-  id?: string
-  submitTime?: string
-  status?: string
-  score?: number
-  codes?: CodeTuple[]
-  msg?: string
+interface CodeJudgeInfo extends CodeJudgeResult {
+  id: ID
 }
 
 export default Vue.extend({
@@ -199,20 +164,21 @@ export default Vue.extend({
   data() {
     return {
       tab: 0,
-      commits: [] as CommitInfo[],
+      codeJudgeResults: [] as CodeJudgeInfo[],
       languages: ['cpp', 'java', 'python'],
+      inOutMapping: [] as Object[],
       currentLang: '',
       currentCode: '',
       codeCache: [] as CodeTuple[], // 切换语言时,将当前编辑的保存到cache中,下次切换回来可继续编辑
-      // currentCommit: (null as unknown) as CommitInfo,
       statusTags: [
-        '所有用例通过',
-        '有用例未通过',
-        '编译或运行失败',
-        '运行超时',
-        '等待运行'
+        'Accepted',
+        'Wrong Answer',
+        'Compile Error',
+        'Time Limit Exceeded',
+        'Memory Limit Exceeded'
       ],
-      statusColors: ['success', 'warning', 'error', 'accent', 'info']
+      statusColors: ['success', 'error', 'warning', 'warning', 'warning']
+      // lastCommitCode: [] as CodeSubmitSerializer[]
     }
   },
   watch: {
@@ -241,16 +207,10 @@ export default Vue.extend({
     this.codeCache = this.languages.map((val) => {
       return { lang: val, code: '' }
     })
-    // TODO this is test
-    for (let i = 0; i < 10; i++) {
-      const r = Math.floor(Math.random() * 4 + 1)
-      this.commits.push({
-        id: i.toString(),
-        submitTime: '2020-12-11 20:02',
-        status: this.statusTags[r],
-        score: 6 * i
-      })
-    }
+
+    this.codeJudgeResults = [
+      { id: 1, result: 'Accepted', timeused: 123, memoryused: 12345 }
+    ]
   },
   methods: {
     submitCode(): void {
@@ -261,22 +221,21 @@ export default Vue.extend({
         language: this.currentLang
       }
       codeJudge(examId, this.question.id, codeToSubmit).then(({ data }) => {
-        console.log(data.result)
+        this.codeJudgeResults = data.result.result_list.map((val, index) => {
+          return { ...val, id: index + 1 }
+        })
       })
     },
-    // getCurrentCommit(): CommitInfo {
-    //   return this.commits[0]
-    // },
     statusToColor(status: string): string {
       return this.statusColors[this.statusTags.indexOf(status)]
     },
     resetLastCommit() {
-      // const lastCommitCodes = this.question.lastCommitCodes
-      // const lastCommitTuple = lastCommitCodes?.find((val) => {
-      //   return val.lang === this.currentLang
+      // const lastSubmitCodes = this.lastCommitCode
+      // const curLangLastSubmit = lastSubmitCodes.find((val) => {
+      //   return val.language === this.currentLang
       // })
       //
-      // this.currentCode = lastCommitTuple?.code ?? ''
+      // this.currentCode = curLangLastSubmit?.code ?? ''
     },
     resetOriginCode() {
       // const originCodes = this.question.codeTuples ?? []