Files
school-developer/src/views/stuwork/stuconduct/indexYear.vue
2026-03-11 17:51:17 +08:00

397 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="学年" prop="schoolYear">
<el-select v-model="queryForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 200px">
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="班级" prop="classCode">
<el-select v-model="queryForm.classCode" placeholder="请选择班级" clearable filterable style="width: 200px">
<el-option v-for="item in classList" :key="item.classCode" :label="item.classNo" :value="item.classCode"> </el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
学年操行考核统计
</span>
</div>
</template>
<!-- 统计表格 -->
<el-table
:data="statisticsData"
v-loading="loading"
stripe
style="width: 100%; margin-bottom: 20px"
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column prop="label" label="" width="120" align="center" fixed="left" />
<el-table-column prop="classNo" label="班级" min-width="150" align="center" />
<el-table-column prop="excellent" label="优秀" min-width="100" align="center" />
<el-table-column prop="good" label="良好" min-width="100" align="center" />
<el-table-column prop="pass" label="及格" min-width="100" align="center" />
<el-table-column prop="fail" label="不及格" min-width="100" align="center" />
</el-table>
<!-- 学生列表表格 -->
<el-table
:data="studentList"
v-loading="loading"
stripe
style="width: 100%"
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column type="index" label="序号" width="70" align="center" fixed="left" />
<el-table-column prop="stuNo" label="学号" min-width="120" show-overflow-tooltip align="center" fixed="left" />
<el-table-column prop="realName" label="姓名" min-width="80" show-overflow-tooltip align="center" fixed="left" />
<!-- 第一学期月份 -->
<el-table-column label="第一学期" align="center">
<el-table-column v-for="(month, index) in firstTermMonths" :key="'first-' + index" :label="month.label" min-width="60" align="center">
<template #default="scope">
<span>{{ formatScore(scope.row[month.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="学期评" min-width="70" align="center">
<template #default="scope">
<el-tag :type="getScoreType(scope.row.scoreOneTerm)" size="small" effect="plain">
{{ formatScore(scope.row.scoreOneTerm) }}
</el-tag>
</template>
</el-table-column>
</el-table-column>
<!-- 第二学期月份 -->
<el-table-column label="第二学期" align="center">
<el-table-column v-for="(month, index) in secondTermMonths" :key="'second-' + index" :label="month.label" min-width="60" align="center">
<template #default="scope">
<span>{{ formatScore(scope.row[month.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="学期评" min-width="70" align="center">
<template #default="scope">
<el-tag :type="getScoreType(scope.row.scoreTwoTerm)" size="small" effect="plain">
{{ formatScore(scope.row.scoreTwoTerm) }}
</el-tag>
</template>
</el-table-column>
</el-table-column>
<!-- 学年总评 -->
<el-table-column prop="scoreYear" label="学年总评" min-width="100" align="center" fixed="right">
<template #default="scope">
<el-tag :type="getScoreType(scope.row.scoreYear)" size="small" effect="dark">
{{ formatScore(scope.row.scoreYear) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button icon="View" link type="primary" @click="handleView(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<!-- 查看详情弹窗 -->
<el-dialog v-model="viewDialogVisible" title="学年操行考核详情" width="900px" destroy-on-close @close="viewDetailList = []">
<div v-if="viewRow" class="view-summary">
<el-descriptions :column="3" border size="small">
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{ viewRow.realName }}</el-descriptions-item>
<el-descriptions-item label="学年总评">
<el-tag :type="getScoreType(viewRow.scoreYear)" size="small">
{{ formatScore(viewRow.scoreYear) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="第一学期">
<el-tag :type="getScoreType(viewRow.scoreOneTerm)" size="small" effect="plain">
{{ formatScore(viewRow.scoreOneTerm) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="第二学期">
<el-tag :type="getScoreType(viewRow.scoreTwoTerm)" size="small" effect="plain">
{{ formatScore(viewRow.scoreTwoTerm) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="学年">{{ queryForm.schoolYear }}</el-descriptions-item>
</el-descriptions>
</div>
<div class="view-detail-title">考核记录</div>
<el-table
:data="viewDetailList"
v-loading="viewLoading"
stripe
size="small"
max-height="400"
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="schoolTerm" label="学期" width="80" align="center">
<template #default="scope">
<el-tag size="small" effect="plain">{{ scope.row.schoolTerm === '1' ? '第一学期' : '第二学期' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="recordDate" label="考核日期" width="120" align="center" show-overflow-tooltip />
<el-table-column prop="conductType" label="类型" width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
{{ scope.row.conductType === '1' ? '加分' : scope.row.conductType === '0' ? '扣分' : scope.row.conductType || '-' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="score" label="分数" width="80" align="center">
<template #default="scope">
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
</template>
</el-table-column>
<el-table-column prop="description" label="情况记录" min-width="150" show-overflow-tooltip />
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
</el-table>
<template v-if="viewDetailList.length === 0 && !viewLoading">
<el-empty description="暂无考核记录" :image-size="80" />
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="StuConductYear">
import { reactive, ref, onMounted, computed } from 'vue';
import { getStuConductYear, queryDataByStuNo } from '/@/api/stuwork/stuconduct';
import { getClassListByRole } from '/@/api/basic/basicclass';
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
import { useMessage } from '/@/hooks/message';
import { Search, Document } from '@element-plus/icons-vue';
// 表格样式
const tableStyle = {
cellStyle: { padding: '8px 0', textAlign: 'center' },
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold', textAlign: 'center' },
};
// 定义变量内容
const searchFormRef = ref();
const showSearch = ref(true);
const loading = ref(false);
const schoolYearList = ref<any[]>([]);
const classList = ref<any[]>([]);
const studentList = ref<any[]>([]);
const viewDialogVisible = ref(false);
const viewLoading = ref(false);
const viewRow = ref<any>(null);
const viewDetailList = ref<any[]>([]);
// 查询表单
const queryForm = reactive({
schoolYear: '',
classCode: '',
});
// 第一学期月份列9月-1月
const firstTermMonths = [
{ label: '9月', prop: 'scoreOneMonth' },
{ label: '10月', prop: 'scoreTwoMonth' },
{ label: '11月', prop: 'scoreThreeMonth' },
{ label: '12月', prop: 'scoreFourMonth' },
{ label: '1月', prop: 'scoreFiveMonth' },
];
// 第二学期月份列2月-6月
const secondTermMonths = [
{ label: '2月', prop: 'scoreSixMonth' },
{ label: '3月', prop: 'scoreSevenMonth' },
{ label: '4月', prop: 'scoreEightMonth' },
{ label: '5月', prop: 'scoreNineMonth' },
{ label: '6月', prop: 'scoreTenMonth' },
];
// 格式化分数
const formatScore = (score: any) => {
if (score === null || score === undefined || score === '') return '-';
return Number(score).toFixed(2);
};
// 根据分数获取标签类型
const getScoreType = (score: any) => {
if (score === null || score === undefined) return 'info';
const num = Number(score);
if (num >= 90) return 'success';
if (num >= 80) return 'primary';
if (num >= 60) return 'warning';
return 'danger';
};
// 统计表格数据
const statisticsData = computed(() => {
if (studentList.value.length === 0) {
return [];
}
let excellent = 0;
let good = 0;
let pass = 0;
let fail = 0;
const total = studentList.value.length;
studentList.value.forEach((student: any) => {
const score = student.scoreYear;
if (score !== null && score !== undefined) {
if (Number(score) >= 90) {
excellent++;
} else if (Number(score) >= 80) {
good++;
} else if (Number(score) >= 60) {
pass++;
} else {
fail++;
}
}
});
const excellentRate = total > 0 ? ((excellent / total) * 100).toFixed(2) + '%' : '0%';
const goodRate = total > 0 ? ((good / total) * 100).toFixed(2) + '%' : '0%';
const passRate = total > 0 ? ((pass / total) * 100).toFixed(2) + '%' : '0%';
const failRate = total > 0 ? ((fail / total) * 100).toFixed(2) + '%' : '0%';
const excellentGoodRate = total > 0 ? (((excellent + good) / total) * 100).toFixed(2) + '%' : '0%';
const classNo = studentList.value.length > 0 ? studentList.value[0].classNo || '-' : '-';
return [
{ label: '人数', classNo, excellent, good, pass, fail },
{ label: '比率', classNo, excellent: excellentRate, good: goodRate, pass: passRate, fail: failRate },
{ label: '优良率', classNo, excellent: excellentGoodRate, good: '-', pass: '-', fail: '-' },
{ label: '备注', classNo, excellent: '-', good: '-', pass: '-', fail: '-' },
];
});
// 获取数据列表
const getDataList = async () => {
if (!queryForm.schoolYear || !queryForm.classCode) {
useMessage().warning('请选择学年和班级');
return;
}
loading.value = true;
try {
const res = await getStuConductYear({
schoolYear: queryForm.schoolYear,
classCode: queryForm.classCode,
});
if (res.data && Array.isArray(res.data)) {
const tempList: any[] = [];
res.data.forEach((item: any) => {
if (item.basicStudentVOList && Array.isArray(item.basicStudentVOList)) {
item.basicStudentVOList.forEach((student: any) => {
tempList.push({
...student,
classNo: item.classNo,
classCode: item.classCode,
});
});
}
});
studentList.value = tempList;
} else {
studentList.value = [];
}
} catch (_err) {
studentList.value = [];
} finally {
loading.value = false;
}
};
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields();
queryForm.schoolYear = '';
queryForm.classCode = '';
studentList.value = [];
};
// 查看详情
const handleView = async (row: any) => {
if (!queryForm.schoolYear || !row.stuNo) {
useMessage().warning('缺少学年或学号');
return;
}
viewRow.value = row;
viewDialogVisible.value = true;
viewDetailList.value = [];
viewLoading.value = true;
try {
const res = await queryDataByStuNo({
schoolYear: queryForm.schoolYear,
stuNo: row.stuNo,
});
viewDetailList.value = Array.isArray(res.data) ? res.data : [];
} catch (_err) {
viewDetailList.value = [];
} finally {
viewLoading.value = false;
}
};
// 获取学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear();
schoolYearList.value = res.data && Array.isArray(res.data) ? res.data : [];
} catch (err) {
schoolYearList.value = [];
}
};
// 获取班级列表
const getClassListData = async () => {
try {
const res = await getClassListByRole();
classList.value = res.data && Array.isArray(res.data) ? res.data : [];
} catch (err) {
classList.value = [];
}
};
// 初始化
onMounted(() => {
getSchoolYearList();
getClassListData();
});
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
.view-summary {
margin-bottom: 16px;
}
.view-detail-title {
margin-bottom: 8px;
font-weight: 600;
color: #303133;
}
</style>