- UserInfo.balance 改为可选字段 - 无 token 时不设 balance,PaymentForm 不渲染余额行 - COMPLETED 触发时立即调 loadUserAndOrders 刷新余额 2.2s 结果页展示期间余额已在后台拉取,回表单时即刻呈现新值
106 lines
2.9 KiB
TypeScript
106 lines
2.9 KiB
TypeScript
export interface UserInfo {
|
|
id?: number;
|
|
username: string;
|
|
balance?: number;
|
|
}
|
|
|
|
export interface MyOrder {
|
|
id: string;
|
|
amount: number;
|
|
status: string;
|
|
paymentType: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export type OrderStatusFilter = 'ALL' | 'PENDING' | 'PAID' | 'COMPLETED' | 'CANCELLED' | 'EXPIRED' | 'FAILED';
|
|
|
|
export const STATUS_TEXT_MAP: Record<string, string> = {
|
|
PENDING: '待支付',
|
|
PAID: '已支付',
|
|
RECHARGING: '充值中',
|
|
COMPLETED: '已完成',
|
|
EXPIRED: '已超时',
|
|
CANCELLED: '已取消',
|
|
FAILED: '失败',
|
|
REFUNDING: '退款中',
|
|
REFUNDED: '已退款',
|
|
REFUND_FAILED: '退款失败',
|
|
};
|
|
|
|
export const FILTER_OPTIONS: { key: OrderStatusFilter; label: string }[] = [
|
|
{ key: 'ALL', label: '全部' },
|
|
{ key: 'PENDING', label: '待支付' },
|
|
{ key: 'COMPLETED', label: '已完成' },
|
|
{ key: 'CANCELLED', label: '已取消' },
|
|
{ key: 'EXPIRED', label: '已超时' },
|
|
];
|
|
|
|
export function detectDeviceIsMobile(): boolean {
|
|
if (typeof window === 'undefined') return false;
|
|
|
|
const ua = navigator.userAgent || '';
|
|
const mobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Windows Phone|Mobile/i.test(ua);
|
|
const smallPhysicalScreen = Math.min(window.screen.width, window.screen.height) <= 768;
|
|
const touchCapable = navigator.maxTouchPoints > 1;
|
|
|
|
return mobileUA || (touchCapable && smallPhysicalScreen);
|
|
}
|
|
|
|
export function formatStatus(status: string): string {
|
|
return STATUS_TEXT_MAP[status] || status;
|
|
}
|
|
|
|
export function formatCreatedAt(value: string): string {
|
|
const date = new Date(value);
|
|
if (Number.isNaN(date.getTime())) return value;
|
|
return date.toLocaleString();
|
|
}
|
|
|
|
export interface PaymentTypeMeta {
|
|
label: string;
|
|
sublabel?: string;
|
|
color: string;
|
|
selectedBorder: string;
|
|
selectedBg: string;
|
|
iconBg: string;
|
|
}
|
|
|
|
export const PAYMENT_TYPE_META: Record<string, PaymentTypeMeta> = {
|
|
alipay: {
|
|
label: '支付宝',
|
|
sublabel: 'ALIPAY',
|
|
color: '#00AEEF',
|
|
selectedBorder: 'border-cyan-400',
|
|
selectedBg: 'bg-cyan-50',
|
|
iconBg: 'bg-[#00AEEF]',
|
|
},
|
|
wxpay: {
|
|
label: '微信支付',
|
|
color: '#2BB741',
|
|
selectedBorder: 'border-green-500',
|
|
selectedBg: 'bg-green-50',
|
|
iconBg: 'bg-[#2BB741]',
|
|
},
|
|
stripe: {
|
|
label: 'Stripe',
|
|
sublabel: '信用卡 / 借记卡',
|
|
color: '#635bff',
|
|
selectedBorder: 'border-[#635bff]',
|
|
selectedBg: 'bg-[#635bff]/10',
|
|
iconBg: 'bg-[#635bff]',
|
|
},
|
|
};
|
|
|
|
export function getStatusBadgeClass(status: string, isDark: boolean): string {
|
|
if (['COMPLETED', 'PAID'].includes(status)) {
|
|
return isDark ? 'bg-emerald-500/20 text-emerald-200' : 'bg-emerald-100 text-emerald-700';
|
|
}
|
|
if (status === 'PENDING') {
|
|
return isDark ? 'bg-blue-500/20 text-blue-200' : 'bg-blue-100 text-blue-700';
|
|
}
|
|
if (['CANCELLED', 'EXPIRED', 'FAILED'].includes(status)) {
|
|
return isDark ? 'bg-slate-600 text-slate-200' : 'bg-slate-100 text-slate-700';
|
|
}
|
|
return isDark ? 'bg-slate-700 text-slate-200' : 'bg-slate-100 text-slate-700';
|
|
}
|