فهرست منبع

feat: 开课课程考试分析调整

zhangjie 5 ماه پیش
والد
کامیت
b0ff02e313

+ 13 - 12
.eslintrc.js

@@ -3,23 +3,24 @@ module.exports = {
   env: {
     node: true
   },
-  extends: ['plugin:vue/essential', '@vue/prettier'],
+  extends: ["plugin:vue/essential", "@vue/prettier"],
   rules: {
-    'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
-    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
-    'no-unused-vars': [
-      'error',
-      { vars: 'all', args: 'none', ignoreRestSiblings: false }
+    "no-console": process.env.NODE_ENV === "production" ? "off" : "off",
+    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
+    "no-unused-vars": [
+      "error",
+      { vars: "all", args: "none", ignoreRestSiblings: false }
     ],
-    'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
-    'vue/no-use-v-if-with-v-for': [
-      'error',
+    "vue/no-parsing-error": [2, { "x-invalid-end-tag": false }],
+    "vue/no-use-v-if-with-v-for": [
+      "error",
       {
         allowUsingIterationVar: true
       }
-    ]
+    ],
+    "vue/no-template-key": "off"
   },
   parserOptions: {
-    parser: 'babel-eslint'
+    parser: "babel-eslint"
   }
-}
+};

+ 51 - 0
src/assets/styles/base.scss

@@ -301,6 +301,57 @@ body {
     }
   }
 }
+.table-more {
+  color: $--color-text-gray;
+  border: 1px solid $--color-border;
+  border-top: none;
+  text-align: center;
+  padding: 8px;
+  cursor: pointer;
+
+  > i {
+    margin-right: 3px;
+    font-size: 12px;
+  }
+
+  &:hover {
+    color: $--color-primary;
+  }
+}
+
+// more-text
+.more-text {
+  &-body {
+    max-height: 50px;
+    overflow: hidden;
+    position: relative;
+  }
+
+  &-more {
+    position: absolute;
+    padding-left: 40px;
+    bottom: 0;
+    right: 0;
+    z-index: 9;
+    color: $--color-primary;
+    background-image: linear-gradient(
+      90deg,
+      rgba(255, 255, 255, 0.5),
+      rgba(255, 255, 255, 1) 50%
+    );
+    cursor: pointer;
+
+    &:hover {
+      color: $--color-primary-light;
+      text-decoration: underline;
+    }
+  }
+  &-icon {
+    position: absolute;
+    bottom: 0;
+    left: 20px;
+  }
+}
 
 /* list */
 .list-lr-right {

+ 45 - 3
src/modules/college/chartOption.js

@@ -841,13 +841,15 @@ export const getDistinctionAnalyseOption = datas => {
           params[0].axisValue,
           params[0].data.detailName,
           `区分度:${params[0].value}`,
-          `容易度:${params[1].value}`
+          `容易度:${params[1].value}`,
+          `信度:${params[2].value}`,
+          `效度:${params[3].value}`
         ].join("<br />");
       }
     },
     legend: {
       top: 0,
-      data: ["区分度", "容易度"],
+      data: ["区分度", "容易度", "信度", "效度"],
       itemWidth: 8,
       itemHeight: 8,
       itemGap: 22,
@@ -893,7 +895,7 @@ export const getDistinctionAnalyseOption = datas => {
       },
       {
         type: "value",
-        name: "容易度",
+        name: "其他",
         max: 1,
         min: -1,
         splitNumber: 10,
@@ -980,6 +982,46 @@ export const getDistinctionAnalyseOption = datas => {
             detailName: item.detailName
           };
         })
