zhxy-jsd/src/pages/view/homeSchool/ChengJiFenXi.vue
2025-08-23 02:23:02 +08:00

329 lines
8.2 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>
<BasicLayout :show-nav-bar="true" :nav-bar-props="{ title: '成绩分析' }">
<view class="cjfx-page">
<!-- 1. 班级选择器 -->
<CjBjPicker v-model="xzbjId" @change="onBjChange" />
<!-- 2. 考试场次选择器 -->
<CjKsccPicker v-model="xzksccId" :bj-id="xzbjId" @change="onKsccChange" />
<view v-if="xzbjId && xzksccId">
<!-- 3. 科目选择Tabs (当选择了考试场次后显示) -->
<view class="km-xz-section" v-if="xzksccId && ksccKmLb.length > 0">
<view class="km-xz-tabs">
<scroll-view
scroll-x="true"
class="scroll-view-h"
:show-scrollbar="false"
>
<view
class="tab-item"
v-for="km in ksccKmLb"
:key="km.id"
:class="{ active: xzkmId === km.id }"
@click="selectKm(km.id)"
>
<text>{{ km.kmmc }}</text>
</view>
</scroll-view>
</view>
</view>
<view v-if="xzkmId">
<!-- 4. 本班学生成绩列表 -->
<CjBjXsList :xs-cj-lb="xsCjLb" />
<!-- 5. 班级统计概览 -->
<CjBjTjGl :tjgl-data="tjglData" />
<!-- 6. 本班与年级平均分对比分析图表 -->
<CjBjNjPjf
v-model:selected-km-id="xzkmId"
:bj-fx-list="bjFxList"
:current-bj-id="xzbjId"
/>
<!-- 7. 总分分数段图表 -->
<CjZfFs :dj-rs-list="djRsList" />
<!-- 8. 总分等级段图表 -->
<CjZfDj :dj-rs-list="djRsList" />
</view>
</view>
</view>
</BasicLayout>
</template>
<script lang="ts" setup>
import { jsdBjKscjKmApi, ksccKmFindByKsccIdApi } from "@/api/base/server";
import { onMounted, ref } from "vue";
import CjBjNjPjf from "./components/CjBjNjPjf.vue";
import CjBjPicker from "./components/CjBjPicker.vue";
import CjBjTjGl from "./components/CjBjTjGl.vue";
import CjBjXsList from "./components/CjBjXsList.vue";
import CjKsccPicker from "./components/CjKsccPicker.vue";
import CjZfDj from "./components/CjZfDj.vue";
import CjZfFs from "./components/CjZfFs.vue";
// --- Emits ---
const emit = defineEmits(["kmDataChange"]);
// --- Interfaces ---
interface KsccKm {
id: string | number;
kmmc: string;
kmfs: number;
}
interface XsCj {
id: string | number;
name: string;
score: number;
bjpm: number;
}
interface TjglData {
bjrs: number;
njpjzf: number;
bjpjf: number;
njzgf: number;
ysl: number;
bjzgf: number;
ysrs: number;
jgl: number;
jgrs: number;
}
interface FsDuan {
name: string;
value: number;
color: string;
}
interface DjDuan {
name: string;
value: number;
color: string;
}
interface BjPjInfo {
bjId: string;
bjmc: string;
njmc: string;
fs: number;
}
interface DjRsInfo {
dj: string;
rs: number;
zgf: string;
zdf: string;
}
// --- Static Mock Data ---
// 这些静态数据现在不再需要,因为组件直接使用 djRsList
// --- State ---
const xzbjId = ref<string | number | null>(null);
const xzksccId = ref<string | number | null>(null);
const xzkmId = ref<string | number | null>(null);
const ksccKmLb = ref<KsccKm[]>([]);
const xsCjLb = ref<XsCj[]>([]);
const tjglData = ref<TjglData>({
bjrs: 0,
njpjzf: 0,
bjpjf: 0,
njzgf: 0,
ysl: 0,
bjzgf: 0,
ysrs: 0,
jgl: 0,
jgrs: 0,
});
const bjFxList = ref<BjPjInfo[]>([]);
const djRsList = ref<DjRsInfo[]>([]);
// --- API Functions ---
const getKsccKmLb = async (ksccId: string | number) => {
try {
const res = await ksccKmFindByKsccIdApi({
ksccId,
});
if (res && res.resultCode === 1 && res.result) {
ksccKmLb.value = res.result.map((km: any) => ({
id: km.id,
kmmc: km.kmmc,
kmfs: 100, // 默认分数,实际应该从后端获取
}));
console.log("获取科目列表成功:", ksccKmLb.value);
// 默认选择第一个科目
if (ksccKmLb.value.length > 0) {
xzkmId.value = ksccKmLb.value[0].id;
// 获取该科目的成绩数据
await getBjKscjKmData(xzbjId.value!, ksccId, ksccKmLb.value[0].id);
}
}
} catch (error) {
console.error("获取科目列表失败:", error);
}
};
const getBjKscjKmData = async (
bjId: string | number,
ksccId: string | number,
kmId: string | number
) => {
try {
const res = await jsdBjKscjKmApi({ bjId, ksccId, kmId });
if (res && res.resultCode === 1 && res.result) {
// 处理学生成绩数据
const ksccKscjList = res.result.ksccKscjList || [];
xsCjLb.value = ksccKscjList.map((cj: any) => ({
id: cj.xsId,
name: cj.xsxm || cj.xm || "未知学生",
score: cj.ksfs || 0,
bjpm: cj.bjpm || 0, // 使用后端返回的班级排名
}));
console.log("学生成绩列表:", xsCjLb.value);
// 处理统计数据
tjglData.value.bjrs = res.result.bjRs || 0;
tjglData.value.bjpjf = res.result.bjPjf || 0;
tjglData.value.njpjzf = res.result.njPjf || 0;
tjglData.value.bjzgf = res.result.bjZgf || 0;
tjglData.value.njzgf = res.result.njZgf || 0;
tjglData.value.ysrs = res.result.ysRs || 0;
tjglData.value.jgrs = res.result.jgRs || 0;
tjglData.value.ysl = res.result.ysl || 0;
tjglData.value.jgl = res.result.jgl || 0;
// 处理班级平均分列表数据传递给CjBjNjPjf组件
bjFxList.value = res.result.bjFxList || [];
// 处理等级段数据列表传递给CjZfFs和CjZfDj组件
djRsList.value = res.result.djRsList || [];
console.log("获取班级科目成绩数据成功:", res.result);
console.log("等级人数列表:", djRsList.value);
}
} catch (error) {
console.error("获取班级科目成绩数据失败:", error);
}
};
// --- Event Handlers ---
const onBjChange = async (bjId: string | number | null, bjInfo: any | null) => {
xzbjId.value = bjId;
xzksccId.value = null; // 重置考试场次选择
xzkmId.value = null; // 重置科目选择
ksccKmLb.value = []; // 清空科目列表
console.log(`切换到班级: ${bjId}, 班级名称: ${bjInfo?.name}`);
};
const onKsccChange = async (
ksccId: string | number | null,
ksccInfo: any | null
) => {
xzksccId.value = ksccId;
xzkmId.value = null; // 重置科目选择
console.log(`切换到考试场次: ${ksccId}, 考试名称: ${ksccInfo?.name}`);
if (ksccId) {
// 获取该考试场次的科目列表
await getKsccKmLb(ksccId);
} else {
ksccKmLb.value = [];
}
};
const selectKm = async (kmId: string | number) => {
xzkmId.value = kmId;
console.log(`选中科目: ${kmId}`);
// 获取该科目的详细数据
if (xzbjId.value && xzksccId.value) {
await getBjKscjKmData(xzbjId.value, xzksccId.value, kmId);
}
};
const onKmChange = (kmId: string | number | null, km: any) => {
console.log(`科目变化: ${kmId}, 科目信息:`, km);
// 处理科目变化逻辑
};
// --- Lifecycle ---
onMounted(async () => {
console.log("成绩分析页面初始化完成");
});
</script>
<style scoped lang="scss">
.cjfx-page {
background-color: #f4f5f7;
min-height: calc(100vh - var(--window-top));
padding-bottom: 20rpx;
}
.km-xz-section {
background-color: #ffffff;
padding: 0;
.km-xz-tabs {
padding: 0;
.scroll-view-h {
white-space: nowrap;
width: 100%;
height: 80rpx;
line-height: 80rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.tab-item {
display: inline-block;
padding: 0 30rpx;
font-size: 28rpx;
color: #666;
position: relative;
text-align: center;
height: 100%;
&.active {
color: #447ade;
font-weight: bold;
&::after {
content: "";
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40%;
height: 6rpx;
background-color: #447ade;
border-radius: 3rpx;
}
}
}
}
}
.debug-info {
background-color: #f0f0f0;
padding: 20rpx;
margin-top: 20rpx;
border-radius: 10rpx;
.debug-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
text-align: center;
}
.debug-item {
font-size: 28rpx;
color: #333;
margin-bottom: 5rpx;
}
}
</style>