fix: 优化运维管理仪表盘展示

This commit is contained in:
XieYongHong 2023-07-19 18:16:18 +08:00
parent 1d238367be
commit 2feafcb017
9 changed files with 387 additions and 194 deletions

View File

@ -5,7 +5,7 @@ import { cloneDeep, isArray } from 'lodash-es'
import { usePermissionStore } from './permission' import { usePermissionStore } from './permission'
import router from '@/router' import router from '@/router'
import { onlyMessage } from '@/utils/comm' import { onlyMessage } from '@/utils/comm'
// import { AccountMenu, NotificationRecordCode, NotificationSubscriptionCode } from '@/router/menu' import { AccountMenu, NotificationRecordCode, NotificationSubscriptionCode } from '@/router/menu'
import { USER_CENTER_MENU_CODE } from '@/utils/consts' import { USER_CENTER_MENU_CODE } from '@/utils/consts'
import {isNoCommunity} from "@/utils/utils"; import {isNoCommunity} from "@/utils/utils";
@ -109,6 +109,7 @@ export const useMenuStore = defineStore({
permission.permissions = {} permission.permissions = {}
const { menusData, silderMenus } = filterAsyncRouter(resultData) const { menusData, silderMenus } = filterAsyncRouter(resultData)
this.menus = findCodeRoute([...resultData]) // AccountMenu this.menus = findCodeRoute([...resultData]) // AccountMenu
Object.keys(this.menus).forEach((item) => { Object.keys(this.menus).forEach((item) => {
const _item = this.menus[item] const _item = this.menus[item]
@ -125,7 +126,7 @@ export const useMenuStore = defineStore({
} }
}) })
// menusData.push(AccountMenu) // menusData.push(AccountMenu)
this.siderMenus = silderMenus.filter((item: { name: string }) => ![USER_CENTER_MENU_CODE].includes(item.name)) this.siderMenus = silderMenus.filter((item: { name: string }) => ![USER_CENTER_MENU_CODE, NotificationRecordCode, NotificationSubscriptionCode].includes(item.name))
res(menusData) res(menusData)
} }
}) })

View File

