重构充值页面和统一控制台页面风格

- 充值页面:全新设计余额概览、预设金额卡片、支付方式、兑换码区域
- 控制台主页:移除灰色背景,统一页面边距和宽度
- API测速页:移至控制台下,调整字体大小和间距
- 侧边栏:新增API测速菜单入口
- 修复个人设置页TwoFASetting组件缺少IconShield导入
- 多个页面统一顶部间距和背景样式
This commit is contained in:
wangxiaoji 2026-07-01 14:13:15 +08:00
parent 44356571a6
commit ac1120fc33
8 changed files with 540 additions and 619 deletions

40
web/src/App.jsx vendored
View File

@ -18,6 +18,38 @@ For commercial licensing, please contact support@quantumnous.com
*/ */
import React, { lazy, Suspense, useContext, useMemo } from 'react'; import React, { lazy, Suspense, useContext, useMemo } from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, info: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
this.setState({ info });
console.error('ErrorBoundary caught:', error, info);
}
render() {
if (this.state.hasError) {
return (
<div style={{ padding: 40, color: 'red', fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
<h2>页面崩溃: {this.state.error?.message}</h2>
<details>
<summary>Stack</summary>
<pre>{this.state.error?.stack}</pre>
</details>
<details>
<summary>Component Stack</summary>
<pre>{this.state.info?.componentStack}</pre>
</details>
</div>
);
}
return this.props.children;
}
}
import { Route, Routes, useLocation, useParams } from 'react-router-dom'; import { Route, Routes, useLocation, useParams } from 'react-router-dom';
import Loading from './components/common/ui/Loading'; import Loading from './components/common/ui/Loading';
import User from './pages/User'; import User from './pages/User';
@ -352,9 +384,11 @@ function App() {
path='/console/personal' path='/console/personal'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>} key={location.pathname}> <ErrorBoundary>
<PersonalSetting /> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
</Suspense> <PersonalSetting />
</Suspense>
</ErrorBoundary>
</PrivateRoute> </PrivateRoute>
} }
/> />

View File

