mirror of
https://github.com/acepanel/panel.git
synced 2026-02-05 12:23:35 +08:00
517 lines
12 KiB
Vue
517 lines
12 KiB
Vue
<script setup lang="ts">
|
|
defineOptions({
|
|
name: 'monitor-index'
|
|
})
|
|
|
|
import { LineChart } from 'echarts/charts'
|
|
import {
|
|
DataZoomComponent,
|
|
GridComponent,
|
|
LegendComponent,
|
|
TitleComponent,
|
|
TooltipComponent
|
|
} from 'echarts/components'
|
|
import { use } from 'echarts/core'
|
|
import { CanvasRenderer } from 'echarts/renderers'
|
|
import { NButton } from 'naive-ui'
|
|
import VChart from 'vue-echarts'
|
|
import { useGettext } from 'vue3-gettext'
|
|
|
|
import monitor from '@/api/panel/monitor'
|
|
|
|
const { $gettext } = useGettext()
|
|
|
|
use([
|
|
CanvasRenderer,
|
|
LineChart,
|
|
TitleComponent,
|
|
TooltipComponent,
|
|
LegendComponent,
|
|
GridComponent,
|
|
DataZoomComponent
|
|
])
|
|
|
|
const start = ref(Math.floor(new Date(new Date().setHours(0, 0, 0, 0)).getTime()))
|
|
const end = ref(Math.floor(Date.now()))
|
|
|
|
useRequest(monitor.setting()).onSuccess(({ data }) => {
|
|
monitorSwitch.value = data.enabled
|
|
saveDay.value = data.days
|
|
})
|
|
|
|
const { loading, data } = useWatcher(monitor.list(start.value, end.value), [start, end], {
|
|
initialData: {
|
|
times: [],
|
|
load: {},
|
|
cpu: {},
|
|
mem: {},
|
|
swap: {},
|
|
net: {}
|
|
},
|
|
debounce: [500],
|
|
immediate: true
|
|
})
|
|
|
|
const monitorSwitch = ref(false)
|
|
const saveDay = ref(30)
|
|
|
|
const load = ref<any>({
|
|
title: {
|
|
text: $gettext('Load'),
|
|
textAlign: 'left',
|
|
textStyle: {
|
|
fontSize: 20
|
|
}
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
legend: {
|
|
align: 'left',
|
|
data: [$gettext('1 minute'), $gettext('5 minutes'), $gettext('15 minutes')]
|
|
},
|
|
xAxis: [{ type: 'category', boundaryGap: false, data: data.value.times }],
|
|
yAxis: [
|
|
{
|
|
type: 'value'
|
|
}
|
|
],
|
|
dataZoom: {
|
|
show: true,
|
|
realtime: true,
|
|
start: 0,
|
|
end: 100
|
|
},
|
|
series: [
|
|
{
|
|
name: $gettext('1 minute'),
|
|
type: 'line',
|
|
smooth: true,
|
|
data: data.value.load.load1,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
},
|
|
{
|
|
name: $gettext('5 minutes'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.load.load5,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
},
|
|
{
|
|
name: $gettext('15 minutes'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.load.load15,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
const cpu = ref<any>({
|
|
title: {
|
|
text: 'CPU',
|
|
textAlign: 'left',
|
|
textStyle: {
|
|
fontSize: 20
|
|
}
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
xAxis: [{ type: 'category', boundaryGap: false, data: data.value.times }],
|
|
yAxis: [
|
|
{
|
|
name: $gettext('Unit %'),
|
|
min: 0,
|
|
max: 100,
|
|
type: 'value',
|
|
axisLabel: {
|
|
formatter: '{value} %'
|
|
}
|
|
}
|
|
],
|
|
dataZoom: {
|
|
show: true,
|
|
realtime: true,
|
|
start: 0,
|
|
end: 100
|
|
},
|
|
series: [
|
|
{
|
|
name: $gettext('Usage'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.cpu.percent,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
const mem = ref<any>({
|
|
title: {
|
|
text: $gettext('Memory'),
|
|
textAlign: 'left',
|
|
textStyle: {
|
|
fontSize: 20
|
|
}
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
legend: {
|
|
align: 'left',
|
|
data: [$gettext('Memory'), 'Swap']
|
|
},
|
|
xAxis: [{ type: 'category', boundaryGap: false, data: data.value.times }],
|
|
yAxis: [
|
|
{
|
|
name: $gettext('Unit MB'),
|
|
min: 0,
|
|
max: data.value.mem.total,
|
|
type: 'value',
|
|
axisLabel: {
|
|
formatter: '{value} M'
|
|
}
|
|
}
|
|
],
|
|
dataZoom: {
|
|
show: true,
|
|
realtime: true,
|
|
start: 0,
|
|
end: 100
|
|
},
|
|
series: [
|
|
{
|
|
name: $gettext('Memory'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.mem.used,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
},
|
|
{
|
|
name: 'Swap',
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.swap.used,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
const net = ref<any>({
|
|
title: {
|
|
text: $gettext('Network'),
|
|
textAlign: 'left',
|
|
textStyle: {
|
|
fontSize: 20
|
|
}
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
legend: {
|
|
align: 'left',
|
|
data: [
|
|
$gettext('Total Out'),
|
|
$gettext('Total In'),
|
|
$gettext('Per Second Out'),
|
|
$gettext('Per Second In')
|
|
]
|
|
},
|
|
xAxis: [{ type: 'category', boundaryGap: false, data: data.value.times }],
|
|
yAxis: [
|
|
{
|
|
name: $gettext('Unit MB'),
|
|
type: 'value',
|
|
axisLabel: {
|
|
formatter: '{value} MB'
|
|
}
|
|
}
|
|
],
|
|
dataZoom: {
|
|
show: true,
|
|
realtime: true,
|
|
start: 0,
|
|
end: 100
|
|
},
|
|
series: [
|
|
{
|
|
name: $gettext('Total Out'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.net.sent,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
},
|
|
{
|
|
name: $gettext('Total In'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.net.recv,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
},
|
|
{
|
|
name: $gettext('Per Second Out'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.net.tx,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
},
|
|
{
|
|
name: $gettext('Per Second In'),
|
|
type: 'line',
|
|
smooth: true,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
},
|
|
data: data.value.net.rx,
|
|
markPoint: {
|
|
data: [
|
|
{ type: 'max', name: $gettext('Maximum') },
|
|
{ type: 'min', name: $gettext('Minimum') }
|
|
]
|
|
},
|
|
markLine: {
|
|
data: [{ type: 'average', name: $gettext('Average') }]
|
|
}
|
|
}
|
|
]
|
|
})
|
|
|
|
const handleUpdate = async () => {
|
|
useRequest(monitor.updateSetting(monitorSwitch.value, saveDay.value)).onSuccess(() => {
|
|
window.$message.success($gettext('Operation successful'))
|
|
})
|
|
}
|
|
|
|
const handleClear = async () => {
|
|
useRequest(monitor.clear()).onSuccess(() => {
|
|
window.$message.success($gettext('Operation successful'))
|
|
})
|
|
}
|
|
|
|
// 监听 data 的变化
|
|
watch(data, () => {
|
|
load.value.xAxis[0].data = data.value.times
|
|
load.value.series[0].data = data.value.load.load1
|
|
load.value.series[1].data = data.value.load.load5
|
|
load.value.series[2].data = data.value.load.load15
|
|
cpu.value.xAxis[0].data = data.value.times
|
|
cpu.value.series[0].data = data.value.cpu.percent
|
|
mem.value.xAxis[0].data = data.value.times
|
|
mem.value.yAxis[0].max = data.value.mem.total
|
|
mem.value.series[0].data = data.value.mem.used
|
|
mem.value.series[1].data = data.value.swap.used
|
|
net.value.xAxis[0].data = data.value.times
|
|
net.value.series[0].data = data.value.net.sent
|
|
net.value.series[1].data = data.value.net.recv
|
|
net.value.series[2].data = data.value.net.tx
|
|
net.value.series[3].data = data.value.net.rx
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<common-page show-footer>
|
|
<template #action>
|
|
<n-popconfirm @positive-click="handleClear">
|
|
<template #trigger>
|
|
<n-button type="error">
|
|
<TheIcon :size="18" icon="material-symbols:delete-outline" />
|
|
{{ $gettext('Clear Monitoring Records') }}
|
|
</n-button>
|
|
</template>
|
|
{{ $gettext('Are you sure you want to clear?') }}
|
|
</n-popconfirm>
|
|
</template>
|
|
<n-card :segmented="true" flex items-center>
|
|
<n-form
|
|
inline
|
|
label-placement="left"
|
|
label-width="auto"
|
|
require-mark-placement="right-hanging"
|
|
>
|
|
<n-flex items-center>
|
|
<n-form-item :label="$gettext('Enable Monitoring')">
|
|
<n-switch v-model:value="monitorSwitch" @update-value="handleUpdate" />
|
|
</n-form-item>
|
|
<n-form-item :label="$gettext('Save Days')">
|
|
<n-input-number v-model:value="saveDay">
|
|
<template #suffix> {{ $gettext('days') }} </template>
|
|
</n-input-number>
|
|
</n-form-item>
|
|
<n-form-item>
|
|
<n-button type="primary" @click="handleUpdate">{{ $gettext('Confirm') }}</n-button>
|
|
</n-form-item>
|
|
<n-form-item :label="$gettext('Time Selection')">
|
|
<n-date-picker v-model:value="start" type="datetime" />
|
|
-
|
|
<n-date-picker v-model:value="end" type="datetime" />
|
|
</n-form-item>
|
|
</n-flex>
|
|
</n-form>
|
|
</n-card>
|
|
<n-grid
|
|
v-if="!loading"
|
|
cols="1 s:1 m:1 l:2 xl:2 2xl:2"
|
|
item-responsive
|
|
responsive="screen"
|
|
pt-20
|
|
>
|
|
<n-gi m-10>
|
|
<n-card :bordered="false" style="height: 40vh">
|
|
<v-chart class="chart" :option="load" autoresize />
|
|
</n-card>
|
|
</n-gi>
|
|
<n-gi m-10>
|
|
<n-card :bordered="false" style="height: 40vh">
|
|
<v-chart class="chart" :option="cpu" autoresize />
|
|
</n-card>
|
|
</n-gi>
|
|
<n-gi m-10>
|
|
<n-card :bordered="false" style="height: 40vh">
|
|
<v-chart class="chart" :option="mem" autoresize />
|
|
</n-card>
|
|
</n-gi>
|
|
<n-gi m-10>
|
|
<n-card :bordered="false" style="height: 40vh">
|
|
<v-chart class="chart" :option="net" autoresize />
|
|
</n-card>
|
|
</n-gi>
|
|
</n-grid>
|
|
<n-skeleton v-else text :repeat="40" />
|
|
</common-page>
|
|
</template>
|
|
|
|
<style scoped lang="scss"></style>
|