Browse Source

成绩管理

zhangjie 1 year ago
parent
commit
120b73d060

+ 16 - 0
src/modules/mark/api.js

@@ -121,6 +121,9 @@ export const markQualityUpdate = (datas) => {
 export const markQualityChart = (datas) => {
 export const markQualityChart = (datas) => {
   return $postParam("/api/admin/mark/quality/chart", datas);
   return $postParam("/api/admin/mark/quality/chart", datas);
 };
 };
+export const markGroupQuestions = (datas) => {
+  return $postParam("/api/admin/mark/group/list/groupQuestions", datas);
+};
 // mark-detail-issue
 // mark-detail-issue
 export const markIssueListPage = (datas) => {
 export const markIssueListPage = (datas) => {
   return $postParam("/api/admin/mark/problem/list", datas);
   return $postParam("/api/admin/mark/problem/list", datas);
@@ -143,3 +146,16 @@ export const scoreDetailListPage = (datas) => {
 export const packageListPage = (datas) => {
 export const packageListPage = (datas) => {
   return $postParam("/api/admin/mark/paper/package/list", datas);
   return $postParam("/api/admin/mark/paper/package/list", datas);
 };
 };
+
+// score manage --------->
+export const scoreListPage = (datas) => {
+  return $postParam("/api/admin/archive/score/list", datas);
+};
+export const scoreClassDetailListPage = (datas) => {
+  return $postParam("/api/admin/archive/student/list", datas);
+};
+export const scoreClassDetailScoreExport = (datas) => {
+  return $postParam("/api/admin/archive/score/export", datas, {
+    responseType: "blob",
+  });
+};

+ 1 - 1
src/modules/mark/components/ScoreDetail.vue → src/modules/mark/components/ScoreCheckDetail.vue

@@ -267,7 +267,7 @@ import { scoreDetailListPage } from "../api";
 import SimpleImagePreview from "@/components/SimpleImagePreview";
 import SimpleImagePreview from "@/components/SimpleImagePreview";
 
 
 export default {
 export default {
-  name: "score-detail",
+  name: "score-check-detail",
   components: { SimpleImagePreview },
   components: { SimpleImagePreview },
   props: {
   props: {
     instance: {
     instance: {

+ 280 - 0
src/modules/mark/components/ScoreClassDetail.vue

@@ -0,0 +1,280 @@
+<template>
+  <el-dialog
+    :visible.sync="modalIsShow"
+    top="0"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    fullscreen
+    @open="initData"
+  >
+    <div slot="title">
+      <h2 class="el-dialog__title">成绩详情</h2>
+      <button class="el-dialog__headerbtn" @click="cancel"></button>
+    </div>
+
+    <div class="part-box part-box-filter part-box-flex">
+      <el-form ref="FilterForm" label-position="left" label-width="85px" inline>
+        <el-form-item label="学院">
+          <college-select
+            v-model="filter.college"
+            placeholder="学院"
+          ></college-select>
+        </el-form-item>
+        <el-form-item label="专业">
+          <el-select v-model="filter.major" placeholder="专业" clearable>
+            <!-- TODO: -->
+            <el-option :value="1">班级1</el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="班级">
+          <major-class-select
+            v-model="filter.className"
+            :major-id="filter.majorId"
+            cascader
+            placeholder="班级"
+          ></major-class-select>
+        </el-form-item>
+        <el-form-item label="姓名">
+          <el-input
+            v-model.trim="filter.studentName"
+            placeholder="姓名"
+            clearable
+          >
+          </el-input>
+        </el-form-item>
+        <el-form-item label="学号">
+          <el-input
+            v-model.trim="filter.studentCode"
+            placeholder="学号"
+            clearable
+          >
+          </el-input>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" @click="search">查询</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="part-box-action">
+        <el-button
+          type="primary"
+          :disabled="downloading"
+          @click="toExportScore"
+        >
+          成绩导出
+        </el-button>
+      </div>
+    </div>
+
+    <div class="part-box part-box-pad">
+      <el-table ref="TableList" :data="dataList">
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column prop="studentName" label="姓名" min-width="100">
+        </el-table-column>
+        <el-table-column
+          prop="studentCode"
+          label="学号"
+          width="180"
+        ></el-table-column>
+        <el-table-column
+          prop="secretNumber"
+          label="院系"
+          width="180"
+        ></el-table-column>
+        <el-table-column
+          prop="className"
+          label="班级"
+          min-width="100"
+        ></el-table-column>
+        <el-table-column prop="checkTime" label="考试时间" width="170">
+          <span slot-scope="scope">{{
+            scope.row.checkTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column
+          prop="className"
+          label="课程名称"
+          min-width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="className"
+          label="成绩"
+          min-width="100"
+        ></el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          width="300"
+          fixed="right"
+        >
+          <template slot-scope="scope">
+            <el-button
+              class="btn-primary"
+              type="text"
+              @click="toViewSheetPaper(scope.row)"
+              >原图</el-button
+            >
+            <el-button
+              class="btn-primary"
+              type="text"
+              @click="toViewPaper(scope.row)"
+              >轨迹图</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="part-page">
+        <el-pagination
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          :pager-count="5"
+          :current-page="current"
+          :total="total"
+          :page-size="size"
+          @current-change="toPage"
+          @size-change="pageSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+
+    <!-- image-preview -->
+    <simple-image-preview
+      :cur-image="curImage"
+      @on-prev="toPrevImage"
+      @on-next="toNextImage"
+      ref="SimpleImagePreview"
+    ></simple-image-preview>
+
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import { scoreDetailListPage, scoreClassDetailScoreExport } from "../api";
+import SimpleImagePreview from "@/components/SimpleImagePreview";
+import { downloadByApi } from "@/plugins/download";
+
+export default {
+  name: "score-class-detail",
+  components: { SimpleImagePreview },
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      filter: {
+        college: "",
+        className: "",
+        major: "",
+        studentName: "",
+        studentCode: "",
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      downloading: false,
+      // img view
+      curImage: {},
+      curImageIndex: 0,
+      imageList: [],
+    };
+  },
+  methods: {
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async getList() {
+      const datas = {
+        ...this.filter,
+        examId: this.instance.examId,
+        paperNumber: this.instance.pageNumber,
+        pageNumber: this.current,
+        pageSize: this.size,
+      };
+      if (datas.absent !== null && datas.absent !== "")
+        datas.absent = !!datas.absent;
+      if (datas.breach !== null && datas.breach !== "")
+        datas.breach = !!datas.breach;
+
+      const data = await scoreDetailListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+    },
+    async toExportScore() {
+      if (this.downloading) return;
+      this.downloading = true;
+
+      const res = await downloadByApi(() => {
+        return scoreClassDetailScoreExport({
+          ...this.filter,
+          examId: this.instance.examId,
+          paperNumber: this.instance.pageNumber,
+        });
+      }).catch((e) => {
+        this.$message.error(e || "下载失败,请重新尝试!");
+      });
+      this.downloading = false;
+
+      if (!res) return;
+      this.$message.success("下载成功!");
+    },
+    // img view
+    async toViewPaper() {
+      // TODO:
+    },
+    toViewSheetPaper(row) {
+      this.curImageIndex = 0;
+      this.imageList = (row.sheetUrls || []).map((url, index) => {
+        return { url, index: index + 1 };
+      });
+      this.selectImage(this.curImageIndex);
+      this.$refs.SimpleImagePreview.open();
+    },
+    selectImage(index) {
+      this.curImage = this.imageList[index];
+    },
+    toPrevImage() {
+      if (this.curImageIndex === 0) {
+        this.curImageIndex = this.imageList.length - 1;
+      } else {
+        this.curImageIndex--;
+      }
+
+      this.selectImage(this.curImageIndex);
+    },
+    toNextImage() {
+      if (this.curImageIndex === this.imageList.length - 1) {
+        this.curImageIndex = 0;
+      } else {
+        this.curImageIndex++;
+      }
+
+      this.selectImage(this.curImageIndex);
+    },
+  },
+};
+</script>

+ 52 - 0
src/modules/mark/components/ScoreReportPreview.vue

@@ -0,0 +1,52 @@
+<template>
+  <el-dialog
+    :visible.sync="modalIsShow"
+    top="0"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    fullscreen
+    @open="initData"
+  >
+    <div slot="title">
+      <h2 class="el-dialog__title">成绩报告</h2>
+      <div class="el-dialog__headerbtn">
+        <button type="primary" @click="toExportScore">下载报告</button>
+        <button type="primary" @click="cancel">取消</button>
+      </div>
+    </div>
+
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: "score-report-preview",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+    };
+  },
+  methods: {
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    toExportScore() {
+      // TODO:
+    },
+  },
+};
+</script>

+ 23 - 2
src/modules/mark/components/markDetail/MarkDetailQuality.vue

@@ -8,7 +8,12 @@
             placeholder="评阅题目"
             placeholder="评阅题目"
             clearable
             clearable
           >
           >
-            <el-option :value="1">班级1</el-option>
+            <el-option
+              v-for="group in questions"
+              :key="group.groupNumber"
+              :value="group.groupNumber"
+              >{{ group.groupQuestions }}</el-option
+            >
           </el-select>
           </el-select>
         </el-form-item>
         </el-form-item>
         <el-form-item label="评卷员">
         <el-form-item label="评卷员">
@@ -109,7 +114,11 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import { markQualityListPage, markQualityUpdate } from "../api";
+import {
+  markQualityListPage,
+  markQualityUpdate,
+  markGroupQuestions,
+} from "../api";
 import QualityChartDialog from "./QualityChartDialog.vue";
 import QualityChartDialog from "./QualityChartDialog.vue";
 
 
 export default {
 export default {
@@ -135,9 +144,21 @@ export default {
       dataList: [],
       dataList: [],
       multipleSelection: [],
       multipleSelection: [],
       chartData: {},
       chartData: {},
+      questions: [],
     };
     };
   },
   },
+  mounted() {
+    this.getQuestions();
+    this.search();
+  },
   methods: {
   methods: {
+    async getQuestions() {
+      const res = await markGroupQuestions({
+        examId: this.baseInfo.examId,
+        paperNumber: this.baseInfo.paperNumber,
+      });
+      this.questions = res || [];
+    },
     async getList() {
     async getList() {
       const datas = {
       const datas = {
         ...this.filter,
         ...this.filter,

+ 8 - 5
src/modules/mark/views/ScoreCheck.vue

@@ -83,18 +83,21 @@
       </div>
       </div>
     </div>
     </div>
 
 
-    <!-- ScoreDetail -->
-    <score-detail ref="ScoreDetail" :instance="curRow"></score-detail>
+    <!-- ScoreCheckDetail -->
+    <score-check-detail
+      ref="ScoreCheckDetail"
+      :instance="curRow"
+    ></score-check-detail>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
 import { scoreCheckListPage } from "../api";
 import { scoreCheckListPage } from "../api";
-import ScoreDetail from "../components/ScoreDetail.vue";
+import ScoreCheckDetail from "../components/ScoreCheckDetail.vue";
 
 
 export default {
 export default {
   name: "score-check",
   name: "score-check",
-  components: { ScoreDetail },
+  components: { ScoreCheckDetail },
   data() {
   data() {
     return {
     return {
       filter: {
       filter: {
@@ -141,7 +144,7 @@ export default {
     },
     },
     toDetail(row) {
     toDetail(row) {
       this.curRow = row;
       this.curRow = row;
-      this.$refs.ScoreDetail.open();
+      this.$refs.ScoreCheckDetail.open();
     },
     },
     toViewSign(row) {
     toViewSign(row) {
       // TODO:
       // TODO:

+ 210 - 0
src/modules/mark/views/ScoreManage.vue

@@ -0,0 +1,210 @@
+<template>
+  <div class="score-archive">
+    <div class="part-box part-box-filter">
+      <el-form ref="FilterForm" label-position="left" inline>
+        <template v-if="checkPrivilege('condition', 'condition')">
+          <el-form-item label="学期:">
+            <semester-select
+              v-model="filter.semesterId"
+              default-select
+              :clearable="false"
+            ></semester-select>
+          </el-form-item>
+          <el-form-item label="考试:">
+            <exam-select
+              v-model="filter.examId"
+              :semester-id="filter.semesterId"
+              default-select
+              :clearable="false"
+              @default-selected="toPage(1)"
+            ></exam-select>
+          </el-form-item>
+          <el-form-item label="课程:">
+            <course-select
+              v-model="filter.courseCode"
+              placeholder="课程"
+              filterable
+              clearable
+              :semester-id="filter.semesterId"
+              :exam-id="filter.examId"
+            ></course-select>
+          </el-form-item>
+        </template>
+        <el-form-item label-width="0px">
+          <el-button
+            v-if="checkPrivilege('button', 'select')"
+            type="primary"
+            @click="toPage(1)"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="part-box part-box-pad">
+      <el-table ref="TableList" :data="dataList">
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column prop="name" label="课程名称(代码)" min-width="200">
+          <span slot-scope="scope"
+            >{{ scope.row.courseName }}({{ scope.row.courseCode }})</span
+          >
+        </el-table-column>
+        <el-table-column
+          prop="paperNumber"
+          label="试卷编号"
+          width="200"
+        ></el-table-column>
+        <el-table-column
+          prop="studentCount"
+          label="总考生"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="classCount"
+          label="参考班级"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="absentCount"
+          label="缺考人数"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="avgScore"
+          label="平均分"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="maxScore"
+          label="最高分"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="minScore"
+          label="最低分"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="passCount"
+          label="及格人数"
+          width="100"
+        ></el-table-column>
+        <el-table-column prop="passRate" label="及格率" width="100">
+          <template slot-scope="scope">{{ scope.row.passRate || 0 }}%</template>
+        </el-table-column>
+        <el-table-column
+          prop="excellentCount"
+          label="优秀人数"
+          width="100"
+        ></el-table-column>
+        <el-table-column prop="excellentRate" label="优秀率" width="100">
+          <template slot-scope="scope"
+            >{{ scope.row.excellentRate || 0 }}%</template
+          >
+        </el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          width="160px"
+          fixed="right"
+        >
+          <template slot-scope="scope">
+            <el-button
+              v-if="checkPrivilege('link', 'Preview')"
+              class="btn-primary"
+              type="text"
+              @click="toClass(scope.row)"
+              >班级详情</el-button
+            >
+            <el-button
+              v-if="checkPrivilege('link', 'Preview')"
+              class="btn-primary"
+              type="text"
+              @click="toReport(scope.row)"
+              >成绩报告</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="part-page">
+        <el-pagination
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          :pager-count="5"
+          :current-page="current"
+          :total="total"
+          :page-size="size"
+          @current-change="toPage"
+          @size-change="pageSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+
+    <!-- ScoreClassDetail -->
+    <score-class-detail
+      ref="ScoreClassDetail"
+      :instance="curRow"
+    ></score-class-detail>
+    <!-- ScoreReportPreview -->
+    <score-report-preview
+      ref="ScoreReportPreview"
+      :instance="curRow"
+    ></score-report-preview>
+  </div>
+</template>
+
+<script>
+import { scoreListPage } from "../api";
+import ScoreClassDetail from "../components/ScoreClassDetail.vue";
+import ScoreReportPreview from "../components/ScoreReportPreview.vue";
+
+export default {
+  name: "score-manage",
+  components: { ScoreClassDetail, ScoreReportPreview },
+  data() {
+    return {
+      filter: {
+        semesterId: "",
+        examId: "",
+        courseCode: "",
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      curRow: {},
+    };
+  },
+  methods: {
+    async getList() {
+      if (!this.checkPrivilege("list", "list")) return;
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size,
+      };
+      const data = await scoreListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    toClass(row) {
+      this.curRow = row;
+      this.$refs.ScoreClassDetail.open();
+    },
+    toReport(row) {
+      this.curRow = row;
+      this.$refs.ScoreReportPreview.open();
+    },
+  },
+};
+</script>