Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
6eb1ff55
authored
Feb 11, 2026
by
Jony.L
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
大屏页面和后台管理调整1.0
parent
71ffe7d8
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1539 additions
and
227 deletions
+1539
-227
src/api/biz/home/index.ts
+50
-0
src/views/Home/ChinaMap.vue
+62
-35
src/views/Home/ComputeResource.vue
+26
-18
src/views/Home/Home.vue
+127
-82
src/views/Home/Nvestment.vue
+25
-6
src/views/Home/apiEchart.vue
+91
-74
src/views/Home/orderManage.vue
+17
-8
src/views/Home/userManage.vue
+12
-4
src/views/biz/home/HomeDashboardMockForm.vue
+157
-0
src/views/biz/home/index.vue
+972
-0
No files found.
src/api/biz/home/index.ts
0 → 100644
View file @
6eb1ff55
import
request
from
'@/config/axios'
import
type
{
Dayjs
}
from
'dayjs'
;
/** 首页大屏模拟数据配置信息 */
export
interface
HomeDashboardMock
{
id
:
number
;
// 主键ID
configKey
?:
string
;
// 配置key
configType
?:
string
;
// 配置类型:switch开关/data数据
configValue
:
string
;
// 配置值
description
:
string
;
// 配置描述
}
// 首页大屏模拟数据配置 API
export
const
HomeDashboardMockApi
=
{
// 查询首页大屏模拟数据配置分页
getHomeDashboardMockPage
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/biz/home-dashboard-mock/page`
,
params
})
},
// 查询首页大屏模拟数据配置详情
getHomeDashboardMock
:
async
(
id
:
number
)
=>
{
return
await
request
.
get
({
url
:
`/biz/home-dashboard-mock/get?id=`
+
id
})
},
// 新增首页大屏模拟数据配置
createHomeDashboardMock
:
async
(
data
:
HomeDashboardMock
)
=>
{
return
await
request
.
post
({
url
:
`/biz/home-dashboard-mock/create`
,
data
})
},
// 修改首页大屏模拟数据配置
updateHomeDashboardMock
:
async
(
data
:
HomeDashboardMock
)
=>
{
return
await
request
.
put
({
url
:
`/biz/home-dashboard-mock/update`
,
data
})
},
// 删除首页大屏模拟数据配置
deleteHomeDashboardMock
:
async
(
id
:
number
)
=>
{
return
await
request
.
delete
({
url
:
`/biz/home-dashboard-mock/delete?id=`
+
id
})
},
/** 批量删除首页大屏模拟数据配置 */
deleteHomeDashboardMockList
:
async
(
ids
:
number
[])
=>
{
return
await
request
.
delete
({
url
:
`/biz/home-dashboard-mock/delete-list?ids=
${
ids
.
join
(
','
)}
`
})
},
// 导出首页大屏模拟数据配置 Excel
exportHomeDashboardMock
:
async
(
params
)
=>
{
return
await
request
.
download
({
url
:
`/biz/home-dashboard-mock/export-excel`
,
params
})
}
}
\ No newline at end of file
src/views/Home/ChinaMap.vue
View file @
6eb1ff55
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
onBeforeUnmount
,
onMounted
,
ref
,
nextTick
}
from
'vue'
import
{
onBeforeUnmount
,
onMounted
,
ref
,
nextTick
,
inject
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
*
as
echarts
from
'echarts/core'
import
{
TooltipComponent
,
VisualMapComponent
,
GeoComponent
,
GridComponent
,
LegendComponent
}
from
'echarts/components'
import
{
TooltipComponent
,
VisualMapComponent
,
GeoComponent
,
GridComponent
,
LegendComponent
}
from
'echarts/components'
import
{
import
{
...
@@ -18,6 +18,9 @@ import geoJson from '@/assets/mapJson/china.json'
...
@@ -18,6 +18,9 @@ import geoJson from '@/assets/mapJson/china.json'
import
labelGreen
from
'@/assets/images/label-green.png'
import
labelGreen
from
'@/assets/images/label-green.png'
import
labelRed
from
'@/assets/images/label-red.png'
import
labelRed
from
'@/assets/images/label-red.png'
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
echarts
.
use
([
echarts
.
use
([
GridComponent
,
GridComponent
,
LegendComponent
,
LegendComponent
,
...
@@ -33,14 +36,40 @@ echarts.use([
...
@@ -33,14 +36,40 @@ echarts.use([
])
])
const
geoCoordMap
=
ref
({
const
geoCoordMap
=
ref
({
浙江省
:
[
120.153576
,
30.287459
],
北京市
:
[
116.403874
,
39.914885
],
上海市
:
[
121.472644
,
31.231706
],
天津市
:
[
117.190182
,
39.125523
],
江苏省
:
[
87.617733
,
43.792818
],
河北省
:
[
114.502462
,
38.045494
],
辽宁省
:
[
123.429096
,
41.796767
],
湖南省
:
[
112.982279
,
28.19409
],
山西省
:
[
112.549248
,
37.857014
],
山西省
:
[
112.549248
,
37.857014
],
内蒙古自治区
:
[
111.751990
,
40.841470
],
辽宁省
:
[
123.429096
,
41.796767
],
吉林省
:
[
125.324498
,
43.886845
],
黑龙江省
:
[
126.642464
,
45.802755
],
上海市
:
[
121.472644
,
31.231706
],
江苏省
:
[
118.783957
,
32.062785
],
浙江省
:
[
120.153576
,
30.287459
],
安徽省
:
[
117.283447
,
31.861193
],
福建省
:
[
119.306239
,
26.075302
],
江西省
:
[
115.892151
,
28.676461
],
山东省
:
[
117.000923
,
36.675807
],
河南省
:
[
113.625367
,
34.746570
],
湖北省
:
[
114.305539
,
30.593098
],
湖南省
:
[
112.982279
,
28.194090
],
广东省
:
[
113.264385
,
23.129110
],
广西壮族自治区
:
[
108.320004
,
22.824018
],
海南省
:
[
110.198289
,
20.044008
],
重庆市
:
[
106.551559
,
29.563010
],
四川省
:
[
104.065735
,
30.659462
],
贵州省
:
[
106.707221
,
26.598278
],
云南省
:
[
102.712251
,
25.040609
],
西藏自治区
:
[
91.132212
,
29.660359
],
陕西省
:
[
108.939838
,
34.341275
],
甘肃省
:
[
103.823557
,
36.058039
],
甘肃省
:
[
103.823557
,
36.058039
],
福建省
:
[
119.306239
,
26.075302
]
青海省
:
[
101.777820
,
36.616990
],
宁夏回族自治区
:
[
106.278080
,
38.466370
],
新疆维吾尔自治区
:
[
87.617733
,
43.792818
],
台湾省
:
[
121.520076
,
25.047310
],
香港特别行政区
:
[
114.165460
,
22.275345
],
澳门特别行政区
:
[
113.549148
,
22.198755
]
})
})
const
mapData
=
ref
([
const
mapData
=
ref
([
...
@@ -62,28 +91,7 @@ const mapData = ref([
...
@@ -62,28 +91,7 @@ const mapData = ref([
// }
// }
])
])
const
mapData2
=
ref
([
const
mapData2
=
ref
([])
{
name
:
'浙江省'
,
value
:
12.4920
},
{
name
:
'山西省'
,
value
:
1.7948
},
{
name
:
'江苏省'
,
value
:
19.0543
},
{
name
:
'福建省'
,
value
:
7.4283
},
{
name
:
'甘肃省'
,
value
:
3.5283
}
])
const
convertData
=
function
(
data
)
{
const
convertData
=
function
(
data
)
{
const
res
=
[]
const
res
=
[]
...
@@ -174,7 +182,7 @@ function initMapChart (el) {
...
@@ -174,7 +182,7 @@ function initMapChart (el) {
label
:
{
label
:
{
show
:
true
,
show
:
true
,
position
:
[
-
25
,
-
80
],
position
:
[
-
25
,
-
80
],
width
:
36
0
,
width
:
42
0
,
height
:
72
,
height
:
72
,
color
:
'#FFFFFF'
,
color
:
'#FFFFFF'
,
backgroundColor
:
{
image
:
labelGreen
},
backgroundColor
:
{
image
:
labelGreen
},
...
@@ -218,8 +226,8 @@ function initMapChart (el) {
...
@@ -218,8 +226,8 @@ function initMapChart (el) {
symbolSize
:
0
,
symbolSize
:
0
,
label
:
{
label
:
{
show
:
true
,
show
:
true
,
position
:
[
-
25
,
-
80
],
position
:
[
-
40
,
-
80
],
width
:
36
0
,
width
:
50
0
,
height
:
72
,
height
:
72
,
color
:
'#FFFFFF'
,
color
:
'#FFFFFF'
,
backgroundColor
:
{
image
:
labelRed
},
backgroundColor
:
{
image
:
labelRed
},
...
@@ -228,12 +236,12 @@ function initMapChart (el) {
...
@@ -228,12 +236,12 @@ function initMapChart (el) {
rich
:
{
rich
:
{
txt
:
{
txt
:
{
align
:
'left'
,
align
:
'left'
,
padding
:
[
40
,
0
,
0
,
130
],
padding
:
[
40
,
6
0
,
0
,
130
],
fontSize
:
22
fontSize
:
22
},
},
txt2
:
{
txt2
:
{
fontSize
:
36
,
fontSize
:
36
,
padding
:
[
38
,
0
,
0
,
10
]
padding
:
[
38
,
2
0
,
0
,
10
]
},
},
txt3
:
{
txt3
:
{
fontSize
:
24
,
fontSize
:
24
,
...
@@ -246,7 +254,7 @@ function initMapChart (el) {
...
@@ -246,7 +254,7 @@ function initMapChart (el) {
data
data
}
=
params
}
=
params
if
(
data
&&
data
.
value
)
{
if
(
data
&&
data
.
value
)
{
return
`{txt|
${
' 算力资源'
}
}{txt2|
${
data
.
value
[
2
]}
}{txt3|
${
'P'
}
}`
return
`{txt|
${
' 算力资源'
}
}{txt2|
${
data
.
value
[
2
]}
}{txt3|
${
'P
TOPS
'
}
}`
}
}
return
'data'
return
'data'
}
}
...
@@ -270,6 +278,8 @@ const resize = () => {
...
@@ -270,6 +278,8 @@ const resize = () => {
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
await
nextTick
()
await
nextTick
()
// 从 dashboardData 初始化地图数据
mapData2
.
value
=
dashboardData
.
value
.
mapData
||
[]
if
(
mapRef
.
value
)
{
if
(
mapRef
.
value
)
{
chartInst
=
initMapChart
(
mapRef
.
value
)
chartInst
=
initMapChart
(
mapRef
.
value
)
window
.
addEventListener
(
'resize'
,
resize
)
window
.
addEventListener
(
'resize'
,
resize
)
...
@@ -277,6 +287,23 @@ onMounted(async () => {
...
@@ -277,6 +287,23 @@ onMounted(async () => {
}
}
})
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
mapData
,
(
newData
)
=>
{
mapData2
.
value
=
newData
||
[]
if
(
chartInst
)
{
chartInst
.
setOption
({
series
:
[
{
data
:
convertData
(
mapData
.
value
)
},
{
data
:
convertData
(
mapData2
.
value
)
}
]
})
}
},
{
deep
:
true
})
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
resize
)
window
.
removeEventListener
(
'resize'
,
resize
)
document
.
removeEventListener
(
'fullscreenchange'
,
resize
)
document
.
removeEventListener
(
'fullscreenchange'
,
resize
)
...
...
src/views/Home/ComputeResource.vue
View file @
6eb1ff55
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
ref
,
watch
,
onMounted
,
onBeforeUnmount
}
from
'vue'
import
{
ref
,
watch
,
onMounted
,
onBeforeUnmount
,
inject
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
*
as
echarts
from
'echarts/core'
import
{
TitleComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
TitleComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
PieChart
}
from
'echarts/charts'
import
{
PieChart
}
from
'echarts/charts'
...
@@ -23,22 +23,21 @@ let chart = null
...
@@ -23,22 +23,21 @@ let chart = null
const
dim
=
ref
(
'gpu'
)
const
dim
=
ref
(
'gpu'
)
const
datasets
=
{
// 从父组件获取统一数据
gpu
:
[
const
dashboardData
=
inject
(
'dashboardData'
,
{})
{
value
:
45.11
,
name
:
'4090'
},
{
value
:
29.88
,
name
:
'A100'
},
const
datasets
=
ref
({
{
value
:
25
,
name
:
'H100'
}
gpu
:
[],
],
source
:
[],
source
:
[
resource
:
[]
{
value
:
50
,
name
:
'自有'
},
})
{
value
:
30
,
name
:
'合作'
},
{
value
:
20
,
name
:
'社会'
}
// 获取算力资源分布数据
],
const
fetchComputeDistribution
=
()
=>
{
resource
:
[
const
data
=
dashboardData
.
value
.
computeDistribution
||
{
gpu
:
[],
source
:
[],
resource
:
[]
}
{
value
:
40
,
name
:
'裸金属'
},
datasets
.
value
.
gpu
=
data
.
gpu
||
[]
{
value
:
35
,
name
:
'VM'
},
datasets
.
value
.
source
=
data
.
source
||
[]
{
value
:
25
,
name
:
'容器'
}
datasets
.
value
.
resource
=
data
.
resource
||
[]
]
}
}
const
titles
=
{
const
titles
=
{
...
@@ -50,7 +49,7 @@ const titles = {
...
@@ -50,7 +49,7 @@ const titles = {
const
baseColors
=
[
'#16FCFF'
,
'#39E9D5'
,
'#1DBAFF'
,
'#77CCFF'
,
'#E99102'
,
'#3AEDCE'
]
const
baseColors
=
[
'#16FCFF'
,
'#39E9D5'
,
'#1DBAFF'
,
'#77CCFF'
,
'#E99102'
,
'#3AEDCE'
]
const
getOption
=
(
which
)
=>
{
const
getOption
=
(
which
)
=>
{
const
data
=
datasets
[
which
]
const
data
=
datasets
.
value
[
which
]
||
[
]
const
total
=
data
.
reduce
((
s
,
i
)
=>
s
+
Number
(
i
.
value
||
0
),
0
)
const
total
=
data
.
reduce
((
s
,
i
)
=>
s
+
Number
(
i
.
value
||
0
),
0
)
const
dataMap
=
data
.
reduce
((
acc
,
i
)
=>
((
acc
[
i
.
name
]
=
Number
(
i
.
value
||
0
)),
acc
),
{})
const
dataMap
=
data
.
reduce
((
acc
,
i
)
=>
((
acc
[
i
.
name
]
=
Number
(
i
.
value
||
0
)),
acc
),
{})
return
{
return
{
...
@@ -139,10 +138,19 @@ watch(dim, () => {
...
@@ -139,10 +138,19 @@ watch(dim, () => {
})
})
onMounted
(()
=>
{
onMounted
(()
=>
{
fetchComputeDistribution
()
render
()
render
()
window
.
addEventListener
(
'resize'
,
resize
)
window
.
addEventListener
(
'resize'
,
resize
)
})
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
computeDistribution
,
()
=>
{
fetchComputeDistribution
()
if
(
chart
)
{
chart
.
setOption
(
getOption
(
dim
.
value
),
true
)
}
},
{
deep
:
true
})
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
resize
)
window
.
removeEventListener
(
'resize'
,
resize
)
if
(
chart
)
{
if
(
chart
)
{
...
...
src/views/Home/Home.vue
View file @
6eb1ff55
...
@@ -13,20 +13,21 @@
...
@@ -13,20 +13,21 @@
<div
class=
"header-title"
>
平台总体态势
</div>
<div
class=
"header-title"
>
平台总体态势
</div>
<div
class=
"cumulative-delivery"
>
<div
class=
"cumulative-delivery"
>
<img
src=
"@/assets/images/cumulative-delivery-icon.png"
alt=
""
/>
<img
src=
"@/assets/images/cumulative-delivery-icon.png"
alt=
""
/>
<span
class=
"label"
style=
"margin-left: 20px"
>
算力总规模(P)
</span>
<span
class=
"label"
style=
"margin-left: 20px"
>
算力总规模(P
TOPS
)
</span>
<span
class=
"value"
>
<span
class=
"value"
>
432.58
{{
dashboardData
.
overallSituation
.
allCompute
}}
P
</span>
P
TOPS
</span>
</div>
</div>
<div
class=
"statistical"
>
<div
class=
"statistical"
>
<div
class=
"statistical-item"
>
<div
class=
"statistical-item"
>
<i></i>
<i></i>
<div>
<div>
<div
class=
"label"
>
已租赁算力(P)
</div>
<div
class=
"label"
>
已租赁算力(P
TOPS
)
</div>
<div
class=
"value"
>
<div
class=
"value"
>
139.94
{{
dashboardData
.
overallSituation
.
leaseCompute
}}
PTOPS
<!--
<animation-count
:end-val=
"53632"
decimals
:range-min=
"0.01"
:range-max=
"0.02"
/>
-->
<!-- 139.94
<animation-count
:end-val=
"53632"
decimals
:range-min=
"0.01"
:range-max=
"0.02"
/>
-->
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -34,7 +35,8 @@
...
@@ -34,7 +35,8 @@
<i></i>
<i></i>
<div>
<div>
<div
class=
"label"
>
算力利用率
</div>
<div
class=
"label"
>
算力利用率
</div>
<div
class=
"value"
>
42.37%
</div>
<div
class=
"value"
>
{{
dashboardData
.
overallSituation
.
computeUtilizationRate
}}
%
</div>
<!-- 42.37%-->
</div>
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<div
class=
"statistical-item"
>
...
@@ -42,8 +44,9 @@
...
@@ -42,8 +44,9 @@
<div>
<div>
<div
class=
"label"
>
运行中任务数
</div>
<div
class=
"label"
>
运行中任务数
</div>
<div
class=
"value"
>
<div
class=
"value"
>
29
{{
dashboardData
.
overallSituation
.
runningTaskCount
}}
<!--
<animation-count
:end-val=
"0.83"
decimals
:range-min=
"40"
:range-max=
"42"
/>
-->
<!-- 29
<animation-count
:end-val=
"0.83"
decimals
:range-min=
"40"
:range-max=
"42"
/>
-->
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -63,106 +66,39 @@
...
@@ -63,106 +66,39 @@
<div
class=
"center"
>
<div
class=
"center"
>
<el-carousel
indicator-position=
"none"
arrow=
"never"
>
<el-carousel
indicator-position=
"none"
arrow=
"never"
>
<el-carousel-item>
<el-carousel-item
v-for=
"(item, index) in dashboardData.carouselItems"
:key=
"index"
>
<button
class=
"year-button"
type=
"button"
>
2025-08
</button>
<button
class=
"year-button"
type=
"button"
>
{{
item
.
yearMonth
}}
</button>
<div
class=
"statistical"
>
<div
class=
"statistical"
>
<div
class=
"statistical-item"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon1.png"
/>
<img
src=
"@/assets/images/statistical-icon1.png"
/>
<div>
<div>
<div
class=
"label"
>
上线应用数
</div>
<div
class=
"label"
>
上线应用数
</div>
<div
class=
"value"
>
4
</div>
<div
class=
"value"
>
{{
item
.
appOnlineCount
}}
</div>
</div>
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon2.png"
/>
<img
src=
"@/assets/images/statistical-icon2.png"
/>
<div>
<div>
<div
class=
"label"
>
可用API
</div>
<div
class=
"label"
>
可用API
</div>
<div
class=
"value"
>
14
个
</div>
<div
class=
"value"
>
{{
item
.
apiOnline
}}
个
</div>
</div>
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon3.png"
/>
<img
src=
"@/assets/images/statistical-icon3.png"
/>
<div>
<div>
<div
class=
"label"
>
模型服务在线率
</div>
<div
class=
"label"
>
模型服务在线率
</div>
<div
class=
"value"
>
12.47
%
</div>
<div
class=
"value"
>
{{
item
.
modelOnlineRate
}}
%
</div>
</div>
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon4.png"
/>
<img
src=
"@/assets/images/statistical-icon4.png"
/>
<div>
<div>
<div
class=
"label"
>
Api调用次数
</div>
<div
class=
"label"
>
Api调用次数
</div>
<div
class=
"value"
>
1202
次
</div>
<div
class=
"value"
>
{{
item
.
apiCallTotal
}}
次
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel-item>
<el-carousel-item>
<button
class=
"year-button"
type=
"button"
>
2025-09
</button>
<div
class=
"statistical"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon1.png"
/>
<div>
<div
class=
"label"
>
上线应用数
</div>
<div
class=
"value"
>
8
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon2.png"
/>
<div>
<div
class=
"label"
>
可用API
</div>
<div
class=
"value"
>
21个
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon3.png"
/>
<div>
<div
class=
"label"
>
模型服务在线率
</div>
<div
class=
"value"
>
14.11%
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon4.png"
/>
<div>
<div
class=
"label"
>
Api调用次数
</div>
<div
class=
"value"
>
1602 次
</div>
</div>
</div>
</div>
</el-carousel-item>
<el-carousel-item>
<button
class=
"year-button"
type=
"button"
>
2025-10
</button>
<div
class=
"statistical"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon1.png"
/>
<div>
<div
class=
"label"
>
上线应用数
</div>
<div
class=
"value"
>
12
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon2.png"
/>
<div>
<div
class=
"label"
>
可用API
</div>
<div
class=
"value"
>
24个
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon3.png"
/>
<div>
<div
class=
"label"
>
模型服务在线率
</div>
<div
class=
"value"
>
15.14%
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon4.png"
/>
<div>
<div
class=
"label"
>
Api调用次数
</div>
<div
class=
"value"
>
3048次
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</el-carousel>
<ChinaMap
v-if=
"showMap === 'china'"
:key=
"'china'"
/>
<ChinaMap
v-if=
"showMap === 'china'"
:key=
"'china'"
/>
...
@@ -217,6 +153,113 @@ import ChinaMap from './ChinaMap'
...
@@ -217,6 +153,113 @@ import ChinaMap from './ChinaMap'
import
{
onBeforeUnmount
,
onMounted
,
ref
,
computed
,
provide
,
watch
}
from
'vue'
import
{
onBeforeUnmount
,
onMounted
,
ref
,
computed
,
provide
,
watch
}
from
'vue'
import
{
useRoute
}
from
'vue-router'
import
{
useRoute
}
from
'vue-router'
import
WorldMap
from
'./WorldMap'
import
WorldMap
from
'./WorldMap'
import
{
HomeDashboardMockApi
}
from
'@/api/biz/home'
import
*
as
IndexCountApi
from
'@/api/Home/count'
// 平台总体态势数据
const
overallSituation
=
ref
({})
// 统一的大屏数据对象
const
dashboardData
=
ref
({
overallSituation
:
{},
apiCalls
:
[],
computeDistribution
:
{
gpu
:
[],
source
:
[],
resource
:
[]
},
users
:
[],
serviceCapability
:
{
years
:
[],
appOnline
:
[],
apiOnline
:
[]
},
carouselItems
:
[],
orders
:
[],
mapData
:
[]
})
// 一次性获取所有数据(根据开关决定来源)
const
fetchAllMockData
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
&&
res
.
list
)
{
// 先获取开关状态
const
switchConfig
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'use_mock_data'
)
const
useMock
=
switchConfig
?.
configValue
===
'true'
if
(
useMock
)
{
// 使用模拟数据
res
.
list
.
forEach
(
item
=>
{
if
(
item
.
configKey
&&
item
.
configValue
)
{
try
{
const
data
=
JSON
.
parse
(
item
.
configValue
)
if
(
item
.
configKey
===
'mock_overall_situation'
)
{
dashboardData
.
value
.
overallSituation
=
data
}
else
if
(
item
.
configKey
===
'mock_api_calls'
)
{
dashboardData
.
value
.
apiCalls
=
data
}
else
if
(
item
.
configKey
===
'mock_compute_distribution'
)
{
dashboardData
.
value
.
computeDistribution
=
data
}
else
if
(
item
.
configKey
===
'mock_users'
)
{
dashboardData
.
value
.
users
=
data
}
else
if
(
item
.
configKey
===
'mock_service_capability'
)
{
dashboardData
.
value
.
serviceCapability
=
data
}
else
if
(
item
.
configKey
===
'mock_app_and_model'
)
{
dashboardData
.
value
.
carouselItems
=
data
}
else
if
(
item
.
configKey
===
'mock_orders'
)
{
dashboardData
.
value
.
orders
=
data
}
else
if
(
item
.
configKey
===
'mock_map_data'
)
{
dashboardData
.
value
.
mapData
=
data
}
}
catch
(
e
)
{
console
.
error
(
'解析模拟数据失败:'
,
item
.
configKey
,
e
)
}
}
})
}
else
{
// 使用真实数据 - 调用真实统计接口
await
fetchRealData
()
}
}
}
catch
(
error
)
{
console
.
error
(
'获取数据失败:'
,
error
)
}
}
// 获取真实数据
const
fetchRealData
=
async
()
=>
{
try
{
// 并行调用所有真实数据接口
const
[
topBarRes
,
apiCallsRes
,
usersRes
]
=
await
Promise
.
all
([
IndexCountApi
.
getTopBarData
(),
IndexCountApi
.
getApiCallsData
(
'm'
),
IndexCountApi
.
getUsersData
(
'd'
)
])
// 总体态势数据
if
(
topBarRes
)
{
dashboardData
.
value
.
overallSituation
=
topBarRes
}
// API 调用趋势
if
(
Array
.
isArray
(
apiCallsRes
))
{
dashboardData
.
value
.
apiCalls
=
apiCallsRes
}
// 用户数据
if
(
Array
.
isArray
(
usersRes
))
{
dashboardData
.
value
.
users
=
usersRes
}
// TODO: 算力分布和服务能力数据的真实接口待实现
}
catch
(
error
)
{
console
.
error
(
'获取真实数据失败:'
,
error
)
}
}
// 提供数据给子组件
provide
(
'dashboardData'
,
dashboardData
)
// 记住地图选择,避免切换/刷新或全屏导致回到默认值
// 记住地图选择,避免切换/刷新或全屏导致回到默认值
const
MAP_TYPE_KEY
=
'home_show_map_type'
const
MAP_TYPE_KEY
=
'home_show_map_type'
...
@@ -359,6 +402,8 @@ onMounted(() => {
...
@@ -359,6 +402,8 @@ onMounted(() => {
document
.
addEventListener
(
'mozfullscreenchange'
,
handleFsChange
)
document
.
addEventListener
(
'mozfullscreenchange'
,
handleFsChange
)
// @ts-ignore
// @ts-ignore
document
.
addEventListener
(
'MSFullscreenChange'
,
handleFsChange
)
document
.
addEventListener
(
'MSFullscreenChange'
,
handleFsChange
)
// 获取所有模拟数据
fetchAllMockData
()
})
})
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
...
...
src/views/Home/Nvestment.vue
View file @
6eb1ff55
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
<div
id=
"nvestment"
class=
"echart-wrap"
></div>
<div
id=
"nvestment"
class=
"echart-wrap"
></div>
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
onMounted
}
from
'vue'
import
{
onMounted
,
inject
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
*
as
echarts
from
'echarts/core'
import
{
import
{
TitleComponent
,
TitleComponent
,
...
@@ -27,9 +27,23 @@ echarts.use([
...
@@ -27,9 +27,23 @@ echarts.use([
UniversalTransition
UniversalTransition
])
])
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
let
myChart
=
null
function
init
()
{
function
init
()
{
const
chartDom
=
document
.
getElementById
(
'nvestment'
)
const
chartDom
=
document
.
getElementById
(
'nvestment'
)
const
myChart
=
echarts
.
init
(
chartDom
)
if
(
!
myChart
)
{
myChart
=
echarts
.
init
(
chartDom
)
}
// 从 dashboardData 获取服务能力数据
const
serviceCapability
=
dashboardData
.
value
.
serviceCapability
||
{}
const
years
=
serviceCapability
.
years
||
[]
const
appOnline
=
serviceCapability
.
appOnline
||
[]
const
apiOnline
=
serviceCapability
.
apiOnline
||
[]
const
option
=
{
const
option
=
{
tooltip
:
{
tooltip
:
{
trigger
:
'axis'
,
trigger
:
'axis'
,
...
@@ -41,7 +55,7 @@ function init () {
...
@@ -41,7 +55,7 @@ function init () {
}
}
},
},
legend
:
{
legend
:
{
data
:
[
'
水能消耗'
,
'电能消耗
'
],
data
:
[
'
上线应用'
,
'上线API
'
],
show
:
false
,
show
:
false
,
right
:
'2%'
,
right
:
'2%'
,
top
:
30
,
top
:
30
,
...
@@ -63,7 +77,7 @@ function init () {
...
@@ -63,7 +77,7 @@ function init () {
{
{
type
:
'category'
,
type
:
'category'
,
boundaryGap
:
true
,
boundaryGap
:
true
,
data
:
[
'2018'
,
'2019'
,
'2020'
,
'2021'
,
'2022'
]
,
data
:
years
,
axisLabel
:
{
axisLabel
:
{
fontSize
:
24
,
fontSize
:
24
,
color
:
'#ffffff'
color
:
'#ffffff'
...
@@ -133,7 +147,7 @@ function init () {
...
@@ -133,7 +147,7 @@ function init () {
}
}
])
])
},
},
data
:
[
10.19
,
189.61
,
120.4
,
75.14
,
''
]
data
:
appOnline
},
},
{
{
name
:
'上线API'
,
name
:
'上线API'
,
...
@@ -150,7 +164,7 @@ function init () {
...
@@ -150,7 +164,7 @@ function init () {
emphasis
:
{
emphasis
:
{
// focus: 'series'
// focus: 'series'
},
},
data
:
[
23.22
,
12027.48
,
75935.47
,
195109.5381
,
''
]
data
:
apiOnline
}
}
]
]
}
}
...
@@ -160,6 +174,11 @@ function init () {
...
@@ -160,6 +174,11 @@ function init () {
onMounted
(()
=>
{
onMounted
(()
=>
{
init
()
init
()
})
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
serviceCapability
,
()
=>
{
init
()
},
{
deep
:
true
})
</
script
>
</
script
>
<
style
scoped
lang=
"scss"
>
<
style
scoped
lang=
"scss"
>
...
...
src/views/Home/apiEchart.vue
View file @
6eb1ff55
...
@@ -29,7 +29,7 @@
...
@@ -29,7 +29,7 @@
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
ref
,
onMounted
,
onBeforeUnmount
}
from
'vue'
import
{
ref
,
onMounted
,
onBeforeUnmount
,
inject
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
*
as
echarts
from
'echarts/core'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
LineChart
}
from
'echarts/charts'
import
{
LineChart
}
from
'echarts/charts'
...
@@ -43,78 +43,99 @@ let chart = null
...
@@ -43,78 +43,99 @@ let chart = null
// 维度切换:d=日, m=月, y=年
// 维度切换:d=日, m=月, y=年
const
rangeType
=
ref
(
'm'
)
const
rangeType
=
ref
(
'm'
)
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
// 嵌入 iframe 和全屏自适应
// 嵌入 iframe 和全屏自适应
const
inIframe
=
ref
(
false
)
const
inIframe
=
ref
(
false
)
const
isFullscreen
=
ref
(
false
)
const
isFullscreen
=
ref
(
false
)
const
outerPadding
=
ref
(
'0px'
)
const
outerPadding
=
ref
(
'0px'
)
// 模拟接口:返回近 N 个月的请求量
// 原始生成模拟数据的方式(备份)
function
mockFetchApiTrend
(
months
=
6
)
{
// function mockFetchApiTrend(months = 6) {
return
new
Promise
((
resolve
)
=>
{
// return new Promise((resolve) => {
setTimeout
(()
=>
{
// setTimeout(() => {
const
now
=
new
Date
()
// const now = new Date()
const
items
=
[]
// const items = []
for
(
let
i
=
months
-
1
;
i
>=
0
;
i
--
)
{
// for (let i = months - 1; i >= 0; i--) {
const
d
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
()
-
i
,
1
)
// const d = new Date(now.getFullYear(), now.getMonth() - i, 1)
const
y
=
d
.
getFullYear
()
// const y = d.getFullYear()
const
m
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
// const m = String(d.getMonth() + 1).padStart(2, '0')
// 构造一个看起来合理的请求数:基础值 + 波动
// const base = 20000 + (i * 1500)
const
base
=
20000
+
(
i
*
1500
)
// const noise = Math.floor(Math.random() * 4000) - 2000
const
noise
=
Math
.
floor
(
Math
.
random
()
*
4000
)
-
2000
// ±2000 波动
// const value = Math.max(0, base + noise)
const
value
=
Math
.
max
(
0
,
base
+
noise
)
// items.push({ month: `${y}-${m}`, requests: value })
items
.
push
({
month
:
`
${
y
}
-
${
m
}
`
,
requests
:
value
})
// }
}
// resolve({ code: 0, data: items })
resolve
({
code
:
0
,
data
:
items
})
// }, 500)
},
500
)
// })
})
// }
}
// 生成不同维度的数据
// 生成不同维度的数据(备份)
function
genDaySeries
(
days
=
7
)
{
// function genDaySeries(days = 7) {
const
now
=
new
Date
()
// const now = new Date()
const
x
=
[]
// const x = []
const
y
=
[]
// const y = []
for
(
let
i
=
days
-
1
;
i
>=
0
;
i
--
)
{
// for (let i = days - 1; i >= 0; i--) {
const
d
=
new
Date
(
now
)
// const d = new Date(now)
d
.
setDate
(
now
.
getDate
()
-
i
)
// d.setDate(now.getDate() - i)
const
mm
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
// const mm = String(d.getMonth() + 1).padStart(2, '0')
const
dd
=
String
(
d
.
getDate
()).
padStart
(
2
,
'0'
)
// const dd = String(d.getDate()).padStart(2, '0')
x
.
push
(
`
${
mm
}
-
${
dd
}
`
)
// x.push(`${mm}-${dd}`)
// 日数据:较小基数 + 波动
// const base = 800 + (days - i) * 30
const
base
=
800
+
(
days
-
i
)
*
30
// const noise = Math.floor(Math.random() * 200) - 100
const
noise
=
Math
.
floor
(
Math
.
random
()
*
200
)
-
100
// y.push(Math.max(0, base + noise))
y
.
push
(
Math
.
max
(
0
,
base
+
noise
))
// }
}
// return { x, y }
return
{
x
,
y
}
// }
}
function
genMonthSeries
(
months
=
12
)
{
// function genMonthSeries(months = 12) {
const
now
=
new
Date
()
// const now = new Date()
const
x
=
[]
// const x = []
const
y
=
[]
// const y = []
for
(
let
i
=
months
-
1
;
i
>=
0
;
i
--
)
{
// for (let i = months - 1; i >= 0; i--) {
const
d
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
()
-
i
,
1
)
// const d = new Date(now.getFullYear(), now.getMonth() - i, 1)
const
m
=
d
.
getMonth
()
+
1
// const m = d.getMonth() + 1
x
.
push
(
`
${
m
}
月`
)
// x.push(`${m}月`)
const
base
=
20000
+
(
months
-
i
)
*
1500
// const base = 20000 + (months - i) * 1500
const
noise
=
Math
.
floor
(
Math
.
random
()
*
4000
)
-
2000
// const noise = Math.floor(Math.random() * 4000) - 2000
y
.
push
(
Math
.
max
(
0
,
base
+
noise
))
// y.push(Math.max(0, base + noise))
}
// }
return
{
x
,
y
}
// return { x, y }
}
// }
// function genYearSeries(years = 5) {
// const now = new Date()
// const x = []
// const y = []
// for (let i = years - 1; i >= 0; i--) {
// const d = new Date(now.getFullYear() - i, 0, 1)
// const yr = d.getFullYear()
// x.push(`${yr}`)
// const base = 200000 + (years - i) * 30000
// const noise = Math.floor(Math.random() * 40000) - 20000
// y.push(Math.max(0, base + noise))
// }
// return { x, y }
// }
function
genYearSeries
(
years
=
5
)
{
// 从 dashboardData 获取图表数据
const
now
=
new
Date
()
const
getChartData
=
()
=>
{
const
x
=
[]
const
data
=
dashboardData
.
value
.
apiCalls
||
[]
const
y
=
[]
if
(
!
Array
.
isArray
(
data
))
{
for
(
let
i
=
years
-
1
;
i
>=
0
;
i
--
)
{
return
{
x
:
[],
y
:
[]
}
const
d
=
new
Date
(
now
.
getFullYear
()
-
i
,
0
,
1
)
const
yr
=
d
.
getFullYear
()
x
.
push
(
`
${
yr
}
`
)
const
base
=
200000
+
(
years
-
i
)
*
30000
const
noise
=
Math
.
floor
(
Math
.
random
()
*
40000
)
-
20000
y
.
push
(
Math
.
max
(
0
,
base
+
noise
))
}
}
// 根据数据格式转换为图表数据
// apiCalls 格式: [{countDate: "2025-01-01", callsCount: 1202}, ...]
const
x
=
data
.
map
(
item
=>
{
const
date
=
new
Date
(
item
.
countDate
)
const
m
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
const
d
=
String
(
date
.
getDate
()).
padStart
(
2
,
'0'
)
return
`
${
m
}
-
${
d
}
`
})
const
y
=
data
.
map
(
item
=>
item
.
callsCount
)
return
{
x
,
y
}
return
{
x
,
y
}
}
}
...
@@ -173,16 +194,7 @@ function getOption(x, y) {
...
@@ -173,16 +194,7 @@ function getOption(x, y) {
}
}
async
function
render
()
{
async
function
render
()
{
let
x
=
[]
const
{
x
,
y
}
=
getChartData
()
let
y
=
[]
// 按维度生成数据
if
(
rangeType
.
value
===
'd'
)
{
;({
x
,
y
}
=
genDaySeries
(
7
))
}
else
if
(
rangeType
.
value
===
'm'
)
{
;({
x
,
y
}
=
genMonthSeries
(
12
))
}
else
{
;({
x
,
y
}
=
genYearSeries
(
5
))
}
if
(
!
chart
)
chart
=
echarts
.
init
(
chartRef
.
value
)
if
(
!
chart
)
chart
=
echarts
.
init
(
chartRef
.
value
)
chart
.
setOption
(
getOption
(
x
,
y
),
true
)
chart
.
setOption
(
getOption
(
x
,
y
),
true
)
...
@@ -237,6 +249,11 @@ onMounted(() => {
...
@@ -237,6 +249,11 @@ onMounted(() => {
document
.
addEventListener
(
'MSFullscreenChange'
,
onFsChange
)
document
.
addEventListener
(
'MSFullscreenChange'
,
onFsChange
)
})
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
apiCalls
,
()
=>
{
render
()
},
{
deep
:
true
})
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
onResize
)
window
.
removeEventListener
(
'resize'
,
onResize
)
document
.
removeEventListener
(
'fullscreenchange'
,
onFsChange
)
document
.
removeEventListener
(
'fullscreenchange'
,
onFsChange
)
...
...
src/views/Home/orderManage.vue
View file @
6eb1ff55
...
@@ -22,17 +22,19 @@
...
@@ -22,17 +22,19 @@
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
inject
,
computed
,
onMounted
,
onBeforeUnmount
,
ref
}
from
'vue'
import
{
inject
,
computed
,
onMounted
,
onBeforeUnmount
,
ref
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
*
as
echarts
from
'echarts/core'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
LineChart
}
from
'echarts/charts'
import
{
LineChart
}
from
'echarts/charts'
import
{
CanvasRenderer
}
from
'echarts/renderers'
import
{
CanvasRenderer
}
from
'echarts/renderers'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
type
{
EChartsOption
}
from
'echarts'
import
type
{
EChartsOption
}
from
'echarts'
import
*
as
IndexCountApi
from
'@/api/Home/count'
echarts
.
use
([
GridComponent
,
TooltipComponent
,
LegendComponent
,
LineChart
,
CanvasRenderer
])
echarts
.
use
([
GridComponent
,
TooltipComponent
,
LegendComponent
,
LineChart
,
CanvasRenderer
])
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
// 从父组件(Home.vue)注入全屏状态与方法
// 从父组件(Home.vue)注入全屏状态与方法
const
fs
=
inject
(
'fsState'
,
null
)
as
null
|
{
isFullscreen
:
any
;
toggleFullscreen
:
()
=>
void
}
const
fs
=
inject
(
'fsState'
,
null
)
as
null
|
{
isFullscreen
:
any
;
toggleFullscreen
:
()
=>
void
}
const
fsIsFullscreen
=
computed
(()
=>
!!
(
fs
&&
fs
.
isFullscreen
&&
fs
.
isFullscreen
.
value
))
const
fsIsFullscreen
=
computed
(()
=>
!!
(
fs
&&
fs
.
isFullscreen
&&
fs
.
isFullscreen
.
value
))
...
@@ -138,12 +140,14 @@ function getOption(xData: string[], seriesData: {
...
@@ -138,12 +140,14 @@ function getOption(xData: string[], seriesData: {
}
}
async
function
render
()
{
async
function
render
()
{
const
res
=
await
IndexCountApi
.
getOrdersData
(
dateType
.
value
)
const
orders
=
dashboardData
.
value
.
orders
||
{}
const
x
=
(
res
||
[]).
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
res
=
orders
[
dateType
.
value
]
||
[]
const
computeCount
=
(
res
||
[]).
map
((
item
:
any
)
=>
item
.
computeOrdersCount
||
0
)
const
apiCount
=
(
res
||
[]).
map
((
item
:
any
)
=>
item
.
apiOrdersCount
||
0
)
const
x
=
res
.
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
computeAmount
=
(
res
||
[]).
map
((
item
:
any
)
=>
Number
(((
item
.
computeOrdersAmount
||
0
)
/
100
).
toFixed
(
2
)))
const
computeCount
=
res
.
map
((
item
:
any
)
=>
item
.
computeOrdersCount
||
0
)
const
apiAmount
=
(
res
||
[]).
map
((
item
:
any
)
=>
Number
(((
item
.
apiOrdersAmount
||
0
)
/
100
).
toFixed
(
2
)))
const
apiCount
=
res
.
map
((
item
:
any
)
=>
item
.
apiOrdersCount
||
0
)
const
computeAmount
=
res
.
map
((
item
:
any
)
=>
Number
(((
item
.
computeOrdersAmount
||
0
)
/
100
).
toFixed
(
2
)))
const
apiAmount
=
res
.
map
((
item
:
any
)
=>
Number
(((
item
.
apiOrdersAmount
||
0
)
/
100
).
toFixed
(
2
)))
if
(
!
chart
)
{
if
(
!
chart
)
{
const
el
=
document
.
getElementById
(
'energyManage'
)
as
HTMLElement
|
null
const
el
=
document
.
getElementById
(
'energyManage'
)
as
HTMLElement
|
null
...
@@ -161,6 +165,11 @@ function changeType(t: 'd' | 'm' | 'y') {
...
@@ -161,6 +165,11 @@ function changeType(t: 'd' | 'm' | 'y') {
const
onResize
=
()
=>
chart
&&
chart
.
resize
()
const
onResize
=
()
=>
chart
&&
chart
.
resize
()
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
orders
,
()
=>
{
render
()
},
{
deep
:
true
})
onMounted
(()
=>
{
onMounted
(()
=>
{
render
()
render
()
window
.
addEventListener
(
'resize'
,
onResize
)
window
.
addEventListener
(
'resize'
,
onResize
)
...
...
src/views/Home/userManage.vue
View file @
6eb1ff55
...
@@ -12,17 +12,19 @@
...
@@ -12,17 +12,19 @@
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
inject
,
computed
,
onMounted
,
onBeforeUnmount
,
ref
}
from
'vue'
import
{
inject
,
computed
,
onMounted
,
onBeforeUnmount
,
ref
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
*
as
echarts
from
'echarts/core'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
LineChart
}
from
'echarts/charts'
import
{
LineChart
}
from
'echarts/charts'
import
{
CanvasRenderer
}
from
'echarts/renderers'
import
{
CanvasRenderer
}
from
'echarts/renderers'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
type
{
EChartsOption
}
from
'echarts'
import
type
{
EChartsOption
}
from
'echarts'
import
*
as
IndexCountApi
from
'@/api/Home/count'
echarts
.
use
([
GridComponent
,
TooltipComponent
,
LegendComponent
,
LineChart
,
CanvasRenderer
])
echarts
.
use
([
GridComponent
,
TooltipComponent
,
LegendComponent
,
LineChart
,
CanvasRenderer
])
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
// 从父组件(Home.vue)注入全屏状态与方法
// 从父组件(Home.vue)注入全屏状态与方法
const
fs
=
inject
(
'fsState'
,
null
)
as
null
|
{
isFullscreen
:
any
;
toggleFullscreen
:
()
=>
void
}
const
fs
=
inject
(
'fsState'
,
null
)
as
null
|
{
isFullscreen
:
any
;
toggleFullscreen
:
()
=>
void
}
const
fsIsFullscreen
=
computed
(()
=>
!!
(
fs
&&
fs
.
isFullscreen
&&
fs
.
isFullscreen
.
value
))
const
fsIsFullscreen
=
computed
(()
=>
!!
(
fs
&&
fs
.
isFullscreen
&&
fs
.
isFullscreen
.
value
))
...
@@ -88,8 +90,9 @@ function getOption(xData: string[], growth: number[], active: number[]): ECharts
...
@@ -88,8 +90,9 @@ function getOption(xData: string[], growth: number[], active: number[]): ECharts
}
}
async
function
render
()
{
async
function
render
()
{
const
res
=
await
IndexCountApi
.
getUsersData
(
dateType
.
value
)
// 直接从 dashboardData 获取数据
const
arr
=
Array
.
isArray
(
res
)
?
res
:
[]
const
arr
=
dashboardData
.
value
.
users
||
[]
const
x
=
arr
.
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
x
=
arr
.
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
growth
=
arr
.
map
((
item
:
any
)
=>
item
.
growthUsersCount
??
item
.
usersCount
??
0
)
const
growth
=
arr
.
map
((
item
:
any
)
=>
item
.
growthUsersCount
??
item
.
usersCount
??
0
)
const
active
=
arr
.
map
((
item
:
any
,
idx
:
number
)
=>
const
active
=
arr
.
map
((
item
:
any
,
idx
:
number
)
=>
...
@@ -112,6 +115,11 @@ function changeType(t: 'd' | 'm' | 'y') {
...
@@ -112,6 +115,11 @@ function changeType(t: 'd' | 'm' | 'y') {
const
onResize
=
()
=>
chart
&&
chart
.
resize
()
const
onResize
=
()
=>
chart
&&
chart
.
resize
()
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
users
,
()
=>
{
render
()
},
{
deep
:
true
})
onMounted
(()
=>
{
onMounted
(()
=>
{
render
()
render
()
window
.
addEventListener
(
'resize'
,
onResize
)
window
.
addEventListener
(
'resize'
,
onResize
)
...
...
src/views/biz/home/HomeDashboardMockForm.vue
0 → 100644
View file @
6eb1ff55
<
template
>
<Dialog
:title=
"dialogTitle"
v-model=
"dialogVisible"
>
<el-form
ref=
"formRef"
:model=
"formData"
:rules=
"formRules"
label-width=
"100px"
v-loading=
"formLoading"
>
<el-form-item
label=
"配置key"
prop=
"configKey"
>
<el-input
v-model=
"formData.configKey"
placeholder=
"请输入配置key"
/>
</el-form-item>
<el-form-item
label=
"配置类型"
prop=
"configType"
>
<el-select
v-model=
"formData.configType"
placeholder=
"请选择配置类型"
:disabled=
"formType === 'update'"
>
<el-option
label=
"开关 (switch)"
value=
"switch"
/>
<el-option
label=
"数据 (data)"
value=
"data"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"配置值"
prop=
"configValue"
>
<el-input
v-model=
"formData.configValue"
type=
"textarea"
:rows=
"10"
placeholder=
"请输入配置值,JSON格式"
v-if=
"formData.configType === 'data'"
/>
<el-switch
v-model=
"switchValue"
active-text=
"开启"
inactive-text=
"关闭"
v-else
/>
</el-form-item>
<el-form-item
v-if=
"formData.configType === 'data'"
>
<el-button
@
click=
"formatJson"
type=
"primary"
size=
"small"
>
格式化 JSON
</el-button>
<el-button
@
click=
"validateJson"
type=
"success"
size=
"small"
>
校验 JSON
</el-button>
</el-form-item>
<el-form-item
label=
"配置描述"
prop=
"description"
>
<Editor
v-model=
"formData.description"
height=
"150px"
/>
</el-form-item>
</el-form>
<template
#
footer
>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
>
确 定
</el-button>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
</Dialog>
</template>
<
script
setup
lang=
"ts"
>
import
{
HomeDashboardMockApi
,
HomeDashboardMock
}
from
'@/api/biz/home'
/** 首页大屏模拟数据配置 表单 */
defineOptions
({
name
:
'HomeDashboardMockForm'
})
const
{
t
}
=
useI18n
()
// 国际化
const
message
=
useMessage
()
// 消息弹窗
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
''
)
// 弹窗的标题
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
formType
=
ref
(
''
)
// 表单的类型:create - 新增;update - 修改
const
formData
=
ref
({
id
:
undefined
,
configKey
:
undefined
,
configType
:
undefined
,
configValue
:
undefined
,
description
:
undefined
})
const
formRules
=
reactive
({
configKey
:
[{
required
:
true
,
message
:
'配置key不能为空'
,
trigger
:
'blur'
}],
configType
:
[{
required
:
true
,
message
:
'配置类型不能为空'
,
trigger
:
'change'
}],
configValue
:
[{
required
:
true
,
message
:
'配置值不能为空'
,
trigger
:
'blur'
}]
})
const
formRef
=
ref
()
// 表单 Ref
// 开关类型的值处理
const
switchValue
=
computed
({
get
:
()
=>
formData
.
value
.
configValue
===
'true'
,
set
:
(
val
)
=>
{
formData
.
value
.
configValue
=
val
?
'true'
:
'false'
}
})
// 格式化JSON
const
formatJson
=
()
=>
{
try
{
const
parsed
=
JSON
.
parse
(
formData
.
value
.
configValue
)
formData
.
value
.
configValue
=
JSON
.
stringify
(
parsed
,
null
,
2
)
message
.
success
(
'JSON格式化成功'
)
}
catch
(
e
)
{
message
.
error
(
'JSON格式错误,无法格式化'
)
}
}
// 校验JSON
const
validateJson
=
()
=>
{
try
{
JSON
.
parse
(
formData
.
value
.
configValue
)
message
.
success
(
'JSON格式正确'
)
}
catch
(
e
)
{
message
.
error
(
'JSON格式错误:'
+
e
.
message
)
}
}
/** 打开弹窗 */
const
open
=
async
(
type
:
string
,
id
?:
number
)
=>
{
dialogVisible
.
value
=
true
dialogTitle
.
value
=
t
(
'action.'
+
type
)
formType
.
value
=
type
resetForm
()
// 修改时,设置数据
if
(
id
)
{
formLoading
.
value
=
true
try
{
formData
.
value
=
await
HomeDashboardMockApi
.
getHomeDashboardMock
(
id
)
}
finally
{
formLoading
.
value
=
false
}
}
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
/** 提交表单 */
const
emit
=
defineEmits
([
'success'
])
// 定义 success 事件,用于操作成功后的回调
const
submitForm
=
async
()
=>
{
// 校验表单
await
formRef
.
value
.
validate
()
// 提交请求
formLoading
.
value
=
true
try
{
const
data
=
formData
.
value
as
unknown
as
HomeDashboardMock
if
(
formType
.
value
===
'create'
)
{
await
HomeDashboardMockApi
.
createHomeDashboardMock
(
data
)
message
.
success
(
t
(
'common.createSuccess'
))
}
else
{
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
data
)
message
.
success
(
t
(
'common.updateSuccess'
))
}
dialogVisible
.
value
=
false
// 发送操作成功的事件
emit
(
'success'
)
}
finally
{
formLoading
.
value
=
false
}
}
/** 重置表单 */
const
resetForm
=
()
=>
{
formData
.
value
=
{
id
:
undefined
,
configKey
:
undefined
,
configType
:
undefined
,
configValue
:
undefined
,
description
:
undefined
}
formRef
.
value
?.
resetFields
()
}
</
script
>
src/views/biz/home/index.vue
0 → 100644
View file @
6eb1ff55
<
template
>
<ContentWrap>
<div
class=
"mock-config-page"
>
<!-- 模拟数据开关 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:setting"
:size=
"20"
/>
<span
class=
"title"
>
模拟数据开关
</span>
</div>
</
template
>
<div
class=
"switch-section"
>
<el-radio-group
v-model=
"mockDataEnabled"
@
change=
"handleSwitchChange"
>
<el-radio
:label=
"false"
>
关闭(使用真实数据)
</el-radio>
<el-radio
:label=
"true"
>
开启(使用模拟数据)
</el-radio>
</el-radio-group>
<el-text
type=
"info"
style=
"margin-left: 20px;"
>
开启后,首页大屏将使用下方的模拟数据进行展示
</el-text>
</div>
</el-card>
<!-- 第一行:平台总体态势 + API请求趋势 -->
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<!-- 平台总体态势 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:data-analysis"
:size=
"20"
/>
<span
class=
"title"
>
平台总体态势
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveOverallSituation"
>
保存
</el-button>
</div>
</
template
>
<el-form
:model=
"overallSituation"
label-width=
"140px"
class=
"config-form"
>
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"算力总规模(P)"
>
<el-input-number
v-model=
"overallSituation.allCompute"
:precision=
"2"
:min=
"0"
/>
</el-form-item>
</el-col>
<el-col
:span=
"12"
>
<el-form-item
label=
"已租赁算力(P)"
>
<el-input-number
v-model=
"overallSituation.leaseCompute"
:precision=
"2"
:min=
"0"
/>
</el-form-item>
</el-col>
</el-row>
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"算力利用率(%)"
>
<el-input-number
v-model=
"overallSituation.computeUtilizationRate"
:precision=
"2"
:min=
"0"
:max=
"100"
/>
</el-form-item>
</el-col>
<el-col
:span=
"12"
>
<el-form-item
label=
"运行中任务数"
>
<el-input-number
v-model=
"overallSituation.runningTaskCount"
:min=
"0"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</el-col>
<el-col
:span=
"12"
>
<!-- API请求趋势 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:line-chart"
:size=
"20"
/>
<span
class=
"title"
>
API请求趋势
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveApiCalls"
>
保存
</el-button>
</div>
</
template
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addApiCallItem"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加数据项
</el-button>
<table
class=
"data-table"
v-if=
"apiCallsList.length > 0"
>
<thead>
<tr>
<th>
日期
</th>
<th>
调用次数
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in apiCallsList"
:key=
"index"
>
<td>
<el-date-picker
v-model=
"item.countDate"
type=
"date"
placeholder=
"选择日期"
format=
"YYYY-MM-DD"
value-format=
"YYYY-MM-DD"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.callsCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeApiCallItem(index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-card>
</el-col>
</el-row>
<!-- 第二行:算力资源分布 + 用户管理 -->
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<!-- 算力资源分布 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:pie-chart"
:size=
"20"
/>
<span
class=
"title"
>
算力资源分布
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveComputeDistribution"
>
保存
</el-button>
</div>
</
template
>
<el-tabs
v-model=
"activeTab"
type=
"border-card"
>
<!-- GPU型号 -->
<el-tab-pane
label=
"GPU型号"
name=
"gpu"
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addComputeItem('gpu')"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加GPU型号
</el-button>
<table
class=
"data-table"
v-if=
"computeDistribution.gpu && computeDistribution.gpu.length > 0"
>
<thead>
<tr>
<th>
型号名称
</th>
<th>
占比
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in computeDistribution.gpu"
:key=
"index"
>
<td>
<el-input
v-model=
"item.name"
placeholder=
"如:4090"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.value"
:precision=
"2"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeComputeItem('gpu', index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-tab-pane>
<!-- 算力来源 -->
<el-tab-pane
label=
"算力来源"
name=
"source"
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addComputeItem('source')"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加来源
</el-button>
<table
class=
"data-table"
v-if=
"computeDistribution.source && computeDistribution.source.length > 0"
>
<thead>
<tr>
<th>
来源名称
</th>
<th>
占比
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in computeDistribution.source"
:key=
"index"
>
<td>
<el-input
v-model=
"item.name"
placeholder=
"如:自有"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.value"
:precision=
"2"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeComputeItem('source', index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-tab-pane>
<!-- 计算资源 -->
<el-tab-pane
label=
"计算资源"
name=
"resource"
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addComputeItem('resource')"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加资源类型
</el-button>
<table
class=
"data-table"
v-if=
"computeDistribution.resource && computeDistribution.resource.length > 0"
>
<thead>
<tr>
<th>
资源名称
</th>
<th>
占比
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in computeDistribution.resource"
:key=
"index"
>
<td>
<el-input
v-model=
"item.name"
placeholder=
"如:裸金属"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.value"
:precision=
"2"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeComputeItem('resource', index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
<el-col
:span=
"12"
>
<!-- 用户管理 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:user"
:size=
"20"
/>
<span
class=
"title"
>
用户管理
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveUsers"
>
保存
</el-button>
</div>
</
template
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addUserItem"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加数据项
</el-button>
<table
class=
"data-table"
v-if=
"usersList.length > 0"
>
<thead>
<tr>
<th>
日期
</th>
<th>
用户总数
</th>
<th>
增长用户数
</th>
<th>
活跃用户数
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in usersList"
:key=
"index"
>
<td>
<el-date-picker
v-model=
"item.countDate"
type=
"date"
placeholder=
"选择日期"
format=
"YYYY-MM-DD"
value-format=
"YYYY-MM-DD"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.usersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.growthUsersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.activeUsersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeUserItem(index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-card>
</el-col>
</el-row>
<!-- 第三行:服务能力 + 订单管理 -->
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<!-- 服务能力 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:trend-charts"
:size=
"20"
/>
<span
class=
"title"
>
服务能力
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveServiceCapability"
>
保存
</el-button>
</div>
</
template
>
<el-form
:model=
"serviceCapability"
label-width=
"120px"
class=
"config-form"
>
<el-form-item
label=
"年份"
>
<el-input
v-model=
"serviceCapability.yearsStr"
placeholder=
"如:2018,2019,2020"
/>
</el-form-item>
<el-form-item
label=
"上线应用"
>
<el-input
v-model=
"serviceCapability.appOnlineStr"
placeholder=
"如:10,20,30,40,50(逗号分隔)"
/>
</el-form-item>
<el-form-item
label=
"上线API"
>
<el-input
v-model=
"serviceCapability.apiOnlineStr"
placeholder=
"如:100,200,300,400,500(逗号分隔)"
/>
</el-form-item>
</el-form>
</el-card>
</el-col>
<el-col
:span=
"12"
>
<!-- 订单管理 -->
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:shopping-cart"
:size=
"20"
/>
<span
class=
"title"
>
订单管理
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveOrders"
>
保存
</el-button>
</div>
</
template
>
<el-tabs
v-model=
"ordersTab"
type=
"border-card"
>
<!-- 日维度 -->
<el-tab-pane
label=
"日"
name=
"d"
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addOrderItem('d')"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加数据项
</el-button>
<table
class=
"data-table"
v-if=
"ordersData.d && ordersData.d.length > 0"
>
<thead>
<tr>
<th>
日期
</th>
<th>
算力订单数
</th>
<th>
API订单数
</th>
<th>
算力订单金额(分)
</th>
<th>
API订单金额(分)
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in ordersData.d"
:key=
"index"
>
<td>
<el-date-picker
v-model=
"item.countDate"
type=
"date"
placeholder=
"选择日期"
format=
"YYYY-MM-DD"
value-format=
"YYYY-MM-DD"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.computeOrdersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.apiOrdersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.computeOrdersAmount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.apiOrdersAmount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeOrderItem('d', index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-tab-pane>
<!-- 月维度 -->
<el-tab-pane
label=
"月"
name=
"m"
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addOrderItem('m')"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加数据项
</el-button>
<table
class=
"data-table"
v-if=
"ordersData.m && ordersData.m.length > 0"
>
<thead>
<tr>
<th>
日期
</th>
<th>
算力订单数
</th>
<th>
API订单数
</th>
<th>
算力订单金额(分)
</th>
<th>
API订单金额(分)
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in ordersData.m"
:key=
"index"
>
<td>
<el-date-picker
v-model=
"item.countDate"
type=
"month"
placeholder=
"选择月份"
format=
"YYYY-MM"
value-format=
"YYYY-MM"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.computeOrdersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.apiOrdersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.computeOrdersAmount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.apiOrdersAmount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeOrderItem('m', index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-tab-pane>
<!-- 年维度 -->
<el-tab-pane
label=
"年"
name=
"y"
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addOrderItem('y')"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加数据项
</el-button>
<table
class=
"data-table"
v-if=
"ordersData.y && ordersData.y.length > 0"
>
<thead>
<tr>
<th>
年份
</th>
<th>
算力订单数
</th>
<th>
API订单数
</th>
<th>
算力订单金额(分)
</th>
<th>
API订单金额(分)
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in ordersData.y"
:key=
"index"
>
<td>
<el-input
v-model=
"item.countDate"
placeholder=
"如:2025"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.computeOrdersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.apiOrdersCount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.computeOrdersAmount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-input-number
v-model=
"item.apiOrdersAmount"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeOrderItem('y', index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加"
:image-size=
"80"
/>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
<!-- 地图数据 -->
<el-row
:gutter=
"20"
>
<el-col
:span=
"24"
>
<el-card
class=
"config-card"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"card-header"
>
<Icon
icon=
"ep:location"
:size=
"20"
/>
<span
class=
"title"
>
地图数据
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"saveMapData"
>
保存
</el-button>
</div>
</
template
>
<div
class=
"table-section"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"addMapDataItem"
style=
"margin-bottom: 10px;"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
添加省份
</el-button>
<table
class=
"data-table"
v-if=
"mapDataList.length > 0"
>
<thead>
<tr>
<th
style=
"width: 200px"
>
省份
</th>
<th>
算力资源(PTOPS)
</th>
<th
style=
"width: 80px"
>
操作
</th>
</tr>
</thead>
<tbody>
<tr
v-for=
"(item, index) in mapDataList"
:key=
"index"
>
<td>
<el-select
v-model=
"item.name"
placeholder=
"选择省份"
size=
"small"
style=
"width: 100%"
>
<el-option
v-for=
"province in provinces"
:key=
"province"
:label=
"province"
:value=
"province"
/>
</el-select>
</td>
<td>
<el-input-number
v-model=
"item.value"
:precision=
"4"
:min=
"0"
size=
"small"
/>
</td>
<td>
<el-button
type=
"danger"
size=
"small"
@
click=
"removeMapDataItem(index)"
>
删除
</el-button>
</td>
</tr>
</tbody>
</table>
<el-empty
v-else
description=
"暂无数据,请添加省份"
:image-size=
"80"
/>
</div>
</el-card>
</el-col>
</el-row>
</div>
</ContentWrap>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
}
from
'vue'
import
{
HomeDashboardMockApi
}
from
'@/api/biz/home'
import
{
ElMessage
}
from
'element-plus'
defineOptions
({
name
:
'HomeDashboardConfig'
})
const
message
=
useMessage
()
// 模拟数据开关
const
mockDataEnabled
=
ref
(
false
)
// 平台总体态势
const
overallSituation
=
ref
({
allCompute
:
432.58
,
leaseCompute
:
139.94
,
computeUtilizationRate
:
42.37
,
runningTaskCount
:
29
})
// API请求趋势
const
apiCallsList
=
ref
([])
// 用户管理
const
usersList
=
ref
([])
// 服务能力
const
serviceCapability
=
ref
({
yearsStr
:
''
,
appOnlineStr
:
''
,
apiOnlineStr
:
''
})
// 算力资源分布
const
activeTab
=
ref
(
'gpu'
)
const
computeDistribution
=
ref
({
gpu
:
[],
source
:
[],
resource
:
[]
})
// 订单管理
const
ordersTab
=
ref
(
'd'
)
const
ordersData
=
ref
({
d
:
[],
m
:
[],
y
:
[]
})
// 地图数据
const
mapDataList
=
ref
([])
const
provinces
=
[
'北京市'
,
'天津市'
,
'河北省'
,
'山西省'
,
'内蒙古自治区'
,
'辽宁省'
,
'吉林省'
,
'黑龙江省'
,
'上海市'
,
'江苏省'
,
'浙江省'
,
'安徽省'
,
'福建省'
,
'江西省'
,
'山东省'
,
'河南省'
,
'湖北省'
,
'湖南省'
,
'广东省'
,
'广西壮族自治区'
,
'海南省'
,
'重庆市'
,
'四川省'
,
'贵州省'
,
'云南省'
,
'西藏自治区'
,
'陕西省'
,
'甘肃省'
,
'青海省'
,
'宁夏回族自治区'
,
'新疆维吾尔自治区'
,
'台湾省'
,
'香港特别行政区'
,
'澳门特别行政区'
]
// 加载所有模拟数据
const
loadAllMockData
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
res
.
list
.
forEach
(
item
=>
{
if
(
item
.
configKey
===
'use_mock_data'
)
{
mockDataEnabled
.
value
=
item
.
configValue
===
'true'
}
else
if
(
item
.
configKey
===
'mock_overall_situation'
)
{
try
{
overallSituation
.
value
=
JSON
.
parse
(
item
.
configValue
)
}
catch
(
e
)
{
console
.
error
(
'解析平台总体态势失败'
,
e
)
}
}
else
if
(
item
.
configKey
===
'mock_api_calls'
)
{
try
{
apiCallsList
.
value
=
JSON
.
parse
(
item
.
configValue
)
}
catch
(
e
)
{
console
.
error
(
'解析API请求趋势失败'
,
e
)
}
}
else
if
(
item
.
configKey
===
'mock_compute_distribution'
)
{
try
{
computeDistribution
.
value
=
JSON
.
parse
(
item
.
configValue
)
}
catch
(
e
)
{
console
.
error
(
'解析算力资源分布失败'
,
e
)
}
}
else
if
(
item
.
configKey
===
'mock_users'
)
{
try
{
usersList
.
value
=
JSON
.
parse
(
item
.
configValue
)
}
catch
(
e
)
{
console
.
error
(
'解析用户管理失败'
,
e
)
}
}
else
if
(
item
.
configKey
===
'mock_service_capability'
)
{
try
{
const
data
=
JSON
.
parse
(
item
.
configValue
)
serviceCapability
.
value
=
{
yearsStr
:
data
.
years
?
data
.
years
.
join
(
','
)
:
''
,
appOnlineStr
:
data
.
appOnline
?
data
.
appOnline
.
join
(
','
)
:
''
,
apiOnlineStr
:
data
.
apiOnline
?
data
.
apiOnline
.
join
(
','
)
:
''
}
}
catch
(
e
)
{
console
.
error
(
'解析服务能力失败'
,
e
)
}
}
else
if
(
item
.
configKey
===
'mock_orders'
)
{
try
{
ordersData
.
value
=
JSON
.
parse
(
item
.
configValue
)
}
catch
(
e
)
{
console
.
error
(
'解析订单管理失败'
,
e
)
}
}
else
if
(
item
.
configKey
===
'mock_map_data'
)
{
try
{
mapDataList
.
value
=
JSON
.
parse
(
item
.
configValue
)
}
catch
(
e
)
{
console
.
error
(
'解析地图数据失败'
,
e
)
}
}
})
}
}
catch
(
error
)
{
console
.
error
(
'加载模拟数据失败'
,
error
)
}
}
// 保存开关
const
handleSwitchChange
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
switchConfig
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'use_mock_data'
)
if
(
switchConfig
)
{
const
updateData
=
{
...
switchConfig
,
configValue
:
mockDataEnabled
.
value
?
'true'
:
'false'
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
message
.
success
(
mockDataEnabled
.
value
?
'模拟数据已开启'
:
'模拟数据已关闭'
)
}
}
}
catch
(
error
)
{
console
.
error
(
'保存开关失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 保存平台总体态势
const
saveOverallSituation
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_overall_situation'
)
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
overallSituation
.
value
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
message
.
success
(
'保存成功'
)
}
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 保存API请求趋势
const
saveApiCalls
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_api_calls'
)
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
apiCallsList
.
value
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
message
.
success
(
'保存成功'
)
}
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 添加API请求数据项
const
addApiCallItem
=
()
=>
{
apiCallsList
.
value
.
push
({
countDate
:
''
,
callsCount
:
0
})
}
// 删除API请求数据项
const
removeApiCallItem
=
(
index
:
number
)
=>
{
apiCallsList
.
value
.
splice
(
index
,
1
)
}
// 保存算力资源分布
const
saveComputeDistribution
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_compute_distribution'
)
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
computeDistribution
.
value
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
message
.
success
(
'保存成功'
)
}
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 添加算力资源项
const
addComputeItem
=
(
type
:
string
)
=>
{
if
(
!
computeDistribution
.
value
[
type
])
{
computeDistribution
.
value
[
type
]
=
[]
}
computeDistribution
.
value
[
type
].
push
({
name
:
''
,
value
:
0
})
}
// 删除算力资源项
const
removeComputeItem
=
(
type
:
string
,
index
:
number
)
=>
{
computeDistribution
.
value
[
type
].
splice
(
index
,
1
)
}
// 保存用户管理
const
saveUsers
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_users'
)
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
usersList
.
value
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
message
.
success
(
'保存成功'
)
}
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 添加用户数据项
const
addUserItem
=
()
=>
{
usersList
.
value
.
push
({
countDate
:
''
,
usersCount
:
0
,
growthUsersCount
:
0
,
activeUsersCount
:
0
})
}
// 删除用户数据项
const
removeUserItem
=
(
index
:
number
)
=>
{
usersList
.
value
.
splice
(
index
,
1
)
}
// 保存服务能力
const
saveServiceCapability
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_service_capability'
)
const
data
=
{
years
:
serviceCapability
.
value
.
yearsStr
.
split
(
','
).
map
(
s
=>
s
.
trim
()),
appOnline
:
serviceCapability
.
value
.
appOnlineStr
.
split
(
','
).
map
(
s
=>
parseFloat
(
s
.
trim
())
||
0
),
apiOnline
:
serviceCapability
.
value
.
apiOnlineStr
.
split
(
','
).
map
(
s
=>
parseFloat
(
s
.
trim
())
||
0
)
}
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
data
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
}
else
{
const
createData
=
{
configKey
:
'mock_service_capability'
,
configType
:
'data'
,
configValue
:
JSON
.
stringify
(
data
),
description
:
'服务能力模拟数据'
}
await
HomeDashboardMockApi
.
createHomeDashboardMock
(
createData
)
}
message
.
success
(
'保存成功'
)
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 订单管理 - 保存
const
saveOrders
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_orders'
)
const
data
=
ordersData
.
value
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
data
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
}
else
{
const
createData
=
{
configKey
:
'mock_orders'
,
configType
:
'data'
,
configValue
:
JSON
.
stringify
(
data
),
description
:
'订单管理模拟数据'
}
await
HomeDashboardMockApi
.
createHomeDashboardMock
(
createData
)
}
message
.
success
(
'保存成功'
)
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 订单管理 - 添加数据项
const
addOrderItem
=
(
type
:
'd'
|
'm'
|
'y'
)
=>
{
if
(
!
ordersData
.
value
[
type
])
{
ordersData
.
value
[
type
]
=
[]
}
ordersData
.
value
[
type
].
push
({
countDate
:
''
,
computeOrdersCount
:
0
,
apiOrdersCount
:
0
,
computeOrdersAmount
:
0
,
apiOrdersAmount
:
0
})
}
// 订单管理 - 删除数据项
const
removeOrderItem
=
(
type
:
'd'
|
'm'
|
'y'
,
index
:
number
)
=>
{
ordersData
.
value
[
type
].
splice
(
index
,
1
)
}
// 地图数据 - 保存
const
saveMapData
=
async
()
=>
{
try
{
const
res
=
await
HomeDashboardMockApi
.
getHomeDashboardMockPage
({
pageNo
:
1
,
pageSize
:
100
})
if
(
res
&&
res
.
list
)
{
const
config
=
res
.
list
.
find
(
item
=>
item
.
configKey
===
'mock_map_data'
)
const
data
=
mapDataList
.
value
if
(
config
)
{
const
updateData
=
{
...
config
,
configValue
:
JSON
.
stringify
(
data
)
}
await
HomeDashboardMockApi
.
updateHomeDashboardMock
(
updateData
)
}
else
{
const
createData
=
{
configKey
:
'mock_map_data'
,
configType
:
'data'
,
configValue
:
JSON
.
stringify
(
data
),
description
:
'地图数据模拟数据'
}
await
HomeDashboardMockApi
.
createHomeDashboardMock
(
createData
)
}
message
.
success
(
'保存成功'
)
}
}
catch
(
error
)
{
console
.
error
(
'保存失败'
,
error
)
message
.
error
(
'保存失败'
)
}
}
// 地图数据 - 添加数据项
const
addMapDataItem
=
()
=>
{
mapDataList
.
value
.
push
({
name
:
''
,
value
:
0
})
}
// 地图数据 - 删除数据项
const
removeMapDataItem
=
(
index
:
number
)
=>
{
mapDataList
.
value
.
splice
(
index
,
1
)
}
onMounted
(()
=>
{
loadAllMockData
()
})
</
script
>
<
style
scoped
lang=
"scss"
>
.mock-config-page
{
.config-card
{
margin-bottom
:
20px
;
.card-header
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
.title
{
font-size
:
16px
;
font-weight
:
600
;
flex
:
1
;
}
}
}
.switch-section
{
display
:
flex
;
align-items
:
center
;
}
.config-form
{
max-width
:
800px
;
}
.table-section
{
.data-table
{
width
:
100%
;
border-collapse
:
collapse
;
margin-top
:
10px
;
th,
td
{
padding
:
12px
8px
;
text-align
:
center
;
border
:
1px
solid
#ebeef5
;
}
th
{
background-color
:
#f5f7fa
;
font-weight
:
600
;
color
:
#606266
;
}
td
{
:deep(.el-input),
:deep(.el-input-number),
:deep(.el-date-picker)
{
width
:
100%
;
}
}
}
}
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment