zhangjie hai 1 ano
pai
achega
c7dca9764c

+ 1 - 0
card/assets/styles/card-preview.scss

@@ -58,6 +58,7 @@
       display: block;
       height: 28px;
       width: 100%;
+      border: none;
     }
     p {
       line-height: 1;

+ 1 - 0
card/assets/styles/card-temp.css

@@ -52,6 +52,7 @@
   display: block;
   height: 28px;
   width: 100%;
+  border: none;
 }
 .page-box .package-number p {
   line-height: 1;

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
card/previewTemp.js


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

@@ -159,3 +159,6 @@ export const scoreClassDetailScoreExport = (datas) => {
     responseType: "blob",
   });
 };
+export const scoreReport = (datas) => {
+  return $postParam("/api/admin/mark/archive/score/report", datas);
+};

+ 74 - 0
src/modules/mark/components/report/ReportClass.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="report-class">
+    <report-box title="班级成绩分析">
+      <div
+        v-if="(chart, cindex) in classCharts"
+        :key="cindex"
+        class="page-chart"
+      >
+        <v-chart :option="chart"></v-chart>
+      </div>
+      <table
+        v-for="(tgroup, tindex) in classTables"
+        :key="tindex"
+        class="table table-border"
+      >
+        <tr>
+          <th>班级</th>
+          <th>平均分</th>
+          <th>最高分</th>
+          <th>最低分</th>
+          <th>及格数</th>
+          <th>及格率(%)</th>
+          <th>优秀数</th>
+          <th>优秀率(%)</th>
+        </tr>
+        <tr v-for="(item, ind) in tgroup" :key="ind">
+          <td>{{ item.name }}</td>
+          <td>{{ item.avgScore }}</td>
+          <td>{{ item.maxScore }}</td>
+          <td>{{ item.minScore }}</td>
+          <td>{{ item.passCount }}</td>
+          <td>{{ item.passRate }}</td>
+          <td>{{ item.excellentCount }}</td>
+          <td>{{ item.excellentRate }}</td>
+        </tr>
+      </table>
+    </report-box>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import ReportBox from "./ReportBox.vue";
+import { sectionArr } from "./utils";
+import { getBarsOptions } from "./chart";
+
+export default {
+  name: "report-class",
+  components: { ReportBox },
+  data() {
+    return {
+      classCharts: [],
+      classTables: [],
+    };
+  },
+  computed: {
+    ...mapState("report", ["class"]),
+  },
+  methods: {
+    initData() {
+      const classes = this.class.map((item) => {
+        return {
+          ...item,
+          name: item.class,
+        };
+      });
+      this.classCharts = sectionArr(classes, 10).map((data) =>
+        getBarsOptions(data)
+      );
+      this.classTables = sectionArr(classes, 30);
+    },
+  },
+};
+</script>

+ 74 - 0
src/modules/mark/components/report/ReportCollege.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="report-college">
+    <report-box title="学院成绩分析">
+      <div
+        v-if="(chart, cindex) in collegeCharts"
+        :key="cindex"
+        class="page-chart"
+      >
+        <v-chart :option="chart"></v-chart>
+      </div>
+      <table
+        v-for="(tgroup, tindex) in collegeTables"
+        :key="tindex"
+        class="table table-border"
+      >
+        <tr>
+          <th>学院</th>
+          <th>平均分</th>
+          <th>最高分</th>
+          <th>最低分</th>
+          <th>及格数</th>
+          <th>及格率(%)</th>
+          <th>优秀数</th>
+          <th>优秀率(%)</th>
+        </tr>
+        <tr v-for="(item, ind) in tgroup" :key="ind">
+          <td>{{ item.name }}</td>
+          <td>{{ item.avgScore }}</td>
+          <td>{{ item.maxScore }}</td>
+          <td>{{ item.minScore }}</td>
+          <td>{{ item.passCount }}</td>
+          <td>{{ item.passRate }}</td>
+          <td>{{ item.excellentCount }}</td>
+          <td>{{ item.excellentRate }}</td>
+        </tr>
+      </table>
+    </report-box>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import ReportBox from "./ReportBox.vue";
+import { sectionArr } from "./utils";
+import { getBarsOptions } from "./chart";
+
+export default {
+  name: "report-college",
+  components: { ReportBox },
+  data() {
+    return {
+      collegeCharts: [],
+      collegeTables: [],
+    };
+  },
+  computed: {
+    ...mapState("report", ["college"]),
+  },
+  methods: {
+    initData() {
+      const college = this.college.map((item) => {
+        return {
+          ...item,
+          name: item.college,
+        };
+      });
+      this.collegeCharts = sectionArr(college, 10).map((data) =>
+        getBarsOptions(data)
+      );
+      this.collegeTables = sectionArr(college, 30);
+    },
+  },
+};
+</script>

+ 8 - 11
src/modules/mark/components/report/ReportCover.vue

@@ -3,32 +3,29 @@
     <div class="cover-title"></div>
     <div class="cover-footer">
       <p>