+      },
+      {
+        type: "line",
+        name: "信度",
+        showSymbol: false,
+        smooth: true,
+        symbol: "circle",
+        yAxisIndex: 1,
+        // lineStyle: {
+        //   color: "#FE5D4E"
+        // },
+        // itemStyle: {
+        //   color: "#FE5D4E"
+        // },
+        data: datas.map(item => {
+          return {
+            value: item.sind,
+            detailName: item.detailName
+          };
+        })
+      },
+      {
+        type: "line",
+        name: "效度",
+        showSymbol: false,
+        smooth: true,
+        symbol: "circle",
+        yAxisIndex: 1,
+        // lineStyle: {
+        //   color: "#FE5D4E"
+        // },
+        // itemStyle: {
+        //   color: "#FE5D4E"
+        // },
+        data: datas.map((item, index) => {
+          return {
+            value: item.xd,
+            detailName: item.detailName
+          };
+        })
       }
     ]
   };

+ 1 - 0
src/modules/college/course/owner-course-exam-analysis/DifficultLevelAnalysis.vue

@@ -75,6 +75,7 @@
     <div class="chart-box chart-ndspzd">
       <v-chart
         :option="curData.chartOption"
+        autoresize
         v-if="curData.chartOption"
       ></v-chart>
     </div>

+ 1 - 0
src/modules/college/course/owner-course-exam-analysis/MistakeAnalysis.vue

@@ -24,6 +24,7 @@
     <div class="chart-box chart-ksctnd">
       <v-chart
         :option="curMistake.chartOption"
+        autoresize
         v-if="curMistake.chartOption"
       ></v-chart>
     </div>

+ 386 - 238
src/modules/college/course/owner-course-exam-analysis/OwnerCourseExamAnalysis.vue

@@ -8,7 +8,7 @@
         <i class="icon icon-sign"></i>开课学院:{{ orgName }}
       </div>
     </div>
-    <div class="part-box part-box-filter" v-if="setReady">
+    <div class="part-box part-box-filter part-box-flex" v-if="setReady">
       <el-form ref="FilterForm" label-position="left" label-width="60px" inline>
         <el-form-item label="学期:">
           <semester-select
@@ -42,250 +42,294 @@
           <el-button type="primary" round @click="search">查询</el-button>
         </el-form-item>
       </el-form>
+      <div class="part-box-action">
+        <el-button round @click="toCustom">自定义分析报告</el-button>
+      </div>
     </div>
 
-    <el-row :gutter="20" type="flex">
-      <el-col :span="12">
-        <div class="part-box part-box-pad">
-          <div class="part-box-head">
-            <h3>
-              <span>考试概况</span>
-              <el-popover
-                popper-class="el-popper-dark"
-                placement="right-start"
-                width="300"
-                trigger="hover"
-              >
-                <i class="el-icon-question" slot="reference"></i>
-                <div>
-                  <p>1.统计成绩范围包含应届、非应届类型的考生数据;</p>
-                  <p>
-                    2.有效人数:成绩有效的实考人数。
-                  </p>
-                </div>
-              </el-popover>
-            </h3>
-          </div>
-          <div class="summary-info">
-            <div class="summary-info-row">
-              <div class="summary-info-item">
-                <i class="summary-info-icon"><i class="icon icon-start"></i></i>
-                <p>考试时间</p>
-                <p>{{ summary.examTime | timestampFilter }}</p>
-              </div>
-              <div class="summary-info-item">
-                <i class="summary-info-icon"
-                  ><i class="icon icon-course"></i
-                ></i>
-                <p>课程</p>
-                <p>{{ summary.courseName }}</p>
+    <div class="waterfall-container" ref="waterfallRef" :key="reportKey">
+      <!-- 考试概况 -->
+      <div v-if="customReportSet.summary" class="part-box part-box-pad">
+        <div class="part-box-head">
+          <h3>
+            <span>考试概况</span>
+            <el-popover
+              popper-class="el-popper-dark"
+              placement="right-start"
+              width="300"
+              trigger="hover"
+            >
+              <i class="el-icon-question" slot="reference"></i>
+              <div>
+                <p>1.统计成绩范围包含应届、非应届类型的考生数据;</p>
+                <p>
+                  2.有效人数:成绩有效的实考人数。
+                </p>
               </div>
