fix: connect remaining sms pages to real backend
This commit is contained in:
@@ -1,84 +1,32 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Clock3, Eye, Search } from 'lucide-react';
|
||||
import { Breadcrumb, Button, DateRangeInput, Input, Modal, Select, Table, Tag, type DateRangeValue, type TableColumn } from '@/components/ui';
|
||||
import { adminApi, type ReportRecord } from '@/api/adminApi';
|
||||
import { Breadcrumb, Button, DateRangeInput, Input, Modal, Table, Tag, type DateRangeValue, type TableColumn } from '@/components/ui';
|
||||
|
||||
type RecordStatus = 'unreported' | 'reporting' | 'success' | 'failed' | 'withdrawn' | 'abandoned';
|
||||
|
||||
type ReportRecord = {
|
||||
id: string;
|
||||
taskId: string;
|
||||
channel: string;
|
||||
enterprise: string;
|
||||
type: '签名' | '引流信息';
|
||||
content: string;
|
||||
carrier: '移动' | '联通' | '电信';
|
||||
status: RecordStatus;
|
||||
submittedAt: string;
|
||||
reportedAt?: string;
|
||||
updatedAt: string;
|
||||
reason?: string;
|
||||
const statusTone: Record<string, 'neutral' | 'info' | 'success' | 'warning' | 'danger'> = {
|
||||
pending: 'neutral',
|
||||
reporting: 'warning',
|
||||
success: 'success',
|
||||
completed: 'success',
|
||||
failed: 'danger',
|
||||
rejected: 'danger',
|
||||
};
|
||||
|
||||
const statusMeta: Record<RecordStatus, { label: string; tone: 'neutral' | 'info' | 'success' | 'warning' | 'danger' }> = {
|
||||
unreported: { label: '未报备', tone: 'neutral' },
|
||||
reporting: { label: '报备中', tone: 'warning' },
|
||||
success: { label: '报备成功', tone: 'success' },
|
||||
failed: { label: '报备失败', tone: 'danger' },
|
||||
withdrawn: { label: '被清退', tone: 'danger' },
|
||||
abandoned: { label: '放弃报备', tone: 'warning' },
|
||||
};
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '全部状态', value: 'all' },
|
||||
{ label: '未报备', value: 'unreported' },
|
||||
{ label: '报备中', value: 'reporting' },
|
||||
{ label: '报备成功', value: 'success' },
|
||||
{ label: '报备失败', value: 'failed' },
|
||||
{ label: '被清退', value: 'withdrawn' },
|
||||
{ label: '放弃报备', value: 'abandoned' },
|
||||
];
|
||||
|
||||
const carrierOptions = [
|
||||
{ label: '全部运营商', value: 'all' },
|
||||
{ label: '移动', value: '移动' },
|
||||
{ label: '联通', value: '联通' },
|
||||
{ label: '电信', value: '电信' },
|
||||
];
|
||||
|
||||
const initialRecords: ReportRecord[] = [
|
||||
{ id: 'RPT-REC-001', taskId: 'RPT-TASK-20260628-003', channel: '移动-行北-上海甲医院-38', enterprise: '超感世纪互三网', type: '签名', content: '【促销活动】', carrier: '移动', status: 'success', submittedAt: '2026-06-28 12:20:16', reportedAt: '2026-06-29 10:05:44', updatedAt: '2026-06-29 10:05:44' },
|
||||
{ id: 'RPT-REC-002', taskId: 'RPT-TASK-20260629-006', channel: '联通-行政-杭州甲医院-37', enterprise: '重庆进载数智', type: '引流信息', content: 'https://service.example.com', carrier: '联通', status: 'reporting', submittedAt: '2026-06-29 16:02:11', updatedAt: '2026-06-29 16:02:11' },
|
||||
{ id: 'RPT-REC-003', taskId: 'RPT-TASK-20260629-006', channel: '联通-行政-杭州甲医院-37', enterprise: '重庆进载数智', type: '签名', content: '【客户服务】', carrier: '联通', status: 'failed', submittedAt: '2026-06-29 16:02:11', reportedAt: '2026-06-30 09:12:35', updatedAt: '2026-06-30 09:12:35', reason: '责任人身份证照片不清晰。' },
|
||||
{ id: 'RPT-REC-004', taskId: 'RPT-TASK-20260630-001', channel: '行北-集市三甲医院-39', enterprise: '上海XXXXX科技有限公司', type: '签名', content: '【科技公司】', carrier: '移动', status: 'unreported', submittedAt: '2026-06-30 09:12:00', updatedAt: '2026-06-30 09:12:00' },
|
||||
];
|
||||
|
||||
function RemarkCell({ value }: { value?: string }) {
|
||||
return (
|
||||
<div className={['admin-remark-cell', value ? '' : 'admin-remark-cell--empty'].filter(Boolean).join(' ')}>
|
||||
{value || '暂无备注'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RecordDetailModal({ record, onClose }: { record: ReportRecord; onClose: () => void }) {
|
||||
return (
|
||||
<Modal footer={<Button onClick={onClose}>关闭</Button>} onClose={onClose} open size="xl" title={<div className="template-modal-title"><h2>报备记录详情</h2><p>{record.id}</p></div>}>
|
||||
<div className="report-record-detail">
|
||||
<div className="detail-grid">
|
||||
<div><span>任务编号</span><strong>{record.taskId}</strong></div>
|
||||
<div><span>通道</span><strong>{record.channel}</strong></div>
|
||||
<div><span>企业</span><strong>{record.enterprise}</strong></div>
|
||||
<div><span>报备对象</span><strong>{record.type} {record.content}</strong></div>
|
||||
<div><span>运营商</span><strong>{record.carrier}</strong></div>
|
||||
<div><span>当前状态</span><Tag tone={statusMeta[record.status].tone}>{statusMeta[record.status].label}</Tag></div>
|
||||
<div><span>提交时间</span><strong>{record.submittedAt}</strong></div>
|
||||
<div><span>回执时间</span><strong>{record.reportedAt ?? '-'}</strong></div>
|
||||
<div><span>通道</span><strong>{record.channelId}</strong></div>
|
||||
<div><span>动作</span><strong>{record.action}</strong></div>
|
||||
<div><span>状态前</span><strong>{record.statusBefore ?? '-'}</strong></div>
|
||||
<div><span>状态后</span><strong>{record.statusAfter ?? '-'}</strong></div>
|
||||
<div className="detail-grid__wide"><span>失败/备注原因</span><strong>{record.reason ?? '-'}</strong></div>
|
||||
</div>
|
||||
<section className="report-history">
|
||||
<h3><Clock3 size={17} />状态历史</h3>
|
||||
<div><span>{record.submittedAt}</span><strong>生成记录</strong><em>由报备任务生成并等待导出。</em></div>
|
||||
<div><span>{record.reportedAt ?? record.updatedAt}</span><strong>{statusMeta[record.status].label}</strong><em>{record.reason ?? '运营商回执同步。'}</em></div>
|
||||
<div><span>{record.createdAt ?? '-'}</span><strong>{record.action}</strong><em>{record.reason ?? '系统记录真实报备状态变化。'}</em></div>
|
||||
</section>
|
||||
</div>
|
||||
</Modal>
|
||||
@@ -86,31 +34,40 @@ function RecordDetailModal({ record, onClose }: { record: ReportRecord; onClose:
|
||||
}
|
||||
|
||||
export function AdminReportRecordsPage() {
|
||||
const [records] = useState(initialRecords);
|
||||
const [records, setRecords] = useState<ReportRecord[]>([]);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [carrier, setCarrier] = useState('all');
|
||||
const [status, setStatus] = useState('all');
|
||||
const [dateRange, setDateRange] = useState<DateRangeValue>({});
|
||||
const [detail, setDetail] = useState<ReportRecord | null>(null);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
function loadData() {
|
||||
adminApi.listReportRecords()
|
||||
.then((items) => {
|
||||
setRecords(items);
|
||||
setError('');
|
||||
})
|
||||
.catch((failure: Error) => setError(failure.message || '报备记录加载失败'));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const filteredRecords = useMemo(() => records.filter((record) => {
|
||||
const text = `${record.taskId}${record.channel}${record.enterprise}${record.content}`;
|
||||
const date = record.submittedAt.slice(0, 10);
|
||||
const text = `${record.taskId}${record.channelId}${record.action}${record.reason ?? ''}`;
|
||||
const date = record.createdAt?.slice(0, 10) ?? '';
|
||||
return (!keyword || text.includes(keyword))
|
||||
&& (carrier === 'all' || record.carrier === carrier)
|
||||
&& (status === 'all' || record.status === status)
|
||||
&& (!dateRange.start || date >= dateRange.start)
|
||||
&& (!dateRange.end || date <= dateRange.end);
|
||||
}), [carrier, dateRange.end, dateRange.start, keyword, records, status]);
|
||||
}), [dateRange.end, dateRange.start, keyword, records]);
|
||||
|
||||
const columns: Array<TableColumn<ReportRecord>> = [
|
||||
{ key: 'object', title: '报备对象', width: '260px', render: (record) => <div className="admin-task-enterprise"><strong>{record.content}</strong><span>{record.type} / {record.enterprise}</span></div> },
|
||||
{ key: 'task', title: '任务编号', width: '190px', render: (record) => <strong className="admin-task-id">{record.taskId}</strong> },
|
||||
{ key: 'channel', title: '通道', width: '230px', render: (record) => record.channel },
|
||||
{ key: 'carrier', title: '运营商', width: '90px', render: (record) => <Tag tone="info">{record.carrier}</Tag> },
|
||||
{ key: 'status', title: '状态', width: '110px', render: (record) => <Tag tone={statusMeta[record.status].tone}>{statusMeta[record.status].label}</Tag> },
|
||||
{ key: 'time', title: '时间', width: '190px', render: (record) => <div className="report-task-time"><span>提交 {record.submittedAt}</span><span>回执 {record.reportedAt ?? '-'}</span></div> },
|
||||
{ key: 'reason', title: '备注', width: '280px', render: (record) => <RemarkCell value={record.reason} /> },
|
||||
{ key: 'channel', title: '通道', width: '230px', render: (record) => record.channelId },
|
||||
{ key: 'action', title: '动作', width: '150px', render: (record) => record.action },
|
||||
{ key: 'status', title: '状态变化', width: '180px', render: (record) => <Tag tone={statusTone[record.statusAfter ?? 'pending'] ?? 'info'}>{`${record.statusBefore ?? '-'} -> ${record.statusAfter ?? '-'}`}</Tag> },
|
||||
{ key: 'time', title: '记录时间', width: '190px', render: (record) => record.createdAt ?? '-' },
|
||||
{ key: 'reason', title: '备注', render: (record) => record.reason ?? '-' },
|
||||
{ key: 'actions', title: '操作', align: 'right', width: '120px', render: (record) => <Button icon={<Eye size={14} />} onClick={() => setDetail(record)} size="sm" variant="ghost">详情</Button> },
|
||||
];
|
||||
|
||||
@@ -122,15 +79,14 @@ export function AdminReportRecordsPage() {
|
||||
<h1>报备记录</h1>
|
||||
</div>
|
||||
</div>
|
||||
{error ? <p className="form-error">{error}</p> : null}
|
||||
|
||||
<div className="surface admin-task-filter">
|
||||
<Input label="任务/通道/企业/签名" onChange={(event) => setKeyword(event.target.value)} placeholder="请输入关键字" value={keyword} />
|
||||
<Select label="运营商" onChange={(event) => setCarrier(event.target.value)} options={carrierOptions} value={carrier} />
|
||||
<Select label="报备状态" onChange={(event) => setStatus(event.target.value)} options={statusOptions} value={status} />
|
||||
<Input label="任务/通道/动作/备注" onChange={(event) => setKeyword(event.target.value)} placeholder="请输入关键字" value={keyword} />
|
||||
<DateRangeInput label="提交时间" onChange={setDateRange} value={dateRange} />
|
||||
<div className="admin-task-filter__actions">
|
||||
<Button icon={<Search size={16} />}>查询</Button>
|
||||
<Button onClick={() => { setKeyword(''); setCarrier('all'); setStatus('all'); setDateRange({}); }} variant="ghost">重置</Button>
|
||||
<Button icon={<Search size={16} />} onClick={loadData}>查询</Button>
|
||||
<Button onClick={() => { setKeyword(''); setDateRange({}); }} variant="ghost">重置</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user