-        <span>考试名称:</span><span>{{ data.examName }}</span>
+        <span>考试名称:</span><span>{{ baseinfo.examName }}</span>
       </p>
       <p>
-        <span>学校:</span><span>{{ data.schoolName }}</span>
+        <span>学校:</span><span>{{ baseinfo.schoolName }}</span>
       </p>
       <p>
-        <span>科目:</span><span>{{ data.courseName }}</span>
+        <span>科目:</span><span>{{ baseinfo.courseName }}</span>
       </p>
     </div>
   </div>
 </template>
 
 <script>
+import { mapState } from "vuex";
+
 export default {
   name: "report-cover",
-  props: {
-    data: {
-      type: Object,
-      default() {
-        return {};
-      },
-    },
-  },
   data() {
     return {};
   },
+  computed: {
+    ...mapState("report", ["baseinfo"]),
+  },
   methods: {},
 };
 </script>

+ 77 - 0
src/modules/mark/components/report/ReportQuestion.vue

@@ -0,0 +1,77 @@
+<template>
+  <div class="report-question">
+    <report-box :title="title">
+      <div
+        v-if="(chart, cindex) in questionCharts"
+        :key="cindex"
+        class="page-chart"
+      >
+        <v-chart :option="chart"></v-chart>
+      </div>
+      <table
+        v-for="(tgroup, tindex) in questionTables"
+        :key="tindex"
+        class="table table-border"
+      >
+        <tr>
+          <th>题目名称</th>
+          <th>大题号</th>
+          <th>小题号</th>
+          <th>单题分数</th>
+          <th>单题平均分</th>
+          <th>得分率(%)</th>
+          <th>满分率(%)</th>
+        </tr>
+        <tr v-for="(item, ind) in tgroup" :key="ind">
+          <td>{{ item.title }}</td>
+          <td>{{ item.mainNumber }}</td>
+          <td>{{ item.subNumber }}</td>
+          <td>{{ item.score }}</td>
+          <td>{{ item.avgScore }}</td>
+          <td>{{ item.socreRate }}</td>
+          <td>{{ item.fullScoreRate }}</td>
+        </tr>
+      </table>
+    </report-box>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import ReportBox from "./ReportBox.vue";
+import { sectionArr } from "./utils";
+import { getBarPointTopicOptions } from "./chart";
+
+export default {
+  name: "report-question",
+  components: { ReportBox },
+  props: {
+    type: String,
+  },
+  data() {
+    return {
+      questionCharts: [],
+      questionTables: [],
+    };
+  },
+  computed: {
+    ...mapState("report", ["objective", "subjective"]),
+    typeName() {
+      return this.type === "subjective" ? "主观题" : "客观题";
+    },
+    title() {
+      return `${this.typeName}成绩分析`;
+    },
+  },
+  methods: {
+    initData() {
+      const question =
+        this.type === "subjective" ? this.subjective : this.objective;
+      this.questionCharts = sectionArr(question, 26).map((data) =>
+        getBarPointTopicOptions(data)
+      );
+      this.questionTables = sectionArr(question, 32);
+    },
+  },
+};
+</script>

+ 84 - 0
src/modules/mark/components/report/ReportSummary.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="report-summary">
+    <report-box title="总览">
+      <h2 class="page-title-2">考试概况</h2>
+      <table class="table table-border">
+        <tr>
+          <th>总考生数</th>
+          <th>实考</th>
+          <th>缺考</th>
+          <th>违纪</th>
+          <th>平均分</th>
+          <th>最高分</th>
+          <th>最低分</th>
+          <th>及格人数</th>
+          <th>及格率(%)</th>
+          <th>优秀人数</th>
+          <th>优秀率(%)</th>
+        </tr>
+        <tr>
+          <td>{{ overview.studentCount }}</td>
+          <td>{{ overview.actualCount }}</td>
+          <td>{{ overview.absentCount }}</td>
+          <td>{{ overview.breachCount }}</td>
+          <td>{{ overview.avgScore }}</td>
+          <td>{{ overview.maxScore }}</td>
+          <td>{{ overview.minScore }}</td>
+          <td>{{ overview.passCount }}</td>
+          <td>{{ overview.passRate }}</td>
+          <td>{{ overview.excellentCount }}</td>
+          <td>{{ overview.excellentRate }}</td>
+        </tr>
+      </table>
+      <h2>成绩分数段分析</h2>
+      <div
+        v-if="(chart, cindex) in scoreRangeCharts"
+        :key="cindex"
+        class="page-chart"
+      >
+        <v-chart :option="chart"></v-chart>
+      </div>
+      <table class="table table-border">
+        <tr>
+          <th>分数段</th>
+          <th>分数段人数</th>
+          <th>占比(%)</th>
+        </tr>
+        <tr v-for="(item, ind) in scoreRangeTables" :key="ind">
+          <td>{{ item.minScore }}~{{ item.maxScore }}</td>
+          <td>{{ item.studentCount }}</td>
+          <td>{{ item.rate }}</td>
+        </tr>
+      </table>
+    </report-box>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import ReportBox from "./ReportBox.vue";
+import { sectionArr } from "./utils";
+import { getScoreBarOptions } from "./chart";
+
+export default {
+  name: "report-summary",
+  components: { ReportBox },
+  data() {
+    return {
+      scoreRangeCharts: [],
+      scoreRangeTables: [],
+    };
+  },
+  computed: {
+    ...mapState("report", ["overview", "scoreRange"]),
+  },
+  methods: {
+    initData() {
+      this.scoreRangeCharts = sectionArr(this.scoreRange, 10).map((data) =>
+        getScoreBarOptions(data)
+      );
+      this.scoreRangeTables = sectionArr(this.scoreRange, 30);
+    },
+  },
+};
+</script>