+            </el-popover>
+          </h3>
+        </div>
+        <div class="summary-info">
+          <div class="summary-info-row">
+            <div class="summary-info-item">
+              <i class="summary-info-icon"><i class="icon icon-start"></i></i>
+              <p>考试时间</p>
+              <p>{{ summary.examTime | timestampFilter }}</p>
             </div>
-            <div class="summary-info-row">
-              <div class="summary-info-item">
-                <i class="summary-info-icon"><i class="icon icon-full"></i></i>
-                <p>满分</p>
-                <p>{{ summary.fullScore }}</p>
-              </div>
-              <div class="summary-info-item">
-                <i class="summary-info-icon"><i class="icon icon-users"></i></i>
-                <p>有效(实考)人数</p>
-                <p>{{ summary.realityCount }}</p>
-              </div>
-              <div class="summary-info-item">
-                <i class="summary-info-icon"
-                  ><i class="icon icon-college"></i
-                ></i>
-                <p>考查学院</p>
-                <p>{{ summary.inspectCollegeCount }}</p>
-              </div>
-              <div class="summary-info-item">
-                <i class="summary-info-icon"
-                  ><i class="icon icon-absent"></i
-                ></i>
-                <p>缺考/违纪人数</p>
-                <p>{{ summary.absentCount }}</p>
-              </div>
+            <div class="summary-info-item">
+              <i class="summary-info-icon"><i class="icon icon-course"></i></i>
+              <p>课程</p>
+              <p>{{ summary.courseName }}</p>
             </div>
           </div>
-          <div class="summary-school">
-            <p>
-              <span>赋分系数:</span><span>{{ summary.coefficient }}</span>
-            </p>
-            <p>
-              <span>开课学院:</span><span>{{ summary.teachCollegeName }}</span>
-            </p>
-            <p>
-              <span>考查学院:</span>
-              <span>
-                <MoreText
-                  v-if="summary.inspectCollegeNames"
-                  :content="summary.inspectCollegeNames"
-                />
-              </span>
-              <!-- <span>{{ summary.inspectCollegeNames }}</span> -->
-            </p>
-          </div>
-        </div>
-        <QuestionDifficultScoreAnalysis ref="QuestionDifficultScoreAnalysis" />
-        <div class="part-box part-box-pad">
-          <div class="part-box-head">
-            <h3>
-              <span>区分度及容易度分析情况</span>
-              <el-popover
-                popper-class="el-popper-dark"
-                placement="right-start"
-                width="300"
-                trigger="hover"
-              >
-                <i class="el-icon-question" slot="reference"></i>
-                <div>
-                  <p>
-                    1.区分度与参加本次考试的学生水平有关。因此,这里计算出来的区分度大小,只是对本次考试测评的学生相对而言的容易度评估;
-                  </p>
-                  <p>
-                    2.一般情况下,区分度应为正值,值越大则区分度越好;若区分度为负值,则为消极区分,则说明这个题目可能存在问题,建议审查原因;区分度为0,为无区分作用。
-                  </p>
-                </div>
-              </el-popover>
-            </h3>
-            <!-- modules -->
-            <div class="module-select">
-              <div
-                v-for="mod in distinctionList"
-                :key="mod.id"
-                :class="[
-                  'module-select-item',
-                  {
-                    'module-select-item-act': mod.id === curDistinction.id
-                  }
-                ]"
-                @click="distinctionChange(mod)"
-              >
-                {{ mod.paperType }}
-              </div>
+          <div class="summary-info-row">
+            <div class="summary-info-item">
+              <i class="summary-info-icon"><i class="icon icon-full"></i></i>
+              <p>满分</p>
+              <p>{{ summary.fullScore }}</p>
+            </div>
+            <div class="summary-info-item">
+              <i class="summary-info-icon"><i class="icon icon-users"></i></i>
+              <p>有效(实考)人数</p>
+              <p>{{ summary.realityCount }}</p>
+            </div>
+            <div class="summary-info-item">
+              <i class="summary-info-icon"><i class="icon icon-college"></i></i>
+              <p>考查学院</p>
+              <p>{{ summary.inspectCollegeCount }}</p>
+            </div>
+            <div class="summary-info-item">
+              <i class="summary-info-icon"><i class="icon icon-absent"></i></i>
+              <p>缺考/违纪人数</p>
+              <p>{{ summary.absentCount }}</p>
             </div>
