You've already forked DataMate
init datamate
This commit is contained in:
574
frontend/src/pages/DataEvaluation/Create/CreateTask.tsx
Normal file
574
frontend/src/pages/DataEvaluation/Create/CreateTask.tsx
Normal file
@@ -0,0 +1,574 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Badge,
|
||||
Input,
|
||||
Select,
|
||||
Checkbox,
|
||||
Form,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {
|
||||
PlusOutlined,
|
||||
ArrowLeftOutlined,
|
||||
EditOutlined,
|
||||
SaveOutlined,
|
||||
DeleteOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
evaluationTemplates,
|
||||
presetEvaluationDimensions,
|
||||
sliceOperators,
|
||||
} from "@/mock/evaluation";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
const { Title, Paragraph } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
const EvaluationTaskCreate = () => {
|
||||
const navigate = useNavigate();
|
||||
const [datasets, setDatasets] = useState([]);
|
||||
const [selectedTemplate, setSelectedTemplate] =
|
||||
useState<string>("dialogue_text");
|
||||
const [allDimensions, setAllDimensions] = useState<EvaluationDimension[]>([
|
||||
...presetEvaluationDimensions,
|
||||
]);
|
||||
const [editingDimension, setEditingDimension] = useState<string | null>(null);
|
||||
const [newDimension, setNewDimension] = useState({
|
||||
name: "",
|
||||
description: "",
|
||||
});
|
||||
const [createForm, setCreateForm] = useState({
|
||||
name: "",
|
||||
datasetId: "",
|
||||
evaluationType: "model" as "model" | "manual",
|
||||
dimensions: [] as string[],
|
||||
customDimensions: [] as EvaluationDimension[],
|
||||
sliceConfig: {
|
||||
threshold: 0.8,
|
||||
sampleCount: 100,
|
||||
method: "语义分割",
|
||||
},
|
||||
modelConfig: {
|
||||
url: "",
|
||||
apiKey: "",
|
||||
prompt: "",
|
||||
temperature: 0.3,
|
||||
maxTokens: 2000,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTemplateChange = (templateKey: string) => {
|
||||
setSelectedTemplate(templateKey);
|
||||
const template =
|
||||
evaluationTemplates[templateKey as keyof typeof evaluationTemplates];
|
||||
if (template) {
|
||||
const customDimensions = allDimensions.filter((d) => d.isCustom);
|
||||
setAllDimensions([...template.dimensions, ...customDimensions]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddCustomDimension = () => {
|
||||
if (newDimension.name.trim() && newDimension.description.trim()) {
|
||||
const customDimension: EvaluationDimension = {
|
||||
id: `custom_${Date.now()}`,
|
||||
name: newDimension.name.trim(),
|
||||
description: newDimension.description.trim(),
|
||||
category: "custom",
|
||||
isCustom: true,
|
||||
isEnabled: true,
|
||||
};
|
||||
setAllDimensions([...allDimensions, customDimension]);
|
||||
setNewDimension({ name: "", description: "" });
|
||||
}
|
||||
};
|
||||
|
||||
const handleDimensionToggle = (id: string, checked: boolean) => {
|
||||
setAllDimensions(
|
||||
allDimensions.map((d) => (d.id === id ? { ...d, isEnabled: checked } : d))
|
||||
);
|
||||
};
|
||||
|
||||
const handleEditDimension = (
|
||||
id: string,
|
||||
field: "name" | "description",
|
||||
value: string
|
||||
) => {
|
||||
setAllDimensions(
|
||||
allDimensions.map((d) => (d.id === id ? { ...d, [field]: value } : d))
|
||||
);
|
||||
};
|
||||
|
||||
const handleDeleteCustomDimension = (id: string) => {
|
||||
setAllDimensions(allDimensions.filter((d) => d.id !== id));
|
||||
};
|
||||
|
||||
const handleDeletePresetDimension = (id: string) => {
|
||||
setAllDimensions(
|
||||
allDimensions.map((d) => (d.id === id ? { ...d, isEnabled: false } : d))
|
||||
);
|
||||
};
|
||||
|
||||
const handleCreateTask = () => {
|
||||
const selectedDataset = datasets.find((d) => d.id === createForm.datasetId);
|
||||
if (!selectedDataset) return;
|
||||
|
||||
const enabledDimensions = allDimensions.filter((d) => d.isEnabled);
|
||||
const presetDimensionIds = enabledDimensions
|
||||
.filter((d) => !d.isCustom)
|
||||
.map((d) => d.id);
|
||||
const customDimensions = enabledDimensions.filter((d) => d.isCustom);
|
||||
|
||||
let finalPrompt = createForm.modelConfig.prompt;
|
||||
if (createForm.evaluationType === "model" && !finalPrompt.trim()) {
|
||||
finalPrompt = generateDefaultPrompt(selectedDataset.name);
|
||||
}
|
||||
|
||||
const newTask: EvaluationTask = {
|
||||
id: Date.now().toString(),
|
||||
name: createForm.name,
|
||||
datasetId: createForm.datasetId,
|
||||
datasetName: selectedDataset.name,
|
||||
evaluationType: createForm.evaluationType,
|
||||
status: "pending",
|
||||
progress: 0,
|
||||
createdAt: new Date().toLocaleString(),
|
||||
description: `${
|
||||
createForm.evaluationType === "model" ? "模型自动" : "人工"
|
||||
}评估${selectedDataset.name}`,
|
||||
dimensions: presetDimensionIds,
|
||||
customDimensions: customDimensions,
|
||||
modelConfig:
|
||||
createForm.evaluationType === "model"
|
||||
? {
|
||||
...createForm.modelConfig,
|
||||
prompt: finalPrompt,
|
||||
}
|
||||
: undefined,
|
||||
metrics: {
|
||||
accuracy: 0,
|
||||
completeness: 0,
|
||||
consistency: 0,
|
||||
relevance: 0,
|
||||
},
|
||||
issues: [],
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
setCreateForm({
|
||||
name: "",
|
||||
datasetId: "",
|
||||
evaluationType: "model",
|
||||
dimensions: [],
|
||||
customDimensions: [],
|
||||
modelConfig: {
|
||||
url: "",
|
||||
apiKey: "",
|
||||
prompt: "",
|
||||
temperature: 0.3,
|
||||
maxTokens: 2000,
|
||||
},
|
||||
});
|
||||
navigate("/data/evaluation");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
{/* 页面头部 */}
|
||||
<div className="flex items-center mb-2">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={() => navigate("/data/evaluation")}
|
||||
></Button>
|
||||
<div className="text-xl font-bold">创建评估任务</div>
|
||||
</div>
|
||||
|
||||
<Form layout="vertical">
|
||||
{/* 基本信息 */}
|
||||
<Card title="基本信息" style={{ marginBottom: 24 }}>
|
||||
<Form.Item label="任务名称" required>
|
||||
<Input
|
||||
value={createForm.name}
|
||||
onChange={(e) =>
|
||||
setCreateForm({ ...createForm, name: e.target.value })
|
||||
}
|
||||
placeholder="输入任务名称"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="选择数据集" required>
|
||||
<Select
|
||||
value={createForm.datasetId || undefined}
|
||||
onChange={(value) =>
|
||||
setCreateForm({ ...createForm, datasetId: value })
|
||||
}
|
||||
placeholder="选择要评估的数据集"
|
||||
>
|
||||
{datasets.map((dataset) => (
|
||||
<Option key={dataset.id} value={dataset.id}>
|
||||
{dataset.name}({dataset.fileCount} 文件 • {dataset.size})
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="评估方式" required>
|
||||
<Select
|
||||
value={createForm.evaluationType}
|
||||
onChange={(value: "model" | "manual") =>
|
||||
setCreateForm({ ...createForm, evaluationType: value })
|
||||
}
|
||||
>
|
||||
<Option value="model">模型自动评估</Option>
|
||||
<Option value="manual">人工评估</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
{/* 算子配置 */}
|
||||
<Card title="切片算子配置" style={{ marginBottom: 24 }}>
|
||||
<Form.Item label="切片算子">
|
||||
<Select
|
||||
value={createForm.sliceConfig.method}
|
||||
onChange={(value) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
sliceConfig: { ...createForm.sliceConfig, method: value },
|
||||
})
|
||||
}
|
||||
placeholder="选择切片算子"
|
||||
>
|
||||
{sliceOperators.map((operator) => (
|
||||
<Option key={operator.id} value={operator.name}>
|
||||
{operator.name}{" "}
|
||||
<Badge style={{ marginLeft: 8 }} count={operator.type} />
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="分隔符">
|
||||
<Input
|
||||
placeholder="输入分隔符,如 \\n\\n"
|
||||
value={createForm.sliceConfig.delimiter}
|
||||
onChange={(e) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
sliceConfig: {
|
||||
...createForm.sliceConfig,
|
||||
delimiter: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="分块大小">
|
||||
<Input
|
||||
type="number"
|
||||
value={createForm.sliceConfig.chunkSize}
|
||||
onChange={(e) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
sliceConfig: {
|
||||
...createForm.sliceConfig,
|
||||
chunkSize: Number(e.target.value),
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="重叠长度">
|
||||
<Input
|
||||
type="number"
|
||||
value={createForm.sliceConfig.overlapLength}
|
||||
onChange={(e) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
sliceConfig: {
|
||||
...createForm.sliceConfig,
|
||||
overlapLength: Number(e.target.value),
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="抽样比例">
|
||||
<Input
|
||||
type="number"
|
||||
value={createForm.sliceConfig.threshold}
|
||||
onChange={(e) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
sliceConfig: {
|
||||
...createForm.sliceConfig,
|
||||
threshold: Number(e.target.value),
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
{/* 评估维度配置 */}
|
||||
<Card
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<span>评估维度配置</span>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<Select
|
||||
value={selectedTemplate}
|
||||
onChange={handleTemplateChange}
|
||||
style={{ width: 160 }}
|
||||
>
|
||||
{Object.entries(evaluationTemplates).map(
|
||||
([key, template]) => (
|
||||
<Option key={key} value={key}>
|
||||
{template.name}
|
||||
</Option>
|
||||
)
|
||||
)}
|
||||
</Select>
|
||||
<Badge
|
||||
count={allDimensions.filter((d) => d.isEnabled).length}
|
||||
style={{ background: "#f0f0f0", color: "#333" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
style={{ marginBottom: 24 }}
|
||||
>
|
||||
{/* 维度表格 */}
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #f0f0f0",
|
||||
borderRadius: 6,
|
||||
marginBottom: 16,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: "#fafafa",
|
||||
padding: "8px 12px",
|
||||
borderBottom: "1px solid #f0f0f0",
|
||||
fontWeight: 500,
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<div style={{ width: 60 }}>启用</div>
|
||||
<div style={{ width: 160 }}>维度名称</div>
|
||||
<div style={{ flex: 1 }}>描述</div>
|
||||
<div style={{ width: 120 }}>操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ maxHeight: 320, overflowY: "auto" }}>
|
||||
{allDimensions.map((dimension) => (
|
||||
<div
|
||||
key={dimension.id}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "8px 12px",
|
||||
borderBottom: "1px solid #f5f5f5",
|
||||
}}
|
||||
>
|
||||
<div style={{ width: 60 }}>
|
||||
<Checkbox
|
||||
checked={dimension.isEnabled}
|
||||
onChange={(e) =>
|
||||
handleDimensionToggle(dimension.id, e.target.checked!)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ width: 160 }}>
|
||||
{editingDimension === dimension.id && dimension.isCustom ? (
|
||||
<Input
|
||||
value={dimension.name}
|
||||
onChange={(e) =>
|
||||
handleEditDimension(
|
||||
dimension.id,
|
||||
"name",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<span style={{ fontWeight: 500 }}>
|
||||
{dimension.name}
|
||||
{dimension.isCustom && (
|
||||
<Badge
|
||||
style={{
|
||||
marginLeft: 4,
|
||||
background: "#f9f0ff",
|
||||
color: "#722ed1",
|
||||
}}
|
||||
count="自定义"
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
{editingDimension === dimension.id && dimension.isCustom ? (
|
||||
<Input
|
||||
value={dimension.description}
|
||||
onChange={(e) =>
|
||||
handleEditDimension(
|
||||
dimension.id,
|
||||
"description",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<span style={{ color: "#888" }}>
|
||||
{dimension.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ width: 120 }}>
|
||||
{editingDimension === dimension.id && dimension.isCustom ? (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<SaveOutlined />}
|
||||
size="small"
|
||||
onClick={() => setEditingDimension(null)}
|
||||
/>
|
||||
) : (
|
||||
dimension.isCustom && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
size="small"
|
||||
onClick={() => setEditingDimension(dimension.id)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
size="small"
|
||||
danger
|
||||
onClick={() =>
|
||||
dimension.isCustom
|
||||
? handleDeleteCustomDimension(dimension.id)
|
||||
: handleDeletePresetDimension(dimension.id)
|
||||
}
|
||||
disabled={
|
||||
allDimensions.filter((d) => d.isEnabled).length <= 1 &&
|
||||
dimension.isEnabled
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* 添加自定义维度 */}
|
||||
<div style={{ background: "#fafafa", borderRadius: 6, padding: 16 }}>
|
||||
<div style={{ fontWeight: 500, marginBottom: 8 }}>
|
||||
添加自定义维度
|
||||
</div>
|
||||
<Input
|
||||
value={newDimension.name}
|
||||
onChange={(e) =>
|
||||
setNewDimension({ ...newDimension, name: e.target.value })
|
||||
}
|
||||
placeholder="维度名称"
|
||||
style={{ width: 180, marginRight: 8 }}
|
||||
size="small"
|
||||
/>
|
||||
<Input
|
||||
value={newDimension.description}
|
||||
onChange={(e) =>
|
||||
setNewDimension({
|
||||
...newDimension,
|
||||
description: e.target.value,
|
||||
})
|
||||
}
|
||||
placeholder="维度描述"
|
||||
style={{ width: 260, marginRight: 8 }}
|
||||
size="small"
|
||||
/>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAddCustomDimension}
|
||||
disabled={
|
||||
!newDimension.name.trim() || !newDimension.description.trim()
|
||||
}
|
||||
size="small"
|
||||
>
|
||||
添加维度
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 模型配置(仅在选择模型评估时显示) */}
|
||||
{createForm.evaluationType === "model" && (
|
||||
<Card title="模型配置" style={{ marginBottom: 24 }}>
|
||||
<Form.Item label="模型 URL" required>
|
||||
<Input
|
||||
value={createForm.modelConfig.url}
|
||||
onChange={(e) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
modelConfig: {
|
||||
...createForm.modelConfig,
|
||||
url: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
placeholder="https://api.openai.com/v1/chat/completions"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="API Key" required>
|
||||
<Input.Password
|
||||
value={createForm.modelConfig.apiKey}
|
||||
onChange={(e) =>
|
||||
setCreateForm({
|
||||
...createForm,
|
||||
modelConfig: {
|
||||
...createForm.modelConfig,
|
||||
apiKey: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
placeholder="sk-***"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<Form.Item>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 12 }}>
|
||||
<Button onClick={() => navigate("/data/evaluation")}>取消</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleCreateTask}
|
||||
disabled={
|
||||
!createForm.name ||
|
||||
!createForm.datasetId ||
|
||||
allDimensions.filter((d) => d.isEnabled).length === 0 ||
|
||||
(createForm.evaluationType === "model" &&
|
||||
(!createForm.modelConfig.url ||
|
||||
!createForm.modelConfig.apiKey))
|
||||
}
|
||||
>
|
||||
创建评估任务
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EvaluationTaskCreate;
|
||||
407
frontend/src/pages/DataEvaluation/Evaluate/ManualEvaluate.tsx
Normal file
407
frontend/src/pages/DataEvaluation/Evaluate/ManualEvaluate.tsx
Normal file
@@ -0,0 +1,407 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Button, Card, Badge, Input, Typography, Breadcrumb } from "antd";
|
||||
import {
|
||||
LeftOutlined,
|
||||
RightOutlined,
|
||||
SaveOutlined,
|
||||
ScissorOutlined,
|
||||
AimOutlined,
|
||||
CalendarOutlined,
|
||||
FileTextOutlined,
|
||||
StarFilled,
|
||||
DatabaseOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { mockTasks, presetEvaluationDimensions } from "@/mock/evaluation";
|
||||
import { useNavigate } from "react-router";
|
||||
import DetailHeader from "@/components/DetailHeader";
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Title } = Typography;
|
||||
|
||||
// 生成切片内容
|
||||
const generateSliceContent = (index: number) => {
|
||||
const contents = [
|
||||
"用户咨询产品退换货政策的相关问题,希望了解具体的退货流程和时间限制。客服详细解释了7天无理由退货政策,包括商品需要保持原包装完整的要求。这个回答涵盖了用户关心的主要问题,提供了明确的时间限制和条件说明。",
|
||||
"客服回复关于质量问题商品的处理方式,说明15天内免费换货服务,并承诺承担相关物流费用。用户对此表示满意,认为这个政策很合理。回答中明确区分了质量问题和非质量问题的不同处理方式。",
|
||||
"用户询问特殊商品的退换货政策,客服解释个人定制商品不支持退货的规定,并建议用户在购买前仔细确认商品信息。这个回答帮助用户理解了特殊商品的限制条件。",
|
||||
"关于退货流程的详细说明,客服介绍了在线申请退货的步骤,包括订单页面操作和快递上门取件服务。整个流程描述清晰,用户可以轻松按照步骤操作。",
|
||||
"用户对物流费用承担问题提出疑问,客服明确说明质量问题导致的退换货由公司承担物流费用,非质量问题由用户承担。这个回答消除了用户的疑虑。",
|
||||
];
|
||||
return contents[index % contents.length];
|
||||
};
|
||||
|
||||
const slices: EvaluationSlice[] = Array.from(
|
||||
{ length: mockTasks[0].sliceConfig?.sampleCount || 50 },
|
||||
(_, index) => ({
|
||||
id: `slice_${index + 1}`,
|
||||
content: generateSliceContent(index),
|
||||
sourceFile: `file_${Math.floor(index / 5) + 1}.txt`,
|
||||
sliceIndex: index % 5,
|
||||
sliceType: ["paragraph", "sentence", "semantic"][index % 3],
|
||||
metadata: {
|
||||
startPosition: index * 200,
|
||||
endPosition: (index + 1) * 200,
|
||||
pageNumber: Math.floor(index / 10) + 1,
|
||||
section: `Section ${Math.floor(index / 5) + 1}`,
|
||||
processingMethod: mockTasks[0].sliceConfig?.method || "语义分割",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const ManualEvaluatePage = () => {
|
||||
const navigate = useNavigate();
|
||||
const taskId = mockTasks[0].id;
|
||||
// 人工评估状态
|
||||
const [currentEvaluationTask, setCurrentEvaluationTask] =
|
||||
useState<EvaluationTask | null>(mockTasks[0]);
|
||||
const [evaluationSlices, setEvaluationSlices] =
|
||||
useState<EvaluationSlice[]>(slices);
|
||||
const [currentSliceIndex, setCurrentSliceIndex] = useState(0);
|
||||
const [sliceScores, setSliceScores] = useState<{
|
||||
[key: string]: { [dimensionId: string]: number };
|
||||
}>({});
|
||||
const [sliceComments, setSliceComments] = useState<{ [key: string]: string }>(
|
||||
{}
|
||||
);
|
||||
|
||||
const currentSlice = evaluationSlices[currentSliceIndex];
|
||||
const currentScores = sliceScores[currentSlice?.id] || {};
|
||||
const progress =
|
||||
evaluationSlices.length > 0
|
||||
? ((currentSliceIndex + 1) / evaluationSlices.length) * 100
|
||||
: 0;
|
||||
|
||||
// 获取任务的所有维度
|
||||
const getTaskAllDimensions = (task: EvaluationTask) => {
|
||||
const presetDimensions = presetEvaluationDimensions.filter((d) =>
|
||||
task.dimensions.includes(d.id)
|
||||
);
|
||||
return [...presetDimensions, ...(task.customDimensions || [])];
|
||||
};
|
||||
|
||||
const allDimensions = getTaskAllDimensions(mockTasks[0]);
|
||||
|
||||
// 更新切片评分
|
||||
const updateSliceScore = (
|
||||
sliceId: string,
|
||||
dimensionId: string,
|
||||
score: number
|
||||
) => {
|
||||
setSliceScores((prev) => ({
|
||||
...prev,
|
||||
[sliceId]: {
|
||||
...prev[sliceId],
|
||||
[dimensionId]: score,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
// 保存当前切片评分并进入下一个
|
||||
const handleSaveAndNext = () => {
|
||||
const currentSlice = evaluationSlices[currentSliceIndex];
|
||||
if (!currentSlice) return;
|
||||
|
||||
// 检查是否所有维度都已评分
|
||||
const allDimensions = getTaskAllDimensions(currentEvaluationTask!);
|
||||
const currentScores = sliceScores[currentSlice.id] || {};
|
||||
const hasAllScores = allDimensions.every(
|
||||
(dim) => currentScores[dim.id] > 0
|
||||
);
|
||||
|
||||
if (!hasAllScores) {
|
||||
window.alert("请为所有维度评分后再保存");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是最后一个切片,完成评估
|
||||
if (currentSliceIndex === evaluationSlices.length - 1) {
|
||||
handleCompleteEvaluation();
|
||||
} else {
|
||||
setCurrentSliceIndex(currentSliceIndex + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 完成评估
|
||||
const handleCompleteEvaluation = () => {
|
||||
navigate(`/data/evaluation/task-report/${mockTasks[0].id}`);
|
||||
};
|
||||
|
||||
// 星星评分组件
|
||||
const StarRating = ({
|
||||
value,
|
||||
onChange,
|
||||
dimension,
|
||||
}: {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
dimension: EvaluationDimension;
|
||||
}) => {
|
||||
return (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontWeight: 500 }}>{dimension.name}</span>
|
||||
<span style={{ fontSize: 13, color: "#888" }}>{value}/5</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: "#888", marginBottom: 4 }}>
|
||||
{dimension.description}
|
||||
</div>
|
||||
<div>
|
||||
{[1, 2, 3, 4, 5].map((star) => (
|
||||
<Button
|
||||
key={star}
|
||||
type="text"
|
||||
icon={
|
||||
<StarFilled
|
||||
style={{
|
||||
color: star <= value ? "#fadb14" : "#d9d9d9",
|
||||
fontSize: 22,
|
||||
transition: "color 0.2s",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onClick={() => onChange(star)}
|
||||
style={{ padding: 0, marginRight: 2 }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 头部统计信息
|
||||
const statistics = [
|
||||
{
|
||||
icon: <DatabaseOutlined className="text-gray-500" />,
|
||||
label: "数据集",
|
||||
value: currentEvaluationTask?.datasetName || "",
|
||||
},
|
||||
{
|
||||
icon: <ScissorOutlined className="text-gray-500" />,
|
||||
label: "切片方法",
|
||||
value: currentEvaluationTask?.sliceConfig?.method || "",
|
||||
},
|
||||
{
|
||||
icon: <AimOutlined className="text-gray-500" />,
|
||||
label: "样本数量",
|
||||
value: evaluationSlices.length,
|
||||
},
|
||||
{
|
||||
icon: <CalendarOutlined className="text-gray-500" />,
|
||||
label: "创建时间",
|
||||
value: currentEvaluationTask?.createdAt || "",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{
|
||||
title: (
|
||||
<span onClick={() => navigate("/data/evaluation")}>数据评估</span>
|
||||
),
|
||||
},
|
||||
{ title: "人工评估", key: "manual-evaluate" },
|
||||
]}
|
||||
/>
|
||||
{/* 头部信息 */}
|
||||
<DetailHeader
|
||||
data={{
|
||||
name: currentEvaluationTask?.name || "",
|
||||
description: "人工评估任务",
|
||||
icon: <FileTextOutlined />,
|
||||
createdAt: currentEvaluationTask?.createdAt,
|
||||
lastUpdated: currentEvaluationTask?.createdAt,
|
||||
}}
|
||||
statistics={statistics}
|
||||
operations={[]}
|
||||
/>
|
||||
{/* 进度条 */}
|
||||
<div className="flex justify-between items-center mt-4 mb-6">
|
||||
<div className="text-xs text-gray-500">
|
||||
当前进度: {currentSliceIndex + 1} / {evaluationSlices.length}
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-xs text-gray-500">
|
||||
{Math.round(progress)}% 完成
|
||||
</span>
|
||||
<div className="w-48 bg-gray-200 rounded h-2">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded transition-all"
|
||||
style={{ width: `${progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-2xl font-bold text-blue-600">
|
||||
{Math.round(progress)}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{/* 左侧:切片内容 */}
|
||||
<Card>
|
||||
<div className="border-b border-gray-100 pb-4 mb-4 flex justify-between items-center">
|
||||
<span className="text-base font-semibold flex items-center gap-2">
|
||||
<FileTextOutlined />
|
||||
切片内容
|
||||
</span>
|
||||
<Badge
|
||||
count={`切片 ${currentSliceIndex + 1}`}
|
||||
style={{ background: "#fafafa", color: "#333" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{currentSlice && (
|
||||
<>
|
||||
{/* 切片元信息 */}
|
||||
<div className="bg-gray-50 rounded p-4 text-sm">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<span className="text-gray-500">来源文件:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{currentSlice.sourceFile}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">处理方法:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{currentSlice.metadata.processingMethod}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">位置:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{currentSlice.metadata.startPosition}-
|
||||
{currentSlice.metadata.endPosition}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">章节:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{currentSlice.metadata.section}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 切片内容 */}
|
||||
<div className="border border-gray-100 rounded p-4 min-h-[180px]">
|
||||
<div className="text-xs text-gray-500 mb-2">内容预览</div>
|
||||
<div className="text-gray-900 leading-relaxed">
|
||||
{currentSlice.content}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 导航按钮 */}
|
||||
<div className="flex items-center justify-between border-t border-gray-100 pt-4 mt-2">
|
||||
<Button
|
||||
type="default"
|
||||
icon={<LeftOutlined />}
|
||||
onClick={() =>
|
||||
setCurrentSliceIndex(Math.max(0, currentSliceIndex - 1))
|
||||
}
|
||||
disabled={currentSliceIndex === 0}
|
||||
>
|
||||
上一个
|
||||
</Button>
|
||||
<span className="text-xs text-gray-500">
|
||||
{currentSliceIndex + 1} / {evaluationSlices.length}
|
||||
</span>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<RightOutlined />}
|
||||
onClick={() =>
|
||||
setCurrentSliceIndex(
|
||||
Math.min(
|
||||
evaluationSlices.length - 1,
|
||||
currentSliceIndex + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
disabled={currentSliceIndex === evaluationSlices.length - 1}
|
||||
>
|
||||
下一个
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 右侧:评估维度 */}
|
||||
<Card>
|
||||
<div className="border-b border-gray-100 pb-4 mb-4">
|
||||
<span className="text-base font-semibold flex items-center gap-2">
|
||||
<StarFilled className="text-yellow-400" />
|
||||
评估维度
|
||||
</span>
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
请为每个维度进行1-5星评分
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{allDimensions.map((dimension) => (
|
||||
<div
|
||||
key={dimension.id}
|
||||
className="border border-gray-100 rounded p-4"
|
||||
>
|
||||
<StarRating
|
||||
value={currentScores[dimension.id] || 0}
|
||||
onChange={(score) =>
|
||||
updateSliceScore(
|
||||
currentSlice?.id || "",
|
||||
dimension.id,
|
||||
score
|
||||
)
|
||||
}
|
||||
dimension={dimension}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 评论区域 */}
|
||||
<div className="border border-gray-100 rounded p-4">
|
||||
<span className="font-medium mb-2 block">评估备注</span>
|
||||
<TextArea
|
||||
placeholder="请输入对该切片的评估备注和建议..."
|
||||
value={sliceComments[currentSlice?.id || ""] || ""}
|
||||
onChange={(e) =>
|
||||
setSliceComments((prev) => ({
|
||||
...prev,
|
||||
[currentSlice?.id || ""]: e.target.value,
|
||||
}))
|
||||
}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 保存按钮 */}
|
||||
<div className="border-t border-gray-100 pt-4">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={handleSaveAndNext}
|
||||
block
|
||||
size="large"
|
||||
>
|
||||
{currentSliceIndex === evaluationSlices.length - 1
|
||||
? "完成评估"
|
||||
: "保存并下一个"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManualEvaluatePage;
|
||||
484
frontend/src/pages/DataEvaluation/Home/DataEvaluation.tsx
Normal file
484
frontend/src/pages/DataEvaluation/Home/DataEvaluation.tsx
Normal file
@@ -0,0 +1,484 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Button, Card, Badge, Progress, Table } from "antd";
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
EyeOutlined,
|
||||
ClockCircleOutlined,
|
||||
DatabaseOutlined,
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
ReloadOutlined,
|
||||
UserOutlined,
|
||||
RobotOutlined,
|
||||
EditOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { SearchControls } from "@/components/SearchControls";
|
||||
import { mockTasks } from "@/mock/evaluation";
|
||||
import CardView from "@/components/CardView";
|
||||
import { useNavigate } from "react-router";
|
||||
import type { Dataset } from "@/pages/DataManagement/dataset.model";
|
||||
import DevelopmentInProgress from "@/components/DevelopmentInProgress";
|
||||
|
||||
export default function DataEvaluationPage() {
|
||||
const navigate = useNavigate();
|
||||
const [tasks, setTasks] = useState<EvaluationTask[]>(mockTasks);
|
||||
const [datasets, setDatasets] = useState<Dataset[]>([]);
|
||||
|
||||
// 搜索和过滤状态
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [selectedFilters, setSelectedFilters] = useState<
|
||||
Record<string, string[]>
|
||||
>({});
|
||||
const [sortBy, setSortBy] = useState("createdAt");
|
||||
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
|
||||
const [viewMode, setViewMode] = useState<"card" | "table">("card");
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem("evaluation-tasks", JSON.stringify(tasks));
|
||||
}
|
||||
}, [tasks]);
|
||||
|
||||
// 搜索和过滤配置
|
||||
const filterOptions = [
|
||||
{
|
||||
key: "evaluationType",
|
||||
label: "评估方式",
|
||||
options: [
|
||||
{ label: "模型评估", value: "model" },
|
||||
{ label: "人工评估", value: "manual" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
options: [
|
||||
{ label: "待处理", value: "pending" },
|
||||
{ label: "运行中", value: "running" },
|
||||
{ label: "已完成", value: "completed" },
|
||||
{ label: "失败", value: "failed" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "dataset",
|
||||
label: "数据集",
|
||||
options: datasets.map((d) => ({ label: d.name, value: d.id })),
|
||||
},
|
||||
];
|
||||
|
||||
const sortOptions = [
|
||||
{ label: "创建时间", value: "createdAt" },
|
||||
{ label: "任务名称", value: "name" },
|
||||
{ label: "完成时间", value: "completedAt" },
|
||||
{ label: "评分", value: "score" },
|
||||
];
|
||||
|
||||
// 过滤和排序逻辑
|
||||
const filteredTasks = tasks.filter((task) => {
|
||||
// 搜索过滤
|
||||
if (
|
||||
searchTerm &&
|
||||
!task.name.toLowerCase().includes(searchTerm.toLowerCase()) &&
|
||||
!task.datasetName.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 评估方式过滤
|
||||
if (
|
||||
selectedFilters.evaluationType?.length &&
|
||||
!selectedFilters.evaluationType.includes(task.evaluationType)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 状态过滤
|
||||
if (
|
||||
selectedFilters.status?.length &&
|
||||
!selectedFilters.status.includes(task.status)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 数据集过滤
|
||||
if (
|
||||
selectedFilters.dataset?.length &&
|
||||
!selectedFilters.dataset.includes(task.datasetId)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// 排序
|
||||
const sortedTasks = [...filteredTasks].sort((a, b) => {
|
||||
let aValue: any = a[sortBy as keyof EvaluationTask];
|
||||
let bValue: any = b[sortBy as keyof EvaluationTask];
|
||||
|
||||
if (sortBy === "score") {
|
||||
aValue = a.score || 0;
|
||||
bValue = b.score || 0;
|
||||
}
|
||||
|
||||
if (typeof aValue === "string") {
|
||||
aValue = aValue.toLowerCase();
|
||||
bValue = bValue.toLowerCase();
|
||||
}
|
||||
|
||||
if (sortOrder === "asc") {
|
||||
return aValue > bValue ? 1 : -1;
|
||||
} else {
|
||||
return aValue < bValue ? 1 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
return "green";
|
||||
case "running":
|
||||
return "blue";
|
||||
case "failed":
|
||||
return "red";
|
||||
case "pending":
|
||||
return "gold";
|
||||
default:
|
||||
return "gray";
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
return <CheckCircleOutlined />;
|
||||
case "running":
|
||||
return <ReloadOutlined spin />;
|
||||
case "failed":
|
||||
return <CloseCircleOutlined />;
|
||||
case "pending":
|
||||
return <ClockCircleOutlined />;
|
||||
default:
|
||||
return <ExclamationCircleOutlined />;
|
||||
}
|
||||
};
|
||||
|
||||
// 开始人工评估
|
||||
const handleStartManualEvaluation = (task: EvaluationTask) => {
|
||||
navigate(`/data/evaluation/manual-evaluate/${task.id}`);
|
||||
};
|
||||
|
||||
// 查看评估报告
|
||||
const handleViewReport = (task: EvaluationTask) => {
|
||||
navigate(`/data/evaluation/task-report/${task.id}`);
|
||||
};
|
||||
|
||||
// 删除任务
|
||||
const handleDeleteTask = (taskId: string) => {
|
||||
setTasks(tasks.filter((task) => task.id !== taskId));
|
||||
};
|
||||
|
||||
return (
|
||||
<DevelopmentInProgress />
|
||||
);
|
||||
// 主列表界面
|
||||
return (
|
||||
<div>
|
||||
{/* 页面头部 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-xl font-bold">数据评估</h1>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => navigate("/data/evaluation/create-task")}
|
||||
>
|
||||
创建评估任务
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 搜索和过滤控件 */}
|
||||
<SearchControls
|
||||
searchTerm={searchTerm}
|
||||
onSearchChange={setSearchTerm}
|
||||
searchPlaceholder="搜索任务名称或数据集..."
|
||||
filters={filterOptions}
|
||||
selectedFilters={selectedFilters}
|
||||
onFiltersChange={setSelectedFilters}
|
||||
viewMode={viewMode}
|
||||
onViewModeChange={setViewMode}
|
||||
/>
|
||||
|
||||
{/* 任务列表 */}
|
||||
{viewMode === "card" ? (
|
||||
<CardView
|
||||
data={sortedTasks.map((task) => ({
|
||||
id: task.id,
|
||||
name: task.name,
|
||||
type: task.evaluationType,
|
||||
icon:
|
||||
task.evaluationType === "model" ? (
|
||||
<RobotOutlined style={{ fontSize: 24, color: "#722ed1" }} />
|
||||
) : (
|
||||
<UserOutlined style={{ fontSize: 24, color: "#52c41a" }} />
|
||||
),
|
||||
iconColor: "",
|
||||
status: {
|
||||
label:
|
||||
task.status === "completed"
|
||||
? "已完成"
|
||||
: task.status === "running"
|
||||
? "运行中"
|
||||
: task.status === "failed"
|
||||
? "失败"
|
||||
: "待处理",
|
||||
icon: getStatusIcon(task.status),
|
||||
color: getStatusColor(task.status),
|
||||
},
|
||||
description: task.description,
|
||||
tags: [task.datasetName],
|
||||
statistics: [
|
||||
{
|
||||
label: "进度",
|
||||
value: task.progress !== undefined ? `${task.progress}%` : "-",
|
||||
},
|
||||
{ label: "评分", value: task.score ? `${task.score}分` : "-" },
|
||||
],
|
||||
lastModified: task.createdAt,
|
||||
}))}
|
||||
operations={[
|
||||
{
|
||||
key: "view",
|
||||
label: "查看报告",
|
||||
icon: <EyeOutlined />,
|
||||
onClick: (item) => {
|
||||
const task = tasks.find((t) => t.id === item.id);
|
||||
if (task) handleViewReport(task);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "evaluate",
|
||||
label: "开始评估",
|
||||
icon: <EditOutlined />,
|
||||
onClick: (item) => {
|
||||
const task = tasks.find((t) => t.id === item.id);
|
||||
if (task) handleStartManualEvaluation(task);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
label: "删除",
|
||||
icon: <DeleteOutlined />,
|
||||
onClick: (item) => handleDeleteTask(item.id as string),
|
||||
},
|
||||
]}
|
||||
onView={(item) => {
|
||||
const task = tasks.find((t) => t.id === item.id);
|
||||
if (task) handleViewReport(task);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Card>
|
||||
<Table
|
||||
rowKey="id"
|
||||
dataSource={sortedTasks}
|
||||
pagination={false}
|
||||
scroll={{ x: "max-content" }}
|
||||
columns={[
|
||||
{
|
||||
title: "任务名称",
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<div style={{ fontWeight: 500 }}>{text}</div>
|
||||
<div style={{ fontSize: 13, color: "#888" }}>
|
||||
{record.description}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "数据集",
|
||||
dataIndex: "datasetName",
|
||||
key: "datasetName",
|
||||
render: (text) => (
|
||||
<div
|
||||
style={{ display: "flex", alignItems: "center", gap: 4 }}
|
||||
>
|
||||
<DatabaseOutlined />
|
||||
<span style={{ fontSize: 13 }}>{text}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "评估方式",
|
||||
dataIndex: "evaluationType",
|
||||
key: "evaluationType",
|
||||
render: (type) => (
|
||||
<div
|
||||
style={{ display: "flex", alignItems: "center", gap: 4 }}
|
||||
>
|
||||
{type === "model" ? (
|
||||
<RobotOutlined style={{ color: "#722ed1" }} />
|
||||
) : (
|
||||
<UserOutlined style={{ color: "#52c41a" }} />
|
||||
)}
|
||||
<span style={{ fontSize: 13 }}>
|
||||
{type === "model" ? "模型评估" : "人工评估"}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
render: (status) => (
|
||||
<Badge
|
||||
color={getStatusColor(status)}
|
||||
style={{ background: "none", padding: 0 }}
|
||||
count={
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
{getStatusIcon(status)}
|
||||
<span>
|
||||
{status === "completed" && "已完成"}
|
||||
{status === "running" && "运行中"}
|
||||
{status === "failed" && "失败"}
|
||||
{status === "pending" && "待处理"}
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
showZero={false}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "进度",
|
||||
dataIndex: "progress",
|
||||
key: "progress",
|
||||
render: (progress) =>
|
||||
progress !== undefined ? (
|
||||
<div style={{ width: 100 }}>
|
||||
<Progress
|
||||
percent={progress}
|
||||
size="small"
|
||||
showInfo={false}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: "#888",
|
||||
textAlign: "right",
|
||||
}}
|
||||
>
|
||||
{progress}%
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<span style={{ fontSize: 13, color: "#bbb" }}>-</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "评分",
|
||||
dataIndex: "score",
|
||||
key: "score",
|
||||
render: (score) =>
|
||||
score ? (
|
||||
<span style={{ fontWeight: 500, color: "#389e0d" }}>
|
||||
{score}分
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ fontSize: 13, color: "#bbb" }}>-</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "创建时间",
|
||||
dataIndex: "createdAt",
|
||||
key: "createdAt",
|
||||
render: (text) => <span style={{ fontSize: 13 }}>{text}</span>,
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
render: (_, task) => (
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
{task.status === "completed" && (
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => handleViewReport(task)}
|
||||
>
|
||||
报告
|
||||
</Button>
|
||||
)}
|
||||
{task.evaluationType === "manual" &&
|
||||
task.status === "pending" && (
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => handleStartManualEvaluation(task)}
|
||||
>
|
||||
评估
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => handleDeleteTask(task.id)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
locale={{
|
||||
emptyText: (
|
||||
<div
|
||||
style={{ textAlign: "center", padding: 48, color: "#bbb" }}
|
||||
>
|
||||
<DatabaseOutlined style={{ fontSize: 48, marginBottom: 8 }} />
|
||||
<div style={{ marginTop: 8 }}>暂无评估任务</div>
|
||||
<div style={{ fontSize: 13, color: "#ccc" }}>
|
||||
点击"创建评估任务"开始评估数据集质量
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{sortedTasks.length === 0 && (
|
||||
<div style={{ textAlign: "center", padding: "48px 0" }}>
|
||||
<DatabaseOutlined
|
||||
style={{ fontSize: 64, color: "#bbb", marginBottom: 16 }}
|
||||
/>
|
||||
<div style={{ fontSize: 18, fontWeight: 500, marginBottom: 8 }}>
|
||||
暂无评估任务
|
||||
</div>
|
||||
<div style={{ color: "#888", marginBottom: 24 }}>
|
||||
创建您的第一个数据评估任务
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => navigate("/data/evaluation/create-task")}
|
||||
>
|
||||
创建评估任务
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
310
frontend/src/pages/DataEvaluation/Report/EvaluationReport.tsx
Normal file
310
frontend/src/pages/DataEvaluation/Report/EvaluationReport.tsx
Normal file
@@ -0,0 +1,310 @@
|
||||
import { Button, Card, Badge, Breadcrumb } from "antd";
|
||||
import {
|
||||
ArrowLeft,
|
||||
Download,
|
||||
Users,
|
||||
Scissors,
|
||||
BarChart3,
|
||||
Target,
|
||||
Calendar,
|
||||
TrendingUp,
|
||||
MessageSquare,
|
||||
Star,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
mockQAPairs,
|
||||
mockTasks,
|
||||
presetEvaluationDimensions,
|
||||
} from "@/mock/evaluation";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
|
||||
const EvaluationTaskReport = () => {
|
||||
const navigate = useNavigate();
|
||||
const selectedTask = mockTasks[0]; // 假设我们只展示第一个任务的报告
|
||||
|
||||
// 获取任务的所有维度
|
||||
const getTaskAllDimensions = (task: EvaluationTask) => {
|
||||
const presetDimensions = presetEvaluationDimensions.filter((d) =>
|
||||
task.dimensions.includes(d.id)
|
||||
);
|
||||
return [...presetDimensions, ...(task.customDimensions || [])];
|
||||
};
|
||||
const allDimensions = getTaskAllDimensions(selectedTask);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="mx-auto space-y-2">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{
|
||||
title: <Link to="/data/evaluation">数据评估</Link>,
|
||||
},
|
||||
{ title: "评估报告", key: "report" },
|
||||
]}
|
||||
></Breadcrumb>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
className="flex items-center gap-2"
|
||||
icon={<Download className="w-4 h-4" />}
|
||||
>
|
||||
导出报告
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 基本信息卡片 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<Card>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-blue-100 rounded-lg">
|
||||
<BarChart3 className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
{selectedTask.score || 0}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">总体评分</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-green-100 rounded-lg">
|
||||
<Target className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
{selectedTask.sliceConfig?.sampleCount}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">评估样本数</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-purple-100 rounded-lg">
|
||||
<Users className="w-5 h-5 text-purple-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
{selectedTask.evaluationType === "manual" ? "人工" : "模型"}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">评估方式</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-orange-100 rounded-lg">
|
||||
<Calendar className="w-5 h-5 text-orange-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
{selectedTask.progress}%
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">完成进度</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 详细信息 */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* 评估结果 */}
|
||||
<Card
|
||||
title={
|
||||
<span className="flex items-center gap-2">
|
||||
<TrendingUp className="w-5 h-5" />
|
||||
评估结果
|
||||
</span>
|
||||
}
|
||||
bodyStyle={{ paddingTop: 0 }}
|
||||
>
|
||||
{/* 维度评分 */}
|
||||
<div className="mt-4">
|
||||
<h4 className="font-medium mb-3">维度评分</h4>
|
||||
<div className="space-y-3">
|
||||
{allDimensions.map((dimension) => {
|
||||
const score = 75 + Math.floor(Math.random() * 20); // 模拟评分
|
||||
return (
|
||||
<div
|
||||
key={dimension.id}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-sm font-medium">
|
||||
{dimension.name}
|
||||
</span>
|
||||
<span className="text-sm font-bold text-blue-600">
|
||||
{score}分
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-blue-500 h-2 rounded-full"
|
||||
style={{ width: `${score}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 质量分数解读 */}
|
||||
<div className="border-t pt-4 mt-4">
|
||||
<h4 className="font-medium mb-3">质量分数解读</h4>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full" />
|
||||
<span>90-100分: 优秀,质量很高</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 bg-blue-500 rounded-full" />
|
||||
<span>80-89分: 良好,质量较好</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 bg-yellow-500 rounded-full" />
|
||||
<span>70-79分: 一般,需要改进</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 bg-red-500 rounded-full" />
|
||||
<span>60-69分: 较差,需要重点关注</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 切片信息 */}
|
||||
<Card
|
||||
title={
|
||||
<span className="flex items-center gap-2">
|
||||
<Scissors className="w-5 h-5" />
|
||||
切片信息
|
||||
</span>
|
||||
}
|
||||
bodyStyle={{ paddingTop: 0 }}
|
||||
>
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-500">切片阈值:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{selectedTask.sliceConfig?.threshold}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">抽样数量:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{selectedTask.sliceConfig?.sampleCount}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">切片方法:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{selectedTask.sliceConfig?.method}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">评估时间:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{selectedTask.completedAt || selectedTask.createdAt}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t pt-4">
|
||||
<h4 className="font-medium mb-3">评估维度</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{allDimensions.map((dimension) => (
|
||||
<Badge
|
||||
key={dimension.id}
|
||||
style={{
|
||||
border: "1px solid #d9d9d9",
|
||||
background: "#fafafa",
|
||||
padding: "0 8px",
|
||||
}}
|
||||
>
|
||||
{dimension.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* QA对详情 */}
|
||||
<Card
|
||||
title={
|
||||
<span className="flex items-center gap-2">
|
||||
<MessageSquare className="w-5 h-5" />
|
||||
QA对详情
|
||||
</span>
|
||||
}
|
||||
bodyStyle={{ paddingTop: 0 }}
|
||||
>
|
||||
<div className="space-y-4 mt-4">
|
||||
{mockQAPairs.map((qa) => (
|
||||
<div key={qa.id} className="border rounded-lg p-4">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-700 mb-1">
|
||||
问题:
|
||||
</span>
|
||||
<span className="text-gray-900">{qa.question}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-700 mb-1">
|
||||
回答:
|
||||
</span>
|
||||
<span className="text-gray-900">{qa.answer}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pt-2 border-t">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-500">评分:</span>
|
||||
<div className="flex items-center gap-1">
|
||||
{[1, 2, 3, 4, 5].map((star) => (
|
||||
<Star
|
||||
key={star}
|
||||
className={`w-4 h-4 ${
|
||||
star <= qa.score
|
||||
? "text-yellow-400"
|
||||
: "text-gray-300"
|
||||
}`}
|
||||
style={star <= qa.score ? { fill: "#facc15" } : {}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<span className="text-sm font-medium">{qa.score}/5</span>
|
||||
</div>
|
||||
{qa.feedback && (
|
||||
<div className="text-sm text-gray-600">{qa.feedback}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EvaluationTaskReport;
|
||||
243
frontend/src/pages/DataEvaluation/data-evaluation.api.ts
Normal file
243
frontend/src/pages/DataEvaluation/data-evaluation.api.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import { get, post, put, del, download } from "@/utils/request";
|
||||
|
||||
// 数据质量评估相关接口
|
||||
export function evaluateDataQualityUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/quality", data);
|
||||
}
|
||||
|
||||
export function getQualityEvaluationByIdUsingGet(evaluationId: string | number) {
|
||||
return get(`/api/v1/evaluation/quality/${evaluationId}`);
|
||||
}
|
||||
|
||||
// 适配性评估相关接口
|
||||
export function evaluateCompatibilityUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/compatibility", data);
|
||||
}
|
||||
|
||||
// 价值评估相关接口
|
||||
export function evaluateValueUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/value", data);
|
||||
}
|
||||
|
||||
// 评估报告管理接口
|
||||
export function queryEvaluationReportsUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/reports", params);
|
||||
}
|
||||
|
||||
export function getEvaluationReportByIdUsingGet(reportId: string | number) {
|
||||
return get(`/api/v1/evaluation/reports/${reportId}`);
|
||||
}
|
||||
|
||||
export function exportEvaluationReportUsingGet(reportId: string | number, format = "PDF", filename?: string) {
|
||||
return download(`/api/v1/evaluation/reports/${reportId}/export`, { format }, filename);
|
||||
}
|
||||
|
||||
// 批量评估接口
|
||||
export function batchEvaluationUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/batch", data);
|
||||
}
|
||||
|
||||
// 扩展功能接口(基于常见需求添加)
|
||||
|
||||
// 评估模板管理
|
||||
export function queryEvaluationTemplatesUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/templates", params);
|
||||
}
|
||||
|
||||
export function createEvaluationTemplateUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/templates", data);
|
||||
}
|
||||
|
||||
export function getEvaluationTemplateByIdUsingGet(templateId: string | number) {
|
||||
return get(`/api/v1/evaluation/templates/${templateId}`);
|
||||
}
|
||||
|
||||
export function updateEvaluationTemplateByIdUsingPut(templateId: string | number, data: any) {
|
||||
return put(`/api/v1/evaluation/templates/${templateId}`, data);
|
||||
}
|
||||
|
||||
export function deleteEvaluationTemplateByIdUsingDelete(templateId: string | number) {
|
||||
return del(`/api/v1/evaluation/templates/${templateId}`);
|
||||
}
|
||||
|
||||
// 评估历史记录
|
||||
export function queryEvaluationHistoryUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/history", params);
|
||||
}
|
||||
|
||||
export function getEvaluationHistoryByDatasetUsingGet(datasetId: string | number, params?: any) {
|
||||
return get(`/api/v1/evaluation/history/dataset/${datasetId}`, params);
|
||||
}
|
||||
|
||||
// 评估指标配置
|
||||
export function queryQualityMetricsUsingGet() {
|
||||
return get("/api/v1/evaluation/metrics/quality");
|
||||
}
|
||||
|
||||
export function queryCompatibilityMetricsUsingGet() {
|
||||
return get("/api/v1/evaluation/metrics/compatibility");
|
||||
}
|
||||
|
||||
export function queryValueMetricsUsingGet() {
|
||||
return get("/api/v1/evaluation/metrics/value");
|
||||
}
|
||||
|
||||
// 评估规则管理
|
||||
export function queryEvaluationRulesUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/rules", params);
|
||||
}
|
||||
|
||||
export function createEvaluationRuleUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/rules", data);
|
||||
}
|
||||
|
||||
export function updateEvaluationRuleByIdUsingPut(ruleId: string | number, data: any) {
|
||||
return put(`/api/v1/evaluation/rules/${ruleId}`, data);
|
||||
}
|
||||
|
||||
export function deleteEvaluationRuleByIdUsingDelete(ruleId: string | number) {
|
||||
return del(`/api/v1/evaluation/rules/${ruleId}`);
|
||||
}
|
||||
|
||||
// 评估统计信息
|
||||
export function getEvaluationStatisticsUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/statistics", params);
|
||||
}
|
||||
|
||||
export function getDatasetEvaluationSummaryUsingGet(datasetId: string | number) {
|
||||
return get(`/api/v1/evaluation/datasets/${datasetId}/summary`);
|
||||
}
|
||||
|
||||
// 评估任务管理
|
||||
export function queryEvaluationTasksUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/tasks", params);
|
||||
}
|
||||
|
||||
export function createEvaluationTaskUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/tasks", data);
|
||||
}
|
||||
|
||||
export function getEvaluationTaskByIdUsingGet(taskId: string | number) {
|
||||
return get(`/api/v1/evaluation/tasks/${taskId}`);
|
||||
}
|
||||
|
||||
export function cancelEvaluationTaskUsingPost(taskId: string | number) {
|
||||
return post(`/api/v1/evaluation/tasks/${taskId}/cancel`);
|
||||
}
|
||||
|
||||
export function retryEvaluationTaskUsingPost(taskId: string | number) {
|
||||
return post(`/api/v1/evaluation/tasks/${taskId}/retry`);
|
||||
}
|
||||
|
||||
// 评估结果比较
|
||||
export function compareEvaluationResultsUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/compare", data);
|
||||
}
|
||||
|
||||
// 评估配置管理
|
||||
export function getEvaluationConfigUsingGet() {
|
||||
return get("/api/v1/evaluation/config");
|
||||
}
|
||||
|
||||
export function updateEvaluationConfigUsingPut(data: any) {
|
||||
return put("/api/v1/evaluation/config", data);
|
||||
}
|
||||
|
||||
// 数据质量监控
|
||||
export function createQualityMonitorUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/quality/monitors", data);
|
||||
}
|
||||
|
||||
export function queryQualityMonitorsUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/quality/monitors", params);
|
||||
}
|
||||
|
||||
export function updateQualityMonitorByIdUsingPut(monitorId: string | number, data: any) {
|
||||
return put(`/api/v1/evaluation/quality/monitors/${monitorId}`, data);
|
||||
}
|
||||
|
||||
export function deleteQualityMonitorByIdUsingDelete(monitorId: string | number) {
|
||||
return del(`/api/v1/evaluation/quality/monitors/${monitorId}`);
|
||||
}
|
||||
|
||||
// 评估基准管理
|
||||
export function queryEvaluationBenchmarksUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/benchmarks", params);
|
||||
}
|
||||
|
||||
export function createEvaluationBenchmarkUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/benchmarks", data);
|
||||
}
|
||||
|
||||
export function updateEvaluationBenchmarkByIdUsingPut(benchmarkId: string | number, data: any) {
|
||||
return put(`/api/v1/evaluation/benchmarks/${benchmarkId}`, data);
|
||||
}
|
||||
|
||||
export function deleteEvaluationBenchmarkByIdUsingDelete(benchmarkId: string | number) {
|
||||
return del(`/api/v1/evaluation/benchmarks/${benchmarkId}`);
|
||||
}
|
||||
|
||||
// 评估算法管理
|
||||
export function queryEvaluationAlgorithmsUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/algorithms", params);
|
||||
}
|
||||
|
||||
export function runCustomEvaluationUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/custom", data);
|
||||
}
|
||||
|
||||
// 评估可视化数据
|
||||
export function getEvaluationVisualizationUsingGet(evaluationId: string | number, chartType?: string) {
|
||||
return get(`/api/v1/evaluation/${evaluationId}/visualization`, { chartType });
|
||||
}
|
||||
|
||||
// 评估通知和警报
|
||||
export function queryEvaluationAlertsUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/alerts", params);
|
||||
}
|
||||
|
||||
export function createEvaluationAlertUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/alerts", data);
|
||||
}
|
||||
|
||||
export function updateEvaluationAlertByIdUsingPut(alertId: string | number, data: any) {
|
||||
return put(`/api/v1/evaluation/alerts/${alertId}`, data);
|
||||
}
|
||||
|
||||
export function deleteEvaluationAlertByIdUsingDelete(alertId: string | number) {
|
||||
return del(`/api/v1/evaluation/alerts/${alertId}`);
|
||||
}
|
||||
|
||||
// 批量操作扩展
|
||||
export function batchDeleteEvaluationReportsUsingPost(data: { reportIds: string[] }) {
|
||||
return post("/api/v1/evaluation/reports/batch-delete", data);
|
||||
}
|
||||
|
||||
export function batchExportEvaluationReportsUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/reports/batch-export", data);
|
||||
}
|
||||
|
||||
// 评估调度管理
|
||||
export function queryEvaluationSchedulesUsingGet(params?: any) {
|
||||
return get("/api/v1/evaluation/schedules", params);
|
||||
}
|
||||
|
||||
export function createEvaluationScheduleUsingPost(data: any) {
|
||||
return post("/api/v1/evaluation/schedules", data);
|
||||
}
|
||||
|
||||
export function updateEvaluationScheduleByIdUsingPut(scheduleId: string | number, data: any) {
|
||||
return put(`/api/v1/evaluation/schedules/${scheduleId}`, data);
|
||||
}
|
||||
|
||||
export function deleteEvaluationScheduleByIdUsingDelete(scheduleId: string | number) {
|
||||
return del(`/api/v1/evaluation/schedules/${scheduleId}`);
|
||||
}
|
||||
|
||||
export function enableEvaluationScheduleUsingPost(scheduleId: string | number) {
|
||||
return post(`/api/v1/evaluation/schedules/${scheduleId}/enable`);
|
||||
}
|
||||
|
||||
export function disableEvaluationScheduleUsingPost(scheduleId: string | number) {
|
||||
return post(`/api/v1/evaluation/schedules/${scheduleId}/disable`);
|
||||
}
|
||||
73
frontend/src/pages/DataEvaluation/data-evaluation.d.ts
vendored
Normal file
73
frontend/src/pages/DataEvaluation/data-evaluation.d.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
interface EvaluationDimension {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: "quality" | "accuracy" | "completeness" | "consistency" | "bias" | "custom"
|
||||
isCustom?: boolean
|
||||
isEnabled?: boolean
|
||||
}
|
||||
|
||||
interface EvaluationTask {
|
||||
id: string
|
||||
name: string
|
||||
datasetId: string
|
||||
datasetName: string
|
||||
evaluationType: "model" | "manual"
|
||||
status: "running" | "completed" | "failed" | "pending"
|
||||
score?: number
|
||||
progress?: number
|
||||
createdAt: string
|
||||
completedAt?: string
|
||||
description: string
|
||||
dimensions: string[]
|
||||
customDimensions: EvaluationDimension[]
|
||||
sliceConfig?: {
|
||||
threshold: number
|
||||
sampleCount: number
|
||||
method: string
|
||||
}
|
||||
modelConfig?: {
|
||||
url: string
|
||||
apiKey: string
|
||||
prompt: string
|
||||
temperature: number
|
||||
maxTokens: number
|
||||
}
|
||||
metrics: {
|
||||
accuracy: number
|
||||
completeness: number
|
||||
consistency: number
|
||||
relevance: number
|
||||
}
|
||||
issues: {
|
||||
type: string
|
||||
count: number
|
||||
severity: "high" | "medium" | "low"
|
||||
}[]
|
||||
}
|
||||
|
||||
interface EvaluationSlice {
|
||||
id: string
|
||||
content: string
|
||||
sourceFile: string
|
||||
sliceIndex: number
|
||||
sliceType: string
|
||||
metadata: {
|
||||
startPosition?: number
|
||||
endPosition?: number
|
||||
pageNumber?: number
|
||||
section?: string
|
||||
processingMethod: string
|
||||
}
|
||||
scores?: { [dimensionId: string]: number }
|
||||
comment?: string
|
||||
}
|
||||
|
||||
interface QAPair {
|
||||
id: string
|
||||
question: string
|
||||
answer: string
|
||||
sliceId: string
|
||||
score: number
|
||||
feedback?: string
|
||||
}
|
||||
Reference in New Issue
Block a user