feat(web): add reusable admin form dialog

This commit is contained in:
2026-05-09 20:15:35 +08:00
parent c0335bd5d0
commit a5e66e79cd
49 changed files with 6013 additions and 1108 deletions
+127
View File
@@ -0,0 +1,127 @@
import type { ReactNode } from 'react';
import { Activity, Boxes, Image, Layers, Route, ServerCog, ShieldCheck, Video, Workflow } from 'lucide-react';
import { Badge, Button, Card, CardContent } from '../components/ui';
import type { PageKey } from '../types';
const coverage = [
{ icon: <Activity size={18} />, label: '大模型对话', value: 'Chat / Responses', detail: '兼容 OpenAI 对话接口与 EasyAI 站内调用' },
{ icon: <Image size={18} />, label: '图像生成', value: 'Image Generation', detail: '支持尺寸、质量、计费权重和结果转存' },
{ icon: <Layers size={18} />, label: '图像编辑', value: 'Image Edit', detail: '参考图、mask 和主服务文件上传链路' },
{ icon: <Video size={18} />, label: '视频能力', value: 'Video Ready', detail: '保留任务队列、进度事件和后续 Client 扩展位' },
];
const advantages = [
{ icon: <Route size={18} />, title: '多客户端路由', body: '同一模型可配置多个平台客户端,前一个失败后按策略切换下一个。' },
{ icon: <Workflow size={18} />, title: '持久化任务', body: '任务、事件、队列和回调 outbox 入库,服务重启后可以恢复运行状态。' },
{ icon: <ShieldCheck size={18} />, title: '策略化限流', body: 'TPM、RPM、并发、用户组折扣和平台优先级统一纳入调度。' },
{ icon: <ServerCog size={18} />, title: 'Hybrid 接入', body: '既可独立闭环运行,也可以对接 server-main 的认证、上传和业务前端。' },
];
export function HomePage(props: { onNavigate: (page: PageKey) => void }) {
return (
<div className="landingPage">
<section className="releaseNotice">
<Badge variant="success">线</Badge>
<strong>OpenAI Gemini Phase 1 Client </strong>
<span></span>
</section>
<section className="landingHero">
<div className="landingCopy">
<Badge variant="secondary">EasyAI AI Gateway</Badge>
<h1> AI </h1>
<p> server-main </p>
<div className="landingActions">
<Button type="button" onClick={() => props.onNavigate('models')}></Button>
<Button type="button" variant="outline" onClick={() => props.onNavigate('docs')}> API </Button>
</div>
</div>
<GatewayPreview />
</section>
<section className="landingSection">
<div className="landingSectionHeader">
<p className="eyebrow">Model Coverage</p>
<h2></h2>
</div>
<div className="coverageGrid">
{coverage.map((item) => (
<FeatureCard {...item} key={item.label} />
))}
</div>
</section>
<section className="landingSection">
<div className="landingSectionHeader">
<p className="eyebrow">Gateway Advantage</p>
<h2></h2>
</div>
<div className="advantageGrid">
{advantages.map((item) => (
<AdvantageCard {...item} key={item.title} />
))}
</div>
</section>
</div>
);
}
function GatewayPreview() {
return (
<div className="gatewayPreview" aria-label="网关能力预览">
<div className="previewHeader">
<span>Gateway Runtime</span>
<Badge variant="success">hybrid</Badge>
</div>
<div className="previewGrid">
<PreviewItem label="Provider" value="OpenAI / Gemini" />
<PreviewItem label="Routes" value="Chat · Image · Edit · Video" />
<PreviewItem label="Limits" value="TPM · RPM · Concurrent" />
<PreviewItem label="Queue" value="Persistent Recovery" />
</div>
<div className="previewFlow">
<span>Request</span>
<span>Policy</span>
<span>Client Retry</span>
<span>Callback</span>
<span>Result</span>
</div>
</div>
);
}
function PreviewItem(props: { label: string; value: string }) {
return (
<div>
<span>{props.label}</span>
<strong>{props.value}</strong>
</div>
);
}
function FeatureCard(props: { detail: string; icon: ReactNode; label: string; value: string }) {
return (
<Card>
<CardContent className="landingFeatureCard">
<div className="iconBox">{props.icon}</div>
<div>
<span>{props.label}</span>
<strong>{props.value}</strong>
<p>{props.detail}</p>
</div>
</CardContent>
</Card>
);
}
function AdvantageCard(props: { body: string; icon: ReactNode; title: string }) {
return (
<Card>
<CardContent className="advantageCard">
<div className="iconBox">{props.icon}</div>
<strong>{props.title}</strong>
<p>{props.body}</p>
</CardContent>
</Card>
);
}