329 lines
8.2 KiB
Vue
329 lines
8.2 KiB
Vue
<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>
|