Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-resource
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-resource
Commits
c8379c83
提交
c8379c83
authored
9月 25, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 新增数据可视化
上级
bd9a443c
隐藏空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
628 行增加
和
0 行删除
+628
-0
menus.ts
src/assets/menus.ts
+6
-0
index.ts
src/modules/teach/chart/index.ts
+1
-0
Visualization.vue
src/modules/teach/chart/views/Visualization.vue
+621
-0
没有找到文件。
src/assets/menus.ts
浏览文件 @
c8379c83
...
@@ -210,6 +210,12 @@ export const menus: IMenuItem[] = [
...
@@ -210,6 +210,12 @@ export const menus: IMenuItem[] = [
// import.meta.env.VITE_BI_URL +
// import.meta.env.VITE_BI_URL +
// '/bi/?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!5b66!!4e60!!884c!!4e3a!!753b!!50cf!.db&platform=PC&browserType=chrome',
// '/bi/?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!5b66!!4e60!!884c!!4e3a!!753b!!50cf!.db&platform=PC&browserType=chrome',
},
},
{
tag
:
''
,
icon
:
DataAnalysis
,
name
:
'数据可视化'
,
path
:
'/teach/chart/visualization'
,
},
// {
// {
// tag: '',
// tag: '',
// icon: DataAnalysis,
// icon: DataAnalysis,
...
...
src/modules/teach/chart/index.ts
浏览文件 @
c8379c83
...
@@ -8,6 +8,7 @@ export const routes: Array<RouteRecordRaw> = [
...
@@ -8,6 +8,7 @@ export const routes: Array<RouteRecordRaw> = [
children
:
[
children
:
[
{
path
:
'resource'
,
component
:
()
=>
import
(
'./views/Resource.vue'
)
},
{
path
:
'resource'
,
component
:
()
=>
import
(
'./views/Resource.vue'
)
},
{
path
:
'learning'
,
component
:
()
=>
import
(
'./views/Learning.vue'
)
},
{
path
:
'learning'
,
component
:
()
=>
import
(
'./views/Learning.vue'
)
},
{
path
:
'visualization'
,
component
:
()
=>
import
(
'./views/Visualization.vue'
)
},
],
],
},
},
]
]
src/modules/teach/chart/views/Visualization.vue
0 → 100644
浏览文件 @
c8379c83
<
script
setup
>
import
{
ref
,
onMounted
,
onUnmounted
}
from
'vue'
import
*
as
echarts
from
'echarts'
import
{
User
,
School
,
Trophy
,
TrendCharts
,
Monitor
,
Document
}
from
'@element-plus/icons-vue'
// 教学数据统计
const
teachingStats
=
ref
({
totalTeachers
:
0
,
totalStudents
:
0
,
totalCourses
:
0
,
totalClasses
:
0
,
avgScore
:
0
,
completionRate
:
0
,
})
// 学习进度数据
const
learningProgressData
=
ref
({
students
:
[],
progress
:
[],
})
// 成绩分析数据
const
scoreAnalysisData
=
ref
({
distribution
:
[],
subjects
:
[],
trends
:
[],
})
// 图表实例
let
teachingOverviewChart
=
null
let
learningProgressChart
=
null
let
scoreDistributionChart
=
null
let
subjectScoreChart
=
null
let
scoreTrendChart
=
null
// 加载模拟数据
const
loadData
=
()
=>
{
// 教学数据统计
teachingStats
.
value
=
{
totalTeachers
:
128
,
totalStudents
:
2850
,
totalCourses
:
76
,
totalClasses
:
38
,
avgScore
:
87.2
,
completionRate
:
94.5
,
}
// 学习进度数据
learningProgressData
.
value
=
{
students
:
[
'计算机1班'
,
'数学1班'
,
'物理1班'
,
'化学1班'
,
'英语1班'
,
'语文1班'
],
progress
:
[
92
,
88
,
85
,
90
,
87
,
83
],
}
// 成绩分析数据
scoreAnalysisData
.
value
=
{
distribution
:
[
{
name
:
'优秀(90-100)'
,
value
:
35
,
count
:
998
},
{
name
:
'良好(80-89)'
,
value
:
40
,
count
:
1140
},
{
name
:
'中等(70-79)'
,
value
:
20
,
count
:
570
},
{
name
:
'及格(60-69)'
,
value
:
4
,
count
:
114
},
{
name
:
'不及格(<60)'
,
value
:
1
,
count
:
28
},
],
subjects
:
[
{
name
:
'数学'
,
score
:
89.5
},
{
name
:
'英语'
,
score
:
85.2
},
{
name
:
'计算机'
,
score
:
92.8
},
{
name
:
'物理'
,
score
:
87.6
},
{
name
:
'化学'
,
score
:
88.9
},
{
name
:
'语文'
,
score
:
84.3
},
],
trends
:
{
months
:
[
'1月'
,
'2月'
,
'3月'
,
'4月'
,
'5月'
,
'6月'
],
scores
:
[
85.2
,
86.8
,
87.5
,
88.1
,
87.9
,
87.2
],
},
}
}
// 初始化图表
const
initCharts
=
()
=>
{
// 教学数据总览
const
teachingEl
=
document
.
getElementById
(
'teachingOverviewChart'
)
if
(
teachingEl
)
{
if
(
teachingOverviewChart
)
{
teachingOverviewChart
.
dispose
()
}
teachingOverviewChart
=
echarts
.
init
(
teachingEl
)
const
option
=
{
title
:
{
text
:
'教学数据总览'
,
left
:
'center'
,
top
:
'5%'
,
textStyle
:
{
fontSize
:
16
},
},
tooltip
:
{},
radar
:
{
indicator
:
[
{
name
:
'教师数量'
,
max
:
150
},
{
name
:
'学生数量'
,
max
:
3000
},
{
name
:
'课程数量'
,
max
:
100
},
{
name
:
'班级数量'
,
max
:
50
},
{
name
:
'平均成绩'
,
max
:
100
},
{
name
:
'完成率'
,
max
:
100
},
],
center
:
[
'50%'
,
'60%'
],
radius
:
'70%'
,
},
series
:
[
{
name
:
'教学数据'
,
type
:
'radar'
,
data
:
[
{
value
:
[
teachingStats
.
value
.
totalTeachers
,
teachingStats
.
value
.
totalStudents
,
teachingStats
.
value
.
totalCourses
,
teachingStats
.
value
.
totalClasses
,
teachingStats
.
value
.
avgScore
,
teachingStats
.
value
.
completionRate
,
],
name
:
'当前数据'
,
areaStyle
:
{
color
:
'rgba(64, 158, 255, 0.3)'
},
},
],
},
],
}
teachingOverviewChart
.
setOption
(
option
)
}
// 学习进度柱状图
const
progressEl
=
document
.
getElementById
(
'learningProgressChart'
)
if
(
progressEl
)
{
if
(
learningProgressChart
)
{
learningProgressChart
.
dispose
()
}
learningProgressChart
=
echarts
.
init
(
progressEl
)
const
option
=
{
title
:
{
text
:
'各班级学习进度'
,
left
:
'center'
,
top
:
'5%'
,
textStyle
:
{
fontSize
:
16
},
},
tooltip
:
{
trigger
:
'axis'
,
axisPointer
:
{
type
:
'shadow'
},
},
grid
:
{
top
:
'20%'
,
left
:
'3%'
,
right
:
'4%'
,
bottom
:
'3%'
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'category'
,
data
:
learningProgressData
.
value
.
students
,
axisLabel
:
{
rotate
:
45
},
},
yAxis
:
{
type
:
'value'
,
name
:
'进度(%)'
,
max
:
100
,
},
series
:
[
{
name
:
'学习进度'
,
type
:
'bar'
,
data
:
learningProgressData
.
value
.
progress
,
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'#83bff6'
},
{
offset
:
0.5
,
color
:
'#188df0'
},
{
offset
:
1
,
color
:
'#188df0'
},
]),
},
},
],
}
learningProgressChart
.
setOption
(
option
)
}
// 成绩分布饼图
const
distributionEl
=
document
.
getElementById
(
'scoreDistributionChart'
)
if
(
distributionEl
)
{
if
(
scoreDistributionChart
)
{
scoreDistributionChart
.
dispose
()
}
scoreDistributionChart
=
echarts
.
init
(
distributionEl
)
const
option
=
{
title
:
{
text
:
'成绩分布情况'
,
left
:
'center'
,
top
:
'5%'
,
textStyle
:
{
fontSize
:
16
},
},
tooltip
:
{
trigger
:
'item'
,
formatter
:
'{a} <br/>{b}: {c}% ({d}人)'
,
},
legend
:
{
orient
:
'vertical'
,
right
:
'10px'
,
top
:
'center'
,
itemWidth
:
15
,
itemHeight
:
12
,
textStyle
:
{
fontSize
:
11
,
},
formatter
:
function
(
name
)
{
return
name
.
length
>
6
?
name
.
substring
(
0
,
6
)
+
'...'
:
name
},
},
series
:
[
{
name
:
'成绩分布'
,
type
:
'pie'
,
radius
:
[
'30%'
,
'60%'
],
center
:
[
'35%'
,
'50%'
],
data
:
scoreAnalysisData
.
value
.
distribution
,
emphasis
:
{
itemStyle
:
{
shadowBlur
:
10
,
shadowOffsetX
:
0
,
shadowColor
:
'rgba(0, 0, 0, 0.5)'
,
},
},
},
],
}
scoreDistributionChart
.
setOption
(
option
)
}
// 学科成绩柱状图
const
subjectEl
=
document
.
getElementById
(
'subjectScoreChart'
)
if
(
subjectEl
)
{
if
(
subjectScoreChart
)
{
subjectScoreChart
.
dispose
()
}
subjectScoreChart
=
echarts
.
init
(
subjectEl
)
const
option
=
{
title
:
{
text
:
'各学科平均成绩'
,
left
:
'center'
,
top
:
'5%'
,
textStyle
:
{
fontSize
:
16
},
},
tooltip
:
{
trigger
:
'axis'
,
axisPointer
:
{
type
:
'shadow'
},
},
grid
:
{
top
:
'20%'
,
left
:
'3%'
,
right
:
'4%'
,
bottom
:
'3%'
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'category'
,
data
:
scoreAnalysisData
.
value
.
subjects
.
map
((
item
)
=>
item
.
name
),
},
yAxis
:
{
type
:
'value'
,
name
:
'平均成绩'
,
max
:
100
,
},
series
:
[
{
name
:
'平均成绩'
,
type
:
'bar'
,
data
:
scoreAnalysisData
.
value
.
subjects
.
map
((
item
)
=>
item
.
score
),
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'#f7ba2c'
},
{
offset
:
0.5
,
color
:
'#ff9f7f'
},
{
offset
:
1
,
color
:
'#ff9f7f'
},
]),
},
},
],
}
subjectScoreChart
.
setOption
(
option
)
}
// 成绩趋势折线图
const
trendEl
=
document
.
getElementById
(
'scoreTrendChart'
)
if
(
trendEl
)
{
if
(
scoreTrendChart
)
{
scoreTrendChart
.
dispose
()
}
scoreTrendChart
=
echarts
.
init
(
trendEl
)
const
option
=
{
title
:
{
text
:
'成绩变化趋势'
,
left
:
'center'
,
top
:
'5%'
,
textStyle
:
{
fontSize
:
16
},
},
tooltip
:
{
trigger
:
'axis'
,
},
grid
:
{
top
:
'20%'
,
left
:
'3%'
,
right
:
'4%'
,
bottom
:
'3%'
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'category'
,
data
:
scoreAnalysisData
.
value
.
trends
.
months
,
},
yAxis
:
{
type
:
'value'
,
name
:
'平均成绩'
,
},
series
:
[
{
name
:
'平均成绩'
,
type
:
'line'
,
data
:
scoreAnalysisData
.
value
.
trends
.
scores
,
smooth
:
true
,
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'rgba(255, 99, 132, 0.3)'
},
{
offset
:
1
,
color
:
'rgba(255, 99, 132, 0.1)'
},
]),
},
},
],
}
scoreTrendChart
.
setOption
(
option
)
}
}
// 窗口大小改变时重新调整图表
const
handleResize
=
()
=>
{
teachingOverviewChart
?.
resize
()
learningProgressChart
?.
resize
()
scoreDistributionChart
?.
resize
()
subjectScoreChart
?.
resize
()
scoreTrendChart
?.
resize
()
}
// 清理所有图表实例
const
disposeAllCharts
=
()
=>
{
teachingOverviewChart
?.
dispose
()
learningProgressChart
?.
dispose
()
scoreDistributionChart
?.
dispose
()
subjectScoreChart
?.
dispose
()
scoreTrendChart
?.
dispose
()
teachingOverviewChart
=
null
learningProgressChart
=
null
scoreDistributionChart
=
null
subjectScoreChart
=
null
scoreTrendChart
=
null
}
onMounted
(()
=>
{
loadData
()
setTimeout
(()
=>
{
initCharts
()
},
500
)
window
.
addEventListener
(
'resize'
,
handleResize
)
})
onUnmounted
(()
=>
{
disposeAllCharts
()
window
.
removeEventListener
(
'resize'
,
handleResize
)
})
</
script
>
<
template
>
<div
class=
"visualization-page"
>
<div
class=
"page-header"
>
<h1>
数据可视化分析
</h1>
<p>
教学数据统计、学习进度可视化、成绩分析图表
</p>
</div>
<!-- 教学数据统计 -->
<div
class=
"stats-section"
>
<h2>
教学数据统计
</h2>
<div
class=
"stats-grid"
>
<div
class=
"stat-card"
>
<div
class=
"stat-icon teachers"
>
<User
/>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-number"
>
{{
teachingStats
.
totalTeachers
}}
</div>
<div
class=
"stat-label"
>
教师总数
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon students"
>
<School
/>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-number"
>
{{
teachingStats
.
totalStudents
.
toLocaleString
()
}}
</div>
<div
class=
"stat-label"
>
学生总数
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon courses"
>
<Document
/>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-number"
>
{{
teachingStats
.
totalCourses
}}
</div>
<div
class=
"stat-label"
>
课程总数
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon classes"
>
<Monitor
/>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-number"
>
{{
teachingStats
.
totalClasses
}}
</div>
<div
class=
"stat-label"
>
班级总数
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon score"
>
<Trophy
/>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-number"
>
{{
teachingStats
.
avgScore
}}
</div>
<div
class=
"stat-label"
>
平均成绩
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon completion"
>
<TrendCharts
/>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-number"
>
{{
teachingStats
.
completionRate
}}
%
</div>
<div
class=
"stat-label"
>
完成率
</div>
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div
class=
"charts-section"
>
<div
class=
"charts-row"
>
<div
class=
"chart-container"
>
<div
id=
"teachingOverviewChart"
class=
"chart"
></div>
</div>
<div
class=
"chart-container"
>
<div
id=
"learningProgressChart"
class=
"chart"
></div>
</div>
</div>
<div
class=
"charts-row"
>
<div
class=
"chart-container"
>
<div
id=
"scoreDistributionChart"
class=
"chart"
></div>
</div>
<div
class=
"chart-container"
>
<div
id=
"subjectScoreChart"
class=
"chart"
></div>
</div>
</div>
<div
class=
"charts-row"
>
<div
class=
"chart-container large"
>
<div
id=
"scoreTrendChart"
class=
"chart"
></div>
</div>
</div>
</div>
</div>
</
template
>
<
style
lang=
"scss"
scoped
>
.visualization-page
{
padding
:
20px
;
background-color
:
#f8f8f8
;
min-height
:
100vh
;
.page-header
{
text-align
:
center
;
margin-bottom
:
30px
;
h1
{
font-size
:
28px
;
color
:
#303133
;
margin-bottom
:
10px
;
}
p
{
font-size
:
16px
;
color
:
#606266
;
}
}
.stats-section
,
.charts-section
{
margin-bottom
:
40px
;
h2
{
font-size
:
20px
;
color
:
#303133
;
margin-bottom
:
20px
;
padding-left
:
10px
;
border-left
:
4px
solid
#409eff
;
}
}
.stats-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
200px
,
1fr
));
gap
:
20px
;
}
.stat-card
{
background
:
white
;
border-radius
:
8px
;
padding
:
20px
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0
.1
);
display
:
flex
;
align-items
:
center
;
transition
:
transform
0
.3s
ease
;
&
:hover
{
transform
:
translateY
(
-2px
);
}
.stat-icon
{
width
:
60px
;
height
:
60px
;
border-radius
:
50%
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
margin-right
:
15px
;
svg
{
width
:
24px
;
height
:
24px
;
color
:
#ba8b45
;
}
&
.teachers
,
&
.students
,
&
.courses
,
&
.classes
,
&
.score
,
&
.completion
{
background
:
#f5ebda
;
}
}
.stat-content
{
flex
:
1
;
.stat-number
{
font-size
:
32px
;
font-weight
:
bold
;
color
:
#303133
;
line-height
:
1
;
margin-bottom
:
5px
;
}
.stat-label
{
font-size
:
14px
;
color
:
#909399
;
}
}
}
.charts-row
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
300px
,
1fr
));
gap
:
20px
;
margin-bottom
:
20px
;
&
:last-child
{
margin-bottom
:
0
;
}
}
.chart-container
{
background
:
white
;
border-radius
:
8px
;
padding
:
20px
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0
.1
);
&
.large
{
grid-column
:
span
2
;
}
.chart
{
width
:
100%
;
height
:
300px
;
}
}
}
@media
(
max-width
:
768px
)
{
.visualization-page
{
padding
:
10px
;
.stats-grid
{
grid-template-columns
:
1fr
;
}
.charts-row
{
grid-template-columns
:
1fr
;
.chart-container.large
{
grid-column
:
span
1
;
}
}
}
}
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论