gcx/frontend/admin-web/src/views/users/UserManagementPage.tsx

158 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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>
);
};