194 lines
8.0 KiB
TypeScript
194 lines
8.0 KiB
TypeScript
'use client'
|
||
|
||
import { useEffect, useState } from 'react'
|
||
import { useSession } from 'next-auth/react'
|
||
import { Loader2, User, Mail, AlertCircle, PackageCheck, KeyRound, CreditCard } from 'lucide-react'
|
||
import Link from 'next/link'
|
||
import Header from '@/components/Header'
|
||
|
||
type Order = {
|
||
id: number
|
||
order_no: string
|
||
product_type: string
|
||
product_name: string
|
||
amount: number
|
||
status: string
|
||
payment_method?: string
|
||
trade_no?: string
|
||
created_at: string
|
||
paid_at?: string
|
||
metadata?: any
|
||
}
|
||
|
||
export default function AccountPage() {
|
||
const { data: session, status } = useSession()
|
||
const [orders, setOrders] = useState<Order[]>([])
|
||
const [loading, setLoading] = useState(true)
|
||
const [error, setError] = useState<string | null>(null)
|
||
|
||
useEffect(() => {
|
||
if (status !== 'authenticated') return
|
||
const fetchOrders = async () => {
|
||
try {
|
||
const res = await fetch('/api/orders')
|
||
const data = await res.json()
|
||
if (res.ok && data.success) {
|
||
setOrders(data.orders || [])
|
||
} else {
|
||
setError(data.message || '加载订单失败')
|
||
}
|
||
} catch (e: any) {
|
||
setError(e?.message || '加载订单失败')
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
fetchOrders()
|
||
}, [status])
|
||
|
||
if (status === 'loading') {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center">
|
||
<Loader2 className="w-8 h-8 animate-spin text-apple-blue" />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (status !== 'authenticated' || !session) {
|
||
return (
|
||
<div className="min-h-screen flex flex-col items-center justify-center space-y-4">
|
||
<AlertCircle className="w-10 h-10 text-orange-500" />
|
||
<p className="text-apple-gray-700">请先登录后查看个人中心</p>
|
||
<Link
|
||
href="/auth/signin"
|
||
className="px-4 py-2 bg-apple-blue text-white rounded-full font-semibold"
|
||
>
|
||
去登录
|
||
</Link>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gradient-to-b from-apple-gray-50 to-white">
|
||
<Header scrolled />
|
||
<div className="max-w-6xl mx-auto px-4 py-14">
|
||
<div className="text-center mb-8">
|
||
<h1 className="text-3xl font-bold text-apple-gray-900">个人中心</h1>
|
||
<p className="text-apple-gray-600 text-sm mt-2">查看账户信息、订单和激活码</p>
|
||
</div>
|
||
|
||
<div className="grid md:grid-cols-3 gap-6 mb-8 items-start">
|
||
<div className="md:col-span-1 bg-gradient-to-br from-apple-blue/10 to-white rounded-2xl p-6 border border-apple-gray-100 apple-shadow-lg">
|
||
<div className="flex items-center space-x-3 mb-4">
|
||
<div className="w-12 h-12 rounded-full bg-apple-blue text-white flex items-center justify-center">
|
||
<User className="w-6 h-6" />
|
||
</div>
|
||
<div>
|
||
<p className="font-bold text-apple-gray-900">{session.user?.name || '未命名用户'}</p>
|
||
<p className="text-sm text-apple-gray-500">ID: {session.user?.id}</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center space-x-2 text-sm text-apple-gray-700 mb-2">
|
||
<Mail className="w-4 h-4 text-apple-blue" />
|
||
<span className="truncate max-w-[220px]">{session.user?.email}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="md:col-span-2 bg-white rounded-2xl p-6 border border-apple-gray-100 apple-shadow-lg">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-xl font-bold text-apple-gray-900">订单列表</h2>
|
||
{loading && <Loader2 className="w-4 h-4 animate-spin text-apple-blue" />}
|
||
</div>
|
||
|
||
{error && (
|
||
<div className="text-red-600 text-sm mb-3 flex items-center space-x-2">
|
||
<AlertCircle className="w-4 h-4" />
|
||
<span>{error}</span>
|
||
</div>
|
||
)}
|
||
|
||
{orders.length === 0 && !loading ? (
|
||
<p className="text-apple-gray-600 text-sm">暂无订单</p>
|
||
) : (
|
||
<div className="space-y-3">
|
||
{orders.map((o) => {
|
||
const license = o.metadata?.license
|
||
const licenseError = o.metadata?.licenseError
|
||
return (
|
||
<div
|
||
key={o.id}
|
||
className="border border-apple-gray-100 rounded-xl p-4 hover:border-apple-blue transition bg-gradient-to-br from-white to-apple-gray-50"
|
||
>
|
||
<div className="flex items-center justify-between mb-1">
|
||
<div className="font-mono text-sm text-apple-gray-800">{o.order_no}</div>
|
||
<div
|
||
className={`text-xs px-2 py-1 rounded-full ${
|
||
o.status === 'paid'
|
||
? 'bg-green-100 text-green-700'
|
||
: o.status === 'failed'
|
||
? 'bg-red-100 text-red-600'
|
||
: 'bg-amber-100 text-amber-700'
|
||
}`}
|
||
>
|
||
{o.status}
|
||
</div>
|
||
</div>
|
||
<div className="text-sm text-apple-gray-700">
|
||
{o.product_name} · ¥{o.amount} · {o.payment_method || '—'}
|
||
</div>
|
||
{o.trade_no && (
|
||
<div className="text-xs text-apple-gray-500 break-all mt-1">交易号:{o.trade_no}</div>
|
||
)}
|
||
|
||
{/* 激活码区域 */}
|
||
{o.status === 'paid' && (
|
||
<div className="mt-3">
|
||
{license ? (
|
||
<div className="bg-gradient-to-r from-emerald-50 to-green-50 border border-green-200 rounded-lg p-3">
|
||
<div className="flex items-center justify-between mb-1">
|
||
<div className="flex items-center space-x-1 text-sm text-green-700 font-semibold">
|
||
<KeyRound className="w-4 h-4" />
|
||
<span>激活码</span>
|
||
</div>
|
||
<span className="text-[11px] text-apple-gray-500">
|
||
{license.planName || license.plan}
|
||
</span>
|
||
</div>
|
||
<div className="font-mono text-xs break-all text-apple-gray-900">
|
||
{license.activationCode}
|
||
</div>
|
||
{license.isPermanent === false && license.expiresAt && (
|
||
<div className="text-[11px] text-apple-gray-500 mt-1">
|
||
到期:{license.expiresAt}
|
||
</div>
|
||
)}
|
||
{license.isPermanent && (
|
||
<div className="text-[11px] text-apple-gray-500 mt-1">永久有效</div>
|
||
)}
|
||
</div>
|
||
) : licenseError ? (
|
||
<div className="bg-red-50 border border-red-200 rounded-lg p-3 text-xs text-red-700">
|
||
激活码生成失败:{licenseError.message || '请联系客服'}{' '}
|
||
{licenseError.at ? `(${licenseError.at})` : ''}
|
||
</div>
|
||
) : (
|
||
<div className="bg-amber-50 border border-amber-200 rounded-lg p-3 text-xs text-amber-700">
|
||
激活码生成中,请稍后再查看
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|