初始化

This commit is contained in:
2025-07-28 15:52:07 +08:00
commit cd0e77b332
1304 changed files with 302802 additions and 0 deletions

View File

@@ -0,0 +1,408 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
<el-form-item label="学号" prop="stuNo">
<el-input v-model="queryParams.stuNo" placeholder="请输入学号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="学生状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择学生状态" clearable style="width: 240px">
<el-option v-for="dict in dict.type.srs_stu_status" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="studentList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="学号" align="center" prop="stuNo" width="150" />
<el-table-column label="姓名" align="center" prop="name" width="100" />
<el-table-column label="性别" align="center" prop="gender" />
<el-table-column label="学院名称" align="center" prop="deptId">
<template slot-scope="scope">
<span>{{ scope.row.dept.deptName }}</span>
</template>
</el-table-column>
<el-table-column label="专业名称" align="center" prop="majorId" width="250">
<template slot-scope="scope">
<span>{{ scope.row.srsMajors.majorName }}</span>
</template>
</el-table-column>
<el-table-column label="班级名称" align="center" prop="classId" width="270">
<template slot-scope="scope">
<span>{{ scope.row.srsClass.className }}</span>
</template>
</el-table-column>
<el-table-column label="学生状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.srs_stu_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view"
@click="picVClick(scope.row)">查看报告</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" />
<el-dialog :visible.sync="picV" fullscreen append-to-body>
<look v-if="picV" :reportData="reportData" />
</el-dialog>
</div>
</template>
<script>
import { listOwnStu as listStudent, getStudent, delStudent, addStudent, updateStudent, getClassName, initPwd, initOnePwd } from "@/api/stuCQS/basedata/student";
import { getTokenKeySessionStorage } from "@/utils/auth";
import look from "@/views/diagnostic/DiaFillOutReport/look";
export default {
name: "own-Student",
dicts: ['srs_stu_status'],
components: {
look
},
props: {
reportData: {
type: Object,
required: true
}
},
data() {
return {
pic_stu_no: null,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 学生信息表格数据
studentList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
name: null,
stuNo: null,
gender: null,
birthday: null,
idCard: null,
gradeId: null,
deptId: null,
majorId: null,
classId: null,
phone: null,
address: null,
status: null,
},
// 学生导入参数
upload: {
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的学生数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getTokenKeySessionStorage() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/system/student/importData"
},
// 学生状态导入参数
upload2: {
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的学生数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getTokenKeySessionStorage() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/system/student/imporStatustData"
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [
{ required: true, message: "名字不能为空", trigger: "change" }
],
stuNo: [
{ required: true, message: "学号不能为空", trigger: "change" }
],
gender: [
{ required: true, message: "性别不能为空", trigger: "change" }
],
idCard: [
{ required: true, message: "身份证不能为空", trigger: "change" }
],
majorId: [
{ required: true, message: "所属班级不能为空", trigger: "change" }
],
phone: [
{ required: true, message: "手机号不能为空", trigger: "change" }
],
address: [
{ required: true, message: "手机号不能为空", trigger: "change" }
],
delFlag: [
{ required: true, message: "删除标志不能为空", trigger: "blur" }
]
},
ClassNameList: [],//班级名称
classVlue1: [],//班级搜索选择
classVlue2: [],//班级添加修改选择
picV: false,
token: getTokenKeySessionStorage()
};
},
created() {
this.getList();
this.getClassNameList();
},
methods: {
picVClick(row) {
this.reportData.stuNo = row.stuNo;
this.reportData.className = row.srsClass.className;
this.reportData.studentName = row.name;
this.picV = true;
},
async doInitPwdOne(val) {
let sdata = {
username: val.stuNo
}
let res = await initOnePwd(sdata);
if (res.code == 200) {
this.$message.success(res.msg);
}
},
doInitPwd() {
this.$message.info("正在初始化中,请稍候");
initPwd().then(res => {
this.$message.info(res.msg);
});
},
/** 查询学生信息列表 */
getList() {
this.loading = true;
listStudent(this.queryParams).then(response => {
this.studentList = response.rows;
this.total = response.total;
this.loading = false;
});
},
getClassNameList() {
getClassName().then(res => {
this.ClassNameList = res.data
})
},
//搜索班级选择
handleChange1(value) {
this.queryParams.classId = value[2]
console.log(value);
},
//班级选择添加修改
handleChange2(value) {
this.form.classId = value[2]
console.log(value);
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
stuId: null,
name: null,
stuNo: null,
gender: null,
birthday: null,
idCard: null,
deptId: null,
majorId: null,
classId: null,
phone: null,
address: null,
status: null,
createBy: null,
createTime: null,
updateBy: null,
updateTime: null,
delFlag: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.classVlue1 = [],
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.stuId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.classVlue2 = [],
this.open = true;
this.title = "添加学生信息";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const stuId = row.stuId || this.ids
getStudent(stuId).then(response => {
this.form = response.data;
var data = response.data;
this.classVlue2 = [data.dept.deptId, data.srsMajors.majorId, data.srsClass.classId]
console.log(this.classVlue2);
this.open = true;
this.title = "修改学生信息";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.stuId != null) {
updateStudent(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addStudent(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const stuIds = row.stuId || this.ids;
this.$modal.confirm('是否确认删除学生信息编号为"' + stuIds + '"的数据项?').then(function () {
return delStudent(stuIds);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "学生导入";
this.upload.open = true;
},
handleImportStatus() {
this.upload2.title = "学生状态导入";
this.upload2.open = true;
},
/** 导出按钮操作 */
handleExport() {
this.download('system/student/export', {
...this.queryParams
}, `student_${new Date().getTime()}.xlsx`)
},
/** 下载模板操作 */
importTemplate() {
this.download('system/student/importTemplate', {
}, `student_info_template_${new Date().getTime()}.xlsx`)
},
/** 下载模板操作 */
importStatusTemplate() {
debugger;
this.download('system/student/importTemplate2', {
}, `studentstatus_info_template_${new Date().getTime()}.xlsx`)
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
this.upload2.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
// 提交上传文件
submitFileForm() {
// console.log(this.token);
this.$refs.upload.submit();
},
// 提交上传文件
submitFileForm2() {
this.$refs.upload2.submit();
},
// 文件上传成功处理
handleFileSuccess2(response, file, fileList) {
debugger;
this.upload2.open = false;
this.upload2.isUploading = false;
this.$refs.upload2.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
}
};
</script>
<style scoped>
.stu_pic {
margin: 0px;
background: url("~@/assets/stu_pic/stu.png") no-repeat;
background-size: 100% 100%;
width: 100%;
height: 100vh;
}
</style>

View File

@@ -0,0 +1,201 @@
<template>
<div class="app-container">
<el-table :data="tableData" :span-method="objectSpanMethod" border style="width: 100%">
<!-- 维度名称列将合并相同内容 -->
<el-table-column label="维度名称" align="center" prop="dimension" width="180" />
<el-table-column label="质控点名称" align="center" prop="controlPoint" />
<el-table-column label="指标名称" align="center" prop="indicatorName" />
<el-table-column label="学校合格标准" align="center" prop="xgStandard" />
<el-table-column label="学院合格标准" align="center" prop="xwStandard" v-if="deptColV" />
<el-table-column label="在校人数" align="center" prop="stuCount" />
<el-table-column label="达成人数" align="center" prop="finishCount" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stuCount
? ((scope.row.finishCount / scope.row.stuCount) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { isEmpty } from "@/api/helpFunc";
export default {
name: "TotalForm",
props: {
tableData: {
type: Array,
required: true
}
},
data() {
return {
loading: true,
saveLoading: false,
submitLoading: false,
detailsList: [],
modifiedData: [],
spanArr: [], // 用于存储维度名称列的合并规则
qcSpanArr: [], // 用于存储质控点名称列的合并规则
queryParams: {
pageNum: 1,
pageSize: 10,
reportId: this.reportId
},
initialData: [
{
dimension: "思想成长",
controlPoint: "质控点1",
indicatorName: "指标1",
schoolQualifiedStandard: "",
secondaryCollegeStandard: "",
personalTargetStandard: ""
},
{
dimension: "思想成长",
controlPoint: "质控点2",
indicatorName: "指标2",
schoolQualifiedStandard: "",
secondaryCollegeStandard: "",
personalTargetStandard: ""
},
{
dimension: "学业发展",
controlPoint: "质控点3",
indicatorName: "指标3",
schoolQualifiedStandard: "",
secondaryCollegeStandard: "",
personalTargetStandard: ""
},
{
dimension: "学业发展",
controlPoint: "质控点4",
indicatorName: "指标4",
schoolQualifiedStandard: "",
secondaryCollegeStandard: "",
personalTargetStandard: ""
},
{
dimension: "学业发展",
controlPoint: "质控点5",
indicatorName: "指标5",
schoolQualifiedStandard: "",
secondaryCollegeStandard: "",
personalTargetStandard: ""
}
],
deptColV: false
};
},
methods: {
// 计算合并规则的方法 - 保持原始顺序
getSpanArr(data) {
this.spanArr = [];
this.qcSpanArr = [];
// 记录上一行的维度和质控点
let prevDimension = null;
let prevQcPoint = null;
// 记录当前维度和质控点的合并行数
let currentDimensionRowspan = 0;
let currentQcRowspan = 0;
for (let i = 0; i < data.length; i++) {
const current = data[i];
// 处理维度名称列的合并
if (prevDimension === null || prevDimension !== current.dimension) {
// 新维度,开始新的合并组
this.spanArr.push(1);
currentDimensionRowspan = 1;
prevDimension = current.dimension;
} else {
// 延续当前维度的合并组
this.spanArr[i - currentDimensionRowspan] += 1;
this.spanArr.push(0);
currentDimensionRowspan += 1;
}
// 处理质控点名称列的合并
if (prevDimension !== current.dimension || prevQcPoint !== current.controlPoint) {
// 新维度或新质控点,开始新的合并组
this.qcSpanArr.push(1);
currentQcRowspan = 1;
prevQcPoint = current.controlPoint;
} else {
// 延续当前质控点的合并组
this.qcSpanArr[i - currentQcRowspan] += 1;
this.qcSpanArr.push(0);
currentQcRowspan += 1;
}
}
},
// 合并单元格的方法
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
const _row = this.spanArr[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
};
} else if (columnIndex === 1) {
const _row = this.qcSpanArr[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
};
}
// 其他列不合并
return { rowspan: 1, colspan: 1 };
},
formatDate(date) {
const d = new Date(date);
return [
d.getFullYear(),
String(d.getMonth() + 1).padStart(2, '0'),
String(d.getDate()).padStart(2, '0')
].join('-') + ' ' + [
String(d.getHours()).padStart(2, '0'),
String(d.getMinutes()).padStart(2, '0'),
String(d.getSeconds()).padStart(2, '0')
].join(':');
},
},
created() {
if (!isEmpty(this.tableData)) {
if (!isEmpty(this.tableData[0].xwStandard)) {
this.deptColV = true;
}
}
this.getSpanArr(this.tableData);
}
};
</script>
<style scoped>
.app-container {
padding: 20px;
}
.action-buttons {
margin-top: 20px;
text-align: center;
}
</style>

View File

@@ -0,0 +1,534 @@
<template>
<div class="app-container">
<el-card class="box-card">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="120px">
<el-form-item label="报告" prop="relId">
<el-select v-model="queryParams.relId" placeholder="请选择报告">
<el-option v-for="item in relList" :key="item.relId" :label="item.reportName"
:value="item.relId"></el-option>
</el-select>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" icon="el-icon-search" plain @click="queryData">查询</el-button>
<el-button type="success" icon="el-icon-view" plain @click="lookVClick">查看学生数据</el-button>
</el-form-item>
<el-form-item label="班级" prop="classId">
<el-select v-model="queryParams.classId" placeholder="请选择班级" filterable clearable>
<el-option v-for="(v, i) in search_class_list" :key="i" :label="v.className" :value="v.classId">
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-card>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>报告达成率报告的全部指标都达成的情况</span>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<div class="chart-container">
<div ref="reportFinishChart" style="height: 400px"></div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>指标达成率学生达成了多少个指标</span>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<div class="chart-container">
<div ref="itemFinishChart" style="height: 400px"></div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各班级报告达成率</span>
<el-button @click='exportClassFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedClassFinishList">
<el-table-column label="班级" width="140" align="center" prop="class_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageClassFinish" :page-size="pageSizeClassFinish"
:total="classFinishList.length" @current-change="handleClassFinishPageChange"
@size-change="handleClassFinishSizeChange" :page-sizes="[10, 20, 50, 100]" :pager-count="5"
style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各班级指标达成率</span>
<el-button @click='exportClassItemFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedClassItemFinishList">
<el-table-column label="班级" width="140" align="center" prop="class_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageClassItemFinish" :page-size="pageSizeClassItemFinish"
:total="classItemFinishList.length" @current-change="handleClassItemFinishPageChange"
@size-change="handleClassItemFinishSizeChange" :page-sizes="[10, 20, 50, 100]"
:pager-count="5" style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
</el-row>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>指标达成情况</span>
<el-button @click='exportTable("TotalForm", searchRelName
+ "--" + searchClassName + "指标达成情况")' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<TotalForm id="TotalForm" v-if="tableV" :tableData="tableData" />
</el-card>
<table id="tempTable">
</table>
<el-dialog :visible.sync="lookV" fullscreen append-to-body>
<FdyList v-if="lookV" :reportData="lookData" />
</el-dialog>
</div>
</template>
<script>
import {
countFdyItemFinishByRelId as countItemFinishByRelId,
countFdyFinishNumByRelId as countFinishNumByRelId,
countFdyItemFinishNumByRelId as countItemFinishNumByRelId,
countFdyClassFinishNumByRelId as countClassFinishNumByRelId,
countFdyClassItemFinishNumByRelId as countClassItemFinishNumByRelId
} from "@/api/diagnostic/reportStandards";
import { listAllRelease } from '@/api/diagnostic/DiaReportRelease';
import { listOwnClass as listClass } from "@/api/stuCQS/info-fill/stu_eva_task";
import { isEmpty, fullLoading } from '@/api/helpFunc';
import TotalForm from './cpnt/TotalForm';
import FdyList from "./cpnt/FdyList.vue";
import * as echarts from 'echarts';
import XLSX from 'xlsx';
export default {
name: "DataView",
components: {
TotalForm,
FdyList
},
data() {
return {
isEmpty,
queryParams: {
relId: null
},
relList: [],
gradeList: [],
search_major_list: [],
search_class_list: [],
tableData: [],
tableV: false,
reportFinishChart: null,
itemFinishChart: null,
majorItemFinishList: [],
majorFinishList: [],
gradeItemFinishList: [],
gradeFinishList: [],
classItemFinishList: [],
classFinishList: [],
searchRelName: "",
searchMajorName: "",
searchClassName: "",
searchGradeName: "",
currentPageMajorFinish: 1,
pageSizeMajorFinish: 10,
currentPageMajorItemFinish: 1,
pageSizeMajorItemFinish: 10,
currentPageClassFinish: 1,
pageSizeClassFinish: 10,
currentPageClassItemFinish: 1,
pageSizeClassItemFinish: 10,
lookV: false,
lookData: {},
}
},
created() {
this.listAllRelease();
this.listClass();
},
mounted() {
this.initCharts();
},
computed: {
pagedClassFinishList() {
const start = (this.currentPageClassFinish - 1) * this.pageSizeClassFinish;
const end = start + this.pageSizeClassFinish;
return this.classFinishList.slice(start, end);
},
pagedClassItemFinishList() {
const start = (this.currentPageClassItemFinish - 1) * this.pageSizeClassItemFinish;
const end = start + this.pageSizeClassItemFinish;
return this.classItemFinishList.slice(start, end);
},
},
methods: {
lookVClick() {
if (isEmpty(this.queryParams.relId)) {
this.$message.warning("请先选择报告");
return;
}
this.lookData = {};
let reportName = this.relList.find(item => item.relId == this.queryParams.relId).reportName;
let reportId = this.relList.find(item => item.relId == this.queryParams.relId).reportId;
let sdata = {
relId: this.queryParams.relId,
reportName,
reportId
}
this.lookData = sdata;
this.lookV = true;
},
exportClassFinishList() {
let classFinishList = [...this.classFinishList];
const data = [
['学院', '专业', '班级', '在校生数', '达成数', '达成率']
];
classFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.class_name,
row.stu_num,
row.finish_num,
row.stu_num
? ((row.finish_num / row.stu_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各班级报告达成统计');
let title = this.searchRelName + "--" + this.searchMajorName;
XLSX.writeFile(workbook, title + '各班级报告达成统计.xlsx');
},
exportClassItemFinishList() {
let classItemFinishList = [...this.classItemFinishList];
const data = [
['学院', '专业', '班级', '在校生数', '总指标数', '达成数', '达成率']
];
classItemFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.class_name,
row.stu_num,
row.total_num,
row.finish_num,
row.total_num
? ((row.finish_num / row.total_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各班级指标达成统计');
let title = this.searchRelName + "--" + this.searchMajorName;
XLSX.writeFile(workbook, title + '各班级指标达成统计.xlsx');
},
exportTable(id, title) {
let thead = document.getElementById(id).getElementsByTagName('thead')[0];
let tbody = document.getElementById(id).getElementsByTagName('tbody')[0];
let tempTable = document.getElementById('tempTable');
if (thead) {
tempTable.appendChild(thead.cloneNode(true))
}
if (tbody) {
Array.from(tbody.getElementsByTagName('td')).forEach(td => {
if (td.innerText.trim().endsWith('%')) {
td.innerHTML = `<span style="mso-number-format:'\\@';">${td.innerText}</span>`;
}
});
tempTable.appendChild(tbody.cloneNode(true))
}
const wb = XLSX.utils.table_to_book(tempTable, { sheet: 'Sheet1', cellText: true })
XLSX.writeFile(wb, title + ".xlsx");
tempTable.innerHTML = '';
},
handleClassItemFinishPageChange(page) {
this.currentPageClassItemFinish = page;
},
handleClassItemFinishSizeChange(size) {
this.pageSizeClassItemFinish = size;
this.currentPageClassItemFinish = 1;
},
handleClassFinishPageChange(page) {
this.currentPageClassFinish = page;
},
handleClassFinishSizeChange(size) {
this.pageSizeClassFinish = size;
this.currentPageClassFinish = 1;
},
async queryData() {
let sdata = { ...this.queryParams };
if (isEmpty(sdata.relId)) {
this.$message.info("请选择报告");
return;
}
this.searchRelName = this.relList.find(item => item.relId == sdata.relId).reportName;
if (!isEmpty(sdata.classId)) {
this.searchClassName = this.search_class_list.find(item => item.classId == sdata.classId).className;
}
// 只开一次 loading
let loading = fullLoading(this);
// 并行等待接口
try {
await Promise.all([
this.countFinishNumByRelId(true), // 传入参数表示不在内部开loading
this.countItemFinishByRelId(true),
this.countItemFinishNumByRelId(true),
this.countClassFinishNumByRelId(true),
this.countClassItemFinishNumByRelId(true),
]);
} finally {
loading.close();
}
},
async countClassFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countClassFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.classFinishList = [...res.data];
}
},
async countClassItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countClassItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.classItemFinishList = [...res.data];
}
},
async countItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
let temp = { ...res.data };
let data = [];
data.push({ value: temp.finish_num, name: '已达成' });
data.push({ value: temp.total_num - temp.finish_num, name: '未达成' });
this.renderPieChart(this.itemFinishChart, '指标达成率', data);
}
},
async countFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
let temp = { ...res.data };
let data = [];
data.push({ value: temp.finish_num, name: '已达成' });
data.push({ value: temp.stu_num - temp.finish_num, name: '未达成' });
this.renderPieChart(this.reportFinishChart, '报告达成率', data);
}
},
async countItemFinishByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let sdata = { ...this.queryParams };
this.tableV = false;
let res = await countItemFinishByRelId(sdata);
if (loading) loading.close();
if (res.code == 200) {
this.tableV = true;
this.tableData = [...res.data];
}
},
renderPieChart(chart, title, data) {
// 添加数据检查
if (!data || !Array.isArray(data)) {
console.warn('数据格式不正确或为空:', data);
data = []; // 设置为空数组,避免报错
}
const option = {
title: {
text: title,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '达成情况',
type: 'pie',
radius: '50%',
center: ['70%', '50%'],
data: [...data],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
// 如果数据为空,显示暂无数据
if (data.length === 0) {
option.title.subtext = '暂无数据';
}
chart.setOption(option);
},
initCharts() {
if (this.$refs.reportFinishChart && this.$refs.itemFinishChart) {
this.reportFinishChart = echarts.init(this.$refs.reportFinishChart);
this.itemFinishChart = echarts.init(this.$refs.itemFinishChart);
}
},
async listClass() {
let res = await listClass();
if (res.code == 200) {
this.search_class_list = [...res.data];
}
},
async listAllRelease() {
let res = await listAllRelease();
if (res.code == 200) {
this.relList = [...res.data];
}
},
}
}
</script>
<style scoped lang="scss">
.box-card {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.chart-container {
background: #fff;
padding: 10px;
border-radius: 5px;
}
.mt20 {
margin-top: 20px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: '';
}
.clearfix:after {
clear: both;
}
</style>

View File

@@ -0,0 +1,739 @@
<template>
<div class="app-container">
<el-card class="box-card">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="120px">
<el-form-item label="报告" prop="relId">
<el-select v-model="queryParams.relId" placeholder="请选择报告">
<el-option v-for="item in relList" :key="item.relId" :label="item.reportName"
:value="item.relId"></el-option>
</el-select>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" icon="el-icon-search" plain @click="queryData">查询</el-button>
</el-form-item>
<el-form-item label="年级" prop="gradeId">
<el-select v-model="queryParams.gradeId" placeholder="请选择年级" clearable>
<el-option v-for="item in gradeList" :key="item.gradeId" :label="item.gradeName"
:value="item.gradeId"></el-option>
</el-select>
</el-form-item>
<el-form-item label="学院" prop="deptId">
<el-select size="mini" @change="changeSearchDept" v-model="queryParams.deptId" placeholder="请选择学院"
clearable>
<el-option v-for="(v, i) in deptList" :key="i" :label="v.label" :value="v.value" />
</el-select>
</el-form-item>
<el-form-item label="专业" prop="majorId">
<el-select @change="changeSearchMajor" v-model="queryParams.majorId" placeholder="请先选择学院再选择专业"
filterable clearable>
<el-option v-for="(v, i) in search_major_list" :key="i" :label="v.majorName" :value="v.majorId">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="班级" prop="classId">
<el-select v-model="queryParams.classId" placeholder="请先选择专业再选择班级" filterable clearable>
<el-option v-for="(v, i) in search_class_list" :key="i" :label="v.className" :value="v.classId">
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-card>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>报告达成率报告的全部指标都达成的情况</span>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<div class="chart-container">
<div ref="reportFinishChart" style="height: 400px"></div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>指标达成率学生达成了多少个指标</span>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<div class="chart-container">
<div ref="itemFinishChart" style="height: 400px"></div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各年级报告达成率可根据学院专业查询</span>
<el-button
@click='exportTable("gradeFinishList", searchRelName + "--" + searchDeptName + "--" + searchMajorName + "各年级报告达成率")'
style="float: right;" icon="el-icon-download" type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}
</span>
</div>
<div class="chart-container">
<el-table id="gradeFinishList" :data="gradeFinishList">
<el-table-column label="年级" width="140" align="center" prop="grade_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各年级指标达成率可根据学院专业查询</span>
<el-button
@click='exportTable("gradeItemFinishList", searchRelName + "--" + searchDeptName + "--" + searchMajorName + "各年级指标达成率")'
style="float: right;" icon="el-icon-download" type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}
</span>
</div>
<div class="chart-container">
<el-table id="gradeItemFinishList" :data="gradeItemFinishList">
<el-table-column label="年级" width="140" align="center" prop="grade_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各学院报告达成率可根据年级查询</span>
<el-button
@click='exportTable("deptFinishList", searchRelName + "--" + searchGradeName + "各学院报告达成率")'
style="float: right;" icon="el-icon-download" type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}
</span>
</div>
<div class="chart-container">
<el-table id="deptFinishList" :data="deptFinishList">
<el-table-column label="学院" width="140" align="center" prop="dept_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各学院指标达成率可根据年级查询</span>
<el-button
@click='exportTable("deptItemFinishList", searchRelName + "--" + searchGradeName + "各学院指标达成率")'
style="float: right;" icon="el-icon-download" type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}
</span>
</div>
<div class="chart-container">
<el-table id="deptItemFinishList" :data="deptItemFinishList">
<el-table-column label="学院" width="140" align="center" prop="dept_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各专业报告达成率可根据年级学院查询</span>
<el-button @click='exportMajorFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedMajorFinishList">
<el-table-column label="学院" width="140" align="center" prop="dept_name" />
<el-table-column label="专业" width="140" align="center" prop="major_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageMajorFinish" :page-size="pageSizeMajorFinish"
:total="majorFinishList.length" @current-change="handleMajorFinishPageChange"
@size-change="handleMajorFinishSizeChange" :page-sizes="[10, 20, 50, 100]" :pager-count="5"
style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各专业指标达成率可根据年级学院查询</span>
<el-button @click='exportMajorItemFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedMajorItemFinishList">
<el-table-column label="学院" width="140" align="center" prop="dept_name" />
<el-table-column label="专业" width="140" align="center" prop="major_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageMajorItemFinish" :page-size="pageSizeMajorItemFinish"
:total="majorItemFinishList.length" @current-change="handleMajorItemFinishPageChange"
@size-change="handleMajorItemFinishSizeChange" :page-sizes="[10, 20, 50, 100]"
:pager-count="5" style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
</el-row>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>指标达成情况</span>
<el-button @click='exportTable("TotalForm", searchRelName + "--" + searchGradeName
+ "--" + searchDeptName
+ "--" + searchMajorName
+ "--" + searchClassName + "指标达成情况")' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchDeptName) ? "" : searchDeptName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<TotalForm id="TotalForm" v-if="tableV" :tableData="tableData" />
</el-card>
<table id="tempTable">
</table>
</div>
</template>
<script>
import {
countItemFinishByRelId, countFinishNumByRelId, countItemFinishNumByRelId, countDeptItemFinishNumByRelId, countDeptFinishNumByRelId,
countMajorFinishNumByRelId, countMajorItemFinishNumByRelId, countGradeFinishNumByRelId, countGradeItemFinishNumByRelId
} from "@/api/diagnostic/reportStandards";
import { listAllRelease } from '@/api/diagnostic/DiaReportRelease';
import { listEnableGrade } from '@/api/stuCQS/basedata/grade';
import { getDeptName } from "@/api/system/dept";
import { listMajors } from "@/api/stuCQS/basedata/majors";
import { listClass } from "@/api/stuCQS/basedata/class";
import { isEmpty, fullLoading } from '@/api/helpFunc';
import TotalForm from './cpnt/TotalForm';
import * as echarts from 'echarts';
import XLSX from 'xlsx';
export default {
name: "DataView",
components: {
TotalForm
},
data() {
return {
isEmpty,
queryParams: {
relId: null
},
relList: [],
gradeList: [],
deptList: [],
search_major_list: [],
search_class_list: [],
tableData: [],
tableV: false,
reportFinishChart: null,
itemFinishChart: null,
deptItemFinishList: [],
deptFinishList: [],
majorItemFinishList: [],
majorFinishList: [],
gradeItemFinishList: [],
gradeFinishList: [],
searchRelName: "",
searchDeptName: "",
searchMajorName: "",
searchClassName: "",
searchGradeName: "",
currentPageMajorFinish: 1,
pageSizeMajorFinish: 10,
currentPageMajorItemFinish: 1,
pageSizeMajorItemFinish: 10,
}
},
created() {
this.listAllRelease();
this.listEnableGrade();
this.listDept();
},
mounted() {
this.initCharts();
},
computed: {
pagedMajorFinishList() {
const start = (this.currentPageMajorFinish - 1) * this.pageSizeMajorFinish;
const end = start + this.pageSizeMajorFinish;
return this.majorFinishList.slice(start, end);
},
pagedMajorItemFinishList() {
const start = (this.currentPageMajorItemFinish - 1) * this.pageSizeMajorItemFinish;
const end = start + this.pageSizeMajorItemFinish;
return this.majorItemFinishList.slice(start, end);
},
},
methods: {
exportMajorFinishList() {
let majorFinishList = [...this.majorFinishList];
const data = [
['学院', '专业', '在校生数', '达成数', '达成率']
];
majorFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.stu_num,
row.finish_num,
row.stu_num
? ((row.finish_num / row.stu_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各专业报告达成统计');
let title = this.searchRelName + "--" + this.searchGradeName + "--" + this.searchDeptName;
XLSX.writeFile(workbook, title + '各专业报告达成统计.xlsx');
},
exportMajorItemFinishList() {
let majorItemFinishList = [...this.majorItemFinishList];
const data = [
['学院', '专业', '在校生数', '总指标数', '达成数', '达成率']
];
majorItemFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.stu_num,
row.total_num,
row.finish_num,
row.total_num
? ((row.finish_num / row.total_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各专业指标达成统计');
let title = this.searchRelName + "--" + this.searchGradeName + "--" + this.searchDeptName;
XLSX.writeFile(workbook, title + '各专业指标达成统计.xlsx');
},
exportTable(id, title) {
let thead = document.getElementById(id).getElementsByTagName('thead')[0];
let tbody = document.getElementById(id).getElementsByTagName('tbody')[0];
let tempTable = document.getElementById('tempTable');
if (thead) {
tempTable.appendChild(thead.cloneNode(true))
}
if (tbody) {
Array.from(tbody.getElementsByTagName('td')).forEach(td => {
if (td.innerText.trim().endsWith('%')) {
td.innerHTML = `<span style="mso-number-format:'\\@';">${td.innerText}</span>`;
}
});
tempTable.appendChild(tbody.cloneNode(true))
}
const wb = XLSX.utils.table_to_book(tempTable, { sheet: 'Sheet1', cellText: true })
XLSX.writeFile(wb, title + ".xlsx");
tempTable.innerHTML = '';
},
handleMajorItemFinishPageChange(page) {
this.currentPageMajorItemFinish = page;
},
handleMajorItemFinishSizeChange(size) {
this.pageSizeMajorItemFinish = size;
this.currentPageMajorItemFinish = 1;
},
handleMajorFinishPageChange(page) {
this.currentPageMajorFinish = page;
},
handleMajorFinishSizeChange(size) {
this.pageSizeMajorFinish = size;
this.currentPageMajorFinish = 1;
},
async queryData() {
let sdata = { ...this.queryParams };
if (isEmpty(sdata.relId)) {
this.$message.info("请选择报告");
return;
}
this.searchRelName = this.relList.find(item => item.relId == sdata.relId).reportName;
if (!isEmpty(sdata.deptId)) {
this.searchDeptName = this.deptList.find(item => item.value == sdata.deptId).label;
}
if (!isEmpty(sdata.majorId)) {
this.searchMajorName = this.search_major_list.find(item => item.majorId == sdata.majorId).majorName;
}
if (!isEmpty(sdata.classId)) {
this.searchClassName = this.search_class_list.find(item => item.classId == sdata.classId).className;
}
if (!isEmpty(sdata.gradeId)) {
this.searchGradeName = this.gradeList.find(item => item.gradeId == sdata.gradeId).gradeName;
}
// 只开一次 loading
let loading = fullLoading(this);
// 并行等待接口
try {
await Promise.all([
this.countFinishNumByRelId(true), // 传入参数表示不在内部开loading
this.countItemFinishByRelId(true),
this.countItemFinishNumByRelId(true),
this.countDeptItemFinishNumByRelId(true),
this.countDeptFinishNumByRelId(true),
this.countMajorFinishNumByRelId(true),
this.countMajorItemFinishNumByRelId(true),
this.countGradeFinishNumByRelId(true),
this.countGradeItemFinishNumByRelId(true),
]);
} finally {
loading.close();
}
},
async countGradeItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countGradeItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.gradeItemFinishList = [...res.data];
}
},
async countGradeFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countGradeFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.gradeFinishList = [...res.data];
}
},
async countMajorItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countMajorItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.majorItemFinishList = [...res.data];
}
},
async countMajorFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countMajorFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.majorFinishList = [...res.data];
}
},
async countDeptFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countDeptFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.deptFinishList = [...res.data];
}
},
async countDeptItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countDeptItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.deptItemFinishList = [...res.data];
}
},
async countItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
let temp = { ...res.data };
let data = [];
data.push({ value: temp.finish_num, name: '已达成' });
data.push({ value: temp.total_num - temp.finish_num, name: '未达成' });
this.renderPieChart(this.itemFinishChart, '指标达成率', data);
}
},
async countFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
let temp = { ...res.data };
let data = [];
data.push({ value: temp.finish_num, name: '已达成' });
data.push({ value: temp.stu_num - temp.finish_num, name: '未达成' });
this.renderPieChart(this.reportFinishChart, '报告达成率', data);
}
},
async countItemFinishByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let sdata = { ...this.queryParams };
this.tableV = false;
let res = await countItemFinishByRelId(sdata);
if (loading) loading.close();
if (res.code == 200) {
this.tableV = true;
this.tableData = [...res.data];
}
},
renderPieChart(chart, title, data) {
// 添加数据检查
if (!data || !Array.isArray(data)) {
console.warn('数据格式不正确或为空:', data);
data = []; // 设置为空数组,避免报错
}
const option = {
title: {
text: title,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '达成情况',
type: 'pie',
radius: '50%',
center: ['70%', '50%'],
data: [...data],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
// 如果数据为空,显示暂无数据
if (data.length === 0) {
option.title.subtext = '暂无数据';
}
chart.setOption(option);
},
initCharts() {
if (this.$refs.reportFinishChart && this.$refs.itemFinishChart) {
this.reportFinishChart = echarts.init(this.$refs.reportFinishChart);
this.itemFinishChart = echarts.init(this.$refs.itemFinishChart);
}
},
async changeSearchMajor() {
let sdata = {
pageNum: 1,
pageSize: 100,
majorId: this.queryParams.majorId
}
let res = await listClass(sdata);
if (res.code == 200) {
this.search_class_list = [...res.rows];
}
},
async changeSearchDept() {
let sdata = {
collegeId: this.queryParams.deptId,
pageNum: 1,
pageSize: 100
}
let res = await listMajors(sdata);
this.search_major_list = [...res.rows];
},
async listDept() {
let res = await getDeptName();
this.deptList = [...res.data];
},
async listAllRelease() {
let res = await listAllRelease();
if (res.code == 200) {
this.relList = [...res.data];
}
},
async listEnableGrade() {
let res = await listEnableGrade();
if (res.code == 200) {
this.gradeList = [...res.data];
}
}
}
}
</script>
<style scoped lang="scss">
.box-card {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.chart-container {
background: #fff;
padding: 10px;
border-radius: 5px;
}
.mt20 {
margin-top: 20px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: '';
}
.clearfix:after {
clear: both;
}
</style>

View File

@@ -0,0 +1,812 @@
<template>
<div class="app-container">
<el-card class="box-card">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="120px">
<el-form-item label="报告" prop="relId">
<el-select v-model="queryParams.relId" placeholder="请选择报告">
<el-option v-for="item in relList" :key="item.relId" :label="item.reportName"
:value="item.relId"></el-option>
</el-select>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" icon="el-icon-search" plain @click="queryData">查询</el-button>
</el-form-item>
<el-form-item label="年级" prop="gradeId">
<el-select v-model="queryParams.gradeId" placeholder="请选择年级" clearable>
<el-option v-for="item in gradeList" :key="item.gradeId" :label="item.gradeName"
:value="item.gradeId"></el-option>
</el-select>
</el-form-item>
<el-form-item label="专业" prop="majorId">
<el-select @change="changeSearchMajor" v-model="queryParams.majorId" placeholder="请选择专业" filterable
clearable>
<el-option v-for="(v, i) in search_major_list" :key="i" :label="v.majorName" :value="v.majorId">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="班级" prop="classId">
<el-select v-model="queryParams.classId" placeholder="请先选择专业再选择班级" filterable clearable>
<el-option v-for="(v, i) in search_class_list" :key="i" :label="v.className" :value="v.classId">
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-card>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>报告达成率报告的全部指标都达成的情况</span>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<div class="chart-container">
<div ref="reportFinishChart" style="height: 400px"></div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>指标达成率学生达成了多少个指标</span>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<div class="chart-container">
<div ref="itemFinishChart" style="height: 400px"></div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各年级报告达成率可根据专业查询</span>
<el-button
@click='exportTable("gradeFinishList", searchRelName + "--" + searchMajorName + "各年级报告达成率")'
style="float: right;" icon="el-icon-download" type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}
</span>
</div>
<div class="chart-container">
<el-table id="gradeFinishList" :data="gradeFinishList">
<el-table-column label="年级" width="140" align="center" prop="grade_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各年级指标达成率可根据专业查询</span>
<el-button
@click='exportTable("gradeItemFinishList", searchRelName + "--" + searchMajorName + "各年级指标达成率")'
style="float: right;" icon="el-icon-download" type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}
</span>
</div>
<div class="chart-container">
<el-table id="gradeItemFinishList" :data="gradeItemFinishList">
<el-table-column label="年级" width="140" align="center" prop="grade_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各专业报告达成率可根据年级查询</span>
<el-button @click='exportMajorFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedMajorFinishList">
<el-table-column label="专业" width="140" align="center" prop="major_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageMajorFinish" :page-size="pageSizeMajorFinish"
:total="majorFinishList.length" @current-change="handleMajorFinishPageChange"
@size-change="handleMajorFinishSizeChange" :page-sizes="[10, 20, 50, 100]" :pager-count="5"
style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各专业指标达成率可根据年级查询</span>
<el-button @click='exportMajorItemFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedMajorItemFinishList">
<el-table-column label="专业" width="140" align="center" prop="major_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageMajorItemFinish" :page-size="pageSizeMajorItemFinish"
:total="majorItemFinishList.length" @current-change="handleMajorItemFinishPageChange"
@size-change="handleMajorItemFinishSizeChange" :page-sizes="[10, 20, 50, 100]"
:pager-count="5" style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="mt20">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各班级报告达成率可根据年级专业查询</span>
<el-button @click='exportClassFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedClassFinishList">
<el-table-column label="班级" width="140" align="center" prop="class_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.stu_num
? ((scope.row.finish_num / scope.row.stu_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageClassFinish" :page-size="pageSizeClassFinish"
:total="classFinishList.length" @current-change="handleClassFinishPageChange"
@size-change="handleClassFinishSizeChange" :page-sizes="[10, 20, 50, 100]" :pager-count="5"
style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>各班级指标达成率可根据年级专业查询</span>
<el-button @click='exportClassItemFinishList()' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}
</span>
</div>
<div class="chart-container">
<el-table :data="pagedClassItemFinishList">
<el-table-column label="班级" width="140" align="center" prop="class_name" />
<el-table-column label="在校生数" align="center" prop="stu_num" />
<el-table-column label="总指标数" align="center" prop="total_num" />
<el-table-column label="达成数" align="center" prop="finish_num" />
<el-table-column label="达成率" align="center">
<template slot-scope="scope">
{{
scope.row.total_num
? ((scope.row.finish_num / scope.row.total_num) * 100).toFixed(4)
: '0.0000'
}}
</template>
</el-table-column>
</el-table>
<el-pagination background layout="sizes, prev, pager, next"
:current-page="currentPageClassItemFinish" :page-size="pageSizeClassItemFinish"
:total="classItemFinishList.length" @current-change="handleClassItemFinishPageChange"
@size-change="handleClassItemFinishSizeChange" :page-sizes="[10, 20, 50, 100]"
:pager-count="5" style="text-align: right; margin-top: 20px;" />
</div>
</el-card>
</el-col>
</el-row>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>指标达成情况</span>
<el-button @click='exportTable("TotalForm", searchRelName + "--" + searchGradeName
+ "--" + searchMajorName
+ "--" + searchClassName + "指标达成情况")' style="float: right;" icon="el-icon-download"
type="text">导出</el-button>
<br />
<span style="font-size: 10px;color: red;">
当前数据:{{ isEmpty(searchRelName) ? "" : searchRelName }}--
{{ isEmpty(searchGradeName) ? "" : searchGradeName }}--
{{ isEmpty(searchMajorName) ? "" : searchMajorName }}--
{{ isEmpty(searchClassName) ? "" : searchClassName }}
</span>
</div>
<TotalForm id="TotalForm" v-if="tableV" :tableData="tableData" />
</el-card>
<table id="tempTable">
</table>
</div>
</template>
<script>
import {
countXwItemFinishByRelId as countItemFinishByRelId,
countXwFinishNumByRelId as countFinishNumByRelId,
countXwItemFinishNumByRelId as countItemFinishNumByRelId,
countXwMajorFinishNumByRelId as countMajorFinishNumByRelId,
countXwMajorItemFinishNumByRelId as countMajorItemFinishNumByRelId,
countXwGradeFinishNumByRelId as countGradeFinishNumByRelId,
countXwGradeItemFinishNumByRelId as countGradeItemFinishNumByRelId,
countXwClassFinishNumByRelId as countClassFinishNumByRelId,
countXwClassItemFinishNumByRelId as countClassItemFinishNumByRelId
} from "@/api/diagnostic/reportStandards";
import { listAllRelease } from '@/api/diagnostic/DiaReportRelease';
import { listEnableGrade } from '@/api/stuCQS/basedata/grade';
import { listOwnMajor as listMajors } from "@/api/stuCQS/info-fill/cqScore";
import { listClass } from "@/api/stuCQS/basedata/class";
import { isEmpty, fullLoading } from '@/api/helpFunc';
import TotalForm from './cpnt/TotalForm';
import * as echarts from 'echarts';
import XLSX from 'xlsx';
export default {
name: "DataView",
components: {
TotalForm
},
data() {
return {
isEmpty,
queryParams: {
relId: null
},
relList: [],
gradeList: [],
search_major_list: [],
search_class_list: [],
tableData: [],
tableV: false,
reportFinishChart: null,
itemFinishChart: null,
majorItemFinishList: [],
majorFinishList: [],
gradeItemFinishList: [],
gradeFinishList: [],
classItemFinishList: [],
classFinishList: [],
searchRelName: "",
searchMajorName: "",
searchClassName: "",
searchGradeName: "",
currentPageMajorFinish: 1,
pageSizeMajorFinish: 10,
currentPageMajorItemFinish: 1,
pageSizeMajorItemFinish: 10,
currentPageClassFinish: 1,
pageSizeClassFinish: 10,
currentPageClassItemFinish: 1,
pageSizeClassItemFinish: 10,
}
},
created() {
this.listAllRelease();
this.listEnableGrade();
this.listMajors();
},
mounted() {
this.initCharts();
},
computed: {
pagedMajorFinishList() {
const start = (this.currentPageMajorFinish - 1) * this.pageSizeMajorFinish;
const end = start + this.pageSizeMajorFinish;
return this.majorFinishList.slice(start, end);
},
pagedMajorItemFinishList() {
const start = (this.currentPageMajorItemFinish - 1) * this.pageSizeMajorItemFinish;
const end = start + this.pageSizeMajorItemFinish;
return this.majorItemFinishList.slice(start, end);
},
pagedClassFinishList() {
const start = (this.currentPageClassFinish - 1) * this.pageSizeClassFinish;
const end = start + this.pageSizeClassFinish;
return this.classFinishList.slice(start, end);
},
pagedClassItemFinishList() {
const start = (this.currentPageClassItemFinish - 1) * this.pageSizeClassItemFinish;
const end = start + this.pageSizeClassItemFinish;
return this.classItemFinishList.slice(start, end);
},
},
methods: {
exportClassFinishList() {
let classFinishList = [...this.classFinishList];
const data = [
['学院', '专业', '班级', '在校生数', '达成数', '达成率']
];
classFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.class_name,
row.stu_num,
row.finish_num,
row.stu_num
? ((row.finish_num / row.stu_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各班级报告达成统计');
let title = this.searchRelName + "--" + this.searchGradeName + "--" + this.searchMajorName;
XLSX.writeFile(workbook, title + '各班级报告达成统计.xlsx');
},
exportClassItemFinishList() {
let classItemFinishList = [...this.classItemFinishList];
const data = [
['学院', '专业', '班级', '在校生数', '总指标数', '达成数', '达成率']
];
classItemFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.class_name,
row.stu_num,
row.total_num,
row.finish_num,
row.total_num
? ((row.finish_num / row.total_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各班级指标达成统计');
let title = this.searchRelName + "--" + this.searchGradeName + "--" + this.searchMajorName;
XLSX.writeFile(workbook, title + '各班级指标达成统计.xlsx');
},
exportMajorFinishList() {
let majorFinishList = [...this.majorFinishList];
const data = [
['学院', '专业', '在校生数', '达成数', '达成率']
];
majorFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.stu_num,
row.finish_num,
row.stu_num
? ((row.finish_num / row.stu_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各专业报告达成统计');
let title = this.searchRelName + "--" + this.searchGradeName;
XLSX.writeFile(workbook, title + '各专业报告达成统计.xlsx');
},
exportMajorItemFinishList() {
let majorItemFinishList = [...this.majorItemFinishList];
const data = [
['学院', '专业', '在校生数', '总指标数', '达成数', '达成率']
];
majorItemFinishList.forEach(row => {
data.push([
row.dept_name,
row.major_name,
row.stu_num,
row.total_num,
row.finish_num,
row.total_num
? ((row.finish_num / row.total_num) * 100).toFixed(4) + '%'
: '0.0000%'
]);
});
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '各专业指标达成统计');
let title = this.searchRelName + "--" + this.searchGradeName;
XLSX.writeFile(workbook, title + '各专业指标达成统计.xlsx');
},
exportTable(id, title) {
let thead = document.getElementById(id).getElementsByTagName('thead')[0];
let tbody = document.getElementById(id).getElementsByTagName('tbody')[0];
let tempTable = document.getElementById('tempTable');
if (thead) {
tempTable.appendChild(thead.cloneNode(true))
}
if (tbody) {
Array.from(tbody.getElementsByTagName('td')).forEach(td => {
if (td.innerText.trim().endsWith('%')) {
td.innerHTML = `<span style="mso-number-format:'\\@';">${td.innerText}</span>`;
}
});
tempTable.appendChild(tbody.cloneNode(true))
}
const wb = XLSX.utils.table_to_book(tempTable, { sheet: 'Sheet1', cellText: true })
XLSX.writeFile(wb, title + ".xlsx");
tempTable.innerHTML = '';
},
handleClassItemFinishPageChange(page) {
this.currentPageClassItemFinish = page;
},
handleClassItemFinishSizeChange(size) {
this.pageSizeClassItemFinish = size;
this.currentPageClassItemFinish = 1;
},
handleClassFinishPageChange(page) {
this.currentPageClassFinish = page;
},
handleClassFinishSizeChange(size) {
this.pageSizeClassFinish = size;
this.currentPageClassFinish = 1;
},
handleMajorItemFinishPageChange(page) {
this.currentPageMajorItemFinish = page;
},
handleMajorItemFinishSizeChange(size) {
this.pageSizeMajorItemFinish = size;
this.currentPageMajorItemFinish = 1;
},
handleMajorFinishPageChange(page) {
this.currentPageMajorFinish = page;
},
handleMajorFinishSizeChange(size) {
this.pageSizeMajorFinish = size;
this.currentPageMajorFinish = 1;
},
async queryData() {
let sdata = { ...this.queryParams };
if (isEmpty(sdata.relId)) {
this.$message.info("请选择报告");
return;
}
this.searchRelName = this.relList.find(item => item.relId == sdata.relId).reportName;
if (!isEmpty(sdata.majorId)) {
this.searchMajorName = this.search_major_list.find(item => item.majorId == sdata.majorId).majorName;
}
if (!isEmpty(sdata.classId)) {
this.searchClassName = this.search_class_list.find(item => item.classId == sdata.classId).className;
}
if (!isEmpty(sdata.gradeId)) {
this.searchGradeName = this.gradeList.find(item => item.gradeId == sdata.gradeId).gradeName;
}
// 只开一次 loading
let loading = fullLoading(this);
// 并行等待接口
try {
await Promise.all([
this.countFinishNumByRelId(true), // 传入参数表示不在内部开loading
this.countItemFinishByRelId(true),
this.countItemFinishNumByRelId(true),
this.countMajorFinishNumByRelId(true),
this.countMajorItemFinishNumByRelId(true),
this.countGradeFinishNumByRelId(true),
this.countGradeItemFinishNumByRelId(true),
this.countClassFinishNumByRelId(true),
this.countClassItemFinishNumByRelId(true),
]);
} finally {
loading.close();
}
},
async countClassFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countClassFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.classFinishList = [...res.data];
}
},
async countClassItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countClassItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.classItemFinishList = [...res.data];
}
},
async countGradeItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countGradeItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.gradeItemFinishList = [...res.data];
}
},
async countGradeFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countGradeFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.gradeFinishList = [...res.data];
}
},
async countMajorItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countMajorItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.majorItemFinishList = [...res.data];
}
},
async countMajorFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countMajorFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
this.majorFinishList = [...res.data];
}
},
async countItemFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countItemFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
let temp = { ...res.data };
let data = [];
data.push({ value: temp.finish_num, name: '已达成' });
data.push({ value: temp.total_num - temp.finish_num, name: '未达成' });
this.renderPieChart(this.itemFinishChart, '指标达成率', data);
}
},
async countFinishNumByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let res = await countFinishNumByRelId(this.queryParams);
if (loading) loading.close();
if (res.code == 200) {
let temp = { ...res.data };
let data = [];
data.push({ value: temp.finish_num, name: '已达成' });
data.push({ value: temp.stu_num - temp.finish_num, name: '未达成' });
this.renderPieChart(this.reportFinishChart, '报告达成率', data);
}
},
async countItemFinishByRelId(noLoading) {
let loading;
if (!noLoading) loading = fullLoading(this);
let sdata = { ...this.queryParams };
this.tableV = false;
let res = await countItemFinishByRelId(sdata);
if (loading) loading.close();
if (res.code == 200) {
this.tableV = true;
this.tableData = [...res.data];
}
},
renderPieChart(chart, title, data) {
// 添加数据检查
if (!data || !Array.isArray(data)) {
console.warn('数据格式不正确或为空:', data);
data = []; // 设置为空数组,避免报错
}
const option = {
title: {
text: title,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '达成情况',
type: 'pie',
radius: '50%',
center: ['70%', '50%'],
data: [...data],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
// 如果数据为空,显示暂无数据
if (data.length === 0) {
option.title.subtext = '暂无数据';
}
chart.setOption(option);
},
initCharts() {
if (this.$refs.reportFinishChart && this.$refs.itemFinishChart) {
this.reportFinishChart = echarts.init(this.$refs.reportFinishChart);
this.itemFinishChart = echarts.init(this.$refs.itemFinishChart);
}
},
async changeSearchMajor() {
let sdata = {
pageNum: 1,
pageSize: 100,
majorId: this.queryParams.majorId
}
let res = await listClass(sdata);
if (res.code == 200) {
this.search_class_list = [...res.rows];
}
},
async listMajors() {
let res = await listMajors();
this.search_major_list = [...res.data];
},
async listAllRelease() {
let res = await listAllRelease();
if (res.code == 200) {
this.relList = [...res.data];
}
},
async listEnableGrade() {
let res = await listEnableGrade();
if (res.code == 200) {
this.gradeList = [...res.data];
}
}
}
}
</script>
<style scoped lang="scss">
.box-card {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.chart-container {
background: #fff;
padding: 10px;
border-radius: 5px;
}
.mt20 {
margin-top: 20px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: '';
}
.clearfix:after {
clear: both;
}
</style>