Files
lislgosms/src/apps/admin/AdminSmsTaskProgressPage.tsx
T

180 lines
6.7 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import { adminApi } from '@/api/adminApi';
import { Breadcrumb, type DateRangeValue } from '@/components/ui';
import { SmsTaskFilter } from './sms-task-progress/SmsTaskFilter';
import { SmsTaskTable } from './sms-task-progress/SmsTaskTable';
import { TaskDetailModal } from './sms-task-progress/TaskDetailModal';
import { TaskPhoneListModal } from './sms-task-progress/TaskPhoneListModal';
import { TerminateTaskModal } from './sms-task-progress/TerminateTaskModal';
import { mapTask } from './sms-task-progress/taskModel';
import type { SmsTask } from './sms-task-progress/taskTypes';
import './sms-task-progress/AdminSmsTaskProgressPage.css';
const pageSize = 10;
export function AdminSmsTaskProgressPage() {
const [tasks, setTasks] = useState<SmsTask[]>([]);
const [keyword, setKeyword] = useState('');
const [enterprise, setEnterprise] = useState('all');
const [application, setApplication] = useState('all');
const [status, setStatus] = useState('all');
const [submittedDateRange, setSubmittedDateRange] = useState<DateRangeValue>({});
const [hoveredTaskId, setHoveredTaskId] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [selectedTask, setSelectedTask] = useState<SmsTask | null>(null);
const [phoneTarget, setPhoneTarget] = useState<SmsTask | null>(null);
const [terminateTarget, setTerminateTarget] = useState<SmsTask | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [total, setTotal] = useState(0);
const [filterTenants, setFilterTenants] = useState<string[]>([]);
const [filterApplications, setFilterApplications] = useState<Array<{ tenantName: string; name: string }>>([]);
function loadTasks(targetPage = page) {
setLoading(true);
adminApi.listAdminBatchTasksPage({
keyword: keyword || undefined,
enterpriseKeyword: enterprise === 'all' ? undefined : enterprise,
applicationKeyword: application === 'all' ? undefined : application,
status: status === 'all' ? undefined : status,
createdAtFrom: submittedDateRange.start || undefined,
createdAtTo: submittedDateRange.end || undefined,
page: targetPage,
pageSize,
})
.then((result) => {
setTasks(result.items.map(mapTask));
setTotal(result.total);
setError('');
})
.catch((reason: Error) => setError(reason.message || '短信任务进度加载失败'))
.finally(() => setLoading(false));
}
useEffect(() => {
loadTasks(page);
}, [page]);
useEffect(() => {
Promise.all([adminApi.listTenantOptions(), adminApi.listEnterpriseApplicationOptions()])
.then(([tenants, applications]) => {
const tenantNameById = new Map(tenants.map((item) => [item.id, item.name]));
setFilterTenants(tenants.filter((item) => item.status !== 'deleted').map((item) => item.name));
setFilterApplications(applications
.filter((item) => item.status !== 'deleted')
.map((item) => ({ tenantName: tenantNameById.get(item.tenantId) ?? '', name: item.name })));
})
.catch((failure: Error) => setError(failure.message || '任务筛选项加载失败'));
}, []);
const enterpriseOptions = useMemo(
() => [{ label: '全部企业', value: 'all' }, ...filterTenants.map((name) => ({ label: name, value: name }))],
[filterTenants],
);
const applicationOptions = useMemo(() => {
const names = Array.from(new Set(filterApplications
.filter((item) => enterprise === 'all' || item.tenantName === enterprise)
.map((item) => item.name)));
return [{ label: '全部应用', value: 'all' }, ...names.map((name) => ({ label: name, value: name }))];
}, [enterprise, filterApplications]);
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const currentPage = Math.min(page, totalPages);
function resetFilters() {
setKeyword('');
setEnterprise('all');
setApplication('all');
setStatus('all');
setSubmittedDateRange({});
}
function terminateTask(task: SmsTask) {
adminApi.terminateAdminBatchTask(task.backendId)
.then(() => {
setTerminateTarget(null);
setSelectedTask(null);
loadTasks();
})
.catch((reason: Error) => setError(reason.message || '发送批次终止失败'));
}
return (
<section className="page-stack admin-sms-task-page">
<div className="page-heading">
<div>
<Breadcrumb items={['发送任务', '短信任务进度']} />
<h1>短信任务进度</h1>
</div>
</div>
<SmsTaskFilter
application={application}
applicationOptions={applicationOptions}
enterprise={enterprise}
enterpriseOptions={enterpriseOptions}
keyword={keyword}
status={status}
statusOptions={[
{ label: '全部状态', value: 'all' },
{ label: '待审核', value: 'pending_review' },
{ label: '等待定时发送', value: 'scheduled' },
{ label: '排队中', value: 'queued' },
{ label: '发送中', value: 'sending' },
{ label: '已完成', value: 'finished' },
{ label: '失败', value: 'failed' },
{ label: '已拒绝', value: 'rejected' },
{ label: '已终止', value: 'canceled' },
]}
submittedDateRange={submittedDateRange}
onApplicationChange={setApplication}
onEnterpriseChange={(value) => {
setEnterprise(value);
setApplication('all');
}}
onKeywordChange={setKeyword}
onStatusChange={setStatus}
onQuery={() => {
if (page !== 1) setPage(1);
else loadTasks(1);
}}
onReset={resetFilters}
onSubmittedDateRangeChange={setSubmittedDateRange}
/>
{error ? <p className="form-error">{error}</p> : null}
<SmsTaskTable
currentPage={currentPage}
hoveredTaskId={hoveredTaskId}
loading={loading}
tasks={tasks}
total={total}
totalPages={totalPages}
onHoverTask={setHoveredTaskId}
onOpenDetail={setSelectedTask}
onOpenPhones={setPhoneTarget}
onPageChange={setPage}
onTerminate={setTerminateTarget}
/>
{selectedTask ? <TaskDetailModal onClose={() => setSelectedTask(null)} task={selectedTask} /> : null}
{phoneTarget ? (
<TaskPhoneListModal
onClose={() => setPhoneTarget(null)}
onError={setError}
task={phoneTarget}
/>
) : null}
{terminateTarget ? (
<TerminateTaskModal
onCancel={() => setTerminateTarget(null)}
onConfirm={() => terminateTask(terminateTarget)}
task={terminateTarget}
/>
) : null}
</section>
);
}