725 lines
17 KiB
Vue
725 lines
17 KiB
Vue
<template>
|
||
<view class="grades-page flex flex-col">
|
||
<!-- 顶部蓝色背景 -->
|
||
<view class="blue-header">
|
||
<view class="header-content">
|
||
<text class="title">2024学年第一学期二年级期中</text>
|
||
<text class="subtitle">教学质量监测成绩报告</text>
|
||
|
||
<view class="color-bar"></view>
|
||
|
||
<text class="total-score">全科(满分700分)</text>
|
||
|
||
<text class="grade">A</text>
|
||
|
||
<view class="grade-info">
|
||
<text class="grade-explanation" @click="showGradeInfo">等级说明</text>
|
||
<view class="grade-box">
|
||
<text class="grade-label">区域等级</text>
|
||
<text class="grade-value">相对较好</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 白色内容区 -->
|
||
<view class="flex-1" style="background-color: #f6f6f6; position: relative">
|
||
<view class="content-area">
|
||
<!-- 选项卡 -->
|
||
<view class="tabs">
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'diagnosis' }"
|
||
@click="switchTab('diagnosis')"
|
||
>
|
||
<text>学科诊断</text>
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'scores' }"
|
||
@click="switchTab('scores')"
|
||
>
|
||
<text>学科成绩</text>
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'trend' }"
|
||
@click="switchTab('trend')"
|
||
>
|
||
<text>分数趋势</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 学科成绩视图 -->
|
||
<view class="score-view flex-1 po-re" v-if="activeTab === 'scores'">
|
||
<view class="po-ab inset-0 px-15" style="overflow: auto">
|
||
<view
|
||
class="subject-item"
|
||
v-for="(subject, index) in subjects"
|
||
:key="index"
|
||
>
|
||
<view class="subject-header">
|
||
<text class="subject-name"
|
||
>{{ subject.name }}(满分{{ subject.fullScore }})</text
|
||
>
|
||
</view>
|
||
<view class="subject-body">
|
||
<text class="subject-grade">{{ subject.grade }}</text>
|
||
<text class="detail-btn">详情</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 分数趋势视图 -->
|
||
<view class="trend-view" v-if="activeTab === 'trend'">
|
||
<!-- 图表占位区域 -->
|
||
<view class="radar-placeholder" id="chart-container1">
|
||
<canvas
|
||
style="width: 100%; height: 100%"
|
||
canvas-id="trendCanvas"
|
||
id="trendCanvas"
|
||
class="charts"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 学科诊断视图 -->
|
||
<view class="diagnosis-view flex-1 po-re" v-if="activeTab === 'diagnosis'">
|
||
<view class="po-ab inset-0 p-15" style="overflow: auto">
|
||
<view class="radar-placeholder" id="chart-container">
|
||
<canvas
|
||
style="width: 100%; height: 100%"
|
||
canvas-id="radarCanvas"
|
||
id="radarCanvas"
|
||
class="charts"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 诊断评语 -->
|
||
<view class="diagnosis-comment">
|
||
<text class="comment-title">尊敬的家长,您好</text>
|
||
<text class="comment-text"
|
||
>向您祝贺,朱信权同学本次考试成绩位于较优秀之列!</text
|
||
>
|
||
<text class="comment-text"
|
||
>这次考试中道德与法治学科表现最优秀,继续保持学科优势!相对来说英语学科表现最弱,不过也有27道题的答题表现超出同层次的平均。</text
|
||
>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 等级说明弹窗 -->
|
||
<u-popup
|
||
:show="gradeInfoPopupVisible"
|
||
mode="center"
|
||
round="10"
|
||
:closeable="true"
|
||
@close="hideGradeInfo"
|
||
>
|
||
<view class="grade-info-popup">
|
||
<view class="popup-header">
|
||
<text class="popup-title">等级说明</text>
|
||
<u-icon
|
||
name="close"
|
||
size="20"
|
||
color="#999"
|
||
@click="hideGradeInfo"
|
||
></u-icon>
|
||
</view>
|
||
<view class="popup-content">
|
||
<view
|
||
class="grade-item"
|
||
v-for="(item, index) in gradeInfoList"
|
||
:key="index"
|
||
>
|
||
<view
|
||
class="grade-label"
|
||
:style="{ backgroundColor: item.color }"
|
||
>{{ item.grade }}</view
|
||
>
|
||
<view class="grade-desc">
|
||
<text class="grade-title">{{ item.desc }}</text>
|
||
<text class="grade-detail">{{ item.description }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, watch, nextTick } from "vue";
|
||
import { navigateBack } from "@/utils/uniapp";
|
||
import uCharts from "@/components/charts/u-charts.js";
|
||
|
||
// 当前选中的选项卡
|
||
const activeTab = ref("scores");
|
||
|
||
// 切换选项卡
|
||
function switchTab(tab: string) {
|
||
activeTab.value = tab;
|
||
}
|
||
|
||
// 科目数据
|
||
const subjects = ref([
|
||
{ name: "语文", fullScore: 98, grade: "B" },
|
||
{ name: "数学", fullScore: 96, grade: "A" },
|
||
{ name: "英语", fullScore: 98, grade: "A" },
|
||
{ name: "科学", fullScore: 94, grade: "A" },
|
||
{ name: "音乐", fullScore: 93, grade: "A" },
|
||
{ name: "美术", fullScore: 93, grade: "A" },
|
||
{ name: "体育", fullScore: 93, grade: "A" },
|
||
]);
|
||
|
||
// 雷达图数据
|
||
const radarData = {
|
||
categories: ["语文", "数学", "英语", "科学", "音乐", "美术", "体育"],
|
||
series: [{ name: "分数", data: [85, 90, 88, 92, 86, 91, 89] }],
|
||
};
|
||
|
||
// 趋势图数据
|
||
const trendData = {
|
||
categories: ["01-04", "02-04", "03-04", "04-04", "05-04"],
|
||
series: [
|
||
{
|
||
name: "成绩趋势",
|
||
data: [84, 81, 89, 92, 99],
|
||
},
|
||
],
|
||
};
|
||
|
||
// 绘制雷达图
|
||
const drawRadarChart = () => {
|
||
nextTick(() => {
|
||
try {
|
||
// 获取容器信息
|
||
const query = uni.createSelectorQuery();
|
||
query
|
||
.select("#chart-container")
|
||
.boundingClientRect((data) => {
|
||
if (!data) {
|
||
console.error("未找到雷达图容器");
|
||
return;
|
||
}
|
||
|
||
// 安全类型判断
|
||
const rect = data as any;
|
||
const canvasWidth = rect.width || 300;
|
||
const canvasHeight = rect.height || 300;
|
||
|
||
const ctx = uni.createCanvasContext("radarCanvas");
|
||
|
||
// 绘制雷达图配置
|
||
const options = {
|
||
type: "radar",
|
||
context: ctx,
|
||
width: canvasWidth,
|
||
height: canvasHeight,
|
||
categories: radarData.categories,
|
||
series: radarData.series,
|
||
animation: true,
|
||
background: "#FFFFFF",
|
||
padding: [10, 10, 10, 10],
|
||
dataLabel: false,
|
||
legend: {
|
||
show: false,
|
||
},
|
||
extra: {
|
||
radar: {
|
||
gridType: "radar",
|
||
gridColor: "#CCCCCC",
|
||
gridCount: 3,
|
||
opacity: 0.2,
|
||
labelShow: true,
|
||
},
|
||
},
|
||
};
|
||
|
||
new uCharts(options);
|
||
})
|
||
.exec();
|
||
} catch (error) {
|
||
console.error("绘制雷达图出错:", error);
|
||
}
|
||
});
|
||
};
|
||
|
||
// 绘制趋势图
|
||
const drawTrendChart = () => {
|
||
nextTick(() => {
|
||
try {
|
||
// 获取容器信息
|
||
const query = uni.createSelectorQuery();
|
||
query
|
||
.select("#chart-container1")
|
||
.boundingClientRect((data) => {
|
||
if (!data) {
|
||
console.error("未找到趋势图容器");
|
||
return;
|
||
}
|
||
|
||
// 安全类型判断
|
||
const rect = data as any;
|
||
const canvasWidth = rect.width || 300;
|
||
const canvasHeight = rect.height || 300;
|
||
|
||
const ctx = uni.createCanvasContext("trendCanvas");
|
||
|
||
// 绘制折线图配置
|
||
const options = {
|
||
type: "line",
|
||
context: ctx,
|
||
width: canvasWidth,
|
||
height: canvasHeight,
|
||
categories: trendData.categories,
|
||
series: trendData.series,
|
||
animation: true,
|
||
background: "#FFFFFF",
|
||
padding: [15, 15, 0, 15],
|
||
dataLabel: true,
|
||
dataPointShape: true,
|
||
enableScroll: false,
|
||
legend: {
|
||
show: false,
|
||
},
|
||
xAxis: {
|
||
disableGrid: true,
|
||
axisLine: true,
|
||
axisLineColor: "#CCCCCC",
|
||
},
|
||
yAxis: {
|
||
gridType: "dash",
|
||
dashLength: 2,
|
||
data: [
|
||
{
|
||
min: 80,
|
||
max: 100,
|
||
},
|
||
],
|
||
},
|
||
extra: {
|
||
line: {
|
||
type: "straight",
|
||
width: 2,
|
||
},
|
||
},
|
||
};
|
||
|
||
new uCharts(options);
|
||
})
|
||
.exec();
|
||
} catch (error) {
|
||
console.error("绘制趋势图出错:", error);
|
||
}
|
||
});
|
||
};
|
||
|
||
// 监听选项卡变化,重新渲染对应图表
|
||
watch(activeTab, (newValue) => {
|
||
if (newValue === "diagnosis") {
|
||
setTimeout(drawRadarChart, 50);
|
||
} else if (newValue === "trend") {
|
||
setTimeout(drawTrendChart, 50);
|
||
}
|
||
});
|
||
|
||
onMounted(() => {
|
||
// 默认绘制学科成绩视图
|
||
// 延迟执行确保DOM已经渲染
|
||
setTimeout(() => {
|
||
if (activeTab.value === "diagnosis") {
|
||
drawRadarChart();
|
||
} else if (activeTab.value === "trend") {
|
||
drawTrendChart();
|
||
}
|
||
}, 300);
|
||
});
|
||
|
||
// 等级说明弹窗状态
|
||
const gradeInfoPopupVisible = ref(false);
|
||
|
||
// 等级说明数据
|
||
const gradeInfoList = ref([
|
||
{
|
||
grade: "A+",
|
||
color: "#FF6B6B",
|
||
desc: "优异",
|
||
description: "超过90%同学,处于年级前列",
|
||
},
|
||
{
|
||
grade: "A",
|
||
color: "#4D96FF",
|
||
desc: "相对较好",
|
||
description: "超过80%同学,表现良好",
|
||
},
|
||
{
|
||
grade: "B",
|
||
color: "#6BCB77",
|
||
desc: "良好",
|
||
description: "超过60%同学,达到年级中上水平",
|
||
},
|
||
{
|
||
grade: "C",
|
||
color: "#FFD93D",
|
||
desc: "一般",
|
||
description: "达到年级平均水平",
|
||
},
|
||
{
|
||
grade: "D",
|
||
color: "#B8B8B8",
|
||
desc: "待提高",
|
||
description: "未达到年级平均水平,需要加强",
|
||
},
|
||
]);
|
||
|
||
// 显示等级说明弹窗
|
||
function showGradeInfo() {
|
||
gradeInfoPopupVisible.value = true;
|
||
}
|
||
|
||
// 隐藏等级说明弹窗
|
||
function hideGradeInfo() {
|
||
gradeInfoPopupVisible.value = false;
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.grades-page {
|
||
background-color: #f8f8f8;
|
||
height: 100%;
|
||
}
|
||
|
||
/* 顶部蓝色背景 */
|
||
.blue-header {
|
||
background-color: #2672ff;
|
||
padding: 15px 15px 50px 15px;
|
||
position: relative;
|
||
color: white;
|
||
box-sizing: border-box;
|
||
|
||
.back-icon {
|
||
position: absolute;
|
||
top: 15px;
|
||
left: 15px;
|
||
}
|
||
|
||
.more-icon {
|
||
position: absolute;
|
||
top: 15px;
|
||
right: 15px;
|
||
}
|
||
|
||
.header-content {
|
||
padding-top: 30px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.title {
|
||
font-size: 20px;
|
||
font-weight: 500;
|
||
text-align: center;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 20px;
|
||
font-weight: 500;
|
||
margin-bottom: 15px;
|
||
text-align: center;
|
||
}
|
||
|
||
.color-bar {
|
||
width: 180px;
|
||
height: 15px;
|
||
background: linear-gradient(
|
||
to right,
|
||
#29b6f6,
|
||
#4caf50,
|
||
#ff9800,
|
||
#f57c00,
|
||
#ffab91,
|
||
#ffe0b2
|
||
);
|
||
border-radius: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.total-score {
|
||
font-size: 16px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.grade {
|
||
font-size: 50px;
|
||
font-weight: bold;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.grade-info {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
|
||
.grade-explanation {
|
||
font-size: 14px;
|
||
margin-right: 15px;
|
||
position: relative;
|
||
padding-bottom: 2px;
|
||
|
||
&::after {
|
||
content: "";
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 1px;
|
||
background-color: rgba(255, 255, 255, 0.7);
|
||
}
|
||
}
|
||
|
||
.grade-box {
|
||
background-color: rgba(255, 255, 255, 0.2);
|
||
padding: 8px 20px;
|
||
border-radius: 6px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.grade-label {
|
||
font-size: 14px;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.grade-value {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 内容区域 */
|
||
.content-area {
|
||
border-top-left-radius: 20px;
|
||
border-top-right-radius: 20px;
|
||
background-color: white;
|
||
overflow: hidden;
|
||
position: absolute;
|
||
z-index: 3;
|
||
top: -70rpx;
|
||
left: 30rpx;
|
||
right: 30rpx;
|
||
bottom: 30rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
/* 选项卡 */
|
||
.tabs {
|
||
display: flex;
|
||
border-bottom: 1px solid #ebeef5;
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 15px 0;
|
||
position: relative;
|
||
color: #606266;
|
||
font-size: 15px;
|
||
|
||
&.active {
|
||
color: #1976d2;
|
||
font-weight: 500;
|
||
|
||
&::after {
|
||
content: "";
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 25%;
|
||
width: 50%;
|
||
height: 2px;
|
||
background-color: #1976d2;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 学科成绩视图 */
|
||
.score-view {
|
||
padding: 15px;
|
||
|
||
.subject-item {
|
||
padding-top: 30rpx;
|
||
.subject-header {
|
||
margin-bottom: 5px;
|
||
|
||
.subject-name {
|
||
font-size: 15px;
|
||
color: #606266;
|
||
}
|
||
}
|
||
|
||
.subject-body {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-bottom: 1px solid #ebeef5;
|
||
padding-bottom: 15px;
|
||
|
||
.subject-grade {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.detail-btn {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 分数趋势视图 */
|
||
.trend-view {
|
||
padding: 15px;
|
||
height: 100%;
|
||
|
||
.chart-placeholder {
|
||
height: 300px;
|
||
background-color: #f8f8f8;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.placeholder-text {
|
||
color: #909399;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
}
|
||
.radar-placeholder {
|
||
height: 300px;
|
||
background-color: #f8f8f8;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.placeholder-text {
|
||
color: #909399;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
|
||
/* 学科诊断视图 */
|
||
.diagnosis-view {
|
||
padding: 15px;
|
||
|
||
.diagnosis-comment {
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
background-color: #f8f8f8;
|
||
|
||
.comment-title {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
margin-bottom: 10px;
|
||
display: block;
|
||
}
|
||
|
||
.comment-text {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
line-height: 1.6;
|
||
margin-bottom: 10px;
|
||
display: block;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 等级说明弹窗 */
|
||
.grade-info-popup {
|
||
width: 280px;
|
||
background-color: #fff;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
|
||
.popup-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px;
|
||
border-bottom: 1px solid #f2f2f2;
|
||
|
||
.popup-title {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.popup-content {
|
||
padding: 10px 15px 20px;
|
||
max-height: 60vh;
|
||
overflow-y: auto;
|
||
|
||
.grade-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 12px;
|
||
padding: 8px;
|
||
border-radius: 6px;
|
||
transition: all 0.3s;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
&:hover {
|
||
background-color: #f9f9f9;
|
||
}
|
||
|
||
.grade-label {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
color: #fff;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
margin-right: 12px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.grade-desc {
|
||
flex: 1;
|
||
|
||
.grade-title {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.grade-detail {
|
||
font-size: 12px;
|
||
color: #666;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|