-          </div>
-          <div class="chart-box chart-qfdnd">
-            <v-chart
-              :option="curDistinction.chartOption"
-              v-if="curDistinction.chartOption"
-            ></v-chart>
           </div>
         </div>
-      </el-col>
-      <el-col :span="12">
-        <div class="part-box part-box-pad school-course">
-          <div class="part-box-head">
-            <h3>
-              <span>赋分分数段分布情况</span>
-              <el-popover
-                popper-class="el-popper-dark"
-                placement="right-start"
-                width="300"
-                trigger="hover"
-              >
-                <i class="el-icon-question" slot="reference"></i>
-                <div>
-                  <p>1.总体:包含应届、非应届类型;</p>
-                  <p>2.应届:只包含应届类型;</p>
-                </div>
-              </el-popover>
-            </h3>
-          </div>
-          <div class="chart-box chart-fsdfb">
-            <v-chart
-              :option="scoreAnalyseOption"
-              v-if="scoreAnalyseOption"
-            ></v-chart>
-          </div>
-          <div class="chart-summary">
-            <div class="chart-summary-body">
-              <div class="chart-summary-item">
-                <i class="chart-summary-icon"
-                  ><i class="icon icon-wave-cyan"></i
-                ></i>
-                <p>平均分<span>(总体)</span></p>
-                <p>{{ scoreAnalyseSummary.avgScore }}<span>分</span></p>
-              </div>
-              <div class="chart-summary-item">
-                <i class="chart-summary-icon"
-                  ><i class="icon icon-data-cyan"></i
-                ></i>
-                <p>不及格率<span>(总体)</span></p>
-                <p>{{ scoreAnalyseSummary.notPassRate }}<span>%</span></p>
-              </div>
-              <div class="chart-summary-item">
-                <i class="chart-summary-icon"
-                  ><i class="icon icon-wave-blue"></i
-                ></i>
-                <p>平均分<span>(应届)</span></p>
-                <p>{{ scoreAnalyseSummary.currentAvgScore }}<span>分</span></p>
-              </div>
-              <div class="chart-summary-item">
-                <i class="chart-summary-icon"
-                  ><i class="icon icon-data-blue"></i
-                ></i>
-                <p>不及格率<span>(应届)</span></p>
+        <div class="summary-school">
+          <p>
+            <span>赋分系数:</span><span>{{ summary.coefficient }}</span>
+          </p>
+          <p>
+            <span>开课学院:</span><span>{{ summary.teachCollegeName }}</span>
+          </p>
+          <p>
+            <span>考查学院:</span>
+            <span>
+              <MoreText
+                v-if="summary.inspectCollegeNames"
+                :content="summary.inspectCollegeNames"
+              />
+            </span>
+            <!-- <span>{{ summary.inspectCollegeNames }}</span> -->
+          </p>
+        </div>
+      </div>
+
+      <!-- 各难度水平题目上的作答分析 -->
+      <DifficultLevelAnalysis
+        v-if="customReportSet.difficultLevelAnalysis"
+        ref="DifficultLevelAnalysis"
+      />
+
+      <!-- 试题难度得分情况分析 -->
+      <QuestionDifficultScoreAnalysis
+        v-if="customReportSet.questionDifficultScoreAnalysis"
+        ref="QuestionDifficultScoreAnalysis"
+      />
+
+      <!-- 区分度及难度分析情况 -->
+      <div v-if="customReportSet.distinctionList" class="part-box part-box-pad">
+        <div class="part-box-head">
+          <h3>
+            <span>区分度及容易度分析情况</span>
+            <el-popover
+              popper-class="el-popper-dark"
+              placement="right-start"
+              width="300"
+              trigger="hover"
+            >
+              <i class="el-icon-question" slot="reference"></i>
+              <div>
                 <p>
