data collection page (#31)

* feat: Update site name to DataMate and refine text for AI data processing

* feat: Refactor settings page and implement model access functionality

- Created a new ModelAccess component for managing model configurations.
- Removed the old Settings component and replaced it with a new SettingsPage component that integrates ModelAccess, SystemConfig, and WebhookConfig.
- Added SystemConfig component for managing system settings.
- Implemented WebhookConfig component for managing webhook configurations.
- Updated API functions for model management in settings.apis.ts.
- Adjusted routing to point to the new SettingsPage component.

* feat: Implement Data Collection Page with Task Management and Execution Log

- Created DataCollectionPage component to manage data collection tasks.
- Added TaskManagement and ExecutionLog components for task handling and logging.
- Integrated task operations including start, stop, edit, and delete functionalities.
- Implemented filtering and searching capabilities in task management.
- Introduced SimpleCronScheduler for scheduling tasks with cron expressions.
- Updated CreateTask component to utilize new scheduling and template features.
- Enhanced BasicInformation component to conditionally render fields based on visibility settings.
- Refactored ImportConfiguration component to remove NAS import section.
This commit is contained in:
chenghh-9609
2025-10-28 16:15:06 +08:00
committed by GitHub
parent 1a6e25758e
commit fad76e7477
11 changed files with 1180 additions and 418 deletions

View File

@@ -0,0 +1,238 @@
import {
Card,
Button,
Badge,
Table,
Dropdown,
App,
Tooltip,
Popconfirm,
} from "antd";
import {
DeleteOutlined,
EditOutlined,
EllipsisOutlined,
PauseCircleOutlined,
PauseOutlined,
PlayCircleOutlined,
StopOutlined,
} from "@ant-design/icons";
import { SearchControls } from "@/components/SearchControls";
import {
deleteTaskByIdUsingDelete,
executeTaskByIdUsingPost,
queryTasksUsingGet,
stopTaskByIdUsingPost,
} from "../collection.apis";
import { TaskStatus, type CollectionTask } from "../collection.model";
import { StatusMap, SyncModeMap } from "../collection.const";
import useFetchData from "@/hooks/useFetchData";
import { useNavigate } from "react-router";
import { mapCollectionTask } from "../collection.const";
export default function TaskManagement() {
const { message } = App.useApp();
const navigate = useNavigate();
const filters = [
{
key: "status",
label: "状态筛选",
options: [
{ value: "all", label: "全部状态" },
...Object.values(StatusMap),
],
},
];
const {
loading,
tableData,
pagination,
searchParams,
setSearchParams,
fetchData,
handleFiltersChange,
} = useFetchData(queryTasksUsingGet, mapCollectionTask);
const handleStartTask = async (taskId: string) => {
await executeTaskByIdUsingPost(taskId);
message.success("任务启动请求已发送");
fetchData();
};
const handleStopTask = async (taskId: string) => {
await stopTaskByIdUsingPost(taskId);
message.success("任务停止请求已发送");
fetchData();
};
const handleDeleteTask = async (taskId: string) => {
await deleteTaskByIdUsingDelete(taskId);
message.success("任务已删除");
fetchData();
};
const taskOperations = (record: CollectionTask) => {
const isStopped = record.status === TaskStatus.STOPPED;
const startButton = {
key: "start",
label: "启动",
icon: <PlayCircleOutlined />,
onClick: () => handleStartTask(record.id),
};
const stopButton = {
key: "stop",
label: "停止",
icon: <PauseCircleOutlined />,
onClick: () => handleStopTask(record.id),
};
const items = [
isStopped ? startButton : stopButton,
{
key: "edit",
label: "编辑",
icon: <EditOutlined />,
onClick: () => {
showEditTaskModal(record);
},
},
{
key: "delete",
label: "删除",
danger: true,
icon: <DeleteOutlined />,
confirm: {
title: "确定要删除该任务吗?此操作不可撤销。",
okText: "删除",
cancelText: "取消",
okType: "danger",
},
onClick: () => handleDeleteTask(record.id),
},
];
return items;
};
const columns = [
{
title: "任务名称",
dataIndex: "name",
key: "name",
fixed: "left",
},
{
title: "状态",
dataIndex: "status",
key: "status",
render: (status: string) => (
<Badge text={status.label} color={status.color} />
),
},
{
title: "同步方式",
dataIndex: "syncMode",
key: "syncMode",
render: (text: string) => <span>{SyncModeMap[text]?.label}</span>,
},
{
title: "创建时间",
dataIndex: "createdAt",
key: "createdAt",
},
{
title: "更新时间",
dataIndex: "updatedAt",
key: "updatedAt",
},
{
title: "最近执行ID",
dataIndex: "lastExecutionId",
key: "lastExecutionId",
},
{
title: "描述",
dataIndex: "description",
key: "description",
ellipsis: true,
},
{
title: "操作",
key: "action",
fixed: "right" as const,
render: (_: any, record: CollectionTask) => {
return taskOperations(record).map((op) => {
const button = (
<Tooltip key={op.key} title={op.label}>
<Button
type="text"
icon={op.icon}
danger={op?.danger}
onClick={() => op.onClick(record)}
/>
</Tooltip>
);
if (op.confirm) {
return (
<Popconfirm
key={op.key}
title={op.confirm.title}
okText={op.confirm.okText}
cancelText={op.confirm.cancelText}
okType={op.danger ? "danger" : "primary"}
onConfirm={() => op.onClick(record)}
>
<Tooltip key={op.key} title={op.label}>
<Button type="text" icon={op.icon} danger={op?.danger} />
</Tooltip>
</Popconfirm>
);
}
return button;
});
},
},
];
return (
<div className="space-y-4">
{/* Header Actions */}
<SearchControls
searchTerm={searchParams.keyword}
onSearchChange={(newSearchTerm) =>
setSearchParams((prev) => ({
...prev,
keyword: newSearchTerm,
current: 1,
}))
}
searchPlaceholder="搜索任务名称或描述..."
filters={filters}
onFiltersChange={handleFiltersChange}
showViewToggle={false}
onClearFilters={() =>
setSearchParams((prev) => ({
...prev,
filters: {},
}))
}
/>
{/* Tasks Table */}
<Card>
<Table
columns={columns}
dataSource={tableData}
loading={loading}
rowKey="id"
pagination={{
...pagination,
current: searchParams.current,
pageSize: searchParams.pageSize,
total: pagination.total,
}}
scroll={{ x: "max-content", y: "calc(100vh - 25rem)" }}
/>
</Card>
</div>
);
}