[feat]:[FL-214][任务监控页面改造]

改动内容:
1. 移除生成时间、执行节点、在线、离线、任务统计信息的展示
2. 移除自动刷新按钮和自动刷新功能
3. 过滤"到期定时任务派发"任务,不在页面上展示
4. 移除监控分组、接收时间、位置参数、关键字参数、异常信息列
5. 将"查看日志"按钮重命名为"日志",增加"详情"按钮(查看任务参数),增加"异常"按钮(查看异常日志)
6. 同步更新移动端卡片视图

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
chengkai3
2026-06-28 16:06:18 +08:00
parent e0d357eb52
commit d69c449821
+143 -92
View File
@@ -156,7 +156,7 @@ export default function AdminTaskMonitorPage() {
const isMobile = useMobileDetection(); const isMobile = useMobileDetection();
const canRead = hasPermission("celery.read") || hasPermission("celery.manage"); const canRead = hasPermission("celery.read") || hasPermission("celery.manage");
const [autoRefresh, setAutoRefresh] = useState(true); const [autoRefresh, setAutoRefresh] = useState(false);
const [workerKeyword, setWorkerKeyword] = useState(""); const [workerKeyword, setWorkerKeyword] = useState("");
const [queueKeyword, setQueueKeyword] = useState(""); const [queueKeyword, setQueueKeyword] = useState("");
const [taskKeyword, setTaskKeyword] = useState(""); const [taskKeyword, setTaskKeyword] = useState("");
@@ -174,6 +174,10 @@ export default function AdminTaskMonitorPage() {
const [logModalContent, setLogModalContent] = useState(""); const [logModalContent, setLogModalContent] = useState("");
const [logModalTaskId, setLogModalTaskId] = useState(""); const [logModalTaskId, setLogModalTaskId] = useState("");
const [logModalLoading, setLogModalLoading] = useState(false); const [logModalLoading, setLogModalLoading] = useState(false);
const [detailModalVisible, setDetailModalVisible] = useState(false);
const [detailModalTask, setDetailModalTask] = useState<TaskTableRow | null>(null);
const [exceptionModalVisible, setExceptionModalVisible] = useState(false);
const [exceptionModalTask, setExceptionModalTask] = useState<TaskTableRow | null>(null);
const resetTaskListPagination = useCallback(() => { const resetTaskListPagination = useCallback(() => {
setPagination((prev) => ({ ...prev, current: 1 })); setPagination((prev) => ({ ...prev, current: 1 }));
@@ -210,6 +214,26 @@ export default function AdminTaskMonitorPage() {
setLogModalContent(""); setLogModalContent("");
}; };
const handleViewDetail = (task: TaskTableRow) => {
setDetailModalTask(task);
setDetailModalVisible(true);
};
const handleCloseDetailModal = () => {
setDetailModalVisible(false);
setDetailModalTask(null);
};
const handleViewException = (task: TaskTableRow) => {
setExceptionModalTask(task);
setExceptionModalVisible(true);
};
const handleCloseExceptionModal = () => {
setExceptionModalVisible(false);
setExceptionModalTask(null);
};
const workersOverviewQuery = useQuery({ const workersOverviewQuery = useQuery({
queryKey: ["flower-workers-overview"], queryKey: ["flower-workers-overview"],
enabled: Boolean(user) && canRead, enabled: Boolean(user) && canRead,
@@ -274,13 +298,6 @@ export default function AdminTaskMonitorPage() {
width: 110, width: 110,
render: (value: string) => renderTaskStateTag(value), render: (value: string) => renderTaskStateTag(value),
}, },
{
title: "监控分组",
dataIndex: "source",
key: "source",
width: 120,
render: (value: string) => renderTaskSourceTag(value),
},
{ {
title: "队列", title: "队列",
dataIndex: "queue_name", dataIndex: "queue_name",
@@ -295,13 +312,6 @@ export default function AdminTaskMonitorPage() {
width: 220, width: 220,
render: (value: string) => value || "-", render: (value: string) => value || "-",
}, },
{
title: "接收时间",
dataIndex: "received_at",
key: "received_at",
width: 170,
render: (value: string | null) => formatDateTime(value),
},
{ {
title: "开始时间", title: "开始时间",
dataIndex: "started_at", dataIndex: "started_at",
@@ -323,43 +333,23 @@ export default function AdminTaskMonitorPage() {
width: 110, width: 110,
render: (value: number | null) => formatTaskMonitorDuration(value), render: (value: number | null) => formatTaskMonitorDuration(value),
}, },
{
title: "位置参数",
dataIndex: "args_text",
key: "args_text",
width: 220,
render: (value: string | null) => (value ? <Text ellipsis={{ tooltip: value }}>{value}</Text> : "-"),
},
{
title: "关键字参数",
dataIndex: "kwargs_text",
key: "kwargs_text",
width: 220,
render: (value: string | null) => (value ? <Text ellipsis={{ tooltip: value }}>{value}</Text> : "-"),
},
{
title: "异常信息",
dataIndex: "exception_text",
key: "exception_text",
width: 220,
render: (value: string | null) =>
value ? (
<Text type="danger" ellipsis={{ tooltip: value }}>
{value}
</Text>
) : (
"-"
),
},
{ {
title: "操作", title: "操作",
key: "actions", key: "actions",
width: 100, width: 180,
fixed: "right", fixed: "right",
render: (_: unknown, record: TaskTableRow) => ( render: (_: unknown, record: TaskTableRow) => (
<Button size="small" onClick={() => handleViewLog(record.task_id)}> <Space size={4}>
<Button size="small" onClick={() => handleViewLog(record.task_id)}>
</Button>
</Button>
<Button size="small" onClick={() => handleViewDetail(record)}>
</Button>
<Button size="small" onClick={() => handleViewException(record)} disabled={!record.exception_text}>
</Button>
</Space>
), ),
}, },
], ],
@@ -401,7 +391,10 @@ export default function AdminTaskMonitorPage() {
rows.push(...toTaskRows(overview.worker, "SCHEDULED", overview.scheduled_tasks)); rows.push(...toTaskRows(overview.worker, "SCHEDULED", overview.scheduled_tasks));
rows.push(...toTaskRows(overview.worker, "RECENT", overview.recent_tasks)); rows.push(...toTaskRows(overview.worker, "RECENT", overview.recent_tasks));
} }
return rows.filter((task) => task.name !== "app.tasks.worker_registry_tasks.sweep_worker_registry_offline"); return rows.filter((task) =>
task.name !== "app.tasks.worker_registry_tasks.sweep_worker_registry_offline" &&
task.name !== "app.tasks.scheduled_task_tasks.dispatch_due_scheduled_tasks"
);
}, [allTasksQuery.data]); }, [allTasksQuery.data]);
const filteredTaskRows = useMemo(() => { const filteredTaskRows = useMemo(() => {
@@ -620,7 +613,6 @@ export default function AdminTaskMonitorPage() {
{renderTaskStateTag(task.state)} {renderTaskStateTag(task.state)}
</Space> </Space>
} }
extra={renderTaskSourceTag(task.source)}
> >
<Space direction="vertical" size={10} style={{ width: "100%" }}> <Space direction="vertical" size={10} style={{ width: "100%" }}>
<div className="admin-task-monitor-task-card-field"> <div className="admin-task-monitor-task-card-field">
@@ -640,8 +632,8 @@ export default function AdminTaskMonitorPage() {
</Typography.Text> </Typography.Text>
</div> </div>
<div className="admin-task-monitor-task-card-field"> <div className="admin-task-monitor-task-card-field">
<Typography.Text type="secondary"></Typography.Text> <Typography.Text type="secondary"></Typography.Text>
<Typography.Text>{formatDateTime(task.received_at)}</Typography.Text> <Typography.Text>{formatDateTime(task.started_at)}</Typography.Text>
</div> </div>
<div className="admin-task-monitor-task-card-field"> <div className="admin-task-monitor-task-card-field">
<Typography.Text type="secondary"></Typography.Text> <Typography.Text type="secondary"></Typography.Text>
@@ -651,18 +643,18 @@ export default function AdminTaskMonitorPage() {
<Typography.Text type="secondary"></Typography.Text> <Typography.Text type="secondary"></Typography.Text>
<Typography.Text>{formatTaskMonitorDuration(task.runtime_seconds)}</Typography.Text> <Typography.Text>{formatTaskMonitorDuration(task.runtime_seconds)}</Typography.Text>
</div> </div>
{task.exception_text && (
<div className="admin-task-monitor-task-card-field">
<Typography.Text type="secondary"></Typography.Text>
<Typography.Text type="danger" ellipsis={{ tooltip: task.exception_text }}>
{task.exception_text}
</Typography.Text>
</div>
)}
<div style={{ marginTop: 8 }}> <div style={{ marginTop: 8 }}>
<Button size="small" block onClick={() => handleViewLog(task.task_id)}> <Space direction="vertical" size={8} style={{ width: "100%" }}>
<Button size="small" block onClick={() => handleViewLog(task.task_id)}>
</Button>
</Button>
<Button size="small" block onClick={() => handleViewDetail(task)}>
</Button>
<Button size="small" block onClick={() => handleViewException(task)} disabled={!task.exception_text}>
</Button>
</Space>
</div> </div>
</Space> </Space>
</AntCard> </AntCard>
@@ -713,15 +705,6 @@ export default function AdminTaskMonitorPage() {
extra={( extra={(
<Space> <Space>
{(workersOverviewQuery.isFetching || allTasksQuery.isFetching) && <Spin size="small" />} {(workersOverviewQuery.isFetching || allTasksQuery.isFetching) && <Spin size="small" />}
<Button
onClick={() => {
void workersOverviewQuery.refetch();
void allTasksQuery.refetch();
}}
loading={workersOverviewQuery.isFetching || allTasksQuery.isFetching}
>
</Button>
</Space> </Space>
)} )}
> >
@@ -779,13 +762,7 @@ export default function AdminTaskMonitorPage() {
</Form.Item> </Form.Item>
<Form.Item style={{ marginBottom: 0 }}> <Form.Item style={{ marginBottom: 0 }}>
<Space size={12} wrap> <Button onClick={handleResetFilters}></Button>
<Space size={8}>
<Text></Text>
<Switch checked={autoRefresh} onChange={setAutoRefresh} />
</Space>
<Button onClick={handleResetFilters}></Button>
</Space>
</Form.Item> </Form.Item>
</Form> </Form>
) : ( ) : (
@@ -841,26 +818,12 @@ export default function AdminTaskMonitorPage() {
/> />
</Form.Item> </Form.Item>
<Form.Item>
<Space size={8}>
<Text></Text>
<Switch checked={autoRefresh} onChange={setAutoRefresh} />
</Space>
</Form.Item>
<Form.Item> <Form.Item>
<Button onClick={handleResetFilters}></Button> <Button onClick={handleResetFilters}></Button>
</Form.Item> </Form.Item>
</Form> </Form>
)} )}
<Space className="mt-4" size={[16, 8]} wrap>
<Text type="secondary">{formatDateTime(workersOverview?.generated_at)}</Text>
<Text type="secondary">{filteredWorkers.length}/{workersOverview?.summary.total ?? 0}</Text>
<Text type="secondary">线{workersOverview?.summary.online ?? 0}</Text>
<Text type="secondary">线{workersOverview?.summary.offline ?? 0}</Text>
<Text type="secondary">{filteredTaskRows.length} </Text>
</Space>
{!workersOverview && !workersOverviewQuery.isFetching && ( {!workersOverview && !workersOverviewQuery.isFetching && (
<div className="mt-4"> <div className="mt-4">
@@ -981,6 +944,94 @@ export default function AdminTaskMonitorPage() {
</pre> </pre>
)} )}
</Modal> </Modal>
<Modal
title={`任务参数详情 - ${detailModalTask?.task_id || ""}`}
open={detailModalVisible}
onCancel={handleCloseDetailModal}
footer={[
<Button key="close" onClick={handleCloseDetailModal}>
</Button>,
]}
width={800}
style={{ top: 20 }}
>
<Space direction="vertical" size={16} style={{ width: "100%" }}>
<div>
<Typography.Text strong></Typography.Text>
<Typography.Text>{getTaskDisplayName(detailModalTask?.name)}</Typography.Text>
</div>
<div>
<Typography.Text strong></Typography.Text>
<pre
style={{
marginTop: "8px",
padding: "12px",
backgroundColor: "#f5f5f5",
borderRadius: "4px",
fontSize: "12px",
lineHeight: "1.5",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
maxHeight: "30vh",
overflow: "auto",
}}
>
{detailModalTask?.args_text || "无"}
</pre>
</div>
<div>
<Typography.Text strong></Typography.Text>
<pre
style={{
marginTop: "8px",
padding: "12px",
backgroundColor: "#f5f5f5",
borderRadius: "4px",
fontSize: "12px",
lineHeight: "1.5",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
maxHeight: "30vh",
overflow: "auto",
}}
>
{detailModalTask?.kwargs_text || "无"}
</pre>
</div>
</Space>
</Modal>
<Modal
title={`任务异常信息 - ${exceptionModalTask?.task_id || ""}`}
open={exceptionModalVisible}
onCancel={handleCloseExceptionModal}
footer={[
<Button key="close" onClick={handleCloseExceptionModal}>
</Button>,
]}
width={800}
style={{ top: 20 }}
>
<pre
style={{
maxHeight: "60vh",
overflow: "auto",
padding: "16px",
backgroundColor: "#fff2f0",
borderRadius: "4px",
fontSize: "12px",
lineHeight: "1.5",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
color: "#cf1322",
}}
>
{exceptionModalTask?.exception_text || "无异常信息"}
</pre>
</Modal>
</div> </div>
); );
} }