import { useEffect, useState } from 'react'; import { Button, Descriptions, Drawer, Form, Input, Segmented, Space, Table, Tag, Typography } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { USER_LOG_CATEGORY_OPTIONS, resolveUserLogCategory, USER_LOG_CATEGORY_LABELS, type UserLogCategory } from '../lib/user-log'; import { useSearchParams } from 'react-router-dom'; import { request } from '../lib/api'; import { AdminCellLine } from '../components/AdminCellLine'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; type Row = { id: string; userId: string | null; userNo: string | null; phone: string | null; nickname: string | null; category: UserLogCategory | null; eventName: string; clientApp: string | null; refType: string | null; refId: string | null; extraJson: Record | null; createdAt: string; }; const CATEGORY_LABELS = USER_LOG_CATEGORY_LABELS; function summarizeExtra(json: Record | null) { if (!json) return '—'; const text = JSON.stringify(json); return text.length > 80 ? `${text.slice(0, 80)}…` : text; } export default function UserLogsPage() { const [searchParams, setSearchParams] = useSearchParams(); const [form] = Form.useForm(); const [category, setCategory] = useState(searchParams.get('category') ?? ''); const [filters, setFilters] = useState>(() => ({ userId: searchParams.get('userId') ?? '', phone: searchParams.get('phone') ?? '', userNo: searchParams.get('userNo') ?? '', eventName: searchParams.get('eventName') ?? '', })); const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList( '/admin/logs/users', () => { const qs = new URLSearchParams(); if (filters.userId) qs.set('userId', filters.userId); if (filters.phone) qs.set('phone', filters.phone); if (filters.userNo) qs.set('userNo', filters.userNo); if (filters.eventName) qs.set('eventName', filters.eventName); if (category) qs.set('category', category); return qs; }, [filters, category], ); const [detail, setDetail] = useState | null>(null); const [drawerOpen, setDrawerOpen] = useState(false); useEffect(() => { form.setFieldsValue(filters); }, [form, filters]); const columns: ColumnsType = [ { title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime }, { title: '用户', width: 180, ellipsis: true, render: (_, r) => ( ), }, { title: '分类', dataIndex: 'category', width: 100, render: (v: UserLogCategory | null, r) => ( {CATEGORY_LABELS[v ?? ''] || resolveUserLogCategory(r.eventName) || '其他'} ), }, { title: '事件', dataIndex: 'eventName', width: 160 }, { title: '关联', width: 120, render: (_, r) => (r.refType && r.refId ? `${r.refType}#${r.refId}` : '—'), }, { title: '摘要', ellipsis: true, render: (_, r) => summarizeExtra(r.extraJson), }, { title: '操作', width: 80, render: (_, row) => ( ), }, ]; return (
用户日志 ({ value: o.value, label: o.label }))} value={category} onChange={(v) => { setCategory(String(v)); setPage(1); const next = new URLSearchParams(searchParams); if (v) next.set('category', String(v)); else next.delete('category'); setSearchParams(next); }} />
{ setFilters(v); setPage(1); const next = new URLSearchParams(searchParams); for (const key of ['userId', 'phone', 'userNo', 'eventName'] as const) { if (v[key]) next.set(key, v[key]); else next.delete(key); } setSearchParams(next); }}>
{ setPage(p); setPageSize(ps); } }} /> setDrawerOpen(false)}> {detail && ( {String(detail.id)} {String(detail.nickname || detail.userNo || detail.userId || '—')} {String(detail.phone || '—')} {CATEGORY_LABELS[String(detail.category ?? '')] || String(detail.category || '—')} {String(detail.eventName)} {String(detail.clientApp || '—')} {detail.refType ? `${String(detail.refType)}#${String(detail.refId)}` : '—'} {fmtTime(String(detail.createdAt))}
                {JSON.stringify(detail.extraJson ?? {}, null, 2)}
              
)}
); }