教师端统计页面1版

pull/868/head
3291353822.@qq.com 2026-03-21 16:59:37 +08:00
parent 36d9eab00b
commit 245581822c
1 changed files with 208 additions and 148 deletions

View File

@ -4,7 +4,6 @@
<div class="header-left">
<h2 class="main-title">教师端作业统计概览</h2>
</div>
<div class="header-center">
<el-select v-model="currentClass" class="tech-select main-selector">
<el-option label="高一(1)班" value="1" />
@ -12,7 +11,6 @@
<el-option label="高一(3)班" value="3" />
</el-select>
</div>
<div class="header-right">
<el-select v-model="currentSubject" class="tech-select subject-selector">
<el-option label="英语" value="english" />
@ -52,36 +50,86 @@
</el-col>
</el-row>
<el-row :gutter="15" class="margin-top-20 equal-height-row">
<el-col :span="12">
<div class="data-box full-height">
<div class="box-title">各题错误率排行 (Top 5)</div>
<div class="error-rank-list">
<div v-for="(item, index) in errorRank" :key="index" class="rank-item">
<span class="rank-no">{{ index + 1 }}. {{ item.name }}</span>
<div class="progress-bg">
<div class="progress-bar" :style="{ width: item.rate + '%' }"></div>
<div class="extension-tabs margin-top-20">
<el-tabs v-model="activeTab" type="border-card" class="custom-tabs">
<el-tab-pane label="学情预警" name="warning">
<el-row :gutter="15">
<el-col :span="14">
<div class="inner-box">
<div class="sub-title">成绩波动趋势</div>
<div ref="trendChartRef" style="height: 250px"></div>
</div>
<div class="rank-tag">{{ item.rate }}%</div>
</div>
</el-col>
<el-col :span="10">
<div class="inner-box">
<div class="sub-title">需关注学生 (近期下滑)</div>
<div class="warning-list">
<div v-for="stu in warningStudents" :key="stu.name" class="warning-item">
<span class="stu-name">{{ stu.name }}</span>
<span class="stu-desc">连续{{ stu.count }}次表现下降</span>
<el-tag size="small" type="danger">风险</el-tag>
</div>
</div>
</div>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="知识点分析" name="knowledge">
<el-row :gutter="15">
<el-col :span="12">
<div class="inner-box">
<div class="sub-title">核心能力雷达</div>
<div ref="radarChartRef" style="height: 250px"></div>
</div>
</el-col>
<el-col :span="12">
<div class="inner-box">
<div class="sub-title">薄弱知识点 Top 3</div>
<div class="weak-points">
<div class="point-card">1. 虚拟语气 (正确率 42%)</div>
<div class="point-card">2. 介词搭配 (正确率 55%)</div>
<div class="point-card">3. 长难句理解 (正确率 58%)</div>
</div>
</div>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="作业质量" name="quality">
<div class="inner-box">
<el-row :gutter="20" class="quality-stats">
<el-col :span="8">
<div class="q-item">
<div class="q-label">平均用时</div>
<div class="q-val">45 min</div>
</div>
</el-col>
<el-col :span="8">
<div class="q-item">
<div class="q-label">晚间 11点后提交</div>
<div class="q-val" style="color: #ff4d4f">12 </div>
</div>
</el-col>
<el-col :span="8">
<div class="q-item">
<div class="q-label">有效互动率</div>
<div class="q-val">78%</div>
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="12">
<div class="data-box full-height">
<div class="box-title">待催促名单 (表格)</div>
<el-table :data="urgeList" class="custom-table" size="small">
<el-table-column prop="name" label="姓名" align="center" />
<el-table-column prop="reason" label="未交详情" align="center" />
<el-table-column label="操作" align="center">
<template #default>
<el-button type="primary" size="small" plain class="urge-btn">催一下</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="报告生成" name="report">
<div class="report-area">
<p>系统已基于本周数据分析为该班级生成了 50 份个性化学习周报</p>
<el-button type="primary" class="urge-btn">一键推送到家长端</el-button>
<el-button type="success" plain>预览班级分析报告 (PDF)</el-button>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
@ -89,14 +137,18 @@
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'
// --- ---
const currentClass = ref('3')
const currentSubject = ref('english')
const activeTab = ref('warning')
//
const barChartRef = ref(null)
const pieChartRef = ref(null)
const miniPieRef = ref(null)
const trendChartRef = ref(null)
const radarChartRef = ref(null)
//
const stats = [
{ label: '应交数', value: '50', icon: '👥', color: '#00a2ff', bgColor: 'rgba(0,162,255,0.1)' },
{ label: '实交数', value: '42', icon: '📝', color: '#4caf50', bgColor: 'rgba(76,175,80,0.1)' },
@ -104,67 +156,40 @@ const stats = [
{ label: '批改率', value: '90%', icon: '✔️', color: '#00bcd4', bgColor: 'rgba(0,188,212,0.1)' }
]
const errorRank = [
{ name: '完形填空', rate: 80 },
{ name: '语法填空', rate: 65 },
{ name: '阅读理解A', rate: 50 },
{ name: '单词拼写', rate: 35 },
{ name: '作文', rate: 20 }
]
const urgeList = [
{ name: '张三', reason: '连续2次未交' },
{ name: '李四', reason: '连续1次未交' },
{ name: '王五', reason: '共3次未交' }
const warningStudents = [
{ name: '王同学', count: 3 },
{ name: '李同学', count: 2 },
{ name: '赵同学', count: 2 }
]
onMounted(() => {
renderCharts()
renderBaseCharts()
renderExtensionCharts()
})
const renderCharts = () => {
const renderBaseCharts = () => {
//
const bar = echarts.init(barChartRef.value)
bar.setOption({
tooltip: { trigger: 'axis', axisPointer: { type: 'none' } },
grid: { top: '15%', bottom: '15%', left: '8%', right: '5%' },
xAxis: {
data: ['<60', '60-70', '70-80', '80-90', '>90'],
axisLine: { lineStyle: { color: 'rgba(0,162,255,0.2)' } },
axisLabel: { color: '#8899aa' }
},
xAxis: { data: ['<60', '60-70', '70-80', '80-90', '>90'], axisLabel: { color: '#8899aa' } },
yAxis: {
splitLine: { lineStyle: { color: 'rgba(255,255,255,0.05)' } },
axisLabel: { color: '#8899aa' }
},
series: [
{
type: 'bar',
data: [3, 8, 15, 20, 4],
barWidth: 20,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#00e5ff' },
{ offset: 1, color: '#0072ff' }
]),
borderRadius: [4, 4, 0, 0]
}
}
]
series: [{ type: 'bar', data: [3, 8, 15, 20, 4], itemStyle: { color: '#00e5ff' } }]
})
const pieData = [
{ value: 16, name: '未交' },
{ value: 84, name: '已交' }
]
const pie = echarts.init(pieChartRef.value)
pie.setOption({
series: [
{
type: 'pie',
radius: ['45%', '70%'],
data: pieData,
label: { show: true, formatter: '{b}\n{d}%', color: '#8899aa' },
itemStyle: { color: (p) => (p.name === '已交' ? '#00e5ff' : 'rgba(0, 114, 255, 0.4)') }
data: [
{ value: 16, name: '未交' },
{ value: 84, name: '已交' }
],
label: { color: '#8899aa' }
}
]
})
@ -176,9 +201,48 @@ const renderCharts = () => {
type: 'pie',
radius: ['70%', '100%'],
silent: true,
data: pieData,
label: { show: false },
itemStyle: { color: (p) => (p.name === '已交' ? '#00e5ff' : 'rgba(0, 114, 255, 0.4)') }
data: [
{ value: 16, name: '未交' },
{ value: 84, name: '已交' }
],
label: { show: false }
}
]
})
}
const renderExtensionCharts = () => {
// 1. 线
const trend = echarts.init(trendChartRef.value)
trend.setOption({
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五'],
axisLabel: { color: '#8899aa' }
},
yAxis: { type: 'value', axisLabel: { color: '#8899aa' }, splitLine: { show: false } },
series: [{ data: [82, 85, 84, 88, 85], type: 'line', smooth: true, color: '#00e5ff' }]
})
// 2.
const radar = echarts.init(radarChartRef.value)
radar.setOption({
radar: {
indicator: [
{ name: '词汇', max: 100 },
{ name: '语法', max: 100 },
{ name: '阅读', max: 100 },
{ name: '写作', max: 100 },
{ name: '听力', max: 100 }
],
axisName: { color: '#8899aa' }
},
series: [
{
type: 'radar',
data: [{ value: [85, 60, 90, 75, 80], name: '班级能力值' }],
itemStyle: { color: '#ff9800' },
areaStyle: { color: 'rgba(255,152,0,0.3)' }
}
]
})
@ -193,11 +257,11 @@ const renderCharts = () => {
color: #fff;
.header-section {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
position: relative;
.main-title {
color: #00e5ff;
font-size: 24px;
@ -215,7 +279,6 @@ const renderCharts = () => {
.el-input__wrapper {
background-color: rgba(0, 162, 255, 0.05) !important;
box-shadow: 0 0 0 1px rgba(0, 162, 255, 0.4) inset !important;
border-radius: 2px;
}
.el-input__inner {
color: #00e5ff !important;
@ -242,16 +305,10 @@ const renderCharts = () => {
align-items: center;
justify-content: center;
margin-right: 12px;
font-size: 20px;
}
.label {
font-size: 13px;
color: #8899aa;
}
.value {
font-size: 22px;
font-weight: bold;
margin-top: 4px;
}
}
@ -260,97 +317,100 @@ const renderCharts = () => {
border: 1px solid rgba(0, 162, 255, 0.15);
padding: 15px;
border-radius: 4px;
&.relative {
position: relative;
}
.box-title {
font-size: 15px;
margin-bottom: 15px;
border-left: 4px solid #00a2ff;
padding-left: 10px;
margin-bottom: 15px;
color: #e0e6ed;
}
}
.mini-pie-wrapper {
position: absolute;
top: 15px;
right: 15px;
padding: 4px;
border: 1px dashed rgba(0, 229, 255, 0.2);
border-radius: 50%;
}
.chart-height {
height: 210px;
}
.error-rank-list .rank-item {
display: flex;
align-items: center;
margin-bottom: 15px;
.rank-no {
width: 90px;
color: #8899aa;
font-size: 13px;
/* 拓展 Tabs 样式定制 */
.custom-tabs {
background: rgba(255, 255, 255, 0.02) !important;
border: 1px solid rgba(0, 162, 255, 0.15) !important;
:deep(.el-tabs__header) {
background-color: rgba(0, 162, 255, 0.05) !important;
border-bottom: 1px solid rgba(0, 162, 255, 0.15) !important;
}
.progress-bg {
flex: 1;
height: 6px;
background: #1a2f45;
margin: 0 15px;
border-radius: 3px;
overflow: hidden;
.progress-bar {
height: 100%;
background: linear-gradient(to right, #0072ff, #00e5ff);
:deep(.el-tabs__item) {
color: #8899aa !important;
&.is-active {
color: #00e5ff !important;
background-color: rgba(0, 162, 255, 0.1) !important;
}
}
.rank-tag {
background: rgba(0, 229, 255, 0.1);
border: 1px solid rgba(0, 229, 255, 0.3);
color: #00e5ff;
padding: 1px 8px;
border-radius: 10px;
font-size: 11px;
min-width: 42px;
text-align: center;
.inner-box {
padding: 10px;
.sub-title {
font-size: 14px;
color: #00e5ff;
margin-bottom: 10px;
}
}
}
.custom-table {
background: transparent !important;
--el-table-bg-color: transparent !important;
--el-table-tr-bg-color: transparent !important;
--el-table-header-bg-color: rgba(0, 162, 255, 0.05) !important;
--el-table-border-color: rgba(0, 162, 255, 0.1) !important;
:deep(th.el-table__cell) {
border-bottom: none !important;
color: #00e5ff;
.warning-list .warning-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: rgba(255, 77, 79, 0.05);
margin-bottom: 8px;
border-radius: 4px;
.stu-name {
color: #e0e6ed;
}
:deep(td.el-table__cell) {
border-bottom: 1px solid rgba(0, 162, 255, 0.05) !important;
.stu-desc {
font-size: 12px;
color: #8899aa;
}
}
.equal-height-row {
display: flex;
& > .el-col {
display: flex;
.point-card {
background: rgba(0, 229, 255, 0.05);
border: 1px solid rgba(0, 229, 255, 0.1);
padding: 12px;
margin-bottom: 10px;
border-radius: 4px;
color: #e0e6ed;
}
.quality-stats .q-item {
text-align: center;
padding: 20px;
background: rgba(255, 255, 255, 0.02);
border-radius: 8px;
.q-label {
color: #8899aa;
margin-bottom: 10px;
}
.full-height {
flex: 1;
display: flex;
flex-direction: column;
.q-val {
font-size: 24px;
font-weight: bold;
color: #00e5ff;
}
}
.report-area {
padding: 30px;
text-align: center;
p {
margin-bottom: 20px;
color: #8899aa;
}
}
.chart-height {
height: 210px;
}
.margin-top-20 {
margin-top: 20px;
}
.urge-btn {
background: rgba(0, 162, 255, 0.2);
border-color: #00a2ff;
color: #00e5ff;
background: rgba(0, 162, 255, 0.1);
}
}
</style>