+ 75 - 0
src/modules/mark/components/report/ReportTeacher.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="report-teacher">
+    <report-box title="任课老师分析">
+      <div
+        v-if="(chart, cindex) in teacherCharts"
+        :key="cindex"
+        class="page-chart"
+      >
+        <v-chart :option="chart"></v-chart>
+      </div>
+      <table
+        v-for="(tgroup, tindex) in teacherTables"
+        :key="tindex"
+        class="table table-border"
+      >
+        <tr>
+          <th>任课老师</th>
+          <th>班级</th>
+          <th>有效人数</th>
+          <th>最高分</th>
+          <th>最低分</th>
+          <th>及格数</th>
+          <th>及格率(%)</th>
+          <th>优秀数</th>
+          <th>优秀率(%)</th>
+          <th>平均分</th>
+          <th>相对平均分</th>
+        </tr>
+        <tr v-for="(item, ind) in tgroup" :key="ind">
+          <td>{{ item.teacherName }}</td>
+          <td>{{ item.className }}</td>
+          <td>{{ item.totalCount }}</td>
+          <td>{{ item.maxScore }}</td>
+          <td>{{ item.minScore }}</td>
+          <td>{{ item.passCount }}</td>
+          <td>{{ item.passRate }}</td>
+          <td>{{ item.excellentCount }}</td>
+          <td>{{ item.excellentRate }}</td>
+          <td>{{ item.avgScore }}</td>
+          <td>{{ item.relativeAvgScore }}</td>
+        </tr>
+      </table>
+    </report-box>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import ReportBox from "./ReportBox.vue";
+import { sectionArr } from "./utils";
+import { getBarsOptions } from "./chart";
+// TODO:
+export default {
+  name: "report-teacher",
+  components: { ReportBox },
+  data() {
+    return {
+      teacherCharts: [],
+      teacherTables: [],
+    };
+  },
+  computed: {
+    ...mapState("report", ["teacher"]),
+  },
+  methods: {
+    initData() {
+      const teacher = this.teacher;
+      this.teacherCharts = sectionArr(teacher, 10).map((data) =>
+        getBarsOptions(data)
+      );
+      this.teacherTables = sectionArr(teacher, 30);
+    },
+  },
+};
+</script>

+ 926 - 0
src/modules/mark/components/report/chart.js