-                  {{ scoreAnalyseSummary.currentNotPassRate }}<span>%</span>
+                  1.区分度与参加本次考试的学生水平有关。因此,这里计算出来的区分度大小,只是对本次考试测评的学生相对而言的容易度评估;
+                </p>
+                <p>
+                  2.一般情况下,区分度应为正值,值越大则区分度越好;若区分度为负值,则为消极区分,则说明这个题目可能存在问题,建议审查原因;区分度为0,为无区分作用。
                 </p>
               </div>
+            </el-popover>
+          </h3>
+          <!-- modules -->
+          <div class="module-select">
+            <div
+              v-for="mod in distinctionList"
+              :key="mod.id"
+              :class="[
+                'module-select-item',
+                {
+                  'module-select-item-act': mod.id === curDistinction.id
+                }
+              ]"
+              @click="distinctionChange(mod)"
+            >
+              {{ mod.paperType }}
             </div>
           </div>
-          <div class="chart-table">
-            <table class="table">
-              <tr>
-                <th rowspan="3">分数段</th>
-                <th colspan="4" class="text-center">赋分前</th>
-                <th colspan="4" class="text-center">赋分后</th>
-              </tr>
-              <tr class="text-center">
-                <th colspan="2">总体</th>
-                <th colspan="2">应届</th>
-                <th colspan="2">总体</th>
-                <th colspan="2">应届</th>
-              </tr>
-              <tr>
-                <th>有效人数</th>
-                <th>占比</th>
-                <th>有效人数</th>
-                <th>占比</th>
-                <th>有效人数</th>
-                <th>占比</th>
-                <th>有效人数</th>
-                <th>占比</th>
-              </tr>
-              <tr v-for="(grade, index) in scoreAnalyseList" :key="index">
-                <td class="text-left">{{ grade.describe }}</td>
-                <td>{{ grade.allCountBefore }}</td>
-                <td>{{ grade.allCountRateBefore }}%</td>
-                <td>{{ grade.currentCountBefore }}</td>
-                <td>{{ grade.currentCountRateBefore }}%</td>
-                <td>{{ grade.allCountAfter }}</td>
-                <td>{{ grade.allCountRateAfter }}%</td>
-                <td>{{ grade.currentCountAfter }}</td>
-                <td>{{ grade.currentCountRateAfter }}%</td>
-              </tr>
-              <tr>
-                <td class="text-left">小计</td>
-                <td>{{ scoreAnalyseListSum.allCountBefore }}</td>
-                <td></td>
-                <td>{{ scoreAnalyseListSum.currentCountBefore }}</td>
-                <td></td>
-                <td>{{ scoreAnalyseListSum.allCountAfter }}</td>
-                <td></td>
-                <td>{{ scoreAnalyseListSum.currentCountAfter }}</td>
-                <td></td>
-              </tr>
-            </table>
+        </div>
+        <div class="chart-box chart-qfdnd">
+          <v-chart
+            :option="curDistinction.chartOption"
+            autoresize
+            v-if="curDistinction.chartOption"
+          ></v-chart>
+        </div>
+      </div>
+      <!-- 教师各课堂成绩排名 -->
+      <TeacherClazzScore
+        v-if="customReportSet.teacherClazzScore"
+        ref="TeacherClazzScore"
+        :filter="filter"
+      />
+
+      <!-- 赋分分数段分布情况 -->
+      <div
+        v-if="customReportSet.scoreAnalyse"
+        class="part-box part-box-pad school-course"
+      >
+        <div class="part-box-head">
+          <h3>
+            <span>赋分分数段分布情况</span>
+            <el-popover
+              popper-class="el-popper-dark"
+              placement="right-start"
+              width="300"
+              trigger="hover"
+            >
+              <i class="el-icon-question" slot="reference"></i>
+              <div>
+                <p>1.总体:包含应届、非应届类型;</p>
+                <p>2.应届:只包含应届类型;</p>
+              </div>
+            </el-popover>
+          </h3>
+        </div>
+        <div class="chart-box chart-fsdfb">
+          <v-chart
+            :option="scoreAnalyseOption"
+            autoresize
+            v-if="scoreAnalyseOption"
+          ></v-chart>
+        </div>
+        <div class="chart-summary">
+          <div class="chart-summary-body">
+            <div class="chart-summary-item">
+              <i class="chart-summary-icon"
+                ><i class="icon icon-wave-cyan"></i
+              ></i>
+              <p>平均分<span>(总体)</span></p>
+              <p>{{ scoreAnalyseSummary.avgScore }}<span>分</span></p>
+            </div>
+            <div class="chart-summary-item">
+              <i class="chart-summary-icon"
+                ><i class="icon icon-data-cyan"></i
+              ></i>
+              <p>不及格率<span>(总体)</span></p>
+              <p>{{ scoreAnalyseSummary.notPassRate }}<span>%</span></p>
+            </div>
+            <div class="chart-summary-item">
+              <i class="chart-summary-icon"
+                ><i class="icon icon-wave-blue"></i
+              ></i>
+              <p>平均分<span>(应届)</span></p>
+              <p>{{ scoreAnalyseSummary.currentAvgScore }}<span>分</span></p>
+            </div>
+            <div class="chart-summary-item">
+              <i class="chart-summary-icon"
+                ><i class="icon icon-data-blue"></i
+              ></i>
+              <p>不及格率<span>(应届)</span></p>
+              <p>{{ scoreAnalyseSummary.currentNotPassRate }}<span>%</span></p>
+            </div>
           </div>
         </div>
