fix(): 修改整体布局
This commit is contained in:
83
src/App.css
83
src/App.css
@@ -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%}
|
||||
|
||||
@@ -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>
|
||||
|
||||
{/* 主内容区 */}
|
||||
|
||||
@@ -2,9 +2,11 @@ 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">
|
||||
@@ -40,7 +42,7 @@ const Finance: React.FC = () => {
|
||||
<h2>财务报表</h2>
|
||||
<div className="chart">详细财务报表占位</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -550,7 +550,7 @@ const InviteCodes: React.FC = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '24px' }}>
|
||||
<div>
|
||||
<Card title="邀请码管理" style={{ marginBottom: 16 }}>
|
||||
{/* 统计信息 */}
|
||||
{statistics && (
|
||||
|
||||
@@ -1,62 +1,20 @@
|
||||
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 {
|
||||
// 静态统计数据
|
||||
const stats = {
|
||||
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]);
|
||||
|
||||
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">
|
||||
@@ -76,7 +34,7 @@ const Monitoring: React.FC = () => {
|
||||
<div className="num">{stats.totalModels}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
<div className="page-header">
|
||||
<h1>运营管理</h1>
|
||||
<p className="page-description">用户运营、内容管理、活动推广</p>
|
||||
</div>
|
||||
|
||||
<div style={{ margin: '16px 0' }}>
|
||||
<Card>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ 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">
|
||||
@@ -21,7 +23,7 @@ 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>
|
||||
|
||||
@@ -35,7 +37,7 @@ const Overview: React.FC = () => {
|
||||
<div className="chart">模型使用量排行占位</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -305,7 +305,7 @@ const UserFeedback: React.FC = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '24px' }}>
|
||||
<div>
|
||||
<Title level={2}>用户反馈管理</Title>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
|
||||
@@ -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,14 +428,17 @@ const UserManagement: React.FC = () => {
|
||||
</Row>
|
||||
|
||||
{/* 操作栏 */}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
|
||||
<h2>用户管理</h2>
|
||||
<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>
|
||||
|
||||
{/* 用户表格 */}
|
||||
<Card bordered={false}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={users}
|
||||
@@ -450,6 +458,7 @@ const UserManagement: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 用户表单弹窗 */}
|
||||
<Modal
|
||||
|
||||
Reference in New Issue
Block a user