fix(style): chart style

This commit is contained in:
jialin
2026-05-18 20:14:28 +08:00
committed by jialin
parent abc3c2c25a
commit bcea46fa8f
10 changed files with 208 additions and 99 deletions
+14 -2
View File
@@ -1,7 +1,7 @@
import useCoolColors from '@/hooks/use-cool-colors';
import { Chart } from '@gpustack/core-ui';
import { formatLargeNumber } from '@gpustack/core-ui/utils';
import { theme } from 'antd';
import { Empty, theme } from 'antd';
import _ from 'lodash';
import React, { useEffect, useMemo, useRef } from 'react';
@@ -254,7 +254,19 @@ const BarChart: React.FC<BarChartProps> = (props) => {
}, [legendIsolate, seriesData, legendData]);
if (!seriesData.length) {
return <div style={{ width: width || '100%', height }} />;
return (
<div
style={{
width: width || '100%',
height,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</div>
);
}
return (
+36 -7
View File
@@ -1,4 +1,3 @@
import useQueryBreakdownList from '@/pages/usage/services/use-query-breakdown-list';
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
import { PageTools } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
@@ -6,7 +5,13 @@ import { Col, Row } from 'antd';
import dayjs from 'dayjs';
import { useEffect, useMemo } from 'react';
import styled from 'styled-components';
import { DashboardUsageCommonParams } from '../config';
import {
DashboardUsageCommonParams,
getUsageRankHeight,
getUsageRankSlotCount
} from '../config';
import useTopTokenUsageByApiKey from '../hooks/use-top-token-usage-by-api-key';
import useTopTokenUsageByUser from '../hooks/use-top-token-usage-by-user';
import ApiRequestsByModel from './usage-charts/api-requests-by-model';
import TokenUsageByModel from './usage-charts/token-usage-by-model';
import TopTokenUsageByApiKey from './usage-charts/top-token-usage-by-api-key';
@@ -21,9 +26,6 @@ const NewUsage = () => {
const tokenByModelQuery = useQueryTimeSeriesData({
key: 'tokenUsageByModelData'
});
const activeUsersQuery = useQueryBreakdownList({
key: 'dashboardActiveUsersData'
});
const dateRange = useMemo(
() => ({
@@ -58,6 +60,23 @@ const NewUsage = () => {
fetchData().catch(() => {});
}, [commonParams, dateRange]);
const userUsage = useTopTokenUsageByUser(commonParams);
const apiKeyUsage = useTopTokenUsageByApiKey(commonParams);
const userCount = userUsage.rankData.names.length;
const apiKeyCount = apiKeyUsage.rankData.names.length;
const rankHeight = Math.max(
getUsageRankHeight(userCount),
getUsageRankHeight(apiKeyCount)
);
const rankMaxItems = Math.max(
getUsageRankSlotCount(rankHeight),
userCount,
apiKeyCount
);
return (
<>
<PageTools
@@ -84,10 +103,20 @@ const NewUsage = () => {
<Section>
<Row gutter={[20, 20]}>
<Col xs={24} sm={24} md={24} lg={12}>
<TopTokenUsageByUser commonParams={commonParams} height={460} />
<TopTokenUsageByUser
rankData={userUsage.rankData}
loading={userUsage.loading}
height={rankHeight}
maxItems={rankMaxItems}
/>
</Col>
<Col xs={24} sm={24} md={24} lg={12}>
<TopTokenUsageByApiKey commonParams={commonParams} height={460} />
<TopTokenUsageByApiKey
rankData={apiKeyUsage.rankData}
loading={apiKeyUsage.loading}
height={rankHeight}
maxItems={rankMaxItems}
/>
</Col>
</Row>
</Section>
@@ -1,72 +1,39 @@
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
import { HBarChart } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { useEffect, useMemo } from 'react';
import {
baseColorMap,
DashboardUsageCommonParams,
toUsageRankData,
usageChartHeight
} from '../../config';
import { usageChartHeight, usageRankMaxItems } from '../../config';
import type useTopTokenUsageByApiKey from '../../hooks/use-top-token-usage-by-api-key';
import UsageChartCard, { ChartLoadingBox } from './shared';
type RankData = ReturnType<typeof useTopTokenUsageByApiKey>['rankData'];
interface TopTokenUsageByApiKeyProps {
commonParams: DashboardUsageCommonParams;
rankData: RankData;
loading: boolean;
height?: number;
maxItems?: number;
}
const TopTokenUsageByApiKey: React.FC<TopTokenUsageByApiKeyProps> = ({
commonParams,
height = usageChartHeight
rankData,
loading,
height = usageChartHeight,
maxItems = usageRankMaxItems
}) => {
const intl = useIntl();
const query = useQueryTimeSeriesData({
key: 'topTokenUsageByApiKeyData'
});
const tokenUsageText = intl.formatMessage({ id: 'dashboard.tokens' });
useEffect(() => {
query
.fetchData({
...commonParams,
metric: 'total_tokens',
group_by: ['date', 'api_key'],
page: 1,
perPage: 10,
sort_by: '-total_tokens'
})
.catch(() => {});
}, [commonParams]);
const rankData = useMemo(() => {
const data = toUsageRankData(
query.detailData,
'api_key',
tokenUsageText,
baseColorMap.baseR3
);
return {
names: data.names.filter((name) => name !== '-'),
series: data.series.map((item) => ({
...item,
data: item.data.filter((point) => point.name !== '-')
}))
};
}, [query.detailData, tokenUsageText]);
return (
<UsageChartCard
title={intl.formatMessage({ id: 'dashboard.topTokenUsageByApiKey' })}
height={height + 52}
>
{query.loading && rankData.names.length === 0 ? (
{loading && rankData.names.length === 0 ? (
<ChartLoadingBox height={height} />
) : (
<HBarChart
seriesData={rankData.series}
xAxisData={rankData.names}
height={height}
maxItems={10}
maxItems={maxItems}
/>
)}
</UsageChartCard>
@@ -1,67 +1,39 @@
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
import { HBarChart } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { useEffect, useMemo } from 'react';
import {
baseColorMap,
DashboardUsageCommonParams,
toUsageRankData,
usageChartHeight
} from '../../config';
import { usageChartHeight, usageRankMaxItems } from '../../config';
import type useTopTokenUsageByUser from '../../hooks/use-top-token-usage-by-user';
import UsageChartCard, { ChartLoadingBox } from './shared';
type RankData = ReturnType<typeof useTopTokenUsageByUser>['rankData'];
interface TopTokenUsageByUserProps {
commonParams: DashboardUsageCommonParams;
rankData: RankData;
loading: boolean;
height?: number;
maxItems?: number;
}
const TopTokenUsageByUser: React.FC<TopTokenUsageByUserProps> = ({
commonParams,
height = usageChartHeight
rankData,
loading,
height = usageChartHeight,
maxItems = usageRankMaxItems
}) => {
const intl = useIntl();
const query = useQueryTimeSeriesData({
key: 'topTokenUsageByUserData'
});
const tokenUsageText = intl.formatMessage({ id: 'dashboard.tokens' });
useEffect(() => {
query
.fetchData({
...commonParams,
metric: 'total_tokens',
group_by: ['date', 'user'],
page: 1,
perPage: 10,
sort_by: '-total_tokens'
})
.catch(() => {});
}, [commonParams]);
const rankData = useMemo(
() =>
toUsageRankData(
query.detailData,
'user',
tokenUsageText,
baseColorMap.base
),
[query.detailData, tokenUsageText]
);
return (
<UsageChartCard
title={intl.formatMessage({ id: 'dashboard.topTokenUsageByUser' })}
height={height + 52}
>
{query.loading && rankData.names.length === 0 ? (
{loading && rankData.names.length === 0 ? (
<ChartLoadingBox height={height} />
) : (
<HBarChart
seriesData={rankData.series}
xAxisData={rankData.names}
height={height}
maxItems={10}
maxItems={maxItems}
/>
)}
</UsageChartCard>
+18
View File
@@ -60,6 +60,24 @@ export interface DashboardUsageCommonParams {
export const usageChartHeight = 300;
export const usageChartCardHeight = usageChartHeight + 52;
export const usageRankMaxItems = 10;
export const usageRankDefaultHeight = 460;
export const usageRankMinHeight = 300;
export const usageRankItemHeight = 46;
export const getUsageRankHeight = (count: number) => {
const safeCount = Math.min(Math.max(count, 0), usageRankMaxItems);
return Math.max(
usageRankMinHeight,
usageRankDefaultHeight -
(usageRankMaxItems - safeCount) * usageRankItemHeight
);
};
export const getUsageRankSlotCount = (height: number) => {
return Math.max(Math.floor(height / usageRankItemHeight), 1);
};
export const buildUsageLabel = (item: BreakdownItem, groupBy: UsageGroupBy) => {
const rawItem = item as any;
const groupItem = rawItem[groupBy];
@@ -0,0 +1,52 @@
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
import { useIntl } from '@umijs/max';
import { useEffect, useMemo } from 'react';
import {
baseColorMap,
DashboardUsageCommonParams,
toUsageRankData
} from '../config';
export default function useTopTokenUsageByApiKey(
commonParams: DashboardUsageCommonParams
) {
const intl = useIntl();
const query = useQueryTimeSeriesData({
key: 'topTokenUsageByApiKeyData'
});
const tokenUsageText = intl.formatMessage({ id: 'dashboard.tokens' });
useEffect(() => {
query
.fetchData({
...commonParams,
metric: 'total_tokens',
group_by: ['date', 'api_key'],
page: 1,
perPage: 10,
sort_by: '-total_tokens'
})
.catch(() => {});
}, [commonParams]);
const rankData = useMemo(() => {
const data = toUsageRankData(
query.detailData,
'api_key',
tokenUsageText,
baseColorMap.baseR3
);
return {
names: data.names.filter((name) => name !== '-'),
series: data.series.map((item) => ({
...item,
data: item.data.filter((point) => point.name !== '-')
}))
};
}, [query.detailData, tokenUsageText]);
return {
rankData,
loading: query.loading
};
}
@@ -0,0 +1,47 @@
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
import { useIntl } from '@umijs/max';
import { useEffect, useMemo } from 'react';
import {
baseColorMap,
DashboardUsageCommonParams,
toUsageRankData
} from '../config';
export default function useTopTokenUsageByUser(
commonParams: DashboardUsageCommonParams
) {
const intl = useIntl();
const query = useQueryTimeSeriesData({
key: 'topTokenUsageByUserData'
});
const tokenUsageText = intl.formatMessage({ id: 'dashboard.tokens' });
useEffect(() => {
query
.fetchData({
...commonParams,
metric: 'total_tokens',
group_by: ['date', 'user'],
page: 1,
perPage: 10,
sort_by: '-total_tokens'
})
.catch(() => {});
}, [commonParams]);
const rankData = useMemo(
() =>
toUsageRankData(
query.detailData,
'user',
tokenUsageText,
baseColorMap.base
),
[query.detailData, tokenUsageText]
);
return {
rankData,
loading: query.loading
};
}
@@ -69,7 +69,11 @@ const useModelsColumns = (): Array<{
)
},
{
title: intl.formatMessage({ id: 'usage.table.inputTokensCached' }),
title: (
<AutoTooltip ghost>
{intl.formatMessage({ id: 'usage.table.inputTokensCached' })}
</AutoTooltip>
),
dataIndex: 'input_cached_tokens',
key: 'input_cached_tokens',
sorter: true,
+5 -1
View File
@@ -81,7 +81,11 @@ const useModelsColumns = (): Array<{
)
},
{
title: intl.formatMessage({ id: 'usage.table.inputTokensCached' }),
title: (
<AutoTooltip ghost>
{intl.formatMessage({ id: 'usage.table.inputTokensCached' })}
</AutoTooltip>
),
dataIndex: 'input_cached_tokens',
key: 'input_cached_tokens',
sorter: true,
+5 -1
View File
@@ -78,7 +78,11 @@ const useModelsColumns = (): Array<{
)
},
{
title: intl.formatMessage({ id: 'usage.table.inputTokensCached' }),
title: (
<AutoTooltip ghost>
{intl.formatMessage({ id: 'usage.table.inputTokensCached' })}
</AutoTooltip>
),
dataIndex: 'input_cached_tokens',
key: 'input_cached_tokens',
sorter: true,