-        <DifficultLevelAnalysis ref="DifficultLevelAnalysis" />
-        <TeacherClazzScore ref="TeacherClazzScore" :filter="filter" />
-      </el-col>
-    </el-row>
-    <MistakeAnalysis v-if="mistakeDatas" :datas="mistakeDatas" />
+        <div class="chart-table">
+          <table class="table">
+            <tr>
+              <th rowspan="3">分数段</th>
+              <th colspan="4" class="text-center">赋分前</th>
+              <th colspan="4" class="text-center">赋分后</th>
+            </tr>
+            <tr class="text-center">
+              <th colspan="2">总体</th>
+              <th colspan="2">应届</th>
+              <th colspan="2">总体</th>
+              <th colspan="2">应届</th>
+            </tr>
+            <tr>
+              <th>有效人数</th>
+              <th>占比</th>
+              <th>有效人数</th>
+              <th>占比</th>
+              <th>有效人数</th>
+              <th>占比</th>
+              <th>有效人数</th>
+              <th>占比</th>
+            </tr>
+            <tr v-for="(grade, index) in scoreAnalyseList" :key="index">
+              <td class="text-left">{{ grade.describe }}</td>
+              <td>{{ grade.allCountBefore }}</td>
+              <td>{{ grade.allCountRateBefore }}%</td>
+              <td>{{ grade.currentCountBefore }}</td>
+              <td>{{ grade.currentCountRateBefore }}%</td>
+              <td>{{ grade.allCountAfter }}</td>
+              <td>{{ grade.allCountRateAfter }}%</td>
+              <td>{{ grade.currentCountAfter }}</td>
+              <td>{{ grade.currentCountRateAfter }}%</td>
+            </tr>
+            <tr>
+              <td class="text-left">小计</td>
+              <td>{{ scoreAnalyseListSum.allCountBefore }}</td>
+              <td></td>
+              <td>{{ scoreAnalyseListSum.currentCountBefore }}</td>
+              <td></td>
+              <td>{{ scoreAnalyseListSum.allCountAfter }}</td>
+              <td></td>
+              <td>{{ scoreAnalyseListSum.currentCountAfter }}</td>
+              <td></td>
+            </tr>
+          </table>
+        </div>
+      </div>
+    </div>
+    <MistakeAnalysis
+      v-if="mistakeDatas && customReportSet.mistakeAnalysis"
+      :datas="mistakeDatas"
+    />
+
+    <!-- 自定义报告对话框 -->
+    <el-dialog
+      :visible.sync="customModalIsShow"
+      title="自定义报告设置"
+      top="10vh"
+      width="400px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      append-to-body
+    >
+      <el-checkbox-group v-model="customMenus">
+        <div v-for="item in customReports" :key="item.field">
+          <el-checkbox :label="item.field">{{ item.name }}</el-checkbox>
+          <br />
+        </div>
+      </el-checkbox-group>
+
+      <div slot="footer">
+        <el-button type="primary" @click="customConfirm">确认</el-button>
+        <el-button type="danger" @click="customModalIsShow = false" plain
+          >取消</el-button
+        >
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -300,7 +344,7 @@ import DifficultLevelAnalysis from "./DifficultLevelAnalysis";
 import TeacherClazzScore from "./TeacherClazzScore";
 import MistakeAnalysis from "./MistakeAnalysis";
 import MoreText from "../../../../components/MoreText";
