import { useEffect, useMemo, useState } from 'react'; import { adminApi, type SmsMessageRecord, type SmsMessageSegmentAudit } from '@/api/adminApi'; import { Breadcrumb, type DateRangeValue } from '@/components/ui'; import { SendDetailModal } from './sms-records/SendDetailModal'; import { SmsRecordFilter } from './sms-records/SmsRecordFilter'; import { SmsRecordList } from './sms-records/SmsRecordList'; import { defaultSmsRecordDateRange } from './sms-records/smsRecordModel'; import type { ApplicationOption, MessageFilters, TenantOption } from './sms-records/smsRecordTypes'; import './sms-records/AdminSmsRecordsPage.css'; const pageSize = 25; export function AdminSmsRecordsPage() { const [records, setRecords] = useState([]); const [enterprise, setEnterprise] = useState('all'); const [application, setApplication] = useState('all'); const [dateRange, setDateRange] = useState(defaultSmsRecordDateRange); const [phoneKeyword, setPhoneKeyword] = useState(''); const [contentKeyword, setContentKeyword] = useState(''); const [channelKeyword, setChannelKeyword] = useState(''); const [carrier, setCarrier] = useState('all'); const [status, setStatus] = useState('all'); const [selectedRecord, setSelectedRecord] = useState(null); const [segmentAudits, setSegmentAudits] = useState([]); const [segmentLoading, setSegmentLoading] = useState(false); const [error, setError] = useState(''); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [filterTenants, setFilterTenants] = useState([]); const [filterApplications, setFilterApplications] = useState([]); function currentFilters(): MessageFilters { return { tenantId: enterprise === 'all' ? undefined : enterprise, applicationId: application === 'all' ? undefined : application, phoneNumber: phoneKeyword || undefined, contentKeyword: contentKeyword || undefined, channelKeyword: channelKeyword || undefined, carrier: carrier === 'all' ? undefined : carrier, queuedAtFrom: dateRange.start, queuedAtTo: dateRange.end, status: status === 'all' ? undefined : status, }; } function loadData(filters = currentFilters(), targetPage = page) { setLoading(true); adminApi.listOperationMessages({ ...filters, page: targetPage, pageSize }) .then((result) => { setRecords(result.items); setTotal(result.total); setSelectedRecord((current) => current ? result.items.find((item) => item.id === current.id) ?? null : null); setError(''); }) .catch((failure: Error) => setError(failure.message || '短信记录加载失败')) .finally(() => setLoading(false)); } useEffect(() => { loadData(currentFilters(), page); }, [page]); useEffect(() => { Promise.all([adminApi.listTenants(), adminApi.listEnterpriseApplicationOptions()]) .then(([tenants, applications]) => { setFilterTenants(tenants .filter((item) => item.status !== 'deleted') .map((item) => ({ id: item.id, name: item.name }))); setFilterApplications(applications .filter((item) => item.status !== 'deleted') .map((item) => ({ id: item.id, tenantId: item.tenantId, name: item.name }))); }) .catch((failure: Error) => setError(failure.message || '短信记录筛选项加载失败')); }, []); useEffect(() => { if (!selectedRecord) { setSegmentAudits([]); return; } setSegmentLoading(true); adminApi.listMessageSegmentAudits({ messageRecordId: selectedRecord.id }) .then((items) => { setSegmentAudits(items); setError(''); }) .catch((failure: Error) => setError(failure.message || '分片审计加载失败')) .finally(() => setSegmentLoading(false)); }, [selectedRecord]); const enterpriseOptions = useMemo( () => [{ label: '全部企业', value: 'all' }, ...filterTenants.map((item) => ({ label: item.name, value: item.id }))], [filterTenants], ); const applicationOptions = useMemo( () => [{ label: '全部应用', value: 'all' }, ...filterApplications .filter((item) => enterprise === 'all' || item.tenantId === enterprise) .map((item) => ({ label: item.name, value: item.id }))], [enterprise, filterApplications], ); const totalPages = Math.max(1, Math.ceil(total / pageSize)); const currentPage = Math.min(page, totalPages); function resetFilters() { const defaultDateRange = defaultSmsRecordDateRange(); setEnterprise('all'); setApplication('all'); setDateRange(defaultDateRange); setPhoneKeyword(''); setContentKeyword(''); setChannelKeyword(''); setCarrier('all'); setStatus('all'); if (page !== 1) setPage(1); else loadData({ queuedAtFrom: defaultDateRange.start, queuedAtTo: defaultDateRange.end }, 1); } async function exportRecords() { try { const blob = await adminApi.exportOperationMessages(currentFilters()); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = `sms-records-${new Date().toISOString().slice(0, 10)}.csv`; anchor.click(); URL.revokeObjectURL(url); } catch (failure) { setError(failure instanceof Error ? failure.message : '短信记录导出失败'); } } return (

短信记录

{error ?

{error}

: null} { setEnterprise(value); setApplication('all'); }} onPhoneKeywordChange={setPhoneKeyword} onQuery={() => { if (page !== 1) setPage(1); else loadData(currentFilters(), 1); }} onReset={resetFilters} onStatusChange={setStatus} /> void exportRecords()} onOpenDetail={setSelectedRecord} onPageChange={setPage} /> {selectedRecord ? ( setSelectedRecord(null)} record={selectedRecord} segmentAudits={segmentAudits} segmentLoading={segmentLoading} /> ) : null}
); }