add select dataset files component (#94)

* feat: Refactor AddDataDialog and introduce DatasetFileTransfer component for improved file selection and management

* feat: Refactor SynthesisTask and InstructionTemplate components for improved UI and functionality; integrate DatasetFileTransfer for file management

* feat: Enhance CollectionTaskCreate form with additional fields for MYSQL configuration and prefix input
This commit is contained in:
chenghh-9609
2025-11-20 14:12:59 +08:00
committed by GitHub
parent a07fba23f2
commit 955ffff6cd
8 changed files with 755 additions and 1264 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import type { Dataset } from "@/pages/DataManagement/dataset.model";
import {
Steps,
@@ -36,18 +36,21 @@ import {
Brain,
} from "lucide-react";
import { Link, useNavigate } from "react-router";
import DevelopmentInProgress from "@/components/DevelopmentInProgress";
import { queryDatasetsUsingGet } from "../DataManagement/dataset.api";
import { formatBytes } from "@/utils/unit";
import DatasetFileTransfer from "../KnowledgeBase/components/DatasetFileTransfer";
const { TextArea } = Input;
export default function SynthesisTaskCreate() {
return <DevelopmentInProgress showTime="2025.11.30" />;
const navigate = useNavigate();
const [form] = Form.useForm();
const [searchQuery, setSearchQuery] = useState("");
const [createStep, setCreateStep] = useState(1);
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
const [datasets] = useState<Dataset[]>([]);
const [selectedMap, setSelectedMap] = useState<Record<string, DatasetFile[]>>(
{}
);
const [files] = useState<File[]>([]);
const [selectedSynthesisTypes, setSelectedSynthesisTypes] = useState<
string[]
@@ -59,6 +62,15 @@ export default function SynthesisTaskCreate() {
"distillation",
]);
const fetchDatasets = async () => {
const { data } = await queryDatasetsUsingGet({ page: 1, size: 1000 });
setDatasets(data.content || []);
};
useEffect(() => {
fetchDatasets();
}, []);
// 表单数据
const [formValues, setFormValues] = useState({
name: "",
@@ -270,7 +282,7 @@ export default function SynthesisTaskCreate() {
const renderCreateTaskPage = () => {
if (createStep === 1) {
return (
<Card className="overflow-y-auto p-2">
<div className="flex-1 p-4 overflow-auto">
<Form
form={form}
layout="vertical"
@@ -305,152 +317,11 @@ export default function SynthesisTaskCreate() {
className="resize-none text-sm"
/>
</Form.Item>
<Form.Item
label="源数据集"
name="sourceDataset"
rules={[{ required: true, message: "请选择数据集" }]}
>
<Select
className="w-full"
placeholder="选择数据集"
options={datasets.map((dataset) => ({
label: (
<div key={dataset.id}>
<div className="flex flex-col py-1">
<span className="font-medium text-sm">
{dataset.name}
</span>
<span className="text-xs text-gray-500">
{dataset.type} {dataset.total} {dataset.size}
</span>
</div>
</div>
),
value: dataset.id,
}))}
/>
</Form.Item>
{form.getFieldValue("sourceDataset") && (
<div className="space-y-2">
<span className="text-sm font-semibold text-gray-700">
</span>
<div className="grid grid-cols-2 gap-4">
{/* 文件选择区域 */}
<Card className="border-gray-200">
<div className="space-y-3">
<div className="flex items-center justify-between">
<div className="relative flex-1">
<Search className="w-3 h-3 absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-400" />
<Input
placeholder="搜索文件..."
className="pl-7 h-8 text-sm"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<Button
onClick={handleSelectAllFiles}
className="ml-2 text-xs"
type="default"
>
{selectedFiles.length ===
files.filter((file) =>
file.name
.toLowerCase()
.includes(searchQuery.toLowerCase())
).length
? "取消全选"
: "全选"}
</Button>
</div>
<div className="space-y-1">
{files
.filter((file) =>
file.name
.toLowerCase()
.includes(searchQuery.toLowerCase())
)
.map((file) => (
<div
key={file.id}
className="flex items-center space-x-2 p-2 hover:bg-gray-50 rounded"
>
<Checkbox
checked={selectedFiles.includes(file.id)}
onChange={(e) => {
if (e.target.checked) {
setSelectedFiles([
...selectedFiles,
file.id,
]);
} else {
setSelectedFiles(
selectedFiles.filter(
(id) => id !== file.id
)
);
}
}}
/>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">
{file.name}
</p>
<p className="text-xs text-gray-500">
{file.size} {file.type}
</p>
</div>
</div>
))}
</div>
</div>
</Card>
{/* 已选文件列表 */}
<Card className="border-gray-200">
<div className="flex items-center justify-between">
<span className="text-sm font-medium"></span>
<Badge count={selectedFiles.length} className="text-xs" />
</div>
<div className="space-y-1">
{selectedFiles.length === 0 ? (
<div className="text-center py-4 text-xs text-gray-500">
</div>
) : (
selectedFiles.map((fileId) => {
const file = files.find((f) => f.id === fileId);
if (!file) return null;
return (
<div
key={fileId}
className="flex items-center justify-between p-2 bg-blue-50 rounded border border-blue-200"
>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-blue-900 truncate">
{file.name}
</p>
<p className="text-xs text-blue-600">
{file.size} {file.type}
</p>
</div>
<Button
type="text"
onClick={() => handleRemoveSelectedFile(fileId)}
className="p-1 h-6 w-6 hover:bg-blue-100"
>
<X className="w-3 h-3" />
</Button>
</div>
);
})
)}
</div>
</Card>
</div>
</div>
)}
<DatasetFileTransfer
open
selectedMap={selectedMap}
onSelectedChange={setSelectedMap}
/>
<h2 className="font-medium text-gray-900 text-lg mt-6 mb-2">
</h2>
@@ -514,32 +385,8 @@ export default function SynthesisTaskCreate() {
</Form.Item>
</div>
</div>
<Divider />
<div className="flex gap-2 justify-end">
<Button onClick={() => navigate("/data/synthesis/task")}>
</Button>
<Button
type="primary"
onClick={() => {
form
.validateFields()
.then(() => setCreateStep(2))
.catch(() => {});
}}
disabled={
!form.getFieldValue("name") ||
!form.getFieldValue("sourceDataset") ||
selectedFiles.length === 0 ||
!form.getFieldValue("targetCount")
}
>
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</div>
</Form>
</Card>
</div>
);
}
@@ -1224,26 +1071,47 @@ export default function SynthesisTaskCreate() {
};
return (
<div className="min-h-screen bg-gray-50">
<div className="p-6">
{/* Header */}
<div className="flex justify-between items-center mb-2">
<div className="flex items-center">
<Link to="/data/synthesis/task">
<Button type="text">
<ArrowLeft className="w-4 h-4 mr-1" />
</Button>
</Link>
<h1 className="text-xl font-bold bg-clip-text"></h1>
</div>
<Steps
current={createStep - 1}
size="small"
items={[{ title: "基本信息" }, { title: "算子编排" }]}
style={{ width: "50%", marginLeft: "auto" }}
/>
<div className="h-full flex flex-col">
{/* Header */}
<div className="flex justify-between items-center mb-2">
<div className="flex items-center">
<Link to="/data/synthesis/task">
<Button type="text">
<ArrowLeft className="w-4 h-4 mr-1" />
</Button>
</Link>
<h1 className="text-xl font-bold bg-clip-text"></h1>
</div>
<Steps
current={createStep - 1}
size="small"
items={[{ title: "基本信息" }, { title: "算子编排" }]}
style={{ width: "50%", marginLeft: "auto" }}
/>
</div>
<div className="border-card flex-overflow-auto">
{renderCreateTaskPage()}
<div className="flex gap-2 justify-end p-4 border-top">
<Button onClick={() => navigate("/data/synthesis/task")}></Button>
<Button
type="primary"
onClick={() => {
form
.validateFields()
.then(() => setCreateStep(2))
.catch(() => {});
}}
disabled={
!form.getFieldValue("name") ||
!form.getFieldValue("sourceDataset") ||
selectedFiles.length === 0 ||
!form.getFieldValue("targetCount")
}
>
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</div>
</div>
</div>
);

View File

@@ -12,12 +12,10 @@ import {
import { Plus, ArrowLeft, Play, Save, RefreshCw, Code, X } from "lucide-react";
import { useNavigate } from "react-router";
import { mockTemplates } from "@/mock/annotation";
import DevelopmentInProgress from "@/components/DevelopmentInProgress";
const { TextArea } = Input;
export default function InstructionTemplateCreate() {
return <DevelopmentInProgress showTime="2025.11.30" />;
const navigate = useNavigate();
const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(
null
@@ -129,7 +127,7 @@ export default function InstructionTemplateCreate() {
};
return (
<div className="min-h-screen">
<div className="h-full flex flex-col gap-4">
{/* Header */}
<div className="flex items-center justify-between mb-2">
<div className="flex items-center">
@@ -141,171 +139,170 @@ export default function InstructionTemplateCreate() {
</h1>
</div>
</div>
<Card className="overflow-y-auto p-2">
<Form
form={form}
layout="vertical"
initialValues={initialValues}
autoComplete="off"
>
<h2 className="font-medium text-gray-900 text-lg mb-2"></h2>
<div className="grid grid-cols-2 gap-4">
<Form.Item
label="模板名称"
name="name"
rules={[{ required: true, message: "请输入模板名称" }]}
>
<Input placeholder="输入模板名称" />
</Form.Item>
<Form.Item
label="分类"
name="category"
rules={[{ required: true, message: "请选择分类" }]}
>
<Select
placeholder="选择分类"
options={[
{ label: "问答对生成", value: "问答对生成" },
{ label: "蒸馏数据集", value: "蒸馏数据集" },
{ label: "文本生成", value: "文本生成" },
{ label: "多模态生成", value: "多模态生成" },
]}
/>
</Form.Item>
</div>
<Form.Item label="模板描述" name="description">
<Input placeholder="简要描述模板的用途和特点" />
</Form.Item>
<h2 className="font-medium text-gray-900 text-lg mt-6 mb-2">
Prompt内容
</h2>
<Form.Item
label="Prompt内容"
name="prompt"
rules={[{ required: true, message: "请输入Prompt内容" }]}
<div className="flex-overflow-auto border-card p-4">
<div className="flex-1 overflow-auto">
<Form
form={form}
layout="vertical"
initialValues={initialValues}
autoComplete="off"
>
<TextArea
placeholder="输入prompt内容,使用 {变量名} 格式定义变量"
rows={10}
className="font-mono text-xs resize-none"
onChange={handlePromptChange}
/>
</Form.Item>
<p className="text-xs text-gray-500 mb-2">
使 {"{变量名}"} {"{text}"} {"{input}"}
</p>
<div className="mb-4">
<span className="text-sm font-semibold text-gray-700">
</span>
<div className="flex flex-wrap gap-2 min-h-[50px] p-3 border rounded-xl bg-gray-50 mt-2">
{variables.map((variable, index) => (
<Badge
key={index}
count={
<X
className="w-3 h-3 cursor-pointer"
onClick={() => handleRemoveVariable(index)}
/>
}
style={{ backgroundColor: "#fff" }}
>
<span className="flex items-center gap-1 px-2 py-1 text-xs">
<Code className="w-3 h-3" />
{variable}
</span>
</Badge>
))}
{variables.length === 0 && (
<span className="text-xs text-gray-400">
Prompt中使用 {"{变量名}"}
</span>
)}
</div>
<div className="flex gap-2 mt-2">
<Input
ref={variableInputRef}
placeholder="添加变量名(如:text, input, question)"
className="h-8 text-sm"
onPressEnter={handleAddVariable}
/>
<Button
onClick={handleAddVariable}
type="default"
className="px-4 text-sm"
<h2 className="font-medium text-gray-900 text-lg mb-2"></h2>
<div className="grid grid-cols-2 gap-4">
<Form.Item
label="模板名称"
name="name"
rules={[{ required: true, message: "请输入模板名称" }]}
>
<Plus className="w-3 h-3 mr-1" />
</Button>
<Input placeholder="输入模板名称" />
</Form.Item>
<Form.Item
label="分类"
name="category"
rules={[{ required: true, message: "请选择分类" }]}
>
<Select
placeholder="选择分类"
options={[
{ label: "问答对生成", value: "问答对生成" },
{ label: "蒸馏数据集", value: "蒸馏数据集" },
{ label: "文本生成", value: "文本生成" },
{ label: "多模态生成", value: "多模态生成" },
]}
/>
</Form.Item>
</div>
</div>
<h2 className="font-medium text-gray-900 text-lg mb-2 pt-2">
</h2>
<div className="grid grid-cols-2 gap-4">
<Form.Item label="测试输入" name="testInput">
<TextArea
placeholder="输入测试数据"
rows={5}
className="resize-none text-sm"
/>
<Form.Item label="模板描述" name="description">
<Input placeholder="简要描述模板的用途和特点" />
</Form.Item>
<Form.Item label="测试输出" name="testOutput">
<TextArea
readOnly
placeholder="点击测试按钮查看输出结果"
rows={5}
className="resize-none bg-gray-50 text-sm"
/>
</Form.Item>
</div>
<Button
onClick={handleTestTemplate}
disabled={
!form.getFieldValue("prompt") ||
!form.getFieldValue("testInput") ||
isTestingTemplate
}
type="default"
className="px-4 py-2 text-sm"
>
{isTestingTemplate ? (
<>
<RefreshCw className="w-3 h-3 mr-1 animate-spin" />
...
</>
) : (
<>
<Play className="w-3 h-3 mr-1" />
</>
)}
</Button>
<Divider />
<div className="flex gap-2 justify-end">
<Button
type="primary"
onClick={handleSaveTemplate}
disabled={
!form.getFieldValue("name") ||
!form.getFieldValue("prompt") ||
!form.getFieldValue("category")
}
className="px-6 py-2 text-sm font-semibold bg-purple-600 hover:bg-purple-700 shadow-lg"
<h2 className="font-medium text-gray-900 text-lg mt-6 mb-2">
Prompt内容
</h2>
<Form.Item
label="Prompt内容"
name="prompt"
rules={[{ required: true, message: "请输入Prompt内容" }]}
>
<Save className="w-3 h-3 mr-1" />
</Button>
<TextArea
placeholder="输入prompt内容,使用 {变量名} 格式定义变量"
rows={10}
className="font-mono text-xs resize-none"
onChange={handlePromptChange}
/>
</Form.Item>
<p className="text-xs text-gray-500 mb-2">
使 {"{变量名}"} {"{text}"} {" "}
{"{input}"}
</p>
<div className="mb-4">
<span className="text-sm font-semibold text-gray-700">
</span>
<div className="flex flex-wrap gap-2 min-h-[50px] p-3 border rounded-xl bg-gray-50 mt-2">
{variables.map((variable, index) => (
<Badge
key={index}
count={
<X
className="w-3 h-3 cursor-pointer"
onClick={() => handleRemoveVariable(index)}
/>
}
style={{ backgroundColor: "#fff" }}
>
<span className="flex items-center gap-1 px-2 py-1 text-xs">
<Code className="w-3 h-3" />
{variable}
</span>
</Badge>
))}
{variables.length === 0 && (
<span className="text-xs text-gray-400">
Prompt中使用 {"{变量名}"}
</span>
)}
</div>
<div className="flex gap-2 mt-2">
<Input
ref={variableInputRef}
placeholder="添加变量名(如:text, input, question)"
className="h-8 text-sm"
onPressEnter={handleAddVariable}
/>
<Button
onClick={handleAddVariable}
type="default"
className="px-4 text-sm"
>
<Plus className="w-3 h-3 mr-1" />
</Button>
</div>
</div>
<h2 className="font-medium text-gray-900 text-lg mb-2 pt-2">
</h2>
<div className="grid grid-cols-2 gap-4">
<Form.Item label="测试输入" name="testInput">
<TextArea
placeholder="输入测试数据"
rows={5}
className="resize-none text-sm"
/>
</Form.Item>
<Form.Item label="测试输出" name="testOutput">
<TextArea
readOnly
placeholder="点击测试按钮查看输出结果"
rows={5}
className="resize-none bg-gray-50 text-sm"
/>
</Form.Item>
</div>
<Button
onClick={() => navigate("/data/synthesis/task")}
onClick={handleTestTemplate}
disabled={
!form.getFieldValue("prompt") ||
!form.getFieldValue("testInput") ||
isTestingTemplate
}
type="default"
className="px-4 py-2 text-sm"
>
{isTestingTemplate ? (
<>
<RefreshCw className="w-3 h-3 mr-1 animate-spin" />
...
</>
) : (
<></>
)}
</Button>
</div>
</Form>
</Card>
</Form>
</div>
<div className="flex gap-2 justify-end p-4 border-top">
<Button
type="primary"
onClick={handleSaveTemplate}
disabled={
!form.getFieldValue("name") ||
!form.getFieldValue("prompt") ||
!form.getFieldValue("category")
}
className="px-6 py-2 text-sm font-semibold bg-purple-600 hover:bg-purple-700 shadow-lg"
>
<Save className="w-3 h-3 mr-1" />
</Button>
<Button
onClick={() => navigate("/data/synthesis/task")}
type="default"
className="px-4 py-2 text-sm"
>
</Button>
</div>
</div>
</div>
);
}

View File

@@ -1,14 +1,13 @@
import { useState } from "react";
import { Tabs, Button } from "antd";
import { PlusOutlined } from "@ant-design/icons";
import { Plus, ArrowRight } from "lucide-react";
import DataAnnotation from "../DataAnnotation/Annotate/components/TextAnnotation";
import { useNavigate } from "react-router";
import InstructionTemplateTab from "./components/InstructionTemplateTab";
import SynthesisTaskTab from "./components/SynthesisTaskTab";
import DevelopmentInProgress from "@/components/DevelopmentInProgress";
export default function DataSynthesisPage() {
return <DevelopmentInProgress showTime="2025.11.30" />;
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState("tasks");
@@ -40,45 +39,42 @@ export default function DataSynthesisPage() {
}
return (
<div className="min-h-screen bg-gray-50">
<div className=" p-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-xl font-bold text-gray-900"></h2>
</div>
<div className="flex items-center gap-2">
<Button
onClick={() => {
navigate("/data/synthesis/task/create-template");
}}
>
<Plus className="w-3 h-3 mr-1" />
</Button>
<Button
type="primary"
onClick={() => navigate("/data/synthesis/task/create")}
>
<Plus className="w-3 h-3 mr-1" />
</Button>
</div>
<div className="h-full flex flex-col">
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-xl font-bold text-gray-900"></h2>
</div>
<div className="flex items-center gap-2">
<Button
onClick={() => {
navigate("/data/synthesis/task/create-template");
}}
icon={<PlusOutlined />}
>
</Button>
<Button
type="primary"
onClick={() => navigate("/data/synthesis/task/create")}
icon={<PlusOutlined />}
>
</Button>
</div>
<Tabs
items={[
{ key: "tasks", label: "合成任务", children: <SynthesisTaskTab /> },
{
key: "templates",
label: "指令模板",
children: <InstructionTemplateTab />,
},
]}
activeKey={activeTab}
onChange={setActiveTab}
></Tabs>
</div>
<Tabs
items={[
{ key: "tasks", label: "合成任务", children: <SynthesisTaskTab /> },
{
key: "templates",
label: "指令模板",
children: <InstructionTemplateTab />,
},
]}
activeKey={activeTab}
onChange={setActiveTab}
></Tabs>
</div>
);
}

View File

@@ -1,14 +1,7 @@
import { useState } from "react";
import { Card, Table, Badge, Button } from "antd";
import {
Plus,
FileText,
Search,
Edit,
Copy,
Trash2,
MoreHorizontal,
} from "lucide-react";
import { EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { Plus, FileText } from "lucide-react";
import type { Template } from "@/pages/SynthesisTask/synthesis";
import { useNavigate } from "react-router";
import { mockTemplates } from "@/mock/synthesis";
@@ -45,6 +38,7 @@ export default function InstructionTemplateTab() {
title: "模板名称",
dataIndex: "name",
key: "name",
fixed: "left",
render: (text: string, template: Template) => (
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-purple-500 rounded-lg flex items-center justify-center shadow-sm">
@@ -103,14 +97,7 @@ export default function InstructionTemplateTab() {
title: "质量评分",
dataIndex: "quality",
key: "quality",
render: (quality: number) =>
quality ? (
<Badge className={`font-medium text-xs ${getQualityColor(quality)}`}>
{quality}%
</Badge>
) : (
<span className="text-sm text-gray-400">-</span>
),
render: (quality: number) => (quality ? `${quality}%` : "-"),
},
{
title: "最后使用",
@@ -123,25 +110,14 @@ export default function InstructionTemplateTab() {
{
title: "操作",
key: "actions",
align: "center" as const,
fixed: "right",
render: (_: any, template: Template) => (
<div className="flex items-center justify-center gap-1">
<Button
onClick={() =>
navigate(`/data/synthesis/task/create-template/${template.id}`)
}
type="text"
>
<Edit className="w-3 h-3" />
</Button>
<div className="flex items-center justify-center">
<Button type="text">
<Copy className="w-3 h-3" />
<EditOutlined />
</Button>
<Button type="text">
<Trash2 className="w-3 h-3" />
</Button>
<Button type="text">
<MoreHorizontal className="w-3 h-3" />
<Button type="text" danger>
<DeleteOutlined />
</Button>
</div>
),

View File

@@ -9,11 +9,14 @@ import {
Play,
DownloadIcon,
CheckCircle,
Check,
StopCircle,
} from "lucide-react";
import type { SynthesisTask } from "@/pages/SynthesisTask/synthesis";
import { mockSynthesisTasks } from "@/mock/synthesis";
import { useNavigate } from "react-router";
import { Link, useNavigate } from "react-router";
import { SearchControls } from "@/components/SearchControls";
import { formatDateTime } from "@/utils/unit";
export default function SynthesisTaskTab() {
const navigate = useNavigate();
@@ -73,28 +76,28 @@ export default function SynthesisTaskTab() {
const getStatusBadge = (status: string) => {
const statusConfig = {
pending: {
span: "等待中",
color: "bg-yellow-50 text-yellow-700 border-yellow-200",
label: "等待中",
color: "#F59E0B",
icon: Pause,
},
running: {
span: "运行中",
color: "bg-blue-50 text-blue-700 border-blue-200",
label: "运行中",
color: "#3B82F6",
icon: Play,
},
completed: {
span: "已完成",
color: "bg-green-50 text-green-700 border-green-200",
label: "已完成",
color: "#10B981",
icon: CheckCircle,
},
failed: {
span: "失败",
color: "bg-red-50 text-red-700 border-red-200",
label: "失败",
color: "#EF4444",
icon: Pause,
},
paused: {
span: "已暂停",
color: "bg-gray-50 text-gray-700 border-gray-200",
label: "已暂停",
color: "#E5E7EB",
icon: Pause,
},
};
@@ -130,6 +133,7 @@ export default function SynthesisTaskTab() {
),
dataIndex: "name",
key: "name",
fixed: "left" as const,
render: (text: string, task: SynthesisTask) => (
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-blue-500 rounded-lg flex items-center justify-center shadow-sm">
@@ -139,7 +143,7 @@ export default function SynthesisTaskTab() {
</span>
</div>
<div>
<div className="font-medium text-gray-900 text-sm">{task.name}</div>
<Link to={`/data/synthesis/task/${task.id}`}>{task.name}</Link>
<div className="text-xs text-gray-500">{task.template}</div>
</div>
</div>
@@ -149,11 +153,7 @@ export default function SynthesisTaskTab() {
title: "类型",
dataIndex: "type",
key: "type",
render: (type: string) => (
<Badge className="bg-blue-50 text-blue-700 border-blue-200 text-xs">
{type?.toUpperCase()}
</Badge>
),
render: (type: string) => type.toUpperCase(),
},
{
title: "状态",
@@ -161,38 +161,17 @@ export default function SynthesisTaskTab() {
key: "status",
render: (status: string) => {
const statusConfig = getStatusBadge(status);
const StatusIcon = statusConfig.icon;
return (
<Badge
className={`${statusConfig.color} flex items-center gap-1 w-fit text-xs`}
>
<StatusIcon className="w-3 h-3" />
{statusConfig.span}
</Badge>
);
return <Badge color={statusConfig.color} text={statusConfig.label} />;
},
},
{
title: "进度",
dataIndex: "progress",
key: "progress",
render: (_: any, task: SynthesisTask) =>
task.status === "running" ? (
<div className="space-y-1">
<Progress percent={task.progress} size="small" showInfo={false} />
<div className="text-xs text-gray-500">
{Math.round(task.progress)}%
</div>
</div>
) : (
<div className="text-sm text-gray-600">
{task.status === "completed"
? "100%"
: task.status === "failed"
? `${Math.round(task.progress)}%`
: "-"}
</div>
),
width: 150,
render: (_: any, task: SynthesisTask) => (
<Progress percent={task.progress} size="small" />
),
},
{
title: "源数据集",
@@ -217,48 +196,18 @@ export default function SynthesisTaskTab() {
title: "质量评分",
dataIndex: "quality",
key: "quality",
render: (quality: number) =>
quality ? (
<Badge className="font-medium text-xs text-green-600 bg-green-50 border-green-200">
{quality}%
</Badge>
) : (
<span className="text-sm text-gray-400">-</span>
),
render: (quality: number) => (quality ? `${quality}%` : "-"),
},
{
title: (
<Button
type="text"
onClick={() => {
if (sortBy === "createdAt") {
setSortOrder(sortOrder === "asc" ? "desc" : "asc");
} else {
setSortBy("createdAt");
setSortOrder("desc");
}
}}
className="h-auto p-0 font-semibold text-gray-700 hover:bg-transparent"
>
{sortBy === "createdAt" &&
(sortOrder === "asc" ? (
<ArrowUp className="w-3 h-3 ml-1" />
) : (
<ArrowDown className="w-3 h-3 ml-1" />
))}
</Button>
),
title: "创建时间",
dataIndex: "createdAt",
key: "createdAt",
render: (createdAt: string) => (
<div className="text-sm text-gray-600">{createdAt}</div>
),
render: formatDateTime,
},
{
title: "操作",
key: "actions",
align: "center" as const,
fixed: "right" as const,
render: (_: any, task: SynthesisTask) => (
<div className="flex items-center justify-center gap-1">
{task.status === "running" && (
@@ -266,36 +215,24 @@ export default function SynthesisTaskTab() {
onClick={() => handleTaskAction(task.id, "pause")}
className="hover:bg-orange-50 p-1 h-7 w-7"
type="text"
>
<Pause className="w-3 h-3" />
</Button>
icon={<Pause className="w-4 h-4" />}
></Button>
)}
{task.status === "paused" && (
<Button
onClick={() => handleTaskAction(task.id, "resume")}
className="hover:bg-green-50 p-1 h-7 w-7"
type="text"
>
<Play className="w-3 h-3" />
</Button>
icon={<Play className="w-4 h-4" />}
></Button>
)}
<Button
className="hover:bg-blue-50 p-2 h-7 w-7"
type="text"
onClick={() => navigate(`/data/synthesis/task/${task.id}`)}
>
</Button>
<Button className="hover:bg-green-50 p-1 h-7 w-7" type="text">
<DownloadIcon className="w-3 h-3" />
</Button>
</div>
),
},
];
return (
<div className="">
<div className="space-y-4">
{/* 搜索和筛选 */}
<SearchControls
searchTerm={searchQuery}