128 lines
5.1 KiB
TypeScript
128 lines
5.1 KiB
TypeScript
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>
|
||
);
|
||
}
|