fix(): 修改布局

This commit is contained in:
yuj
2025-12-04 20:54:44 +08:00
parent b4e0470b6e
commit b67a7cae2e
6 changed files with 616 additions and 115 deletions

3
.vite/deps/package.json Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -29,32 +29,32 @@
box-shadow: 0 8px 20px rgba(0,0,0,0.25);
}
.form-split { color: var(--muted); }
:root{ /* 深色科技风主题 - 参考 x.ai */
--bg: #0a0a0f;
--bg-secondary: #12121a;
--panel: rgba(20, 20, 30, 0.6);
--panel-solid: #14141e;
--soft: rgba(30, 30, 45, 0.5);
--text: #e8e8f0;
--text-secondary: #a0a0b0;
--muted: #6a6a7a;
--accent: #7c5cff;
--accent-glow: rgba(124, 92, 255, 0.3);
--accent-gradient: linear-gradient(135deg, #7c5cff 0%, #a084ff 100%);
--primary-gradient: linear-gradient(135deg, #7c5cff 0%, #5d3fd3 100%);
:root{ /* 浅色主题 */
--bg: #f5f7fb;
--bg-secondary: #ffffff;
--panel: #ffffff;
--panel-solid: #ffffff;
--soft: #f1f3f7;
--text: #171a1f;
--text-secondary: #4d5562;
--muted: #6b7280;
--accent: #5f97d2;
--accent-glow: rgba(95, 151, 210, 0.2);
--accent-gradient: linear-gradient(135deg, #5f97d2 0%, #4a7bb8 100%);
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--secondary-gradient: linear-gradient(135deg, #ff6b9d 0%, #c06cf5 100%);
--success-gradient: linear-gradient(135deg, #00d4ff 0%, #0088ff 100%);
--warning-gradient: linear-gradient(135deg, #ff9f1c 0%, #ffbf69 100%);
--info-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--good: #00d4aa;
--warn: #ffb347;
--bad: #ff6b9d;
--border: rgba(124, 92, 255, 0.15);
--border-subtle: rgba(255, 255, 255, 0.05);
--shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 20px 60px rgba(0, 0, 0, 0.6);
--shadow-colored: 0 10px 30px rgba(124, 92, 255, 0.3);
--glow: 0 0 20px rgba(124, 92, 255, 0.4);
--good: #2fb167;
--warn: #caa410;
--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-colored: 0 10px 30px rgba(102, 126, 234, 0.15);
--glow: 0 0 20px rgba(102, 126, 234, 0.2);
}
:root.light{
--bg: #f5f7fb;
@@ -83,42 +83,6 @@ body{
overflow:hidden;
position: relative;
}
/* 科技感网格背景 - 类似 x.ai */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(124, 92, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(124, 92, 255, 0.03) 1px, transparent 1px);
background-size: 50px 50px;
pointer-events: none;
z-index: 0;
}
/* 光晕效果 */
body::after {
content: '';
position: fixed;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(
circle at 50% 50%,
rgba(124, 92, 255, 0.08) 0%,
transparent 50%
);
pointer-events: none;
z-index: 0;
animation: pulse 8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.5; transform: scale(1); }
50% { opacity: 1; transform: scale(1.1); }
}
a{color:inherit; text-decoration:none}
.container{display:flex; flex:1; width:100vw; height:100vh; max-width:none; margin:0; padding:0}
@@ -346,6 +310,30 @@ a{color:inherit; text-decoration:none}
.hidden{display:none}
/* Ant Design Spin 加载组件样式 */
.ant-spin {
color: var(--text) !important;
}
.ant-spin-text {
color: var(--text) !important;
display: inline-block !important;
white-space: nowrap !important;
writing-mode: horizontal-tb !important;
text-orientation: mixed !important;
}
.ant-spin-dot {
color: var(--accent) !important;
}
.ant-spin-spinning {
display: flex !important;
flex-direction: column !important;
align-items: center !important;
gap: 12px !important;
}
@media (max-width: 1200px){
.kpis{grid-template-columns: repeat(2, minmax(0,1fr));}
.flex-3{grid-template-columns: repeat(2, minmax(0,1fr));}

View File

@@ -59,6 +59,500 @@
border: 1px solid var(--border);
}
/* Card 深色主题 */
.ant-card {
background: var(--panel) !important;
border: 1px solid var(--border) !important;
color: var(--text) !important;
backdrop-filter: blur(20px);
}
.ant-card-head {
background: transparent !important;
border-bottom: 1px solid var(--border) !important;
color: var(--text) !important;
}
.ant-card-head-title {
color: var(--text) !important;
}
.ant-card-body {
color: var(--text) !important;
}
/* Table 深色主题 */
.ant-table {
background: var(--panel) !important;
color: var(--text) !important;
}
.ant-table-thead > tr > th {
background: var(--soft) !important;
border-bottom: 1px solid var(--border) !important;
color: var(--text-secondary) !important;
}
.ant-table-tbody > tr {
background: var(--panel) !important;
}
.ant-table-tbody > tr > td {
border-bottom: 1px solid var(--border-subtle) !important;
color: var(--text) !important;
background: transparent !important;
}
.ant-table-tbody > tr:hover > td {
background: rgba(124, 92, 255, 0.08) !important;
}
.ant-table-wrapper {
background: var(--panel) !important;
}
.ant-table-container {
border: 1px solid var(--border) !important;
background: var(--panel) !important;
}
.ant-table-tbody {
background: var(--panel) !important;
}
.ant-table-cell {
background: transparent !important;
}
.ant-table-placeholder {
background: var(--panel) !important;
}
.ant-table-placeholder .ant-empty-description {
color: var(--muted) !important;
}
.ant-table-pagination {
background: transparent !important;
}
/* 表格内容区域强制深色 */
.ant-table-content {
background: var(--panel) !important;
}
.ant-table-body {
background: var(--panel) !important;
}
/* 修复表格加载时的闪烁 - 更全面的覆盖 */
.ant-spin-container {
background: var(--panel) !important;
}
.ant-spin-nested-loading {
background: var(--panel) !important;
}
.ant-spin-nested-loading > div > .ant-spin {
background: transparent !important;
}
.ant-spin-blur {
background: var(--panel) !important;
}
.ant-spin-blur::after {
background: var(--panel) !important;
opacity: 0.4 !important;
}
/* 确保表格区域始终是深色 */
.ant-table-wrapper .ant-table {
background: var(--panel) !important;
}
.ant-table-wrapper .ant-table-container {
background: var(--panel) !important;
}
.ant-table-wrapper .ant-table-content {
background: var(--panel) !important;
}
.ant-table-wrapper .ant-table-body {
background: var(--panel) !important;
}
/* 表格所有可能的子元素都设置深色背景 */
.ant-table-wrapper .ant-spin-nested-loading,
.ant-table-wrapper .ant-spin-container {
background: var(--panel) !important;
}
/* Card 内的表格 */
.ant-card .ant-table-wrapper,
.ant-card .ant-table,
.ant-card .ant-table-container,
.ant-card .ant-table-content,
.ant-card .ant-spin-container,
.ant-card .ant-spin-nested-loading {
background: var(--panel) !important;
}
.ant-pagination-item {
background: var(--soft) !important;
border: 1px solid var(--border) !important;
}
.ant-pagination-item a {
color: var(--text) !important;
}
.ant-pagination-item-active {
background: var(--accent) !important;
border-color: var(--accent) !important;
}
.ant-pagination-item-active a {
color: white !important;
}
/* Form 深色主题 */
.ant-form-item-label > label {
color: var(--text) !important;
}
.ant-input,
.ant-input-affix-wrapper,
.ant-input-number,
.ant-select-selector,
.ant-picker {
background: var(--soft) !important;
border: 1px solid var(--border) !important;
color: var(--text) !important;
}
.ant-input::placeholder,
.ant-input-affix-wrapper input::placeholder {
color: var(--muted) !important;
}
.ant-input:hover,
.ant-input-affix-wrapper:hover,
.ant-input-number:hover,
.ant-select-selector:hover,
.ant-picker:hover {
border-color: var(--accent) !important;
}
.ant-input:focus,
.ant-input-affix-wrapper:focus,
.ant-input-affix-wrapper-focused,
.ant-input-number:focus,
.ant-select-focused .ant-select-selector,
.ant-picker-focused {
border-color: var(--accent) !important;
box-shadow: 0 0 0 2px rgba(124, 92, 255, 0.1) !important;
}
/* Select 深色主题 */
.ant-select-dropdown {
background: var(--panel-solid) !important;
border: 1px solid var(--border) !important;
}
.ant-select-item {
color: var(--text) !important;
}
.ant-select-item-option-selected {
background: rgba(124, 92, 255, 0.15) !important;
}
.ant-select-item-option-active {
background: rgba(124, 92, 255, 0.1) !important;
}
.ant-select-arrow {
color: var(--text) !important;
}
.ant-select-clear {
background: var(--soft) !important;
color: var(--text) !important;
}
/* Button 深色主题 */
.ant-btn {
border: 1px solid var(--border) !important;
background: var(--soft) !important;
color: var(--text) !important;
}
.ant-btn:hover {
border-color: var(--accent) !important;
background: rgba(124, 92, 255, 0.1) !important;
color: var(--accent) !important;
}
.ant-btn-primary {
background: var(--primary-gradient) !important;
border: none !important;
color: white !important;
}
.ant-btn-primary:hover {
background: linear-gradient(135deg, #9075ff 0%, #7051e0 100%) !important;
box-shadow: 0 0 20px rgba(124, 92, 255, 0.4) !important;
}
.ant-btn-link {
color: var(--accent) !important;
border: none !important;
background: transparent !important;
}
.ant-btn-link:hover {
color: #9075ff !important;
}
/* Modal 深色主题 */
.ant-modal-content {
background: var(--panel-solid) !important;
border: 1px solid var(--border) !important;
}
.ant-modal-header {
background: transparent !important;
border-bottom: 1px solid var(--border) !important;
}
.ant-modal-title {
color: var(--text) !important;
}
.ant-modal-body {
color: var(--text) !important;
}
.ant-modal-footer {
border-top: 1px solid var(--border) !important;
}
.ant-modal-close {
color: var(--text) !important;
}
.ant-modal-close:hover {
color: var(--accent) !important;
}
/* Drawer 深色主题 */
.ant-drawer-content {
background: var(--panel-solid) !important;
}
.ant-drawer-header {
background: transparent !important;
border-bottom: 1px solid var(--border) !important;
}
.ant-drawer-title {
color: var(--text) !important;
}
.ant-drawer-body {
color: var(--text) !important;
background: var(--panel-solid) !important;
}
.ant-drawer-close {
color: var(--text) !important;
}
.ant-drawer-close:hover {
color: var(--accent) !important;
}
/* DatePicker 深色主题 */
.ant-picker-panel-container {
background: var(--panel-solid) !important;
border: 1px solid var(--border) !important;
}
.ant-picker-header {
color: var(--text) !important;
border-bottom: 1px solid var(--border) !important;
}
.ant-picker-content th {
color: var(--text-secondary) !important;
}
.ant-picker-cell {
color: var(--text) !important;
}
.ant-picker-cell:hover .ant-picker-cell-inner {
background: rgba(124, 92, 255, 0.1) !important;
}
.ant-picker-cell-selected .ant-picker-cell-inner {
background: var(--accent) !important;
}
.ant-picker-today-btn {
color: var(--accent) !important;
}
/* Tag 深色主题 */
.ant-tag {
background: var(--soft) !important;
border: 1px solid var(--border) !important;
color: var(--text) !important;
}
.ant-tag-success {
background: rgba(0, 212, 170, 0.15) !important;
border-color: var(--good) !important;
color: var(--good) !important;
}
.ant-tag-processing {
background: rgba(102, 126, 234, 0.15) !important;
border-color: #667eea !important;
color: #667eea !important;
}
.ant-tag-error {
background: rgba(255, 107, 157, 0.15) !important;
border-color: var(--bad) !important;
color: var(--bad) !important;
}
.ant-tag-warning {
background: rgba(255, 179, 71, 0.15) !important;
border-color: var(--warn) !important;
color: var(--warn) !important;
}
/* Divider 深色主题 */
.ant-divider {
border-color: var(--border) !important;
}
/* Typography 深色主题 */
.ant-typography {
color: var(--text) !important;
}
.ant-typography-secondary {
color: var(--text-secondary) !important;
}
/* Statistic 深色主题 */
.ant-statistic-title {
color: var(--text-secondary) !important;
}
.ant-statistic-content {
color: var(--text) !important;
}
/* Message 深色主题 */
.ant-message-notice-content {
background: var(--panel-solid) !important;
border: 1px solid var(--border) !important;
color: var(--text) !important;
}
/* Empty 深色主题 */
.ant-empty-description {
color: var(--muted) !important;
}
/* Spin 深色主题 */
.ant-spin-text {
color: var(--text) !important;
}
/* Switch 深色主题 */
.ant-switch {
background: var(--soft) !important;
}
.ant-switch-checked {
background: var(--accent) !important;
}
/* Checkbox & Radio 深色主题 */
.ant-checkbox-wrapper,
.ant-radio-wrapper {
color: var(--text) !important;
}
.ant-checkbox-inner,
.ant-radio-inner {
background: var(--soft) !important;
border-color: var(--border) !important;
}
.ant-checkbox-checked .ant-checkbox-inner,
.ant-radio-checked .ant-radio-inner {
background: var(--accent) !important;
border-color: var(--accent) !important;
}
/* Dropdown 深色主题 */
.ant-dropdown-menu {
background: var(--panel-solid) !important;
border: 1px solid var(--border) !important;
}
.ant-dropdown-menu-item {
color: var(--text) !important;
}
.ant-dropdown-menu-item:hover {
background: rgba(124, 92, 255, 0.1) !important;
}
/* Tabs 深色主题 */
.ant-tabs-nav {
background: transparent !important;
}
.ant-tabs-tab {
color: var(--text-secondary) !important;
}
.ant-tabs-tab:hover {
color: var(--accent) !important;
}
.ant-tabs-tab-active {
color: var(--accent) !important;
}
.ant-tabs-ink-bar {
background: var(--accent) !important;
}
/* Alert 深色主题 */
.ant-alert {
background: var(--soft) !important;
border: 1px solid var(--border) !important;
}
.ant-alert-message {
color: var(--text) !important;
}
.ant-alert-description {
color: var(--text-secondary) !important;
}
/* 容器层级 */
.container {
position: relative;

View File

@@ -3,7 +3,6 @@ import { createRoot } from 'react-dom/client'
import { Provider } from 'jotai'
import { BrowserRouter } from 'react-router-dom'
import './App.css'
import './darkTheme.css'
import 'antd/dist/reset.css'
import App from './App.tsx'
import AuthGuard from './components/AuthGuard'

View File

@@ -33,27 +33,27 @@ const FinanceTokenUsage: React.FC = () => {
useEffect(() => { fetchList(); }, []);
const columns: ColumnsType<any> = useMemo(() => ([
{ title: '用户id', dataIndex: 'user_id', key: 'user_id' },
{ title: 'API分组', dataIndex: 'api_group', key: 'api_group' },
{ title: '对话id', dataIndex: 'project_id', key: 'project_id' },
{ title: '日期', dataIndex: 'day', key: 'day' },
{ title: '小时', dataIndex: 'hour', key: 'hour' },
{ title: '服务商', dataIndex: 'provider', key: 'provider' },
{ title: '账号', dataIndex: 'account', key: 'account' },
{ title: '模型', dataIndex: 'model', key: 'model' },
{ title: '代理类型', dataIndex: 'agent_type', key: 'agent_type' },
{ title: '任务id', dataIndex: 'chat_id', key: 'chat_id' },
{ title: '花费金额(总)', dataIndex: 'cost', key: 'cost' },
{ title: '提示Tokens', dataIndex: 'prompt_token', key: 'prompt_token' },
{ title: '输出Tokens', dataIndex: 'completion_token', key: 'completion_token' },
{ title: '缓存创建Tokens', dataIndex: 'cache_create_token', key: 'cache_create_token' },
{ title: '缓存命中Tokens', dataIndex: 'cache_read_token', key: 'cache_read_token' },
{ title: '提示花费', dataIndex: 'prompt_cost', key: 'prompt_cost' },
{ title: '输出花费', dataIndex: 'completion_cost', key: 'completion_cost' },
{ title: '缓存创建花费', dataIndex: 'cache_create_cost', key: 'cache_create_cost' },
{ title: '缓存命花费', dataIndex: 'cache_read_cost', key: 'cache_read_cost' },
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at' },
{ title: '修改时间', dataIndex: 'updated_at', key: 'updated_at' },
{ title: '用户id', dataIndex: 'user_id', key: 'user_id', width: 100 },
{ title: 'API分组', dataIndex: 'api_group', key: 'api_group', width: 120 },
{ title: '对话id', dataIndex: 'project_id', key: 'project_id', width: 80 },
{ title: '日期', dataIndex: 'day', key: 'day', width: 110 },
{ title: '小时', dataIndex: 'hour', key: 'hour', width: 60 },
{ title: '服务商', dataIndex: 'provider', key: 'provider', width: 80 },
{ title: '账号', dataIndex: 'account', key: 'account', width: 80 },
{ title: '模型', dataIndex: 'model', key: 'model', width: 150 },
{ title: '代理类型', dataIndex: 'agent_type', key: 'agent_type', width: 80 },
{ title: '任务id', dataIndex: 'chat_id', key: 'chat_id', width: 100 },
{ title: '花费金额(总)', dataIndex: 'cost', key: 'cost', width: 120 },
{ title: '提示Tokens', dataIndex: 'prompt_token', key: 'prompt_token', width: 110 },
{ title: '输出Tokens', dataIndex: 'completion_token', key: 'completion_token', width: 110 },
{ title: '缓存创建Tokens', dataIndex: 'cache_create_token', key: 'cache_create_token', width: 130 },
{ title: '缓存命中Tokens', dataIndex: 'cache_read_token', key: 'cache_read_token', width: 130 },
{ title: '提示花费', dataIndex: 'prompt_cost', key: 'prompt_cost', width: 100 },
{ title: '输出花费', dataIndex: 'completion_cost', key: 'completion_cost', width: 100 },
{ title: '缓存创建花费', dataIndex: 'cache_create_cost', key: 'cache_create_cost', width: 120 },
{ title: '缓存命花费', dataIndex: 'cache_read_cost', key: 'cache_read_cost', width: 120 },
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 160 },
{ title: '修改时间', dataIndex: 'updated_at', key: 'updated_at', width: 160 },
]), []);
return (
@@ -81,7 +81,7 @@ const FinanceTokenUsage: React.FC = () => {
dataSource={data}
loading={loading}
pagination={{ current: page, pageSize: size, total, onChange: (p, s) => { setPage(p); setSize(s); fetchList(p, s); } }}
scroll={{ x: 1200 }}
scroll={{ x: 2300 }}
/>
</Card>
);

View File

@@ -58,15 +58,22 @@ const FinanceTransactionLogs: React.FC = () => {
return (
<Card bordered={false} title={<Typography.Title level={4} style={{ margin: 0 }}></Typography.Title>}>
<Form form={form} layout="inline" onFinish={() => fetchList(1, size)} style={{ marginBottom: 12 }}>
<Form.Item name="user_id" label="用户id">
<Input allowClear placeholder="用户id" style={{ width: 180 }} />
<Form
form={form}
onFinish={() => fetchList(1, size)}
style={{ marginBottom: 20 }}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
{/* 第一行用户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 }} />
</Form.Item>
<Form.Item name="order_id" label="订单id">
<Input allowClear placeholder="订单id" style={{ width: 180 }} />
<Form.Item name="order_id" label="订单编号:" style={{ margin: 0, flex: '0 0 auto' }}>
<Input allowClear placeholder="订单编号" style={{ width: 200 }} />
</Form.Item>
<Form.Item name="type" label="流水类型">
<Select allowClear placeholder="recharge/refund/consume" style={{ width: 200 }}
<Form.Item name="type" label="流水类型:" style={{ margin: 0, flex: '0 0 auto' }}>
<Select allowClear placeholder="充值/退款/消费" style={{ width: 220 }}
options={[
{ label: 'recharge', value: 'recharge' },
{ label: 'refund', value: 'refund' },
@@ -74,8 +81,8 @@ const FinanceTransactionLogs: React.FC = () => {
]}
/>
</Form.Item>
<Form.Item name="status" label="状态">
<Select allowClear placeholder="success/failed/pending" style={{ width: 200 }}
<Form.Item name="status" label="状态:" style={{ margin: 0, flex: '0 0 auto' }}>
<Select allowClear placeholder="成功/失败/待定" style={{ width: 220 }}
options={[
{ label: 'success', value: 'success' },
{ label: 'failed', value: 'failed' },
@@ -83,15 +90,25 @@ const FinanceTransactionLogs: React.FC = () => {
]}
/>
</Form.Item>
<Form.Item name="range" label="时间范围">
<DatePicker.RangePicker />
</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>
<Form.Item>
<Space>
<Button type="primary" htmlType="submit" onClick={() => { setPage(1); }}></Button>
<Button onClick={() => { form.resetFields(); fetchList(1, size); setPage(1); }}></Button>
<Form.Item style={{ margin: 0, flex: '0 0 auto' }}>
<Space size="middle">
<Button type="primary" htmlType="submit" onClick={() => { setPage(1); }} size="middle">
</Button>
<Button onClick={() => { form.resetFields(); fetchList(1, size); setPage(1); }} size="middle">
</Button>
</Space>
</Form.Item>
</div>
</div>
</Form>
<Table
rowKey={(r) => r.id || r.transaction_id}