Commit 6eb1ff55 by Jony.L

大屏页面和后台管理调整1.0

parent 71ffe7d8
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
...@@ -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: 360, width: 420,
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: 360, width: 500,
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, 60, 0, 130],
fontSize: 22 fontSize: 22
}, },
txt2: { txt2: {
fontSize: 36, fontSize: 36,
padding: [38, 0, 0, 10] padding: [38, 20, 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|${'PTOPS'}}`
} }
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)
......
...@@ -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) {
......
...@@ -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">
......
...@@ -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)
......
...@@ -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)
......
...@@ -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)
......
<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>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment