feat: fix the problem in the Operator Market frontend pages

This commit is contained in:
root
2025-12-29 11:38:47 +08:00
parent 29e4a333a9
commit 844add27ea
213 changed files with 45547 additions and 45537 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,308 +1,308 @@
import { useState, useRef } from "react";
import {
Card,
Select,
Input,
Button,
Badge,
Divider,
Form,
message,
} from "antd";
import { Plus, ArrowLeft, Play, Save, RefreshCw, Code, X } from "lucide-react";
import { useNavigate } from "react-router";
import { mockTemplates } from "@/mock/annotation";
const { TextArea } = Input;
export default function InstructionTemplateCreate() {
const navigate = useNavigate();
const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(
null
);
const [isTestingTemplate, setIsTestingTemplate] = useState(false);
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
const [variables, setVariables] = useState<string[]>([]);
const variableInputRef = useRef<Input | null>(null);
const [form] = Form.useForm();
// 初始化表单数据
const initialValues = selectedTemplate
? {
name: selectedTemplate.name,
category: selectedTemplate.category,
prompt: selectedTemplate.prompt,
description: selectedTemplate.description,
testInput: "",
testOutput: "",
}
: {
name: "",
category: "",
prompt: "",
description: "",
testInput: "",
testOutput: "",
};
// 变量同步
const handlePromptChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
form.setFieldValue("prompt", value);
// 自动提取变量
const matches = Array.from(value.matchAll(/\{(\w+)\}/g)).map((m) => m[1]);
setVariables(Array.from(new Set(matches)));
};
// 添加变量(手动)
const handleAddVariable = () => {
const input = variableInputRef.current?.input;
const value = input?.value.trim();
if (value && !variables.includes(value)) {
setVariables([...variables, value]);
input.value = "";
}
};
// 删除变量
const handleRemoveVariable = (index: number) => {
setVariables(variables.filter((_, i) => i !== index));
};
// 测试模板
const handleTestTemplate = async () => {
const values = form.getFieldsValue();
if (!values.prompt || !values.testInput) return;
setIsTestingTemplate(true);
setTimeout(() => {
form.setFieldValue(
"testOutput",
`基于输入"${values.testInput}"生成的测试结果:
这是一个模拟的输出结果,展示了模板的工作效果。在实际使用中,这里会显示AI模型根据您的模板和输入生成的真实结果。
模板变量已正确替换,输出格式符合预期。`
);
setIsTestingTemplate(false);
}, 2000);
};
// 保存模板
const handleSaveTemplate = async () => {
try {
const values = await form.validateFields();
if (!values.name || !values.prompt || !values.category) return;
if (selectedTemplate) {
setTemplates((prev) =>
prev.map((t) =>
t.id === selectedTemplate.id
? {
...t,
...values,
variables,
type: "custom" as const,
usageCount: t.usageCount,
lastUsed: new Date().toISOString().split("T")[0],
}
: t
)
);
} else {
const newTemplate: Template = {
id: Date.now(),
...values,
variables,
type: "custom",
usageCount: 0,
quality: 85,
};
setTemplates([newTemplate, ...templates]);
}
message.success("模板已保存");
navigate("/data/synthesis/task");
} catch {
// 校验失败
}
};
return (
<div className="h-full flex flex-col gap-4">
{/* Header */}
<div className="flex items-center justify-between mb-2">
<div className="flex items-center">
<Button onClick={() => navigate("/data/synthesis/task")} type="text">
<ArrowLeft className="w-4 h-4 mr-2" />
</Button>
<h1 className="text-xl font-bold bg-clip-text">
{selectedTemplate ? "编辑模板" : "创建新模板"}
</h1>
</div>
</div>
<div className="flex-overflow-auto border-card p-4">
<div className="flex-1 overflow-auto">
<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内容" }]}
>
<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={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>
</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>
);
}
import { useState, useRef } from "react";
import {
Card,
Select,
Input,
Button,
Badge,
Divider,
Form,
message,
} from "antd";
import { Plus, ArrowLeft, Play, Save, RefreshCw, Code, X } from "lucide-react";
import { useNavigate } from "react-router";
import { mockTemplates } from "@/mock/annotation";
const { TextArea } = Input;
export default function InstructionTemplateCreate() {
const navigate = useNavigate();
const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(
null
);
const [isTestingTemplate, setIsTestingTemplate] = useState(false);
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
const [variables, setVariables] = useState<string[]>([]);
const variableInputRef = useRef<Input | null>(null);
const [form] = Form.useForm();
// 初始化表单数据
const initialValues = selectedTemplate
? {
name: selectedTemplate.name,
category: selectedTemplate.category,
prompt: selectedTemplate.prompt,
description: selectedTemplate.description,
testInput: "",
testOutput: "",
}
: {
name: "",
category: "",
prompt: "",
description: "",
testInput: "",
testOutput: "",
};
// 变量同步
const handlePromptChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
form.setFieldValue("prompt", value);
// 自动提取变量
const matches = Array.from(value.matchAll(/\{(\w+)\}/g)).map((m) => m[1]);
setVariables(Array.from(new Set(matches)));
};
// 添加变量(手动)
const handleAddVariable = () => {
const input = variableInputRef.current?.input;
const value = input?.value.trim();
if (value && !variables.includes(value)) {
setVariables([...variables, value]);
input.value = "";
}
};
// 删除变量
const handleRemoveVariable = (index: number) => {
setVariables(variables.filter((_, i) => i !== index));
};
// 测试模板
const handleTestTemplate = async () => {
const values = form.getFieldsValue();
if (!values.prompt || !values.testInput) return;
setIsTestingTemplate(true);
setTimeout(() => {
form.setFieldValue(
"testOutput",
`基于输入"${values.testInput}"生成的测试结果:
这是一个模拟的输出结果,展示了模板的工作效果。在实际使用中,这里会显示AI模型根据您的模板和输入生成的真实结果。
模板变量已正确替换,输出格式符合预期。`
);
setIsTestingTemplate(false);
}, 2000);
};
// 保存模板
const handleSaveTemplate = async () => {
try {
const values = await form.validateFields();
if (!values.name || !values.prompt || !values.category) return;
if (selectedTemplate) {
setTemplates((prev) =>
prev.map((t) =>
t.id === selectedTemplate.id
? {
...t,
...values,
variables,
type: "custom" as const,
usageCount: t.usageCount,
lastUsed: new Date().toISOString().split("T")[0],
}
: t
)
);
} else {
const newTemplate: Template = {
id: Date.now(),
...values,
variables,
type: "custom",
usageCount: 0,
quality: 85,
};
setTemplates([newTemplate, ...templates]);
}
message.success("模板已保存");
navigate("/data/synthesis/task");
} catch {
// 校验失败
}
};
return (
<div className="h-full flex flex-col gap-4">
{/* Header */}
<div className="flex items-center justify-between mb-2">
<div className="flex items-center">
<Button onClick={() => navigate("/data/synthesis/task")} type="text">
<ArrowLeft className="w-4 h-4 mr-2" />
</Button>
<h1 className="text-xl font-bold bg-clip-text">
{selectedTemplate ? "编辑模板" : "创建新模板"}
</h1>
</div>
</div>
<div className="flex-overflow-auto border-card p-4">
<div className="flex-1 overflow-auto">
<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内容" }]}
>
<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={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>
</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,80 +1,80 @@
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";
export default function DataSynthesisPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState("tasks");
const [showAnnotatePage, setShowAnnotatePage] = useState(false);
if (showAnnotatePage) {
return (
<div>
<div className="flex">
<Button
onClick={() => setShowAnnotatePage(false)}
className="hover:bg-white/70"
>
<ArrowRight className="w-4 h-4 rotate-180 mr-2" />
</Button>
</div>
<DataAnnotation
task={undefined}
currentFileIndex={0}
onSaveAndNext={function (): void {
throw new Error("Function not implemented.");
}}
onSkipAndNext={function (): void {
throw new Error("Function not implemented.");
}}
/>
</div>
);
}
return (
<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>
</div>
<Tabs
items={[
{ key: "tasks", label: "合成任务", children: <SynthesisTaskTab /> },
{
key: "templates",
label: "指令模板",
children: <InstructionTemplateTab />,
},
]}
activeKey={activeTab}
onChange={setActiveTab}
></Tabs>
</div>
);
}
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";
export default function DataSynthesisPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState("tasks");
const [showAnnotatePage, setShowAnnotatePage] = useState(false);
if (showAnnotatePage) {
return (
<div>
<div className="flex">
<Button
onClick={() => setShowAnnotatePage(false)}
className="hover:bg-white/70"
>
<ArrowRight className="w-4 h-4 rotate-180 mr-2" />
</Button>
</div>
<DataAnnotation
task={undefined}
currentFileIndex={0}
onSaveAndNext={function (): void {
throw new Error("Function not implemented.");
}}
onSkipAndNext={function (): void {
throw new Error("Function not implemented.");
}}
/>
</div>
);
}
return (
<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>
</div>
<Tabs
items={[
{ key: "tasks", label: "合成任务", children: <SynthesisTaskTab /> },
{
key: "templates",
label: "指令模板",
children: <InstructionTemplateTab />,
},
]}
activeKey={activeTab}
onChange={setActiveTab}
></Tabs>
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,342 +1,342 @@
import { App } from "antd";
import { useEffect, useState } from "react";
import { Link, useNavigate, useParams } from "react-router";
import { DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import { Badge, Breadcrumb, Button, Table, Tabs, Progress, Tooltip } from "antd";
import type { BadgeProps } from "antd";
import type { ColumnsType, TablePaginationConfig } from "antd/es/table";
import DetailHeader from "@/components/DetailHeader";
import {
querySynthesisFileTasksUsingGet,
querySynthesisTaskByIdUsingGet,
deleteSynthesisTaskByIdUsingDelete,
} from "@/pages/SynthesisTask/synthesis-api";
import { formatDateTime } from "@/utils/unit";
import { Folder, Sparkles, Trash2 } from "lucide-react";
interface SynthesisFileTaskItem {
id: string;
synthesis_instance_id: string;
file_name: string;
source_file_id: string;
target_file_location: string;
status?: string;
total_chunks: number;
processed_chunks: number;
created_at?: string;
updated_at?: string;
}
interface PagedResponse<T> {
content: T[];
totalElements: number;
totalPages: number;
page: number;
size: number;
}
interface SynthesisTaskInfo {
id: string;
name: string;
synthesis_type: string;
status: string;
created_at: string;
updated_at?: string;
model_id: string;
total_files?: number;
total_synthesis_data?: number;
description?: string;
}
export default function SynthFileTask() {
const { id: taskId = "" } = useParams();
const navigate = useNavigate();
const { message } = App.useApp();
const [loading, setLoading] = useState(false);
const [data, setData] = useState<SynthesisFileTaskItem[]>([]);
const [pagination, setPagination] = useState<TablePaginationConfig>({
current: 1,
pageSize: 10,
total: 0,
});
const [taskInfo, setTaskInfo] = useState<SynthesisTaskInfo | null>(null);
const [activeTab, setActiveTab] = useState("files");
// 查询总任务详情
const fetchTaskDetail = async () => {
if (!taskId) return;
try {
const res = await querySynthesisTaskByIdUsingGet(taskId);
const raw = res?.data?.data ?? res?.data;
if (!raw) return;
setTaskInfo(raw);
} catch {
message.error("获取合成任务详情失败");
navigate("/data/synthesis/task");
}
};
useEffect(() => {
fetchTaskDetail();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [taskId]);
const fetchData = async (page = 1, pageSize = 10, withTopLoading = false) => {
if (!taskId) return;
if (withTopLoading) {
window.dispatchEvent(new Event("loading:show"));
}
setLoading(true);
try {
const res = await querySynthesisFileTasksUsingGet(taskId, {
page,
page_size: pageSize,
});
const payload: PagedResponse<SynthesisFileTaskItem> =
res?.data?.data ?? res?.data ?? {
content: [],
totalElements: 0,
totalPages: 0,
page,
size: pageSize,
};
setData(payload.content || []);
setPagination({
current: payload.page ?? page,
pageSize: payload.size ?? pageSize,
total: payload.totalElements ?? payload.content?.length ?? 0,
});
} finally {
setLoading(false);
if (withTopLoading) {
window.dispatchEvent(new Event("loading:hide"));
}
}
};
useEffect(() => {
// 首次进入或任务切换时,不触发顶部 loading,只用表格自带的 loading
fetchData(1, pagination.pageSize || 10, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [taskId]);
const handleTableChange = (pag: TablePaginationConfig) => {
// 分页切换时,也只用表格 loading,不闪顶部条
fetchData(pag.current || 1, pag.pageSize || 10, false);
};
const columns: ColumnsType<SynthesisFileTaskItem> = [
{
title: "文件",
key: "file",
render: (_text, record) => (
<div className="flex items-center gap-2">
<Folder className="w-4 h-4 text-blue-500" />
<Button
type="link"
onClick={() =>
navigate(`/data/synthesis/task/file/${record.id}/detail`, {
state: { fileName: record.file_name, taskId },
})
}
>
{record.file_name}
</Button>
</div>
),
},
{
title: "状态",
dataIndex: "status",
key: "status",
render: (status?: string) => {
let badgeStatus: BadgeProps["status"] = "default";
let text = status || "未知";
if (status === "pending" || status === "PROCESSING" || status === "processing") {
badgeStatus = "processing";
text = "处理中";
} else if (status === "COMPLETED" || status === "completed") {
badgeStatus = "success";
text = "已完成";
} else if (status === "FAILED" || status === "failed") {
badgeStatus = "error";
text = "失败";
}
return <Badge status={badgeStatus} text={text} />;
},
},
{
title: "切片总数",
dataIndex: "total_chunks",
key: "total_chunks",
},
{
title: "处理进度",
key: "progress",
render: (_text, record) => {
const total = record.total_chunks || 0;
const processed = record.processed_chunks || 0;
const percent = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
return (
<div style={{ minWidth: 160 }}>
<Progress
percent={percent}
size="small"
status={percent === 100 ? "success" : undefined}
format={() => `${processed}/${total}`}
/>
</div>
);
},
},
{
title: "创建时间",
dataIndex: "created_at",
key: "created_at",
render: (val?: string) => (val ? formatDateTime(val) : "-"),
},
{
title: "更新时间",
dataIndex: "updated_at",
key: "updated_at",
render: (val?: string) => (val ? formatDateTime(val) : "-"),
},
{
title: "操作",
key: "actions",
render: () => (
<Tooltip title="删除">
<Button
type="text"
danger
disabled
icon={<Trash2 className="w-4 h-4" />}
/>
</Tooltip>
),
},
];
const handleRefresh = async () => {
// 刷新按钮:明确触发一次顶部 loading,让用户看到“闪一下”的效果
window.dispatchEvent(new Event("loading:show"));
try {
await fetchTaskDetail();
await fetchData(pagination.current || 1, pagination.pageSize || 10, false);
} finally {
window.dispatchEvent(new Event("loading:hide"));
}
};
const handleDelete = async () => {
if (!taskId) return;
try {
await deleteSynthesisTaskByIdUsingDelete(taskId);
message.success("合成任务已删除");
navigate("/data/synthesis/task");
} catch {
message.error("删除合成任务失败");
}
};
// 头部统计与操作
const headerData: Record<string, unknown> = taskInfo
? {
name: taskInfo.name,
id: taskInfo.id,
icon: <Sparkles className="w-8 h-8" />,
description: taskInfo.description,
createdAt: taskInfo.created_at ? formatDateTime(taskInfo.created_at) : "--",
}
: {};
const statistics = [
{
key: "type",
icon: <Sparkles className="w-4 h-4 text-blue-500" />,
label: "类型",
value:
taskInfo?.synthesis_type === "QA"
? "问答对生成"
: taskInfo?.synthesis_type === "COT"
? "链式推理生成"
: taskInfo?.synthesis_type || "--",
},
{
key: "fileCount",
icon: <Folder className="w-4 h-4 text-purple-500" />,
label: "文件数",
value: taskInfo?.total_files ?? "--",
},
];
const operations = [
{
key: "refresh",
label: "刷新",
icon: <ReloadOutlined className="w-4 h-4" />,
onClick: handleRefresh,
},
{
key: "delete",
label: "删除任务",
icon: <DeleteOutlined className="w-4 h-4" />,
danger: true,
confirm: {
title: "确认删除该合成任务?",
description: "删除后将无法恢复,请谨慎操作。",
okText: "确认删除",
cancelText: "取消",
onConfirm: handleDelete,
placement: "top",
overlayStyle: {
marginTop: 40,
},
},
},
];
const tabList = [
{
key: "files",
label: "处理文件",
},
];
const breadItems = [
{
title: <Link to="/data/synthesis/task"></Link>,
},
{
title: taskInfo?.name || "任务详情",
},
];
return (
<>
<Breadcrumb items={breadItems} />
<div className="mb-4 mt-4">
<DetailHeader data={headerData} statistics={statistics} operations={operations} />
</div>
<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 flex-1 overflow-auto">
{activeTab === "files" && (
<Table<SynthesisFileTaskItem>
rowKey="id"
loading={loading}
dataSource={data}
columns={columns}
pagination={pagination}
onChange={handleTableChange}
/>
)}
</div>
</div>
</>
);
}
import { App } from "antd";
import { useEffect, useState } from "react";
import { Link, useNavigate, useParams } from "react-router";
import { DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import { Badge, Breadcrumb, Button, Table, Tabs, Progress, Tooltip } from "antd";
import type { BadgeProps } from "antd";
import type { ColumnsType, TablePaginationConfig } from "antd/es/table";
import DetailHeader from "@/components/DetailHeader";
import {
querySynthesisFileTasksUsingGet,
querySynthesisTaskByIdUsingGet,
deleteSynthesisTaskByIdUsingDelete,
} from "@/pages/SynthesisTask/synthesis-api";
import { formatDateTime } from "@/utils/unit";
import { Folder, Sparkles, Trash2 } from "lucide-react";
interface SynthesisFileTaskItem {
id: string;
synthesis_instance_id: string;
file_name: string;
source_file_id: string;
target_file_location: string;
status?: string;
total_chunks: number;
processed_chunks: number;
created_at?: string;
updated_at?: string;
}
interface PagedResponse<T> {
content: T[];
totalElements: number;
totalPages: number;
page: number;
size: number;
}
interface SynthesisTaskInfo {
id: string;
name: string;
synthesis_type: string;
status: string;
created_at: string;
updated_at?: string;
model_id: string;
total_files?: number;
total_synthesis_data?: number;
description?: string;
}
export default function SynthFileTask() {
const { id: taskId = "" } = useParams();
const navigate = useNavigate();
const { message } = App.useApp();
const [loading, setLoading] = useState(false);
const [data, setData] = useState<SynthesisFileTaskItem[]>([]);
const [pagination, setPagination] = useState<TablePaginationConfig>({
current: 1,
pageSize: 10,
total: 0,
});
const [taskInfo, setTaskInfo] = useState<SynthesisTaskInfo | null>(null);
const [activeTab, setActiveTab] = useState("files");
// 查询总任务详情
const fetchTaskDetail = async () => {
if (!taskId) return;
try {
const res = await querySynthesisTaskByIdUsingGet(taskId);
const raw = res?.data?.data ?? res?.data;
if (!raw) return;
setTaskInfo(raw);
} catch {
message.error("获取合成任务详情失败");
navigate("/data/synthesis/task");
}
};
useEffect(() => {
fetchTaskDetail();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [taskId]);
const fetchData = async (page = 1, pageSize = 10, withTopLoading = false) => {
if (!taskId) return;
if (withTopLoading) {
window.dispatchEvent(new Event("loading:show"));
}
setLoading(true);
try {
const res = await querySynthesisFileTasksUsingGet(taskId, {
page,
page_size: pageSize,
});
const payload: PagedResponse<SynthesisFileTaskItem> =
res?.data?.data ?? res?.data ?? {
content: [],
totalElements: 0,
totalPages: 0,
page,
size: pageSize,
};
setData(payload.content || []);
setPagination({
current: payload.page ?? page,
pageSize: payload.size ?? pageSize,
total: payload.totalElements ?? payload.content?.length ?? 0,
});
} finally {
setLoading(false);
if (withTopLoading) {
window.dispatchEvent(new Event("loading:hide"));
}
}
};
useEffect(() => {
// 首次进入或任务切换时,不触发顶部 loading,只用表格自带的 loading
fetchData(1, pagination.pageSize || 10, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [taskId]);
const handleTableChange = (pag: TablePaginationConfig) => {
// 分页切换时,也只用表格 loading,不闪顶部条
fetchData(pag.current || 1, pag.pageSize || 10, false);
};
const columns: ColumnsType<SynthesisFileTaskItem> = [
{
title: "文件",
key: "file",
render: (_text, record) => (
<div className="flex items-center gap-2">
<Folder className="w-4 h-4 text-blue-500" />
<Button
type="link"
onClick={() =>
navigate(`/data/synthesis/task/file/${record.id}/detail`, {
state: { fileName: record.file_name, taskId },
})
}
>
{record.file_name}
</Button>
</div>
),
},
{
title: "状态",
dataIndex: "status",
key: "status",
render: (status?: string) => {
let badgeStatus: BadgeProps["status"] = "default";
let text = status || "未知";
if (status === "pending" || status === "PROCESSING" || status === "processing") {
badgeStatus = "processing";
text = "处理中";
} else if (status === "COMPLETED" || status === "completed") {
badgeStatus = "success";
text = "已完成";
} else if (status === "FAILED" || status === "failed") {
badgeStatus = "error";
text = "失败";
}
return <Badge status={badgeStatus} text={text} />;
},
},
{
title: "切片总数",
dataIndex: "total_chunks",
key: "total_chunks",
},
{
title: "处理进度",
key: "progress",
render: (_text, record) => {
const total = record.total_chunks || 0;
const processed = record.processed_chunks || 0;
const percent = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
return (
<div style={{ minWidth: 160 }}>
<Progress
percent={percent}
size="small"
status={percent === 100 ? "success" : undefined}
format={() => `${processed}/${total}`}
/>
</div>
);
},
},
{
title: "创建时间",
dataIndex: "created_at",
key: "created_at",
render: (val?: string) => (val ? formatDateTime(val) : "-"),
},
{
title: "更新时间",
dataIndex: "updated_at",
key: "updated_at",
render: (val?: string) => (val ? formatDateTime(val) : "-"),
},
{
title: "操作",
key: "actions",
render: () => (
<Tooltip title="删除">
<Button
type="text"
danger
disabled
icon={<Trash2 className="w-4 h-4" />}
/>
</Tooltip>
),
},
];
const handleRefresh = async () => {
// 刷新按钮:明确触发一次顶部 loading,让用户看到“闪一下”的效果
window.dispatchEvent(new Event("loading:show"));
try {
await fetchTaskDetail();
await fetchData(pagination.current || 1, pagination.pageSize || 10, false);
} finally {
window.dispatchEvent(new Event("loading:hide"));
}
};
const handleDelete = async () => {
if (!taskId) return;
try {
await deleteSynthesisTaskByIdUsingDelete(taskId);
message.success("合成任务已删除");
navigate("/data/synthesis/task");
} catch {
message.error("删除合成任务失败");
}
};
// 头部统计与操作
const headerData: Record<string, unknown> = taskInfo
? {
name: taskInfo.name,
id: taskInfo.id,
icon: <Sparkles className="w-8 h-8" />,
description: taskInfo.description,
createdAt: taskInfo.created_at ? formatDateTime(taskInfo.created_at) : "--",
}
: {};
const statistics = [
{
key: "type",
icon: <Sparkles className="w-4 h-4 text-blue-500" />,
label: "类型",
value:
taskInfo?.synthesis_type === "QA"
? "问答对生成"
: taskInfo?.synthesis_type === "COT"
? "链式推理生成"
: taskInfo?.synthesis_type || "--",
},
{
key: "fileCount",
icon: <Folder className="w-4 h-4 text-purple-500" />,
label: "文件数",
value: taskInfo?.total_files ?? "--",
},
];
const operations = [
{
key: "refresh",
label: "刷新",
icon: <ReloadOutlined className="w-4 h-4" />,
onClick: handleRefresh,
},
{
key: "delete",
label: "删除任务",
icon: <DeleteOutlined className="w-4 h-4" />,
danger: true,
confirm: {
title: "确认删除该合成任务?",
description: "删除后将无法恢复,请谨慎操作。",
okText: "确认删除",
cancelText: "取消",
onConfirm: handleDelete,
placement: "top",
overlayStyle: {
marginTop: 40,
},
},
},
];
const tabList = [
{
key: "files",
label: "处理文件",
},
];
const breadItems = [
{
title: <Link to="/data/synthesis/task"></Link>,
},
{
title: taskInfo?.name || "任务详情",
},
];
return (
<>
<Breadcrumb items={breadItems} />
<div className="mb-4 mt-4">
<DetailHeader data={headerData} statistics={statistics} operations={operations} />
</div>
<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 flex-1 overflow-auto">
{activeTab === "files" && (
<Table<SynthesisFileTaskItem>
rowKey="id"
loading={loading}
dataSource={data}
columns={columns}
pagination={pagination}
onChange={handleTableChange}
/>
)}
</div>
</div>
</>
);
}

View File

@@ -1,190 +1,190 @@
import { useState } from "react";
import { Card, Table, Badge, Button } from "antd";
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";
import { SearchControls } from "@/components/SearchControls";
export default function InstructionTemplateTab() {
const navigate = useNavigate();
const [searchQuery, setSearchQuery] = useState("");
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
const [filterTemplateType, setFilterTemplateType] = useState("all");
// 过滤模板
const filteredTemplates = templates.filter((template) => {
const matchesSearch =
template.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
template.description.toLowerCase().includes(searchQuery.toLowerCase());
const matchesType =
filterTemplateType === "all" || template.type === filterTemplateType;
return matchesSearch && matchesType;
});
const getQualityColor = (quality: number) => {
if (quality >= 90) return "text-green-600 bg-green-50 border-green-200";
if (quality >= 80) return "text-blue-600 bg-blue-50 border-blue-200";
if (quality >= 70) return "text-yellow-600 bg-yellow-50 border-yellow-200";
return "text-red-600 bg-red-50 border-red-200";
};
// 模板表格列
const templateColumns = [
{
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">
<FileText className="w-4 h-4 text-white" />
</div>
<div>
<div className="font-medium text-gray-900 text-sm">
{template.name}
</div>
<div className="text-xs text-gray-500 line-clamp-1">
{template.description}
</div>
</div>
</div>
),
},
{
title: "类型",
dataIndex: "type",
key: "type",
render: (type: string) => (
<Badge className="text-xs">
{type === "preset" ? "预置" : "自定义"}
</Badge>
),
},
{
title: "分类",
dataIndex: "category",
key: "category",
render: (category: string) => (
<Badge className="bg-purple-50 text-purple-700 border-purple-200 text-xs">
{category}
</Badge>
),
},
{
title: "变量数量",
dataIndex: "variables",
key: "variables",
render: (variables: string[]) => (
<div className="text-sm font-medium text-gray-900">
{variables.length}
</div>
),
},
{
title: "使用次数",
dataIndex: "usageCount",
key: "usageCount",
render: (usageCount: number) => (
<div className="text-sm font-medium text-gray-900">{usageCount}</div>
),
},
{
title: "质量评分",
dataIndex: "quality",
key: "quality",
render: (quality: number) => (quality ? `${quality}%` : "-"),
},
{
title: "最后使用",
dataIndex: "lastUsed",
key: "lastUsed",
render: (lastUsed: string) => (
<div className="text-sm text-gray-600">{lastUsed || "-"}</div>
),
},
{
title: "操作",
key: "actions",
fixed: "right",
render: (_: any, template: Template) => (
<div className="flex items-center justify-center">
<Button type="text">
<EditOutlined />
</Button>
<Button type="text" danger>
<DeleteOutlined />
</Button>
</div>
),
},
];
return (
<div className="space-y-4">
<SearchControls
searchTerm={searchQuery}
onSearchChange={setSearchQuery}
searchPlaceholder="搜索模板名称或描述..."
filters={[
{
key: "type",
label: "类型",
options: [
{ label: "全部类型", value: "all" },
{ label: "预置模板", value: "preset" },
{ label: "自定义模板", value: "custom" },
],
},
]}
selectedFilters={{ type: [filterTemplateType] }}
onFiltersChange={(filters) => {
setFilterTemplateType(filters.type?.[0] || "all");
}}
showFilters
showViewToggle={false}
/>
{/* 模板表格 */}
<Card className="shadow-sm border-0 bg-white">
<Table
scroll={{ x: "max-content" }}
columns={templateColumns}
dataSource={filteredTemplates}
rowKey="id"
pagination={false}
locale={{
emptyText: (
<div className="text-center py-12">
<FileText className="w-12 h-12 text-gray-400 mx-auto mb-3" />
<h3 className="text-base font-semibold text-gray-900 mb-2">
</h3>
<p className="text-gray-500 mb-4 text-sm">
{searchQuery
? "没有找到匹配的模板"
: "开始创建您的第一个指令模板"}
</p>
{!searchQuery && (
<Button
onClick={() =>
navigate("/data/synthesis/task/create-template")
}
className="px-6 py-2 text-sm font-semibold bg-purple-600 hover:bg-purple-700 shadow-lg"
>
<Plus className="w-3 h-3 mr-1" />
</Button>
)}
</div>
),
}}
/>
</Card>
</div>
);
}
import { useState } from "react";
import { Card, Table, Badge, Button } from "antd";
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";
import { SearchControls } from "@/components/SearchControls";
export default function InstructionTemplateTab() {
const navigate = useNavigate();
const [searchQuery, setSearchQuery] = useState("");
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
const [filterTemplateType, setFilterTemplateType] = useState("all");
// 过滤模板
const filteredTemplates = templates.filter((template) => {
const matchesSearch =
template.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
template.description.toLowerCase().includes(searchQuery.toLowerCase());
const matchesType =
filterTemplateType === "all" || template.type === filterTemplateType;
return matchesSearch && matchesType;
});
const getQualityColor = (quality: number) => {
if (quality >= 90) return "text-green-600 bg-green-50 border-green-200";
if (quality >= 80) return "text-blue-600 bg-blue-50 border-blue-200";
if (quality >= 70) return "text-yellow-600 bg-yellow-50 border-yellow-200";
return "text-red-600 bg-red-50 border-red-200";
};
// 模板表格列
const templateColumns = [
{
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">
<FileText className="w-4 h-4 text-white" />
</div>
<div>
<div className="font-medium text-gray-900 text-sm">
{template.name}
</div>
<div className="text-xs text-gray-500 line-clamp-1">
{template.description}
</div>
</div>
</div>
),
},
{
title: "类型",
dataIndex: "type",
key: "type",
render: (type: string) => (
<Badge className="text-xs">
{type === "preset" ? "预置" : "自定义"}
</Badge>
),
},
{
title: "分类",
dataIndex: "category",
key: "category",
render: (category: string) => (
<Badge className="bg-purple-50 text-purple-700 border-purple-200 text-xs">
{category}
</Badge>
),
},
{
title: "变量数量",
dataIndex: "variables",
key: "variables",
render: (variables: string[]) => (
<div className="text-sm font-medium text-gray-900">
{variables.length}
</div>
),
},
{
title: "使用次数",
dataIndex: "usageCount",
key: "usageCount",
render: (usageCount: number) => (
<div className="text-sm font-medium text-gray-900">{usageCount}</div>
),
},
{
title: "质量评分",
dataIndex: "quality",
key: "quality",
render: (quality: number) => (quality ? `${quality}%` : "-"),
},
{
title: "最后使用",
dataIndex: "lastUsed",
key: "lastUsed",
render: (lastUsed: string) => (
<div className="text-sm text-gray-600">{lastUsed || "-"}</div>
),
},
{
title: "操作",
key: "actions",
fixed: "right",
render: (_: any, template: Template) => (
<div className="flex items-center justify-center">
<Button type="text">
<EditOutlined />
</Button>
<Button type="text" danger>
<DeleteOutlined />
</Button>
</div>
),
},
];
return (
<div className="space-y-4">
<SearchControls
searchTerm={searchQuery}
onSearchChange={setSearchQuery}
searchPlaceholder="搜索模板名称或描述..."
filters={[
{
key: "type",
label: "类型",
options: [
{ label: "全部类型", value: "all" },
{ label: "预置模板", value: "preset" },
{ label: "自定义模板", value: "custom" },
],
},
]}
selectedFilters={{ type: [filterTemplateType] }}
onFiltersChange={(filters) => {
setFilterTemplateType(filters.type?.[0] || "all");
}}
showFilters
showViewToggle={false}
/>
{/* 模板表格 */}
<Card className="shadow-sm border-0 bg-white">
<Table
scroll={{ x: "max-content" }}
columns={templateColumns}
dataSource={filteredTemplates}
rowKey="id"
pagination={false}
locale={{
emptyText: (
<div className="text-center py-12">
<FileText className="w-12 h-12 text-gray-400 mx-auto mb-3" />
<h3 className="text-base font-semibold text-gray-900 mb-2">
</h3>
<p className="text-gray-500 mb-4 text-sm">
{searchQuery
? "没有找到匹配的模板"
: "开始创建您的第一个指令模板"}
</p>
{!searchQuery && (
<Button
onClick={() =>
navigate("/data/synthesis/task/create-template")
}
className="px-6 py-2 text-sm font-semibold bg-purple-600 hover:bg-purple-700 shadow-lg"
>
<Plus className="w-3 h-3 mr-1" />
</Button>
)}
</div>
),
}}
/>
</Card>
</div>
);
}

View File

@@ -1,92 +1,92 @@
import { get, post, del } from "@/utils/request";
// 创建数据合成任务
export function createSynthesisTaskUsingPost(data: Record<string, unknown>) {
return post("/api/synthesis/gen/task", data as unknown as Record<string, never>);
}
// 获取数据合成任务详情
export function querySynthesisTaskByIdUsingGet(taskId: string) {
return get(`/api/synthesis/gen/task/${taskId}`);
}
// 分页查询数据合成任务列表
export function querySynthesisTasksUsingGet(params: {
page?: number;
page_size?: number;
synthesis_type?: string;
status?: string;
name?: string;
}) {
const searchParams = new URLSearchParams();
if (params.page !== undefined) searchParams.append("page", String(params.page));
if (params.page_size !== undefined) searchParams.append("page_size", String(params.page_size));
if (params.synthesis_type) searchParams.append("synthesis_type", params.synthesis_type);
if (params.status) searchParams.append("status", params.status);
if (params.name) searchParams.append("name", params.name);
const qs = searchParams.toString();
return get(`/api/synthesis/gen/tasks${qs ? `?${qs}` : ""}`);
}
// 删除整个数据合成任务
export function deleteSynthesisTaskByIdUsingDelete(taskId: string) {
return del(`/api/synthesis/gen/task/${taskId}`);
}
// 分页查询某个任务下的文件任务列表
export function querySynthesisFileTasksUsingGet(taskId: string, params: { page?: number; page_size?: number }) {
const searchParams = new URLSearchParams();
if (params.page !== undefined) searchParams.append("page", String(params.page));
if (params.page_size !== undefined) searchParams.append("page_size", String(params.page_size));
const qs = searchParams.toString();
return get(`/api/synthesis/gen/task/${taskId}/files${qs ? `?${qs}` : ""}`);
}
// 根据文件任务 ID 分页查询 chunk 记录
export function queryChunksByFileUsingGet(fileId: string, params: { page?: number; page_size?: number }) {
const searchParams = new URLSearchParams();
if (params.page !== undefined) searchParams.append("page", String(params.page));
if (params.page_size !== undefined) searchParams.append("page_size", String(params.page_size));
const qs = searchParams.toString();
return get(`/api/synthesis/gen/file/${fileId}/chunks${qs ? `?${qs}` : ""}`);
}
// 根据 chunk ID 查询所有合成结果数据
export function querySynthesisDataByChunkUsingGet(chunkId: string) {
return get(`/api/synthesis/gen/chunk/${chunkId}/data`);
}
// 获取不同合成类型对应的 Prompt
export function getPromptByTypeUsingGet(synthType: string) {
const searchParams = new URLSearchParams();
searchParams.append("synth_type", synthType);
const qs = searchParams.toString();
return get(`/api/synthesis/gen/prompt${qs ? `?${qs}` : ""}`);
}
// 将合成任务数据归档到已存在的数据集中
export function archiveSynthesisTaskToDatasetUsingPost(taskId: string, datasetId: string) {
return post(`/api/synthesis/gen/task/${taskId}/export-dataset/${datasetId}`);
}
// ---------------- 数据记录级别:chunk 与 synthesis data ----------------
// 根据 chunkId 删除单个 chunk 及其下所有合成数据
export function deleteChunkWithDataUsingDelete(chunkId: string) {
return del(`/api/synthesis/gen/chunk/${chunkId}`);
}
// 删除某个 chunk 下的所有合成数据,返回删除条数
export function deleteSynthesisDataByChunkUsingDelete(chunkId: string) {
return del(`/api/synthesis/gen/chunk/${chunkId}/data`);
}
// 批量删除合成数据记录
export function batchDeleteSynthesisDataUsingDelete(body: { ids: string[] }) {
return del(`/api/synthesis/gen/data/batch`, null, { body: JSON.stringify(body) });
}
// 更新单条合成数据的完整 JSON 内容
export function updateSynthesisDataUsingPatch(dataId: string, body: { data: Record<string, unknown> }) {
return post(`/api/synthesis/gen/data/${dataId}`, body, { method: "PATCH" });
}
import { get, post, del } from "@/utils/request";
// 创建数据合成任务
export function createSynthesisTaskUsingPost(data: Record<string, unknown>) {
return post("/api/synthesis/gen/task", data as unknown as Record<string, never>);
}
// 获取数据合成任务详情
export function querySynthesisTaskByIdUsingGet(taskId: string) {
return get(`/api/synthesis/gen/task/${taskId}`);
}
// 分页查询数据合成任务列表
export function querySynthesisTasksUsingGet(params: {
page?: number;
page_size?: number;
synthesis_type?: string;
status?: string;
name?: string;
}) {
const searchParams = new URLSearchParams();
if (params.page !== undefined) searchParams.append("page", String(params.page));
if (params.page_size !== undefined) searchParams.append("page_size", String(params.page_size));
if (params.synthesis_type) searchParams.append("synthesis_type", params.synthesis_type);
if (params.status) searchParams.append("status", params.status);
if (params.name) searchParams.append("name", params.name);
const qs = searchParams.toString();
return get(`/api/synthesis/gen/tasks${qs ? `?${qs}` : ""}`);
}
// 删除整个数据合成任务
export function deleteSynthesisTaskByIdUsingDelete(taskId: string) {
return del(`/api/synthesis/gen/task/${taskId}`);
}
// 分页查询某个任务下的文件任务列表
export function querySynthesisFileTasksUsingGet(taskId: string, params: { page?: number; page_size?: number }) {
const searchParams = new URLSearchParams();
if (params.page !== undefined) searchParams.append("page", String(params.page));
if (params.page_size !== undefined) searchParams.append("page_size", String(params.page_size));
const qs = searchParams.toString();
return get(`/api/synthesis/gen/task/${taskId}/files${qs ? `?${qs}` : ""}`);
}
// 根据文件任务 ID 分页查询 chunk 记录
export function queryChunksByFileUsingGet(fileId: string, params: { page?: number; page_size?: number }) {
const searchParams = new URLSearchParams();
if (params.page !== undefined) searchParams.append("page", String(params.page));
if (params.page_size !== undefined) searchParams.append("page_size", String(params.page_size));
const qs = searchParams.toString();
return get(`/api/synthesis/gen/file/${fileId}/chunks${qs ? `?${qs}` : ""}`);
}
// 根据 chunk ID 查询所有合成结果数据
export function querySynthesisDataByChunkUsingGet(chunkId: string) {
return get(`/api/synthesis/gen/chunk/${chunkId}/data`);
}
// 获取不同合成类型对应的 Prompt
export function getPromptByTypeUsingGet(synthType: string) {
const searchParams = new URLSearchParams();
searchParams.append("synth_type", synthType);
const qs = searchParams.toString();
return get(`/api/synthesis/gen/prompt${qs ? `?${qs}` : ""}`);
}
// 将合成任务数据归档到已存在的数据集中
export function archiveSynthesisTaskToDatasetUsingPost(taskId: string, datasetId: string) {
return post(`/api/synthesis/gen/task/${taskId}/export-dataset/${datasetId}`);
}
// ---------------- 数据记录级别:chunk 与 synthesis data ----------------
// 根据 chunkId 删除单个 chunk 及其下所有合成数据
export function deleteChunkWithDataUsingDelete(chunkId: string) {
return del(`/api/synthesis/gen/chunk/${chunkId}`);
}
// 删除某个 chunk 下的所有合成数据,返回删除条数
export function deleteSynthesisDataByChunkUsingDelete(chunkId: string) {
return del(`/api/synthesis/gen/chunk/${chunkId}/data`);
}
// 批量删除合成数据记录
export function batchDeleteSynthesisDataUsingDelete(body: { ids: string[] }) {
return del(`/api/synthesis/gen/data/batch`, null, { body: JSON.stringify(body) });
}
// 更新单条合成数据的完整 JSON 内容
export function updateSynthesisDataUsingPatch(dataId: string, body: { data: Record<string, unknown> }) {
return post(`/api/synthesis/gen/data/${dataId}`, body, { method: "PATCH" });
}

View File

@@ -1,35 +1,35 @@
export interface SynthesisTask {
id: number;
name: string;
type: "qa" | "distillation" | "text" | "multimodal";
status: "pending" | "running" | "completed" | "failed" | "paused";
progress: number;
sourceDataset: string;
targetCount: number;
generatedCount: number;
createdAt: string;
template: string;
estimatedTime?: string;
quality?: number;
errorMessage?: string;
}
export interface Template {
id: number;
name: string;
type: "preset" | "custom";
category: string;
prompt: string;
variables: string[];
description: string;
usageCount: number;
lastUsed?: string;
quality?: number;
}
interface File {
id: string;
name: string;
size: string;
type: string;
}
export interface SynthesisTask {
id: number;
name: string;
type: "qa" | "distillation" | "text" | "multimodal";
status: "pending" | "running" | "completed" | "failed" | "paused";
progress: number;
sourceDataset: string;
targetCount: number;
generatedCount: number;
createdAt: string;
template: string;
estimatedTime?: string;
quality?: number;
errorMessage?: string;
}
export interface Template {
id: number;
name: string;
type: "preset" | "custom";
category: string;
prompt: string;
variables: string[];
description: string;
usageCount: number;
lastUsed?: string;
quality?: number;
}
interface File {
id: string;
name: string;
size: string;
type: string;
}