| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- <!-- 考试情况统计 -->
- <template>
- <div class="exam-situation-card">
- <div class="card-header">
- <span class="card-title">考试情况统计</span>
- <div class="header-right">
- <el-select v-model="valueYear" @change="topChange()" size="small" style="width:220px">
- <el-option
- v-for="dict in optionList"
- :key="dict.value"
- :label="dict.label"
- :value="dict.value"
- />
- </el-select>
- <!-- 分组按钮 -->
- <div class="table-top-button-box">
- <div>
- <p @click="checkButton(2)" :class="checkIndex == 2?'check-table-button':''">研究生</p>
- <p @click="checkButton(3)" :class="checkIndex == 3?'check-table-button':''">本科生</p>
- <p @click="checkButton(1)" :class="checkIndex == 1?'check-table-button':''">教职工</p>
- </div>
- </div>
- <!-- 导出按钮 -->
- <p class="export-btn" @click="exportButton()">
- <i class="el-icon-download"></i> 导出数据
- </p>
- </div>
- </div>
- <!-- 图表区 -->
- <div class="chart-area">
- <!-- 堆叠柱状图 -->
- <p class="bar-chart-wrap" v-if="!chartData.deptStats[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
- <div class="bar-chart-wrap" v-if="chartData.deptStats[0]" id="bar-chart-wrap" ref="barChart"></div>
- <!-- 环形图 -->
- <p class="pie-chart-wrap" v-if="!chartData.deptStats[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
- <div class="pie-chart-wrap" v-if="chartData.deptStats[0]" id="pie-chart-wrap" ref="pieChart"></div>
- </div>
- <!-- 筛选 -->
- <div class="filter-row">
- <el-cascader
- style="width: 180px"
- clearable
- v-model="queryParams.deptId"
- :options="optionListA"
- :props="{
- emitPath:false,
- checkStrictly: true,
- value: 'deptId',
- label: 'deptName',
- children: 'child',
- }"
- :show-all-levels="false"
- filterable
- placeholder="筛选学院"
- @change="queryParamsChange"
- ></el-cascader>
- <el-select
- @change="queryParamsChange"
- v-model="queryParams.examTypeId" clearable placeholder="考试类型" style="width: 160px">
- <el-option
- v-for="dict in optionListB"
- :key="dict.id"
- :label="dict.examTypeName"
- :value="dict.id"
- />
- </el-select>
- </div>
- <!-- 统计表格 -->
- <div class="page-content-box">
- <el-table class="table-box" :data="dataList" border :span-method="objectSpanMethod">
- <el-table-column prop="deptName" label="学院" />
- <el-table-column prop="examTypeName" label="考试类型" width="200" />
- <el-table-column prop="examCount" label="应考人数" width="160" align="center" />
- <el-table-column prop="passedCount" label="通过人数" width="160" align="center" />
- <el-table-column prop="notPassedCount" label="未通过人数" width="160" align="center"/>
- <el-table-column prop="absentCount" label="未考人数" width="160" align="center" />
- <el-table-column prop="passRate" label="通过率" width="160" align="center" />
- </el-table>
- </div>
- </div>
- </template>
- <script>
- import echarts from 'echarts'
- import {
- examElStatisticsGetAcademicYear,examElStatisticsExamList,examElStatisticsExamChart,
- examElExamTypeList,systemDeptCurrentDept,
- } from "@/api/safetyEducationExaminationNew/index";
- export default {
- name: 'ExamSituation',
- data() {
- return {
- chartData: {
- barGroups: [
- { label: '化学与化工', pass: 106, fail: 8, notExam: 6 },
- { label: '生命科学', pass: 84, fail: 7, notExam: 4 },
- { label: '物理', pass: 67, fail: 6, notExam: 5 },
- { label: '电气工程', pass: 98, fail: 7, notExam: 5 },
- { label: '材料科学', pass: 78, fail: 5, notExam: 5 },
- { label: '机械工程', pass: 63, fail: 5, notExam: 4 },
- { label: '自动化', pass: 59, fail: 3, notExam: 3 },
- { label: '资源环境', pass: 49, fail: 4, notExam: 3 },
- ]
- },
- chartList:[],
- spanMap: {},
- optionList:[],
- optionListA:[],
- optionListB:[],
- valueYear:'',
- checkIndex:2,
- queryParams:{
- deptId:'',
- examTypeId:'',
- },
- dataList:[
- {
- deptName:'学院1',
- examType:'类型1',
- total:'1',
- pass:'2',
- fail:'3',
- notExam:'4',
- rate:'5',
- },
- {
- deptName:'学院12',
- examType:'类型1',
- total:'1',
- pass:'2',
- fail:'3',
- notExam:'4',
- rate:'5',
- },
- {
- deptName:'学院1',
- examType:'类型2',
- total:'1',
- pass:'2',
- fail:'3',
- notExam:'4',
- rate:'5',
- },
- ],
- }
- },
- created() {
- },
- mounted() {
- this.examElExamTypeList();
- this.systemDeptCurrentDept();
- this.examElStatisticsGetAcademicYear();
- },
- beforeDestroy() {
- if (this._barChart) this._barChart.dispose()
- if (this._pieChart) this._pieChart.dispose()
- },
- methods: {
- //导出
- exportButton(){
- let self = this;
- self.$confirm(`确认导出数据?`, "提示", {
- confirmButtonText: "确定",
- cancelButtonText: "取消",
- type: "warning"
- }).then(async () => {
- let obj = {
- academicYear:this.valueYear,
- personType:this.checkIndex,
- deptId:this.queryParams.deptId,
- examTypeId:this.queryParams.examTypeId,
- };
- self.download('/exam/elStatistics/exam/export', {...obj}, '考试情况统计.xlsx')
- }).catch(() => {})
- },
- //图表
- initBarChart() {
- const groups = this.chartData.deptStats
- const chart = echarts.init(this.$refs.barChart)
- this._barChart = chart
- chart.setOption({
- legend: {
- data: ['通过', '未通过', '未考'],
- top: 10,
- itemWidth: 14,
- itemHeight: 14,
- textStyle: { fontSize: 12, color: '#555' },
- },
- grid: { top: 60, bottom: 60, left: 40, right: 10, containLabel: false },
- xAxis: {
- type: 'category',
- data: groups.map(g => g.deptName),
- axisLine: { lineStyle: { color: '#ddd' } },
- axisLabel: { fontSize: 11, color: '#666', rotate: 25 },
- axisTick: { show: false },
- },
- yAxis: {
- type: 'value',
- splitLine: { lineStyle: { color: '#f0f0f0' } },
- axisLabel: { fontSize: 11, color: '#aaa' },
- axisLine: { show: false },
- axisTick: { show: false },
- },
- series: [
- {
- name: '通过',
- type: 'bar',
- stack: 'total',
- data: groups.map(g => g.passedCount),
- itemStyle: { color: '#4fbe7e' },
- label: { show: true, position: 'inside', fontSize: 11, color: '#fff',
- formatter: (p) => p.value > 0 ? p.value : '' },
- },
- {
- name: '未通过',
- type: 'bar',
- stack: 'total',
- data: groups.map(g => g.notPassedCount),
- itemStyle: { color: '#f56c6c' },
- label: { show: true, position: 'inside', fontSize: 10, color: '#fff',
- formatter: (p) => p.value > 0 ? p.value : '' },
- },
- {
- name: '未考',
- type: 'bar',
- stack: 'total',
- data: groups.map(g => g.absentCount),
- itemStyle: { color: '#c0c4cc' },
- label: { show: true, position: 'inside', fontSize: 10, color: '#fff',
- formatter: (p) => p.value > 0 ? p.value : '' },
- },
- ],
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'shadow' },
- },
- })
- },
- //图表
- initPieChart() {
- const pass = this.chartData.totalPassed
- const fail = this.chartData.totalNotPassed
- const notExam = this.chartData.totalAbsent
- const chart = echarts.init(this.$refs.pieChart)
- this._pieChart = chart
- chart.setOption({
- legend: {
- orient: 'horizontal',
- bottom: 0,
- data: ['通过', '未通过', '未考'],
- itemWidth: 12,
- itemHeight: 12,
- textStyle: { fontSize: 12, color: '#555' },
- },
- series: [
- {
- type: 'pie',
- radius: ['45%', '68%'],
- center: ['50%', '45%'],
- label: {
- formatter: '{b}',
- fontSize: 12,
- color: '#555',
- },
- labelLine: { length: 12, length2: 8 },
- data: [
- { value: pass, name: '通过', itemStyle: { color: '#4fbe7e' } },
- { value: fail, name: '未通过', itemStyle: { color: '#f56c6c' } },
- { value: notExam, name: '未考', itemStyle: { color: '#c0c4cc' } },
- ],
- emphasis: {
- itemStyle: { shadowBlur: 6, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.2)' }
- },
- },
- ],
- tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
- })
- },
- //顶部学年切换
- topChange(){
- this.examElStatisticsExamChart();
- this.examElStatisticsExamList();
- },
- //身份切换
- checkButton(val){
- if(this.checkIndex!=val){
- this.$set(this,'checkIndex',val);
- this.examElStatisticsExamChart();
- this.examElStatisticsExamList();
- }
- },
- //条件变更
- queryParamsChange(){
- this.examElStatisticsExamList();
- },
- //数据合并
- objectSpanMethod({ row, column, rowIndex, columnIndex }) {
- // 只对第一列(学院列,columnIndex === 0)进行合并
- if (columnIndex === 0) {
- const list = this.dataList;
- // 如果是第一行,或者当前行的学院与上一行的学院不同,则开始合并计算
- if (rowIndex === 0 || row.deptName !== list[rowIndex - 1].deptName) {
- let rowspan = 1;
- // 向下遍历,统计有多少行是相同的学院
- for (let i = rowIndex + 1; i < list.length; i++) {
- if (list[i].deptName === row.deptName) {
- rowspan++;
- } else {
- break; // 遇到不同的学院,停止计算
- }
- }
- return { rowspan, colspan: 1 };
- } else {
- // 如果当前行的学院与上一行相同,则隐藏当前单元格(被合并了)
- return { rowspan: 0, colspan: 0 };
- }
- }
- // 其他列正常显示
- return { rowspan: 1, colspan: 1 };
- },
- //考试统计图表
- examElStatisticsExamChart(){
- let obj = {
- academicYear:this.valueYear,
- personType:this.checkIndex,
- }
- examElStatisticsExamChart(obj).then(response => {
- this.$set(this,'chartData',response.data);
- this.$nextTick(() => {
- if(response.data.deptStats[0]){
- this.$nextTick(() => {
- this.initBarChart()
- this.initPieChart()
- })
- }
- })
- });
- },
- //考试统计列表
- examElStatisticsExamList(){
- let obj = {
- academicYear:this.valueYear,
- personType:this.checkIndex,
- deptId:this.queryParams.deptId,
- examTypeId:this.queryParams.examTypeId,
- }
- examElStatisticsExamList(obj).then(response => {
- this.$set(this,'dataList',response.data);
- });
- },
- //查询全部考试类型
- examElExamTypeList(){
- examElExamTypeList({}).then(response => {
- this.$set(this,'optionListB',response.data);
- });
- },
- //学院列表
- systemDeptCurrentDept(){
- systemDeptCurrentDept({}).then(response => {
- const list = response.data || [];
- this.formatTreeData(list);
- this.$set(this,'optionListA',list);
- });
- },
- //学年列表
- examElStatisticsGetAcademicYear(){
- examElStatisticsGetAcademicYear({}).then(response => {
- this.$set(this,'optionList',response.data);
- this.$set(this,'valueYear',response.data[0].gradeNumber);
- this.examElStatisticsExamList();
- this.examElStatisticsExamChart();
- });
- },
- }
- }
- </script>
- <style scoped lang="scss">
- .exam-situation-card {
- background: #fff;
- /*border-radius: 6px;*/
- padding: 20px;
- margin-bottom: 12px;
- /*box-shadow: 0 1px 4px rgba(0,0,0,0.06);*/
- background: rgba(255, 255, 255, 0.95);
- border: 1px solid #e8edf5;
- border-radius: 16px;
- box-shadow: 0 4px 20px rgba(37, 50, 74, 0.06);
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
- .card-title { font-size: 16px; font-weight: 700; color: #222; }
- .header-right { width:610px;display: flex; align-items: center; gap: 20px; }
- }
- /* 分组筛选按钮 */
- .table-top-button-box {
- display: inline-block;
- vertical-align: top;
- div {
- display: flex;
- p {
- border: 1px solid #DCDFE6;
- width: 80px;
- text-align: center;
- height: 40px;
- line-height: 40px;
- cursor: pointer;
- font-size: 14px;
- &:nth-child(1) { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
- &:nth-child(2) { border-left: none; border-right: none; }
- &:nth-child(3) { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
- }
- .check-table-button{
- border: 1px solid #0183fa!important;
- background-color: #0183fa!important;;
- color:#fff!important;;
- }
- }
- }
- /* 导出按钮 */
- .export-btn {
- display: inline-block;
- padding: 0 16px;
- height: 40px;
- line-height: 40px;
- border-radius: 4px;
- font-size: 14px;
- cursor: pointer;
- background-color: #0183fa;
- color: #fff;
- border: 1px solid #0183fa;
- }
- .chart-area {
- display: flex;
- gap: 0;
- margin-bottom: 16px;
- border: 1px solid #eee;
- border-radius: 6px;
- overflow: hidden;
- .bar-chart-wrap {
- flex: 1;
- height: 280px;
- border-right: 1px solid #eee;
- }
- .pie-chart-wrap {
- width: 320px;
- height: 280px;
- }
- }
- .filter-row {
- display: flex;
- gap: 20px;
- margin-bottom: 12px;
- }
- .page-content-box{
- height:500px;
- padding:0;
- ::v-deep .el-table--medium td:nth-child(1){
- border-left:1px solid #dfe6ec!important;
- }
- }
- .link-blue { color: #409eff; }
- }
- </style>
|