158 lines
6.2 KiB
TypeScript
158 lines
6.2 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState } from 'react';
|
||
|
||
/**
|
||
* D3. 用户管理
|
||
*
|
||
* 用户列表(搜索/KYC筛选)、用户详情、KYC审核
|
||
*/
|
||
|
||
interface User {
|
||
id: string;
|
||
phone: string;
|
||
email: string;
|
||
kycLevel: number;
|
||
couponCount: number;
|
||
totalTraded: string;
|
||
riskTags: string[];
|
||
createdAt: string;
|
||
}
|
||
|
||
const mockUsers: User[] = [
|
||
{ id: 'U-001', phone: '138****1234', email: 'john@mail.com', kycLevel: 2, couponCount: 15, totalTraded: '$2,340', riskTags: [], createdAt: '2026-01-10' },
|
||
{ id: 'U-002', phone: '139****5678', email: 'jane@mail.com', kycLevel: 1, couponCount: 8, totalTraded: '$890', riskTags: ['高频交易'], createdAt: '2026-01-15' },
|
||
{ id: 'U-003', phone: '137****9012', email: 'bob@mail.com', kycLevel: 3, couponCount: 42, totalTraded: '$12,450', riskTags: [], createdAt: '2025-12-01' },
|
||
{ id: 'U-004', phone: '136****3456', email: 'alice@mail.com', kycLevel: 0, couponCount: 0, totalTraded: '-', riskTags: [], createdAt: '2026-02-09' },
|
||
];
|
||
|
||
export const UserManagementPage: React.FC = () => {
|
||
const [search, setSearch] = useState('');
|
||
const [kycFilter, setKycFilter] = useState<number | null>(null);
|
||
|
||
const filtered = mockUsers.filter(u => {
|
||
if (search && !u.phone.includes(search) && !u.email.includes(search) && !u.id.includes(search)) return false;
|
||
if (kycFilter !== null && u.kycLevel !== kycFilter) return false;
|
||
return true;
|
||
});
|
||
|
||
const kycBadge = (level: number) => {
|
||
const colors = ['var(--color-gray-400)', 'var(--color-info)', 'var(--color-primary)', 'var(--color-success)'];
|
||
return (
|
||
<span style={{
|
||
padding: '2px 8px',
|
||
borderRadius: 'var(--radius-full)',
|
||
background: `${colors[level]}15`,
|
||
color: colors[level],
|
||
font: 'var(--text-label-sm)',
|
||
fontWeight: 600,
|
||
}}>
|
||
L{level}
|
||
</span>
|
||
);
|
||
};
|
||
|
||
return (
|
||
<div>
|
||
<h1 style={{ font: 'var(--text-h1)', marginBottom: 24 }}>用户管理</h1>
|
||
|
||
{/* Search + Filters */}
|
||
<div style={{ display: 'flex', gap: 12, marginBottom: 20 }}>
|
||
<input
|
||
placeholder="搜索手机号/邮箱/用户ID..."
|
||
value={search}
|
||
onChange={e => setSearch(e.target.value)}
|
||
style={{
|
||
flex: 1,
|
||
maxWidth: 360,
|
||
height: 40,
|
||
border: '1px solid var(--color-border)',
|
||
borderRadius: 'var(--radius-sm)',
|
||
padding: '0 16px',
|
||
font: 'var(--text-body)',
|
||
}}
|
||
/>
|
||
{[null, 0, 1, 2, 3].map(level => (
|
||
<button
|
||
key={level ?? 'all'}
|
||
onClick={() => setKycFilter(level)}
|
||
style={{
|
||
padding: '8px 14px',
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
background: kycFilter === level ? 'var(--color-primary)' : 'var(--color-gray-50)',
|
||
color: kycFilter === level ? 'white' : 'var(--color-text-secondary)',
|
||
cursor: 'pointer',
|
||
font: 'var(--text-label-sm)',
|
||
}}
|
||
>
|
||
{level === null ? '全部' : `L${level}`}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* Users 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={{ background: 'var(--color-gray-50)', borderBottom: '1px solid var(--color-border)' }}>
|
||
{['用户ID', '手机号', '邮箱', 'KYC等级', '持券数', '交易额', '风险标签', '注册时间', '操作'].map(h => (
|
||
<th key={h} style={{
|
||
font: 'var(--text-label-sm)',
|
||
color: 'var(--color-text-tertiary)',
|
||
padding: '12px 14px',
|
||
textAlign: 'left',
|
||
}}>{h}</th>
|
||
))}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{filtered.map(user => (
|
||
<tr key={user.id} style={{ borderBottom: '1px solid var(--color-border-light)' }}>
|
||
<td style={{ font: 'var(--text-body-sm)', fontFamily: 'var(--font-family-mono)', padding: '12px 14px', color: 'var(--color-text-tertiary)' }}>{user.id}</td>
|
||
<td style={{ font: 'var(--text-body-sm)', padding: '12px 14px' }}>{user.phone}</td>
|
||
<td style={{ font: 'var(--text-body-sm)', padding: '12px 14px' }}>{user.email}</td>
|
||
<td style={{ padding: '12px 14px' }}>{kycBadge(user.kycLevel)}</td>
|
||
<td style={{ font: 'var(--text-body-sm)', padding: '12px 14px' }}>{user.couponCount}</td>
|
||
<td style={{ font: 'var(--text-label-sm)', color: 'var(--color-primary)', padding: '12px 14px' }}>{user.totalTraded}</td>
|
||
<td style={{ padding: '12px 14px' }}>
|
||
{user.riskTags.length > 0
|
||
? user.riskTags.map(tag => (
|
||
<span key={tag} style={{
|
||
padding: '2px 6px',
|
||
borderRadius: 'var(--radius-full)',
|
||
background: 'var(--color-error-light)',
|
||
color: 'var(--color-error)',
|
||
font: 'var(--text-caption)',
|
||
marginRight: 4,
|
||
}}>{tag}</span>
|
||
))
|
||
: <span style={{ color: 'var(--color-text-disabled)', font: 'var(--text-caption)' }}>-</span>
|
||
}
|
||
</td>
|
||
<td style={{ font: 'var(--text-body-sm)', color: 'var(--color-text-tertiary)', padding: '12px 14px' }}>{user.createdAt}</td>
|
||
<td style={{ padding: '12px 14px' }}>
|
||
<button style={{
|
||
padding: '4px 12px',
|
||
border: '1px solid var(--color-border)',
|
||
borderRadius: 'var(--radius-sm)',
|
||
background: 'none',
|
||
cursor: 'pointer',
|
||
font: 'var(--text-caption)',
|
||
color: 'var(--color-primary)',
|
||
}}>详情</button>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|