From 87d303afb6aacaa08a94aaf7be420c470b453055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=AA=E5=B8=A6=E5=A4=A7=E4=BD=AC=E6=B0=94=E5=9C=BA?= <188633308@qq.com> Date: Fri, 25 Jun 2021 15:49:06 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E6=9C=8D=E5=8A=A1=E7=9B=91=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Api/Ewide.Core/Util/MachineUtil.cs | 93 +++++- web-react/src/assets/style/lib/table.less | 2 + web-react/src/pages/system/machine/base.jsx | 33 ++ .../src/pages/system/machine/disk-charts.jsx | 107 +++++++ web-react/src/pages/system/machine/index.jsx | 44 +++ .../src/pages/system/machine/use-charts.jsx | 297 ++++++++++++++++++ .../src/views/main/_layout/content/index.jsx | 7 +- 7 files changed, 569 insertions(+), 14 deletions(-) create mode 100644 web-react/src/pages/system/machine/base.jsx create mode 100644 web-react/src/pages/system/machine/disk-charts.jsx create mode 100644 web-react/src/pages/system/machine/index.jsx create mode 100644 web-react/src/pages/system/machine/use-charts.jsx diff --git a/Api/Ewide.Core/Util/MachineUtil.cs b/Api/Ewide.Core/Util/MachineUtil.cs index fdb12a5..b083552 100644 --- a/Api/Ewide.Core/Util/MachineUtil.cs +++ b/Api/Ewide.Core/Util/MachineUtil.cs @@ -1,5 +1,6 @@ using Furion.RemoteRequest.Extensions; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -26,10 +27,8 @@ namespace Ewide.Core var ramInfo = GetRamInfo(); return new { - TotalRam = Math.Ceiling(ramInfo.Total / 1024).ToString() + " GB", // 总内存 RamRate = Math.Ceiling(100 * ramInfo.Used / ramInfo.Total), // 内存使用率 CpuRate = Math.Ceiling(double.Parse(GetCPURate())), // cpu使用率 - RunTime = GetRunTime() }; } @@ -40,20 +39,28 @@ namespace Ewide.Core public static async Task GetMachineBaseInfo() { var assemblyName = typeof(Furion.App).Assembly.GetName(); - //var networkInfo = NetworkInfo.GetNetworkInfo(); - //var (Received, Send) = networkInfo.GetInternetSpeed(1000); return new { WanIp = await GetWanIpFromPCOnline(), // 外网IP - SendAndReceived = "",// "上行" + Math.Round(networkInfo.SendLength / 1024.0 / 1024 / 1024, 2) + "GB 下行" + Math.Round(networkInfo.ReceivedLength / 1024.0 / 1024 / 1024, 2) + "GB", // 上下行流量统计 - LanIp = "",//networkInfo.AddressIpv4.ToString(), // 局域网IP + LanIp = Dns.GetHostAddresses(string.Empty).Last().ToString(), // 局域网IP IpMac = "",//networkInfo.Mac, // Mac地址 HostName = Environment.MachineName, // HostName + CurrentDirectory = Environment.CurrentDirectory, // 系统路径 SystemOs = RuntimeInformation.OSDescription, // 系统名称 OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(), // 系统架构 - ProcessorCount = Environment.ProcessorCount.ToString() + "核", // CPU核心数 + ProcessorCount = Environment.ProcessorCount, // CPU核心数 FrameworkDescription = RuntimeInformation.FrameworkDescription + " + " + assemblyName.Name.ToString() + assemblyName.Version.ToString(), // .NET和Furion版本 - NetworkSpeed = ""//"上行" + Send / 1024 + "kb/s 下行" + Received / 1024 + "kb/s" // 网络速度 + NetworkSpeed = "",//"上行" + Send / 1024 + "kb/s 下行" + Received / 1024 + "kb/s" // 网络速度 + // Cpu名称 + CpuName = GetCPUName(), + // Cpu基准速度 + CpuBaseSpeed = GetCPUSpeed(), + // 内存总量 + TotalRam = GetRamInfo().Total, + // 运行时间 + RunTime = (long)(DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMilliseconds, + // 磁盘信息 + DiskInfo = GetDiskInfo(), }; } @@ -94,24 +101,86 @@ namespace Ewide.Core return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux); } + private static string GetCPUName() + { + string result; + if (IsUnix()) + { + // ?????? + var output = ShellUtil.Bash(""); + result = output.Trim(); + } + else + { + var output = ShellUtil.Cmd("wmic", "cpu get Name"); + result = output.Replace("Name", string.Empty).Trim(); + } + return result; + } + + private static string GetCPUSpeed() + { + string result; + if (IsUnix()) + { + // ?????? + var output = ShellUtil.Bash(""); + result = output.Trim(); + } + else + { + var output = ShellUtil.Cmd("wmic", "cpu get CurrentClockSpeed"); + result = output.Replace("CurrentClockSpeed", string.Empty).Trim(); + } + return result; + } + + private static List> GetDiskInfo() + { + var result = new List>(); + if (IsUnix()) + { + // ?????? + var output = ShellUtil.Bash(""); + } + else + { + var output = ShellUtil.Cmd("wmic", "LOGICALDISK get Name,Description,FileSystem,Size,FreeSpace"); + var strArray = output.Replace("\r", "").Trim().Split('\n') + .Select(p => System.Text.RegularExpressions.Regex.Replace(p.Trim(), @"\s+", ",").Split(',')); + var keyArray = strArray.First(); + var valueArray = strArray.Where((p, i) => i > 0).ToArray(); + foreach(var value in valueArray) + { + var dict = new Dictionary(); + for(var i = 0;i /// 获取CPU使用率 /// /// private static string GetCPURate() { - string cpuRate; + string result; if (IsUnix()) { var output = ShellUtil.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'"); - cpuRate = output.Trim(); + result = output.Trim(); } else { var output = ShellUtil.Cmd("wmic", "cpu get LoadPercentage"); - cpuRate = output.Replace("LoadPercentage", string.Empty).Trim(); + result = output.Replace("LoadPercentage", string.Empty).Trim(); } - return cpuRate; + return result; } /// diff --git a/web-react/src/assets/style/lib/table.less b/web-react/src/assets/style/lib/table.less index 2e3d24e..9bde634 100644 --- a/web-react/src/assets/style/lib/table.less +++ b/web-react/src/assets/style/lib/table.less @@ -139,6 +139,8 @@ } } &--row-no { + width: 30px !important; + background-color: @table-header-bg; } } diff --git a/web-react/src/pages/system/machine/base.jsx b/web-react/src/pages/system/machine/base.jsx new file mode 100644 index 0000000..9437b73 --- /dev/null +++ b/web-react/src/pages/system/machine/base.jsx @@ -0,0 +1,33 @@ +import React, { Component } from 'react' +import { Card, Col, Descriptions } from 'antd' + +export default class base extends Component { + render() { + const { base } = this.props + + const { hostName, systemOs, wanIp, lanIp, osArchitecture, frameworkDescription } = base + + return ( + <> + + + + {hostName} + {systemOs} + {wanIp} + {lanIp} + {osArchitecture} + + {frameworkDescription} + + + + + + ) + } +} diff --git a/web-react/src/pages/system/machine/disk-charts.jsx b/web-react/src/pages/system/machine/disk-charts.jsx new file mode 100644 index 0000000..0024161 --- /dev/null +++ b/web-react/src/pages/system/machine/disk-charts.jsx @@ -0,0 +1,107 @@ +import React, { Component } from 'react' +import { Card, Col, Row } from 'antd' +import * as echarts from 'echarts' + +export default class diskCharts extends Component { + diskInfo = [] + + diskChart1 = [] + diskChart2 = [] + diskChart3 = [] + diskChart4 = [] + + constructor(props) { + super(props) + + const { base } = props + + this.diskInfo = base.diskInfo + } + + componentDidMount() { + this.diskInfo.forEach(({ size, freeSpace }, i) => { + const dom = this.refs[`disk-chart-${i}`] + this[`diskChart${i}`] = echarts.init(dom) + + const usedSpace = size - freeSpace + const sizeGB = (size / 1024 / 1024 / 1024).toFixed(1) + const usedGB = (usedSpace / 1024 / 1024 / 1024).toFixed(1) + const freeGB = (freeSpace / 1024 / 1024 / 1024).toFixed(1) + + const option = { + tooltip: false, + series: [ + { + name: '磁盘使用量', + type: 'pie', + radius: ['70%', '100%'], + label: { + show: true, + fontSize: '14', + position: 'center', + formatter: `共 ${sizeGB} GB`, + }, + emphasis: { + label: { + show: true, + fontSize: '14', + formatter: '{b}{c}GB({d}%)', + }, + }, + labelLine: { + show: false, + }, + data: [ + { + value: usedGB, + name: '已用', + itemStyle: { + color: usedGB / sizeGB >= 0.85 ? '#ff4d4f' : '#007bff', + }, + }, + { + value: freeGB, + name: '可用', + itemStyle: { color: '#e0e0e0' }, + }, + ], + hoverAnimation: false, + animation: false, + }, + ], + } + this[`diskChart${i}`].setOption(option) + }) + + window.addEventListener('resize', this.onResizeCharts) + } + + componentWillUnmount() { + window.removeEventListener('resize', this.onResizeCharts) + } + + onResizeCharts = () => { + this.diskInfo.forEach((item, i) => { + this[`diskChart${i}`].resize() + }) + } + + render() { + const { diskInfo } = this + + return ( + <> + {diskInfo.map((item, i) => ( + + +
+ {item.description}({item.name}) +
+
+
+ + ))} + + ) + } +} diff --git a/web-react/src/pages/system/machine/index.jsx b/web-react/src/pages/system/machine/index.jsx new file mode 100644 index 0000000..fa6a8a9 --- /dev/null +++ b/web-react/src/pages/system/machine/index.jsx @@ -0,0 +1,44 @@ +import React, { Component } from 'react' +import { Card, Col, Descriptions, Row, Statistic } from 'antd' +import { api } from 'common/api' +import { Container } from 'components' +import { isEqual } from 'lodash' +import moment from 'moment' + +import Base from './base' +import UseCharts from './use-charts' +import DiskCharts from './disk-charts' + +export default class index extends Component { + state = { + loading: true, + base: {}, + network: {}, + } + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) || this.props.paneActived !== props.paneActived + } + + async componentDidMount() { + const { data: base } = await api.sysMachineBase() + this.setState({ loading: false, base }) + } + + render() { + const { paneActived } = this.props + + const { loading } = this.state + + return ( + +
+ + {!loading && } + {!loading && } + {!loading && } + +
+ ) + } +} diff --git a/web-react/src/pages/system/machine/use-charts.jsx b/web-react/src/pages/system/machine/use-charts.jsx new file mode 100644 index 0000000..2693159 --- /dev/null +++ b/web-react/src/pages/system/machine/use-charts.jsx @@ -0,0 +1,297 @@ +import React, { Component } from 'react' +import { Card, Col, Descriptions, Row, Statistic } from 'antd' +import * as echarts from 'echarts' +import moment from 'moment' +import { api } from 'common/api' + +export default class useCharts extends Component { + state = { + use: {}, + + nowMoment: moment(), + } + + timer = null + timerMoment = null + + systemStart = moment() + + now = Date.now() + + cpuChart = null + cpuData = [] + + ramChart = null + ramData = [] + + shouldComponentUpdate(props) { + // 当前页签未选中时停止获取状态 + if (this.props.actived !== props.actived) { + if (props.actived) { + this.start() + } else { + this.stop() + } + } + return true + } + + componentDidMount() { + this.systemStart = moment().add(-this.props.base.runTime) + this.initCpuChart() + this.initRamChart() + + this.start() + + window.addEventListener('resize', this.onResizeCharts) + } + + componentWillUnmount() { + this.stop() + + window.removeEventListener('resize', this.onResizeCharts) + } + + start() { + this.timer = setInterval(() => { + this.refreshData() + }, 3000) + this.refreshData() + this.timerMoment = setInterval(() => { + this.setState({ nowMoment: moment() }) + }, 1000) + } + + stop() { + clearInterval(this.timer) + clearInterval(this.timerMoment) + } + + async refreshData() { + const { data: use } = await api.sysMachineUse() + + this.now = Date.now() + + this.cpuData.shift() + this.cpuData.push({ + name: this.now, + value: [this.now, use.cpuRate], + }) + this.cpuChart.setOption({ + series: [{ data: this.cpuData }], + }) + + this.ramData.shift() + this.ramData.push({ + name: this.now, + value: [this.now, use.ramRate], + }) + this.ramChart.setOption({ + series: [{ data: this.ramData }], + }) + + this.setState({ use }) + } + + initCpuChart() { + for (let i = 0; i < 60; i++) { + const past = this.now - (60 - i) * 1000 + this.cpuData.push({ + name: past, + value: [past, -1], + }) + } + + const dom = this.refs['cpu-chart'] + this.cpuChart = echarts.init(dom) + const option = { + grid: { + show: true, + top: 0, + left: 0, + right: 0, + bottom: 0, + borderColor: 'rgba(0, 123, 255, 1)', + borderWidth: 2, + zlevel: 2, + }, + tooltip: false, + xAxis: { + type: 'time', + axisTick: { + show: false, + }, + axisLabel: { + show: false, + }, + axisLine: { + show: false, + }, + }, + yAxis: { + type: 'value', + max: 100, + min: 0, + axisLabel: { + show: false, + }, + }, + series: [ + { + type: 'line', + showSymbol: false, + hoverAnimation: false, + animation: false, + data: this.cpuData, + lineStyle: { + width: 1, + color: 'rgba(0, 123, 255, .8)', + }, + areaStyle: { + color: 'rgba(0, 123, 255, .3)', + }, + }, + ], + } + this.cpuChart.setOption(option) + } + + initRamChart() { + for (let i = 0; i < 60; i++) { + const past = this.now - (60 - i) * 1000 + this.ramData.push({ + name: past, + value: [past, -1], + }) + } + + const dom = this.refs['ram-chart'] + this.ramChart = echarts.init(dom) + const option = { + grid: { + show: true, + top: 0, + left: 0, + right: 0, + bottom: 0, + borderColor: 'rgba(83, 29, 171, 1)', + borderWidth: 2, + zlevel: 2, + }, + tooltip: false, + xAxis: { + type: 'time', + axisTick: { + show: false, + }, + axisLabel: { + show: false, + }, + axisLine: { + show: false, + }, + }, + yAxis: { + type: 'value', + max: 100, + min: 0, + axisLabel: { + show: false, + }, + }, + series: [ + { + type: 'line', + showSymbol: false, + hoverAnimation: false, + animation: false, + data: this.ramData, + lineStyle: { + width: 1, + color: 'rgba(83, 29, 171, .8)', + }, + areaStyle: { + color: 'rgba(83, 29, 171, .3)', + }, + }, + ], + } + this.ramChart.setOption(option) + } + + onResizeCharts = () => { + this.cpuChart.resize() + this.ramChart.resize() + } + + render() { + const { base } = this.props + const { use, nowMoment } = this.state + const { cpuName, cpuBaseSpeed, processorCount, totalRam } = base + const { cpuRate, ramRate } = use + + const diffDays = nowMoment.diff(this.systemStart, 'days') + const diff = + diffDays + ':' + moment(nowMoment.diff(this.systemStart)).utc().format('HH:mm:ss') + + return ( + <> + + + +
CPU
+
{cpuName}
+
+
+ + + + + + + + + {((cpuBaseSpeed || 0) / 1000).toFixed(2)} GHz + + + {processorCount || 0} + + + + +
+ + + + +
内存
+
{((totalRam || 0) / 1024).toFixed(1)} GB
+
+
+ + + + + + +
+ + + ) + } +} diff --git a/web-react/src/views/main/_layout/content/index.jsx b/web-react/src/views/main/_layout/content/index.jsx index f9aadc7..73f2ad5 100644 --- a/web-react/src/views/main/_layout/content/index.jsx +++ b/web-react/src/views/main/_layout/content/index.jsx @@ -113,13 +113,15 @@ export default class index extends Component { render() { this.panes = [] + const { actived } = this.state + return (
this.onChange(activeKey)} onEdit={(targetKey, action) => this.onClose(targetKey, action)} > @@ -183,7 +185,7 @@ export default class index extends Component {
this.panes.push(p)} />