feat: DAG画布嵌入项目详情页,任务节点点击弹详情Modal
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Modal, Descriptions, Spin, Tag, Typography, Empty } from 'antd';
|
||||||
|
import { taskApi } from '@/api';
|
||||||
|
import type { Task } from '@/types';
|
||||||
|
import { TaskStatusTag, PriorityTag } from '@/components/constants';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const { Paragraph, Text } = Typography;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
taskId: number | null;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 任务详情弹窗:在 DAG 画布/项目页内点击任务卡片时弹出,不跳转页面 */
|
||||||
|
export default function TaskDetailModal({ taskId, open, onClose }: Props) {
|
||||||
|
const [task, setTask] = useState<Task | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open || taskId == null) return;
|
||||||
|
setLoading(true);
|
||||||
|
setTask(null);
|
||||||
|
taskApi
|
||||||
|
.get(taskId)
|
||||||
|
.then(setTask)
|
||||||
|
.catch(() => setTask(null))
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
}, [open, taskId]);
|
||||||
|
|
||||||
|
const renderJsonBlock = (raw?: string) => {
|
||||||
|
if (!raw) return <Text type="secondary">无</Text>;
|
||||||
|
let pretty = raw;
|
||||||
|
try {
|
||||||
|
pretty = JSON.stringify(JSON.parse(raw), null, 2);
|
||||||
|
} catch {
|
||||||
|
/* keep raw */
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
background: '#f6f8fa',
|
||||||
|
border: '1px solid #e8e8e8',
|
||||||
|
borderRadius: 6,
|
||||||
|
padding: 12,
|
||||||
|
maxHeight: 260,
|
||||||
|
overflow: 'auto',
|
||||||
|
fontSize: 12,
|
||||||
|
margin: 0,
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pretty}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={task ? `任务 #${task.id} ${task.title}` : '任务详情'}
|
||||||
|
open={open}
|
||||||
|
onCancel={onClose}
|
||||||
|
footer={null}
|
||||||
|
width={720}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div style={{ textAlign: 'center', padding: 40 }}>
|
||||||
|
<Spin />
|
||||||
|
</div>
|
||||||
|
) : !task ? (
|
||||||
|
<Empty description="任务不存在或加载失败" />
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<Descriptions column={2} bordered size="small">
|
||||||
|
<Descriptions.Item label="状态">
|
||||||
|
<TaskStatusTag status={task.status} />
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="优先级">
|
||||||
|
<PriorityTag priority={task.priority} />
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="任务类型">{task.task_type}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="需要审核">
|
||||||
|
{task.requires_review ? (
|
||||||
|
<Tag color="purple">是</Tag>
|
||||||
|
) : (
|
||||||
|
<Tag>否</Tag>
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
{task.review_status && (
|
||||||
|
<Descriptions.Item label="审核状态">{task.review_status}</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
{task.worker_id && (
|
||||||
|
<Descriptions.Item label="AI Worker">#{task.worker_id}</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
{task.due_date && (
|
||||||
|
<Descriptions.Item label="截止时间">
|
||||||
|
{dayjs(task.due_date).format('YYYY-MM-DD HH:mm')}
|
||||||
|
</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
{task.started_at && (
|
||||||
|
<Descriptions.Item label="开始时间">
|
||||||
|
{dayjs(task.started_at).format('YYYY-MM-DD HH:mm')}
|
||||||
|
</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
{task.completed_at && (
|
||||||
|
<Descriptions.Item label="完成时间">
|
||||||
|
{dayjs(task.completed_at).format('YYYY-MM-DD HH:mm')}
|
||||||
|
</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
<Descriptions.Item label="创建时间">
|
||||||
|
{dayjs(task.created_at).format('YYYY-MM-DD HH:mm')}
|
||||||
|
</Descriptions.Item>
|
||||||
|
{task.description && (
|
||||||
|
<Descriptions.Item label="描述" span={2}>
|
||||||
|
{task.description}
|
||||||
|
</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
</Descriptions>
|
||||||
|
|
||||||
|
<div style={{ marginTop: 16 }}>
|
||||||
|
<Paragraph strong style={{ marginBottom: 8 }}>
|
||||||
|
输入(Prompt / 参数)
|
||||||
|
</Paragraph>
|
||||||
|
{renderJsonBlock(task.input_data)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: 16 }}>
|
||||||
|
<Paragraph strong style={{ marginBottom: 8 }}>
|
||||||
|
输出结果
|
||||||
|
</Paragraph>
|
||||||
|
{renderJsonBlock(task.output_data)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user