@ -38,18 +38,26 @@
v-if="isEmpty" v-if="isEmpty"
style="height: 200px; margin-top: 100px" style="height: 200px; margin-top: 100px"
/> />
<div <template v-else>
v-else <div style="height: 300px">
ref="chartRef" <Echarts
style="width: 100%; height: 300px" :options="echartsOptions"
></div> />
</div>
<ServerList
v-if="serverOptions.length > 1"
v-model:value="serverActive"
:options="serverOptions"
color="#2CB6E0"
/>
</template>
</div> </div>
</div> </div>
</j-spin> </j-spin>
</template> </template>
m m
<script lang="ts" setup name="Cpu"> <script lang="ts" setup name="Cpu">
import * as echarts from 'echarts';
import { dashboard } from '@/api/link/dashboard'; import { dashboard } from '@/api/link/dashboard';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { import {
@ -60,7 +68,8 @@ import {
typeDataLine, typeDataLine,
} from './tool.ts'; } from './tool.ts';
import { DataType } from '../typings'; import { DataType } from '../typings';
import ServerList from './ServerList.vue'
import Echarts from './echarts.vue'
const props = defineProps({ const props = defineProps({
serviceId: { serviceId: {
@ -80,10 +89,53 @@ const data = ref<DataType>({
time: [null, null], time: [null, null],
}); });
const isEmpty = ref(false); const isEmpty = ref(false);
const serverActive = ref<string[]>([])
const serverOptions = ref<string[]>([])
const serverData = reactive({
xAxis: [],
data: []
})
const pickerTimeChange = () => { const pickerTimeChange = () => {
data.value.type = undefined; data.value.type = undefined;
}; };
const echartsOptions = computed(() => {
const series = serverActive.value.length
? serverActive.value.map((key) => setOptions(serverData.data, key))
: typeDataLine
return {
xAxis: {
type: 'category',
boundaryGap: false,
data: arrayReverse(serverData.xAxis),
},
tooltip: {
trigger: 'axis',
valueFormatter: (value: any) => `${value}%`,
},
yAxis: {
type: 'value',
},
grid: {
left: '50px',
right: '50px',
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
},
{
start: 0,
end: 100,
},
],
color: ['#2CB6E0'],
series: series
};
})
const getCPUEcharts = async (val: any) => { const getCPUEcharts = async (val: any) => {
loading.value = true; loading.value = true;
const res: any = await dashboard(defulteParamsData('cpu', val)); const res: any = await dashboard(defulteParamsData('cpu', val));
@ -129,50 +181,12 @@ const setOptions = (optionsData: any, key: string) => ({
}); });
const handleCpuOptions = (optionsData: any, xAxis: any) => { const handleCpuOptions = (optionsData: any, xAxis: any) => {
if (optionsData.length === 0 && xAxis.length === 0) return;
const chart: any = chartRef.value;
if (chart) {
const myChart = echarts.init(chart);
const dataKeys = Object.keys(optionsData); const dataKeys = Object.keys(optionsData);
const options = { serverActive.value = dataKeys
xAxis: { serverOptions.value = dataKeys
type: 'category', serverData.xAxis = xAxis
boundaryGap: false, serverData.data = optionsData
data: arrayReverse(xAxis), }
},
tooltip: {
trigger: 'axis',
valueFormatter: (value: any) => `${value}%`,
},
yAxis: {
type: 'value',
},
grid: {
left: '50px',
right: '50px',
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
},
{
start: 0,
end: 100,
},
],
color: ['#2CB6E0'],
series: dataKeys.length
? dataKeys.map((key) => setOptions(optionsData, key))
: typeDataLine,
};
myChart.setOption(options);
window.addEventListener('resize', function () {
myChart.resize();
});
}
};
watch( watch(
() => data.value.type, () => data.value.type,

View File

@ -38,18 +38,26 @@
v-if="isEmpty" v-if="isEmpty"
style="height: 200px; margin-top: 100px" style="height: 200px; margin-top: 100px"
/> />
<div <template v-else>
v-else <div style="height: 300px">
ref="chartRef" <Echarts
style="width: 100%; height: 300px" :options="echartsOptions"
></div> />
</div>
<ServerList
v-if="serverOptions.length > 1"
v-model:value="serverActive"
:options="serverOptions"
color="#60DFC7"
/>
</template>
</div> </div>
</div> </div>
</j-spin> </j-spin>
</template> </template>
<script lang="ts" setup name="Jvm"> <script lang="ts" setup name="Jvm">
import * as echarts from 'echarts';
import { dashboard } from '@/api/link/dashboard'; import { dashboard } from '@/api/link/dashboard';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { import {
@ -60,6 +68,8 @@ import {
defulteParamsData, defulteParamsData,
} from './tool.ts'; } from './tool.ts';
import { DataType } from '../typings'; import { DataType } from '../typings';
import ServerList from './ServerList.vue'
import Echarts from './echarts.vue'
const props = defineProps({ const props = defineProps({
serviceId: { serviceId: {
@ -79,6 +89,13 @@ const data = ref<DataType>({
time: [null, null], time: [null, null],
}); });
const isEmpty = ref(false); const isEmpty = ref(false);
const serverActive = ref<string[]>([])
const serverOptions = ref<string[]>([])
const serverData = reactive({
xAxis: [],
data: []
})
const pickerTimeChange = () => { const pickerTimeChange = () => {
data.value.type = undefined; data.value.type = undefined;
}; };
@ -131,16 +148,22 @@ const setOptions = (optionsData: any, key: string) => ({
areaStyle: areaStyleJvm, areaStyle: areaStyleJvm,
}); });
const handleJVMOptions = (optionsData: any, xAxis: any) => { const handleJVMOptions = (optionsData: any, xAxis: any) => {
if (optionsData.length === 0 && xAxis.length === 0) return;
const chart: any = chartRef.value;
if (chart) {
const myChart = echarts.init(chart);
const dataKeys = Object.keys(optionsData); const dataKeys = Object.keys(optionsData);
const options = { serverActive.value = dataKeys
serverOptions.value = dataKeys
serverData.xAxis = xAxis
serverData.data = optionsData
};
const echartsOptions = computed(() => {
const series = serverActive.value.length
? serverActive.value.map((key) => setOptions(serverData.data, key))
: typeDataLine
return {
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: arrayReverse(xAxis), data: arrayReverse(serverData.xAxis),
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@ -165,16 +188,9 @@ const handleJVMOptions = (optionsData: any, xAxis: any) => {
}, },
], ],
color: ['#60DFC7'], color: ['#60DFC7'],
series: dataKeys.length series: series
? dataKeys.map((key) => setOptions(optionsData, key))
: typeDataLine,
};
myChart.setOption(options);
window.addEventListener('resize', function () {
myChart.resize();
});
} }
}; })
watch( watch(
() => data.value.type, () => data.value.type,

View File

@ -26,7 +26,7 @@
<j-radio-button value="hour"> <j-radio-button value="hour">
最近1小时 最近1小时
</j-radio-button> </j-radio-button>
<j-radio-button value="today"> 今日 </j-radio-button> <j-radio-button value="day"> 近24小时 </j-radio-button>
<j-radio-button value="week"> 近一周 </j-radio-button> <j-radio-button value="week"> 近一周 </j-radio-button>
</j-radio-group> </j-radio-group>
<j-range-picker <j-range-picker
@ -47,11 +47,20 @@
v-if="isEmpty" v-if="isEmpty"
style="height: 250px; margin-top: 100px" style="height: 250px; margin-top: 100px"
/> />
<div <template v-else>
v-else <div style="height: 300px">
ref="chartRef" <Echarts
style="width: 100%; height: 350px" :options="echartsOptions"
></div> />
</div>
<ServerList
v-if="serverOptions.length > 1"
v-model:value="serverActive"
:options="serverOptions"
color="#979AFF"
/>
</template>
</div> </div>
</div> </div>
</j-spin> </j-spin>
@ -63,11 +72,12 @@ import {
getTimeByType, getTimeByType,
typeDataLine, typeDataLine,
areaStyle, areaStyle,
networkParams, networkParams, arrayReverse,
} from './tool.ts'; } from './tool.ts';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import * as echarts from 'echarts';
import { DataType } from '../typings.d'; import { DataType } from '../typings.d';
import ServerList from './ServerList.vue'
import Echarts from './echarts.vue'
const props = defineProps({ const props = defineProps({
serviceId: { serviceId: {
@ -86,6 +96,13 @@ const data = ref<DataType>({
}, },
}); });
const isEmpty = ref(false); const isEmpty = ref(false);
const serverActive = ref<string[]>([])
const serverOptions = ref<string[]>([])
const serverData = reactive({
xAxis: [],
data: []
})
const pickerTimeChange = (value: any) => { const pickerTimeChange = (value: any) => {
data.value.time.type = undefined; data.value.time.type = undefined;
}; };
@ -97,7 +114,8 @@ const getNetworkEcharts = async (val: any) => {
const _networkOptions = {}; const _networkOptions = {};
const _networkXAxis = new Set(); const _networkXAxis = new Set();
if (resp.result.length) { if (resp.result.length) {
const filterArray = resp.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId) const filterArray = resp.result
// const filterArray = resp.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId)
filterArray.forEach((item: any) => { filterArray.forEach((item: any) => {
const value = item.data.value; const value = item.data.value;
const _data: Array<any> = []; const _data: Array<any> = [];
@ -151,16 +169,22 @@ const setOptions = (data: any, key: string) => ({
}); });
const handleNetworkOptions = (optionsData: any, xAxis: any) => { const handleNetworkOptions = (optionsData: any, xAxis: any) => {
if (optionsData.length === 0 && xAxis.length === 0) return;
const chart: any = chartRef.value;
if (chart) {
const myChart = echarts.init(chart);
const dataKeys = Object.keys(optionsData); const dataKeys = Object.keys(optionsData);
const options = { serverActive.value = dataKeys
serverOptions.value = dataKeys
serverData.xAxis = xAxis
serverData.data = optionsData
};
const echartsOptions = computed(() => {
const series = serverActive.value.length
? serverActive.value.map((key) => setOptions(serverData.data, key))
: typeDataLine
return {
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: xAxis, data: serverData.xAxis,
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
@ -179,16 +203,9 @@ const handleNetworkOptions = (optionsData: any, xAxis: any) => {
formatter: (_value: any) => networkValueRender(_value[0]), formatter: (_value: any) => networkValueRender(_value[0]),
}, },
color: ['#979AFF'], color: ['#979AFF'],
series: dataKeys.length series: series
? dataKeys.map((key) => setOptions(optionsData, key))
: typeDataLine,
}; };
myChart.setOption(options); })
window.addEventListener('resize', function () {
myChart.resize();
});
}
};
watch( watch(
() => data.value.time.type, () => data.value.time.type,
@ -203,6 +220,7 @@ watch(
watchEffect(() => { watchEffect(() => {
const time = data.value.time.time const time = data.value.time.time
if (time && Array.isArray(time) && time.length === 2 && time[0] && props.serviceId) { if (time && Array.isArray(time) && time.length === 2 && time[0] && props.serviceId) {
console.log(data.value)
getNetworkEcharts(data.value); getNetworkEcharts(data.value);
} }
}) })

View File

@ -0,0 +1,79 @@
<template>
<div class="server-list-warp">
<j-scrollbar>
<div class="server-list-items">
<div
v-for="item in options" :class="['server-item', myValue.includes(item) ? 'active' : '']"
@click="() => change(item)"
>
<j-badge :color=" myValue.includes(item) ? color : '#a3a3a3'" :text="item" />
</div>
</div>
</j-scrollbar>
</div>
</template>
<script lang="ts" name="ServerList" setup>
const props = defineProps({
options: {
type: Array,
default: () => []
},
value: {
type: Array,
default: () => []
},
color: {
type: String,
default: '#979AFF'
}
})
const emit = defineEmits(['update:value'])
const myValue = ref<any[]>([])
const change = (id: string) => {
const ids = new Set<string>(myValue.value)
console.log(ids)
if (ids.has(id)) {
ids.delete(id)
} else {
ids.add(id)
}
myValue.value = [...ids.values()]
emit('update:value', myValue.value)
}
watch(() => JSON.stringify(props.value), () => {
myValue.value = props.value
}, { immediate: true })
</script>
<style lang="less" scoped>
.server-list-warp {
padding: 0 12px;
margin-top: 12px;
.server-list-items {
display: flex;
gap: 24px;
.server-item {
cursor: pointer;
:deep(.ant-badge-status-text) {
color: #a3a3a3;
transition: color .3s;
}
&.active {
:deep(.ant-badge-status-text) {
color: #000;
}
}
}
}
}
</style>

View File

@ -1,44 +1,48 @@
<template> <template>
<div> <div>
<div class="dash-board">
<j-select <j-select
v-if="serverNodeOptions.length > 1"
:options="serverNodeOptions"
:value="serverId"
style="width: 300px; margin-bottom: 20px" style="width: 300px; margin-bottom: 20px"
@change="serverIdChange" @change="serverIdChange"
:value="serverId"
:options="serverNodeOptions"
v-if="serverNodeOptions.length > 1"
></j-select> ></j-select>
<div class="dash-board"> <div class="dash-board-items">
<div class="dash-board-item"> <div class="dash-board-item">
<TopEchartsItemNode title="CPU使用率" :value="topValues.cpu" /> <TopEchartsItemNode :value="topValues.cpu" title="CPU使用率" />
</div> </div>
<div class="dash-board-item"> <div class="dash-board-item">
<TopEchartsItemNode <TopEchartsItemNode
title="JVM内存"
:max="topValues.jvmTotal"
:bottom="`总JVM内存 ${topValues.jvmTotal}G`" :bottom="`总JVM内存 ${topValues.jvmTotal}G`"
formatter="G" :max="topValues.jvmTotal"
:value="topValues.jvm" :value="topValues.jvm"
formatter="G"
title="JVM内存"
/> />
</div> </div>
<div class="dash-board-item"> <div class="dash-board-item">
<TopEchartsItemNode <TopEchartsItemNode
title="磁盘占用"
:max="topValues.usageTotal"
:bottom="`总磁盘大小 ${topValues.usageTotal}G`" :bottom="`总磁盘大小 ${topValues.usageTotal}G`"
formatter="G" :max="topValues.usageTotal"
:value="topValues.usage" :value="topValues.usage"
formatter="G"
title="磁盘占用"
/> />
</div> </div>
<div class="dash-board-item"> <div class="dash-board-item">
<TopEchartsItemNode <TopEchartsItemNode
title="系统内存"
:max="topValues.systemUsageTotal"
:bottom="`系统内存 ${topValues.systemUsageTotal}G`" :bottom="`系统内存 ${topValues.systemUsageTotal}G`"
formatter="G" :max="topValues.systemUsageTotal"
:value="topValues.systemUsage" :value="topValues.systemUsage"
formatter="G"
title="系统内存"
/> />
</div> </div>
</div> </div>
</div>
</div> </div>
</template> </template>
@ -115,6 +119,7 @@ const getData = () => {
}; };
onMounted(() => { onMounted(() => {
console.log('isNoCommunity')
if (isNoCommunity) { if (isNoCommunity) {
serverNode().then((resp: any) => { serverNode().then((resp: any) => {
if (resp.success) { if (resp.success) {
@ -143,19 +148,21 @@ watch(
emit('serviceChange', val) emit('serviceChange', val)
}, },
); );
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.dash-board { .dash-board {
background-color: #fff;
padding: 24px;
.dash-board-items {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
height: 100%;
background-color: #fff;
// box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
border-radius: 2px; border-radius: 2px;
justify-content: space-between; justify-content: space-between;
padding: 24px;
gap: 24px; gap: 24px;
}
.dash-board-item { .dash-board-item {
flex: 1; flex: 1;
//margin: 24px 12px; //margin: 24px 12px;

View File

@ -0,0 +1,56 @@
<template>
<div ref="echartsDom" class="echarts-warp"></div>
</template>
<script lang="ts" name="Echarts" setup>
import * as echarts from 'echarts';
import {onUnmounted, watch} from "vue";
const props = defineProps({
options: {
type: Object,
default: undefined
}
})
const echartsDom = ref<HTMLElement>()
const myChart = ref()
const echartsRender = () => {
if (!echartsDom.value) return
if (!myChart.value) {
myChart.value = echarts.init(echartsDom.value)
toRaw(myChart.value).setOption(props.options);
} else {
toRaw(myChart.value).setOption(props.options, true);
}
}
const echartsResize = () => {
toRaw(myChart.value)?.resize?.()
}
onMounted(() => {
window.addEventListener('resize', echartsResize)
})
onUnmounted(() => {
window.removeEventListener('resize', echartsResize)
})
watch(() => props.options, () => {
if (props.options) {
nextTick(() => {
echartsRender()
})
}
}, { deep: true })
</script>
<style scoped>
.echarts-warp {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,4 +1,4 @@
import dayjs from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
// export const getInterval = (type: string) => { // export const getInterval = (type: string) => {
@ -41,6 +41,8 @@ export const getTimeByType = (type: string) => {
return dayjs().subtract(29, 'days'); return dayjs().subtract(29, 'days');
case 'year': case 'year':
return dayjs().subtract(365, 'days'); return dayjs().subtract(365, 'days');
case 'day':
return dayjs().subtract(24, 'hours');
default: default:
return dayjs().startOf('day'); return dayjs().startOf('day');
} }
@ -58,14 +60,14 @@ export const networkParams = (val: any) => {
let _time = '1h'; let _time = '1h';
let _limit = 12; let _limit = 12;
let format = 'HH'; let format = 'HH';
// @ts-ignore
const dt = Number(val.time.time[1]) - Number(val.time.time[0]); const dt = dayjs(val.time.time[1]) - dayjs(val.time.time[0])
const hour = 60 * 60 * 1000; const hour = 60 * 60 * 1000;
const days = hour * 24; const days = hour * 24;
const months = days * 30; const months = days * 30;
const year = 365 * days; const year = 365 * days;
if (dt <= hour) { if (dt <= (hour + 10)) {
format = 'mm:ss'; format = 'mm:ss';
_time = '1m'; _time = '1m';
_limit = 30; _limit = 30;

View File

@ -3837,8 +3837,8 @@ jetlinks-ui-components@^1.0.23:
jetlinks-ui-components@^1.0.25: jetlinks-ui-components@^1.0.25:
version "1.0.25" version "1.0.25"
resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.25.tgz#b783da3fe05c1420b2ee5707868a67baa90559f7" resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.25.tgz#fa730dc39f4072f35c34c00d41d343b0da4a99a5"
integrity sha512-4HJM9Wi8gFfBgYFPjCO7JpmEAXVtDUf5u5lnXTQUt1QkGh0QCzFSBGBIStFs6HE5oY3oIIS3ZWoAjvXElpnNZg== integrity sha512-/iz4p86BxmEAPxgTNkr7RqnMdRHQ6vEUKzIlyddtQIvQjuZXTxImh+Ik+V6yxtQVElI8Fjk3l7fiYZZ0E68MSw==
dependencies: dependencies:
"@vueuse/core" "^9.12.0" "@vueuse/core" "^9.12.0"
"@vueuse/router" "^9.13.0" "@vueuse/router" "^9.13.0"