init datamate

This commit is contained in:
Dallas98
2025-10-21 23:00:48 +08:00
commit 1c97afed7d
692 changed files with 135442 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import { useState } from "react";
import { Button, Tabs } from "antd";
import { PlusOutlined } from "@ant-design/icons";
import TaskManagement from "./components/TaskManagement";
import ExecutionLog from "./components/ExecutionLog";
import { useNavigate } from "react-router";
import DevelopmentInProgress from "@/components/DevelopmentInProgress";
export default function DataCollection() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState("task-management");
return <DevelopmentInProgress />;
return (
<div>
<div className="flex justify-between items-end">
<div>
<h1 className="text-xl font-bold text-gray-900 mb-2"></h1>
</div>
<div>
<Button
type="primary"
onClick={() => navigate("/data/collection/create-task")}
icon={<PlusOutlined />}
>
</Button>
</div>
</div>
<Tabs
activeKey={activeTab}
items={[
{ label: "任务管理", key: "task-management" },
{ label: "执行日志", key: "execution-log" },
]}
onChange={(tab) => {
setActiveTab(tab);
}}
/>
{activeTab === "task-management" ? <TaskManagement /> : <ExecutionLog />}
</div>
);
}

View File

@@ -0,0 +1,153 @@
import { Card, Badge, Table } from "antd";
import type { ColumnsType } from "antd/es/table";
import { SearchControls } from "@/components/SearchControls";
import type { CollectionLog } from "@/pages/DataCollection/collection.model";
import { queryExecutionLogUsingPost } from "../../collection.apis";
import { LogStatusMap, LogTriggerTypeMap } from "../../collection.const";
import useFetchData from "@/hooks/useFetchData";
const filterOptions = [
{
key: "status",
label: "状态筛选",
options: Object.values(LogStatusMap),
},
{
key: "triggerType",
label: "触发类型",
options: Object.values(LogTriggerTypeMap),
},
];
export default function ExecutionLog() {
const handleReset = () => {
setSearchParams({
keyword: "",
filters: {},
current: 1,
pageSize: 10,
dateRange: null,
});
};
const {
loading,
tableData,
pagination,
searchParams,
setSearchParams,
handleFiltersChange,
} = useFetchData(queryExecutionLogUsingPost);
const columns: ColumnsType<CollectionLog> = [
{
title: "任务名称",
dataIndex: "taskName",
key: "taskName",
fixed: "left",
render: (text: string) => <span style={{ fontWeight: 500 }}>{text}</span>,
},
{
title: "状态",
dataIndex: "status",
key: "status",
render: (status: string) => (
<Badge
text={LogStatusMap[status]?.label}
color={LogStatusMap[status]?.color}
/>
),
},
{
title: "触发类型",
dataIndex: "triggerType",
key: "triggerType",
render: (type: string) => LogTriggerTypeMap[type].label,
},
{
title: "开始时间",
dataIndex: "startTime",
key: "startTime",
},
{
title: "结束时间",
dataIndex: "endTime",
key: "endTime",
},
{
title: "执行时长",
dataIndex: "duration",
key: "duration",
},
{
title: "重试次数",
dataIndex: "retryCount",
key: "retryCount",
},
{
title: "进程ID",
dataIndex: "processId",
key: "processId",
render: (text: string) => (
<span style={{ fontFamily: "monospace" }}>{text}</span>
),
},
{
title: "错误信息",
dataIndex: "errorMessage",
key: "errorMessage",
render: (msg?: string) =>
msg ? (
<span style={{ color: "#f5222d" }} title={msg}>
{msg}
</span>
) : (
<span style={{ color: "#bbb" }}>-</span>
),
},
];
return (
<div className="flex flex-col gap-4">
{/* Filter Controls */}
<div className="flex items-center justify-between gap-4">
<SearchControls
searchTerm={searchParams.keyword}
onSearchChange={(keyword: string) =>
setSearchParams({
...searchParams,
keyword,
})
}
filters={filterOptions}
onFiltersChange={handleFiltersChange}
showViewToggle={false}
onClearFilters={() =>
setSearchParams((prev) => ({
...prev,
filters: {},
}))
}
showDatePicker
dateRange={searchParams.dateRange || [null, null]}
onDateChange={(date) =>
setSearchParams((prev) => ({ ...prev, dateRange: date }))
}
onReload={handleReset}
searchPlaceholder="搜索任务名称、进程ID或错误信息..."
className="flex-1"
/>
</div>
<Card>
<Table
loading={loading}
columns={columns}
dataSource={tableData}
rowKey="id"
pagination={pagination}
scroll={{ x: "max-content" }}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,200 @@
import { Card, Button, Badge, Table, Dropdown, App } from "antd";
import { EllipsisOutlined } 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";
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);
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 columns = [
{
title: "任务名称",
dataIndex: "name",
key: "name",
fixed: "left",
render: (text: string, record: CollectionTask) => (
<Button
type="link"
onClick={() => navigate("`/data-collection/tasks/${record.id}`)}>")}
>
{text}
</Button>
),
},
{
title: "状态",
dataIndex: "status",
key: "status",
render: (status: string) =>
StatusMap[status] ? (
<Badge
color={StatusMap[status].color}
text={StatusMap[status].label}
/>
) : (
<Badge text={status} />
),
},
{
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: Task) => (
<Dropdown
menu={{
items: [
record.status === TaskStatus.STOPPED
? {
key: "start",
label: "启动",
onClick: () => handleStartTask(record.id),
}
: {
key: "stop",
label: "停止",
onClick: () => handleStopTask(record.id),
},
{
key: "edit",
label: "编辑",
onClick: () => handleViewDetail(record),
},
{
key: "delete",
label: "删除",
danger: true,
onClick: () => handleDeleteTask(record.id),
},
],
}}
trigger={["click"]}
>
<Button
type="text"
icon={<EllipsisOutlined style={{ fontSize: 20 }} />}
/>
</Dropdown>
),
},
];
return (
<div>
{/* 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: {},
}))
}
className="mb-4"
/>
{/* 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" }}
/>
</Card>
</div>
);
}