import { Fragment, useEffect, useMemo, useState } from 'react'; import { FileText, Search, Smartphone } from 'lucide-react'; import { clientApi, type SmsMessageRecord } from '@/api/adminApi'; import { DateRangeInput, CarrierTag, Input, Pagination, QueryPanel, Select, Tag, type DateRangeValue, } from '@/components/ui'; import { recentBeijingDateRange } from '@/utils/dateTime'; const statusLabelMap: Record = { delivered: '成功', queued: '排队中', submitted: '已提交', accepted: '已受理', unknown: '未知', failed: '失败', rejected: '失败', timeout: '超时', pending_review: '待审核', scheduled: '等待定时发送', submit_queued: '等待提交', submit_failed: '提交失败', processing: '处理中', sending: '发送中', canceled: '已取消', cancelled: '已取消', }; const statusToneMap: Record = { delivered: 'success', queued: 'info', submitted: 'info', accepted: 'info', unknown: 'neutral', failed: 'danger', rejected: 'danger', timeout: 'danger', }; function getDate(value?: string | null) { return value ? value.slice(0, 10) : ''; } function getReceipt(record: SmsMessageRecord) { const latest = record.receiptRecords?.[0] as { rawStatus?: string; receiptStatus?: string; deliveredAt?: string } | undefined; return { status: receiptStatusLabel(latest?.rawStatus ?? latest?.receiptStatus ?? record.receiptStatus), time: latest?.deliveredAt ?? record.deliveredAt, }; } function receiptStatusLabel(status?: string | null) { if (!status) return '-'; const normalized = status.trim().toUpperCase(); return { DELIVRD: '送达成功', ACCEPTD: '已受理', UNDELIV: '未送达', REJECTD: '已拒绝', EXPIRED: '已过期', DELETED: '已删除', UNKNOWN: '状态未知', }[normalized] ?? statusLabelMap[status.toLowerCase()] ?? '状态未知'; } export function ClientSendDetailPage() { const [records, setRecords] = useState([]); const [applicationId, setApplicationId] = useState('all'); const [status, setStatus] = useState('all'); const [dateRange, setDateRange] = useState(() => recentBeijingDateRange(7)); const [contentKeyword, setContentKeyword] = useState(''); const [phoneKeyword, setPhoneKeyword] = useState(''); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const [applications, setApplications] = useState>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); function loadData(targetPage = page) { setLoading(true); clientApi.listMessages({ applicationId: applicationId === 'all' ? undefined : applicationId, phoneNumber: phoneKeyword || undefined, status: status === 'all' ? undefined : status, contentKeyword: contentKeyword || undefined, queuedAtFrom: dateRange.start || undefined, queuedAtTo: dateRange.end || undefined, page: targetPage, pageSize: 10, }) .then((result) => { setRecords(result.items); setTotal(result.total); setError(''); }) .catch((reason: Error) => setError(reason.message || '短信发送详情加载失败')) .finally(() => setLoading(false)); } useEffect(() => { loadData(page); }, [applicationId, contentKeyword, dateRange.end, dateRange.start, page, phoneKeyword, status]); useEffect(() => { clientApi.listApplicationOptions() .then((items) => setApplications(items.map((item) => ({ id: item.id, name: item.name })))) .catch((reason: Error) => setError(reason.message || '应用列表加载失败')); }, []); const applicationOptions = useMemo(() => { return [ { label: '全部应用', value: 'all' }, ...applications.map((item) => ({ label: item.name, value: item.id })), ]; }, [applications]); const filteredRows = records; const pageSize = 10; const totalPages = Math.max(1, Math.ceil(total / pageSize)); const currentPage = Math.min(page, totalPages); const visibleRows = filteredRows; useEffect(() => { setPage(1); }, [applicationId, contentKeyword, dateRange.end, dateRange.start, phoneKeyword, status]); return (

短信发送详情

共找到 {total} 条发送记录} > setStatus(event.target.value)} options={[ { label: '全部', value: 'all' }, { label: '成功', value: 'delivered' }, { label: '未知', value: 'unknown' }, { label: '失败', value: 'failed' }, ]} value={status} /> setContentKeyword(event.target.value)} placeholder="输入关键词搜索" prefix={} value={contentKeyword} /> setPhoneKeyword(event.target.value)} placeholder="输入手机号搜索" prefix={} value={phoneKeyword} /> {error ?

{error}

: null}
{loading ? ( ) : filteredRows.length === 0 ? ( ) : visibleRows.map((record) => { const receipt = getReceipt(record); const region = record.province ?? '-'; return ( ); })}
应用名称 发送时间 字符数/条数 手机号码 所属运营商 发送地区 发送状态 短信回执 回执时间
正在加载真实发送记录...
暂无发送记录
{record.application?.name ?? record.applicationId ?? '-'} {record.queuedAt.slice(0, 10)} {record.queuedAt.slice(11, 19)} {[...record.content].length}字 {record.billingUnits}条 {record.phoneNumber} {record.carrier ? : '-'} {region} {statusLabelMap[record.status] ?? '状态未知'} {receipt.status} {receipt.time ? ( {receipt.time.slice(0, 10)} {receipt.time.slice(11, 19)} ) : -}
短信内容

{record.content}

= totalPages} onNext={() => setPage((value) => Math.min(totalPages, value + 1))} onPrevious={() => setPage((value) => Math.max(1, value - 1))} page={currentPage} totalPages={totalPages} onPageChange={setPage} previousDisabled={currentPage <= 1} total={total} />
); }