|
|
|
|
|
<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>
|
|
|
|
|
|
</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" />
|
|
|
|
|
|
</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>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import * as echarts from "echarts";
|
|
|
|
|
|
import * as teacherApi from "@/api/teacher.js";
|
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
|
import { getAssetsFile } from "@/utils/index.js";
|
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
const classTime = ref([]);
|
|
|
|
|
|
const className = ref([]);
|
|
|
|
|
|
const classList = ref([]);
|
|
|
|
|
|
const classParams = ref({
|
|
|
|
|
|
classId: "",
|
|
|
|
|
|
time: "",
|
|
|
|
|
|
});
|
|
|
|
|
|
const initstatus = ref(false);
|
|
|
|
|
|
let trendChart = null;
|
|
|
|
|
|
let statisticsChart = null;
|
|
|
|
|
|
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;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取成绩走势图数据
|
|
|
|
|
|
const getTrendData = async () => {
|
|
|
|
|
|
trendChart = trendChart || echarts.init(document.getElementById("trend"));
|
|
|
|
|
|
const myinde = trendChart;
|
|
|
|
|
|
myinde.setOption({
|
|
|
|
|
|
backgroundColor: "transparent",
|
|
|
|
|
|
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;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
legend: {
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
left: "3%",
|
|
|
|
|
|
right: "4%",
|
|
|
|
|
|
bottom: "8%",
|
|
|
|
|
|
top: "8%",
|
|
|
|
|
|
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,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
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}",
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
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,
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
}),
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 饼图配色方案(亮色背景:科技蓝系分类色)
|
|
|
|
|
|
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;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 成绩统计分析
|
|
|
|
|
|
const getStatisticsData = async () => {
|
|
|
|
|
|
statisticsChart = statisticsChart || echarts.init(document.getElementById("statistics"));
|
|
|
|
|
|
const myChart = statisticsChart;
|
|
|
|
|
|
myChart.setOption({
|
|
|
|
|
|
backgroundColor: "transparent",
|
|
|
|
|
|
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}%)",
|
|
|
|
|
|
},
|
|
|
|
|
|
legend: {
|
|
|
|
|
|
orient: "vertical",
|
|
|
|
|
|
right: "2%",
|
|
|
|
|
|
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 },
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: "pie",
|
|
|
|
|
|
radius: ["50%", "75%"],
|
|
|
|
|
|
center: ["35%", "50%"],
|
|
|
|
|
|
avoidLabelOverlap: true,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
|
borderColor: "#ffffff",
|
|
|
|
|
|
borderWidth: 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
emphasis: {
|
|
|
|
|
|
scale: true,
|
|
|
|
|
|
scaleSize: 12,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
shadowBlur: 16,
|
|
|
|
|
|
shadowColor: "rgba(30, 136, 229, 0.35)",
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
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) },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
})),
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 颜色调整函数
|
|
|
|
|
|
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);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取班级成绩走势函数功能函数
|
|
|
|
|
|
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) => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: item.className,
|
|
|
|
|
|
type: "line",
|
|
|
|
|
|
stack: "Total",
|
|
|
|
|
|
data: item.classAverageScore,
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
getTrendData();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
// 获取班级成绩统计分析函数功能函数
|
|
|
|
|
|
const getClassGradeStatistics = () => {
|
|
|
|
|
|
if (!classParams.value.time) {
|
|
|
|
|
|
classParams.value.time = new Date().toString();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
classParams.value.time = classParams.value.time.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (initstatus.value && !classParams.value.classId) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请选择班级");
|
|
|
|
|
|
}
|
|
|
|
|
|
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];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
circularData2.value.forEach((item, index) => {
|
|
|
|
|
|
item.value = res[item.key];
|
|
|
|
|
|
if (res.hasOwnProperty(item.key)) {
|
|
|
|
|
|
item.value = res[item.key];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
getStatisticsData();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
// 获取班级
|
|
|
|
|
|
const getClassList = () => {
|
|
|
|
|
|
teacherApi.getOwnedTeachingClasses().then((res) => {
|
|
|
|
|
|
userStore.classList = (res.data || []).map((item) => ({
|
|
|
|
|
|
class_name: item.className,
|
|
|
|
|
|
class_id: item.schoolClassId,
|
|
|
|
|
|
}));
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
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;
|
|
|
|
|
|
});
|
|
|
|
|
|
</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 {
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:nth-child(3) {
|
|
|
|
|
|
background: rgba(126, 87, 194, 0.45);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@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 {
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 卡片主体
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 输入框样式
|
|
|
|
|
|
: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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.selected {
|
|
|
|
|
|
color: #1e88e5;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 按钮样式
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&: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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式适配
|
|
|
|
|
|
@media (max-width: 1200px) {
|
|
|
|
|
|
.cards-wrapper {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stats-card {
|
|
|
|
|
|
min-width: unset;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-cards {
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.grade-center {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|