gcx/frontend/admin-web/src/views/coupons/CouponManagementPage.tsx

120 lines
5.4 KiB
TypeScript

'use client';
import React, { useState } from 'react';
/**
* D2. 券管理 - 平台券审核与管理
*
* 发行方提交的券审核、已上架券管理、券数据统计
*/
interface CouponBatch {
id: string;
issuer: string;
name: string;
template: string;
faceValue: number;
quantity: number;
sold: number;
redeemed: number;
status: 'pending' | 'active' | 'suspended' | 'expired';
createdAt: string;
}
const mockCoupons: CouponBatch[] = [
{ id: 'C001', issuer: 'Starbucks', name: '¥25 礼品卡', template: '礼品卡', faceValue: 25, quantity: 5000, sold: 4200, redeemed: 3300, status: 'active', createdAt: '2026-01-15' },
{ id: 'C002', issuer: 'Amazon', name: '¥100 购物券', template: '代金券', faceValue: 100, quantity: 2000, sold: 1580, redeemed: 980, status: 'active', createdAt: '2026-01-20' },
{ id: 'C003', issuer: 'Nike', name: '8折运动券', template: '折扣券', faceValue: 80, quantity: 1000, sold: 0, redeemed: 0, status: 'pending', createdAt: '2026-02-08' },
{ id: 'C004', issuer: 'Walmart', name: '¥50 生活券', template: '代金券', faceValue: 50, quantity: 3000, sold: 3000, redeemed: 2800, status: 'expired', createdAt: '2025-08-01' },
];
const statusColors: Record<string, string> = {
pending: 'var(--color-warning)',
active: 'var(--color-success)',
suspended: 'var(--color-error)',
expired: 'var(--color-text-tertiary)',
};
const statusLabels: Record<string, string> = {
pending: '待审核',
active: '在售中',
suspended: '已暂停',
expired: '已过期',
};
export const CouponManagementPage: React.FC = () => {
const [filter, setFilter] = useState('all');
const filtered = filter === 'all' ? mockCoupons : mockCoupons.filter(c => c.status === filter);
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
<h1 style={{ font: 'var(--text-h1)', color: 'var(--color-text-primary)' }}></h1>
<div style={{ display: 'flex', gap: 8 }}>
{['all', 'pending', 'active', 'suspended', 'expired'].map(f => (
<button key={f} onClick={() => setFilter(f)} style={{
padding: '6px 14px', border: 'none', borderRadius: 'var(--radius-full)',
background: filter === f ? 'var(--color-primary)' : 'var(--color-gray-100)',
color: filter === f ? 'white' : 'var(--color-text-secondary)',
cursor: 'pointer', font: 'var(--text-label-sm)',
}}>
{f === 'all' ? '全部' : statusLabels[f]}
</button>
))}
</div>
</div>
{/* Coupon Table */}
<div style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-md)', border: '1px solid var(--color-border-light)', overflow: 'hidden' }}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '1px solid var(--color-border-light)' }}>
{['券ID', '发行方', '券名称', '模板', '面值', '发行量', '已售', '已核销', '状态', '操作'].map(h => (
<th key={h} style={{ padding: '12px 16px', textAlign: 'left', font: 'var(--text-label-sm)', color: 'var(--color-text-tertiary)' }}>{h}</th>
))}
</tr>
</thead>
<tbody>
{filtered.map(coupon => (
<tr key={coupon.id} style={{ borderBottom: '1px solid var(--color-border-light)' }}>
<td style={cellStyle}><span style={{ font: 'var(--text-label-sm)', fontFamily: 'var(--font-family-mono)' }}>{coupon.id}</span></td>
<td style={cellStyle}>{coupon.issuer}</td>
<td style={cellStyle}><strong>{coupon.name}</strong></td>
<td style={cellStyle}>{coupon.template}</td>
<td style={cellStyle}>${coupon.faceValue}</td>
<td style={cellStyle}>{coupon.quantity.toLocaleString()}</td>
<td style={cellStyle}>{coupon.sold.toLocaleString()}</td>
<td style={cellStyle}>{coupon.redeemed.toLocaleString()}</td>
<td style={cellStyle}>
<span style={{
display: 'inline-block', padding: '2px 8px', borderRadius: 'var(--radius-full)',
background: `${statusColors[coupon.status]}15`, color: statusColors[coupon.status],
font: 'var(--text-caption)', fontWeight: 600,
}}>{statusLabels[coupon.status]}</span>
</td>
<td style={cellStyle}>
{coupon.status === 'pending' && (
<div style={{ display: 'flex', gap: 8 }}>
<button style={btnStyle('var(--color-success)')}></button>
<button style={btnStyle('var(--color-error)')}></button>
</div>
)}
{coupon.status === 'active' && (
<button style={btnStyle('var(--color-warning)')}></button>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
const cellStyle: React.CSSProperties = { padding: '12px 16px', font: 'var(--text-body)', color: 'var(--color-text-primary)' };
const btnStyle = (color: string): React.CSSProperties => ({
padding: '4px 12px', border: `1px solid ${color}`, borderRadius: 'var(--radius-sm)',
background: 'transparent', color, cursor: 'pointer', font: 'var(--text-label-sm)',
});