Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-resource
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-resource
Commits
c8ba2aa9
提交
c8ba2aa9
authored
10月 15, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 增加生源地分布
上级
732a4574
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
950 行增加
和
9 行删除
+950
-9
menus.ts
src/assets/menus.ts
+6
-5
List.vue
src/modules/admin/student/views/List.vue
+5
-4
index.ts
src/modules/teach/chart/index.ts
+1
-0
Origin.vue
src/modules/teach/chart/views/Origin.vue
+938
-0
没有找到文件。
src/assets/menus.ts
浏览文件 @
c8ba2aa9
...
@@ -216,14 +216,15 @@ export const menus: IMenuItem[] = [
...
@@ -216,14 +216,15 @@ export const menus: IMenuItem[] = [
name
:
'数据可视化'
,
name
:
'数据可视化'
,
path
:
'/teach/chart/visualization'
,
path
:
'/teach/chart/visualization'
,
},
},
// {
{
// tag: '',
tag
:
''
,
// icon: DataAnalysis,
icon
:
DataAnalysis
,
// name: '生源地分布',
name
:
'生源地分布'
,
path
:
'/teach/chart/origin'
,
// path:
// path:
// 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!!751f!!6e90!!5730!!5206!!5e03!.db&platform=PC&browserType=chrome',
// '/bi/?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!751f!!6e90!!5730!!5206!!5e03!.db&platform=PC&browserType=chrome',
//
},
},
],
],
},
},
]
]
src/modules/admin/student/views/List.vue
浏览文件 @
c8ba2aa9
...
@@ -154,10 +154,11 @@ const handleSelectionChange = (val: any) => {
...
@@ -154,10 +154,11 @@ const handleSelectionChange = (val: any) => {
}
}
const
handleAnalysis
=
()
=>
{
const
handleAnalysis
=
()
=>
{
// isShowAnalysisDialog.value = true
// isShowAnalysisDialog.value = true
window
.
open
(
window
.
open
(
'/teach/chart/origin'
)
import
.
meta
.
env
.
VITE_BI_URL
+
// window.open(
'/bi/?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!751f!!6e90!!5730!!5206!!5e03!.db&platform=PC&browserType=chrome'
// import.meta.env.VITE_BI_URL +
)
// '/bi/?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!751f!!6e90!!5730!!5206!!5e03!.db&platform=PC&browserType=chrome'
// )
}
}
</
script
>
</
script
>
...
...
src/modules/teach/chart/index.ts
浏览文件 @
c8ba2aa9
...
@@ -9,6 +9,7 @@ export const routes: Array<RouteRecordRaw> = [
...
@@ -9,6 +9,7 @@ export const routes: Array<RouteRecordRaw> = [
{
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'
)
},
{
path
:
'visualization'
,
component
:
()
=>
import
(
'./views/Visualization.vue'
)
},
{
path
:
'origin'
,
component
:
()
=>
import
(
'./views/Origin.vue'
)
},
],
],
},
},
]
]
src/modules/teach/chart/views/Origin.vue
0 → 100644
浏览文件 @
c8ba2aa9
<
template
>
<div
class=
"origin-container"
>
<!-- 页面标题 -->
<div
class=
"header"
>
<h1>
<el-icon
class=
"header-icon"
><Location
/></el-icon>
生源地分布可视化
</h1>
<p>
全面了解学生来源地域分布情况与统计分析
</p>
</div>
<!-- 筛选器 -->
<div
class=
"filter-bar"
>
<div
class=
"filter-group"
>
<el-icon
class=
"filter-icon"
style=
"color: var(--primary-color)"
><Calendar
/></el-icon>
<label>
学期:
</label>
<select
v-model=
"filters.year"
@
change=
"onFilterChange"
>
<option
value=
"2024"
>
2024学年
</option>
<option
value=
"2023"
>
2023学年
</option>
<option
value=
"2022"
>
2022学年
</option>
</select>
</div>
<div
class=
"filter-group"
>
<el-icon
class=
"filter-icon"
style=
"color: var(--secondary-color)"
><School
/></el-icon>
<label>
年级:
</label>
<select
v-model=
"filters.grade"
@
change=
"onFilterChange"
>
<option
value=
"all"
>
全部年级
</option>
<option
value=
"1"
>
一年级
</option>
<option
value=
"2"
>
二年级
</option>
<option
value=
"3"
>
三年级
</option>
<option
value=
"4"
>
四年级
</option>
</select>
</div>
<div
class=
"filter-group"
>
<el-icon
class=
"filter-icon"
style=
"color: var(--success-color)"
><Grid
/></el-icon>
<label>
专业:
</label>
<select
v-model=
"filters.major"
@
change=
"onFilterChange"
>
<option
value=
"all"
>
全部专业
</option>
<option
value=
"cs"
>
计算机科学
</option>
<option
value=
"ee"
>
电子工程
</option>
<option
value=
"ba"
>
工商管理
</option>
<option
value=
"med"
>
医学
</option>
</select>
</div>
</div>
<!-- 统计卡片 -->
<div
class=
"stats-grid"
>
<div
class=
"stat-card"
>
<div
class=
"stat-icon"
style=
"background: linear-gradient(135deg, #c21f30 0%, #e94057 100%)"
>
<el-icon
class=
"stat-icon-inner"
><User
/></el-icon>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-value"
>
{{
stats
.
totalStudents
.
toLocaleString
()
}}
</div>
<div
class=
"stat-label"
>
学生总数
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon"
style=
"background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)"
>
<el-icon
class=
"stat-icon-inner"
><LocationInformation
/></el-icon>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-value"
>
{{
stats
.
provinces
}}
</div>
<div
class=
"stat-label"
>
覆盖省份
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon"
style=
"background: linear-gradient(135deg, #10b981 0%, #34d399 100%)"
>
<el-icon
class=
"stat-icon-inner"
><OfficeBuilding
/></el-icon>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-value"
>
{{
stats
.
cities
}}
</div>
<div
class=
"stat-label"
>
覆盖城市
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon"
style=
"background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)"
>
<el-icon
class=
"stat-icon-inner"
><Location
/></el-icon>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-value"
>
{{
stats
.
internationalCountries
}}
</div>
<div
class=
"stat-label"
>
国际学生国家
</div>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-icon"
style=
"background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%)"
>
<el-icon
class=
"stat-icon-inner"
><TrendCharts
/></el-icon>
</div>
<div
class=
"stat-content"
>
<div
class=
"stat-value"
>
{{
stats
.
localPercentage
}}
%
</div>
<div
class=
"stat-label"
>
省内生源比例
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div
class=
"charts-section"
>
<h2
class=
"section-title"
>
分布分析
</h2>
<div
class=
"charts-grid"
>
<div
class=
"chart-card"
>
<h3>
<el-icon
class=
"chart-title-icon"
><PieChart
/></el-icon>
生源地区域分布
</h3>
<div
ref=
"regionPieChart"
class=
"chart-container"
></div>
</div>
<div
class=
"chart-card"
>
<h3>
<el-icon
class=
"chart-title-icon"
><Histogram
/></el-icon>
省内城市分布 TOP10
</h3>
<div
ref=
"cityBarChart"
class=
"chart-container"
></div>
</div>
</div>
</div>
<!-- 地图可视化 -->
<div
class=
"map-section"
>
<h2
class=
"section-title"
>
全国生源地图分布
</h2>
<div
ref=
"chinaMapChart"
class=
"map-container"
></div>
</div>
<!-- 详细数据表格 -->
<div
class=
"table-section"
>
<h2
class=
"section-title"
>
生源地详细数据
</h2>
<table
class=
"data-table"
>
<thead>
<tr>
<th>
排名
</th>
<th>
省份
</th>
<th>
学生人数
</th>
<th>
占比
</th>
<th>
趋势
</th>
<th>
分布可视化
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"item in provinceData"
:key=
"item.name"
>
<td>
<span
:class=
"`rank-badge $
{getRankClass(item.rank)}`">
{{
item
.
rank
}}
</span>
</td>
<td>
<strong>
{{
item
.
name
}}
</strong>
</td>
<td>
<strong
style=
"color: var(--primary-color)"
>
{{
item
.
value
.
toLocaleString
()
}}
</strong>
人
</td>
<td>
<strong>
{{
item
.
percentage
}}
%
</strong>
</td>
<td
:style=
"
{ color: getTrendColor(item.trend) }">
<el-icon
class=
"trend-icon"
>
<ArrowUp
v-if=
"item.trend.startsWith('+')"
/>
<ArrowDown
v-else-if=
"item.trend.startsWith('-')"
/>
<Minus
v-else
/>
</el-icon>
{{
item
.
trend
}}
</td>
<td>
<div
class=
"progress-bar"
>
<div
class=
"progress-fill"
:style=
"
{ width: `${item.percentage}%` }">
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
reactive
,
onMounted
,
onUnmounted
,
nextTick
}
from
'vue'
import
*
as
echarts
from
'echarts'
import
{
Location
,
Calendar
,
School
,
Grid
,
User
,
LocationInformation
,
OfficeBuilding
,
TrendCharts
,
PieChart
,
Histogram
,
ArrowUp
,
ArrowDown
,
Minus
,
}
from
'@element-plus/icons-vue'
// 类型定义
interface
ProvinceData
{
name
:
string
value
:
number
percentage
:
number
trend
:
string
rank
:
number
}
interface
Stats
{
totalStudents
:
number
provinces
:
number
cities
:
number
internationalCountries
:
number
localPercentage
:
number
}
interface
Filters
{
year
:
string
grade
:
string
major
:
string
}
// 响应式数据
const
regionPieChart
=
ref
<
HTMLElement
>
()
const
cityBarChart
=
ref
<
HTMLElement
>
()
const
chinaMapChart
=
ref
<
HTMLElement
>
()
const
filters
=
reactive
<
Filters
>
({
year
:
'2024'
,
grade
:
'all'
,
major
:
'all'
,
})
const
stats
=
reactive
<
Stats
>
({
totalStudents
:
8650
,
provinces
:
31
,
cities
:
256
,
internationalCountries
:
12
,
localPercentage
:
68
,
})
const
provinceData
=
ref
<
ProvinceData
[]
>
([
{
name
:
'广东'
,
value
:
5882
,
percentage
:
68
,
trend
:
'+5%'
,
rank
:
1
},
{
name
:
'湖南'
,
value
:
432
,
percentage
:
5
,
trend
:
'+2%'
,
rank
:
2
},
{
name
:
'湖北'
,
value
:
389
,
percentage
:
4.5
,
trend
:
'+3%'
,
rank
:
3
},
{
name
:
'江西'
,
value
:
346
,
percentage
:
4
,
trend
:
'+1%'
,
rank
:
4
},
{
name
:
'河南'
,
value
:
302
,
percentage
:
3.5
,
trend
:
'-1%'
,
rank
:
5
},
{
name
:
'四川'
,
value
:
259
,
percentage
:
3
,
trend
:
'+4%'
,
rank
:
6
},
{
name
:
'广西'
,
value
:
216
,
percentage
:
2.5
,
trend
:
'+2%'
,
rank
:
7
},
{
name
:
'福建'
,
value
:
173
,
percentage
:
2
,
trend
:
'0%'
,
rank
:
8
},
{
name
:
'浙江'
,
value
:
156
,
percentage
:
1.8
,
trend
:
'+1%'
,
rank
:
9
},
{
name
:
'江苏'
,
value
:
138
,
percentage
:
1.6
,
trend
:
'+2%'
,
rank
:
10
},
{
name
:
'其他'
,
value
:
357
,
percentage
:
4.1
,
trend
:
'+1%'
,
rank
:
11
},
])
// 图表实例
let
regionPieChartInstance
:
echarts
.
ECharts
|
null
=
null
let
cityBarChartInstance
:
echarts
.
ECharts
|
null
=
null
let
chinaMapChartInstance
:
echarts
.
ECharts
|
null
=
null
// 工具函数
const
getRankClass
=
(
rank
:
number
):
string
=>
{
return
rank
<=
3
?
`rank-
${
rank
}
`
:
'rank-other'
}
const
getTrendColor
=
(
trend
:
string
):
string
=>
{
if
(
trend
.
startsWith
(
'+'
))
return
'#10b981'
if
(
trend
.
startsWith
(
'-'
))
return
'#ef4444'
return
'#6b7280'
}
// 初始化区域分布饼图
const
initRegionPieChart
=
()
=>
{
if
(
!
regionPieChart
.
value
)
return
regionPieChartInstance
=
echarts
.
init
(
regionPieChart
.
value
)
const
option
=
{
tooltip
:
{
trigger
:
'item'
,
formatter
:
'{b}: {c}人 ({d}%)'
,
},
legend
:
{
orient
:
'vertical'
,
right
:
10
,
top
:
'center'
,
textStyle
:
{
fontSize
:
12
},
},
color
:
[
'#C21F30'
,
'#E94057'
,
'#f59e0b'
,
'#10b981'
,
'#3b82f6'
,
'#8b5cf6'
],
series
:
[
{
name
:
'区域分布'
,
type
:
'pie'
,
radius
:
[
'40%'
,
'70%'
],
avoidLabelOverlap
:
false
,
itemStyle
:
{
borderRadius
:
10
,
borderColor
:
'#fff'
,
borderWidth
:
2
,
},
label
:
{
show
:
true
,
formatter
:
'{b}
\
n{d}%'
,
},
emphasis
:
{
label
:
{
show
:
true
,
fontSize
:
16
,
fontWeight
:
'bold'
,
},
},
data
:
[
{
value
:
5882
,
name
:
'省内'
},
{
value
:
1214
,
name
:
'华中地区'
},
{
value
:
518
,
name
:
'西南地区'
},
{
value
:
432
,
name
:
'华东地区'
},
{
value
:
389
,
name
:
'华北地区'
},
{
value
:
215
,
name
:
'其他地区'
},
],
},
],
}
regionPieChartInstance
.
setOption
(
option
)
}
// 初始化城市分布柱状图
const
initCityBarChart
=
()
=>
{
if
(
!
cityBarChart
.
value
)
return
cityBarChartInstance
=
echarts
.
init
(
cityBarChart
.
value
)
const
option
=
{
tooltip
:
{
trigger
:
'axis'
,
axisPointer
:
{
type
:
'shadow'
},
},
grid
:
{
left
:
'3%'
,
right
:
'4%'
,
bottom
:
'3%'
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'value'
,
axisLabel
:
{
fontSize
:
11
},
},
yAxis
:
{
type
:
'category'
,
data
:
[
'惠州'
,
'东莞'
,
'佛山'
,
'中山'
,
'珠海'
,
'江门'
,
'肇庆'
,
'汕头'
,
'湛江'
,
'深圳'
],
axisLabel
:
{
fontSize
:
11
},
},
series
:
[
{
name
:
'学生人数'
,
type
:
'bar'
,
data
:
[
856
,
923
,
1245
,
678
,
534
,
489
,
412
,
367
,
325
,
1053
],
itemStyle
:
{
borderRadius
:
[
0
,
8
,
8
,
0
],
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
1
,
0
,
[
{
offset
:
0
,
color
:
'#C21F30'
},
{
offset
:
1
,
color
:
'#E94057'
},
]),
},
label
:
{
show
:
true
,
position
:
'right'
,
fontSize
:
11
,
},
},
],
}
cityBarChartInstance
.
setOption
(
option
)
}
// 初始化中国地图
const
initChinaMap
=
async
()
=>
{
if
(
!
chinaMapChart
.
value
)
return
chinaMapChartInstance
=
echarts
.
init
(
chinaMapChart
.
value
)
const
mapData
=
[
{
name
:
'广东'
,
value
:
5882
},
{
name
:
'湖南'
,
value
:
432
},
{
name
:
'湖北'
,
value
:
389
},
{
name
:
'江西'
,
value
:
346
},
{
name
:
'河南'
,
value
:
302
},
{
name
:
'四川'
,
value
:
259
},
{
name
:
'广西'
,
value
:
216
},
{
name
:
'福建'
,
value
:
173
},
{
name
:
'浙江'
,
value
:
156
},
{
name
:
'江苏'
,
value
:
138
},
{
name
:
'安徽'
,
value
:
125
},
{
name
:
'山东'
,
value
:
112
},
{
name
:
'重庆'
,
value
:
98
},
{
name
:
'贵州'
,
value
:
87
},
{
name
:
'云南'
,
value
:
76
},
]
const
option
=
{
title
:
{
text
:
'全国生源分布热力图'
,
left
:
'center'
,
top
:
20
,
textStyle
:
{
fontSize
:
16
,
color
:
'#2C3E50'
,
},
},
tooltip
:
{
trigger
:
'item'
,
formatter
:
function
(
params
:
any
)
{
if
(
params
.
value
)
{
return
params
.
name
+
'<br/>学生人数: '
+
params
.
value
+
'人'
}
return
params
.
name
+
'<br/>暂无数据'
},
},
visualMap
:
{
min
:
0
,
max
:
6000
,
text
:
[
'高'
,
'低'
],
realtime
:
false
,
calculable
:
true
,
inRange
:
{
color
:
[
'#fef2f2'
,
'#fecaca'
,
'#f87171'
,
'#dc2626'
,
'#C21F30'
],
},
bottom
:
50
,
left
:
20
,
},
series
:
[
{
name
:
'生源分布'
,
type
:
'scatter'
,
coordinateSystem
:
'geo'
,
data
:
[
{
name
:
'广东'
,
value
:
[
113.23
,
23.16
,
5882
]
},
{
name
:
'湖南'
,
value
:
[
112.59
,
28.21
,
432
]
},
{
name
:
'湖北'
,
value
:
[
114.31
,
30.52
,
389
]
},
{
name
:
'江西'
,
value
:
[
115.89
,
28.68
,
346
]
},
{
name
:
'河南'
,
value
:
[
113.65
,
34.76
,
302
]
},
{
name
:
'四川'
,
value
:
[
104.06
,
30.67
,
259
]
},
{
name
:
'广西'
,
value
:
[
108.33
,
22.84
,
216
]
},
{
name
:
'福建'
,
value
:
[
119.3
,
26.08
,
173
]
},
{
name
:
'浙江'
,
value
:
[
120.19
,
30.26
,
156
]
},
{
name
:
'江苏'
,
value
:
[
118.78
,
32.04
,
138
]
},
],
symbolSize
:
function
(
val
:
number
[])
{
return
Math
.
sqrt
(
val
[
2
])
/
2
},
itemStyle
:
{
color
:
'#C21F30'
,
shadowBlur
:
10
,
shadowColor
:
'rgba(194, 31, 48, 0.5)'
,
},
label
:
{
formatter
:
'{b}'
,
position
:
'top'
,
show
:
false
,
},
emphasis
:
{
label
:
{
show
:
true
,
},
},
},
],
geo
:
{
map
:
'china'
,
roam
:
true
,
scaleLimit
:
{
min
:
1
,
max
:
3
,
},
label
:
{
show
:
false
,
},
itemStyle
:
{
areaColor
:
'#f3f3f3'
,
borderColor
:
'#ccc'
,
},
emphasis
:
{
itemStyle
:
{
areaColor
:
'#e6f2ff'
,
},
label
:
{
show
:
true
,
color
:
'#333'
,
},
},
},
}
try
{
// 尝试加载中国地图数据
const
response
=
await
fetch
(
'https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json'
)
const
chinaJson
=
await
response
.
json
()
echarts
.
registerMap
(
'china'
,
chinaJson
)
chinaMapChartInstance
.
setOption
(
option
)
}
catch
(
error
)
{
// 如果加载失败,显示备用图表
console
.
log
(
'地图加载失败,使用备用可视化'
)
const
backupOption
=
{
title
:
{
text
:
'省份生源排行'
,
left
:
'center'
,
top
:
20
,
},
tooltip
:
{
trigger
:
'axis'
,
axisPointer
:
{
type
:
'shadow'
},
},
grid
:
{
left
:
'10%'
,
right
:
'10%'
,
top
:
'15%'
,
bottom
:
'10%'
,
},
xAxis
:
{
type
:
'category'
,
data
:
mapData
.
map
((
item
)
=>
item
.
name
),
axisLabel
:
{
interval
:
0
,
rotate
:
45
,
fontSize
:
11
,
},
},
yAxis
:
{
type
:
'value'
,
name
:
'学生人数'
,
},
series
:
[
{
type
:
'bar'
,
data
:
mapData
.
map
((
item
)
=>
item
.
value
),
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'#C21F30'
},
{
offset
:
1
,
color
:
'#E94057'
},
]),
borderRadius
:
[
8
,
8
,
0
,
0
],
},
label
:
{
show
:
true
,
position
:
'top'
,
fontSize
:
10
,
},
},
],
}
chinaMapChartInstance
.
setOption
(
backupOption
)
}
}
// 筛选器变化处理
const
onFilterChange
=
()
=>
{
console
.
log
(
'筛选器变化:'
,
filters
)
// 这里可以添加数据刷新逻辑
// 例如:重新获取数据、更新图表等
}
// 窗口大小变化处理
const
handleResize
=
()
=>
{
regionPieChartInstance
?.
resize
()
cityBarChartInstance
?.
resize
()
chinaMapChartInstance
?.
resize
()
}
// 生命周期
onMounted
(
async
()
=>
{
await
nextTick
()
initRegionPieChart
()
initCityBarChart
()
await
initChinaMap
()
window
.
addEventListener
(
'resize'
,
handleResize
)
})
onUnmounted
(()
=>
{
regionPieChartInstance
?.
dispose
()
cityBarChartInstance
?.
dispose
()
chinaMapChartInstance
?.
dispose
()
window
.
removeEventListener
(
'resize'
,
handleResize
)
})
</
script
>
<
style
scoped
>
.origin-container
{
max-width
:
1400px
;
margin
:
0
auto
;
padding
:
2rem
;
/* background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); */
min-height
:
100vh
;
--primary-color
:
#c21f30
;
--secondary-color
:
#e94057
;
--success-color
:
#10b981
;
--warning-color
:
#f59e0b
;
--info-color
:
#3b82f6
;
--dark-color
:
#333333
;
--light-color
:
#f5f5f5
;
--border-color
:
#e0e0e0
;
--shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0.08
);
--shadow-hover
:
0
10px
25px
rgba
(
0
,
0
,
0
,
0.1
);
}
.header
{
text-align
:
center
;
margin-bottom
:
2rem
;
padding
:
2rem
;
background
:
white
;
border-radius
:
16px
;
box-shadow
:
var
(
--shadow
);
}
.header
h1
{
font-size
:
2rem
;
color
:
var
(
--dark-color
);
margin-bottom
:
0.5rem
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
gap
:
1rem
;
}
.header-icon
{
color
:
var
(
--primary-color
);
font-size
:
2.5rem
;
margin-right
:
0.5rem
;
}
.header
p
{
color
:
#7f8c8d
;
font-size
:
1rem
;
}
.stats-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
220px
,
1
fr
));
gap
:
1.5rem
;
margin-bottom
:
2rem
;
}
.stat-card
{
background
:
white
;
padding
:
1.5rem
;
border-radius
:
12px
;
box-shadow
:
var
(
--shadow
);
transition
:
all
0.3s
;
display
:
flex
;
align-items
:
center
;
gap
:
1rem
;
}
.stat-card
:hover
{
transform
:
translateY
(
-4px
);
box-shadow
:
var
(
--shadow-hover
);
}
.stat-icon
{
width
:
60px
;
height
:
60px
;
border-radius
:
12px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
font-size
:
1.8rem
;
flex-shrink
:
0
;
}
.stat-icon-inner
{
color
:
white
;
font-size
:
1.8rem
;
}
.stat-content
{
flex
:
1
;
}
.stat-value
{
font-size
:
1.8rem
;
font-weight
:
bold
;
color
:
var
(
--dark-color
);
margin-bottom
:
0.25rem
;
}
.stat-label
{
color
:
#95a5a6
;
font-size
:
0.9rem
;
}
.section-title
{
font-size
:
1.25rem
;
color
:
var
(
--dark-color
);
margin-bottom
:
1rem
;
padding-left
:
1rem
;
border-left
:
4px
solid
var
(
--primary-color
);
font-weight
:
600
;
}
.charts-section
{
margin-bottom
:
2rem
;
}
.charts-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
350px
,
1
fr
));
gap
:
1.5rem
;
margin-bottom
:
1.5rem
;
}
.chart-card
{
background
:
white
;
padding
:
1.5rem
;
border-radius
:
12px
;
box-shadow
:
var
(
--shadow
);
}
.chart-card
h3
{
font-size
:
1.1rem
;
color
:
var
(
--dark-color
);
margin-bottom
:
1rem
;
padding-bottom
:
0.5rem
;
border-bottom
:
2px
solid
var
(
--border-color
);
display
:
flex
;
align-items
:
center
;
gap
:
0.5rem
;
}
.chart-title-icon
{
color
:
var
(
--primary-color
);
font-size
:
1.2rem
;
}
.chart-container
{
width
:
100%
;
height
:
320px
;
}
.map-section
{
background
:
white
;
padding
:
1.5rem
;
border-radius
:
12px
;
box-shadow
:
var
(
--shadow
);
margin-bottom
:
2rem
;
}
.map-container
{
width
:
100%
;
height
:
500px
;
}
.table-section
{
background
:
white
;
padding
:
1.5rem
;
border-radius
:
12px
;
box-shadow
:
var
(
--shadow
);
overflow
:
hidden
;
}
.table-section
h2
{
margin-bottom
:
1.5rem
;
}
.data-table
{
width
:
100%
;
border-collapse
:
collapse
;
margin-top
:
1rem
;
border
:
1px
solid
var
(
--border-color
);
border-radius
:
8px
;
overflow
:
hidden
;
}
.data-table
thead
{
background
:
linear-gradient
(
135deg
,
var
(
--primary-color
),
var
(
--secondary-color
));
color
:
white
;
}
.data-table
th
,
.data-table
td
{
padding
:
1rem
;
text-align
:
left
;
border-bottom
:
1px
solid
var
(
--border-color
);
border-right
:
1px
solid
var
(
--border-color
);
}
.data-table
th
:last-child
,
.data-table
td
:last-child
{
border-right
:
none
;
}
.data-table
th
{
font-weight
:
600
;
border-bottom
:
2px
solid
var
(
--border-color
);
}
.data-table
tbody
tr
{
transition
:
all
0.2s
;
}
.data-table
tbody
tr
:hover
{
background
:
rgba
(
194
,
31
,
48
,
0.05
);
}
.data-table
tbody
tr
:last-child
td
{
border-bottom
:
none
;
}
.data-table
tbody
tr
:nth-child
(
even
)
{
background
:
rgba
(
0
,
0
,
0
,
0.02
);
}
.data-table
tbody
tr
:nth-child
(
even
)
:hover
{
background
:
rgba
(
194
,
31
,
48
,
0.05
);
}
.trend-icon
{
margin-right
:
0.25rem
;
font-size
:
0.9rem
;
}
.rank-badge
{
display
:
inline-block
;
width
:
28px
;
height
:
28px
;
border-radius
:
50%
;
text-align
:
center
;
line-height
:
28px
;
font-weight
:
bold
;
color
:
white
;
font-size
:
0.9rem
;
}
.rank-1
{
background
:
linear-gradient
(
135deg
,
#c21f30
,
#e94057
);
}
.rank-2
{
background
:
linear-gradient
(
135deg
,
#f59e0b
,
#fbbf24
);
}
.rank-3
{
background
:
linear-gradient
(
135deg
,
#10b981
,
#34d399
);
}
.rank-other
{
background
:
linear-gradient
(
135deg
,
#6b7280
,
#9ca3af
);
}
.progress-bar
{
width
:
100%
;
height
:
12px
;
background
:
#f0f0f0
;
border-radius
:
6px
;
overflow
:
hidden
;
margin-top
:
0.5rem
;
border
:
1px
solid
#e0e0e0
;
box-shadow
:
inset
0
1px
3px
rgba
(
0
,
0
,
0
,
0.1
);
}
.progress-fill
{
height
:
100%
;
background
:
linear-gradient
(
90deg
,
var
(
--primary-color
),
var
(
--secondary-color
));
border-radius
:
6px
;
transition
:
width
1s
ease
;
min-width
:
2px
;
box-shadow
:
0
1px
3px
rgba
(
194
,
31
,
48
,
0.3
);
}
.filter-bar
{
background
:
white
;
padding
:
1.5rem
;
border-radius
:
12px
;
box-shadow
:
var
(
--shadow
);
margin-bottom
:
2rem
;
display
:
flex
;
gap
:
1rem
;
flex-wrap
:
wrap
;
align-items
:
center
;
}
.filter-group
{
display
:
flex
;
align-items
:
center
;
gap
:
0.5rem
;
}
.filter-icon
{
font-size
:
1.1rem
;
}
.filter-group
label
{
color
:
var
(
--dark-color
);
font-weight
:
500
;
font-size
:
0.95rem
;
}
.filter-group
select
{
padding
:
0.5rem
1rem
;
border
:
2px
solid
var
(
--border-color
);
border-radius
:
8px
;
font-size
:
0.95rem
;
color
:
var
(
--dark-color
);
background
:
white
;
cursor
:
pointer
;
transition
:
all
0.3s
;
min-width
:
120px
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.1
);
}
.filter-group
select
:hover
{
border-color
:
var
(
--primary-color
);
box-shadow
:
0
2px
6px
rgba
(
194
,
31
,
48
,
0.15
);
}
.filter-group
select
:focus
{
outline
:
none
;
border-color
:
var
(
--primary-color
);
box-shadow
:
0
0
0
3px
rgba
(
194
,
31
,
48
,
0.1
),
0
2px
6px
rgba
(
194
,
31
,
48
,
0.15
);
}
@media
(
max-width
:
768px
)
{
.origin-container
{
padding
:
1rem
;
}
.header
h1
{
font-size
:
1.5rem
;
}
.stats-grid
{
grid-template-columns
:
1
fr
;
}
.charts-grid
{
grid-template-columns
:
1
fr
;
}
.map-container
{
height
:
350px
;
}
.data-table
{
font-size
:
0.9rem
;
}
.data-table
th
,
.data-table
td
{
padding
:
0.75rem
;
}
}
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论