@ -50,8 +50,7 @@ const Dashboard = () => {
const { unreadCount } = useUserMessageUnreadCount(user); const { unreadCount } = useUserMessageUnreadCount(user);
return ( return (
<div className='min-h-full bg-slate-50 pb-16 text-slate-900 dark:bg-neutral-950 dark:text-white'> <div className='flex flex-col gap-6 pb-8'>
<div className='mx-auto w-full max-w-[1400px] px-4 sm:px-6 lg:px-10'>
<TopBar <TopBar
onSearch={() => {}} onSearch={() => {}}
onRefresh={dashboard.refresh} onRefresh={dashboard.refresh}
@ -60,7 +59,7 @@ const Dashboard = () => {
unreadCount={unreadCount} unreadCount={unreadCount}
/> />
<div className='space-y-6 pt-8 pb-8'> <div className='space-y-6 pb-8'>
<Hero user={user} /> <Hero user={user} />
{/* 第一行5 个核心指标卡 */} {/* 第一行5 个核心指标卡 */}
@ -97,7 +96,6 @@ const Dashboard = () => {
<div className='flex items-center justify-center border-t border-slate-200 py-6 text-xs text-slate-400 dark:border-white/10 dark:text-white/30'> <div className='flex items-center justify-center border-t border-slate-200 py-6 text-xs text-slate-400 dark:border-white/10 dark:text-white/30'>
<span>{getSystemName()} · 2026</span> <span>{getSystemName()} · 2026</span>
</div> </div>
</div>
</div> </div>
); );
}; };

View File

@ -52,6 +52,7 @@ const routerMap = {
'model-heat': '/console/model-heat', 'model-heat': '/console/model-heat',
playground: '/console/playground', playground: '/console/playground',
benchmarks: '/benchmarks', benchmarks: '/benchmarks',
'api-tester': '/console/api-tester',
personal: '/console/personal', personal: '/console/personal',
supplier: null, supplier: null,
distributor: '/console/distributor/admin', distributor: '/console/distributor/admin',
@ -90,6 +91,11 @@ const SiderBar = ({ onNavigate = () => {} }) => {
itemKey: 'benchmarks', itemKey: 'benchmarks',
to: '/benchmarks', to: '/benchmarks',
}, },
{
text: t('API 测速'),
itemKey: 'api-tester',
to: '/api-tester',
},
{ {
text: t('数据看板'), text: t('数据看板'),
itemKey: 'detail', itemKey: 'detail',

View File

@ -35,6 +35,7 @@ import {
IconAlertTriangle, IconAlertTriangle,
IconRefresh, IconRefresh,
IconCopy, IconCopy,
IconShield,
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
import { ScanFace } from 'lucide-react'; import { ScanFace } from 'lucide-react';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';

File diff suppressed because it is too large Load Diff

View File

@ -864,7 +864,7 @@ const TopUp = () => {
}; };
return ( return (
<div className='mx-auto w-full max-w-[1400px] px-4 pb-16 sm:px-6 lg:px-10'> <div className='mx-auto w-full max-w-[1400px] px-4 pb-16 pt-20 sm:px-6 lg:px-10'>
{/* 划转模态框 */} {/* 划转模态框 */}
<TransferModal <TransferModal
t={t} t={t}

View File

@ -308,16 +308,16 @@ const ApiTester = () => {
}; };
return ( return (
<div className='min-h-full bg-slate-50 pb-16 text-slate-900 dark:bg-neutral-950 dark:text-white'> <div className='min-h-full pb-16 text-slate-900 dark:text-white'>
<div className='mx-auto w-full max-w-[1400px] px-4 sm:px-6 lg:px-10'> <div className='mx-auto w-full max-w-[1400px] px-4 pt-20 sm:px-6 lg:px-10'>
<div className='space-y-6 pt-8 pb-8'> <div className='space-y-8 pb-8'>
{/* 页面标题区 */} {/* 页面标题区 */}
<div className='flex flex-wrap items-end justify-between gap-4'> <div className='flex flex-wrap items-end justify-between gap-4'>
<div> <div>
<h1 className='text-3xl font-bold tracking-tight text-slate-900 md:text-4xl dark:text-white'> <h1 className='text-3xl font-bold tracking-tight text-slate-900 md:text-4xl dark:text-white'>
API 快速测试 API 快速测试
</h1> </h1>
<p className='mt-2 text-sm text-slate-500 dark:text-white/50'> <p className='mt-2 text-xs text-slate-500 dark:text-white/50'>
当前协议{format === 'anthropic' ? 'Anthropic Messages' : 'OpenAI Chat Completions'} · 请求地址 {finalEndpoint} 当前协议{format === 'anthropic' ? 'Anthropic Messages' : 'OpenAI Chat Completions'} · 请求地址 {finalEndpoint}
</p> </p>
</div> </div>
@ -330,14 +330,14 @@ const ApiTester = () => {
</div> </div>
{/* 第一行API Key / 测试目标 / 模型 */} {/* 第一行API Key / 测试目标 / 模型 */}
<div className='grid grid-cols-1 gap-5 lg:grid-cols-3'> <div className='grid grid-cols-1 gap-6 lg:grid-cols-3'>
{/* API Key 输入卡片 */} {/* API Key 输入卡片 */}
<div className='rounded-2xl border border-slate-200 bg-white p-5 transition-shadow hover:shadow-md dark:border-white/5 dark:bg-white/[0.02] dark:hover:shadow-black/20'> <div className='rounded-2xl border border-slate-200 bg-white p-6 transition-shadow hover:shadow-md dark:border-white/10 dark:bg-white/[0.02] dark:hover:shadow-black/20'>
<div className='mb-3 flex items-center gap-2'> <div className='mb-3 flex items-center gap-2'>
<div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'> <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'>
<KeyRound size={15} /> <KeyRound size={15} />
</div> </div>
<div className='text-sm font-semibold text-slate-900 dark:text-white'>API Key</div> <div className='text-xs font-semibold text-slate-900 dark:text-white'>API Key</div>
</div> </div>
<div className='relative'> <div className='relative'>
<input <input
@ -354,25 +354,25 @@ const ApiTester = () => {
{showKey ? <EyeOff size={14} /> : <Eye size={14} />} {showKey ? <EyeOff size={14} /> : <Eye size={14} />}
</button> </button>
</div> </div>
<div className='mt-2 text-[11px] text-slate-400 dark:text-white/40'> <div className='mt-2 text-xs text-slate-400 dark:text-white/40'>
{user?.token ? '已自动填充当前登录用户的 Token' : '请填写你的 API Key'} {user?.token ? '已自动填充当前登录用户的 Token' : '请填写你的 API Key'}
</div> </div>
</div> </div>
{/* 测试目标选择卡片 */} {/* 测试目标选择卡片 */}
<div className='rounded-2xl border border-slate-200 bg-white p-5 transition-shadow hover:shadow-md dark:border-white/5 dark:bg-white/[0.02] dark:hover:shadow-black/20'> <div className='rounded-2xl border border-slate-200 bg-white p-6 transition-shadow hover:shadow-md dark:border-white/10 dark:bg-white/[0.02] dark:hover:shadow-black/20'>
<div className='mb-3 flex items-center justify-between'> <div className='mb-3 flex items-center justify-between'>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'> <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'>
<Globe size={15} /> <Globe size={15} />
</div> </div>
<div className='text-sm font-semibold text-slate-900 dark:text-white'> <div className='text-xs font-semibold text-slate-900 dark:text-white'>
测试目标 测试目标
</div> </div>
</div> </div>
<button <button
onClick={() => setShowCustomBase((v) => !v)} onClick={() => setShowCustomBase((v) => !v)}
className='inline-flex items-center gap-1 text-[11px] text-slate-400 transition-colors hover:text-slate-700 dark:text-white/40 dark:hover:text-white' className='inline-flex items-center gap-1 text-xs text-slate-400 transition-colors hover:text-slate-700 dark:text-white/40 dark:hover:text-white'
title='切换自定义域名' title='切换自定义域名'
> >
{showCustomBase ? '收起域名' : '换个平台'} {showCustomBase ? '收起域名' : '换个平台'}
@ -386,10 +386,10 @@ const ApiTester = () => {
return acc; return acc;
}, {}); }, {});
return ( return (
<div className='space-y-2.5'> <div className='space-y-3'>
{Object.entries(grouped).map(([group, items]) => ( {Object.entries(grouped).map(([group, items]) => (
<div key={group}> <div key={group}>
<div className='mb-1.5 text-[10px] font-medium uppercase tracking-wider text-slate-400 dark:text-white/40'> <div className='mb-1.5 text-[11px] font-medium uppercase tracking-wider text-slate-400 dark:text-white/40'>
{group} {group}
</div> </div>
<div className='flex flex-wrap gap-1.5'> <div className='flex flex-wrap gap-1.5'>
@ -397,7 +397,7 @@ const ApiTester = () => {
<button <button
key={s.id} key={s.id}
onClick={() => applyScene(s)} onClick={() => applyScene(s)}
className='inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-[11px] font-medium text-slate-600 transition hover:border-blue-400 hover:bg-blue-50 hover:text-blue-600 dark:border-white/10 dark:bg-white/[0.02] dark:text-white/70 dark:hover:border-blue-400 dark:hover:bg-blue-500/10 dark:hover:text-blue-400' className='inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-600 transition hover:border-blue-400 hover:bg-blue-50 hover:text-blue-600 dark:border-white/10 dark:bg-white/[0.02] dark:text-white/70 dark:hover:border-blue-400 dark:hover:bg-blue-500/10 dark:hover:text-blue-400'
> >
<s.Icon size={11} /> {s.label} <s.Icon size={11} /> {s.label}
</button> </button>
@ -408,18 +408,18 @@ const ApiTester = () => {
<div className='border-t border-slate-100 pt-2.5 dark:border-white/5'> <div className='border-t border-slate-100 pt-2.5 dark:border-white/5'>
<div className='mb-1.5 flex items-center justify-between'> <div className='mb-1.5 flex items-center justify-between'>
<div className='text-[10px] font-medium uppercase tracking-wider text-slate-400 dark:text-white/40'> <div className='text-[11px] font-medium uppercase tracking-wider text-slate-400 dark:text-white/40'>
我的目标 我的目标
</div> </div>
<button <button
onClick={saveCurrentAsCustom} onClick={saveCurrentAsCustom}
className='inline-flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2.5 py-1 text-[10px] font-medium text-slate-600 transition hover:border-blue-400 hover:text-blue-600 dark:border-white/10 dark:bg-white/[0.02] dark:text-white/70 dark:hover:text-blue-400' className='inline-flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2.5 py-1 text-xs font-medium text-slate-600 transition hover:border-blue-400 hover:text-blue-600 dark:border-white/10 dark:bg-white/[0.02] dark:text-white/70 dark:hover:text-blue-400'
> >
<Plus size={10} /> 保存当前 <Plus size={10} /> 保存当前
</button> </button>
</div> </div>
{customScenes.length === 0 ? ( {customScenes.length === 0 ? (
<div className='text-[11px] text-slate-400 dark:text-white/40'> <div className='text-xs text-slate-400 dark:text-white/40'>
调整完地址后点右上保存当前即可收藏到本地 调整完地址后点右上保存当前即可收藏到本地
</div> </div>
) : ( ) : (
@ -431,7 +431,7 @@ const ApiTester = () => {
> >
<button <button
onClick={() => applyScene(s)} onClick={() => applyScene(s)}
className='inline-flex items-center gap-1.5 px-3 py-1.5 text-[11px] font-medium text-slate-600 transition hover:bg-blue-50 hover:text-blue-600 dark:text-white/70 dark:hover:bg-blue-500/10 dark:hover:text-blue-400' className='inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-slate-600 transition hover:bg-blue-50 hover:text-blue-600 dark:text-white/70 dark:hover:bg-blue-500/10 dark:hover:text-blue-400'
> >
<Bookmark size={11} /> {s.label} <Bookmark size={11} /> {s.label}
</button> </button>
@ -452,14 +452,14 @@ const ApiTester = () => {
})()} })()}
<div className='mt-3'> <div className='mt-3'>
<label className='mb-1.5 block text-[11px] font-medium text-slate-500 dark:text-white/50'> <label className='mb-1.5 block text-xs font-medium text-slate-500 dark:text-white/50'>
完整请求地址 完整请求地址
</label> </label>
<input <input
value={isAbsoluteEndpoint ? endpointPath : `${endpointBase}${endpointPath}`} value={isAbsoluteEndpoint ? endpointPath : `${endpointBase}${endpointPath}`}
onChange={(e) => applyFullUrl(e.target.value)} onChange={(e) => applyFullUrl(e.target.value)}
placeholder='https://你的平台域名/v1/chat/completions' placeholder='https://你的平台域名/v1/chat/completions'
className='w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 font-mono text-[11px] text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]' className='w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 font-mono text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]'
/> />
</div> </div>
@ -480,29 +480,29 @@ const ApiTester = () => {
</div> </div>
)} )}
<div className='mt-3 flex items-center gap-1.5 truncate font-mono text-[11px] text-slate-400 dark:text-white/40'> <div className='mt-3 flex items-center gap-1.5 truncate font-mono text-xs text-slate-400 dark:text-white/40'>
<span className='shrink-0'>实际请求</span> <span className='shrink-0'>实际请求</span>
<span className='truncate'>{finalEndpoint}</span> <span className='truncate'>{finalEndpoint}</span>
</div> </div>
</div> </div>
{/* 模型选择卡片 */} {/* 模型选择卡片 */}
<div className='rounded-2xl border border-slate-200 bg-white p-5 transition-shadow hover:shadow-md dark:border-white/5 dark:bg-white/[0.02] dark:hover:shadow-black/20'> <div className='rounded-2xl border border-slate-200 bg-white p-6 transition-shadow hover:shadow-md dark:border-white/10 dark:bg-white/[0.02] dark:hover:shadow-black/20'>
<div className='mb-3 flex items-center gap-2'> <div className='mb-3 flex items-center gap-2'>
<div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'> <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'>
<Cpu size={15} /> <Cpu size={15} />
</div> </div>
<div className='text-sm font-semibold text-slate-900 dark:text-white'>模型</div> <div className='text-xs font-semibold text-slate-900 dark:text-white'>模型</div>
</div> </div>
<input <input
value={model} value={model}
onChange={(e) => setModel(e.target.value)} onChange={(e) => setModel(e.target.value)}
placeholder='例如 gpt-4o-mini / claude-3-5-sonnet ...' placeholder='例如 gpt-4o-mini / claude-3-5-sonnet ...'
className='w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 font-mono text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]' className='w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 font-mono text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]'
/> />
{format === 'anthropic' && ( {format === 'anthropic' && (
<div className='mt-3'> <div className='mt-3'>
<label className='mb-1.5 block text-[11px] font-medium text-slate-500 dark:text-white/50'> <label className='mb-1.5 block text-xs font-medium text-slate-500 dark:text-white/50'>
max_tokensAnthropic 必填 max_tokensAnthropic 必填
</label> </label>
<input <input
@ -511,7 +511,7 @@ const ApiTester = () => {
max={8192} max={8192}
value={maxTokens} value={maxTokens}
onChange={(e) => setMaxTokens(Number(e.target.value) || 1)} onChange={(e) => setMaxTokens(Number(e.target.value) || 1)}
className='w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 font-mono text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]' className='w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 font-mono text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]'
/> />
</div> </div>
)} )}
@ -519,7 +519,7 @@ const ApiTester = () => {
</div> </div>
{/* 第二行:请求参数 + 响应结果 */} {/* 第二行:请求参数 + 响应结果 */}
<div className='grid grid-cols-1 gap-5 lg:grid-cols-2'> <div className='grid grid-cols-1 gap-6 lg:grid-cols-2'>
{/* 请求参数卡片 */} {/* 请求参数卡片 */}
<div className='rounded-2xl border border-slate-200 bg-white p-6 transition-shadow hover:shadow-md dark:border-white/5 dark:bg-white/[0.02] dark:hover:shadow-black/20'> <div className='rounded-2xl border border-slate-200 bg-white p-6 transition-shadow hover:shadow-md dark:border-white/5 dark:bg-white/[0.02] dark:hover:shadow-black/20'>
<div className='mb-4 flex items-center justify-between'> <div className='mb-4 flex items-center justify-between'>
@ -527,7 +527,7 @@ const ApiTester = () => {
<div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'> <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'>
<MessageSquare size={15} /> <MessageSquare size={15} />
</div> </div>
<div className='text-sm font-semibold text-slate-900 dark:text-white'> <div className='text-xs font-semibold text-slate-900 dark:text-white'>
请求参数 请求参数
</div> </div>
</div> </div>
@ -547,7 +547,7 @@ const ApiTester = () => {
<div className='space-y-4'> <div className='space-y-4'>
<div> <div>
<label className='mb-1.5 block text-[11px] font-medium uppercase tracking-wider text-slate-500 dark:text-white/50'> <label className='mb-1.5 block text-xs font-medium uppercase tracking-wider text-slate-500 dark:text-white/50'>
{format === 'anthropic' ? 'system顶层字段' : '系统提示'} {format === 'anthropic' ? 'system顶层字段' : '系统提示'}
</label> </label>
<textarea <textarea
@ -555,12 +555,12 @@ const ApiTester = () => {
onChange={(e) => setSystemPrompt(e.target.value)} onChange={(e) => setSystemPrompt(e.target.value)}
rows={2} rows={2}
placeholder={format === 'anthropic' ? '可选Anthropic 顶层 system 字段' : '(可选)设置模型角色'} placeholder={format === 'anthropic' ? '可选Anthropic 顶层 system 字段' : '(可选)设置模型角色'}
className='w-full resize-none rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]' className='w-full resize-none rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]'
/> />
</div> </div>
<div> <div>
<label className='mb-1.5 block text-[11px] font-medium uppercase tracking-wider text-slate-500 dark:text-white/50'> <label className='mb-1.5 block text-xs font-medium uppercase tracking-wider text-slate-500 dark:text-white/50'>
用户提示 <span className='text-red-500'>*</span> 用户提示 <span className='text-red-500'>*</span>
</label> </label>
<textarea <textarea
@ -568,7 +568,7 @@ const ApiTester = () => {
onChange={(e) => setPrompt(e.target.value)} onChange={(e) => setPrompt(e.target.value)}
rows={5} rows={5}
placeholder='输入你想让模型回答的内容' placeholder='输入你想让模型回答的内容'
className='w-full resize-none rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]' className='w-full resize-none rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 text-xs text-slate-900 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-500/20 dark:border-white/10 dark:bg-white/[0.04] dark:text-white dark:focus:border-blue-400 dark:focus:bg-white/[0.06]'
/> />
</div> </div>
</div> </div>
@ -581,12 +581,12 @@ const ApiTester = () => {
<div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'> <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'>
<Zap size={15} /> <Zap size={15} />
</div> </div>
<div className='text-sm font-semibold text-slate-900 dark:text-white'> <div className='text-xs font-semibold text-slate-900 dark:text-white'>
响应结果 响应结果
</div> </div>
{status && ( {status && (
<span <span
className={`ml-2 inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-[11px] font-medium ${ className={`ml-2 inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium ${
status.ok status.ok
? 'bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400' ? 'bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400'
: 'bg-red-50 text-red-600 dark:bg-red-500/10 dark:text-red-400' : 'bg-red-50 text-red-600 dark:bg-red-500/10 dark:text-red-400'
@ -607,7 +607,7 @@ const ApiTester = () => {
navigator.clipboard?.writeText(response); navigator.clipboard?.writeText(response);
Toast.success('已复制响应'); Toast.success('已复制响应');
}} }}
className='inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-[11px] font-medium text-slate-600 transition-colors hover:border-slate-300 hover:text-slate-900 dark:border-white/10 dark:bg-white/[0.02] dark:text-white/60 dark:hover:border-white/20 dark:hover:text-white' className='inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-600 transition-colors hover:border-slate-300 hover:text-slate-900 dark:border-white/10 dark:bg-white/[0.02] dark:text-white/60 dark:hover:border-white/20 dark:hover:text-white'
> >
<Copy size={11} /> 复制 <Copy size={11} /> 复制
</button> </button>
@ -621,7 +621,7 @@ const ApiTester = () => {
请求中... 请求中...
</div> </div>
) : response ? ( ) : response ? (
<pre className='max-h-[480px] overflow-auto p-4 font-mono text-[11px] leading-relaxed text-slate-800 dark:text-white/80'> <pre className='max-h-[480px] overflow-auto p-4 font-mono text-xs leading-relaxed text-slate-800 dark:text-white/80'>
{response} {response}
</pre> </pre>
) : ( ) : (

View File

@ -21,8 +21,10 @@ import React from 'react';
import Dashboard from '../../components/dashboard'; import Dashboard from '../../components/dashboard';
const Detail = () => ( const Detail = () => (
<div className='mt-[60px] w-full max-w-[1600px] mx-auto px-3 sm:px-4 lg:px-6 pb-6'> <div className='min-h-full pb-16 pt-20 text-slate-900 dark:text-white'>
<Dashboard /> <div className='w-full max-w-[1400px] px-4 sm:px-6 lg:px-10 ml-4 lg:ml-10'>
<Dashboard />
</div>
</div> </div>
); );