import { Fragment, useEffect, useMemo, useState } from 'react'; import { FileText, Search, Smartphone } from 'lucide-react'; import { clientApi, type SmsMessageRecord } from '@/api/adminApi'; import { DateRangeInput, Input, Pagination, QueryPanel, Select, Tag, type DateRangeValue, } from '@/components/ui'; const statusLabelMap: Record = { delivered: '成功', queued: '排队中', submitted: '已提交', accepted: '已受理', unknown: '未知', failed: '失败', rejected: '失败', timeout: '超时', }; const statusToneMap: Record = { delivered: 'success', queued: 'info', submitted: 'info', accepted: 'info', unknown: 'neutral', failed: 'danger', rejected: 'danger', timeout: 'danger', }; const carrierLabelMap: Record = { mobile: '中国移动', unicom: '中国联通', telecom: '中国电信', all: '三网', }; 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: latest?.rawStatus ?? latest?.receiptStatus ?? record.receiptStatus ?? '-', time: latest?.deliveredAt ?? record.deliveredAt, }; } export function ClientSendDetailPage() { const [records, setRecords] = useState([]); const [applicationId, setApplicationId] = useState('all'); const [status, setStatus] = useState('all'); const [dateRange, setDateRange] = useState({}); const [contentKeyword, setContentKeyword] = useState(''); const [phoneKeyword, setPhoneKeyword] = useState(''); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); function loadData() { setLoading(true); clientApi.listMessages({ applicationId: applicationId === 'all' ? undefined : applicationId, phoneNumber: phoneKeyword || undefined, status: status === 'all' ? undefined : status, }) .then((items) => { setRecords(items); setError(''); }) .catch((reason: Error) => setError(reason.message || '短信发送详情加载失败')) .finally(() => setLoading(false)); } useEffect(() => { loadData(); }, [applicationId, phoneKeyword, status]); const applicationOptions = useMemo(() => { const applications = new Map(); records.forEach((item) => { if (item.applicationId) { applications.set(item.applicationId, item.application?.name ?? item.applicationId); } }); return [ { label: '全部应用', value: 'all' }, ...Array.from(applications.entries()).map(([value, label]) => ({ label, value })), ]; }, [records]); const filteredRows = records.filter((item) => { const sentDate = getDate(item.queuedAt); const matchesStartDate = !dateRange.start || sentDate >= dateRange.start; const matchesEndDate = !dateRange.end || sentDate <= dateRange.end; const matchesContent = !contentKeyword || item.content.includes(contentKeyword); return matchesStartDate && matchesEndDate && matchesContent; }); const pageSize = 10; const totalPages = Math.max(1, Math.ceil(filteredRows.length / pageSize)); const currentPage = Math.min(page, totalPages); const visibleRows = filteredRows.slice((currentPage - 1) * pageSize, currentPage * pageSize); useEffect(() => { setPage(1); }, [contentKeyword, dateRange.end, dateRange.start, records.length]); return (

短信发送详情

共找到 {filteredRows.length} 条发送记录} > 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 carrier = record.carrier ? carrierLabelMap[record.carrier] ?? record.carrier : '-'; 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} {carrier} {region} {statusLabelMap[record.status] ?? 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={filteredRows.length} />
); }