[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:
@@ -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) => (
|
||||||
|
<Space size={4}>
|
||||||
<Button size="small" onClick={() => handleViewLog(record.task_id)}>
|
<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 }}>
|
||||||
|
<Space direction="vertical" size={8} style={{ width: "100%" }}>
|
||||||
<Button size="small" block onClick={() => handleViewLog(task.task_id)}>
|
<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>
|
|
||||||
<Space size={8}>
|
|
||||||
<Text>自动刷新</Text>
|
|
||||||
<Switch checked={autoRefresh} onChange={setAutoRefresh} />
|
|
||||||
</Space>
|
|
||||||
<Button onClick={handleResetFilters}>重置筛选</Button>
|
<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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user