Skip to content

Commit 18f4615

Browse files
authored
修复数据处理与算子市场问题 (#410)
* 增加源数据集限制 * 修复上一步之后要操作之后才能点下一步 * 修复算子编排拖动效果有问题,往最后一个拖动不行 * 修复多选取消一下多个都被取消了 * 修复算子页面不调接口的问题 * 刷新时会刷新侧边栏 * 禁止删除预制模版 * 数据处理流程展示支持收起 * 日志改为流式获取 * 数据处理和模版支持分页
1 parent 8bd1204 commit 18f4615

21 files changed

Lines changed: 690 additions & 191 deletions

File tree

frontend/public/config/error-code.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"cleaning.0008": "文件系统错误",
1111
"cleaning.0009": "设置解析错误",
1212
"cleaning.0010": "任务ID不能为空",
13+
"cleaning.0011": "无法删除预制模板",
1314
"operator.0001": "算子不存在",
1415
"operator.0002": "算子被编排于模版中或处在正在进行的任务中,无法删除",
1516
"operator.0003": "无法删除预置算子",

frontend/src/hooks/useFetchData.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ export default function useFetchData<T>(
266266
};
267267
}, [searchParams, fetchData]);
268268

269+
// 组件挂载时重置 prevSearchParamsRef,解决 StrictMode 双重挂载问题
270+
useEffect(() => {
271+
prevSearchParamsRef.current = "";
272+
}, []);
273+
269274
// 组件卸载时清理轮询和状态
270275
useEffect(() => {
271276
isMountedRef.current = true;

frontend/src/i18n/locales/en/common.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,7 +1371,11 @@
13711371
"destDatasetName": "Target Dataset Name",
13721372
"destDatasetNamePlaceholder": "Enter or select target dataset name",
13731373
"destDatasetType": "Target Dataset Type",
1374-
"destDatasetTypeRequired": "Please select target dataset type"
1374+
"destDatasetTypeRequired": "Please select target dataset type",
1375+
"useSourceDataset": "Use Source Dataset",
1376+
"useSourceDatasetHint": "When checked, target dataset will use source dataset and cannot be modified",
1377+
"destDatasetNameRequired": "Please enter target dataset name",
1378+
"cannotUseSourceDataset": "Cannot use source dataset as target. Check 'Use Source Dataset' or enter a different name"
13751379
},
13761380
"sections": {
13771381
"taskInfo": "Task Info",
@@ -1446,11 +1450,12 @@
14461450
"sizeOptimization": "File Size Optimization",
14471451
"reduced": "Reduced by {{percent}}%"
14481452
},
1449-
"logTable": {
1453+
"logTable": {
14501454
"selectRun": "Select Run",
14511455
"currentDisplay": "Current Display: {{num}}th Run",
14521456
"nthRun": "{{num}}th Run",
1453-
"noLogs": "No logs available for this task"
1457+
"noLogs": "No logs available for this task",
1458+
"streaming": "Streaming..."
14541459
},
14551460
"operatorTable": {
14561461
"serialNumber": "Serial Number",

frontend/src/i18n/locales/zh/common.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,7 +1371,11 @@
13711371
"destDatasetName": "目标数据集名称",
13721372
"destDatasetNamePlaceholder": "输入或选择目标数据集名称",
13731373
"destDatasetType": "目标数据集类型",
1374-
"destDatasetTypeRequired": "请选择目标数据集类型"
1374+
"destDatasetTypeRequired": "请选择目标数据集类型",
1375+
"useSourceDataset": "选择源数据集",
1376+
"useSourceDatasetHint": "勾选后目标数据集将使用源数据集,不可修改",
1377+
"destDatasetNameRequired": "请输入目标数据集名称",
1378+
"cannotUseSourceDataset": "不能使用源数据集作为目标数据集,请勾选\"选择源数据集\"或输入其他名称"
13751379
},
13761380
"sections": {
13771381
"taskInfo": "任务信息",
@@ -1450,7 +1454,8 @@
14501454
"selectRun": "选择运行轮次",
14511455
"currentDisplay": "当前展示: 第 {{num}} 次",
14521456
"nthRun": "第 {{num}} 次",
1453-
"noLogs": "当前任务无可用日志"
1457+
"noLogs": "当前任务无可用日志",
1458+
"streaming": "实时流式输出中..."
14541459
},
14551460
"operatorTable": {
14561461
"serialNumber": "序号",

frontend/src/pages/DataCleansing/Create/CreateTask.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default function CleansingTaskCreate() {
2222
destDatasetType: DatasetType.TEXT,
2323
type: DatasetType.TEXT,
2424
});
25+
const [useSourceDataset, setUseSourceDataset] = useState(false);
2526

2627
const {
2728
renderStepTwo,
@@ -53,13 +54,18 @@ export default function CleansingTaskCreate() {
5354
const canProceed = () => {
5455
switch (currentStep) {
5556
case 1: {
56-
const values = form.getFieldsValue();
57-
return (
58-
values.name &&
59-
values.srcDatasetId &&
60-
values.destDatasetName &&
61-
values.destDatasetType
57+
const hasBasicFields = (
58+
taskConfig.name &&
59+
taskConfig.srcDatasetId &&
60+
taskConfig.destDatasetName &&
61+
taskConfig.destDatasetType
6262
);
63+
if (!hasBasicFields) return false;
64+
if (useSourceDataset) return true;
65+
if (taskConfig.destDatasetName === taskConfig.srcDatasetName) {
66+
return false;
67+
}
68+
return true;
6369
}
6470
case 2:
6571
return selectedOperators.length > 0;
@@ -76,6 +82,8 @@ export default function CleansingTaskCreate() {
7682
form={form}
7783
taskConfig={taskConfig}
7884
setTaskConfig={setTaskConfig}
85+
useSourceDataset={useSourceDataset}
86+
setUseSourceDataset={setUseSourceDataset}
7987
/>
8088
);
8189
case 2:

frontend/src/pages/DataCleansing/Create/components/CreateTaskStepOne.tsx

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ import {
66
DatasetSubType,
77
DatasetType,
88
} from "@/pages/DataManagement/dataset.model";
9-
import { Input, Select, Form, AutoComplete } from "antd";
9+
import { Input, Select, Form, AutoComplete, Checkbox, Tooltip } from "antd";
1010
import TextArea from "antd/es/input/TextArea";
1111
import { useEffect, useState } from "react";
1212
import { useTranslation } from "react-i18next";
13+
import { Lock } from "lucide-react";
1314

1415
export default function CreateTaskStepOne({
1516
form,
1617
taskConfig,
1718
setTaskConfig,
19+
useSourceDataset,
20+
setUseSourceDataset,
1821
}: {
1922
form: any;
2023
taskConfig: {
@@ -24,8 +27,12 @@ export default function CreateTaskStepOne({
2427
destDatasetName: string;
2528
type: DatasetType;
2629
destDatasetType: DatasetSubType;
30+
srcDatasetId?: string;
31+
srcDatasetName?: string;
2732
};
2833
setTaskConfig: (config: any) => void;
34+
useSourceDataset: boolean;
35+
setUseSourceDataset: (checked: boolean) => void;
2936
}) {
3037
const { t } = useTranslation();
3138
const [datasets, setDatasets] = useState<Dataset[]>([]);
@@ -45,10 +52,14 @@ export default function CreateTaskStepOne({
4552
let dataset = null;
4653
if (key === "srcDatasetId") {
4754
dataset = datasets.find((d) => d.id === value);
55+
// 如果勾选了"选择源数据集",自动更新目标数据集名称
56+
const newDestName = useSourceDataset ? (dataset?.name || "") : allValues.destDatasetName;
57+
form.setFieldValue("destDatasetName", newDestName);
4858
setTaskConfig({
4959
...taskConfig,
5060
...allValues,
5161
srcDatasetName: dataset?.name || "",
62+
destDatasetName: newDestName,
5263
});
5364
} else if (key === "destDatasetName") {
5465
dataset = datasets.find((d) => d.name === value);
@@ -62,6 +73,36 @@ export default function CreateTaskStepOne({
6273
}
6374
};
6475

76+
const handleUseSourceDatasetChange = (checked: boolean) => {
77+
setUseSourceDataset(checked);
78+
if (checked) {
79+
const srcDatasetId = form.getFieldValue("srcDatasetId");
80+
const srcDataset = datasets.find((d) => d.id === srcDatasetId);
81+
const srcName = srcDataset?.name || "";
82+
form.setFieldValue("destDatasetName", srcName);
83+
setTaskConfig({
84+
...taskConfig,
85+
destDatasetId: srcDataset?.id || "",
86+
destDatasetName: srcName,
87+
});
88+
} else {
89+
form.setFieldValue("destDatasetName", "");
90+
setTaskConfig({
91+
...taskConfig,
92+
destDatasetName: "",
93+
});
94+
}
95+
};
96+
97+
// 过滤掉当前选中的源数据集(当不勾选"选择源数据集"时)
98+
const getFilteredDatasetOptions = () => {
99+
const srcDatasetId = form.getFieldValue("srcDatasetId");
100+
if (useSourceDataset || !srcDatasetId) {
101+
return datasets;
102+
}
103+
return datasets.filter((d) => d.id !== srcDatasetId);
104+
};
105+
65106
return (
66107
<Form
67108
layout="vertical"
@@ -98,9 +139,43 @@ export default function CreateTaskStepOne({
98139
})}
99140
/>
100141
</Form.Item>
101-
<Form.Item label={t("dataCleansing.task.form.destDatasetName")} name="destDatasetName" required>
142+
<div className="flex items-center gap-1 mb-1">
143+
<span className="text-red-500">*</span>
144+
<label className="text-sm text-gray-700 mr-4">{t("dataCleansing.task.form.destDatasetName")}</label>
145+
<Checkbox
146+
checked={useSourceDataset}
147+
onChange={(e) => handleUseSourceDatasetChange(e.target.checked)}
148+
>
149+
<span className="-ml-1">
150+
{t("dataCleansing.task.form.useSourceDataset")}
151+
</span>
152+
</Checkbox>
153+
{useSourceDataset && (
154+
<Tooltip title={t("dataCleansing.task.form.useSourceDatasetHint")}>
155+
<Lock className="w-3.5 h-3.5 text-gray-400 -ml-2.5" />
156+
</Tooltip>
157+
)}
158+
</div>
159+
<Form.Item
160+
name="destDatasetName"
161+
className="mb-0"
162+
rules={[
163+
{ required: true, message: t("dataCleansing.task.form.destDatasetNameRequired") },
164+
{
165+
validator: (_, value) => {
166+
if (useSourceDataset) return Promise.resolve();
167+
const srcDatasetId = form.getFieldValue("srcDatasetId");
168+
const srcDataset = datasets.find((d) => d.id === srcDatasetId);
169+
if (srcDataset && value === srcDataset.name) {
170+
return Promise.reject(new Error(t("dataCleansing.task.form.cannotUseSourceDataset")));
171+
}
172+
return Promise.resolve();
173+
}
174+
}
175+
]}
176+
>
102177
<AutoComplete
103-
options={datasets.map((dataset) => {
178+
options={getFilteredDatasetOptions().map((dataset) => {
104179
return {
105180
label: (
106181
<div className="flex items-center justify-between gap-3 py-2">
@@ -118,6 +193,7 @@ export default function CreateTaskStepOne({
118193
return option.value.toLowerCase().startsWith(inputValue.toLowerCase());
119194
}}
120195
placeholder={t("dataCleansing.task.form.destDatasetNamePlaceholder")}
196+
disabled={useSourceDataset}
121197
/>
122198
</Form.Item>
123199
<Form.Item

frontend/src/pages/DataCleansing/Create/components/ParamConfig.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,22 @@ const ParamConfig: React.FC<ParamConfigProps> = ({
100100
</Radio.Group>
101101
</Form.Item>
102102
);
103-
case "checkbox":
103+
case "checkbox": {
104+
const group = Array.isArray(value) ? value: value.split(",").map(item => item.trim()).filter(Boolean);
104105
return (
105106
<Form.Item
106107
label={param.name}
107108
tooltip={param.description}
108109
key={paramKey}
109110
>
110111
<Checkbox.Group
111-
value={value}
112+
value={group}
112113
onChange={updateValue}
113114
options={param.options || []}
114115
/>
115116
</Form.Item>
116117
);
118+
}
117119
case "slider":
118120
return (
119121
<Form.Item

frontend/src/pages/DataCleansing/Create/hooks/useDragOperators.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ export function useDragOperators({
8484
setOperators([...operators, draggingItem]);
8585
}
8686
}
87+
// 如果是算子编排区域内的重新排序,移动到末尾
88+
else if (draggingSource === "sort") {
89+
const draggedIndex = operators.findIndex(
90+
(item) => item.id === draggingItem.id
91+
);
92+
if (draggedIndex !== -1 && draggedIndex !== operators.length - 1) {
93+
const newItems = [...operators];
94+
const [draggedItem] = newItems.splice(draggedIndex, 1);
95+
newItems.push(draggedItem);
96+
setOperators(newItems);
97+
}
98+
}
8799

88100
resetDragState();
89101
};

0 commit comments

Comments
 (0)