fix(): 修改整体布局

This commit is contained in:
yuj
2025-12-05 10:41:44 +08:00
parent d4dd299c90
commit c4cfc8f9a0
10 changed files with 139 additions and 118 deletions

View File

@@ -34,7 +34,7 @@
--bg-secondary: #ffffff;
--panel: #ffffff;
--panel-solid: #ffffff;
--soft: #f1f3f7;
--soft: #f8f9fc;
--text: #171a1f;
--text-secondary: #4d5562;
--muted: #6b7280;
@@ -51,8 +51,9 @@
--bad: #b6404a;
--border: #e5e7ef;
--border-subtle: #f0f1f5;
--shadow: 0 10px 25px rgba(10,16,25,.08);
--shadow-lg: 0 20px 40px rgba(10,16,25,.12);
--shadow: 0 2px 8px rgba(10,16,25,.04);
--shadow-md: 0 4px 12px rgba(10,16,25,.06);
--shadow-lg: 0 8px 20px rgba(10,16,25,.08);
--shadow-colored: 0 10px 30px rgba(102, 126, 234, 0.15);
--glow: 0 0 20px rgba(102, 126, 234, 0.2);
}
@@ -233,14 +234,67 @@ a{color:inherit; text-decoration:none}
background:var(--panel); color:var(--text); box-shadow:var(--shadow);
}
.content{padding:28px 24px; display:flex; flex-direction:column; gap:20px; flex:1; overflow-y:auto; min-height:0; box-sizing:border-box}
.content{
padding:32px 28px;
display:flex;
flex-direction:column;
gap:24px;
flex:1;
overflow-y:auto;
min-height:0;
box-sizing:border-box;
max-width: 1600px;
margin: 0 auto;
width: 100%;
}
/* 页面标题样式 */
.page-header {
margin-bottom: 8px;
}
.page-header h1 {
font-size: 24px;
font-weight: 700;
color: var(--text);
margin: 0 0 8px 0;
letter-spacing: -0.02em;
}
.page-header .page-description {
font-size: 14px;
color: var(--text-secondary);
margin: 0;
line-height: 1.5;
}
.panel{
background:var(--panel); border:1px solid var(--border); border-radius:16px; padding:18px; box-shadow:var(--shadow);
background:var(--panel);
border:1px solid var(--border);
border-radius:12px;
padding:20px 24px;
box-shadow:var(--shadow);
width:100%;
transition: box-shadow 0.2s ease;
}
.panel:hover {
box-shadow: var(--shadow-md);
}
.panel h2{
margin:0 0 12px 0;
font-size:16px;
font-weight:600;
color:var(--text);
}
.panel .desc{
color:var(--text-secondary);
font-size:13px;
margin-bottom:16px;
line-height: 1.5;
}
.panel h2{margin:0 0 8px 0; font-size:16px}
.panel .desc{color:var(--muted); font-size:13px; margin-bottom:10px}
.flex-2{display:grid; grid-template-columns: 1fr 1fr; gap:16px}
.flex-3{display:grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap:16px}
@@ -251,7 +305,7 @@ a{color:inherit; text-decoration:none}
background: linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.7) 100%);
border:1px solid rgba(102, 126, 234, 0.1);
border-radius:16px;
padding:18px;
padding:20px;
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
@@ -280,21 +334,22 @@ a{color:inherit; text-decoration:none}
.kpi:nth-child(2)::before { background: var(--success-gradient); }
.kpi:nth-child(3)::before { background: var(--warning-gradient); }
.kpi:nth-child(4)::before { background: var(--secondary-gradient); }
.kpi h3{margin:0; font-size:13px; color:var(--muted); font-weight: 600; letter-spacing: 0.3px;}
.kpi h3{margin:0; font-size:12px; color:var(--muted); font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase;}
.kpi .num{
font-size:28px;
font-weight:900;
margin-top:8px;
font-size:32px;
font-weight:800;
margin-top:12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -0.02em;
}
.kpi .delta{font-size:12px; margin-left:8px; padding:2px 6px; border-radius:8px; background:var(--panel); border:1px solid var(--border)}
.table{width:100%; border-collapse:collapse; font-size:14px; min-width:800px; table-layout:fixed; box-sizing:border-box}
.table th, .table td{padding:10px 12px; border-bottom:1px solid var(--border); text-align:left; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.table thead th{color:var(--muted); font-weight:600}
.table th, .table td{padding:12px 16px; border-bottom:1px solid var(--border); text-align:left; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.table thead th{color:var(--muted); font-weight:600; font-size:13px; text-transform: uppercase; letter-spacing: 0.5px;}
.table-container{overflow-x:auto; width:100%; max-width:100%; -webkit-overflow-scrolling:touch; box-sizing:border-box}
.table th:nth-child(1), .table td:nth-child(1){width:8%}
.table th:nth-child(2), .table td:nth-child(2){width:15%}

View File

@@ -165,6 +165,9 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
<DynamicMenu collapsed={collapsed} />
</div>
{/* 间隔区域 - 将登出按钮推到底部 */}
<div style={{ flex: 1 }}></div>
{/* 用户登录状态和登出按钮 */}
<div style={{
padding: collapsed ? '12px 8px' : '12px 16px',
@@ -214,16 +217,6 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
</>
)}
</div>
<div style={{
fontSize: '12px',
color: 'var(--muted)',
textAlign: collapsed ? 'center' : 'left',
padding: '12px 16px',
borderTop: '1px solid var(--border-subtle)'
}}>
{!collapsed ? 'v1.1 · React Admin' : 'v1.1'}
</div>
</aside>
{/* 主内容区 */}

View File

@@ -2,10 +2,12 @@ import React from 'react';
const Finance: React.FC = () => {
return (
<section className="panel">
<h2></h2>
<div className="desc"></div>
<div>
<div className="page-header">
<h1></h1>
<p className="page-description"></p>
</div>
<div className="kpis">
<div className="kpi">
<h3></h3>
@@ -40,7 +42,7 @@ const Finance: React.FC = () => {
<h2></h2>
<div className="chart"></div>
</div>
</section>
</div>
);
};

View File

@@ -64,7 +64,7 @@ const FinanceTransactionLogs: React.FC = () => {
style={{ marginBottom: 20 }}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
{/* 第一行用户ID、订单编号、流水类型、状态、时间范围 */}
{/* 第一行用户ID、订单编号、流水类型 */}
<div style={{ display: 'flex', gap: '16px', flexWrap: 'wrap', alignItems: 'center' }}>
<Form.Item name="user_id" label="用户PID:" style={{ margin: 0, flex: '0 0 auto' }}>
<Input allowClear placeholder="用户ID" style={{ width: 200 }} />
@@ -73,7 +73,7 @@ const FinanceTransactionLogs: React.FC = () => {
<Input allowClear placeholder="订单编号" style={{ width: 200 }} />
</Form.Item>
<Form.Item name="type" label="流水类型:" style={{ margin: 0, flex: '0 0 auto' }}>
<Select allowClear placeholder="充值/退款/消费" style={{ width: 220 }}
<Select allowClear placeholder="充值/退款/消费" style={{ width: 200 }}
options={[
{ label: 'recharge', value: 'recharge' },
{ label: 'refund', value: 'refund' },
@@ -81,8 +81,12 @@ const FinanceTransactionLogs: React.FC = () => {
]}
/>
</Form.Item>
</div>
{/* 第二行:状态、时间范围和按钮 */}
<div style={{ display: 'flex', gap: '16px', alignItems: 'center', flexWrap: 'wrap' }}>
<Form.Item name="status" label="状态:" style={{ margin: 0, flex: '0 0 auto' }}>
<Select allowClear placeholder="成功/失败/待定" style={{ width: 220 }}
<Select allowClear placeholder="成功/失败/待定" style={{ width: 200 }}
options={[
{ label: 'success', value: 'success' },
{ label: 'failed', value: 'failed' },
@@ -90,10 +94,6 @@ const FinanceTransactionLogs: React.FC = () => {
]}
/>
</Form.Item>
</div>
{/* 第二行:时间范围和按钮 */}
<div style={{ display: 'flex', gap: '16px', alignItems: 'center', flexWrap: 'wrap' }}>
<Form.Item name="range" label="时间范围:" style={{ margin: 0, flex: '0 0 auto' }}>
<DatePicker.RangePicker style={{ width: 300 }} />
</Form.Item>

View File

@@ -550,7 +550,7 @@ const InviteCodes: React.FC = () => {
];
return (
<div style={{ padding: '24px' }}>
<div>
<Card title="邀请码管理" style={{ marginBottom: 16 }}>
{/* 统计信息 */}
{statistics && (

View File

@@ -1,63 +1,21 @@
import React, { useState, useEffect } from 'react';
import { QuotaApiService } from '../services/api';
interface QuotaHistoryItem {
id: number;
user_id: string;
api_group: string;
project_id: string;
day: string;
account: string;
model: string;
quota_used: number;
quota_used_text?: string;
created_at: string;
}
import React from 'react';
const Monitoring: React.FC = () => {
const [quotaData, setQuotaData] = useState<QuotaHistoryItem[]>([]);
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
// 初始化日期范围最近30天
useEffect(() => {
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
setEndDate(today.toISOString().split('T')[0]);
setStartDate(thirtyDaysAgo.toISOString().split('T')[0]);
}, []);
// 计算统计数据
const stats = React.useMemo(() => {
if (quotaData.length === 0) {
return {
totalQuota: 0,
totalUsers: 0,
totalProjects: 0,
totalModels: 0,
};
}
const totalQuota = quotaData.reduce((sum, item) => sum + item.quota_used, 0);
const uniqueUsers = new Set(quotaData.map(item => item.user_id)).size;
const uniqueProjects = new Set(quotaData.map(item => item.project_id)).size;
const uniqueModels = new Set(quotaData.map(item => item.model)).size;
return {
totalQuota,
totalUsers: uniqueUsers,
totalProjects: uniqueProjects,
totalModels: uniqueModels,
};
}, [quotaData]);
// 静态统计数据
const stats = {
totalQuota: 0,
totalUsers: 0,
totalProjects: 0,
totalModels: 0,
};
return (
<section className="panel">
<h2>Token 使</h2>
<div className="desc">使</div>
<div>
<div className="page-header">
<h1>Token 使</h1>
<p className="page-description">使</p>
</div>
<div className="kpis">
<div className="kpi">
<h3> Token 使</h3>
@@ -76,7 +34,7 @@ const Monitoring: React.FC = () => {
<div className="num">{stats.totalModels}</div>
</div>
</div>
</section>
</div>
);
};

View File

@@ -5,12 +5,14 @@ import { useNavigate } from 'react-router-dom';
const Operations: React.FC = () => {
const navigate = useNavigate();
return (
<section className="panel">
<h2></h2>
<div className="desc">广</div>
<div style={{ margin: '16px 0' }}>
<Card>
<div>
<div className="page-header">
<h1></h1>
<p className="page-description">广</p>
</div>
<div style={{ marginBottom: '24px' }}>
<Card bordered={false}>
<Space wrap>
<Button type="primary" onClick={() => navigate('/mcp-provider-pricing')}>
MCP
@@ -44,7 +46,7 @@ const Operations: React.FC = () => {
<div className="chart"></div>
</div>
</div>
</section>
</div>
);
};

View File

@@ -2,10 +2,12 @@ import React from 'react';
const Overview: React.FC = () => {
return (
<section className="panel">
<h2></h2>
<div className="desc"></div>
<div>
<div className="page-header">
<h1></h1>
<p className="page-description"></p>
</div>
<div className="kpis">
<div className="kpi">
<h3></h3>
@@ -21,10 +23,10 @@ const Overview: React.FC = () => {
</div>
<div className="kpi">
<h3></h3>
<div className="num" style={{color: 'var(--success)'}}></div>
<div className="num" style={{color: 'var(--good)'}}></div>
</div>
</div>
<div className="flex-2">
<div className="panel">
<h2>7</h2>
@@ -35,7 +37,7 @@ const Overview: React.FC = () => {
<div className="chart">使</div>
</div>
</div>
</section>
</div>
);
};

View File

@@ -305,7 +305,7 @@ const UserFeedback: React.FC = () => {
];
return (
<div style={{ padding: '24px' }}>
<div>
<Title level={2}></Title>
{/* 统计卡片 */}

View File

@@ -378,8 +378,13 @@ const UserManagement: React.FC = () => {
return (
<div>
<div className="page-header">
<h1></h1>
<p className="page-description"></p>
</div>
{/* 统计卡片 */}
<Row gutter={16} style={{ marginBottom: 16 }}>
<Row gutter={16} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card>
<Statistic
@@ -423,15 +428,18 @@ const UserManagement: React.FC = () => {
</Row>
{/* 操作栏 */}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<h2></h2>
<Button type="primary" icon={<SettingOutlined />} onClick={handleOpenChangeRole}>
</Button>
</div>
<Card bordered={false} style={{ marginBottom: 16 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h3 style={{ margin: 0, fontSize: '16px', fontWeight: 600 }}></h3>
<Button type="primary" icon={<SettingOutlined />} onClick={handleOpenChangeRole}>
</Button>
</div>
</Card>
{/* 用户表格 */}
<Table
<Card bordered={false}>
<Table
columns={columns}
dataSource={users}
loading={loading}
@@ -449,7 +457,8 @@ const UserManagement: React.FC = () => {
fetchUsers(current, size);
}
}}
/>
/>
</Card>
{/* 用户表单弹窗 */}
<Modal