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 @@
</
template
>
<
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
{
TooltipComponent
,
VisualMapComponent
,
GeoComponent
,
GridComponent
,
LegendComponent
}
from
'echarts/components'
import
{
...
...
@@ -18,6 +18,9 @@ import geoJson from '@/assets/mapJson/china.json'
import
labelGreen
from
'@/assets/images/label-green.png'
import
labelRed
from
'@/assets/images/label-red.png'
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
echarts
.
use
([
GridComponent
,
LegendComponent
,
...
...
@@ -33,14 +36,40 @@ echarts.use([
])
const
geoCoordMap
=
ref
({
浙江省
:
[
120.153576
,
30.287459
],
上海市
:
[
121.472644
,
31.231706
],
江苏省
:
[
87.617733
,
43.792818
],
辽宁省
:
[
123.429096
,
41.796767
],
湖南省
:
[
112.982279
,
28.19409
],
北京市
:
[
116.403874
,
39.914885
],
天津市
:
[
117.190182
,
39.125523
],
河北省
:
[
114.502462
,
38.045494
],
山西省
:
[
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
],
福建省
:
[
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
([
...
...
@@ -62,28 +91,7 @@ const mapData = 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
mapData2
=
ref
([])
const
convertData
=
function
(
data
)
{
const
res
=
[]
...
...
@@ -174,7 +182,7 @@ function initMapChart (el) {
label
:
{
show
:
true
,
position
:
[
-
25
,
-
80
],
width
:
36
0
,
width
:
42
0
,
height
:
72
,
color
:
'#FFFFFF'
,
backgroundColor
:
{
image
:
labelGreen
},
...
...
@@ -218,8 +226,8 @@ function initMapChart (el) {
symbolSize
:
0
,
label
:
{
show
:
true
,
position
:
[
-
25
,
-
80
],
width
:
36
0
,
position
:
[
-
40
,
-
80
],
width
:
50
0
,
height
:
72
,
color
:
'#FFFFFF'
,
backgroundColor
:
{
image
:
labelRed
},
...
...
@@ -228,12 +236,12 @@ function initMapChart (el) {
rich
:
{
txt
:
{
align
:
'left'
,
padding
:
[
40
,
0
,
0
,
130
],
padding
:
[
40
,
6
0
,
0
,
130
],
fontSize
:
22
},
txt2
:
{
fontSize
:
36
,
padding
:
[
38
,
0
,
0
,
10
]
padding
:
[
38
,
2
0
,
0
,
10
]
},
txt3
:
{
fontSize
:
24
,
...
...
@@ -246,7 +254,7 @@ function initMapChart (el) {
data
}
=
params
if
(
data
&&
data
.
value
)
{
return
`{txt|
${
' 算力资源'
}
}{txt2|
${
data
.
value
[
2
]}
}{txt3|
${
'P'
}
}`
return
`{txt|
${
' 算力资源'
}
}{txt2|
${
data
.
value
[
2
]}
}{txt3|
${
'P
TOPS
'
}
}`
}
return
'data'
}
...
...
@@ -270,6 +278,8 @@ const resize = () => {
onMounted
(
async
()
=>
{
await
nextTick
()
// 从 dashboardData 初始化地图数据
mapData2
.
value
=
dashboardData
.
value
.
mapData
||
[]
if
(
mapRef
.
value
)
{
chartInst
=
initMapChart
(
mapRef
.
value
)
window
.
addEventListener
(
'resize'
,
resize
)
...
...
@@ -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
(()
=>
{
window
.
removeEventListener
(
'resize'
,
resize
)
document
.
removeEventListener
(
'fullscreenchange'
,
resize
)
...
...
src/views/Home/ComputeResource.vue
View file @
6eb1ff55
...
...
@@ -10,7 +10,7 @@
</
template
>
<
script
setup
>
import
{
ref
,
watch
,
onMounted
,
onBeforeUnmount
}
from
'vue'
import
{
ref
,
watch
,
onMounted
,
onBeforeUnmount
,
inject
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
{
TitleComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
PieChart
}
from
'echarts/charts'
...
...
@@ -23,22 +23,21 @@ let chart = null
const
dim
=
ref
(
'gpu'
)
const
datasets
=
{
gpu
:
[
{
value
:
45.11
,
name
:
'4090'
},
{
value
:
29.88
,
name
:
'A100'
},
{
value
:
25
,
name
:
'H100'
}
],
source
:
[
{
value
:
50
,
name
:
'自有'
},
{
value
:
30
,
name
:
'合作'
},
{
value
:
20
,
name
:
'社会'
}
],
resource
:
[
{
value
:
40
,
name
:
'裸金属'
},
{
value
:
35
,
name
:
'VM'
},
{
value
:
25
,
name
:
'容器'
}
]
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
const
datasets
=
ref
({
gpu
:
[],
source
:
[],
resource
:
[]
})
// 获取算力资源分布数据
const
fetchComputeDistribution
=
()
=>
{
const
data
=
dashboardData
.
value
.
computeDistribution
||
{
gpu
:
[],
source
:
[],
resource
:
[]
}
datasets
.
value
.
gpu
=
data
.
gpu
||
[]
datasets
.
value
.
source
=
data
.
source
||
[]
datasets
.
value
.
resource
=
data
.
resource
||
[]
}
const
titles
=
{
...
...
@@ -50,7 +49,7 @@ const titles = {
const
baseColors
=
[
'#16FCFF'
,
'#39E9D5'
,
'#1DBAFF'
,
'#77CCFF'
,
'#E99102'
,
'#3AEDCE'
]
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
dataMap
=
data
.
reduce
((
acc
,
i
)
=>
((
acc
[
i
.
name
]
=
Number
(
i
.
value
||
0
)),
acc
),
{})
return
{
...
...
@@ -139,10 +138,19 @@ watch(dim, () => {
})
onMounted
(()
=>
{
fetchComputeDistribution
()
render
()
window
.
addEventListener
(
'resize'
,
resize
)
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
computeDistribution
,
()
=>
{
fetchComputeDistribution
()
if
(
chart
)
{
chart
.
setOption
(
getOption
(
dim
.
value
),
true
)
}
},
{
deep
:
true
})
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
resize
)
if
(
chart
)
{
...
...
src/views/Home/Home.vue
View file @
6eb1ff55
...
...
@@ -13,20 +13,21 @@
<div
class=
"header-title"
>
平台总体态势
</div>
<div
class=
"cumulative-delivery"
>
<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"
>
432.58
P
</span>
{{
dashboardData
.
overallSituation
.
allCompute
}}
P
TOPS
</span>
</div>
<div
class=
"statistical"
>
<div
class=
"statistical-item"
>
<i></i>
<div>
<div
class=
"label"
>
已租赁算力(P)
</div>
<div
class=
"label"
>
已租赁算力(P
TOPS
)
</div>
<div
class=
"value"
>
139.94
<!--
<animation-count
:end-val=
"53632"
decimals
:range-min=
"0.01"
:range-max=
"0.02"
/>
-->
{{
dashboardData
.
overallSituation
.
leaseCompute
}}
PTOPS
<!-- 139.94
<animation-count
:end-val=
"53632"
decimals
:range-min=
"0.01"
:range-max=
"0.02"
/>
-->
</div>
</div>
</div>
...
...
@@ -34,7 +35,8 @@
<i></i>
<div>
<div
class=
"label"
>
算力利用率
</div>
<div
class=
"value"
>
42.37%
</div>
<div
class=
"value"
>
{{
dashboardData
.
overallSituation
.
computeUtilizationRate
}}
%
</div>
<!-- 42.37%-->
</div>
</div>
<div
class=
"statistical-item"
>
...
...
@@ -42,8 +44,9 @@
<div>
<div
class=
"label"
>
运行中任务数
</div>
<div
class=
"value"
>
29
<!--
<animation-count
:end-val=
"0.83"
decimals
:range-min=
"40"
:range-max=
"42"
/>
-->
{{
dashboardData
.
overallSituation
.
runningTaskCount
}}
<!-- 29
<animation-count
:end-val=
"0.83"
decimals
:range-min=
"40"
:range-max=
"42"
/>
-->
</div>
</div>
</div>
...
...
@@ -63,106 +66,39 @@
<div
class=
"center"
>
<el-carousel
indicator-position=
"none"
arrow=
"never"
>
<el-carousel-item>
<button
class=
"year-button"
type=
"button"
>
2025-08
</button>
<el-carousel-item
v-for=
"(item, index) in dashboardData.carouselItems"
:key=
"index"
>
<button
class=
"year-button"
type=
"button"
>
{{
item
.
yearMonth
}}
</button>
<div
class=
"statistical"
>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon1.png"
/>
<div>
<div
class=
"label"
>
上线应用数
</div>
<div
class=
"value"
>
4
</div>
<div
class=
"value"
>
{{
item
.
appOnlineCount
}}
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon2.png"
/>
<div>
<div
class=
"label"
>
可用API
</div>
<div
class=
"value"
>
14
个
</div>
<div
class=
"value"
>
{{
item
.
apiOnline
}}
个
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon3.png"
/>
<div>
<div
class=
"label"
>
模型服务在线率
</div>
<div
class=
"value"
>
12.47
%
</div>
<div
class=
"value"
>
{{
item
.
modelOnlineRate
}}
%
</div>
</div>
</div>
<div
class=
"statistical-item"
>
<img
src=
"@/assets/images/statistical-icon4.png"
/>
<div>
<div
class=
"label"
>
Api调用次数
</div>
<div
class=
"value"
>
1202
次
</div>
<div
class=
"value"
>
{{
item
.
apiCallTotal
}}
次
</div>
</div>
</div>
</div>
</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>
<ChinaMap
v-if=
"showMap === 'china'"
:key=
"'china'"
/>
...
...
@@ -217,6 +153,113 @@ import ChinaMap from './ChinaMap'
import
{
onBeforeUnmount
,
onMounted
,
ref
,
computed
,
provide
,
watch
}
from
'vue'
import
{
useRoute
}
from
'vue-router'
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'
...
...
@@ -359,6 +402,8 @@ onMounted(() => {
document
.
addEventListener
(
'mozfullscreenchange'
,
handleFsChange
)
// @ts-ignore
document
.
addEventListener
(
'MSFullscreenChange'
,
handleFsChange
)
// 获取所有模拟数据
fetchAllMockData
()
})
onBeforeUnmount
(()
=>
{
...
...
src/views/Home/Nvestment.vue
View file @
6eb1ff55
...
...
@@ -2,7 +2,7 @@
<div
id=
"nvestment"
class=
"echart-wrap"
></div>
</
template
>
<
script
setup
>
import
{
onMounted
}
from
'vue'
import
{
onMounted
,
inject
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
{
TitleComponent
,
...
...
@@ -27,9 +27,23 @@ echarts.use([
UniversalTransition
])
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
let
myChart
=
null
function
init
()
{
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
=
{
tooltip
:
{
trigger
:
'axis'
,
...
...
@@ -41,7 +55,7 @@ function init () {
}
},
legend
:
{
data
:
[
'
水能消耗'
,
'电能消耗
'
],
data
:
[
'
上线应用'
,
'上线API
'
],
show
:
false
,
right
:
'2%'
,
top
:
30
,
...
...
@@ -63,7 +77,7 @@ function init () {
{
type
:
'category'
,
boundaryGap
:
true
,
data
:
[
'2018'
,
'2019'
,
'2020'
,
'2021'
,
'2022'
]
,
data
:
years
,
axisLabel
:
{
fontSize
:
24
,
color
:
'#ffffff'
...
...
@@ -133,7 +147,7 @@ function init () {
}
])
},
data
:
[
10.19
,
189.61
,
120.4
,
75.14
,
''
]
data
:
appOnline
},
{
name
:
'上线API'
,
...
...
@@ -150,7 +164,7 @@ function init () {
emphasis
:
{
// focus: 'series'
},
data
:
[
23.22
,
12027.48
,
75935.47
,
195109.5381
,
''
]
data
:
apiOnline
}
]
}
...
...
@@ -160,6 +174,11 @@ function init () {
onMounted
(()
=>
{
init
()
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
serviceCapability
,
()
=>
{
init
()
},
{
deep
:
true
})
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
src/views/Home/apiEchart.vue
View file @
6eb1ff55
...
...
@@ -29,7 +29,7 @@
</
template
>
<
script
setup
>
import
{
ref
,
onMounted
,
onBeforeUnmount
}
from
'vue'
import
{
ref
,
onMounted
,
onBeforeUnmount
,
inject
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts/core'
import
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
LineChart
}
from
'echarts/charts'
...
...
@@ -43,78 +43,99 @@ let chart = null
// 维度切换:d=日, m=月, y=年
const
rangeType
=
ref
(
'm'
)
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
// 嵌入 iframe 和全屏自适应
const
inIframe
=
ref
(
false
)
const
isFullscreen
=
ref
(
false
)
const
outerPadding
=
ref
(
'0px'
)
// 模拟接口:返回近 N 个月的请求量
function
mockFetchApiTrend
(
months
=
6
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
now
=
new
Date
()
const
items
=
[]
for
(
let
i
=
months
-
1
;
i
>=
0
;
i
--
)
{
const
d
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
()
-
i
,
1
)
const
y
=
d
.
getFullYear
()
const
m
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
// 构造一个看起来合理的请求数:基础值 + 波动
const
base
=
20000
+
(
i
*
1500
)
const
noise
=
Math
.
floor
(
Math
.
random
()
*
4000
)
-
2000
// ±2000 波动
const
value
=
Math
.
max
(
0
,
base
+
noise
)
items
.
push
({
month
:
`
${
y
}
-
${
m
}
`
,
requests
:
value
})
}
resolve
({
code
:
0
,
data
:
items
})
},
500
)
})
}
// 原始生成模拟数据的方式(备份)
// function mockFetchApiTrend(months = 6) {
// return new Promise((resolve) => {
// setTimeout(() => {
// const now = new Date()
// const items = []
// for (let i = months - 1; i >= 0; i--) {
// const d = new Date(now.getFullYear(), now.getMonth() - i, 1)
// const y = d.getFullYear()
// const m = String(d.getMonth() + 1).padStart(2, '0')
// const base = 20000 + (i * 1500)
// const noise = Math.floor(Math.random() * 4000) - 2000
// const value = Math.max(0, base + noise)
// items.push({ month: `${y}-${m}`, requests: value })
// }
// resolve({ code: 0, data: items })
// }, 500)
// })
// }
// 生成不同维度的数据
function
genDaySeries
(
days
=
7
)
{
const
now
=
new
Date
()
const
x
=
[]
const
y
=
[]
for
(
let
i
=
days
-
1
;
i
>=
0
;
i
--
)
{
const
d
=
new
Date
(
now
)
d
.
setDate
(
now
.
getDate
()
-
i
)
const
mm
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
const
dd
=
String
(
d
.
getDate
()).
padStart
(
2
,
'0'
)
x
.
push
(
`
${
mm
}
-
${
dd
}
`
)
// 日数据:较小基数 + 波动
const
base
=
800
+
(
days
-
i
)
*
30
const
noise
=
Math
.
floor
(
Math
.
random
()
*
200
)
-
100
y
.
push
(
Math
.
max
(
0
,
base
+
noise
))
}
return
{
x
,
y
}
}
// 生成不同维度的数据(备份)
// function genDaySeries(days = 7) {
// const now = new Date()
// const x = []
// const y = []
// for (let i = days - 1; i >= 0; i--) {
// const d = new Date(now)
// d.setDate(now.getDate() - i)
// const mm = String(d.getMonth() + 1).padStart(2, '0')
// const dd = String(d.getDate()).padStart(2, '0')
// x.push(`${mm}-${dd}`)
// const base = 800 + (days - i) * 30
// const noise = Math.floor(Math.random() * 200) - 100
// y.push(Math.max(0, base + noise))
// }
// return { x, y }
// }
function
genMonthSeries
(
months
=
12
)
{
const
now
=
new
Date
()
const
x
=
[]
const
y
=
[]
for
(
let
i
=
months
-
1
;
i
>=
0
;
i
--
)
{
const
d
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
()
-
i
,
1
)
const
m
=
d
.
getMonth
()
+
1
x
.
push
(
`
${
m
}
月`
)
const
base
=
20000
+
(
months
-
i
)
*
1500
const
noise
=
Math
.
floor
(
Math
.
random
()
*
4000
)
-
2000
y
.
push
(
Math
.
max
(
0
,
base
+
noise
))
}
return
{
x
,
y
}
}
// function genMonthSeries(months = 12) {
// const now = new Date()
// const x = []
// const y = []
// for (let i = months - 1; i >= 0; i--) {
// const d = new Date(now.getFullYear(), now.getMonth() - i, 1)
// const m = d.getMonth() + 1
// x.push(`${m}月`)
// const base = 20000 + (months - i) * 1500
// const noise = Math.floor(Math.random() * 4000) - 2000
// y.push(Math.max(0, base + noise))
// }
// 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
)
{
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
))
// 从 dashboardData 获取图表数据
const
getChartData
=
()
=>
{
const
data
=
dashboardData
.
value
.
apiCalls
||
[]
if
(
!
Array
.
isArray
(
data
))
{
return
{
x
:
[],
y
:
[]
}
}
// 根据数据格式转换为图表数据
// 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
}
}
...
...
@@ -173,16 +194,7 @@ function getOption(x, y) {
}
async
function
render
()
{
let
x
=
[]
let
y
=
[]
// 按维度生成数据
if
(
rangeType
.
value
===
'd'
)
{
;({
x
,
y
}
=
genDaySeries
(
7
))
}
else
if
(
rangeType
.
value
===
'm'
)
{
;({
x
,
y
}
=
genMonthSeries
(
12
))
}
else
{
;({
x
,
y
}
=
genYearSeries
(
5
))
}
const
{
x
,
y
}
=
getChartData
()
if
(
!
chart
)
chart
=
echarts
.
init
(
chartRef
.
value
)
chart
.
setOption
(
getOption
(
x
,
y
),
true
)
...
...
@@ -237,6 +249,11 @@ onMounted(() => {
document
.
addEventListener
(
'MSFullscreenChange'
,
onFsChange
)
})
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
apiCalls
,
()
=>
{
render
()
},
{
deep
:
true
})
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
onResize
)
document
.
removeEventListener
(
'fullscreenchange'
,
onFsChange
)
...
...
src/views/Home/orderManage.vue
View file @
6eb1ff55
...
...
@@ -22,17 +22,19 @@
</
template
>
<
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
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
LineChart
}
from
'echarts/charts'
import
{
CanvasRenderer
}
from
'echarts/renderers'
import
{
useI18n
}
from
'vue-i18n'
import
type
{
EChartsOption
}
from
'echarts'
import
*
as
IndexCountApi
from
'@/api/Home/count'
echarts
.
use
([
GridComponent
,
TooltipComponent
,
LegendComponent
,
LineChart
,
CanvasRenderer
])
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
// 从父组件(Home.vue)注入全屏状态与方法
const
fs
=
inject
(
'fsState'
,
null
)
as
null
|
{
isFullscreen
:
any
;
toggleFullscreen
:
()
=>
void
}
const
fsIsFullscreen
=
computed
(()
=>
!!
(
fs
&&
fs
.
isFullscreen
&&
fs
.
isFullscreen
.
value
))
...
...
@@ -138,12 +140,14 @@ function getOption(xData: string[], seriesData: {
}
async
function
render
()
{
const
res
=
await
IndexCountApi
.
getOrdersData
(
dateType
.
value
)
const
x
=
(
res
||
[]).
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
computeCount
=
(
res
||
[]).
map
((
item
:
any
)
=>
item
.
computeOrdersCount
||
0
)
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
)))
const
orders
=
dashboardData
.
value
.
orders
||
{}
const
res
=
orders
[
dateType
.
value
]
||
[]
const
x
=
res
.
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
computeCount
=
res
.
map
((
item
:
any
)
=>
item
.
computeOrdersCount
||
0
)
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
)
{
const
el
=
document
.
getElementById
(
'energyManage'
)
as
HTMLElement
|
null
...
...
@@ -161,6 +165,11 @@ function changeType(t: 'd' | 'm' | 'y') {
const
onResize
=
()
=>
chart
&&
chart
.
resize
()
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
orders
,
()
=>
{
render
()
},
{
deep
:
true
})
onMounted
(()
=>
{
render
()
window
.
addEventListener
(
'resize'
,
onResize
)
...
...
src/views/Home/userManage.vue
View file @
6eb1ff55
...
...
@@ -12,17 +12,19 @@
</
template
>
<
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
{
GridComponent
,
TooltipComponent
,
LegendComponent
}
from
'echarts/components'
import
{
LineChart
}
from
'echarts/charts'
import
{
CanvasRenderer
}
from
'echarts/renderers'
import
{
useI18n
}
from
'vue-i18n'
import
type
{
EChartsOption
}
from
'echarts'
import
*
as
IndexCountApi
from
'@/api/Home/count'
echarts
.
use
([
GridComponent
,
TooltipComponent
,
LegendComponent
,
LineChart
,
CanvasRenderer
])
// 从父组件获取统一数据
const
dashboardData
=
inject
(
'dashboardData'
,
{})
// 从父组件(Home.vue)注入全屏状态与方法
const
fs
=
inject
(
'fsState'
,
null
)
as
null
|
{
isFullscreen
:
any
;
toggleFullscreen
:
()
=>
void
}
const
fsIsFullscreen
=
computed
(()
=>
!!
(
fs
&&
fs
.
isFullscreen
&&
fs
.
isFullscreen
.
value
))
...
...
@@ -88,8 +90,9 @@ function getOption(xData: string[], growth: number[], active: number[]): ECharts
}
async
function
render
()
{
const
res
=
await
IndexCountApi
.
getUsersData
(
dateType
.
value
)
const
arr
=
Array
.
isArray
(
res
)
?
res
:
[]
// 直接从 dashboardData 获取数据
const
arr
=
dashboardData
.
value
.
users
||
[]
const
x
=
arr
.
map
((
item
:
any
)
=>
t
(
item
.
countDate
))
const
growth
=
arr
.
map
((
item
:
any
)
=>
item
.
growthUsersCount
??
item
.
usersCount
??
0
)
const
active
=
arr
.
map
((
item
:
any
,
idx
:
number
)
=>
...
...
@@ -112,6 +115,11 @@ function changeType(t: 'd' | 'm' | 'y') {
const
onResize
=
()
=>
chart
&&
chart
.
resize
()
// 监听数据变化
watch
(()
=>
dashboardData
.
value
.
users
,
()
=>
{
render
()
},
{
deep
:
true
})
onMounted
(()
=>
{
render
()
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