@@ -0,0 +1,926 @@
+const colors = [
+  "rgba(67, 80, 136, 0.8)",
+  "rgba(151, 94, 229, 0.8)",
+  "rgba(61, 160, 255, 0.8)",
+  "rgba(48, 203, 203, 0.8)",
+  "rgba(75, 203, 116, 0.8)",
+  "rgba(250, 212, 68, 0.8)",
+  "rgba(217, 217, 217, 0.8)",
+];
+const textStyle = {
+  color: "#555",
+  fontFamily:
+    '"PingFang SC", "Microsoft YaHei", Tahoma,Helvetica, Arial, sans-serif',
+};
+const animationIsOpen = false;
+
+function getArrayMaxNum(arr) {
+  return Math.max.apply(null, arr);
+}
+//  chart -------------------------->
+export function getScoreBarOptions(dataObj) {
+  let series = [
+    {
+      type: "bar",
+      data: dataObj.map((item) => item.studentCount),
+      barWidth: 20,
+    },
+  ];
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    grid: {
+      left: 50,
+      top: 40,
+      bottom: 40,
+      show: true,
+      borderColor: "rgba(230,230,230,1)",
+    },
+    yAxis: [
+      {
+        type: "value",
+        axisTick: {
+          show: false,
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 18,
+        },
+      },
+    ],
+    xAxis: [
+      {
+        type: "category",
+        data: dataObj.map((item) => `${item.minScore}~${item.maxScore}`),
+        axisTick: {
+          show: false,
+        },
+        splitArea: {
+          show: true,
+          areaStyle: {
+            color: ["rgba(250,250,250,1)", "rgba(255,255,255,1)"],
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 17,
+          margin: 20,
+          formatter: function (value) {
+            let str = value.split("#");
+            if (str[0].length > 10) str[0] = str[0].substr(0, 10) + "...";
+            return [str[0], str[1]].join(" ");
+          },
+        },
+      },
+    ],
+    series: series,
+  };
+}
+export function getBarsOptions(dataObj) {
+  let xAxisData = dataObj.map((item) => item.name);
+  let avgDatas = dataObj.map((item) => item.avgScore);
+  let maxDatas = dataObj.map((item) => item.maxScore);
+  let minDatas = dataObj.map((item) => item.minScore);
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    color: [
+      "rgba(61, 160, 255, 0.8)",
+      "#4bcb74",
+      "rgba(151, 94, 229, 0.8)",
+      "#999999",
+      "rgba(67, 80, 136, 0.8)",
+      "rgba(250, 212, 68, 0.8)",
+    ],
+    grid: {
+      top: 50,
+      bottom: 90,
+      show: true,
+      borderColor: "rgba(230,230,230,1)",
+    },
+    xAxis: {
+      type: "category",
+      data: xAxisData,
+      axisTick: {
+        show: false,
+      },
+      splitArea: {
+        show: true,
+        areaStyle: {
+          color: ["rgba(255,255,255,1)", "rgba(250,250,250,1)"],
+        },
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      axisLabel: {
+        fontSize: 16,
+        margin: 12,
+        interval: 0,
+        formatter: function (value) {
+          if (value.length > 6) {
+            return [value.substring(0, 6), value.substring(6)].join("\n");
+          }
+          return value;
+        },
+      },
+      axisLine: {
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+    },
+    yAxis: [
+      {
+        type: "value",
+        axisTick: {
+          show: false,
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 16,
+          color: "#555",
+        },
+      },
+    ],
+    series: [
+      {
+        type: "bar",
+        data: avgDatas,
+        name: "平均分",
+        barWidth: 20,
+      },
+      {
+        type: "bar",
+        data: maxDatas,
+        name: "最高分",
+        barWidth: 20,
+      },
+      {
+        type: "bar",
+        data: minDatas,
+        name: "最低分",
+        barWidth: 20,
+      },
+    ],
+  };
+}
+export function getBarPointTopicOptions(dataObj) {
+  let yAxisLabels = dataObj.map(
+    (item) => `${item.title} ${item.mainNumber}-${item.subNumber}`
+  );
+  let avgDatas = dataObj.map((item) => item.avgScore);
+  let scoreRateDatas = dataObj.map((item) => item.scoreRate);
+  let fullRateDatas = dataObj.map((item) => item.fullScoreRate);
+  const xAxisTopMax = Math.ceil(getArrayMaxNum(avgDatas));
+
+  let legendData = ["单题平均分", "得分率", "满分率"];
+  let series = [
+    {
+      type: "bar",
+      data: avgDatas,
+      barWidth: 6,
+      barGap: "50%",
+      name: "单题平均分",
+      xAxisIndex: 0,
+      itemStyle: {
+        color: "rgba(61, 160, 255, 0.8)",
+      },
+    },
+    {
+      type: "line",
+      data: scoreRateDatas,
+      symbol: "circle",
+      symbolSize: 8,
+      name: "得分率",
+      xAxisIndex: 1,
+      itemStyle: {
+        color: "rgba(67, 80, 136, 0.8)",
+      },
+    },
+    {
+      type: "line",
+      data: fullRateDatas,
+      symbol: "circle",
+      symbolSize: 8,
+      name: "满分率",
+      xAxisIndex: 1,
+      itemStyle: {
+        color: "rgba(250, 212, 68, 0.8)",
+      },
+    },
+  ];
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    legend: {
+      data: legendData,
+      itemGap: 30,
+      itemWidth: 14,
+      bottom: 20,
+      textStyle: {
+        fontSize: 19,
+      },
+    },
+    grid: [
+      {
+        left: "20%",
+        top: 120,
+        width: "66%",
+        bottom: 100,
+        show: true,
+        borderColor: "rgba(230,230,230,1)",
+      },
+    ],
+    xAxis: [
+      {
+        type: "value",
+        position: "top",
+        min: 0,
+        max: xAxisTopMax,
+        interval: xAxisTopMax / 10,
+        axisTick: {
+          show: false,
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 18,
+          formatter: function (value, index) {
+            if (!index) return "0";
+            return value.toFixed(2);
+          },
+        },
+      },
+      {
+        type: "value",
+        min: 0,
+        max: 1,
+        interval: 0.1,
+        position: "bottom",
+        axisTick: {
+          show: false,
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 18,
+          formatter: function (value, index) {
+            if (!index) return "0";
+            return parseInt(value * 100) + "%";
+          },
+        },
+      },
+    ],
+    yAxis: [
+      {
+        type: "category",
+        gridIndex: 0,
+        data: yAxisLabels,
+        inverse: true,
+        axisTick: {
+          show: false,
+        },
+        splitArea: {
+          show: true,
+          areaStyle: {
+            color: ["rgba(250,250,250,1)", "rgba(255,255,255,1)"],
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 17,
+          margin: 20,
+          formatter: function (value) {
+            let str = value.split("#");
+            if (str[0].length > 10) str[0] = str[0].substr(0, 10) + "...";
+            return [str[0], str[1]].join(" ");
+          },
+        },
+      },
+    ],
+    series: series,
+  };
+}
+
+// other
+export function getBarPointGradeOptions(dataObj) {
+  let datas = dataObj.datas;
+  let xAxisTopMax = dataObj.xAxisTopMax;
+
+  let yAxisLabels = datas.map(function (item) {
+    return item.name;
+  });
+  let avgDatas = datas.map(function (item) {
+    return item.avgScore;
+  });
+  let maxDatas = datas.map(function (item) {
+    return item.maxScore;
+  });
+  let minDatas = datas.map(function (item) {
+    return item.minScore;
+  });
+  let passRateDatas = datas.map(function (item) {
+    return Math.round(item.passRate * 100);
+  });
+  let excellentRateDatas = datas.map(function (item) {
+    return Math.round(item.excellentRate * 100);
+  });
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    legend: {
+      data: ["平均分", "最高分", "最低分", "及格率", "优秀率"],
+      itemGap: 30,
+      itemWidth: 14,
+      bottom: 20,
+      textStyle: {
+        fontSize: 19,
+      },
+    },
+    grid: [
+      {
+        left: "20%",
+        top: 120,
+        width: "66%",
+        bottom: 100,
+        show: true,
+        borderColor: "rgba(230,230,230,1)",
+      },
+    ],
+    xAxis: [
+      {
+        type: "value",
+        position: "top",
+        min: 0,
+        max: xAxisTopMax,
+        interval: xAxisTopMax / 10,
+        axisTick: {
+          show: false,
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 18,
+          color: "#555",
+          formatter: function (value, index) {
+            if (!index) return "0";
+            return value;
+          },
+        },
+      },
+      {
+        type: "value",
+        min: 0,
+        max: 100,
+        interval: 10,
+        position: "bottom",
+        axisTick: {
+          show: false,
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 18,
+          color: "#555",
+          formatter: function (value, index) {
+            if (!index) return "0";
+            return value + "%";
+          },
+        },
+      },
+    ],
+    yAxis: [
+      {
+        type: "category",
+        gridIndex: 0,
+        data: yAxisLabels,
+        inverse: true,
+        axisTick: {
+          show: false,
+        },
+        splitArea: {
+          show: true,
+          areaStyle: {
+            color: ["rgba(250,250,250,1)", "rgba(255,255,255,1)"],
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 19,
+          margin: 30,
+          formatter: function (value) {
+            if (value.length > 8) {
+              return [value.substring(0, 8), value.substring(8)].join("\n");
+            }
+            return value;
+          },
+        },
+      },
+    ],
+    series: [
+      {
+        type: "bar",
+        data: avgDatas,
+        barWidth: 6,
+        barGap: "50%",
+        name: "平均分",
+        xAxisIndex: 0,
+        itemStyle: {
+          color: "rgba(61, 160, 255, 0.8)",
+        },
+      },
+      {
+        type: "bar",
+        data: maxDatas,
+        barWidth: 6,
+        barGap: "50%",
+        name: "最高分",
+        xAxisIndex: 0,
+        itemStyle: {
+          color: "rgba(151, 94, 229, 0.8)",
+        },
+      },
+      {
+        type: "bar",
+        data: minDatas,
+        barWidth: 6,
+        barGap: "50%",
+        name: "最低分",
+        xAxisIndex: 0,
+        itemStyle: {
+          color: "rgba(217, 217, 217, 0.8)",
+        },
+      },
+      {
+        type: "line",
+        data: passRateDatas,
+        symbol: "circle",
+        symbolSize: 8,
+        name: "及格率",
+        xAxisIndex: 1,
+        itemStyle: {
+          color: "rgba(67, 80, 136, 0.8)",
+        },
+      },
+      {
+        type: "line",
+        data: excellentRateDatas,
+        symbol: "circle",
+        symbolSize: 8,
+        name: "优秀率",
+        xAxisIndex: 1,
+        itemStyle: {
+          color: "rgba(250, 212, 68, 0.8)",
+        },
+      },
+    ],
+  };
+}
+
+export function getBarTeachersOptions(datas) {
+  let xAxisData = [
+    "实考人数",
+    "最高分",
+    "最低分",
+    "及格人数",
+    "及格率",
+    "优秀人数",
+    "优秀率",
+    "平均分",
+    "相对平均分",
+  ];
+
+  let legendData = datas.map(function (item) {
+    return item.name;
+  });
+  let barWidth = 22 - datas.length * 2;
+  let series = datas.map(function (item, index) {
+    return {
+      type: "bar",
+      data: [
+        item.totalCount,
+        item.maxScore,
+        item.minScore,
+        item.passCount,
+        item.passRate100,
+        item.excellentCount,
+        item.excellentRate100,
+        item.avgScore,
+        item.relativeAvgScore,
+      ],
+      barWidth: barWidth,
+      name: item.name,
+    };
+  });
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    color: colors,
+    legend: {
+      data: legendData,
+      left: "center",
+      bottom: 10,
+      itemGap: 20,
+      itemWidth: 14,
+      textStyle: {
+        fontSize: 19,
+      },
+    },
+    grid: {
+      show: true,
+      left: 80,
+      right: 50,
+      top: 100,
+      bottom: 100,
+      borderColor: "rgba(230,230,230,1)",
+    },
+    xAxis: {
+      type: "category",
+      data: xAxisData,
+      axisTick: {
+        show: false,
+      },
+      splitArea: {
+        show: true,
+        areaStyle: {
+          color: ["rgba(255,255,255,1)", "rgba(250,250,250,1)"],
+        },
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      axisLabel: {
+        fontSize: 18,
+        margin: 12,
+        interval: 0,
+      },
+    },
+    yAxis: {
+      type: "value",
+      z: 1,
+      axisTick: {
+        show: false,
+      },
+      axisLine: {
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      axisLabel: {
+        fontSize: 16,
+        color: "#555",
+      },
+    },
+    series: series,
+  };
+}
+
+export function getBarTeacherGradeOptions(datas) {
+  let title = datas.name + "_任课班级成绩分析";
+  let symbolCircle =
+    "path://M16,9.2c-3.8,0-6.8,3.1-6.8,6.8s3.1,6.8,6.8,6.8s6.8-3.1,6.8-6.8S19.8,9.2,16,9.2z M29.7,16  c0,7.6-6.1,13.7-13.7,13.7S2.3,23.6,2.3,16S8.4,2.3,16,2.3S29.7,8.4,29.7,16z";
+  let classes = datas.classes;
+  let xAxisData = classes.map(function (item) {
+    return item.name;
+  });
+  let avgDatas = classes.map(function (item) {
+    return item.avgScore;
+  });
+  let relateAvgDatas = classes.map(function (item) {
+    return item.relativeAvgScore;
+  });
+  let maxDatas = classes.map(function (item) {
+    return item.maxScore;
+  });
+  let minDatas = classes.map(function (item) {
+    return item.minScore;
+  });
+  let passRateDatas = classes.map(function (item) {
+    return item.passRate;
+  });
+  let excellentRateDatas = classes.map(function (item) {
+    return item.excellentRate;
+  });
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    color: [
+      "rgba(61, 160, 255, 0.8)",
+      "#4bcb74",
+      "rgba(151, 94, 229, 0.8)",
+      "#999999",
+      "rgba(67, 80, 136, 0.8)",
+      "rgba(250, 212, 68, 0.8)",
+    ],
+    title: {
+      text: "▲  " + title,
+      left: "center",
+      bottom: 0,
+      textStyle: {
+        color: "rgba(67, 80, 136, 0.8)",
+      },
+    },
+    grid: {
+      top: 50,
+      bottom: 90,
+      show: true,
+      borderColor: "rgba(230,230,230,1)",
+    },
+    xAxis: {
+      type: "category",
+      data: xAxisData,
+      axisTick: {
+        show: false,
+      },
+      splitArea: {
+        show: true,
+        areaStyle: {
+          color: ["rgba(255,255,255,1)", "rgba(250,250,250,1)"],
+        },
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      axisLabel: {
+        fontSize: 16,
+        margin: 12,
+        interval: 0,
+        formatter: function (value) {
+          if (value.length > 6) {
+            return [value.substring(0, 6), value.substring(6)].join("\n");
+          }
+          return value;
+        },
+      },
+      axisLine: {
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+    },
+    yAxis: [
+      {
+        type: "value",
+        z: 1,
+        position: "left",
+        axisTick: {
+          show: false,
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 16,
+          color: "#555",
+        },
+      },
+      {
+        type: "value",
+        min: 0,
+        max: 1,
+        position: "right",
+        axisTick: {
+          show: false,
+        },
+        axisLine: {
+          lineStyle: {
+            color: "rgba(233,233,233,1)",
+          },
+        },
+        axisLabel: {
+          fontSize: 16,
+          color: "#555",
+          formatter: function (value) {
+            if (!value) return "0";
+            return value.toFixed(1);
+          },
+        },
+      },
+    ],
+    series: [
+      {
+        type: "bar",
+        data: avgDatas,
+        name: "平均分",
+        barWidth: 12,
+      },
+      {
+        type: "bar",
+        data: relateAvgDatas,
+        name: "相对平均分",
+        barWidth: 12,
+      },
+      {
+        type: "bar",
+        data: maxDatas,
+        name: "最高分",
+        barWidth: 12,
+      },
+      {
+        type: "bar",
+        data: minDatas,
+        name: "最低分",
+        barWidth: 12,
+      },
+      {
+        type: "scatter",
+        data: passRateDatas,
+        yAxisIndex: 1,
+        symbol: symbolCircle,
+        symbolSize: 10,
+        name: "及格率",
+      },
+      {
+        type: "scatter",
+        data: excellentRateDatas,
+        yAxisIndex: 1,
+        symbol: symbolCircle,
+        symbolSize: 10,
+        name: "优秀率",
+      },
+    ],
+  };
+}
+
+export function getLineSmoothOptions(datainfo) {
+  let lineColors = colors.slice(0, -1);
+  let datas = datainfo.dataList;
+  let lineColor = lineColors[datainfo.index % colors.length];
+  let xAxisData = datas.map(function (item) {
+    return item.score;
+  });
+  let levelDatas = datas.map(function (item) {
+    return item.rangeRate;
+  });
+
+  return {
+    animation: animationIsOpen,
+    textStyle: textStyle,
+    grid: {
+      top: 110,
+      bottom: 80,
+      show: true,
+      borderColor: "rgba(230,230,230,1)",
+    },
+    xAxis: {
+      type: "category",
+      data: xAxisData,
+      boundaryGap: false,
+      inverse: true,
+      splitLine: {
+        show: false,
+      },
+      axisLabel: {
+        fontSize: 19,
+        margin: 20,
+        interval: xAxisData.length <= 16 ? 0 : "auto",
+        formatter: function (value) {
+          return value + "-";
+        },
+      },
+      axisLine: {
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+    },
+    yAxis: {
+      type: "value",
+      axisTick: {
+        show: false,
+      },
+      axisLine: {
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: "rgba(233,233,233,1)",
+        },
+      },
+      axisLabel: {
+        fontSize: 19,
+        margin: 20,
+        formatter: function (value, index) {
+          return value.toFixed(2);
+        },
+      },
+    },
+    series: [
+      {
+        type: "line",
+        data: levelDatas,
+        symbol: "none",
+        smooth: true,
+        smoothMonotone: "none",
+        lineStyle: {
+          color: lineColor,
+          width: 3,
+        },
+      },
+    ],
+  };
+}

+ 81 - 0
src/modules/mark/components/report/data.json

@@ -0,0 +1,81 @@
+{
+  "overview": {
+    "studentCount": "总考生数",
+    "actualCount": "实考",
+    "absentCount": "缺考",
+    "breachCount": "违纪",
+    "avgScore": "平均分",
+    "maxScore": "最高分",
+    "minScore": "最低分",
+    "passCount": "及格人数",
+    "passRate": "及格率",
+    "excellentCount": "优秀人数",
+    "excellentRate": "优秀率"
+  },
+  "scoreRange": [
+    {
+      "minScore": "分数段(开始)",
+      "maxScore": "分数段(结束)",
+      "studentCount": "人数",
+      "rate": "占比"
+    }
+  ],
+  "college": [
+    {
+      "college": "学院",
+      "avgScore": "平均分",
+      "maxScore": "最高分",
+      "minScore": "最低分",
+      "passCount": "及格人数",
+      "passRate": "及格率",
+      "excellentCount": "优秀人数",
+      "excellentRate": "优秀率"
+    }
+  ],
+  "class": [
+    {
+      "className": "班级",
+      "avgScore": "平均分",
+      "maxScore": "最高分",
+      "minScore": "最低分",
+      "passCount": "及格人数",
+      "passRate": "及格率",
+      "excellentCount": "优秀人数",
+      "excellentRate": "优秀率"
+    }
+  ],
+  "teacher": [
+    {
+      "teacherName": "老师",
+      "avgScore": "平均分",
+      "maxScore": "最高分",
+      "minScore": "最低分",
+      "passCount": "及格人数",
+      "passRate": "及格率",
+      "excellentCount": "优秀人数",
+      "excellentRate": "优秀率"
+    }
+  ],
+  "objective": [
+    {
+      "title": "题目名称",
+      "mainNumber": "大题号",
+      "subNumber": "小题号",
+      "score": "得分",
+      "avgScore": "单题平均分",
+      "socreRate": "得分率",
+      "fullScoreRate": "满分率"
+    }
+  ],
+  "subjective": [
+    {
+      "title": "题目名称",
+      "mainNumber": "大题号",
+      "subNumber": "小题号",
+      "score": "得分",
+      "avgScore": "单题平均分",
+      "socreRate": "得分率",
+      "fullScoreRate": "满分率"
+    }
+  ]
+}

+ 34 - 0
src/modules/mark/components/report/reportStore.js

@@ -0,0 +1,34 @@
+const state = {
+  baseinfo: {},
+  overview: {},
+  scoreRange: [],
+  college: [],
+  class: [],
+  teacher: [],
+  objective: [],
+  subjective: [],
+};
+
+const mutations = {
+  setData(state, data) {
+    Object.keys(state).forEach((k) => {
+      state[k] = data[k];
+    });
+  },
+  resetData(state) {
+    state.baseinfo = {};
+    state.overview = {};
+    state.scoreRange = [];
+    state.college = [];
+    state.class = [];
+    state.teacher = [];
+    state.objective = [];
+    state.subjective = [];
+  },
+};
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+};

+ 127 - 0
src/modules/mark/components/report/utils.js

@@ -0,0 +1,127 @@
+/**
+ *
+ * @param {Array} arrayList 要分段的数组
+ * @param {Number} sectionLength 每段的长度
+ */
+export function sectionArr(arrayList, sectionLength) {
+  var splitArr = [];
+  for (var i = 0, len = arrayList.length; i < len; i += sectionLength) {
+    splitArr.push(arrayList.slice(i, i + sectionLength));
+  }
+  return splitArr;
+}
+
+/**
+ *
+ * @param {Array} arrayList 要分段的数组
+ * @param {Number} sectionLength 每段的长度
+ * @param {Number} firstPageNum 第一段的长度
+ */
+export function sectionArrFirstPage(arrayList, sectionLength, firstPageNum) {
+  var splitArr = [];
+  var currentSectionLength = 0;
+  for (var i = 0, len = arrayList.length; i < len; i += currentSectionLength) {
+    if (i === 0) {
+      currentSectionLength = firstPageNum;
+    } else {
+      currentSectionLength = sectionLength;
+    }
+    splitArr.push(arrayList.slice(i, i + currentSectionLength));
+  }
+  return splitArr;
+}
+
+/**
+ * 组元素个数少于10个时,此法更方便
+ * @param {Array} arrayList 要分段的数组
+ * @param {Number} sectionMaxItemLength 最大分段元素个数
+ */
+export function sectionArrDynamic(
+  arrayList,
+  sectionMaxItemLength,
+  childrenName
+) {
+  var splitArr = [];
+  var splitSectionItemLen = 0;
+  var splitSectionArr = [];
+  for (var i = 0, len = arrayList.length; i < len; i++) {
+    if (
+      splitSectionItemLen + arrayList[i][childrenName].length >
+      sectionMaxItemLength
+    ) {
+      splitArr.push(splitSectionArr);
+      splitSectionItemLen = 0;
+      splitSectionArr = [];
+    }
+    splitSectionItemLen += arrayList[i][childrenName].length;
+    splitSectionArr.push(arrayList[i]);
+  }
+  splitArr.push(splitSectionArr);
+  return splitArr;
+}
+/**
+ * 组元素过多,且无规律时,使用此方法更合理
+ * @param {*} arrayList
+ * @param {*} sectionMaxItemLength
+ * @param {*} childrenName
+ */
+export function groupSectionArrDynamic(
+  arrayList,
+  sectionMaxItemLength,
+  childrenName
+) {
+  var splitArr = [];
+  var splitSectionItemLen = 0;
+  var splitSectionArr = [];
+
+  arrayList.map(function (item) {
+    var groupsList = [];
+    item[childrenName].map(function (elem) {
+      splitSectionItemLen++;
+      groupsList.push(elem);
+
+      if (splitSectionItemLen >= sectionMaxItemLength) {
+        splitSectionArr.push({
+          name: item.name,
+          [childrenName]: groupsList,
+        });
+        splitArr.push(splitSectionArr);
+        splitSectionItemLen = 0;
+        splitSectionArr = [];
+        groupsList = [];
+      }
+    });
+
+    if (groupsList.length)
+      splitSectionArr.push({
+        name: item.name,
+        [childrenName]: groupsList,
+      });
+  });
+  if (splitSectionArr.length) splitArr.push(splitSectionArr);
+
+  return splitArr;
+}
+
+export function getArrayMaxNum(arr) {
+  return Math.max.apply(null, arr);
+}
+export function getAxisTopMax(dataList, chartType) {
+  // barPointTopic
+  // barPointGrade
+  var arrList = [];
+  if (chartType === "barPointTopic") {
+    dataList.map(function (item) {
+      arrList.push(item.avgScore);
+      arrList.push(item.stdevScore);
+    });
+    return Math.ceil(getArrayMaxNum(arrList));
+  } else if (chartType === "barPointGrade") {
+    dataList.map(function (item) {
+      arrList.push(item.avgScore);
+      arrList.push(item.maxScore);
+      arrList.push(item.minScore);
+    });
+    return Math.ceil(getArrayMaxNum(arrList) / 10) * 10;
+  }
+}

+ 2 - 0
src/store.js

@@ -12,6 +12,7 @@ import { card, free } from "./modules/card/store";
 import exam from "./modules/exam/store";
 import baseConfigs from "./modules/analysis/components/baseConfig/baseConfigsStore";
 import markParam from "./modules/mark/components/markParam/store";
+import report from "./modules/mark/components/report/reportStore";
 
 export default new Vuex.Store({
   state: {
@@ -34,5 +35,6 @@ export default new Vuex.Store({
     exam,
     baseConfigs,
     markParam,
+    report,
   },
 });

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio