Skip to content
1 change: 1 addition & 0 deletions frontend/public/config/error-code.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"cleaning.0008": "文件系统错误",
"cleaning.0009": "设置解析错误",
"cleaning.0010": "任务ID不能为空",
"cleaning.0011": "无法删除预制模板",
"operator.0001": "算子不存在",
"operator.0002": "算子被编排于模版中或处在正在进行的任务中,无法删除",
"operator.0003": "无法删除预置算子",
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/hooks/useFetchData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ export default function useFetchData<T>(
};
}, [searchParams, fetchData]);

// 组件挂载时重置 prevSearchParamsRef,解决 StrictMode 双重挂载问题
useEffect(() => {
prevSearchParamsRef.current = "";
}, []);

// 组件卸载时清理轮询和状态
useEffect(() => {
isMountedRef.current = true;
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1371,7 +1371,11 @@
"destDatasetName": "Target Dataset Name",
"destDatasetNamePlaceholder": "Enter or select target dataset name",
"destDatasetType": "Target Dataset Type",
"destDatasetTypeRequired": "Please select target dataset type"
"destDatasetTypeRequired": "Please select target dataset type",
"useSourceDataset": "Use Source Dataset",
"useSourceDatasetHint": "When checked, target dataset will use source dataset and cannot be modified",
"destDatasetNameRequired": "Please enter target dataset name",
"cannotUseSourceDataset": "Cannot use source dataset as target. Check 'Use Source Dataset' or enter a different name"
},
"sections": {
"taskInfo": "Task Info",
Expand Down Expand Up @@ -1446,11 +1450,12 @@
"sizeOptimization": "File Size Optimization",
"reduced": "Reduced by {{percent}}%"
},
"logTable": {
"logTable": {
"selectRun": "Select Run",
"currentDisplay": "Current Display: {{num}}th Run",
"nthRun": "{{num}}th Run",
"noLogs": "No logs available for this task"
"noLogs": "No logs available for this task",
"streaming": "Streaming..."
},
"operatorTable": {
"serialNumber": "Serial Number",
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/i18n/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1371,7 +1371,11 @@
"destDatasetName": "目标数据集名称",
"destDatasetNamePlaceholder": "输入或选择目标数据集名称",
"destDatasetType": "目标数据集类型",
"destDatasetTypeRequired": "请选择目标数据集类型"
"destDatasetTypeRequired": "请选择目标数据集类型",
"useSourceDataset": "选择源数据集",
"useSourceDatasetHint": "勾选后目标数据集将使用源数据集,不可修改",
"destDatasetNameRequired": "请输入目标数据集名称",
"cannotUseSourceDataset": "不能使用源数据集作为目标数据集,请勾选\"选择源数据集\"或输入其他名称"
},
"sections": {
"taskInfo": "任务信息",
Expand Down Expand Up @@ -1450,7 +1454,8 @@
"selectRun": "选择运行轮次",
"currentDisplay": "当前展示: 第 {{num}} 次",
"nthRun": "第 {{num}} 次",
"noLogs": "当前任务无可用日志"
"noLogs": "当前任务无可用日志",
"streaming": "实时流式输出中..."
},
"operatorTable": {
"serialNumber": "序号",
Expand Down
20 changes: 14 additions & 6 deletions frontend/src/pages/DataCleansing/Create/CreateTask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function CleansingTaskCreate() {
destDatasetType: DatasetType.TEXT,
type: DatasetType.TEXT,
});
const [useSourceDataset, setUseSourceDataset] = useState(false);

const {
renderStepTwo,
Expand Down Expand Up @@ -53,13 +54,18 @@ export default function CleansingTaskCreate() {
const canProceed = () => {
switch (currentStep) {
case 1: {
const values = form.getFieldsValue();
return (
values.name &&
values.srcDatasetId &&
values.destDatasetName &&
values.destDatasetType
const hasBasicFields = (
taskConfig.name &&
taskConfig.srcDatasetId &&
taskConfig.destDatasetName &&
taskConfig.destDatasetType
);
if (!hasBasicFields) return false;
if (useSourceDataset) return true;
if (taskConfig.destDatasetName === taskConfig.srcDatasetName) {
return false;
}
return true;
}
case 2:
return selectedOperators.length > 0;
Expand All @@ -76,6 +82,8 @@ export default function CleansingTaskCreate() {
form={form}
taskConfig={taskConfig}
setTaskConfig={setTaskConfig}
useSourceDataset={useSourceDataset}
setUseSourceDataset={setUseSourceDataset}
/>
);
case 2:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import {
DatasetSubType,
DatasetType,
} from "@/pages/DataManagement/dataset.model";
import { Input, Select, Form, AutoComplete } from "antd";
import { Input, Select, Form, AutoComplete, Checkbox, Tooltip } from "antd";
import TextArea from "antd/es/input/TextArea";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Lock } from "lucide-react";

export default function CreateTaskStepOne({
form,
taskConfig,
setTaskConfig,
useSourceDataset,
setUseSourceDataset,
}: {
form: any;
taskConfig: {
Expand All @@ -24,8 +27,12 @@ export default function CreateTaskStepOne({
destDatasetName: string;
type: DatasetType;
destDatasetType: DatasetSubType;
srcDatasetId?: string;
srcDatasetName?: string;
};
setTaskConfig: (config: any) => void;
useSourceDataset: boolean;
setUseSourceDataset: (checked: boolean) => void;
}) {
const { t } = useTranslation();
const [datasets, setDatasets] = useState<Dataset[]>([]);
Expand All @@ -45,10 +52,14 @@ export default function CreateTaskStepOne({
let dataset = null;
if (key === "srcDatasetId") {
dataset = datasets.find((d) => d.id === value);
// 如果勾选了"选择源数据集",自动更新目标数据集名称
const newDestName = useSourceDataset ? (dataset?.name || "") : allValues.destDatasetName;
form.setFieldValue("destDatasetName", newDestName);
setTaskConfig({
...taskConfig,
...allValues,
srcDatasetName: dataset?.name || "",
destDatasetName: newDestName,
});
} else if (key === "destDatasetName") {
dataset = datasets.find((d) => d.name === value);
Expand All @@ -62,6 +73,36 @@ export default function CreateTaskStepOne({
}
};

const handleUseSourceDatasetChange = (checked: boolean) => {
setUseSourceDataset(checked);
if (checked) {
const srcDatasetId = form.getFieldValue("srcDatasetId");
const srcDataset = datasets.find((d) => d.id === srcDatasetId);
const srcName = srcDataset?.name || "";
form.setFieldValue("destDatasetName", srcName);
setTaskConfig({
...taskConfig,
destDatasetId: srcDataset?.id || "",
destDatasetName: srcName,
});
} else {
form.setFieldValue("destDatasetName", "");
setTaskConfig({
...taskConfig,
destDatasetName: "",
});
}
};

// 过滤掉当前选中的源数据集(当不勾选"选择源数据集"时)
const getFilteredDatasetOptions = () => {
const srcDatasetId = form.getFieldValue("srcDatasetId");
if (useSourceDataset || !srcDatasetId) {
return datasets;
}
return datasets.filter((d) => d.id !== srcDatasetId);
};

return (
<Form
layout="vertical"
Expand Down Expand Up @@ -98,9 +139,43 @@ export default function CreateTaskStepOne({
})}
/>
</Form.Item>
<Form.Item label={t("dataCleansing.task.form.destDatasetName")} name="destDatasetName" required>
<div className="flex items-center gap-1 mb-1">
<span className="text-red-500">*</span>
<label className="text-sm text-gray-700 mr-4">{t("dataCleansing.task.form.destDatasetName")}</label>
<Checkbox
checked={useSourceDataset}
onChange={(e) => handleUseSourceDatasetChange(e.target.checked)}
>
<span className="-ml-1">
{t("dataCleansing.task.form.useSourceDataset")}
</span>
</Checkbox>
{useSourceDataset && (
<Tooltip title={t("dataCleansing.task.form.useSourceDatasetHint")}>
<Lock className="w-3.5 h-3.5 text-gray-400 -ml-2.5" />
</Tooltip>
)}
</div>
<Form.Item
name="destDatasetName"
className="mb-0"
rules={[
{ required: true, message: t("dataCleansing.task.form.destDatasetNameRequired") },
{
validator: (_, value) => {
if (useSourceDataset) return Promise.resolve();
const srcDatasetId = form.getFieldValue("srcDatasetId");
const srcDataset = datasets.find((d) => d.id === srcDatasetId);
if (srcDataset && value === srcDataset.name) {
return Promise.reject(new Error(t("dataCleansing.task.form.cannotUseSourceDataset")));
}
return Promise.resolve();
}
}
]}
>
<AutoComplete
options={datasets.map((dataset) => {
options={getFilteredDatasetOptions().map((dataset) => {
return {
label: (
<div className="flex items-center justify-between gap-3 py-2">
Expand All @@ -118,6 +193,7 @@ export default function CreateTaskStepOne({
return option.value.toLowerCase().startsWith(inputValue.toLowerCase());
}}
placeholder={t("dataCleansing.task.form.destDatasetNamePlaceholder")}
disabled={useSourceDataset}
/>
</Form.Item>
<Form.Item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,22 @@ const ParamConfig: React.FC<ParamConfigProps> = ({
</Radio.Group>
</Form.Item>
);
case "checkbox":
case "checkbox": {
const group = Array.isArray(value) ? value: value.split(",").map(item => item.trim()).filter(Boolean);
return (
<Form.Item
label={param.name}
tooltip={param.description}
key={paramKey}
>
<Checkbox.Group
value={value}
value={group}
onChange={updateValue}
options={param.options || []}
/>
</Form.Item>
);
}
case "slider":
return (
<Form.Item
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/pages/DataCleansing/Create/hooks/useDragOperators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ export function useDragOperators({
setOperators([...operators, draggingItem]);
}
}
// 如果是算子编排区域内的重新排序,移动到末尾
else if (draggingSource === "sort") {
const draggedIndex = operators.findIndex(
(item) => item.id === draggingItem.id
);
if (draggedIndex !== -1 && draggedIndex !== operators.length - 1) {
const newItems = [...operators];
const [draggedItem] = newItems.splice(draggedIndex, 1);
newItems.push(draggedItem);
setOperators(newItems);
}
}

resetDragState();
};
Expand Down
Loading
Loading