import { useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Building2, DollarSign, Plus, Trash2, TrendingDown, TrendingUp } from 'lucide-react'; import { adminApi, type TenantManagementRow } from '@/api/adminApi'; import { Breadcrumb, Button, Input, Modal, Select, Table, Tag, Textarea, type TableColumn } from '@/components/ui'; import { formatCents } from '@/utils/currency'; type AdminCustomersPageProps = { basePath?: string; }; type CustomerRow = TenantManagementRow; type RechargeForm = { amount: string; operator: string; remark: string; }; function ConfirmModal({ message, onCancel, onConfirm }: { message: string; onCancel: () => void; onConfirm: () => void }) { return ( 取消确认>} onClose={onCancel} open title="操作确认"> {message} ); } function emptyRechargeForm(): RechargeForm { return { amount: '', operator: '运营', remark: '', }; } export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCustomersPageProps) { const navigate = useNavigate(); const [records, setRecords] = useState([]); const [queryId, setQueryId] = useState(''); const [queryName, setQueryName] = useState(''); const [queryStatus, setQueryStatus] = useState('all'); const [filters, setFilters] = useState({ id: '', name: '', status: 'all' }); const [confirmAction, setConfirmAction] = useState<{ type: 'toggle' | 'delete'; record: CustomerRow } | null>(null); const [rechargeTarget, setRechargeTarget] = useState(null); const [rechargeForm, setRechargeForm] = useState(emptyRechargeForm); const [rechargeError, setRechargeError] = useState(''); const [recharging, setRecharging] = useState(false); const [error, setError] = useState(''); function loadData() { adminApi.listTenantManagementRows() .then((items) => { setRecords(items); setError(''); }) .catch((failure: Error) => setError(failure.message || '企业列表加载失败')); } useEffect(() => { loadData(); }, []); const filteredRecords = useMemo(() => records.filter((record) => { const matchId = filters.id ? record.id.includes(filters.id) : true; const matchName = filters.name ? record.name.includes(filters.name) : true; const matchStatus = filters.status === 'all' ? true : record.status === filters.status; return matchId && matchName && matchStatus; }), [filters, records]); const activeCount = records.filter((record) => record.status === 'active').length; const disabledCount = records.filter((record) => record.status === 'disabled').length; const totalBalance = records.reduce((sum, record) => sum + (record.account?.balanceCents ?? 0), 0); const columns: Array> = [ { key: 'id', title: '企业ID', width: '160px', render: (record) => {record.id} }, { key: 'name', title: '企业名称', width: '260px', render: (record) => {record.name} }, { key: 'balance', title: '当前余额', width: '150px', align: 'right', render: (record) => { const balance = record.account?.balanceCents ?? 0; return ( ¥{formatCents(balance)} {balance < 0 ? 欠费 : null} ); }, }, { key: 'overdraftLimit', title: '透支限额', width: '150px', align: 'right', render: (record) => `¥${formatCents(record.account?.creditCents ?? 0)}` }, { key: 'todaySpend', title: '今日消费', width: '150px', align: 'right', render: (record) => `¥${formatCents(record.todaySpendCents)}` }, { key: 'status', title: '企业状态', width: '130px', render: (record) => {record.status === 'active' ? '正常' : '已禁用'} }, { key: 'actions', title: '操作', align: 'right', width: '300px', render: (record) => ( } onClick={() => openRechargeModal(record)} size="sm" variant="ghost">充值 navigate(`${basePath}/${record.id}/edit`)} size="sm" variant="ghost">编辑 setConfirmAction({ type: 'toggle', record })} size="sm" variant={record.status === 'active' ? 'warning' : 'success'}> {record.status === 'active' ? '禁用' : '启用'} } onClick={() => setConfirmAction({ type: 'delete', record })} size="sm" variant="danger">删除 ), }, ]; function openRechargeModal(record: CustomerRow) { setRechargeTarget(record); setRechargeForm(emptyRechargeForm()); setRechargeError(''); } function updateRechargeForm(key: K, value: RechargeForm[K]) { setRechargeForm((current) => ({ ...current, [key]: value })); setRechargeError(''); } async function submitRecharge() { if (!rechargeTarget) return; const amount = Number(rechargeForm.amount); if (!Number.isFinite(amount) || amount === 0) { setRechargeError('请填写非 0 的充值金额,支持负数冲正'); return; } setRecharging(true); try { await adminApi.createManualRecharge({ tenantId: rechargeTarget.id, amountCents: Math.round(amount * 100), smsUnits: 0, remark: [rechargeForm.operator, rechargeForm.remark].filter(Boolean).join(' / '), }); setRechargeTarget(null); setRechargeForm(emptyRechargeForm()); await loadData(); } catch (failure) { setRechargeError(failure instanceof Error ? failure.message : '企业充值失败'); } finally { setRecharging(false); } } function submitConfirmAction() { if (!confirmAction) return; const action = confirmAction.type === 'delete' ? adminApi.deleteTenant(confirmAction.record.id) : adminApi.changeTenantStatus(confirmAction.record.id, confirmAction.record.status === 'active' ? 'disabled' : 'active'); action.then(() => { setConfirmAction(null); loadData(); }).catch((failure: Error) => setError(failure.message || '企业状态更新失败')); } return ( } onClick={() => navigate(`${basePath}/new`)}>添加企业 {error ? {error} : null} 企业总数{records.length}真实租户数量。 正常运营{activeCount}可正常提交发送任务。 已禁用{disabledCount}已暂停发送能力。 账户余额¥{formatCents(totalBalance)}企业账户余额汇总。 查询条件 setQueryId(event.target.value)} placeholder="请输入企业ID" value={queryId} /> setQueryName(event.target.value)} placeholder="请输入企业名称" value={queryName} /> setQueryStatus(event.target.value)} options={[{ label: '全部状态', value: 'all' }, { label: '正常', value: 'active' }, { label: '禁用', value: 'disabled' }]} value={queryStatus} /> setFilters({ id: queryId, name: queryName, status: queryStatus })} variant="secondary">查询 { setQueryId(''); setQueryName(''); setQueryStatus('all'); setFilters({ id: '', name: '', status: 'all' }); }} variant="ghost">重置 企业列表数据来自租户、账户真实接口。{filteredRecords.length} 条 {confirmAction ? ( setConfirmAction(null)} onConfirm={submitConfirmAction} /> ) : null} {rechargeTarget ? ( setRechargeTarget(null)} variant="ghost">取消 { void submitRecharge(); }}>{recharging ? '充值中...' : '确认充值'} > )} onClose={() => setRechargeTarget(null)} open size="md" title="企业人工充值" > updateRechargeForm('amount', event.target.value)} prefix="¥" required type="number" value={rechargeForm.amount} /> updateRechargeForm('operator', event.target.value)} value={rechargeForm.operator} /> updateRechargeForm('remark', event.target.value)} rows={4} value={rechargeForm.remark} /> {rechargeError ? {rechargeError} : null} ) : null} ); }
{message}
{error}
数据来自租户、账户真实接口。
{rechargeError}