919 lines
28 KiB
Vue
919 lines
28 KiB
Vue
<template>
|
|
<BasicLayout :show-nav-bar="true" :nav-bar-props="{ title: '成绩分析' }">
|
|
<view class="grade-analysis-page">
|
|
<!-- 1. Class Selector -->
|
|
<view class="selector-section class-selector">
|
|
<picker
|
|
mode="selector"
|
|
:range="classList"
|
|
range-key="name"
|
|
:value="selectedClassIndex"
|
|
@change="onClassChange"
|
|
>
|
|
<view class="picker-item">
|
|
<text>{{ selectedClassName || "选择班级" }}</text>
|
|
<uni-icons type="bottom" size="16" color="#666"></uni-icons>
|
|
</view>
|
|
</picker>
|
|
</view>
|
|
|
|
<!-- 2. Subject Tabs -->
|
|
<view class="selector-section subject-tabs">
|
|
<scroll-view
|
|
scroll-x="true"
|
|
class="scroll-view-h"
|
|
:show-scrollbar="false"
|
|
>
|
|
<view
|
|
class="tab-item"
|
|
v-for="subject in subjectList"
|
|
:key="subject.id"
|
|
:class="{ active: selectedSubjectId === subject.id }"
|
|
@click="selectSubject(subject.id)"
|
|
>
|
|
<text>{{ subject.name }}</text>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
|
|
<!-- 3. Student Score Table -->
|
|
<uni-section title="学生成绩" type="line" titleFontSize="16px" padding>
|
|
<view class="score-table">
|
|
<view class="table-header">
|
|
<view class="th">姓名</view>
|
|
<view class="th">得分</view>
|
|
<view class="th">年级排名</view>
|
|
</view>
|
|
<view class="table-body">
|
|
<view
|
|
class="table-row"
|
|
v-for="student in studentScores"
|
|
:key="student.id"
|
|
>
|
|
<view class="td">{{ student.name }}</view>
|
|
<view class="td">{{ student.score }}</view>
|
|
<view class="td">{{ student.rank }}</view>
|
|
</view>
|
|
<view v-if="!studentScores.length" class="table-empty">
|
|
暂无数据
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</uni-section>
|
|
|
|
<!-- 4. Summary Statistics -->
|
|
<uni-section title="统计概览" type="line" titleFontSize="16px" padding>
|
|
<view class="summary-stats-card">
|
|
<view class="stats-grid">
|
|
<view class="stat-item">
|
|
<text class="label">班级人数</text>
|
|
<text class="value">{{ summaryStats.classCount }}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">年级平均总分</text>
|
|
<text class="value primary">{{
|
|
summaryStats.gradeAvgTotal
|
|
}}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">班级平均分</text>
|
|
<text class="value primary">{{ summaryStats.classAvg }}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">年级最高分</text>
|
|
<text class="value primary">{{ summaryStats.gradeMax }}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">优生率</text>
|
|
<text class="value success"
|
|
>{{ summaryStats.excellentRate }}%</text
|
|
>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">班级最高分</text>
|
|
<text class="value primary">{{ summaryStats.classMax }}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">优生人数</text>
|
|
<text class="value">{{ summaryStats.excellentCount }}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">及格率</text>
|
|
<text class="value success">{{ summaryStats.passRate }}%</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="label">及格人数</text>
|
|
<text class="value">{{ summaryStats.passCount }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</uni-section>
|
|
|
|
<!-- 5. Charts -->
|
|
<uni-section
|
|
title="本班与年级平均分对比分析图表"
|
|
type="line"
|
|
titleFontSize="16px"
|
|
padding
|
|
>
|
|
<view class="chart-container" id="line-chart-container">
|
|
<!-- Use canvas for native rendering -->
|
|
<canvas
|
|
canvas-id="GradeAnalysisLineChart"
|
|
id="GradeAnalysisLineChart"
|
|
class="charts"
|
|
/>
|
|
<view v-if="isLoading" class="chart-placeholder"
|
|
>图表加载中或无数据...</view
|
|
>
|
|
</view>
|
|
</uni-section>
|
|
|
|
<uni-section title="总分分数段" type="line" titleFontSize="16px" padding>
|
|
<view class="chart-container" id="donut-chart-container">
|
|
<!-- Use canvas for native rendering -->
|
|
<canvas
|
|
canvas-id="GradeAnalysisDonutChart"
|
|
id="GradeAnalysisDonutChart"
|
|
class="charts"
|
|
/>
|
|
<view v-if="isLoading" class="chart-placeholder"
|
|
>图表加载中或无数据...</view
|
|
>
|
|
</view>
|
|
</uni-section>
|
|
|
|
<uni-section title="总分等级段" type="line" titleFontSize="16px" padding>
|
|
<view class="chart-container" id="area-chart-container">
|
|
<!-- Use canvas for native rendering -->
|
|
<canvas
|
|
canvas-id="GradeAnalysisAreaChart"
|
|
id="GradeAnalysisAreaChart"
|
|
class="charts"
|
|
/>
|
|
<view v-if="isLoading" class="chart-placeholder"
|
|
>图表加载中或无数据...</view
|
|
>
|
|
</view>
|
|
</uni-section>
|
|
</view>
|
|
</BasicLayout>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import {
|
|
ref,
|
|
computed,
|
|
onMounted,
|
|
nextTick,
|
|
getCurrentInstance,
|
|
ComponentInternalInstance,
|
|
} from "vue";
|
|
import uCharts from "@/components/charts/u-charts.js";
|
|
|
|
// --- Interfaces ---
|
|
interface ClassInfo { id: string | number; name: string; }
|
|
interface Subject { id: string | number; name: string; }
|
|
interface StudentScore { id: string | number; name: string; score: number; rank: number; }
|
|
interface SummaryStats { classCount: number; gradeAvgTotal: number; classAvg: number; gradeMax: number; excellentRate: number; classMax: number; excellentCount: number; passRate: number; passCount: number; }
|
|
|
|
// --- Static Mock Data ---
|
|
const classList = ref<ClassInfo[]>([
|
|
{ id: "c1", name: "五年级(1)班" },
|
|
{ id: "c2", name: "五年级(2)班" },
|
|
{ id: "c3", name: "五年级(3)班" },
|
|
{ id: "c4", name: "六年级(1)班" },
|
|
{ id: "c5", name: "六年级(2)班" },
|
|
]);
|
|
|
|
const staticSubjectList: Subject[] = [
|
|
{ id: "subj-yuwen", name: "语文" },
|
|
{ id: "subj-shuxue", name: "数学" },
|
|
{ id: "subj-yingyu", name: "英语" },
|
|
{ id: "subj-kexue", name: "科学" },
|
|
{ id: "subj-daofa", name: "道法" },
|
|
];
|
|
|
|
const staticStudentScores: StudentScore[] = [
|
|
{ id: "st-1", name: "张三", score: 95, rank: 1 },
|
|
{ id: "st-2", name: "李四", score: 88, rank: 5 },
|
|
{ id: "st-3", name: "王五", score: 92, rank: 3 },
|
|
{ id: "st-4", name: "赵六", score: 75, rank: 15 },
|
|
{ id: "st-5", name: "孙七", score: 85, rank: 8 },
|
|
];
|
|
|
|
const staticSummaryStats: SummaryStats = { classCount: 45, gradeAvgTotal: 82.5, classAvg: 85.1, gradeMax: 99, excellentRate: 75.6, classMax: 98, excellentCount: 34, passRate: 95.6, passCount: 43 };
|
|
|
|
// --- State ---
|
|
const selectedClassIndex = ref<number>(0);
|
|
const selectedClassId = ref<string | number | null>(classList.value[0]?.id || null);
|
|
const subjectList = ref<Subject[]>([...staticSubjectList]);
|
|
const selectedSubjectId = ref<string | number | null>(subjectList.value[0]?.id || null);
|
|
const studentScores = ref<StudentScore[]>([...staticStudentScores]);
|
|
const summaryStats = ref<SummaryStats>({ ...staticSummaryStats });
|
|
const isLoading = ref(false);
|
|
|
|
// --- Computed ---
|
|
const selectedClassName = computed(() => classList.value[selectedClassIndex.value]?.name);
|
|
|
|
// --- Event Handlers ---
|
|
const onClassChange = (event: any) => {
|
|
const index = Number(event.detail.value);
|
|
selectedClassIndex.value = index;
|
|
selectedClassId.value = classList.value[index]?.id || null;
|
|
selectedSubjectId.value = subjectList.value[0]?.id || null;
|
|
console.log(`切换到班级: ${selectedClassId.value}, 默认科目: ${selectedSubjectId.value}`);
|
|
};
|
|
|
|
const selectSubject = (subjectId: string | number) => {
|
|
selectedSubjectId.value = subjectId;
|
|
console.log(`选中科目: ${subjectId}`);
|
|
if (subjectId === 'subj-shuxue') {
|
|
summaryStats.value.classAvg = 88.2;
|
|
studentScores.value = [...staticStudentScores].map(s => s.id === 'st-1' ? {...s, score: 98} : s);
|
|
} else if (subjectId === 'subj-yingyu') {
|
|
summaryStats.value.classAvg = 80.5;
|
|
studentScores.value = [...staticStudentScores].map(s => s.id === 'st-2' ? {...s, score: 90} : s);
|
|
} else {
|
|
summaryStats.value = { ...staticSummaryStats };
|
|
studentScores.value = [...staticStudentScores];
|
|
}
|
|
};
|
|
|
|
// --- 图表绘制逻辑 (保持不变) ---
|
|
const drawChartBase = (
|
|
containerId: string,
|
|
canvasId: string,
|
|
type: string,
|
|
data: any
|
|
) => {
|
|
nextTick(() => {
|
|
// 使用 uni.$scope 获取当前页面/组件实例 (根据你的要求)
|
|
// @ts-ignore - Ignore $scope error as requested
|
|
uni
|
|
.createSelectorQuery()
|
|
.in(uni.$scope)
|
|
.select(`#${containerId}`)
|
|
.boundingClientRect((rect) => {
|
|
if (
|
|
rect &&
|
|
!Array.isArray(rect) &&
|
|
typeof rect.width === "number" &&
|
|
rect.width > 0 &&
|
|
typeof rect.height === "number" &&
|
|
rect.height > 0
|
|
) {
|
|
const containerWidth = rect.width;
|
|
const containerHeight = rect.height;
|
|
|
|
// @ts-ignore - Ignore $scope error as requested
|
|
const ctx = uni.createCanvasContext(canvasId, uni.$scope);
|
|
|
|
let specificOptions = {}; // 用于存放特定图表类型的配置
|
|
|
|
switch (type) {
|
|
case "line":
|
|
specificOptions = new uCharts({
|
|
type: "line",
|
|
context: ctx,
|
|
width: containerWidth,
|
|
height: containerHeight,
|
|
categories: data.categories,
|
|
series: data.series,
|
|
animation: true,
|
|
timing: "easeOut",
|
|
duration: 1000,
|
|
rotate: false,
|
|
rotateLock: false,
|
|
background: "#FFFFFF",
|
|
color: [
|
|
"#1890FF",
|
|
"#91CB74",
|
|
"#FAC858",
|
|
"#EE6666",
|
|
"#73C0DE",
|
|
"#3CA272",
|
|
"#FC8452",
|
|
"#9A60B4",
|
|
"#ea7ccc",
|
|
],
|
|
padding: [15, 10, 0, 15],
|
|
fontSize: 13,
|
|
fontColor: "#666666",
|
|
dataLabel: true,
|
|
dataPointShape: true,
|
|
dataPointShapeType: "solid",
|
|
touchMoveLimit: 60,
|
|
enableScroll: false,
|
|
enableMarkLine: false,
|
|
legend: {
|
|
show: false,
|
|
position: "bottom",
|
|
float: "center",
|
|
padding: 5,
|
|
margin: 5,
|
|
backgroundColor: "rgba(0,0,0,0)",
|
|
borderColor: "rgba(0,0,0,0)",
|
|
borderWidth: 0,
|
|
fontSize: 13,
|
|
fontColor: "#666666",
|
|
lineHeight: 11,
|
|
hiddenColor: "#CECECE",
|
|
itemGap: 10,
|
|
},
|
|
xAxis: {
|
|
disableGrid: true,
|
|
disabled: false,
|
|
axisLine: true,
|
|
axisLineColor: "#CCCCCC",
|
|
calibration: false,
|
|
fontColor: "#666666",
|
|
fontSize: 13,
|
|
lineHeight: 20,
|
|
marginTop: 0,
|
|
rotateLabel: false,
|
|
rotateAngle: 45,
|
|
itemCount: 5,
|
|
boundaryGap: "center",
|
|
splitNumber: 5,
|
|
gridColor: "#CCCCCC",
|
|
gridType: "solid",
|
|
dashLength: 4,
|
|
gridEval: 1,
|
|
scrollShow: false,
|
|
scrollAlign: "left",
|
|
scrollColor: "#A6A6A6",
|
|
scrollBackgroundColor: "#EFEBEF",
|
|
title: "",
|
|
titleFontSize: 13,
|
|
titleOffsetY: 0,
|
|
titleOffsetX: 0,
|
|
titleFontColor: "#666666",
|
|
formatter: "",
|
|
},
|
|
yAxis: {
|
|
gridType: "dash",
|
|
dashLength: 2,
|
|
disabled: false,
|
|
disableGrid: false,
|
|
splitNumber: 5,
|
|
gridColor: "#CCCCCC",
|
|
padding: 10,
|
|
showTitle: false,
|
|
data: [],
|
|
},
|
|
extra: {
|
|
line: {
|
|
type: "straight",
|
|
width: 2,
|
|
activeType: "hollow",
|
|
linearType: "none",
|
|
onShadow: false,
|
|
animation: "vertical",
|
|
},
|
|
tooltip: {
|
|
showBox: true,
|
|
showArrow: true,
|
|
showCategory: false,
|
|
borderWidth: 0,
|
|
borderRadius: 0,
|
|
borderColor: "#000000",
|
|
borderOpacity: 0.7,
|
|
bgColor: "#000000",
|
|
bgOpacity: 0.7,
|
|
gridType: "solid",
|
|
dashLength: 4,
|
|
gridColor: "#CCCCCC",
|
|
boxPadding: 3,
|
|
fontSize: 13,
|
|
lineHeight: 20,
|
|
fontColor: "#FFFFFF",
|
|
legendShow: true,
|
|
legendShape: "auto",
|
|
splitLine: true,
|
|
horizentalLine: false,
|
|
xAxisLabel: false,
|
|
yAxisLabel: false,
|
|
labelBgColor: "#FFFFFF",
|
|
labelBgOpacity: 0.7,
|
|
labelFontColor: "#666666",
|
|
},
|
|
markLine: {
|
|
type: "solid",
|
|
dashLength: 4,
|
|
data: [],
|
|
},
|
|
},
|
|
});
|
|
break;
|
|
case "area":
|
|
specificOptions = new uCharts({
|
|
type: "area",
|
|
context: ctx,
|
|
width: containerWidth,
|
|
height: containerHeight,
|
|
categories: data.categories,
|
|
series: data.series,
|
|
animation: true,
|
|
timing: "easeOut",
|
|
duration: 1000,
|
|
rotate: false,
|
|
rotateLock: false,
|
|
background: "#FFFFFF",
|
|
color: [
|
|
"#1890FF",
|
|
"#91CB74",
|
|
"#FAC858",
|
|
"#EE6666",
|
|
"#73C0DE",
|
|
"#3CA272",
|
|
"#FC8452",
|
|
"#9A60B4",
|
|
"#ea7ccc",
|
|
],
|
|
padding: [15, 15, 0, 15],
|
|
fontSize: 13,
|
|
fontColor: "#666666",
|
|
dataLabel: true,
|
|
dataPointShape: true,
|
|
dataPointShapeType: "solid",
|
|
touchMoveLimit: 60,
|
|
enableScroll: false,
|
|
enableMarkLine: false,
|
|
legend: {
|
|
show: false,
|
|
position: "bottom",
|
|
float: "center",
|
|
padding: 5,
|
|
margin: 5,
|
|
backgroundColor: "rgba(0,0,0,0)",
|
|
borderColor: "rgba(0,0,0,0)",
|
|
borderWidth: 0,
|
|
fontSize: 13,
|
|
fontColor: "#666666",
|
|
lineHeight: 11,
|
|
hiddenColor: "#CECECE",
|
|
itemGap: 10,
|
|
},
|
|
xAxis: {
|
|
disableGrid: true,
|
|
disabled: false,
|
|
axisLine: true,
|
|
axisLineColor: "#CCCCCC",
|
|
calibration: false,
|
|
fontColor: "#666666",
|
|
fontSize: 13,
|
|
lineHeight: 20,
|
|
marginTop: 0,
|
|
rotateLabel: false,
|
|
rotateAngle: 45,
|
|
itemCount: 5,
|
|
boundaryGap: "center",
|
|
splitNumber: 5,
|
|
gridColor: "#CCCCCC",
|
|
gridType: "solid",
|
|
dashLength: 4,
|
|
gridEval: 1,
|
|
scrollShow: false,
|
|
scrollAlign: "left",
|
|
scrollColor: "#A6A6A6",
|
|
scrollBackgroundColor: "#EFEBEF",
|
|
title: "",
|
|
titleFontSize: 13,
|
|
titleOffsetY: 0,
|
|
titleOffsetX: 0,
|
|
titleFontColor: "#666666",
|
|
formatter: "",
|
|
},
|
|
yAxis: {
|
|
gridType: "dash",
|
|
dashLength: 2,
|
|
disabled: false,
|
|
disableGrid: false,
|
|
splitNumber: 5,
|
|
gridColor: "#CCCCCC",
|
|
padding: 10,
|
|
showTitle: false,
|
|
data: [],
|
|
},
|
|
extra: {
|
|
area: {
|
|
type: "straight",
|
|
opacity: 0.2,
|
|
addLine: true,
|
|
width: 2,
|
|
gradient: false,
|
|
activeType: "hollow",
|
|
},
|
|
tooltip: {
|
|
showBox: true,
|
|
showArrow: true,
|
|
showCategory: false,
|
|
borderWidth: 0,
|
|
borderRadius: 0,
|
|
borderColor: "#000000",
|
|
borderOpacity: 0.7,
|
|
bgColor: "#000000",
|
|
bgOpacity: 0.7,
|
|
gridType: "solid",
|
|
dashLength: 4,
|
|
gridColor: "#CCCCCC",
|
|
boxPadding: 3,
|
|
fontSize: 13,
|
|
lineHeight: 20,
|
|
fontColor: "#FFFFFF",
|
|
legendShow: true,
|
|
legendShape: "auto",
|
|
splitLine: true,
|
|
horizentalLine: false,
|
|
xAxisLabel: false,
|
|
yAxisLabel: false,
|
|
labelBgColor: "#FFFFFF",
|
|
labelBgOpacity: 0.7,
|
|
labelFontColor: "#666666",
|
|
},
|
|
markLine: {
|
|
type: "solid",
|
|
dashLength: 4,
|
|
data: [],
|
|
},
|
|
},
|
|
});
|
|
break;
|
|
case "ring":
|
|
specificOptions = new uCharts({
|
|
type: "ring",
|
|
context: ctx,
|
|
width: containerWidth,
|
|
height: containerHeight,
|
|
series: data.series,
|
|
animation: true,
|
|
timing: "easeOut",
|
|
duration: 1000,
|
|
rotate: false,
|
|
rotateLock: false,
|
|
background: "#FFFFFF",
|
|
color: [
|
|
"#1890FF",
|
|
"#91CB74",
|
|
"#FAC858",
|
|
"#EE6666",
|
|
"#73C0DE",
|
|
"#3CA272",
|
|
"#FC8452",
|
|
"#9A60B4",
|
|
"#ea7ccc",
|
|
],
|
|
padding: [5, 5, 5, 5],
|
|
fontSize: 13,
|
|
fontColor: "#666666",
|
|
dataLabel: true,
|
|
dataPointShape: true,
|
|
dataPointShapeType: "solid",
|
|
touchMoveLimit: 60,
|
|
enableScroll: false,
|
|
enableMarkLine: false,
|
|
legend: {
|
|
show: true,
|
|
position: "right",
|
|
lineHeight: 25,
|
|
float: "center",
|
|
padding: 5,
|
|
margin: 5,
|
|
backgroundColor: "rgba(0,0,0,0)",
|
|
borderColor: "rgba(0,0,0,0)",
|
|
borderWidth: 0,
|
|
fontSize: 13,
|
|
fontColor: "#666666",
|
|
hiddenColor: "#CECECE",
|
|
itemGap: 10,
|
|
},
|
|
title: {
|
|
name: "",
|
|
fontSize: 15,
|
|
color: "#666666",
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
},
|
|
subtitle: {
|
|
name: "",
|
|
fontSize: 25,
|
|
color: "#7cb5ec",
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
},
|
|
extra: {
|
|
ring: {
|
|
ringWidth: 30,
|
|
activeOpacity: 0.5,
|
|
activeRadius: 10,
|
|
offsetAngle: 0,
|
|
labelWidth: 15,
|
|
border: true,
|
|
borderWidth: 3,
|
|
borderColor: "#FFFFFF",
|
|
centerColor: "#FFFFFF",
|
|
customRadius: 0,
|
|
linearType: "none",
|
|
},
|
|
tooltip: {
|
|
showBox: true,
|
|
showArrow: true,
|
|
showCategory: false,
|
|
borderWidth: 0,
|
|
borderRadius: 0,
|
|
borderColor: "#000000",
|
|
borderOpacity: 0.7,
|
|
bgColor: "#000000",
|
|
bgOpacity: 0.7,
|
|
gridType: "solid",
|
|
dashLength: 4,
|
|
gridColor: "#CCCCCC",
|
|
boxPadding: 3,
|
|
fontSize: 13,
|
|
lineHeight: 20,
|
|
fontColor: "#FFFFFF",
|
|
legendShow: true,
|
|
legendShape: "auto",
|
|
splitLine: true,
|
|
horizentalLine: false,
|
|
xAxisLabel: false,
|
|
yAxisLabel: false,
|
|
labelBgColor: "#FFFFFF",
|
|
labelBgOpacity: 0.7,
|
|
labelFontColor: "#666666",
|
|
},
|
|
},
|
|
});
|
|
break;
|
|
}
|
|
// 使用传入的 type 和合并后的配置创建图表
|
|
new uCharts(specificOptions);
|
|
} else {
|
|
console.error(`无法获取容器 #${containerId} 的有效尺寸:`, rect);
|
|
}
|
|
})
|
|
.exec();
|
|
});
|
|
};
|
|
|
|
const drawLineChart = (containerId: string, canvasId: string, data: any) => {
|
|
if (!data || !data.categories || !data.series) {
|
|
console.warn(`折线图 (${canvasId}) 数据无效`);
|
|
return;
|
|
}
|
|
console.log(`准备绘制折线图: ${canvasId}`);
|
|
drawChartBase(containerId, canvasId, "line", data);
|
|
};
|
|
|
|
const drawDonutChart = (containerId: string, canvasId: string, data: any) => {
|
|
if (!data || !data.series || !data.series[0]?.data) {
|
|
console.warn(`圆环图 (${canvasId}) 数据无效`);
|
|
return;
|
|
}
|
|
console.log(`准备绘制圆环图: ${canvasId}`);
|
|
drawChartBase(containerId, canvasId, "ring", { series: data.series });
|
|
};
|
|
|
|
const drawAreaChart = (containerId: string, canvasId: string, data: any) => {
|
|
if (!data || !data.categories || !data.series) {
|
|
console.warn(`区域图 (${canvasId}) 数据无效`);
|
|
return;
|
|
}
|
|
console.log(`准备绘制区域图: ${canvasId}`);
|
|
drawChartBase(containerId, canvasId, "area", data);
|
|
};
|
|
|
|
// --- 数据获取与触发 ---
|
|
const getServerData = () => {
|
|
setTimeout(() => {
|
|
let lineRes = { categories: ["2018", "2019", "2020", "2021", "2022", "2023"], series: [{ name: "本班平均分", data: [75, 78, 82, 80, 85, 83] }, { name: "年级平均分", data: [70, 72, 75, 74, 78, 77] }] };
|
|
let donutRes = { series: [{ name: "总分分数段", data: [{ name: "240-269", value: 5, color: "#1890ff" }, { name: "210-239", value: 12, color: "#2fc25b" }, { name: "180-210", value: 18, color: "#facc14" }, { name: "180以下", value: 10, color: "#f04864" }] }] };
|
|
let areaRes = { categories: ["A", "B", "C", "D", "E"], series: [{ name: "人数", data: [5, 15, 18, 10, 2], color: "#1890ff" }] };
|
|
drawLineChart("line-chart-container", "GradeAnalysisLineChart", lineRes);
|
|
drawDonutChart("donut-chart-container", "GradeAnalysisDonutChart", donutRes);
|
|
drawAreaChart("area-chart-container", "GradeAnalysisAreaChart", areaRes);
|
|
}, 500);
|
|
};
|
|
|
|
onMounted(() => {
|
|
getServerData();
|
|
});
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.grade-analysis-page {
|
|
background-color: #f4f5f7;
|
|
min-height: calc(100vh - var(--window-top));
|
|
padding-bottom: 20rpx;
|
|
}
|
|
|
|
// --- Selectors ---
|
|
.selector-section {
|
|
background-color: #ffffff;
|
|
padding: 10rpx 30rpx;
|
|
}
|
|
|
|
.class-selector {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 80rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
.picker-item {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 30rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
width: 100%; // Ensure picker takes full width
|
|
|
|
uni-icons {
|
|
margin-left: 10rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.subject-tabs {
|
|
padding: 0; // Remove padding for full width scroll
|
|
.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; // Theme color
|
|
font-weight: bold;
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 40%; // Adjust underline width
|
|
height: 6rpx;
|
|
background-color: #447ade;
|
|
border-radius: 3rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Section Styling ---
|
|
.uni-section {
|
|
background-color: #ffffff;
|
|
margin-top: 20rpx;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
// --- Score Table ---
|
|
.score-table {
|
|
width: 100%;
|
|
font-size: 26rpx;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
|
|
.table-header {
|
|
display: flex;
|
|
background-color: #eaf2ff;
|
|
color: #333;
|
|
font-weight: bold;
|
|
border-bottom: 1rpx solid #e0e0e0;
|
|
|
|
.th {
|
|
flex: 1;
|
|
padding: 16rpx 10rpx;
|
|
text-align: center;
|
|
border-right: 1rpx solid #e0e0e0;
|
|
&:last-child {
|
|
border-right: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
.table-body {
|
|
.table-row {
|
|
display: flex;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.td {
|
|
flex: 1;
|
|
padding: 16rpx 10rpx;
|
|
text-align: center;
|
|
color: #666;
|
|
border-right: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
|
border-right: none;
|
|
}
|
|
}
|
|
}
|
|
.table-empty {
|
|
text-align: center;
|
|
color: #999;
|
|
padding: 40rpx 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Summary Stats ---
|
|
.summary-stats-card {
|
|
background-color: #f8f9fd;
|
|
border-radius: 6px;
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 25rpx 15rpx;
|
|
}
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
|
|
.label {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
margin-bottom: 6rpx;
|
|
}
|
|
|
|
.value {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
|
|
&.primary {
|
|
color: #447ade;
|
|
}
|
|
&.success {
|
|
color: #67c23a;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Charts ---
|
|
.chart-container {
|
|
width: 100%;
|
|
height: 350rpx; // 进一步减小高度
|
|
position: relative;
|
|
margin: 8rpx 0;
|
|
border-radius: 6rpx; // 减小圆角
|
|
overflow: hidden;
|
|
background-color: #ffffff;
|
|
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
// Style the canvas element itself
|
|
.charts {
|
|
width: 100% !important; // 强制100%宽度
|
|
height: 100% !important; // 强制100%高度
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
max-width: 100%;
|
|
max-height: 100%;
|
|
}
|
|
|
|
.chart-placeholder {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: #999;
|
|
font-size: 24rpx; // 减小占位文字大小
|
|
background-color: #ffffff; // Match section background
|
|
z-index: 1; // Ensure placeholder is above canvas initially
|
|
}
|
|
|
|
// Hide placeholder when loading is false (adjust logic if needed)
|
|
.chart-placeholder {
|
|
// ... existing styles
|
|
// Use v-if="isLoading" instead of complex CSS selector
|
|
}
|
|
</style>
|