-import { calcSum } from "@/plugins/utils";
+import { calcSum, toPrecision } from "@/plugins/utils";
 
 export default {
   name: "owner-course-exam-analysis",
@@ -331,12 +375,64 @@ export default {
       scoreAnalyseOption: null,
       distinctionList: [],
       curDistinction: {},
-      mistakeDatas: null
+      mistakeDatas: null,
+      // 自定义报告
+      reportKey: 0,
+      customReportSet: {
+        summary: true,
+        questionDifficultScoreAnalysis: true,
+        distinctionList: true,
+        scoreAnalyse: true,
+        difficultLevelAnalysis: true,
+        teacherClazzScore: true,
+        mistakeAnalysis: true
+      },
+      customModalIsShow: false,
+      customMenus: [],
+      customReports: [
+        {
+          name: "考试概况",
+          field: "summary"
+        },
+        {
+          name: "各难度水平题目上的作答分析",
+          field: "difficultLevelAnalysis"
+        },
+        {
+          name: "区分度及容易度分析情况",
+          field: "distinctionList"
+        },
+        {
+          name: "教师各课堂成绩排名",
+          field: "teacherClazzScore"
+        },
+        {
+          name: "赋分分数段分布情况",
+          field: "scoreAnalyse"
+        },
+        {
+          name: "试题难度得分情况分析",
+          field: "questionDifficultScoreAnalysis"
+        },
+        {
+          name: "考生错题难度分析",
+          field: "mistakeAnalysis"
+        }
+      ]
     };
   },
-  created() {
+  mounted() {
     const orgInfo = this.$ls.get("user", { orgInfo: { name: "" } }).orgInfo;
     if (orgInfo) this.orgName = orgInfo.name;
+
+    this.customMenus = this.$ls.get("customMenus", null);
+    if (!this.customMenus) {
+      this.customMenus = this.customReports.map(item => item.field);
+    }
+    this.customReports.forEach(item => {
+      this.customReportSet[item.field] = this.customMenus.includes(item.field);
+    });
+
     this.initPage();
   },
   methods: {
@@ -355,13 +451,15 @@ export default {
           this.search();
           this.selectDefault = true;
           this.$ls.remove("ownerCourseSet");
+
+          this.updateWaterfallColumn();
         });
       } else {
         this.setReady = true;
         this.selectDefault = true;
       }
     },
