Add software and hardware statistics

This commit is contained in:
2023-12-06 18:30:10 +08:00
parent 46ce9823e2
commit eb264e2b85
16 changed files with 1310 additions and 105 deletions

1089
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@
},
"dependencies": {
"@ant-design/icons": "^5.2.6",
"@ant-design/charts": "^2.0.0-beta.0",
"antd": "^5.12.1",
"axios": "^1.6.2",
"dayjs": "^1.11.10",

View File

@@ -0,0 +1,57 @@
@use '@/assets/css/constants' as constants;
.root-content {
padding: 30px;
gap: 20px;
.root-row {
gap: 20px;
.common-card {
padding: 20px;
gap: 20px;
> .head {
align-items: center;
gap: 5px;
color: constants.$main-color;
.icon {
font-size: constants.$SIZE_ICON_MD;
flex: 0 0 auto;
}
.title {
display: flex;
font-size: 1.2em;
}
}
.card-content {
font-size: 1.1em;
padding: 0 10px;
gap: 10px;
.key {
flex: 0 0 auto;
color: constants.$font-main-color;
}
.value {
color: constants.$font-secondary-color;
overflow: hidden;
> * {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
> * {
gap: 5px;
}
}
}
}
}

View File

@@ -10,6 +10,7 @@
.settings-card {
padding: 20px;
gap: 20px;
color: constants.$main-color;
> .head {
align-items: center;

1
src/assets/svg/chart.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M1000 768c13.2 0 24 10.8 24 24v80c0 13.2-10.8 24-24 24H24c-13.2 0-24-10.8-24-24V152c0-13.2 10.8-24 24-24h80c13.2 0 24 10.8 24 24v616h872zM745.4 319L576 432l-170.6-227.4c-10.2-13.6-31-12.6-39.8 2L192 496v208h768l-179.8-375.6c-6.4-13-22.8-17.4-34.8-9.4z" /></svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M981.333333 533.333333H42.666667l151.68-303.36A85.226667 85.226667 0 0 1 275.626667 170.666667h472.746666c37.12 0 69.973333 23.893333 81.28 59.306666L981.333333 533.333333zM42.666667 597.333333v170.666667c0 47.146667 38.186667 85.333333 85.333333 85.333333h768c47.146667 0 85.333333-38.186667 85.333333-85.333333v-170.666667H42.666667z m778.666666 170.666667h-106.666666c-17.706667 0-32-14.293333-32-32s14.293333-32 32-32h106.666666c17.706667 0 32 14.293333 32 32s-14.293333 32-32 32z" /></svg>

After

Width:  |  Height:  |  Size: 567 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M832 106.666667H192C144.853333 106.666667 106.666667 144.853333 106.666667 192v128h810.666666V192c0-47.146667-38.186667-85.333333-85.333333-85.333333zM106.666667 384v448c0 47.146667 38.186667 85.333333 85.333333 85.333333h640c47.146667 0 85.333333-38.186667 85.333333-85.333333V384H106.666667z m278.613333 338.986667c12.586667 12.586667 12.586667 32.853333 0 45.226666-12.586667 12.586667-32.853333 12.586667-45.226667 0l-105.6-105.6a31.786667 31.786667 0 0 1 0-45.226666l105.6-105.6a31.786667 31.786667 0 0 1 45.226667 0c12.586667 12.586667 12.586667 32.853333 0 45.226666L302.293333 640l82.986667 82.986667z m186.88-212.053334l-57.6 271.36c-3.626667 17.28-20.693333 28.373333-37.973333 24.746667-17.28-3.626667-28.373333-20.693333-24.746667-37.973333l57.6-271.36c3.626667-17.28 20.693333-28.373333 37.973333-24.746667 17.28 3.84 28.373333 20.693333 24.746667 37.973333z m217.386667 151.68l-105.6 105.6c-12.586667 12.586667-32.853333 12.586667-45.226667 0a31.786667 31.786667 0 0 1 0-45.226666L721.706667 640l-82.986667-82.986667a31.786667 31.786667 0 0 1 0-45.226666 31.786667 31.786667 0 0 1 45.226667 0l105.6 105.6c12.586667 12.586667 12.586667 32.64 0 45.226666z" /></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M846.208 435.498667A260.565333 260.565333 0 0 0 781.098667 426.666667V377.429333a21.333333 21.333333 0 0 0-21.333334-21.333333H260.181333a21.333333 21.333333 0 0 0-21.333333 21.333333v306.346667a21.333333 21.333333 0 0 0 21.333333 21.333333h275.072c4.437333 22.229333 12.16 43.349333 22.698667 62.890667H213.333333a42.666667 42.666667 0 0 1-42.666666-42.666667V256a42.666667 42.666667 0 0 1 42.666666-42.666667h590.208a42.666667 42.666667 0 0 1 42.666667 42.666667v179.498667zM274.261333 315.733333c19.882667 0 36.010667-15.274667 36.010667-34.133333 0-18.858667-16.128-34.133333-36.010667-34.133333-19.925333 0-36.053333 15.274667-36.053333 34.133333 0 18.858667 16.128 34.133333 36.053333 34.133333z m112.597334 0c19.882667 0 36.010667-15.274667 36.010666-34.133333 0-18.858667-16.128-34.133333-36.010666-34.133333-19.925333 0-36.053333 15.274667-36.053334 34.133333 0 18.858667 16.128 34.133333 36.053334 34.133333z m589.653333 307.242667c2.901333 12.202667 3.84 25.386667 4.821333 38.570667 0 12.202667-0.981333 24.405333-3.84 38.528v0.938666a26.325333 26.325333 0 0 1-12.586666 14.08c-0.938667 0.981333-0.938667 0.981333-1.92 0.981334-17.365333 4.693333-31.829333 15.018667-39.552 28.16a63.573333 63.573333 0 0 0-5.802667 49.877333c2.901333 7.509333 0 16.896-6.741333 20.650667-18.346667 15.061333-39.594667 27.306667-64.682667 37.632-1.92 0.938667-4.821333 0.938667-6.741333 0.938666-4.821333 0-9.642667-2.816-13.525334-6.570666-13.482667-13.184-28.928-20.693333-45.354666-20.693334a59.733333 59.733333 0 0 0-45.354667 19.754667 22.4 22.4 0 0 1-15.445333 6.570667h-2.901334c-1.92 0-2.858667-0.938667-3.84-0.938667a178.346667 178.346667 0 0 1-66.56-38.570667c-6.826667-5.632-8.704-14.08-5.845333-20.650666a56.021333 56.021333 0 0 0-5.76-47.957334c-9.642667-16-22.186667-25.386667-39.594667-29.141333h-0.938666a18.048 18.048 0 0 1-14.506667-16 160.64 160.64 0 0 1-3.84-37.589333c0-12.245333 0.981333-24.448 3.84-38.570667v-0.938667a26.325333 26.325333 0 0 1 12.586667-14.08c0.938667-0.981333 0.938667-0.981333 1.92-0.981333 17.365333-4.693333 31.829333-15.018667 39.552-28.16a63.573333 63.573333 0 0 0 5.802666-49.877333 17.578667 17.578667 0 0 1 6.741334-20.650667c18.346667-15.061333 39.594667-27.306667 64.682666-37.632a19.370667 19.370667 0 0 1 21.205334 4.693333c13.525333 13.184 28.970667 20.693333 45.354666 20.693334 18.346667 0 34.773333-6.570667 45.354667-19.754667 6.784-6.570667 14.506667-8.448 21.248-5.632a182.144 182.144 0 0 1 65.621333 38.570667c6.784 5.632 8.704 14.08 5.802667 20.650666-5.802667 16-2.901333 33.877333 5.802667 47.957334 9.642667 16 22.186667 25.386667 39.552 29.141333h0.981333a18.048 18.048 0 0 1 14.464 16zM778.666667 725.333333c37.632 0 67.541333-28.330667 67.541333-64s-29.866667-64-67.541333-64c-37.632 0-67.541333 28.330667-67.541334 64s29.866667 64 67.541334 64z" /></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -5,6 +5,7 @@ import { COLOR_FONT_MAIN } from '@/constants/common.constants'
interface LoadingMaskProps extends React.PropsWithChildren {
hidden?: boolean
maskContent?: React.ReactNode
}
const LoadingMask: React.FC<LoadingMaskProps> = (props) => {
const loadingIcon = (
@@ -21,7 +22,7 @@ const LoadingMask: React.FC<LoadingMaskProps> = (props) => {
) : (
<>
<div className={'loading-mask'}>
<AntdSpin indicator={loadingIcon} />
{props.maskContent || <AntdSpin indicator={loadingIcon} />}
</div>
</>
)

View File

@@ -1,5 +1,5 @@
import React from 'react'
import { hasPermission } from '@/util/auth.tsx'
import { hasPermission } from '@/util/auth'
interface PermissionProps extends React.PropsWithChildren {
operationCode?: string

View File

@@ -11,6 +11,9 @@ export const URL_SYS_GROUP = '/system/group'
export const URL_SYS_GROUP_LIST = '/system/group/list'
export const URL_SYS_SETTINGS = '/system/settings'
export const URL_SYS_SETTINGS_MAIL = `${URL_SYS_SETTINGS}/mail`
export const URL_SYS_STATISTICS = '/system/statistics'
export const URL_SYS_STATISTICS_SOFTWARE = `${URL_SYS_STATISTICS}/software`
export const URL_SYS_STATISTICS_HARDWARE = `${URL_SYS_STATISTICS}/hardware`
export const URL_API_V1 = '/api/v1'
export const URL_API_V1_AVATAR_RANDOM_BASE64 = `${URL_API_V1}/avatar/base64`

27
src/global.d.ts vendored
View File

@@ -322,3 +322,30 @@ interface MailSettingsParam {
interface MailSendParam {
to: string
}
interface SoftwareInfoVo {
os: string
bitness: number
javaVersion: string
javaVersionDate: string
javaVendor: string
javaRuntime: string
javaRuntimeVersion: string
jvm: string
jvmVersion: string
jvmInfo: string
jvmVendor: string
javaClassVersion: string
osBootTime: string
serverStartupTime: string
}
interface HardwareInfoVo {
cpu: string
arch: string
is64Bit: boolean
cpuPhysicalPackageCount: number
cpuPhysicalProcessorCount: number
cpuLogicalProcessorCount: number
microarchitecture: string
}

View File

@@ -2,6 +2,7 @@ import React from 'react'
import Icon from '@ant-design/icons'
import '@/assets/css/pages/system/settings.scss'
import { useUpdatedEffect } from '@/util/hooks'
import { hasPermission } from '@/util/auth'
import {
r_sys_settings_mail_get,
r_sys_settings_mail_send,
@@ -12,8 +13,7 @@ import HideScrollbar from '@/components/common/HideScrollbar'
import Card from '@/components/common/Card'
import FlexBox from '@/components/common/FlexBox'
import LoadingMask from '@/components/common/LoadingMask'
import Permission from '@/components/common/Permission.tsx'
import { hasPermission } from '@/util/auth.tsx'
import Permission from '@/components/common/Permission'
interface SettingsCardProps extends React.PropsWithChildren {
icon: IconComponent

View File

@@ -1,7 +1,213 @@
import React from 'react'
import React, { useState } from 'react'
import Icon from '@ant-design/icons'
// import { DualAxes, DualAxesConfig } from '@ant-design/plots'
import '@/assets/css/pages/system/index.scss'
import { useUpdatedEffect } from '@/util/hooks'
import { utcToLocalTime } from '@/util/datetime'
import { r_sys_statistics_hardware, r_sys_statistics_software } from '@/services/system'
import Card from '@/components/common/Card'
import FlexBox from '@/components/common/FlexBox'
import FitFullScreen from '@/components/common/FitFullScreen'
import HideScrollbar from '@/components/common/HideScrollbar'
import LoadingMask from '@/components/common/LoadingMask'
interface CommonCardProps extends React.PropsWithChildren {
icon: IconComponent
title: string
loading?: boolean
}
const CommonCard: React.FC<CommonCardProps> = (props) => {
return (
<Card>
<FlexBox className={'common-card'}>
<FlexBox direction={'horizontal'} className={'head'}>
<Icon component={props.icon} className={'icon'} />
<div className={'title'}>{props.title}</div>
</FlexBox>
<LoadingMask
hidden={!props.loading}
maskContent={<AntdSkeleton active paragraph={{ rows: 10 }} />}
>
{props.children}
</LoadingMask>
</FlexBox>
</Card>
)
}
const SoftwareInfo: React.FC = () => {
const [softwareInfoData, setSoftwareInfoData] = useState<SoftwareInfoVo>()
useUpdatedEffect(() => {
void r_sys_statistics_software().then((res) => {
const response = res.data
if (response.success) {
response.data && setSoftwareInfoData(response.data)
} else {
void message.error('获取软件信息失败,请稍后重试')
}
})
}, [])
return (
<CommonCard
icon={IconFatwebSoftware}
title={'软件信息'}
loading={softwareInfoData === undefined}
>
<FlexBox className={'card-content'} direction={'horizontal'}>
<FlexBox className={'key'}>
<div></div>
<div></div>
<div>Java</div>
<div>Java </div>
<div>Runtime</div>
<div>JVM</div>
<div>JVM </div>
<div></div>
<div></div>
</FlexBox>
<FlexBox className={'value'}>
<div>{softwareInfoData?.os}</div>
<div>{softwareInfoData?.bitness}</div>
<div>{`${softwareInfoData?.javaVersion} (${softwareInfoData?.javaVersionDate})`}</div>
<div>{softwareInfoData?.javaVendor}</div>
<div>{`${softwareInfoData?.javaRuntime} (build ${softwareInfoData?.javaRuntimeVersion})`}</div>
<div>{`${softwareInfoData?.jvm} (build ${softwareInfoData?.jvmVersion}, ${softwareInfoData?.jvmInfo})`}</div>
<div>{softwareInfoData?.jvmVendor}</div>
<div>
{softwareInfoData?.osBootTime &&
utcToLocalTime(softwareInfoData?.osBootTime)}
</div>
<div>
{softwareInfoData?.serverStartupTime &&
utcToLocalTime(softwareInfoData.serverStartupTime)}
</div>
</FlexBox>
</FlexBox>
</CommonCard>
)
}
const HardwareInfo: React.FC = () => {
const [hardwareInfoData, setHardwareInfoData] = useState<HardwareInfoVo>()
useUpdatedEffect(() => {
void r_sys_statistics_hardware().then((res) => {
const response = res.data
if (response.success) {
response.data && setHardwareInfoData(response.data)
} else {
void message.error('获取硬件信息失败,请稍后重试')
}
})
}, [])
return (
<CommonCard
icon={IconFatwebHardware}
title={'硬件信息'}
loading={hardwareInfoData === undefined}
>
<FlexBox className={'card-content'} direction={'horizontal'}>
<FlexBox className={'key'}>
<div>CPU</div>
<div></div>
<div></div>
<div>64</div>
<div>CPU </div>
<div>CPU </div>
<div>CPU </div>
</FlexBox>
<FlexBox className={'value'}>
<div>{hardwareInfoData?.cpu}</div>
<div>{hardwareInfoData?.arch}</div>
<div>{hardwareInfoData?.microarchitecture}</div>
<div>{hardwareInfoData?.is64Bit ? '是' : '否'}</div>
<div>{hardwareInfoData?.cpuPhysicalPackageCount}</div>
<div>{hardwareInfoData?.cpuPhysicalProcessorCount}</div>
<div>{hardwareInfoData?.cpuLogicalProcessorCount}</div>
</FlexBox>
</FlexBox>
</CommonCard>
)
}
const System: React.FC = () => {
return <></>
/*
const dualAxesData = [
{ year: '1991', value: 3, count: 10 },
{ year: '1992', value: 4, count: 4 },
{ year: '1993', value: 3.5, count: 5 },
{ year: '1994', value: 5, count: 5 },
{ year: '1995', value: 4.9, count: 4.9 },
{ year: '1996', value: 6, count: 35 },
{ year: '1997', value: 7, count: 7 },
{ year: '1998', value: 9, count: 1 },
{ year: '1999', value: 13, count: 20 }
]
const userStatisticsData = [
{ time: '2023-12-01', type: 'register', number: 23 },
{ time: '2023-12-02', type: 'register', number: 123 },
{ time: '2023-12-03', type: 'register', number: 1432 },
{ time: '2023-12-05', type: 'register', number: 1 },
{ time: '2023-12-04', type: 'register', number: 234 },
{ time: '2023-12-06', type: 'register', number: 23 },
{ time: '2023-12-07', type: 'register', number: 54 },
{ time: '2023-12-08', type: 'register', number: 87 },
{ time: '2023-12-09', type: 'register', number: 12 },
{ time: '2023-12-10', type: 'register', number: 123 },
{ time: '2023-12-11', type: 'register', number: 20 },
{ time: '2023-12-01', type: 'login', number: 433 },
{ time: '2023-12-02', type: 'login', number: 2 },
{ time: '2023-12-03', type: 'login', number: 34 },
{ time: '2023-12-05', type: 'login', number: 12 },
{ time: '2023-12-04', type: 'login', number: 345 },
{ time: '2023-12-06', type: 'login', number: 121 },
{ time: '2023-12-07', type: 'login', number: 2 },
{ time: '2023-12-08', type: 'login', number: 435 },
{ time: '2023-12-09', type: 'login', number: 1 },
{ time: '2023-12-10', type: 'login', number: 54 },
{ time: '2023-12-11', type: 'login', number: 56 }
]
const dualAxesConfig: DualAxesConfig = {
data: userStatisticsData,
slider: { x: true },
shapeField: 'smooth',
xField: 'time',
children: [
{
type: 'line',
yField: 'number',
colorField: 'type'
}
]
}
*/
return (
<>
<FitFullScreen>
<HideScrollbar isShowVerticalScrollbar autoHideWaitingTime={500}>
<FlexBox className={'root-content'}>
<FlexBox direction={'horizontal'} className={'root-row'}>
<SoftwareInfo />
<HardwareInfo />
</FlexBox>
<FlexBox direction={'horizontal'} className={'root-row'}>
<Card style={{ height: '400px' }}>
{/*<DualAxes {...dualAxesConfig} />*/}
</Card>
<div />
</FlexBox>
</FlexBox>
</HideScrollbar>
</FitFullScreen>
</>
)
}
export default System

View File

@@ -7,8 +7,8 @@ const system: RouteJsonObject[] = [
absolutePath: '/system',
id: 'system',
component: React.lazy(() => import('@/pages/system')),
name: '系统设置',
icon: React.lazy(() => import('~icons/fatweb/setting.jsx')),
name: '系统概况',
icon: React.lazy(() => import('~icons/fatweb/chart.jsx')),
menu: true
},
{

View File

@@ -8,7 +8,9 @@ import {
URL_SYS_GROUP,
URL_SYS_GROUP_LIST,
URL_SYS_LOG,
URL_SYS_SETTINGS_MAIL
URL_SYS_SETTINGS_MAIL,
URL_SYS_STATISTICS_SOFTWARE,
URL_SYS_STATISTICS_HARDWARE
} from '@/constants/urls.constants'
import request from '@/services/index'
@@ -72,3 +74,9 @@ export const r_sys_settings_mail_update = (param: MailSettingsParam) =>
export const r_sys_settings_mail_send = (param: MailSendParam) =>
request.post(URL_SYS_SETTINGS_MAIL, param)
export const r_sys_statistics_software = () =>
request.get<SoftwareInfoVo>(URL_SYS_STATISTICS_SOFTWARE)
export const r_sys_statistics_hardware = () =>
request.get<HardwareInfoVo>(URL_SYS_STATISTICS_HARDWARE)