diff --git a/.vite/deps/package.json b/.vite/deps/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/src/App.css b/src/App.css index 76fad7d..df47fb1 100644 --- a/src/App.css +++ b/src/App.css @@ -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));} diff --git a/src/darkTheme.css b/src/darkTheme.css index 7f4e6e1..50c6223 100644 --- a/src/darkTheme.css +++ b/src/darkTheme.css @@ -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; diff --git a/src/main.tsx b/src/main.tsx index 19e85b4..828b4b0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -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' diff --git a/src/pages/FinanceTokenUsage.tsx b/src/pages/FinanceTokenUsage.tsx index 1a1c97e..3397a70 100644 --- a/src/pages/FinanceTokenUsage.tsx +++ b/src/pages/FinanceTokenUsage.tsx @@ -33,27 +33,27 @@ const FinanceTokenUsage: React.FC = () => { useEffect(() => { fetchList(); }, []); const columns: ColumnsType = 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 }} /> ); diff --git a/src/pages/FinanceTransactionLogs.tsx b/src/pages/FinanceTransactionLogs.tsx index 26bd32d..3b30937 100644 --- a/src/pages/FinanceTransactionLogs.tsx +++ b/src/pages/FinanceTransactionLogs.tsx @@ -58,40 +58,57 @@ const FinanceTransactionLogs: React.FC = () => { return ( 用户流水}> -
fetchList(1, size)} style={{ marginBottom: 12 }}> - - - - - - - - - - - - - - - - - - + fetchList(1, size)} + style={{ marginBottom: 20 }} + > +
+ {/* 第一行:用户ID、订单编号、流水类型、状态、时间范围 */} +
+ + + + + + + + + +
+ + {/* 第二行:时间范围和按钮 */} +
+ + + + + + + + + +
+
r.id || r.transaction_id}