-    search() {
+    async search() {
       if (
         !this.filter.semesterId ||
         !this.filter.examId ||
@@ -371,13 +469,13 @@ export default {
         return;
       }
       this.mistakeDatas = null;
-      this.getData();
+      await this.getData();
       // 教师各课堂成绩排名
-      this.$refs.TeacherClazzScore.getData();
+      await this.$refs.TeacherClazzScore?.getData();
       // 试题难度得分情况分析
-      this.$refs.QuestionDifficultScoreAnalysis.initData(this.filter);
+      await this.$refs.QuestionDifficultScoreAnalysis?.initData(this.filter);
       // 各难度水平题目上的作答分析
-      this.$refs.DifficultLevelAnalysis.initData(this.filter);
+      await this.$refs.DifficultLevelAnalysis?.initData(this.filter);
     },
     async getData() {
       const datas = {
@@ -432,7 +530,9 @@ export default {
               name: `${elem.mainNumber}-${elem.subNumber}`,
               detailName: elem.questionName,
               validity: elem.validity,
-              scoreRate: elem.scoreRate / 100
+              scoreRate: elem.scoreRate / 100,
+              sind: toPrecision(Math.abs(Math.sin(elem.scoreRate) / 2)),
+              xd: toPrecision(Math.abs(Math.sin(elem.scoreRate + 10) / 2))
             };
           });
 
@@ -463,11 +563,59 @@ export default {
       ) {
         this.isInit = true;
         this.search();
+
+        this.updateWaterfallColumn();
       }
     },
     distinctionChange(mod) {
       this.curDistinction = mod;
+    },
+    toCustom() {
+      this.customModalIsShow = true;
+    },
+    updateWaterfallColumn() {
+      this.$nextTick(() => {
+        const menus = this.customMenus.filter(
+          item => item !== "mistakeAnalysis"
+        );
+
+        if (menus.length <= 2) {
+          this.$refs.waterfallRef.style.columnCount = 1;
+        } else {
+          this.$refs.waterfallRef.style.columnCount = 2;
+        }
+      });
+    },
+    customConfirm() {
+      if (this.customMenus.length === 0) {
+        this.$message.error("至少选择一项!");
+        return;
+      }
+
+      this.customReports.forEach(item => {
+        this.customReportSet[item.field] = this.customMenus.includes(
+          item.field
+        );
+      });
+      this.customModalIsShow = false;
+
+      this.reportKey = Date.now();
+      this.$ls.set("customMenus", this.customMenus);
+      this.search();
+      this.updateWaterfallColumn();
     }
   }
 };
 </script>
+
+<style lang="scss">
+.waterfall-container {
+  column-count: 2;
+  column-gap: 20px;
+  .part-box {
+    display: inline-block;
+    width: 100%;
+    margin-bottom: 20px;
+  }
+}
+</style>

+ 1 - 0
src/modules/college/course/owner-course-exam-analysis/QuestionDifficultScoreAnalysis.vue

@@ -81,6 +81,7 @@
     <div class="chart-box chart-nddfqk">
       <v-chart
         :option="curData.chartOption"
+        autoresize
         v-if="curData.chartOption"
       ></v-chart>
     </div>

+ 2 - 1
src/plugins/VueCharts.js

@@ -5,7 +5,7 @@ import { use } from "echarts/core";
 // import ECharts modules manually to reduce bundle size
 // 按需模块:https://github.com/apache/echarts/blob/master/src/echarts.all.ts
 // render type
-import { CanvasRenderer } from "echarts/renderers";
+import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
 
 // charts
 import {
@@ -28,6 +28,7 @@ import {
 
 use([
   CanvasRenderer,
+  SVGRenderer,
   BarChart,
   LineChart,
   PieChart,

+ 7 - 0
src/plugins/utils.js

@@ -420,3 +420,10 @@ export function parseHrefParam(href, paramName = null) {
 export function numToFixed(num, precision = 2) {
   return num.toFixed(precision);
 }
+
+export function toPrecision(num, fractionDigits = 2) {
+  if (fractionDigits < 0 || fractionDigits > 16) return num;
+
+  const tnum = Math.pow(10, fractionDigits);
+  return Math.round(num * tnum) / tnum;
+}