🎨 refactor: Refactor dashboard statistics cards and charts layout
- Consolidate 8 individual stat cards into 4 grouped cards: * Account Data (Current Balance, Historical Consumption) * Usage Statistics (Request Count, Statistics Count) * Resource Consumption (Statistics Quota, Statistics Tokens) * Performance Metrics (Average RPM, Average TPM) - Add gradient header backgrounds with white text for card titles: * Blue gradient for Account Data * Green gradient for Usage Statistics * Yellow gradient for Resource Consumption * Pink gradient for Performance Metrics - Implement mini trend charts using real API data: * Replace mock data with actual time-series data from API * Hide x and y axes to show pure trend lines * Display trends only for metrics with available historical data * Remove trend charts for Current Balance, Historical Consumption, and Request Count - Merge model analysis charts into single card: * Combine "Model Consumption Distribution" and "Model Call Count Ratio" * Use responsive grid layout (vertical on mobile, horizontal on desktop) * Update card title to "Model Data Analysis" - Optimize chart configurations: * Hide axes, legends, and tooltips for mini trend charts * Maintain color consistency between metrics and trend lines * Improve performance by processing all trend data in single API call
This commit is contained in:
@@ -1638,7 +1638,8 @@ const ChannelsTable = () => {
|
||||
<Card
|
||||
className="!rounded-2xl overflow-hidden"
|
||||
title={renderHeader()}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={getVisibleColumns()}
|
||||
|
||||
@@ -1259,7 +1259,8 @@ const LogsTable = () => {
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={getVisibleColumns()}
|
||||
|
||||
@@ -864,7 +864,8 @@ const LogsTable = () => {
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={getVisibleColumns()}
|
||||
|
||||
@@ -241,7 +241,7 @@ const ModelPricing = () => {
|
||||
let price = parseFloat(text) * groupRatio[selectedGroup];
|
||||
content = (
|
||||
<div className="text-gray-700">
|
||||
${t('模型价格')}:${price.toFixed(3)}
|
||||
{t('模型价格')}:${price.toFixed(3)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -451,7 +451,7 @@ const ModelPricing = () => {
|
||||
|
||||
// 搜索和操作区组件
|
||||
const SearchAndActions = useMemo(() => (
|
||||
<Card className="!rounded-xl mb-6" shadows='hover'>
|
||||
<Card className="!rounded-xl mb-6" bordered={false}>
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<Input
|
||||
@@ -482,7 +482,7 @@ const ModelPricing = () => {
|
||||
|
||||
// 表格组件
|
||||
const ModelTable = useMemo(() => (
|
||||
<Card className="!rounded-xl overflow-hidden" shadows='hover'>
|
||||
<Card className="!rounded-xl overflow-hidden" bordered={false}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={filteredModels}
|
||||
|
||||
@@ -501,7 +501,8 @@ const RedemptionsTable = () => {
|
||||
<Card
|
||||
className="!rounded-2xl overflow-hidden"
|
||||
title={renderHeader()}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={columns}
|
||||
|
||||
@@ -702,7 +702,8 @@ const LogsTable = () => {
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={getVisibleColumns()}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useState, useContext } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
API,
|
||||
copy,
|
||||
@@ -21,8 +20,6 @@ import {
|
||||
Table,
|
||||
Tag,
|
||||
Input,
|
||||
Divider,
|
||||
Avatar,
|
||||
} from '@douyinfe/semi-ui';
|
||||
|
||||
import {
|
||||
@@ -36,13 +33,9 @@ import {
|
||||
IconStop,
|
||||
IconPlay,
|
||||
IconMore,
|
||||
IconMoneyExchangeStroked,
|
||||
IconHistogram,
|
||||
IconRotate,
|
||||
} from '@douyinfe/semi-icons';
|
||||
import EditToken from '../../pages/Token/EditToken';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { UserContext } from '../../context/User';
|
||||
|
||||
function renderTimestamp(timestamp) {
|
||||
return <>{timestamp2string(timestamp)}</>;
|
||||
@@ -50,8 +43,6 @@ function renderTimestamp(timestamp) {
|
||||
|
||||
const TokensTable = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [userState, userDispatch] = useContext(UserContext);
|
||||
|
||||
const renderStatus = (status, model_limits_enabled = false) => {
|
||||
switch (status) {
|
||||
@@ -431,26 +422,9 @@ const TokensTable = () => {
|
||||
window.open(url, '_blank');
|
||||
};
|
||||
|
||||
// 获取用户数据
|
||||
const getUserData = async () => {
|
||||
try {
|
||||
const res = await API.get(`/api/user/self`);
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
userDispatch({ type: 'login', payload: data });
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户数据失败:', error);
|
||||
showError(t('获取用户数据失败'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// 获取用户数据以确保显示正确的余额和使用量
|
||||
getUserData();
|
||||
|
||||
loadTokens(0)
|
||||
.then()
|
||||
.catch((reason) => {
|
||||
@@ -574,71 +548,6 @@ const TokensTable = () => {
|
||||
|
||||
const renderHeader = () => (
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Card
|
||||
shadows='hover'
|
||||
className="bg-blue-50 border-0 !rounded-2xl w-full"
|
||||
headerLine={false}
|
||||
onClick={() => navigate('/console/topup')}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Avatar
|
||||
className="mr-3"
|
||||
size="medium"
|
||||
color="blue"
|
||||
>
|
||||
<IconMoneyExchangeStroked size="large" />
|
||||
</Avatar>
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{t('当前余额')}</div>
|
||||
<div className="text-xl font-semibold">{renderQuota(userState?.user?.quota)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
shadows='hover'
|
||||
className="bg-purple-50 border-0 !rounded-2xl w-full"
|
||||
headerLine={false}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Avatar
|
||||
className="mr-3"
|
||||
size="medium"
|
||||
color="purple"
|
||||
>
|
||||
<IconHistogram size="large" />
|
||||
</Avatar>
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{t('累计消费')}</div>
|
||||
<div className="text-xl font-semibold">{renderQuota(userState?.user?.used_quota)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
shadows='hover'
|
||||
className="bg-green-50 border-0 !rounded-2xl w-full"
|
||||
headerLine={false}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Avatar
|
||||
className="mr-3"
|
||||
size="medium"
|
||||
color="green"
|
||||
>
|
||||
<IconRotate size="large" />
|
||||
</Avatar>
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{t('请求次数')}</div>
|
||||
<div className="text-xl font-semibold">{userState?.user?.request_count || 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Divider margin="12px" />
|
||||
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
||||
<div className="flex gap-2 w-full md:w-auto order-2 md:order-1">
|
||||
<Button
|
||||
@@ -723,7 +632,8 @@ const TokensTable = () => {
|
||||
<Card
|
||||
className="!rounded-2xl overflow-hidden"
|
||||
title={renderHeader()}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={columns}
|
||||
|
||||
@@ -551,7 +551,8 @@ const UsersTable = () => {
|
||||
<Card
|
||||
className="!rounded-2xl overflow-hidden"
|
||||
title={renderHeader()}
|
||||
shadows='hover'
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
>
|
||||
<Table
|
||||
columns={columns}
|
||||
|
||||
Reference in New Issue
Block a user