feat: app 端 ui 设计完成
This commit is contained in:
176
wei-ai-demo/pages/TopUp.tsx
Normal file
176
wei-ai-demo/pages/TopUp.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ChevronLeft, Zap, CreditCard, Check, ShieldCheck, Gem } from 'lucide-react';
|
||||
|
||||
interface TopUpProps {
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
const RECHARGE_OPTIONS = [
|
||||
{ id: 1, points: 60, price: '¥6.00', bonus: null, tag: null },
|
||||
{ id: 2, points: 300, price: '¥30.00', bonus: '+15', tag: null },
|
||||
{ id: 3, points: 680, price: '¥68.00', bonus: '+50', tag: '热销' },
|
||||
{ id: 4, points: 1280, price: '¥128.00', bonus: '+120', tag: null },
|
||||
{ id: 5, points: 3280, price: '¥328.00', bonus: '+350', tag: '超值' },
|
||||
{ id: 6, points: 6480, price: '¥648.00', bonus: '+800', tag: null },
|
||||
];
|
||||
|
||||
const PAYMENT_METHODS = [
|
||||
{ id: 'alipay', name: '支付宝', icon: '支' },
|
||||
{ id: 'wechat', name: '微信支付', icon: '微' },
|
||||
];
|
||||
|
||||
const TopUp: React.FC<TopUpProps> = ({ onBack }) => {
|
||||
const [selectedOption, setSelectedOption] = useState<number>(3);
|
||||
const [paymentMethod, setPaymentMethod] = useState('alipay');
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
|
||||
const handlePay = () => {
|
||||
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> {/* Spacer */}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto no-scrollbar pb-32">
|
||||
|
||||
{/* Current Balance Card */}
|
||||
<div className="mx-4 mt-6 mb-8 relative h-32 rounded-2xl overflow-hidden shadow-[0_10px_30px_rgba(139,92,246,0.15)] group">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#1C1F26] via-[#2D3039] to-[#1C1F26]"></div>
|
||||
{/* Neon Accents */}
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-[#8B5CF6]/20 blur-[50px] rounded-full"></div>
|
||||
<div className="absolute bottom-0 left-0 w-24 h-24 bg-[#F43F5E]/10 blur-[40px] rounded-full"></div>
|
||||
|
||||
<div className="relative z-10 p-6 flex flex-col justify-between h-full">
|
||||
<div className="flex items-center gap-2 opacity-70">
|
||||
<Gem size={14} className="text-[#8B5CF6]" />
|
||||
<span className="text-xs font-bold text-[#E2E8F0] tracking-widest uppercase">当前余额</span>
|
||||
</div>
|
||||
<div className="flex items-end gap-3">
|
||||
<span className="text-4xl font-bold text-white font-mono tracking-tighter drop-shadow-lg">2,450</span>
|
||||
<span className="text-sm font-medium text-[#8B5CF6] mb-1.5">积分</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Decorative Pattern */}
|
||||
<div className="absolute right-4 top-1/2 -translate-y-1/2 opacity-5 pointer-events-none">
|
||||
<Zap size={100} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recharge Options Grid */}
|
||||
<div className="px-4">
|
||||
<h3 className="text-xs font-bold text-[#64748B] uppercase tracking-widest mb-4 flex items-center gap-2">
|
||||
<Zap size={12} /> 选择充值金额
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{RECHARGE_OPTIONS.map((opt) => {
|
||||
const isSelected = selectedOption === opt.id;
|
||||
return (
|
||||
<button
|
||||
key={opt.id}
|
||||
onClick={() => setSelectedOption(opt.id)}
|
||||
className={`relative p-4 rounded-xl border flex flex-col items-start transition-all duration-300 active:scale-[0.98] ${
|
||||
isSelected
|
||||
? 'bg-[#8B5CF6]/10 border-[#8B5CF6] shadow-[0_0_15px_rgba(139,92,246,0.15)]'
|
||||
: 'bg-[#1C1F26]/60 border-white/5 hover:border-white/10'
|
||||
}`}
|
||||
>
|
||||
{opt.tag && (
|
||||
<div className={`absolute -top-2.5 -right-2 px-2 py-0.5 rounded text-[9px] font-bold tracking-wider uppercase border shadow-sm ${
|
||||
opt.tag === '热销'
|
||||
? 'bg-[#F43F5E] text-white border-[#F43F5E]'
|
||||
: 'bg-[#F59E0B] text-black border-[#F59E0B]'
|
||||
}`}>
|
||||
{opt.tag}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-1.5 mb-1">
|
||||
<Zap size={14} className={isSelected ? 'text-[#8B5CF6]' : 'text-[#64748B]'} fill={isSelected ? "currentColor" : "none"} />
|
||||
<span className={`text-lg font-bold font-mono ${isSelected ? 'text-white' : 'text-[#E2E8F0]'}`}>
|
||||
{opt.points}
|
||||
</span>
|
||||
</div>
|
||||
{opt.bonus && (
|
||||
<span className="text-[10px] text-[#10B981] font-mono mb-2 block">
|
||||
赠送 {opt.bonus}
|
||||
</span>
|
||||
)}
|
||||
<span className={`text-sm font-medium mt-auto ${isSelected ? 'text-[#8B5CF6]' : 'text-[#94A3B8]'}`}>
|
||||
{opt.price}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Payment Method (Visual Only) */}
|
||||
<div className="px-4 mt-8">
|
||||
<h3 className="text-xs font-bold text-[#64748B] uppercase tracking-widest mb-3">支付方式</h3>
|
||||
<div className="space-y-2">
|
||||
{PAYMENT_METHODS.map(method => (
|
||||
<button
|
||||
key={method.id}
|
||||
onClick={() => setPaymentMethod(method.id)}
|
||||
className={`w-full flex items-center justify-between p-3 rounded-xl border transition-all ${
|
||||
paymentMethod === method.id
|
||||
? 'bg-[#1C1F26] border-[#8B5CF6]/50'
|
||||
: 'bg-transparent border-white/5 opacity-60'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded flex items-center justify-center font-bold text-white ${method.id === 'alipay' ? 'bg-[#1677FF]' : 'bg-[#07C160]'}`}>
|
||||
{method.icon}
|
||||
</div>
|
||||
<span className="text-sm text-white">{method.name}</span>
|
||||
</div>
|
||||
{paymentMethod === method.id && <Check size={16} className="text-[#8B5CF6]" />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Terms */}
|
||||
<div className="px-6 mt-8 mb-4 flex items-start gap-2 text-[10px] text-[#64748B] leading-tight">
|
||||
<ShieldCheck size={12} className="shrink-0 mt-0.5" />
|
||||
<p>充值即代表您已同意《用户充值协议》。虚拟商品一旦售出,不支持退换。</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={handlePay}
|
||||
disabled={isProcessing}
|
||||
className="w-full h-12 rounded-xl bg-gradient-to-r from-[#8B5CF6] to-[#6366f1] text-white font-bold tracking-wide shadow-[0_0_20px_rgba(139,92,246,0.3)] 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>
|
||||
) : (
|
||||
<>
|
||||
<CreditCard size={18} />
|
||||
立即支付 {RECHARGE_OPTIONS.find(o => o.id === selectedOption)?.price}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopUp;
|
||||
Reference in New Issue
Block a user