Files
app/wei-ai-demo/pages/Subscription.tsx
2026-01-28 19:10:19 +08:00

175 lines
8.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { ChevronLeft, Check, Crown, Zap, Star, Shield, Smartphone, Infinity } from 'lucide-react';
interface SubscriptionProps {
onBack: () => void;
}
const PLANS = [
{
id: 'month',
name: '月度协议',
price: '¥28',
period: '/月',
desc: '灵便之选,随时取消',
isPopular: false
},
{
id: 'year',
name: '年度神经连接',
price: '¥298',
period: '/年',
desc: '节省 20%,尊享全年',
originalPrice: '¥336',
isPopular: true
},
];
const PRIVILEGES = [
{ icon: Infinity, title: '剧本库无限畅玩', desc: '解锁全部付费/限定剧本' },
{ icon: Zap, title: '高频信号通道', desc: '解锁 Extreme 级震动强度' },
{ icon: Smartphone, title: '多设备同步', desc: '支持多台 Link-X 设备同时控制' },
{ icon: Star, title: 'AI 专属人格', desc: '解锁隐藏性格与深度记忆模式' },
{ icon: Crown, title: '尊贵身份标识', desc: '专属头像框与社区徽章' },
{ icon: Shield, title: '隐私加密通道', desc: '端对端加密,无痕浏览' },
];
const Subscription: React.FC<SubscriptionProps> = ({ onBack }) => {
const [selectedPlan, setSelectedPlan] = useState('year');
const [isProcessing, setIsProcessing] = useState(false);
const handleSubscribe = () => {
setIsProcessing(true);
setTimeout(() => {
setIsProcessing(false);
alert('订阅成功!欢迎加入神经连接计划。');
onBack();
}, 1500);
};
return (
<div className="fixed inset-0 z-[60] bg-[#0F1014] flex flex-col h-full overflow-hidden animate-in slide-in-from-bottom-10 duration-300 fade-in">
{/* Header */}
<div className="relative z-20 pt-safe-top px-4 py-3 flex items-center justify-between border-b border-white/5 bg-[#0F1014]/80 backdrop-blur-md">
<button onClick={onBack} className="p-2 -ml-2 text-white/70 hover:text-white active:scale-95 transition-transform">
<ChevronLeft size={24} />
</button>
<h1 className="text-base font-bold text-white tracking-wider"></h1>
<div className="w-8"></div>
</div>
<div className="flex-1 overflow-y-auto no-scrollbar pb-32">
{/* Hero Section */}
<div className="relative h-48 overflow-hidden mb-6 shrink-0">
<div className="absolute inset-0 bg-gradient-to-br from-[#1C1F26] via-[#0F1014] to-[#0F1014]"></div>
{/* Gold Glow */}
<div className="absolute top-[-50%] left-1/2 -translate-x-1/2 w-[120%] h-full bg-[#F59E0B]/10 blur-[60px] rounded-full"></div>
<div className="relative z-10 h-full flex flex-col items-center justify-center text-center px-6">
<div className="w-12 h-12 rounded-full bg-gradient-to-tr from-[#F59E0B] to-[#FCD34D] flex items-center justify-center mb-3 shadow-[0_0_20px_rgba(245,158,11,0.3)]">
<Crown size={24} className="text-black" fill="currentColor" />
</div>
<h2 className="text-xl font-bold text-white tracking-wide"> Wei AI Pro</h2>
<p className="text-xs text-[#94A3B8] mt-2 max-w-[200px]"></p>
</div>
</div>
{/* Plan Selection */}
<div className="px-4 space-y-4 mb-8">
{PLANS.map((plan) => {
const isSelected = selectedPlan === plan.id;
return (
<div
key={plan.id}
onClick={() => setSelectedPlan(plan.id)}
className={`relative p-0.5 rounded-2xl transition-all duration-300 active:scale-[0.99] ${
isSelected
? 'bg-gradient-to-r from-[#F59E0B] to-[#FCD34D] shadow-[0_0_20px_rgba(245,158,11,0.15)]'
: 'bg-white/10'
}`}
>
<div className="bg-[#1C1F26] rounded-[14px] p-4 flex items-center justify-between relative z-10">
<div className="flex items-center gap-4">
<div className={`w-5 h-5 rounded-full border-2 flex items-center justify-center transition-colors shrink-0 ${
isSelected ? 'border-[#F59E0B] bg-[#F59E0B]' : 'border-[#64748B]'
}`}>
{isSelected && <Check size={12} className="text-black stroke-[3]" />}
</div>
<div>
<div className="flex items-center gap-2">
<h3 className={`text-base font-bold ${isSelected ? 'text-white' : 'text-[#94A3B8]'}`}>{plan.name}</h3>
{plan.isPopular && (
<span className="text-[9px] font-bold bg-[#F59E0B] text-black px-1.5 py-0.5 rounded tracking-wide shrink-0">
</span>
)}
</div>
<p className="text-xs text-[#64748B] mt-1 line-clamp-1">{plan.desc}</p>
</div>
</div>
<div className="text-right shrink-0">
{plan.originalPrice && (
<span className="block text-[10px] text-[#64748B] line-through mb-0.5">{plan.originalPrice}</span>
)}
<div className="flex items-end justify-end">
<span className={`text-xl font-bold font-mono ${isSelected ? 'text-[#F59E0B]' : 'text-white'}`}>{plan.price}</span>
<span className="text-xs text-[#64748B] mb-1 ml-0.5">{plan.period}</span>
</div>
</div>
</div>
</div>
);
})}
</div>
{/* Privileges Grid */}
<div className="px-5">
<h3 className="text-xs font-bold text-[#64748B] uppercase tracking-widest mb-4 flex items-center gap-2">
<Star size={12} />
</h3>
<div className="grid grid-cols-2 gap-x-4 gap-y-6">
{PRIVILEGES.map((item, idx) => (
<div key={idx} className="flex gap-3">
<div className="shrink-0 w-8 h-8 rounded-lg bg-[#F59E0B]/10 flex items-center justify-center text-[#F59E0B]">
<item.icon size={16} />
</div>
<div>
<h4 className="text-xs font-bold text-white mb-0.5">{item.title}</h4>
<p className="text-[10px] text-[#64748B] leading-tight">{item.desc}</p>
</div>
</div>
))}
</div>
</div>
{/* Terms */}
<div className="px-6 mt-8 mb-4 text-[10px] text-[#475569] text-center leading-relaxed">
<p>24</p>
<p className="mt-2"></p>
</div>
</div>
{/* Bottom Action Bar */}
<div className="absolute bottom-0 left-0 w-full p-4 bg-[#0F1014]/90 backdrop-blur-xl border-t border-white/5 z-30">
<button
onClick={handleSubscribe}
disabled={isProcessing}
className="w-full h-12 rounded-xl bg-gradient-to-r from-[#F59E0B] to-[#FBBF24] text-black font-bold tracking-wide shadow-[0_0_20px_rgba(245,158,11,0.2)] active:scale-[0.98] transition-transform flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed"
>
{isProcessing ? (
<span className="animate-pulse">...</span>
) : (
<>
<Zap size={18} fill="currentColor" />
{PLANS.find(p => p.id === selectedPlan)?.price}
</>
)}
</button>
</div>
</div>
);
};
export default Subscription;