Add active information management to statistic
This commit is contained in:
1
src/assets/svg/analysis.svg
Normal file
1
src/assets/svg/analysis.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 1024"><path d="M265.216 920.348444a41.472 41.472 0 0 1-41.415111-42.211555v-215.608889a42.154667 42.154667 0 1 1 83.512889 0v215.608889a42.097778 42.097778 0 0 1-42.097778 42.211555z m207.701333 0a41.472 41.472 0 0 1-41.358222-42.211555V536.689778a42.097778 42.097778 0 0 1 83.512889 0v340.764444a42.097778 42.097778 0 0 1-42.097778 42.894222z m207.758223 0a41.472 41.472 0 0 1-41.358223-42.211555V728.177778a42.097778 42.097778 0 0 1 83.512889 0v149.959111a42.097778 42.097778 0 0 1-42.097778 42.211555z m207.758222 0a41.472 41.472 0 0 1-41.415111-42.211555v-276.48a42.154667 42.154667 0 1 1 83.569777 0v279.950222a42.097778 42.097778 0 0 1-42.097777 38.684445zM210.659556 502.101333a41.415111 41.415111 0 0 1-33.792-16.554666 42.211556 42.211556 0 0 1 10.353777-58.766223l300.202667-231.537777a42.723556 42.723556 0 0 1 51.768889 0l138.069333 112.64 222.947556-185.969778a41.756444 41.756444 0 0 1 53.816889 63.601778l-247.808 208.782222a41.358222 41.358222 0 0 1-53.134223 0L515.072 280.917333 238.933333 493.112889a42.723556 42.723556 0 0 1-28.273777 8.988444z" /><path d="M927.118222 336.896a42.097778 42.097778 0 0 1-42.097778-41.472V179.313778h-117.361777a42.154667 42.154667 0 0 1 0-83.626667h159.459555a42.097778 42.097778 0 0 1 42.097778 41.415111v158.321778a42.097778 42.097778 0 0 1-42.097778 41.528889z" /></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -17,6 +17,7 @@ export const URL_SYS_STATISTIC_HARDWARE = `${URL_SYS_STATISTIC}/hardware`
|
|||||||
export const URL_SYS_STATISTIC_CPU = `${URL_SYS_STATISTIC}/cpu`
|
export const URL_SYS_STATISTIC_CPU = `${URL_SYS_STATISTIC}/cpu`
|
||||||
export const URL_SYS_STATISTIC_STORAGE = `${URL_SYS_STATISTIC}/storage`
|
export const URL_SYS_STATISTIC_STORAGE = `${URL_SYS_STATISTIC}/storage`
|
||||||
export const URL_SYS_STATISTIC_ONLINE = `${URL_SYS_STATISTIC}/online`
|
export const URL_SYS_STATISTIC_ONLINE = `${URL_SYS_STATISTIC}/online`
|
||||||
|
export const URL_SYS_STATISTIC_ACTIVE = `${URL_SYS_STATISTIC}/active`
|
||||||
|
|
||||||
export const URL_API_V1 = '/api/v1'
|
export const URL_API_V1 = '/api/v1'
|
||||||
export const URL_API_V1_AVATAR_RANDOM_BASE64 = `${URL_API_V1}/avatar/base64`
|
export const URL_API_V1_AVATAR_RANDOM_BASE64 = `${URL_API_V1}/avatar/base64`
|
||||||
|
|||||||
15
src/global.d.ts
vendored
15
src/global.d.ts
vendored
@@ -389,3 +389,18 @@ interface OnlineInfoVo {
|
|||||||
interface OnlineInfoGetParam {
|
interface OnlineInfoGetParam {
|
||||||
scope: string
|
scope: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ActiveInfoVo {
|
||||||
|
registerHistory: {
|
||||||
|
time: string
|
||||||
|
count: number
|
||||||
|
}[]
|
||||||
|
loginHistory: {
|
||||||
|
time: string
|
||||||
|
count: number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActiveInfoGetParam {
|
||||||
|
scope: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ import {
|
|||||||
import { BarChart, BarSeriesOption, LineChart, LineSeriesOption } from 'echarts/charts'
|
import { BarChart, BarSeriesOption, LineChart, LineSeriesOption } from 'echarts/charts'
|
||||||
import { UniversalTransition } from 'echarts/features'
|
import { UniversalTransition } from 'echarts/features'
|
||||||
import { SVGRenderer } from 'echarts/renderers'
|
import { SVGRenderer } from 'echarts/renderers'
|
||||||
|
import { TopLevelFormatterParams } from 'echarts/types/dist/shared'
|
||||||
import '@/assets/css/pages/system/index.scss'
|
import '@/assets/css/pages/system/index.scss'
|
||||||
import { useUpdatedEffect } from '@/util/hooks'
|
import { useUpdatedEffect } from '@/util/hooks'
|
||||||
import { formatByteSize } from '@/util/common'
|
import { formatByteSize } from '@/util/common'
|
||||||
import { utcToLocalTime } from '@/util/datetime'
|
import { utcToLocalTime } from '@/util/datetime'
|
||||||
import {
|
import {
|
||||||
|
r_sys_statistic_active,
|
||||||
r_sys_statistic_cpu,
|
r_sys_statistic_cpu,
|
||||||
r_sys_statistic_hardware,
|
r_sys_statistic_hardware,
|
||||||
r_sys_statistic_online,
|
r_sys_statistic_online,
|
||||||
@@ -90,6 +92,30 @@ const barEChartsBaseOption: EChartsOption = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getTooltipTimeFormatter = (format: string = 'yyyy-MM-DD HH:mm:ss') => {
|
||||||
|
return (params: TopLevelFormatterParams) =>
|
||||||
|
`${utcToLocalTime(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access
|
||||||
|
params[0].data[0],
|
||||||
|
format
|
||||||
|
)}<br><span style="display: flex; justify-content: space-between"><span>${
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
params[0]['marker']
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
}${params[0]['seriesName']}</span><span style="font-weight: bold">${
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access
|
||||||
|
params[0].data[1]
|
||||||
|
}</span></span> `
|
||||||
|
}
|
||||||
|
|
||||||
const lineEChartsBaseOption: EChartsOption = {
|
const lineEChartsBaseOption: EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
@@ -114,11 +140,8 @@ const lineEChartsBaseOption: EChartsOption = {
|
|||||||
{
|
{
|
||||||
type: 'inside',
|
type: 'inside',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 100
|
end: 100,
|
||||||
},
|
minValueSpan: 2 * 60 * 60 * 1000
|
||||||
{
|
|
||||||
start: 0,
|
|
||||||
end: 100
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
series: [{}]
|
series: [{}]
|
||||||
@@ -201,7 +224,6 @@ const OnlineInfo: React.FC = () => {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const dataList = data.history.map((value) => [value.time, value.record])
|
const dataList = data.history.map((value) => [value.time, value.record])
|
||||||
|
|
||||||
onlineInfoEChartsRef.current = echarts.init(
|
onlineInfoEChartsRef.current = echarts.init(
|
||||||
onlineInfoDivRef.current,
|
onlineInfoDivRef.current,
|
||||||
null,
|
null,
|
||||||
@@ -210,6 +232,13 @@ const OnlineInfo: React.FC = () => {
|
|||||||
|
|
||||||
onlineInfoEChartsRef.current?.setOption({
|
onlineInfoEChartsRef.current?.setOption({
|
||||||
...lineEChartsBaseOption,
|
...lineEChartsBaseOption,
|
||||||
|
tooltip: {
|
||||||
|
...lineEChartsBaseOption.tooltip,
|
||||||
|
formatter: getTooltipTimeFormatter('yyyy-MM-DD HH:mm')
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
...lineEChartsBaseOption.xAxis
|
||||||
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '在线人数',
|
name: '在线人数',
|
||||||
@@ -267,6 +296,142 @@ const OnlineInfo: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ActiveInfo: React.FC = () => {
|
||||||
|
const activeInfoDivRef = useRef<HTMLDivElement>(null)
|
||||||
|
const activeInfoEChartsRef = useRef<echarts.EChartsType | null>(null)
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [scope, setScope] = useState('WEAK')
|
||||||
|
|
||||||
|
useUpdatedEffect(() => {
|
||||||
|
const chartResizeObserver = new ResizeObserver(() => {
|
||||||
|
activeInfoEChartsRef.current?.resize()
|
||||||
|
})
|
||||||
|
|
||||||
|
activeInfoDivRef.current && chartResizeObserver.observe(activeInfoDivRef.current)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
activeInfoDivRef.current && chartResizeObserver.unobserve(activeInfoDivRef.current)
|
||||||
|
}
|
||||||
|
}, [isLoading])
|
||||||
|
|
||||||
|
useUpdatedEffect(() => {
|
||||||
|
getActiveInfo()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleOnScopeChange = (value: string) => {
|
||||||
|
setScope(value)
|
||||||
|
getActiveInfo(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnRefresh = () => {
|
||||||
|
getActiveInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getActiveInfo = (_scope: string = scope) => {
|
||||||
|
if (isLoading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
void r_sys_statistic_active({ scope: _scope }).then((res) => {
|
||||||
|
const response = res.data
|
||||||
|
if (response.success) {
|
||||||
|
const data = response.data
|
||||||
|
if (data) {
|
||||||
|
setIsLoading(false)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const registerList = data.registerHistory.map((value) => [
|
||||||
|
value.time,
|
||||||
|
value.count
|
||||||
|
])
|
||||||
|
const loginList = data.loginHistory.map((value) => [
|
||||||
|
value.time,
|
||||||
|
value.count
|
||||||
|
])
|
||||||
|
|
||||||
|
activeInfoEChartsRef.current = echarts.init(
|
||||||
|
activeInfoDivRef.current,
|
||||||
|
null,
|
||||||
|
{ renderer: 'svg' }
|
||||||
|
)
|
||||||
|
|
||||||
|
activeInfoEChartsRef.current?.setOption({
|
||||||
|
...lineEChartsBaseOption,
|
||||||
|
tooltip: {
|
||||||
|
...lineEChartsBaseOption.tooltip,
|
||||||
|
formatter: getTooltipTimeFormatter('yyyy-MM-DD')
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
minValueSpan: 2 * 24 * 60 * 60 * 1000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '注册人数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
areaStyle: {},
|
||||||
|
data: registerList
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '登录人数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
areaStyle: {},
|
||||||
|
data: loginList
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CommonCard
|
||||||
|
icon={IconFatwebAnalysis}
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
<FlexBox gap={10} direction={'horizontal'}>
|
||||||
|
<span style={{ whiteSpace: 'nowrap' }}>用户活跃</span>
|
||||||
|
</FlexBox>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
loading={isLoading}
|
||||||
|
expand={
|
||||||
|
<>
|
||||||
|
<AntdSelect value={scope} onChange={handleOnScopeChange} disabled={isLoading}>
|
||||||
|
<AntdSelect.Option key={'WEAK'}>最近7天</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'MONTH'}>最近30天</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'QUARTER'}>最近3个月</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'YEAR'}>最近12个月</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'TWO_YEARS'}>最近2年</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'THREE_YEARS'}>最近3年</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'FIVE_YEARS'}>最近5年</AntdSelect.Option>
|
||||||
|
<AntdSelect.Option key={'ALL'}>全部</AntdSelect.Option>
|
||||||
|
</AntdSelect>
|
||||||
|
<AntdButton title={'刷新'} onClick={handleOnRefresh} disabled={isLoading}>
|
||||||
|
<Icon component={IconFatwebRefresh} />
|
||||||
|
</AntdButton>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FlexBox className={'card-content'} direction={'horizontal'}>
|
||||||
|
<div className={'big-chart'} ref={activeInfoDivRef} />
|
||||||
|
</FlexBox>
|
||||||
|
</CommonCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const SoftwareInfo: React.FC = () => {
|
const SoftwareInfo: React.FC = () => {
|
||||||
const [softwareInfoData, setSoftwareInfoData] = useState<SoftwareInfoVo>()
|
const [softwareInfoData, setSoftwareInfoData] = useState<SoftwareInfoVo>()
|
||||||
|
|
||||||
@@ -748,6 +913,7 @@ const System: React.FC = () => {
|
|||||||
<HideScrollbar isShowVerticalScrollbar autoHideWaitingTime={500}>
|
<HideScrollbar isShowVerticalScrollbar autoHideWaitingTime={500}>
|
||||||
<FlexBox direction={'horizontal'} className={'root-content'}>
|
<FlexBox direction={'horizontal'} className={'root-content'}>
|
||||||
<OnlineInfo />
|
<OnlineInfo />
|
||||||
|
<ActiveInfo />
|
||||||
<SoftwareInfo />
|
<SoftwareInfo />
|
||||||
<HardwareInfo />
|
<HardwareInfo />
|
||||||
<CPUInfo />
|
<CPUInfo />
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
URL_SYS_STATISTIC_HARDWARE,
|
URL_SYS_STATISTIC_HARDWARE,
|
||||||
URL_SYS_STATISTIC_CPU,
|
URL_SYS_STATISTIC_CPU,
|
||||||
URL_SYS_STATISTIC_STORAGE,
|
URL_SYS_STATISTIC_STORAGE,
|
||||||
URL_SYS_STATISTIC_ONLINE
|
URL_SYS_STATISTIC_ONLINE,
|
||||||
|
URL_SYS_STATISTIC_ACTIVE
|
||||||
} from '@/constants/urls.constants'
|
} from '@/constants/urls.constants'
|
||||||
import request from '@/services/index'
|
import request from '@/services/index'
|
||||||
|
|
||||||
@@ -90,3 +91,6 @@ export const r_sys_statistic_storage = () => request.get<StorageInfoVo>(URL_SYS_
|
|||||||
|
|
||||||
export const r_sys_statistic_online = (param: OnlineInfoGetParam) =>
|
export const r_sys_statistic_online = (param: OnlineInfoGetParam) =>
|
||||||
request.get<OnlineInfoVo>(URL_SYS_STATISTIC_ONLINE, param)
|
request.get<OnlineInfoVo>(URL_SYS_STATISTIC_ONLINE, param)
|
||||||
|
|
||||||
|
export const r_sys_statistic_active = (param: ActiveInfoGetParam) =>
|
||||||
|
request.get<ActiveInfoVo>(URL_SYS_STATISTIC_ACTIVE, param)
|
||||||
|
|||||||
@@ -28,3 +28,11 @@ export const localTimeToUtc = (localTime: string) => {
|
|||||||
export const isPastTime = (utcTime: string) => {
|
export const isPastTime = (utcTime: string) => {
|
||||||
return moment.utc(utcTime).isBefore(moment.now())
|
return moment.utc(utcTime).isBefore(moment.now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const utcToMillisecond = (utcTime: string) => {
|
||||||
|
return moment.utc(utcTime).valueOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const millisecondToUtc = (millisecond: number) => {
|
||||||
|
return moment(millisecond).toISOString()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user