You've already forked DataMate
change settings page and add ratio task detail page (#77)
* 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. * feat: Update task creation API endpoint and enhance task creation form with new fields and validation * Refactor file upload and operator management components - Removed unnecessary console logs from file download and export functions. - Added size property to TaskItem interface for better task management. - Simplified TaskUpload component by utilizing useFileSliceUpload hook for file upload logic. - Enhanced OperatorPluginCreate component to handle file uploads and parsing more efficiently. - Updated ConfigureStep component to use Ant Design Form for better data handling and validation. - Improved PreviewStep component to navigate back to the operator market. - Added support for additional file types in UploadStep component. - Implemented delete operator functionality in OperatorMarketPage with confirmation prompts. - Cleaned up unused API functions in operator.api.ts to streamline the codebase. - Fixed number formatting utility to handle zero values correctly. * Refactor Knowledge Generation to Knowledge Base - Created new API service for Knowledge Base operations including querying, creating, updating, and deleting knowledge bases and files. - Added constants for Knowledge Base status and type mappings. - Defined models for Knowledge Base and related files. - Removed obsolete Knowledge Base creation and home components, replacing them with new implementations under the Knowledge Base structure. - Updated routing to reflect the new Knowledge Base paths. - Adjusted menu items to align with the new Knowledge Base terminology. - Modified ModelAccess interface to include modelName and type properties. * feat: Implement Knowledge Base Page with CRUD operations and data management - Added KnowledgeBasePage component for displaying and managing knowledge bases. - Integrated search and filter functionalities with SearchControls component. - Implemented CreateKnowledgeBase component for creating and editing knowledge bases. - Enhanced AddDataDialog for file uploads and dataset selections. - Introduced TableTransfer component for managing data transfers between tables. - Updated API functions for knowledge base operations, including file management. - Refactored knowledge base model to include file status and metadata. - Adjusted routing to point to the new KnowledgeBasePage. * feat: enhance OperatorPluginCreate and ConfigureStep for better upload handling and UI updates * refactor: remove unused components and clean up API logging in KnowledgeBase * feat: update icons in various components and improve styling for better UI consistency * fix: adjust upload step handling and improve error display in configuration step * feat: Add RatioTransfer component for dataset selection and configuration - Implemented RatioTransfer component to manage dataset selection and ratio configuration. - Integrated dataset fetching with search and filter capabilities. - Added RatioConfig component for displaying and updating selected datasets' configurations. - Enhanced SelectDataset component with improved UI and functionality for dataset selection. - Updated RatioTasksPage to utilize new ratio task status mapping and improved error handling for task deletion. - Refactored ratio model and constants for better type safety and clarity. - Changed Vite configuration to use local backend service for development. * feat: Add .editorconfig and enhance SystemConfig with table for settings display * feat: Enhance parameter configuration for range inputs and update default values * feat: Update site name to DataMate and refine text for AI data processing * Refactor file upload and operator management components - Removed unnecessary console logs from file download and export functions. - Added size property to TaskItem interface for better task management. - Simplified TaskUpload component by utilizing useFileSliceUpload hook for file upload logic. - Enhanced OperatorPluginCreate component to handle file uploads and parsing more efficiently. - Updated ConfigureStep component to use Ant Design Form for better data handling and validation. - Improved PreviewStep component to navigate back to the operator market. - Added support for additional file types in UploadStep component. - Implemented delete operator functionality in OperatorMarketPage with confirmation prompts. - Cleaned up unused API functions in operator.api.ts to streamline the codebase. - Fixed number formatting utility to handle zero values correctly. * Refactor Knowledge Generation to Knowledge Base - Created new API service for Knowledge Base operations including querying, creating, updating, and deleting knowledge bases and files. - Added constants for Knowledge Base status and type mappings. - Defined models for Knowledge Base and related files. - Removed obsolete Knowledge Base creation and home components, replacing them with new implementations under the Knowledge Base structure. - Updated routing to reflect the new Knowledge Base paths. - Adjusted menu items to align with the new Knowledge Base terminology. - Modified ModelAccess interface to include modelName and type properties. * feat: Implement Knowledge Base Page with CRUD operations and data management - Added KnowledgeBasePage component for displaying and managing knowledge bases. - Integrated search and filter functionalities with SearchControls component. - Implemented CreateKnowledgeBase component for creating and editing knowledge bases. - Enhanced AddDataDialog for file uploads and dataset selections. - Introduced TableTransfer component for managing data transfers between tables. - Updated API functions for knowledge base operations, including file management. - Refactored knowledge base model to include file status and metadata. - Adjusted routing to point to the new KnowledgeBasePage. * feat: enhance OperatorPluginCreate and ConfigureStep for better upload handling and UI updates * feat: update icons in various components and improve styling for better UI consistency * fix: adjust upload step handling and improve error display in configuration step * feat: Update site name to DataMate and refine text for AI data processing * Refactor file upload and operator management components - Removed unnecessary console logs from file download and export functions. - Added size property to TaskItem interface for better task management. - Simplified TaskUpload component by utilizing useFileSliceUpload hook for file upload logic. - Enhanced OperatorPluginCreate component to handle file uploads and parsing more efficiently. - Updated ConfigureStep component to use Ant Design Form for better data handling and validation. - Improved PreviewStep component to navigate back to the operator market. - Added support for additional file types in UploadStep component. - Implemented delete operator functionality in OperatorMarketPage with confirmation prompts. - Cleaned up unused API functions in operator.api.ts to streamline the codebase. - Fixed number formatting utility to handle zero values correctly. * Refactor Knowledge Generation to Knowledge Base - Created new API service for Knowledge Base operations including querying, creating, updating, and deleting knowledge bases and files. - Added constants for Knowledge Base status and type mappings. - Defined models for Knowledge Base and related files. - Removed obsolete Knowledge Base creation and home components, replacing them with new implementations under the Knowledge Base structure. - Updated routing to reflect the new Knowledge Base paths. - Adjusted menu items to align with the new Knowledge Base terminology. - Modified ModelAccess interface to include modelName and type properties. * feat: Implement Knowledge Base Page with CRUD operations and data management - Added KnowledgeBasePage component for displaying and managing knowledge bases. - Integrated search and filter functionalities with SearchControls component. - Implemented CreateKnowledgeBase component for creating and editing knowledge bases. - Enhanced AddDataDialog for file uploads and dataset selections. - Introduced TableTransfer component for managing data transfers between tables. - Updated API functions for knowledge base operations, including file management. - Refactored knowledge base model to include file status and metadata. - Adjusted routing to point to the new KnowledgeBasePage. * feat: enhance OperatorPluginCreate and ConfigureStep for better upload handling and UI updates * feat: update icons in various components and improve styling for better UI consistency * fix: adjust upload step handling and improve error display in configuration step * feat: add settings drawer and integrate SettingsPage component * feat: add ratio task management features including detail view and API integration
This commit is contained in:
@@ -209,7 +209,9 @@ function CardView<T extends BaseCardDataType>(props: CardViewProps<T>) {
|
|||||||
{item?.status && (
|
{item?.status && (
|
||||||
<Tag color={item?.status?.color}>
|
<Tag color={item?.status?.color}>
|
||||||
<div className="flex items-center gap-2 text-xs py-0.5">
|
<div className="flex items-center gap-2 text-xs py-0.5">
|
||||||
<span>{item?.status?.icon}</span>
|
{item?.status?.icon && (
|
||||||
|
<span>{item?.status?.icon}</span>
|
||||||
|
)}
|
||||||
<span>{item?.status?.label}</span>
|
<span>{item?.status?.label}</span>
|
||||||
</div>
|
</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
@@ -93,6 +93,17 @@ const MockAPI = {
|
|||||||
instructionTuningUsingPost: "/synthesis/instruction-tuning", // 指令微调
|
instructionTuningUsingPost: "/synthesis/instruction-tuning", // 指令微调
|
||||||
cotDistillationUsingPost: "/synthesis/cot-distillation", // Cot蒸馏
|
cotDistillationUsingPost: "/synthesis/cot-distillation", // Cot蒸馏
|
||||||
|
|
||||||
|
// 数据配比接口
|
||||||
|
createRatioTaskUsingPost: "/synthesis/ratio-task", // 创建配比任务
|
||||||
|
queryRatioTasksUsingGet: "/synthesis/ratio-task", // 获取配比任务列表
|
||||||
|
queryRatioTaskByIdUsingGet: "/synthesis/ratio-task/:taskId", // 根据ID获取配比任务详情
|
||||||
|
deleteRatioTaskByIdUsingDelete: "/synthesis/ratio-task/:taskId", // 删除配比任务
|
||||||
|
updateRatioTaskByIdUsingPut: "/synthesis/ratio-task/:taskId", // 更新配比任务
|
||||||
|
executeRatioTaskByIdUsingPost: "/synthesis/ratio-task/:taskId/execute", // 执行配比任务
|
||||||
|
stopRatioTaskByIdUsingPost: "/synthesis/ratio-task/:taskId/stop", // 停止配比任务
|
||||||
|
queryRatioJobStatusUsingGet: "/synthesis/ratio-task/:taskId/status", // 获取配比任务状态
|
||||||
|
queryRatioModelsUsingGet: "/synthesis/ratio-models", // 获取配比模型列表
|
||||||
|
|
||||||
// 数据评测接口
|
// 数据评测接口
|
||||||
queryEvaluationTasksUsingPost: "/evaluation/tasks", // 获取评测任务列表
|
queryEvaluationTasksUsingPost: "/evaluation/tasks", // 获取评测任务列表
|
||||||
createEvaluationTaskUsingPost: "/evaluation/tasks/create", // 创建评测任务
|
createEvaluationTaskUsingPost: "/evaluation/tasks/create", // 创建评测任务
|
||||||
|
|||||||
@@ -443,7 +443,6 @@ module.exports = function (router) {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const operator = operatorList.find((op) => op.id === id);
|
const operator = operatorList.find((op) => op.id === id);
|
||||||
console.log("获取算子详情:", id, operator);
|
console.log("获取算子详情:", id, operator);
|
||||||
|
|
||||||
if (operator) {
|
if (operator) {
|
||||||
// 增加浏览次数模拟
|
// 增加浏览次数模拟
|
||||||
operator.viewCount = (operator.viewCount || 0) + 1;
|
operator.viewCount = (operator.viewCount || 0) + 1;
|
||||||
|
|||||||
220
frontend/src/mock/mock-seed/data-ratio.cjs
Normal file
220
frontend/src/mock/mock-seed/data-ratio.cjs
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
|
||||||
|
const Mock = require("mockjs");
|
||||||
|
const API = require("../mock-apis.cjs");
|
||||||
|
|
||||||
|
function ratioJobItem() {
|
||||||
|
return {
|
||||||
|
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
name: Mock.Random.ctitle(5, 15),
|
||||||
|
description: Mock.Random.csentence(10, 30),
|
||||||
|
status: Mock.Random.pick(["PENDING", "RUNNING", "COMPLETED", "FAILED", "PAUSED"]),
|
||||||
|
totals: Mock.Random.integer(1000, 10000),
|
||||||
|
ratio_method: Mock.Random.pick(["DATASET", "TAG"]),
|
||||||
|
target_dataset_id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
target_dataset_name: Mock.Random.ctitle(3, 8),
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
counts: Mock.Random.integer(100, 1000).toString(),
|
||||||
|
filter_conditions: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
counts: Mock.Random.integer(100, 1000).toString(),
|
||||||
|
filter_conditions: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
created_at: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
updated_at: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ratioJobList = new Array(20).fill(null).map(ratioJobItem);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function (router) {
|
||||||
|
// 获取配比任务列表
|
||||||
|
router.get(API.queryRatioTasksUsingGet, (req, res) => {
|
||||||
|
const { page = 0, size = 10, status } = req.query;
|
||||||
|
let filteredJobs = ratioJobList;
|
||||||
|
if (status) {
|
||||||
|
filteredJobs = ratioJobList.filter((job) => job.status === status);
|
||||||
|
}
|
||||||
|
const startIndex = page * size;
|
||||||
|
const endIndex = startIndex + parseInt(size);
|
||||||
|
const pageData = filteredJobs.slice(startIndex, endIndex);
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Success",
|
||||||
|
data: {
|
||||||
|
content: pageData,
|
||||||
|
totalElements: filteredJobs.length,
|
||||||
|
totalPages: Math.ceil(filteredJobs.length / size),
|
||||||
|
size: parseInt(size),
|
||||||
|
number: parseInt(page),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建配比任务
|
||||||
|
router.post(API.createRatioTaskUsingPost, (req, res) => {
|
||||||
|
const newJob = {
|
||||||
|
...ratioJobItem(),
|
||||||
|
...req.body,
|
||||||
|
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
status: "PENDING",
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
ratioJobList.push(newJob);
|
||||||
|
res.status(201).send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Ratio job created successfully",
|
||||||
|
data: newJob,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取配比任务详情
|
||||||
|
router.get(API.queryRatioTaskByIdUsingGet, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const job = ratioJobList.find((j) => j.id === taskId);
|
||||||
|
if (job) {
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Success",
|
||||||
|
data: job,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({
|
||||||
|
code: "1",
|
||||||
|
msg: "Ratio job not found",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除配比任务
|
||||||
|
router.delete(API.deleteRatioTaskByIdUsingDelete, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const index = ratioJobList.findIndex((j) => j.id === taskId);
|
||||||
|
if (index !== -1) {
|
||||||
|
ratioJobList.splice(index, 1);
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Ratio job deleted successfully",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({
|
||||||
|
code: "1",
|
||||||
|
msg: "Ratio job not found",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新配比任务
|
||||||
|
router.put(API.updateRatioTaskByIdUsingPut, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const index = ratioJobList.findIndex((j) => j.id === taskId);
|
||||||
|
if (index !== -1) {
|
||||||
|
ratioJobList[index] = {
|
||||||
|
...ratioJobList[index],
|
||||||
|
...req.body,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Ratio job updated successfully",
|
||||||
|
data: ratioJobList[index],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({
|
||||||
|
code: "1",
|
||||||
|
msg: "Ratio job not found",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 执行配比任务
|
||||||
|
router.post(API.executeRatioTaskByIdUsingPost, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const job = ratioJobList.find((j) => j.id === taskId);
|
||||||
|
if (job) {
|
||||||
|
job.status = "RUNNING";
|
||||||
|
job.startedAt = new Date().toISOString();
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Ratio job execution started",
|
||||||
|
data: {
|
||||||
|
executionId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
status: "RUNNING",
|
||||||
|
message: "Job execution started successfully",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({
|
||||||
|
code: "1",
|
||||||
|
msg: "Ratio job not found",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 停止配比任务
|
||||||
|
router.post(API.stopRatioTaskByIdUsingPost, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const job = ratioJobList.find((j) => j.id === taskId);
|
||||||
|
if (job) {
|
||||||
|
job.status = "STOPPED";
|
||||||
|
job.finishedAt = new Date().toISOString();
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Ratio job stopped successfully",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({
|
||||||
|
code: "1",
|
||||||
|
msg: "Ratio job not found",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取配比任务状态
|
||||||
|
router.get(API.queryRatioJobStatusUsingGet, (req, res) => {
|
||||||
|
const { taskId } = req.params;
|
||||||
|
const job = ratioJobList.find((j) => j.id === taskId);
|
||||||
|
if (job) {
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Success",
|
||||||
|
data: {
|
||||||
|
status: job.status,
|
||||||
|
progress: job.progress,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({
|
||||||
|
code: "1",
|
||||||
|
msg: "Ratio job not found",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取配比模型列表
|
||||||
|
router.get(API.queryRatioModelsUsingGet, (req, res) => {
|
||||||
|
const models = [
|
||||||
|
{ id: "model1", name: "均匀分配模型", description: "将目标数量均匀分配到各数据集。" },
|
||||||
|
{ id: "model2", name: "标签优先模型", description: "优先满足标签配比需求。" },
|
||||||
|
{ id: "model3", name: "自定义模型", description: "支持自定义分配逻辑。" },
|
||||||
|
];
|
||||||
|
res.send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Success",
|
||||||
|
data: models,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { memo, useEffect, useState } from "react";
|
import { memo, useEffect, useState } from "react";
|
||||||
import { Button, Menu, Popover } from "antd";
|
import { Button, Drawer, Menu, Popover } from "antd";
|
||||||
import {
|
import {
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
MenuOutlined,
|
MenuOutlined,
|
||||||
@@ -9,6 +9,7 @@ import { ClipboardList, Sparkles, X } from "lucide-react";
|
|||||||
import { menuItems } from "@/pages/Layout/menu";
|
import { menuItems } from "@/pages/Layout/menu";
|
||||||
import { NavLink, useLocation, useNavigate } from "react-router";
|
import { NavLink, useLocation, useNavigate } from "react-router";
|
||||||
import TaskUpload from "./TaskUpload";
|
import TaskUpload from "./TaskUpload";
|
||||||
|
import SettingsPage from "../SettingsPage/SettingsPage";
|
||||||
|
|
||||||
const AsiderAndHeaderLayout = () => {
|
const AsiderAndHeaderLayout = () => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@@ -16,6 +17,7 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
const [activeItem, setActiveItem] = useState<string>("");
|
const [activeItem, setActiveItem] = useState<string>("");
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||||
const [taskCenterVisible, setTaskCenterVisible] = useState(false);
|
const [taskCenterVisible, setTaskCenterVisible] = useState(false);
|
||||||
|
const [settingVisible, setSettingVisible] = useState(false);
|
||||||
|
|
||||||
// Initialize active item based on current pathname
|
// Initialize active item based on current pathname
|
||||||
const initActiveItem = () => {
|
const initActiveItem = () => {
|
||||||
@@ -137,11 +139,8 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
</Popover>
|
</Popover>
|
||||||
<Button
|
<Button
|
||||||
block
|
block
|
||||||
color={pathname === "/data/settings" ? "primary" : "default"}
|
|
||||||
variant={pathname === "/data/settings" ? "filled" : "outlined"}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveItem("");
|
setSettingVisible(true);
|
||||||
navigate("/data/settings");
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
设置
|
设置
|
||||||
@@ -167,11 +166,8 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
block
|
block
|
||||||
color={pathname === "/data/settings" ? "primary" : "default"}
|
|
||||||
variant={pathname === "/data/settings" ? "filled" : "outlined"}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveItem("");
|
setSettingVisible(true);
|
||||||
navigate("/data/settings");
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingOutlined />
|
<SettingOutlined />
|
||||||
@@ -179,7 +175,17 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<Drawer
|
||||||
|
title="设置"
|
||||||
|
placement="bottom"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
open={settingVisible}
|
||||||
|
onClose={() => setSettingVisible(false)}
|
||||||
|
bodyStyle={{ padding: 0 }}
|
||||||
|
>
|
||||||
|
<SettingsPage></SettingsPage>
|
||||||
|
</Drawer>
|
||||||
{/* 添加遮罩层,点击外部区域时关闭 */}
|
{/* 添加遮罩层,点击外部区域时关闭 */}
|
||||||
{taskCenterVisible && (
|
{taskCenterVisible && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Alert, Input, Form} from "antd";
|
import { Alert, Input, Form } from "antd";
|
||||||
import TextArea from "antd/es/input/TextArea";
|
import TextArea from "antd/es/input/TextArea";
|
||||||
import React, {useEffect} from "react";
|
import React, { useEffect } from "react";
|
||||||
import ParamConfig from "@/pages/DataCleansing/Create/components/ParamConfig.tsx";
|
import ParamConfig from "@/pages/DataCleansing/Create/components/ParamConfig.tsx";
|
||||||
|
|
||||||
export default function ConfigureStep({
|
export default function ConfigureStep({
|
||||||
@@ -22,14 +22,14 @@ export default function ConfigureStep({
|
|||||||
setParsedInfo((op) =>
|
setParsedInfo((op) =>
|
||||||
op.id === operatorId
|
op.id === operatorId
|
||||||
? {
|
? {
|
||||||
...op,
|
...op,
|
||||||
overrides: {
|
overrides: {
|
||||||
...(op?.overrides || op?.defaultParams),
|
...(op?.overrides || op?.defaultParams),
|
||||||
[paramKey]: value,
|
[paramKey]: value,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: op
|
: op
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -52,32 +52,40 @@ export default function ConfigureStep({
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
initialValues={parsedInfo}
|
initialValues={parsedInfo}
|
||||||
onValuesChange={(_, allValues) => {
|
onValuesChange={(_, allValues) => {
|
||||||
setParsedInfo({...parsedInfo, ...allValues});
|
setParsedInfo({ ...parsedInfo, ...allValues });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* 基本信息 */}
|
{/* 基本信息 */}
|
||||||
<h3 className="text-lg font-semibold text-gray-900">基本信息</h3>
|
<h3 className="text-lg font-semibold text-gray-900">基本信息</h3>
|
||||||
<Form.Item label="ID" name="id" rules={[{required: true}]}>
|
<Form.Item label="ID" name="id" rules={[{ required: true }]}>
|
||||||
<Input value={parsedInfo.id} readOnly/>
|
<Input value={parsedInfo.id} readOnly />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="名称" name="name" rules={[{required: true}]}>
|
<Form.Item label="名称" name="name" rules={[{ required: true }]}>
|
||||||
<Input value={parsedInfo.name}/>
|
<Input value={parsedInfo.name} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="版本" name="version" rules={[{required: true}]}>
|
<Form.Item label="版本" name="version" rules={[{ required: true }]}>
|
||||||
<Input value={parsedInfo.version}/>
|
<Input value={parsedInfo.version} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="描述"
|
label="描述"
|
||||||
name="description"
|
name="description"
|
||||||
rules={[{required: false}]}
|
rules={[{ required: false }]}
|
||||||
>
|
>
|
||||||
<TextArea value={parsedInfo.description}/>
|
<TextArea value={parsedInfo.description} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="输入类型" name="inputs" rules={[{required: true}]}>
|
<Form.Item
|
||||||
<Input value={parsedInfo.inputs}/>
|
label="输入类型"
|
||||||
|
name="inputs"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<Input value={parsedInfo.inputs} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="输出类型" name="outputs" rules={[{required: true}]}>
|
<Form.Item
|
||||||
<Input value={parsedInfo.outputs}/>
|
label="输出类型"
|
||||||
|
name="outputs"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<Input value={parsedInfo.outputs} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{parsedInfo.configs && Object.keys(parsedInfo.configs).length > 0 && (
|
{parsedInfo.configs && Object.keys(parsedInfo.configs).length > 0 && (
|
||||||
@@ -85,19 +93,17 @@ export default function ConfigureStep({
|
|||||||
<h3 className="text-lg font-semibold text-gray-900 mt-10 mb-2">
|
<h3 className="text-lg font-semibold text-gray-900 mt-10 mb-2">
|
||||||
高级配置
|
高级配置
|
||||||
</h3>
|
</h3>
|
||||||
<div className="border p-4 rounded-lg grid grid-cols-2 gap-4">
|
<Form layout="vertical">
|
||||||
<Form layout="vertical">
|
{Object.entries(parsedInfo?.configs).map(([key, param]) => (
|
||||||
{Object.entries(parsedInfo?.configs).map(([key, param]) =>
|
<ParamConfig
|
||||||
<ParamConfig
|
key={key}
|
||||||
key={key}
|
operator={parsedInfo}
|
||||||
operator={parsedInfo}
|
paramKey={key}
|
||||||
paramKey={key}
|
param={param}
|
||||||
param={param}
|
onParamChange={handleConfigChange}
|
||||||
onParamChange={handleConfigChange}
|
/>
|
||||||
/>
|
))}
|
||||||
)}
|
</Form>
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
312
frontend/src/pages/RatioTask/Detail/RatioTaskDetail.tsx
Normal file
312
frontend/src/pages/RatioTask/Detail/RatioTaskDetail.tsx
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
import { useEffect, useMemo, useState, useCallback } from "react";
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
App,
|
||||||
|
Tabs,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Progress,
|
||||||
|
Badge,
|
||||||
|
Descriptions,
|
||||||
|
DescriptionsProps,
|
||||||
|
} from "antd";
|
||||||
|
import { ReloadOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||||
|
import DetailHeader from "@/components/DetailHeader";
|
||||||
|
import { Link, useNavigate, useParams } from "react-router";
|
||||||
|
import {
|
||||||
|
getRatioTaskByIdUsingGet,
|
||||||
|
deleteRatioTasksUsingDelete,
|
||||||
|
} from "@/pages/RatioTask/ratio.api";
|
||||||
|
import { post } from "@/utils/request";
|
||||||
|
import type { RatioTaskItem } from "@/pages/RatioTask/ratio.model";
|
||||||
|
import { mapRatioTask } from "../ratio.const";
|
||||||
|
import { Copy, Pause, PlayIcon } from "lucide-react";
|
||||||
|
|
||||||
|
const tabList = [
|
||||||
|
{
|
||||||
|
key: "overview",
|
||||||
|
label: "概览",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "analysis",
|
||||||
|
label: "配比分析",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "config",
|
||||||
|
label: "配比配置",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function RatioTaskDetail() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [activeTab, setActiveTab] = useState("overview");
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const [ratioTask, setRatioTask] = useState<RatioTaskItem>(
|
||||||
|
{} as RatioTaskItem
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigateItems = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
title: <Link to="/data/synthesis/ratio-task">首页</Link>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: ratioTask.name || "配比任务详情",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[ratioTask]
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchRatioTask = useCallback(async () => {
|
||||||
|
const { data } = await getRatioTaskByIdUsingGet(id as string);
|
||||||
|
setRatioTask(mapRatioTask(data));
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchRatioTask();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleRefresh = useCallback(
|
||||||
|
async (showMessage = true) => {
|
||||||
|
await fetchRatioTask();
|
||||||
|
if (showMessage) message.success({ content: "任务数据刷新成功" });
|
||||||
|
},
|
||||||
|
[fetchRatioTask, message]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
await deleteRatioTasksUsingDelete(id as string);
|
||||||
|
navigate("/ratio/task");
|
||||||
|
message.success("配比任务删除成功");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExecute = async () => {
|
||||||
|
await post(`/api/synthesis/ratio-task/${id}/execute`);
|
||||||
|
handleRefresh();
|
||||||
|
message.success("任务已启动");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStop = async () => {
|
||||||
|
await post(`/api/synthesis/ratio-task/${id}/stop`);
|
||||||
|
handleRefresh();
|
||||||
|
message.success("任务已停止");
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const refreshData = () => {
|
||||||
|
handleRefresh(false);
|
||||||
|
};
|
||||||
|
window.addEventListener("update:ratio-task", refreshData);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("update:ratio-task", refreshData);
|
||||||
|
};
|
||||||
|
}, [handleRefresh]);
|
||||||
|
|
||||||
|
// 操作列表
|
||||||
|
const operations = [
|
||||||
|
{
|
||||||
|
key: "execute",
|
||||||
|
label: "启动",
|
||||||
|
icon: <PlayIcon className="w-4 h-4 text-gray-500" />,
|
||||||
|
onClick: handleExecute,
|
||||||
|
disabled: ratioTask.status === "RUNNING",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "stop",
|
||||||
|
label: "停止",
|
||||||
|
icon: <Pause className="w-4 h-4 text-gray-500" />,
|
||||||
|
onClick: handleStop,
|
||||||
|
disabled: ratioTask.status !== "RUNNING",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "refresh",
|
||||||
|
label: "刷新",
|
||||||
|
icon: <ReloadOutlined />,
|
||||||
|
onClick: handleRefresh,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "delete",
|
||||||
|
label: "删除",
|
||||||
|
danger: true,
|
||||||
|
confirm: {
|
||||||
|
title: "确认删除该配比任务?",
|
||||||
|
description: "删除后该任务将无法恢复,请谨慎操作。",
|
||||||
|
okText: "删除",
|
||||||
|
cancelText: "取消",
|
||||||
|
okType: "danger",
|
||||||
|
},
|
||||||
|
icon: <DeleteOutlined />,
|
||||||
|
onClick: handleDelete,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 基本信息
|
||||||
|
const items: DescriptionsProps["items"] = [
|
||||||
|
{
|
||||||
|
key: "id",
|
||||||
|
label: "ID",
|
||||||
|
children: ratioTask.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
label: "名称",
|
||||||
|
children: ratioTask.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "dataset",
|
||||||
|
label: "目标数据集",
|
||||||
|
children: (
|
||||||
|
<Link to={`/data/management/detail/${ratioTask.target_dataset_id}`}>
|
||||||
|
{ratioTask.target_dataset_name}
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "status",
|
||||||
|
label: "数据大小",
|
||||||
|
children: (
|
||||||
|
<Badge color={ratioTask.status?.color} text={ratioTask.status?.label} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "type",
|
||||||
|
label: "类型",
|
||||||
|
children: ratioTask.type || "未知",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "status",
|
||||||
|
label: "状态",
|
||||||
|
children: ratioTask?.status?.label || "未知",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "createdBy",
|
||||||
|
label: "创建者",
|
||||||
|
children: ratioTask.createdBy || "未知",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "targetLocation",
|
||||||
|
label: "输出路径",
|
||||||
|
children: ratioTask.targetLocation || "未知",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "createdAt",
|
||||||
|
label: "创建时间",
|
||||||
|
children: ratioTask.createdAt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "updatedAt",
|
||||||
|
label: "更新时间",
|
||||||
|
children: ratioTask.updatedAt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "description",
|
||||||
|
label: "描述",
|
||||||
|
children: ratioTask.description || "无",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col gap-4">
|
||||||
|
<Breadcrumb items={navigateItems} />
|
||||||
|
{/* Header */}
|
||||||
|
<DetailHeader
|
||||||
|
data={ratioTask}
|
||||||
|
statistics={ratioTask?.statistics || []}
|
||||||
|
operations={operations}
|
||||||
|
/>
|
||||||
|
<div className="flex-overflow-auto p-6 pt-2 bg-white rounded-md shadow">
|
||||||
|
<Tabs activeKey={activeTab} items={tabList} onChange={setActiveTab} />
|
||||||
|
<div className="h-full overflow-auto">
|
||||||
|
{activeTab === "overview" && (
|
||||||
|
<>
|
||||||
|
<Descriptions
|
||||||
|
title="基本信息"
|
||||||
|
layout="vertical"
|
||||||
|
size="small"
|
||||||
|
items={items}
|
||||||
|
column={5}
|
||||||
|
/>
|
||||||
|
<h2 className="text-base font-semibold mt-8 mb-4">配比详情</h2>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{/* 目标配比 */}
|
||||||
|
<Card title="目标配比">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{ratioTask.targetRatio &&
|
||||||
|
Object.entries(ratioTask.targetRatio).map(
|
||||||
|
([category, ratio]) => (
|
||||||
|
<div key={category} className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-sm font-medium text-gray-700">
|
||||||
|
{category}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-semibold text-blue-600">
|
||||||
|
{ratioTask.ratio}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Progress value={ratio} className="h-2" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 当前配比 */}
|
||||||
|
<Card title="当前配比">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{ratioTask.currentRatio &&
|
||||||
|
Object.entries(ratioTask.currentRatio).length > 0 ? (
|
||||||
|
Object.entries(ratioTask.currentRatio)?.map(
|
||||||
|
([category, ratio]) => (
|
||||||
|
<div key={category} className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-sm font-medium text-gray-700">
|
||||||
|
{category}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-semibold text-green-600">
|
||||||
|
{ratioTask.ratio}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Progress value={ratio} className="h-2" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-gray-500">等待处理开始</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{activeTab === "analysis" && (
|
||||||
|
<div className="text-center py-20 text-gray-500">
|
||||||
|
配比分析功能正在开发中,敬请期待!
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{activeTab === "config" && (
|
||||||
|
<div className="bg-gray-50 rounded-lg p-4 font-mono text-xs overflow-x-auto">
|
||||||
|
<pre className="text-gray-700 whitespace-pre-wrap break-words">
|
||||||
|
{JSON.stringify(
|
||||||
|
{
|
||||||
|
id: ratioTask.id,
|
||||||
|
name: ratioTask.name,
|
||||||
|
type: ratioTask.type,
|
||||||
|
status: ratioTask.status,
|
||||||
|
strategy: ratioTask.strategy,
|
||||||
|
sourceDatasets: ratioTask.sourceDatasets,
|
||||||
|
targetRatio: ratioTask.targetRatio,
|
||||||
|
outputPath: ratioTask.outputPath,
|
||||||
|
createdAt: ratioTask.createdAt,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button, Card, Table, Tooltip, App } from "antd";
|
import { Button, Card, Table, App, Badge, Popconfirm } from "antd";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { DeleteOutlined } from "@ant-design/icons";
|
import { DeleteOutlined } from "@ant-design/icons";
|
||||||
import type { RatioTaskItem } from "@/pages/RatioTask/ratio.model";
|
import type { RatioTaskItem } from "@/pages/RatioTask/ratio.model";
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
queryRatioTasksUsingGet,
|
queryRatioTasksUsingGet,
|
||||||
} from "../ratio.api";
|
} from "../ratio.api";
|
||||||
import useFetchData from "@/hooks/useFetchData";
|
import useFetchData from "@/hooks/useFetchData";
|
||||||
import { mapRatioTask, ratioTaskStatusMap } from "../ratio.const";
|
import { mapRatioTask } from "../ratio.const";
|
||||||
|
|
||||||
export default function RatioTasksPage() {
|
export default function RatioTasksPage() {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
@@ -68,6 +68,8 @@ export default function RatioTasksPage() {
|
|||||||
title: "任务名称",
|
title: "任务名称",
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
key: "name",
|
key: "name",
|
||||||
|
width: 200,
|
||||||
|
fixed: "left" as const,
|
||||||
render: (text: string, record: RatioTaskItem) => (
|
render: (text: string, record: RatioTaskItem) => (
|
||||||
<a
|
<a
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -82,17 +84,28 @@ export default function RatioTasksPage() {
|
|||||||
title: "状态",
|
title: "状态",
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
render: (status) => ratioTaskStatusMap[status]?.label,
|
width: 120,
|
||||||
|
render: (status) => {
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
color={status?.color}
|
||||||
|
icon={status?.icon}
|
||||||
|
text={status?.label}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "配比方式",
|
title: "配比方式",
|
||||||
dataIndex: "ratio_method",
|
dataIndex: "ratio_method",
|
||||||
key: "ratio_method",
|
key: "ratio_method",
|
||||||
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "目标数量",
|
title: "目标数量",
|
||||||
dataIndex: "totals",
|
dataIndex: "totals",
|
||||||
key: "totals",
|
key: "totals",
|
||||||
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "目标数据集",
|
title: "目标数据集",
|
||||||
@@ -112,21 +125,35 @@ export default function RatioTasksPage() {
|
|||||||
title: "创建时间",
|
title: "创建时间",
|
||||||
dataIndex: "created_at",
|
dataIndex: "created_at",
|
||||||
key: "created_at",
|
key: "created_at",
|
||||||
|
width: 180,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "操作",
|
title: "操作",
|
||||||
key: "actions",
|
key: "actions",
|
||||||
|
width: 120,
|
||||||
|
fixed: "right" as const,
|
||||||
render: (_: any, task: RatioTaskItem) => (
|
render: (_: any, task: RatioTaskItem) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{operations.map((op) => (
|
{operations.map((op) => {
|
||||||
<Tooltip key={op.key} title={op.label}>
|
if (op.confirm) {
|
||||||
|
<Popconfirm
|
||||||
|
title={op.confirm.title}
|
||||||
|
description={op.confirm.description}
|
||||||
|
onConfirm={() => op.onClick(task)}
|
||||||
|
>
|
||||||
|
<Button type="text" icon={op.icon} />
|
||||||
|
</Popconfirm>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
key={op.key}
|
||||||
type="text"
|
type="text"
|
||||||
icon={op.icon}
|
icon={op.icon}
|
||||||
|
danger={op.danger}
|
||||||
onClick={() => op.onClick(task)}
|
onClick={() => op.onClick(task)}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
);
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { formatDate } from "@/utils/unit";
|
import { formatDate } from "@/utils/unit";
|
||||||
import { RatioTaskItem, RatioStatus } from "./ratio.model";
|
import { RatioTaskItem, RatioStatus } from "./ratio.model";
|
||||||
import { BarChart3 } from "lucide-react";
|
import { BarChart3, Calendar, Database } from "lucide-react";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
|
|
||||||
export const ratioTaskStatusMap: Record<
|
export const ratioTaskStatusMap: Record<
|
||||||
@@ -9,22 +9,23 @@ export const ratioTaskStatusMap: Record<
|
|||||||
value: RatioStatus;
|
value: RatioStatus;
|
||||||
label: string;
|
label: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
[RatioStatus.PENDING]: {
|
[RatioStatus.PENDING]: {
|
||||||
value: RatioStatus.PENDING,
|
value: RatioStatus.PENDING,
|
||||||
label: "等待中",
|
label: "等待中",
|
||||||
color: "blue",
|
color: "gray",
|
||||||
},
|
},
|
||||||
[RatioStatus.RUNNING]: {
|
[RatioStatus.RUNNING]: {
|
||||||
value: RatioStatus.RUNNING,
|
value: RatioStatus.RUNNING,
|
||||||
label: "运行中",
|
label: "运行中",
|
||||||
color: "green",
|
color: "blue",
|
||||||
},
|
},
|
||||||
[RatioStatus.COMPLETED]: {
|
[RatioStatus.COMPLETED]: {
|
||||||
value: RatioStatus.COMPLETED,
|
value: RatioStatus.COMPLETED,
|
||||||
label: "已完成",
|
label: "已完成",
|
||||||
color: "gray",
|
color: "green",
|
||||||
},
|
},
|
||||||
[RatioStatus.FAILED]: {
|
[RatioStatus.FAILED]: {
|
||||||
value: RatioStatus.FAILED,
|
value: RatioStatus.FAILED,
|
||||||
@@ -41,20 +42,22 @@ export const ratioTaskStatusMap: Record<
|
|||||||
export function mapRatioTask(task: Partial<RatioTaskItem>): RatioTaskItem {
|
export function mapRatioTask(task: Partial<RatioTaskItem>): RatioTaskItem {
|
||||||
return {
|
return {
|
||||||
...task,
|
...task,
|
||||||
status: ratioTaskStatusMap[task.status || RatioStatus.PENDING]?.value,
|
status: ratioTaskStatusMap[task.status || RatioStatus.PENDING],
|
||||||
createdAt: formatDate(task.created_at),
|
createdAt: formatDate(task.created_at),
|
||||||
updatedAt: formatDate(task.updated_at),
|
updatedAt: formatDate(task.updated_at),
|
||||||
description:
|
description:
|
||||||
task.ratio_method === "DATASET" ? "按数据集配比" : "按标签配比",
|
task.ratio_method === "DATASET" ? "按数据集配比" : "按标签配比",
|
||||||
icon: <BarChart3 className="w-6 h-6" />,
|
icon: <BarChart3 />,
|
||||||
iconColor: task.ratio_method === "DATASET" ? "bg-blue-100" : "bg-green-100",
|
iconColor: task.ratio_method === "DATASET" ? "bg-blue-100" : "bg-green-100",
|
||||||
statistics: [
|
statistics: [
|
||||||
{
|
{
|
||||||
label: "目标数量",
|
label: "目标数量",
|
||||||
|
icon: <BarChart3 className="w-4 h-4 text-gray-500" />,
|
||||||
value: (task.totals ?? 0).toLocaleString(),
|
value: (task.totals ?? 0).toLocaleString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "目标数据集",
|
label: "目标数据集",
|
||||||
|
icon: <Database className="w-4 h-4 text-gray-500" />,
|
||||||
value: task.target_dataset_name ? (
|
value: task.target_dataset_name ? (
|
||||||
<Link to={`/data/management/detail/${task.target_dataset_id}`}>
|
<Link to={`/data/management/detail/${task.target_dataset_id}`}>
|
||||||
{task.target_dataset_name}
|
{task.target_dataset_name}
|
||||||
@@ -65,8 +68,20 @@ export function mapRatioTask(task: Partial<RatioTaskItem>): RatioTaskItem {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "创建时间",
|
label: "创建时间",
|
||||||
|
icon: <Calendar className="w-4 h-4 text-gray-500" />,
|
||||||
value: task.created_at || "-",
|
value: task.created_at || "-",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
type: "balance",
|
||||||
|
progress: 100,
|
||||||
|
sourceDatasets: ["sentiment_dataset", "news_classification"],
|
||||||
|
targetRatio: { 正面: 33, 负面: 33, 中性: 34 },
|
||||||
|
currentRatio: { 正面: 33, 负面: 33, 中性: 34 },
|
||||||
|
totalRecords: 15000,
|
||||||
|
processedRecords: 15000,
|
||||||
|
estimatedTime: "已完成",
|
||||||
|
quality: 95,
|
||||||
|
strategy: "随机下采样",
|
||||||
|
outputPath: "/data/balanced/sentiment_balanced_20250120",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,56 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Tabs } from "antd";
|
import { Button, Menu } from "antd";
|
||||||
import { SettingOutlined, ApiOutlined } from "@ant-design/icons";
|
import { SettingOutlined, ApiOutlined } from "@ant-design/icons";
|
||||||
import WebhookConfig from "./WebhookConfig";
|
import { Component, X } from "lucide-react";
|
||||||
import ModelAccess from "./ModelAccess";
|
import { useNavigate } from "react-router";
|
||||||
import SystemConfig from "./SystemConfig";
|
import SystemConfig from "./SystemConfig";
|
||||||
import { Component } from "lucide-react";
|
import ModelAccess from "./ModelAccess";
|
||||||
|
import WebhookConfig from "./WebhookConfig";
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
const [activeTab, setActiveTab] = useState("modelAccess");
|
const navigate = useNavigate();
|
||||||
|
const [activeTab, setActiveTab] = useState("model-access");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col gap-4">
|
<div className="h-screen flex">
|
||||||
{/* Header */}
|
<div className="border-right h-full">
|
||||||
<div className="flex items-center justify-between">
|
{/* <h1 className="min-w-[200px] w-full border-bottom flex gap-2 text-lg font-bold text-gray-900 p-4">
|
||||||
<div>
|
<Button icon={<X />} type="text" onClick={() => navigate(-1)} />
|
||||||
<h1 className="text-xl font-bold text-gray-900">系统设置</h1>
|
设置中心
|
||||||
|
</h1> */}
|
||||||
|
<div className="h-full">
|
||||||
|
<Menu
|
||||||
|
mode="inline"
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: "system-config",
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
label: "系统设置",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "model-access",
|
||||||
|
icon: <Component className="w-4 h-4" />,
|
||||||
|
label: "模型接入",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "webhook-config",
|
||||||
|
icon: <ApiOutlined />,
|
||||||
|
label: "Webhook",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
selectedKeys={[activeTab]}
|
||||||
|
onClick={({ key }) => {
|
||||||
|
setActiveTab(key);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex-1 h-full p-4">
|
||||||
{/* Settings Tabs */}
|
{/* 内容区域,根据 activeTab 渲染不同的组件 */}
|
||||||
<Tabs
|
{activeTab === "system-config" && <SystemConfig />}
|
||||||
activeKey={activeTab}
|
{activeTab === "model-access" && <ModelAccess />}
|
||||||
onChange={setActiveTab}
|
{activeTab === "webhook-config" && <WebhookConfig />}
|
||||||
items={[
|
</div>
|
||||||
// {
|
|
||||||
// key: "system",
|
|
||||||
// label: (
|
|
||||||
// <span>
|
|
||||||
// <SettingOutlined className="mr-1" />
|
|
||||||
// 系统设置
|
|
||||||
// </span>
|
|
||||||
// ),
|
|
||||||
// children: <SystemConfig />,
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
key: "modelAccess",
|
|
||||||
label: (
|
|
||||||
<span className="flex items-center">
|
|
||||||
<Component className="w-4 h-4 mr-1" />
|
|
||||||
模型接入
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
children: <ModelAccess key="modelAccess" />,
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// key: "webhook",
|
|
||||||
// label: (
|
|
||||||
// <span>
|
|
||||||
// <ApiOutlined className="mr-1" />
|
|
||||||
// Webhook
|
|
||||||
// </span>
|
|
||||||
// ),
|
|
||||||
// children: <WebhookConfig />,
|
|
||||||
// },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ import RatioTasksPage from "@/pages/RatioTask/Home/RatioTask.tsx";
|
|||||||
import CreateRatioTask from "@/pages/RatioTask/Create/CreateRatioTask.tsx";
|
import CreateRatioTask from "@/pages/RatioTask/Create/CreateRatioTask.tsx";
|
||||||
import OrchestrationPage from "@/pages/Orchestration/Orchestration";
|
import OrchestrationPage from "@/pages/Orchestration/Orchestration";
|
||||||
import WorkflowEditor from "@/pages/Orchestration/WorkflowEditor";
|
import WorkflowEditor from "@/pages/Orchestration/WorkflowEditor";
|
||||||
import SettingsPage from "@/pages/SettingsPage/SettingsPage";
|
|
||||||
import { withErrorBoundary } from "@/components/ErrorBoundary";
|
import { withErrorBoundary } from "@/components/ErrorBoundary";
|
||||||
import AgentPage from "@/pages/Agent/Agent.tsx";
|
import AgentPage from "@/pages/Agent/Agent.tsx";
|
||||||
|
import RatioTaskDetail from "@/pages/RatioTask/Detail/RatioTaskDetail";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -165,6 +165,10 @@ const router = createBrowserRouter([
|
|||||||
path: "create",
|
path: "create",
|
||||||
Component: CreateRatioTask,
|
Component: CreateRatioTask,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "detail/:id",
|
||||||
|
Component: RatioTaskDetail,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -225,10 +229,6 @@ const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "settings",
|
|
||||||
Component: SettingsPage,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user