Initial commit: OmniAI Web Frontend

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:38:01 +08:00
commit bedee3ba8d
183 changed files with 94805 additions and 0 deletions
@@ -0,0 +1,230 @@
import { CheckCircleOutlined, CloseOutlined, CrownOutlined, RocketOutlined } from "@ant-design/icons";
import { useMemo, useState, type ReactNode } from "react";
type RechargeAudience = "personal" | "enterprise";
interface MembershipPlan {
id: string;
audience: RechargeAudience;
name: string;
subtitle: string;
period: string;
price: string;
grant: string;
comparisonLabel: string;
badge?: string;
icon: ReactNode;
benefits: string[];
}
const membershipPlans: MembershipPlan[] = [
{
id: "pro-month",
audience: "personal",
name: "专业版",
subtitle: "Pro",
period: "月付",
price: "299 元 / 月",
grant: "每月赠送 10000 积分,30 天有效",
comparisonLabel: "专业版基础权益",
icon: <CrownOutlined />,
benefits: ["通用大模型全解锁", "积分与 API 消耗 9 折", "并发提升到 3 个", "去水印、插队加速、专属客服"],
},
{
id: "pro-quarter",
audience: "personal",
name: "专业版",
subtitle: "Pro",
period: "季付",
price: "897 元 / 季",
grant: "连续 3 个月按月发放 Pro 积分",
comparisonLabel: "相比月付新增",
badge: "季度",
icon: <CrownOutlined />,
benefits: ["一次覆盖 3 个月使用周期", "每月延续 Pro 权益", "适合短期项目排期"],
},
{
id: "pro-year",
audience: "personal",
name: "专业版",
subtitle: "Pro",
period: "年付",
price: "1990 元 / 年",
grant: "全年合计 140000 积分,默认按月分摊",
comparisonLabel: "相比季付新增",
badge: "年费优惠",
icon: <CrownOutlined />,
benefits: ["折合 10 个月费用", "前 100 名额外赠 20000 积分", "适合全年持续高频使用"],
},
{
id: "enterprise-month",
audience: "enterprise",
name: "企业版",
subtitle: "Enterprise",
period: "月付",
price: "499 元 / 月",
grant: "每月赠送 2000 积分,30 天有效",
comparisonLabel: "企业版基础权益",
icon: <RocketOutlined />,
benefits: ["企业私有模型与高性能模型", "默认 10 并发,可申请提升", "积分与 API 消耗 8 折", "用量报表与正式 API 权限"],
},
{
id: "enterprise-quarter",
audience: "enterprise",
name: "企业版",
subtitle: "Enterprise",
period: "季付",
price: "1497 元 / 季",
grant: "连续 3 个月按月发放企业版积分",
comparisonLabel: "相比月付新增",
badge: "季度",
icon: <RocketOutlined />,
benefits: ["一次覆盖季度项目周期", "延续企业资源池与高并发", "适合阶段性团队投放"],
},
{
id: "enterprise-year",
audience: "enterprise",
name: "企业版",
subtitle: "Enterprise",
period: "年付",
price: "4990 元 / 年",
grant: "全年合计 340000 积分,默认按月分摊",
comparisonLabel: "相比季付新增",
badge: "企业年费",
icon: <RocketOutlined />,
benefits: ["折合 10 个月费用", "前 100 名额外赠 100000 积分", "支持对公充值与子账户额度分配"],
},
];
const defaultSelectedPlanIds: Record<RechargeAudience, string> = {
personal: "pro-month",
enterprise: "enterprise-month",
};
const rechargeRules = [
"充值比例:固定 1 元 = 100 积分,平台可限时活动额外赠送积分",
"有效期:充值积分到账起有效期 12 个月,系统按先进先出自动消耗",
"退费规则:充值积分到账后不支持退换、折现,仅限平台内消费",
];
interface RechargeModalProps {
open: boolean;
onClose: () => void;
currentBalance?: number;
}
export function RechargeModal({ open, onClose, currentBalance }: RechargeModalProps) {
const [activeAudience, setActiveAudience] = useState<RechargeAudience>("personal");
const [selectedPlanIds, setSelectedPlanIds] = useState<Record<RechargeAudience, string>>(defaultSelectedPlanIds);
const visiblePlans = useMemo(() => membershipPlans.filter((plan) => plan.audience === activeAudience), [activeAudience]);
const selectedPlanId = selectedPlanIds[activeAudience];
const handlePlanSelect = (plan: MembershipPlan) => {
setSelectedPlanIds((current) => ({
...current,
[plan.audience]: plan.id,
}));
};
if (!open) return null;
return (
<div className="recharge-modal" role="dialog" aria-modal="true" aria-labelledby="recharge-modal-title">
<button type="button" className="recharge-modal__backdrop" onClick={onClose} aria-label="关闭充值弹窗" />
<section className="recharge-modal__panel" aria-describedby="recharge-modal-desc">
<div className="recharge-modal__promo" role="note">
<strong></strong>
<span> 100 12 </span>
</div>
<header className="recharge-modal__header">
<div>
<span className="recharge-modal__eyebrow"></span>
<h2 id="recharge-modal-title"></h2>
<p id="recharge-modal-desc"></p>
</div>
{currentBalance !== undefined ? (
<span className="recharge-modal__balance">{(currentBalance / 100).toFixed(2)} </span>
) : null}
<button type="button" className="recharge-modal__close" onClick={onClose} aria-label="关闭">
<CloseOutlined />
</button>
</header>
<div className="recharge-modal__audience-tabs" role="tablist" aria-label="充值对象">
<button
type="button"
role="tab"
aria-selected={activeAudience === "personal"}
className={activeAudience === "personal" ? "is-active" : ""}
onClick={() => setActiveAudience("personal")}
>
</button>
<button
type="button"
role="tab"
aria-selected={activeAudience === "enterprise"}
className={activeAudience === "enterprise" ? "is-active" : ""}
onClick={() => setActiveAudience("enterprise")}
>
</button>
</div>
<div className="recharge-modal__grid" data-audience={activeAudience}>
{visiblePlans.map((plan) => {
const isSelected = plan.id === selectedPlanId;
return (
<article
key={plan.id}
className={`recharge-modal__card recharge-modal__card--${plan.id}${isSelected ? " is-selected" : ""}`}
>
{plan.badge ? <span className="recharge-modal__badge">{plan.badge}</span> : null}
<div className="recharge-modal__card-head">
<span className="recharge-modal__card-icon">{plan.icon}</span>
<div>
<h3>{plan.name}</h3>
<span>{plan.subtitle}</span>
</div>
</div>
<span className="recharge-modal__period">{plan.period}</span>
<div className="recharge-modal__price">
<strong>{plan.price}</strong>
</div>
<p className="recharge-modal__grant">{plan.grant}</p>
<span className="recharge-modal__diff-label">{plan.comparisonLabel}</span>
<ul className="recharge-modal__features">
{plan.benefits.map((benefit) => (
<li key={benefit}>
<CheckCircleOutlined />
<span>{benefit}</span>
</li>
))}
</ul>
<button
type="button"
className={`recharge-modal__buy${isSelected ? " is-selected" : ""}`}
aria-pressed={isSelected}
onClick={() => handlePlanSelect(plan)}
>
{isSelected ? "当前方案" : "选择方案"}
</button>
</article>
);
})}
</div>
<footer className="recharge-modal__rules">
<h3></h3>
<ol>
{rechargeRules.map((rule) => (
<li key={rule}>{rule}</li>
))}
</ol>
</footer>
</section>
</div>
);
}