| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- <!-- 考试情况统计 -->
- <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-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>
- <el-select filterable
- @change="queryParamsChange"
- v-model="queryParams.examId" clearable placeholder="请输入或选择考试名称" style="width: 360px">
- <el-option
- v-for="dict in optionListB"
- :key="dict.examId"
- :label="dict.examName"
- :value="dict.examId"
- />
- </el-select>
- <!--<el-cascader-->
- <!--v-if="adminType == 1"-->
- <!--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 v-if="adminType == 1"
- @change="queryParamsChange"
- clearable
- v-model="queryParams.deptId" placeholder="学院" style="width: 180px">
- <el-option
- v-for="dict in optionListA"
- :key="dict.deptId"
- :label="dict.deptName"
- :value="dict.deptId"
- />
- </el-select>
- <p style="line-height:40px;font-size:14px;color:#1768d5;" v-if="!queryParams.deptId">共组织常规考试 <span style="font-weight:700;">{{optionListB.length}}</span> 次</p>
- </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="expectedCount" label="应考人数" width="160" align="center" />
- <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-column label="操作" width="200" show-overflow-tooltip>
- <template slot-scope="scope">
- <div class="table-button-box">
- <p class="table-button-null"></p>
- <p class="table-button-p"
- v-if="scope.row.type != 2"
- @click="tableButton(1,scope.row)"
- >人员明细</p>
- <p class="table-button-null"></p>
- </div>
- </template>
- </el-table-column>
- </el-table>
- <pagination :page-sizes="[20, 30, 40, 50]"
- v-show="total>0"
- :total="total"
- :page.sync="queryParams.page"
- :limit.sync="queryParams.pageSize"
- @pagination="examElStatisticsExamList"
- />
- </div>
- <el-drawer
- size="100%"
- custom-class="regularExam-index-drawer"
- :append-to-body="true"
- :title="drawerTitle"
- ref="drawer"
- :visible.sync="drawer"
- @close="drawerReturnButton"
- direction="rtl">
- <userListPage :drawerPropsData="drawerPropsData" v-if="drawerType == 1"></userListPage>
- </el-drawer>
- </div>
- </template>
- <script>
- import echarts from 'echarts'
- import {
- systemUserGetPost,
- } from "@/api/basicsModules/index";
- import {
- examElStatisticsGetAcademicYear,examElStatisticsExamList,examElStatisticsExamChart,
- examElStatisticsExamTitleList,systemDeptCurrentDept,
- } from "@/api/safetyEducationExaminationNew/index";
- import userListPage from "./userListPage.vue";
- export default {
- name: 'ExamSituation',
- components: {
- userListPage,
- },
- data() {
- return {
- adminType:0,
- chartData: {
- deptStats:[],
- barGroups: []
- },
- chartList:[],
- spanMap: {},
- optionList:[],
- optionListA:[],
- optionListB:[],
- valueYear:'',
- checkIndex:2,
- queryParams:{
- page:1,
- pageSize:20,
- deptId:'',
- examId:'',
- },
- dataList:[],
- total:0,
- //弹窗相关
- drawerTitle: '',
- drawerType: '',
- drawer: false,
- drawerPropsData:{},
- }
- },
- created() {
- },
- mounted() {
- this.systemUserGetPost();
- },
- beforeDestroy() {
- if (this._barChart) this._barChart.dispose()
- if (this._pieChart) this._pieChart.dispose()
- },
- methods: {
- //操作按钮
- tableButton(type,row){
- if(type == 1){
- this.$set(this, 'drawerTitle', '常规考试明细')
- let obj = {
- adminType:this.adminType,
- valueYear:this.valueYear,
- examId:this.queryParams.examId,
- personType:this.checkIndex,
- deptId:row.deptId,
- }
- this.$set(this, 'drawerPropsData', obj)
- this.$set(this, 'drawerType', 1)
- this.$set(this, 'drawer', true)
- }
- },
- //弹窗返回
- drawerReturnButton(){
- this.$set(this, 'drawerType', 0)
- this.$refs.drawer.closeDrawer();
- },
- //查询当前登录用户身份-校级/院级
- systemUserGetPost(){
- systemUserGetPost({}).then(response => {
- for(let i=0;i<response.data.length;i++){
- if(response.data[i].postCode == 'school_level_admin'){
- if(response.data[i].isPost){
- this.$set(this,'adminType','1');
- }
- }else if(response.data[i].postCode == 'college_admin'){
- if(response.data[i].isPost){
- this.$set(this,'adminType','2');
- }
- }
- }
- this.systemDeptCurrentDept();
- this.examElStatisticsGetAcademicYear();
- });
- },
- //导出
- exportButton(){
- let self = this;
- self.$confirm(`确认导出数据?`, "提示", {
- confirmButtonText: "确定",
- cancelButtonText: "取消",
- type: "warning"
- }).then(async () => {
- let obj = {
- academicYear:this.valueYear,
- personType:this.checkIndex,
- examId:this.queryParams.examId,
- };
- if(this.adminType == 1){
- obj.deptId = this.queryParams.deptId;
- }else{
- obj.deptId = localStorage.getItem('deptId');
- }
- 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
- const PAGE_SIZE = 10 // 单页最多显示的柱体数(堆叠组数)
- chart.setOption({
- legend: {
- data: ['通过', '未通过', '未考'],
- top: 10,
- itemWidth: 14,
- itemHeight: 14,
- textStyle: { fontSize: 12, color: '#555' },
- },
- grid: { top: 60, bottom: 70, left: 40, right: 10, containLabel: false },
- xAxis: {
- type: 'category',
- data: groups.map(g => g.deptName),
- axisLine: { lineStyle: { color: '#ddd' } },
- axisLabel: {
- fontSize: 11,
- color: '#666',
- interval: 0, // 滚动过程中每个类目都显示标签
- rotate: 25,
- },
- axisTick: { show: false },
- },
- yAxis: {
- type: 'value',
- splitLine: { lineStyle: { color: '#f0f0f0' } },
- axisLabel: { fontSize: 11, color: '#aaa' },
- axisLine: { show: false },
- axisTick: { show: false },
- },
- // 新增:横向滚动
- dataZoom: [
- {
- type: 'slider', // 底部滑块,用户可拖动/点击滚动
- xAxisIndex: 0,
- startValue: 0,
- endValue: Math.min(PAGE_SIZE - 1, groups.length - 1), // 初始窗口:6 个柱体
- minValueSpan: PAGE_SIZE - 1, // 可选:限制最小窗口也是 6 个
- height: 18,
- bottom: 10,
- borderColor: '#e0e0e0',
- fillerColor: 'rgba(79, 190, 126, 0.15)',
- handleStyle: { color: '#4fbe7e' },
- moveHandleStyle: { color: '#4fbe7e' },
- textStyle: { color: '#aaa', fontSize: 10 },
- brushSelect: false,
- },
- {
- type: 'inside', // 鼠标滚轮/触摸拖动也可滚动
- xAxisIndex: 0,
- startValue: 0,
- endValue: Math.min(PAGE_SIZE - 1, groups.length - 1),
- zoomLock: true, // 锁定缩放,只允许平移滚动
- },
- ],
- series: [
- {
- name: '通过',
- type: 'bar',
- stack: 'total',
- barMaxWidth: 60, // 新增:限制最大宽度,防止数据少时柱子过粗
- data: groups.map(g => g.passedCount),
- itemStyle: { color: '#4fbe7e' },
- label: { show: true, position: 'inside', fontSize: 12, color: '#fff',
- formatter: (p) => p.value > 0 ? p.value : '' },
- },
- {
- name: '未通过',
- type: 'bar',
- stack: 'total',
- barMaxWidth: 60,
- data: groups.map(g => g.notPassedCount),
- itemStyle: { color: '#f56c6c' },
- label: { show: true, position: 'inside', fontSize: 12, color: '#fff',
- formatter: (p) => p.value > 0 ? p.value : '' },
- },
- {
- name: '未考',
- type: 'bar',
- stack: 'total',
- barMaxWidth: 60,
- data: groups.map(g => g.absentCount),
- itemStyle: { color: '#c0c4cc' },
- label: { show: true, position: 'inside', fontSize: 12, 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.examElStatisticsExamTitleList();
- this.$set(this.queryParams,'examId','');
- this.$set(this.queryParams,'page',1);
- this.examElStatisticsExamChart();
- this.examElStatisticsExamList();
- },
- //身份切换
- checkButton(val){
- if(this.checkIndex!=val){
- this.$set(this,'checkIndex',val);
- this.$set(this.queryParams,'page',1);
- this.examElStatisticsExamChart();
- this.examElStatisticsExamList();
- }
- },
- //条件变更
- queryParamsChange(){
- this.$set(this.queryParams,'page',1);
- this.examElStatisticsExamChart();
- 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,
- examId:this.queryParams.examId,
- }
- if(this.adminType == 1){
- obj.deptId = this.queryParams.deptId;
- }else if(this.adminType == 2){
- obj.deptId = localStorage.getItem('deptId');
- }
- 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 = {
- page:this.queryParams.page,
- pageSize:this.queryParams.pageSize,
- academicYear:this.valueYear,
- personType:this.checkIndex,
- examId:this.queryParams.examId,
- }
- if(this.adminType == 1){
- obj.deptId = this.queryParams.deptId;
- }else{
- obj.deptId = localStorage.getItem('deptId');
- }
- examElStatisticsExamList(obj).then(response => {
- this.$set(this,'dataList',response.data.records);
- this.$set(this,'total',response.data.total);
- });
- },
- //查询全部考试类型
- examElStatisticsExamTitleList(){
- let obj = {
- year:this.valueYear
- }
- if(this.adminType == 2){
- obj.deptId = localStorage.getItem('deptId');
- }
- examElStatisticsExamTitleList(obj).then(response => {
- this.$set(this,'optionListB',response.data);
- });
- },
- //学院列表
- systemDeptCurrentDept(){
- systemDeptCurrentDept({}).then(response => {
- const list = response.data || [];
- this.formatTreeData(list);
- if(list[0]){
- this.$set(this,'optionListA',list[0].child);
- }else{
- this.$set(this,'optionListA',[]);
- }
- });
- },
- //学年列表
- examElStatisticsGetAcademicYear(){
- examElStatisticsGetAcademicYear({}).then(response => {
- this.$set(this,'optionList',response.data);
- this.$set(this,'valueYear',response.data[0].value);
- this.examElStatisticsExamTitleList();
- 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:370px;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>
- <style lang="scss">
- .regularExam-index-drawer{
- .el-drawer__body{
- padding:0;
- }
- .el-drawer__header{
- margin:0;
- }
- }
- </style>
|