feat: app 端 ui 设计完成

This commit is contained in:
liqupan
2026-01-28 19:10:19 +08:00
commit a4e7898e94
149 changed files with 11302 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
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;