You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dianshang-qianduan/src/views/teacherEnd/index.vue

1162 lines
28 KiB
Vue

2 years ago
<template>
<div class="grade-center">
<!-- 页面标题区 -->
<div class="page-header">
<div class="header-content">
<div class="header-icon-wrap">
<svg class="header-icon-svg" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
2 years ago
</div>
<div class="header-text">
<h1 class="header-title">成绩管理中心</h1>
<p class="header-subtitle">按班级管理 · 数据分析 · 趋势追踪</p>
</div>
</div>
<div class="header-decoration">
<div class="decoration-dot"></div>
<div class="decoration-dot"></div>
<div class="decoration-dot"></div>
</div>
</div>
<!-- 卡片区域 -->
<div class="cards-wrapper">
<!-- 左侧:走势图 -->
<div class="card trend-card">
<div class="card-glow"></div>
<div class="card-inner">
<div class="card-header">
<div class="card-header-left">
<div class="card-icon trend-icon">
<svg viewBox="0 0 24 24" fill="none">
<path d="M3 3V21H21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 14L11 10L15 14L21 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="card-title-group">
<h2 class="card-title">班级平均成绩走势图</h2>
<span class="card-badge">实时数据</span>
</div>
</div>
<div class="card-header-right">
<div class="legend-dots">
<span class="legend-item" v-for="(name, idx) in className" :key="idx">
<span class="legend-dot" :style="{ background: getLineColor(idx) }"></span>
{{ name }}
</span>
</div>
</div>
</div>
<div class="card-body">
<div id="trend" class="chart-area"></div>
</div>
<div class="card-footer">
<div class="trend-insight">
<span class="insight-icon">📈</span>
<span class="insight-text">成绩走势趋势分析</span>
</div>
</div>
</div>
</div>
<!-- 右侧:统计分析 -->
<div class="card stats-card">
<div class="card-glow"></div>
<div class="card-inner">
<div class="card-header">
<div class="card-header-left">
<div class="card-icon stats-icon">
<svg viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
<path d="M12 6V12L16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<div class="card-title-group">
<h2 class="card-title">班级成绩统计分析</h2>
<span class="card-badge success">已更新</span>
</div>
</div>
</div>
<!-- 搜索筛选区 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<label class="filter-label">
<span class="label-icon">📅</span>
日期筛选
</label>
<el-date-picker
v-model="classParams.time"
class="custom-date-picker"
type="datetime"
placeholder="选择日期时间"
format="YYYY/MM/DD HH:mm"
:clearable="true"
/>
</div>
<div class="filter-item">
<label class="filter-label">
<span class="label-icon">🏫</span>
班级选择
</label>
<el-select placeholder="全部班级" v-model="classParams.classId" class="custom-select" :clearable="true">
<el-option v-for="item in userStore.classList" :key="item" :label="item.class_name" :value="item.class_id" />
4 months ago
</el-select>
</div>
<div class="filter-item filter-action">
<el-button type="primary" class="search-btn" @click="getClassGradeStatistics">
<span class="btn-icon-wrap">
<svg viewBox="0 0 24 24" fill="none" class="btn-svg">
<circle cx="11" cy="11" r="8" stroke="currentColor" stroke-width="2"/>
<path d="M21 21L16.65 16.65" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</span>
查询成绩
</el-button>
</div>
</div>
4 months ago
</div>
<!-- 图表与数据 -->
<div class="stats-content">
<div class="chart-section">
<div id="statistics" class="pie-chart"></div>
</div>
<div class="score-cards">
<div class="score-card" v-for="item in circularData2" :key="item.name" :class="getScoreCardClass(item.name)">
<div class="score-card-inner">
<div class="score-indicator" :class="getIndicatorClass(item.name)">
<span class="indicator-arrow">{{ getIndicatorArrow(item.name) }}</span>
</div>
<div class="score-info">
<span class="score-name">{{ item.name }}</span>
<span class="score-number">{{ item.value ?? 0 }}</span>
<span class="score-unit">分</span>
</div>
</div>
<div class="score-decoration"></div>
4 months ago
</div>
</div>
</div>
</div>
</div>
4 months ago
</div>
2 years ago
</div>
</template>
<script setup>
4 months ago
import * as echarts from "echarts";
import * as teacherApi from "@/api/teacher.js";
const { proxy } = getCurrentInstance();
import { getAssetsFile } from "@/utils/index.js";
4 months ago
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
const classTime = ref([]);
const className = ref([]);
const classList = ref([]);
const classParams = ref({
classId: "",
time: "",
2 years ago
});
const initstatus = ref(false);
let trendChart = null;
let statisticsChart = null;
4 months ago
const circularData = ref([
{ value: 0, name: "一般", key: "generalCount" },
{ value: 0, name: "优秀", key: "excellentCount" },
{ value: 0, name: "良好", key: "goodCount" },
{ value: 0, name: "不及格", key: "failCount" },
]);
const circularData2 = ref([
{ score: 0, name: "最高分", key: "maxScore" },
{ score: 0, name: "最低分", key: "minScore" },
{ score: 0, name: "平均分", key: "averageScore" },
]);
// 成绩卡片样式
const getScoreCardClass = (name) => {
const map = {
"最高分": "card-max",
"最低分": "card-min",
"平均分": "card-avg",
};
return map[name] || "";
};
const getIndicatorClass = (name) => {
const map = {
"最高分": "ind-max",
"最低分": "ind-min",
"平均分": "ind-avg",
};
return map[name] || "";
};
const getIndicatorArrow = (name) => {
const map = {
"最高分": "▲",
"最低分": "▼",
"平均分": "◆",
};
return map[name] || "●";
};
// 折线图配色方案(亮色背景:科技蓝系分类色)
const getLineColor = (index) => {
const colors = [
{ main: "#1e88e5", light: "#9cc8ef" },
{ main: "#2ba471", light: "#9fd8bd" },
{ main: "#ec6b8f", light: "#f5b8cb" },
{ main: "#f0a830", light: "#f8d59a" },
{ main: "#7e57c2", light: "#c4b0e4" },
{ main: "#26a69a", light: "#9fd8d1" },
];
return colors[index % colors.length].main;
};
4 months ago
// 获取成绩走势图数据
const getTrendData = async () => {
trendChart = trendChart || echarts.init(document.getElementById("trend"));
const myinde = trendChart;
4 months ago
myinde.setOption({
backgroundColor: "transparent",
4 months ago
tooltip: {
trigger: "axis",
backgroundColor: "rgba(255, 255, 255, 0.98)",
borderColor: "rgba(30, 136, 229, 0.3)",
borderWidth: 1,
padding: [12, 16],
textStyle: {
color: "#1e3a5f",
fontSize: 13,
},
axisPointer: {
type: "cross",
lineStyle: {
color: "rgba(30, 136, 229, 0.35)",
type: "dashed",
},
crossStyle: {
color: "rgba(30, 136, 229, 0.5)",
},
},
formatter: function(params) {
let result = `<div style="font-weight:600;margin-bottom:8px;">${params[0].axisValue}</div>`;
params.forEach(param => {
result += `<div style="display:flex;align-items:center;gap:8px;margin:4px 0;">
<span style="width:10px;height:10px;border-radius:50%;background:${param.color};"></span>
<span style="flex:1;">${param.seriesName}</span>
<span style="font-weight:600;color:${param.color};">${param.value}分</span>
</div>`;
});
return result;
},
4 months ago
},
legend: {
show: false,
4 months ago
},
grid: {
left: "3%",
4 months ago
right: "4%",
bottom: "8%",
top: "8%",
4 months ago
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: classTime.value,
axisLine: {
lineStyle: {
color: "rgba(30, 136, 229, 0.25)",
},
},
axisLabel: {
color: "#6b85a3",
fontSize: 11,
margin: 12,
},
axisTick: {
show: false,
},
4 months ago
},
yAxis: {
type: "value",
min: 0,
max: 100,
splitLine: {
lineStyle: {
color: "rgba(30, 136, 229, 0.12)",
type: "dashed",
},
},
axisLine: {
show: false,
},
axisLabel: {
color: "#6b85a3",
fontSize: 11,
formatter: "{value}",
},
4 months ago
},
series: classList.value.map((item, index) => {
const colorSet = [
{ main: "#1e88e5", light: "rgba(30, 136, 229, 0.22)" },
{ main: "#2ba471", light: "rgba(43, 164, 113, 0.2)" },
{ main: "#ec6b8f", light: "rgba(236, 107, 143, 0.2)" },
{ main: "#f0a830", light: "rgba(240, 168, 48, 0.2)" },
{ main: "#7e57c2", light: "rgba(126, 87, 194, 0.2)" },
{ main: "#26a69a", light: "rgba(38, 166, 154, 0.2)" },
][index % 6];
return {
...item,
smooth: 0.4,
lineStyle: {
width: 3,
color: colorSet.main,
shadowColor: colorSet.main,
shadowBlur: 8,
},
itemStyle: {
color: colorSet.main,
},
areaStyle: {
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colorSet.light },
{ offset: 1, color: "rgba(0, 0, 0, 0)" },
],
},
},
symbol: "circle",
symbolSize: 6,
emphasis: {
scale: true,
scaleSize: 10,
},
};
}),
4 months ago
});
};
// 饼图配色方案(亮色背景:科技蓝系分类色)
const getPieColor = (index) => {
const colors = [
{ main: "#1e88e5", light: "rgba(30, 136, 229, 0.2)" },
{ main: "#26a69a", light: "rgba(38, 166, 154, 0.2)" },
{ main: "#7e57c2", light: "rgba(126, 87, 194, 0.2)" },
{ main: "#ec6b8f", light: "rgba(236, 107, 143, 0.2)" },
];
return colors[index % colors.length].main;
};
4 months ago
// 成绩统计分析
const getStatisticsData = async () => {
statisticsChart = statisticsChart || echarts.init(document.getElementById("statistics"));
const myChart = statisticsChart;
4 months ago
myChart.setOption({
backgroundColor: "transparent",
4 months ago
tooltip: {
trigger: "item",
backgroundColor: "rgba(255, 255, 255, 0.98)",
borderColor: "rgba(30, 136, 229, 0.3)",
borderWidth: 1,
padding: [12, 16],
textStyle: {
color: "#1e3a5f",
fontSize: 13,
},
formatter: "{a} <br/>{b} : {c}人 ({d}%)",
4 months ago
},
legend: {
orient: "vertical",
right: "2%",
4 months ago
top: "center",
itemWidth: 12,
itemHeight: 12,
itemGap: 16,
textStyle: {
color: "#5a7896",
fontSize: 13,
},
formatter: (name) => {
const item = circularData.value.find((d) => d.name === name);
const num = item?.value || 0;
return `{name|${name}} {num|${num}人}`;
},
textStyle: {
rich: {
name: { color: "#5a7896", fontSize: 13 },
num: { color: "#1e88e5", fontSize: 13, fontWeight: 600 },
},
},
4 months ago
},
series: [
{
type: "pie",
radius: ["50%", "75%"],
center: ["35%", "50%"],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 8,
borderColor: "#ffffff",
borderWidth: 3,
},
label: {
show: false,
},
4 months ago
emphasis: {
scale: true,
scaleSize: 12,
4 months ago
itemStyle: {
shadowBlur: 16,
shadowColor: "rgba(30, 136, 229, 0.35)",
4 months ago
},
},
data: circularData.value.map((item, index) => ({
...item,
itemStyle: {
color: {
type: "linear",
x: 0, y: 0, x2: 1, y2: 1,
colorStops: [
{ offset: 0, color: getPieColor(index) },
{ offset: 1, color: adjustColor(getPieColor(index), -30) },
],
},
},
})),
4 months ago
},
],
});
};
// 颜色调整函数
const adjustColor = (hex, amount) => {
const num = parseInt(hex.replace("#", ""), 16);
const r = Math.max(0, Math.min(255, (num >> 16) + amount));
const g = Math.max(0, Math.min(255, ((num >> 8) & 0x00FF) + amount));
const b = Math.max(0, Math.min(255, (num & 0x0000FF) + amount));
return "#" + (0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1);
};
4 months ago
// 获取班级成绩走势函数功能函数
const getClassGradeTrend = () => {
teacherApi.getAverageScoreTrend().then((res) => {
const trends = Array.isArray(res) ? res : [];
classTime.value = trends[0]?.startTime || [];
className.value = trends.map((item) => item.className);
classList.value = trends.map((item) => {
4 months ago
return {
name: item.className,
type: "line",
stack: "Total",
data: item.classAverageScore,
4 months ago
};
});
getTrendData();
});
};
// 获取班级成绩统计分析函数功能函数
const getClassGradeStatistics = () => {
if (!classParams.value.time) {
classParams.value.time = new Date().toString();
} else {
classParams.value.time = classParams.value.time.toString();
2 years ago
}
if (initstatus.value && !classParams.value.classId) {
proxy.$modal.msgWarning("请选择班级");
}
4 months ago
teacherApi.getScoreStatisticsTrend(classParams.value).then((res) => {
circularData.value.forEach((item, index) => {
item.value = res[item.key];
if (res.hasOwnProperty(item.key)) {
item.value = res[item.key];
2 years ago
}
4 months ago
});
circularData2.value.forEach((item, index) => {
item.value = res[item.key];
if (res.hasOwnProperty(item.key)) {
item.value = res[item.key];
2 years ago
}
4 months ago
});
getStatisticsData();
});
};
// 获取班级
const getClassList = () => {
teacherApi.getOwnedTeachingClasses().then((res) => {
userStore.classList = (res.data || []).map((item) => ({
class_name: item.className,
class_id: item.schoolClassId,
4 months ago
}));
});
};
onMounted(() => {
getClassGradeTrend();
getClassGradeStatistics();
getClassList();
initstatus.value = true;
requestAnimationFrame(() => {
trendChart?.resize();
statisticsChart?.resize();
});
});
const handleResize = () => {
trendChart?.resize();
statisticsChart?.resize();
};
window.addEventListener("resize", handleResize);
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
trendChart?.dispose();
statisticsChart?.dispose();
trendChart = null;
statisticsChart = null;
4 months ago
});
</script>
<style lang="scss" scoped>
// 页面主容器
.grade-center {
min-height: calc(100vh - 96px);
display: flex;
flex-direction: column;
gap: 20px;
}
// 页面标题区
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 28px;
background: linear-gradient(135deg, #ffffff 0%, #f3f8ff 100%);
border: 1px solid rgba(30, 136, 229, 0.18);
border-radius: 16px;
position: relative;
overflow: hidden;
box-shadow: 0 6px 20px rgba(30, 60, 100, 0.08);
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(30, 136, 229, 0.45), transparent);
}
.header-content {
4 months ago
display: flex;
align-items: center;
gap: 16px;
position: relative;
z-index: 1;
}
.header-icon-wrap {
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(30, 136, 229, 0.14), rgba(66, 165, 245, 0.06));
border-radius: 14px;
border: 1px solid rgba(30, 136, 229, 0.3);
.header-icon-svg {
width: 28px;
height: 28px;
color: #1e88e5;
}
}
.header-text {
.header-title {
font-size: 22px;
font-weight: 700;
color: #1e3a5f;
margin: 0;
letter-spacing: 1px;
text-shadow: none;
}
.header-subtitle {
font-size: 13px;
color: #5a7896;
margin: 4px 0 0;
letter-spacing: 0.5px;
}
}
.header-decoration {
display: flex;
gap: 8px;
position: relative;
z-index: 1;
.decoration-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(30, 136, 229, 0.45);
&:nth-child(2) {
background: rgba(43, 164, 113, 0.45);
animation: pulse 2s ease-in-out infinite;
2 years ago
}
&:nth-child(3) {
background: rgba(126, 87, 194, 0.45);
2 years ago
}
}
}
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.45; }
50% { transform: scale(1.5); opacity: 0.85; }
}
// 卡片容器
.cards-wrapper {
display: flex;
gap: 20px;
flex: 1;
min-height: 580px;
}
.card {
position: relative;
background: #ffffff;
border: 1px solid rgba(30, 136, 229, 0.15);
border-radius: 20px;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 6px 20px rgba(30, 60, 100, 0.06);
&:hover {
border-color: rgba(30, 136, 229, 0.3);
transform: translateY(-2px);
box-shadow: 0 20px 60px rgba(30, 136, 229, 0.1);
}
.card-glow {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle at 30% 30%, rgba(30, 136, 229, 0.05), transparent 50%);
pointer-events: none;
}
.card-inner {
position: relative;
z-index: 1;
height: 100%;
display: flex;
flex-direction: column;
padding: 24px;
}
}
// 卡片头部
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
flex-shrink: 0;
.card-header-left {
display: flex;
align-items: center;
gap: 14px;
}
.card-icon {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
padding: 10px;
svg {
width: 24px;
height: 24px;
}
&.trend-icon {
background: linear-gradient(135deg, rgba(30, 136, 229, 0.18), rgba(30, 136, 229, 0.06));
border: 1px solid rgba(30, 136, 229, 0.3);
color: #1e88e5;
}
&.stats-icon {
background: linear-gradient(135deg, rgba(43, 164, 113, 0.18), rgba(43, 164, 113, 0.06));
border: 1px solid rgba(43, 164, 113, 0.3);
color: #2ba471;
}
}
.card-title-group {
.card-title {
font-size: 17px;
font-weight: 600;
color: #1e3a5f;
margin: 0;
letter-spacing: 0.5px;
}
.card-badge {
display: inline-block;
margin-top: 4px;
padding: 2px 10px;
font-size: 11px;
color: #1565c0;
background: rgba(30, 136, 229, 0.1);
border: 1px solid rgba(30, 136, 229, 0.25);
border-radius: 20px;
&.success {
color: #1e7a4f;
background: rgba(43, 164, 113, 0.1);
border-color: rgba(43, 164, 113, 0.25);
}
}
}
.card-header-right {
.legend-dots {
display: flex;
flex-wrap: wrap;
gap: 12px;
max-width: 280px;
.legend-item {
4 months ago
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: #5a7896;
.legend-dot {
width: 10px;
height: 10px;
border-radius: 50%;
box-shadow: 0 0 8px currentColor;
4 months ago
}
2 years ago
}
}
}
}
// 卡片主体
.card-body {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.chart-area {
width: 100%;
height: 100%;
min-height: 380px;
}
// 卡片底部
.card-footer {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(30, 136, 229, 0.12);
flex-shrink: 0;
.trend-insight {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
.insight-icon {
font-size: 16px;
}
.insight-text {
font-size: 13px;
color: #5a7896;
letter-spacing: 2px;
}
}
}
// 左侧走势图卡片
.trend-card {
flex: 1.3;
}
// 右侧统计卡片
.stats-card {
flex: 0.9;
min-width: 420px;
.stats-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
}
.chart-section {
flex: 1;
min-height: 220px;
.pie-chart {
width: 100%;
height: 100%;
min-height: 220px;
}
}
}
// 筛选区域
.filter-section {
margin-bottom: 20px;
padding: 16px 18px;
background: #f8fbff;
border: 1px solid rgba(30, 136, 229, 0.12);
border-radius: 14px;
flex-shrink: 0;
.filter-row {
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
flex-direction: column;
gap: 6px;
&.filter-action {
margin-left: auto;
}
.filter-label {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: #2c4a6a;
font-weight: 500;
.label-icon {
font-size: 13px;
2 years ago
}
}
}
// 输入框样式
:deep(.el-input__wrapper) {
background: #ffffff !important;
border: 1px solid rgba(30, 136, 229, 0.2) !important;
box-shadow: none !important;
border-radius: 10px !important;
padding: 4px 12px !important;
transition: all 0.3s ease;
&:hover,
&:focus {
border-color: rgba(30, 136, 229, 0.5) !important;
box-shadow: 0 0 12px rgba(30, 136, 229, 0.12) !important;
}
}
:deep(.el-input__inner) {
color: #2c4a6a !important;
font-size: 13px;
&::placeholder {
color: #8aa8c4 !important;
}
}
:deep(.el-input__prefix) {
color: rgba(30, 136, 229, 0.7);
}
// 日期选择器样式
:deep(.el-date-editor) {
width: 180px !important;
}
// 下拉选择器样式
:deep(.el-select) {
width: 160px;
}
:deep(.el-select__caret) {
color: rgba(30, 136, 229, 0.7) !important;
}
:deep(.el-select-dropdown) {
background: #ffffff !important;
border: 1px solid rgba(30, 136, 229, 0.2) !important;
.el-select-dropdown__item {
color: #2c4a6a;
&:hover,
&.hover {
background: rgba(30, 136, 229, 0.08) !important;
4 months ago
}
&.selected {
color: #1e88e5;
font-weight: 600;
4 months ago
}
2 years ago
}
}
// 按钮样式
.search-btn {
display: flex;
align-items: center;
gap: 8px;
background: #1e88e5 !important;
border: 1px solid #1e88e5 !important;
color: #ffffff !important;
padding: 10px 20px !important;
border-radius: 10px !important;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
.btn-icon-wrap {
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
.btn-svg {
width: 16px;
height: 16px;
2 years ago
}
4 months ago
}
&:hover {
background: #42a5f5 !important;
border-color: #42a5f5 !important;
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(30, 136, 229, 0.25);
}
&:active {
transform: translateY(0);
}
}
}
// 分数卡片组
.score-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
flex-shrink: 0;
}
.score-card {
position: relative;
padding: 16px;
background: #f8fbff;
border: 1px solid rgba(30, 136, 229, 0.15);
border-radius: 14px;
overflow: hidden;
transition: all 0.3s ease;
&:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(30, 60, 100, 0.12);
}
.score-card-inner {
display: flex;
align-items: center;
gap: 14px;
position: relative;
z-index: 1;
}
.score-decoration {
position: absolute;
bottom: -20px;
right: -20px;
width: 80px;
height: 80px;
border-radius: 50%;
opacity: 0.12;
}
&.card-max {
border-color: rgba(236, 107, 143, 0.3);
background: linear-gradient(135deg, rgba(236, 107, 143, 0.08), #f8fbff);
.score-decoration {
background: linear-gradient(135deg, #ec6b8f, #e0557d);
}
&:hover {
border-color: rgba(236, 107, 143, 0.45);
box-shadow: 0 12px 32px rgba(236, 107, 143, 0.15);
}
}
&.card-min {
border-color: rgba(30, 136, 229, 0.3);
background: linear-gradient(135deg, rgba(30, 136, 229, 0.08), #f8fbff);
.score-decoration {
background: linear-gradient(135deg, #1e88e5, #1976d2);
}
&:hover {
border-color: rgba(30, 136, 229, 0.45);
box-shadow: 0 12px 32px rgba(30, 136, 229, 0.15);
}
}
&.card-avg {
border-color: rgba(240, 168, 48, 0.3);
background: linear-gradient(135deg, rgba(240, 168, 48, 0.08), #f8fbff);
.score-decoration {
background: linear-gradient(135deg, #f0a830, #d68f1a);
}
&:hover {
border-color: rgba(240, 168, 48, 0.45);
box-shadow: 0 12px 32px rgba(240, 168, 48, 0.15);
}
}
.score-indicator {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
flex-shrink: 0;
.indicator-arrow {
font-size: 18px;
color: #fff;
}
&.ind-max {
background: linear-gradient(135deg, #ec6b8f, #e0557d);
box-shadow: 0 4px 16px rgba(236, 107, 143, 0.35);
}
&.ind-min {
background: linear-gradient(135deg, #1e88e5, #1976d2);
box-shadow: 0 4px 16px rgba(30, 136, 229, 0.35);
}
&.ind-avg {
background: linear-gradient(135deg, #f0a830, #d68f1a);
box-shadow: 0 4px 16px rgba(240, 168, 48, 0.35);
}
}
.score-info {
display: flex;
flex-direction: column;
gap: 2px;
.score-name {
font-size: 12px;
color: #5a7896;
font-weight: 500;
}
.score-number {
font-size: 28px;
font-weight: 700;
line-height: 1;
background: linear-gradient(135deg, #1e3a5f, #2c4a6a);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.score-unit {
font-size: 11px;
color: #6b85a3;
margin-left: 2px;
}
2 years ago
}
}
// 响应式适配
@media (max-width: 1200px) {
.cards-wrapper {
flex-direction: column;
}
.stats-card {
min-width: unset;
}
.score-cards {
grid-template-columns: repeat(3, 1fr);
}
2 years ago
}
@media (max-width: 768px) {
.grade-center {
2 years ago
}
.page-header {
padding: 16px 20px;
flex-direction: column;
align-items: flex-start;
gap: 12px;
.header-decoration {
display: none;
}
}
.score-cards {
grid-template-columns: 1fr;
gap: 10px;
}
.filter-section .filter-row {
flex-direction: column;
align-items: stretch;
.filter-item.filter-action {
margin-left: 0;
}
2 years ago
}
}
</style>