You've already forked DataMate
fix: 上传文件时任务中心立即显示
问题描述: 在数据管理的数据集详情页上传文件时,点击确认后,弹窗消失,但是需要等待文件处理(特别是启用按行分割时)后任务中心才弹出来,用户体验不好。 修改内容: 1. useSliceUpload.tsx: 在 createTask 函数中添加立即显示任务中心的逻辑,确保任务创建后立即显示 2. ImportConfiguration.tsx: 在 handleImportData 函数中,在执行耗时的文件处理操作(如文件分割)之前,立即触发 show:task-popover 事件显示任务中心 效果: - 修改前:点击确认 → 弹窗消失 → (等待文件处理)→ 任务中心弹出 - 修改后:点击确认 → 弹窗消失 + 任务中心立即弹出 → 文件开始处理
This commit is contained in:
@@ -1,198 +1,202 @@
|
|||||||
import { TaskItem } from "@/pages/DataManagement/dataset.model";
|
import { TaskItem } from "@/pages/DataManagement/dataset.model";
|
||||||
import { calculateSHA256, checkIsFilesExist } from "@/utils/file.util";
|
import { calculateSHA256, checkIsFilesExist } from "@/utils/file.util";
|
||||||
import { App } from "antd";
|
import { App } from "antd";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
export function useFileSliceUpload(
|
export function useFileSliceUpload(
|
||||||
{
|
{
|
||||||
preUpload,
|
preUpload,
|
||||||
uploadChunk,
|
uploadChunk,
|
||||||
cancelUpload,
|
cancelUpload,
|
||||||
}: {
|
}: {
|
||||||
preUpload: (id: string, params: any) => Promise<{ data: number }>;
|
preUpload: (id: string, params: any) => Promise<{ data: number }>;
|
||||||
uploadChunk: (id: string, formData: FormData, config: any) => Promise<any>;
|
uploadChunk: (id: string, formData: FormData, config: any) => Promise<any>;
|
||||||
cancelUpload: ((reqId: number) => Promise<any>) | null;
|
cancelUpload: ((reqId: number) => Promise<any>) | null;
|
||||||
},
|
},
|
||||||
showTaskCenter = true // 上传时是否显示任务中心
|
showTaskCenter = true // 上传时是否显示任务中心
|
||||||
) {
|
) {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const [taskList, setTaskList] = useState<TaskItem[]>([]);
|
const [taskList, setTaskList] = useState<TaskItem[]>([]);
|
||||||
const taskListRef = useRef<TaskItem[]>([]); // 用于固定任务顺序
|
const taskListRef = useRef<TaskItem[]>([]); // 用于固定任务顺序
|
||||||
|
|
||||||
const createTask = (detail: any = {}) => {
|
const createTask = (detail: any = {}) => {
|
||||||
const { dataset } = detail;
|
const { dataset } = detail;
|
||||||
const title = `上传数据集: ${dataset.name} `;
|
const title = `上传数据集: ${dataset.name} `;
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const task: TaskItem = {
|
const task: TaskItem = {
|
||||||
key: dataset.id,
|
key: dataset.id,
|
||||||
title,
|
title,
|
||||||
percent: 0,
|
percent: 0,
|
||||||
reqId: -1,
|
reqId: -1,
|
||||||
controller,
|
controller,
|
||||||
size: 0,
|
size: 0,
|
||||||
updateEvent: detail.updateEvent,
|
updateEvent: detail.updateEvent,
|
||||||
hasArchive: detail.hasArchive,
|
hasArchive: detail.hasArchive,
|
||||||
prefix: detail.prefix,
|
prefix: detail.prefix,
|
||||||
};
|
};
|
||||||
taskListRef.current = [task, ...taskListRef.current];
|
taskListRef.current = [task, ...taskListRef.current];
|
||||||
|
|
||||||
setTaskList(taskListRef.current);
|
setTaskList(taskListRef.current);
|
||||||
return task;
|
|
||||||
};
|
// 立即显示任务中心,让用户感知上传已开始
|
||||||
|
if (showTaskCenter) {
|
||||||
const updateTaskList = (task: TaskItem) => {
|
window.dispatchEvent(
|
||||||
taskListRef.current = taskListRef.current.map((item) =>
|
new CustomEvent("show:task-popover", { detail: { show: true } })
|
||||||
item.key === task.key ? task : item
|
);
|
||||||
);
|
}
|
||||||
setTaskList(taskListRef.current);
|
|
||||||
};
|
return task;
|
||||||
|
};
|
||||||
const removeTask = (task: TaskItem) => {
|
|
||||||
const { key } = task;
|
const updateTaskList = (task: TaskItem) => {
|
||||||
taskListRef.current = taskListRef.current.filter(
|
taskListRef.current = taskListRef.current.map((item) =>
|
||||||
(item) => item.key !== key
|
item.key === task.key ? task : item
|
||||||
);
|
);
|
||||||
setTaskList(taskListRef.current);
|
setTaskList(taskListRef.current);
|
||||||
if (task.isCancel && task.cancelFn) {
|
};
|
||||||
task.cancelFn();
|
|
||||||
}
|
const removeTask = (task: TaskItem) => {
|
||||||
if (task.updateEvent) {
|
const { key } = task;
|
||||||
// 携带前缀信息,便于刷新后仍停留在当前目录
|
taskListRef.current = taskListRef.current.filter(
|
||||||
window.dispatchEvent(
|
(item) => item.key !== key
|
||||||
new CustomEvent(task.updateEvent, {
|
);
|
||||||
detail: { prefix: (task as any).prefix },
|
setTaskList(taskListRef.current);
|
||||||
})
|
if (task.isCancel && task.cancelFn) {
|
||||||
);
|
task.cancelFn();
|
||||||
}
|
}
|
||||||
if (showTaskCenter) {
|
if (task.updateEvent) {
|
||||||
window.dispatchEvent(
|
// 携带前缀信息,便于刷新后仍停留在当前目录
|
||||||
new CustomEvent("show:task-popover", { detail: { show: false } })
|
window.dispatchEvent(
|
||||||
);
|
new CustomEvent(task.updateEvent, {
|
||||||
}
|
detail: { prefix: (task as any).prefix },
|
||||||
};
|
})
|
||||||
|
);
|
||||||
async function buildFormData({ file, reqId, i, j }) {
|
}
|
||||||
const formData = new FormData();
|
if (showTaskCenter) {
|
||||||
const { slices, name, size } = file;
|
window.dispatchEvent(
|
||||||
const checkSum = await calculateSHA256(slices[j]);
|
new CustomEvent("show:task-popover", { detail: { show: false } })
|
||||||
formData.append("file", slices[j]);
|
);
|
||||||
formData.append("reqId", reqId.toString());
|
}
|
||||||
formData.append("fileNo", (i + 1).toString());
|
};
|
||||||
formData.append("chunkNo", (j + 1).toString());
|
|
||||||
formData.append("fileName", name);
|
async function buildFormData({ file, reqId, i, j }) {
|
||||||
formData.append("fileSize", size.toString());
|
const formData = new FormData();
|
||||||
formData.append("totalChunkNum", slices.length.toString());
|
const { slices, name, size } = file;
|
||||||
formData.append("checkSumHex", checkSum);
|
const checkSum = await calculateSHA256(slices[j]);
|
||||||
return formData;
|
formData.append("file", slices[j]);
|
||||||
}
|
formData.append("reqId", reqId.toString());
|
||||||
|
formData.append("fileNo", (i + 1).toString());
|
||||||
async function uploadSlice(task: TaskItem, fileInfo) {
|
formData.append("chunkNo", (j + 1).toString());
|
||||||
if (!task) {
|
formData.append("fileName", name);
|
||||||
return;
|
formData.append("fileSize", size.toString());
|
||||||
}
|
formData.append("totalChunkNum", slices.length.toString());
|
||||||
const { reqId, key } = task;
|
formData.append("checkSumHex", checkSum);
|
||||||
const { loaded, i, j, files, totalSize } = fileInfo;
|
return formData;
|
||||||
const formData = await buildFormData({
|
}
|
||||||
file: files[i],
|
|
||||||
i,
|
async function uploadSlice(task: TaskItem, fileInfo) {
|
||||||
j,
|
if (!task) {
|
||||||
reqId,
|
return;
|
||||||
});
|
}
|
||||||
|
const { reqId, key } = task;
|
||||||
let newTask = { ...task };
|
const { loaded, i, j, files, totalSize } = fileInfo;
|
||||||
await uploadChunk(key, formData, {
|
const formData = await buildFormData({
|
||||||
onUploadProgress: (e) => {
|
file: files[i],
|
||||||
const loadedSize = loaded + e.loaded;
|
i,
|
||||||
const curPercent = Number((loadedSize / totalSize) * 100).toFixed(2);
|
j,
|
||||||
|
reqId,
|
||||||
newTask = {
|
});
|
||||||
...newTask,
|
|
||||||
...taskListRef.current.find((item) => item.key === key),
|
let newTask = { ...task };
|
||||||
size: loadedSize,
|
await uploadChunk(key, formData, {
|
||||||
percent: curPercent >= 100 ? 99.99 : curPercent,
|
onUploadProgress: (e) => {
|
||||||
};
|
const loadedSize = loaded + e.loaded;
|
||||||
updateTaskList(newTask);
|
const curPercent = Number((loadedSize / totalSize) * 100).toFixed(2);
|
||||||
},
|
|
||||||
});
|
newTask = {
|
||||||
}
|
...newTask,
|
||||||
|
...taskListRef.current.find((item) => item.key === key),
|
||||||
async function uploadFile({ task, files, totalSize }) {
|
size: loadedSize,
|
||||||
console.log('[useSliceUpload] Calling preUpload with prefix:', task.prefix);
|
percent: curPercent >= 100 ? 99.99 : curPercent,
|
||||||
const { data: reqId } = await preUpload(task.key, {
|
};
|
||||||
totalFileNum: files.length,
|
updateTaskList(newTask);
|
||||||
totalSize,
|
},
|
||||||
datasetId: task.key,
|
});
|
||||||
hasArchive: task.hasArchive,
|
}
|
||||||
prefix: task.prefix,
|
|
||||||
});
|
async function uploadFile({ task, files, totalSize }) {
|
||||||
console.log('[useSliceUpload] PreUpload response reqId:', reqId);
|
console.log('[useSliceUpload] Calling preUpload with prefix:', task.prefix);
|
||||||
|
const { data: reqId } = await preUpload(task.key, {
|
||||||
const newTask: TaskItem = {
|
totalFileNum: files.length,
|
||||||
...task,
|
totalSize,
|
||||||
reqId,
|
datasetId: task.key,
|
||||||
isCancel: false,
|
hasArchive: task.hasArchive,
|
||||||
cancelFn: () => {
|
prefix: task.prefix,
|
||||||
task.controller.abort();
|
});
|
||||||
cancelUpload?.(reqId);
|
console.log('[useSliceUpload] PreUpload response reqId:', reqId);
|
||||||
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));
|
|
||||||
},
|
const newTask: TaskItem = {
|
||||||
};
|
...task,
|
||||||
updateTaskList(newTask);
|
reqId,
|
||||||
if (showTaskCenter) {
|
isCancel: false,
|
||||||
window.dispatchEvent(
|
cancelFn: () => {
|
||||||
new CustomEvent("show:task-popover", { detail: { show: true } })
|
task.controller.abort();
|
||||||
);
|
cancelUpload?.(reqId);
|
||||||
}
|
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));
|
||||||
// // 更新数据状态
|
},
|
||||||
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));
|
};
|
||||||
|
updateTaskList(newTask);
|
||||||
let loaded = 0;
|
// 注意:show:task-popover 事件已在 createTask 中触发,此处不再重复触发
|
||||||
for (let i = 0; i < files.length; i++) {
|
// // 更新数据状态
|
||||||
const { slices } = files[i];
|
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));
|
||||||
for (let j = 0; j < slices.length; j++) {
|
|
||||||
await uploadSlice(newTask, {
|
let loaded = 0;
|
||||||
loaded,
|
for (let i = 0; i < files.length; i++) {
|
||||||
i,
|
const { slices } = files[i];
|
||||||
j,
|
for (let j = 0; j < slices.length; j++) {
|
||||||
files,
|
await uploadSlice(newTask, {
|
||||||
totalSize,
|
loaded,
|
||||||
});
|
i,
|
||||||
loaded += slices[j].size;
|
j,
|
||||||
}
|
files,
|
||||||
}
|
totalSize,
|
||||||
removeTask(newTask);
|
});
|
||||||
}
|
loaded += slices[j].size;
|
||||||
|
}
|
||||||
const handleUpload = async ({ task, files }) => {
|
}
|
||||||
const isErrorFile = await checkIsFilesExist(files);
|
removeTask(newTask);
|
||||||
if (isErrorFile) {
|
}
|
||||||
message.error("文件被修改或删除,请重新选择文件上传");
|
|
||||||
removeTask({
|
const handleUpload = async ({ task, files }) => {
|
||||||
...task,
|
const isErrorFile = await checkIsFilesExist(files);
|
||||||
isCancel: false,
|
if (isErrorFile) {
|
||||||
...taskListRef.current.find((item) => item.key === task.key),
|
message.error("文件被修改或删除,请重新选择文件上传");
|
||||||
});
|
removeTask({
|
||||||
return;
|
...task,
|
||||||
}
|
isCancel: false,
|
||||||
|
...taskListRef.current.find((item) => item.key === task.key),
|
||||||
try {
|
});
|
||||||
const totalSize = files.reduce((acc, file) => acc + file.size, 0);
|
return;
|
||||||
await uploadFile({ task, files, totalSize });
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
try {
|
||||||
message.error("文件上传失败,请稍后重试");
|
const totalSize = files.reduce((acc, file) => acc + file.size, 0);
|
||||||
removeTask({
|
await uploadFile({ task, files, totalSize });
|
||||||
...task,
|
} catch (err) {
|
||||||
isCancel: true,
|
console.error(err);
|
||||||
...taskListRef.current.find((item) => item.key === task.key),
|
message.error("文件上传失败,请稍后重试");
|
||||||
});
|
removeTask({
|
||||||
}
|
...task,
|
||||||
};
|
isCancel: true,
|
||||||
|
...taskListRef.current.find((item) => item.key === task.key),
|
||||||
return {
|
});
|
||||||
taskList,
|
}
|
||||||
createTask,
|
};
|
||||||
removeTask,
|
|
||||||
handleUpload,
|
return {
|
||||||
};
|
taskList,
|
||||||
}
|
createTask,
|
||||||
|
removeTask,
|
||||||
|
handleUpload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Select, Input, Form, Radio, Modal, Button, UploadFile, Switch, Tooltip } from "antd";
|
import { Select, Input, Form, Radio, Modal, Button, UploadFile, Switch, Tooltip } from "antd";
|
||||||
import { InboxOutlined, QuestionCircleOutlined } from "@ant-design/icons";
|
import { InboxOutlined, QuestionCircleOutlined } from "@ant-design/icons";
|
||||||
import { dataSourceOptions } from "../../dataset.const";
|
import { dataSourceOptions } from "../../dataset.const";
|
||||||
import { Dataset, DatasetType, DataSource } from "../../dataset.model";
|
import { Dataset, DatasetType, DataSource } from "../../dataset.model";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { queryTasksUsingGet } from "@/pages/DataCollection/collection.apis";
|
import { queryTasksUsingGet } from "@/pages/DataCollection/collection.apis";
|
||||||
import { updateDatasetByIdUsingPut } from "../../dataset.api";
|
import { updateDatasetByIdUsingPut } from "../../dataset.api";
|
||||||
import { sliceFile } from "@/utils/file.util";
|
import { sliceFile } from "@/utils/file.util";
|
||||||
import Dragger from "antd/es/upload/Dragger";
|
import Dragger from "antd/es/upload/Dragger";
|
||||||
|
|
||||||
const TEXT_FILE_MIME_PREFIX = "text/";
|
const TEXT_FILE_MIME_PREFIX = "text/";
|
||||||
const TEXT_FILE_MIME_TYPES = new Set([
|
const TEXT_FILE_MIME_TYPES = new Set([
|
||||||
"application/json",
|
"application/json",
|
||||||
@@ -131,18 +131,18 @@ type ImportConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function ImportConfiguration({
|
export default function ImportConfiguration({
|
||||||
data,
|
data,
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
updateEvent = "update:dataset",
|
updateEvent = "update:dataset",
|
||||||
prefix,
|
prefix,
|
||||||
}: {
|
}: {
|
||||||
data: Dataset | null;
|
data: Dataset | null;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
updateEvent?: string;
|
updateEvent?: string;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
}) {
|
}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [collectionOptions, setCollectionOptions] = useState<SelectOption[]>([]);
|
const [collectionOptions, setCollectionOptions] = useState<SelectOption[]>([]);
|
||||||
const availableSourceOptions = dataSourceOptions.filter(
|
const availableSourceOptions = dataSourceOptions.filter(
|
||||||
@@ -160,13 +160,13 @@ export default function ImportConfiguration({
|
|||||||
return files.some((file) => !isTextUploadFile(file));
|
return files.some((file) => !isTextUploadFile(file));
|
||||||
}, [importConfig.files]);
|
}, [importConfig.files]);
|
||||||
const isTextDataset = data?.datasetType === DatasetType.TEXT;
|
const isTextDataset = data?.datasetType === DatasetType.TEXT;
|
||||||
|
|
||||||
// 本地上传文件相关逻辑
|
// 本地上传文件相关逻辑
|
||||||
|
|
||||||
const handleUpload = async (dataset: Dataset) => {
|
const handleUpload = async (dataset: Dataset) => {
|
||||||
let filesToUpload =
|
let filesToUpload =
|
||||||
(form.getFieldValue("files") as UploadFile[] | undefined) || [];
|
(form.getFieldValue("files") as UploadFile[] | undefined) || [];
|
||||||
|
|
||||||
// 如果启用分行分割,处理文件
|
// 如果启用分行分割,处理文件
|
||||||
if (importConfig.splitByLine && !hasNonTextFile) {
|
if (importConfig.splitByLine && !hasNonTextFile) {
|
||||||
const splitResults = await Promise.all(
|
const splitResults = await Promise.all(
|
||||||
@@ -174,9 +174,9 @@ export default function ImportConfiguration({
|
|||||||
);
|
);
|
||||||
filesToUpload = splitResults.flat();
|
filesToUpload = splitResults.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算分片列表
|
// 计算分片列表
|
||||||
const sliceList = filesToUpload.map((file) => {
|
const sliceList = filesToUpload.map((file) => {
|
||||||
const originFile = (file.originFileObj ?? file) as Blob;
|
const originFile = (file.originFileObj ?? file) as Blob;
|
||||||
const slices = sliceFile(originFile);
|
const slices = sliceFile(originFile);
|
||||||
return {
|
return {
|
||||||
@@ -185,22 +185,22 @@ export default function ImportConfiguration({
|
|||||||
name: file.name,
|
name: file.name,
|
||||||
size: originFile.size || 0,
|
size: originFile.size || 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[ImportConfiguration] Uploading with currentPrefix:", currentPrefix);
|
console.log("[ImportConfiguration] Uploading with currentPrefix:", currentPrefix);
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent("upload:dataset", {
|
new CustomEvent("upload:dataset", {
|
||||||
detail: {
|
detail: {
|
||||||
dataset,
|
dataset,
|
||||||
files: sliceList,
|
files: sliceList,
|
||||||
updateEvent,
|
updateEvent,
|
||||||
hasArchive: importConfig.hasArchive,
|
hasArchive: importConfig.hasArchive,
|
||||||
prefix: currentPrefix,
|
prefix: currentPrefix,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchCollectionTasks = useCallback(async () => {
|
const fetchCollectionTasks = useCallback(async () => {
|
||||||
if (importConfig.source !== DataSource.COLLECTION) return;
|
if (importConfig.source !== DataSource.COLLECTION) return;
|
||||||
try {
|
try {
|
||||||
@@ -212,7 +212,7 @@ export default function ImportConfiguration({
|
|||||||
label: task.name,
|
label: task.name,
|
||||||
value: task.id,
|
value: task.id,
|
||||||
}));
|
}));
|
||||||
setCollectionOptions(options);
|
setCollectionOptions(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching collection tasks:", error);
|
console.error("Error fetching collection tasks:", error);
|
||||||
}
|
}
|
||||||
@@ -229,27 +229,31 @@ export default function ImportConfiguration({
|
|||||||
});
|
});
|
||||||
console.log('[ImportConfiguration] resetState done, currentPrefix still:', currentPrefix);
|
console.log('[ImportConfiguration] resetState done, currentPrefix still:', currentPrefix);
|
||||||
}, [currentPrefix, form]);
|
}, [currentPrefix, form]);
|
||||||
|
|
||||||
const handleImportData = async () => {
|
const handleImportData = async () => {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
console.log('[ImportConfiguration] handleImportData called, currentPrefix:', currentPrefix);
|
console.log('[ImportConfiguration] handleImportData called, currentPrefix:', currentPrefix);
|
||||||
if (importConfig.source === DataSource.UPLOAD) {
|
if (importConfig.source === DataSource.UPLOAD) {
|
||||||
await handleUpload(data);
|
// 立即显示任务中心,让用户感知上传已开始(在文件分割等耗时操作之前)
|
||||||
} else if (importConfig.source === DataSource.COLLECTION) {
|
window.dispatchEvent(
|
||||||
await updateDatasetByIdUsingPut(data.id, {
|
new CustomEvent("show:task-popover", { detail: { show: true } })
|
||||||
...importConfig,
|
);
|
||||||
});
|
await handleUpload(data);
|
||||||
}
|
} else if (importConfig.source === DataSource.COLLECTION) {
|
||||||
onClose();
|
await updateDatasetByIdUsingPut(data.id, {
|
||||||
};
|
...importConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
setCurrentPrefix(prefix || "");
|
setCurrentPrefix(prefix || "");
|
||||||
console.log('[ImportConfiguration] Modal opened with prefix:', prefix);
|
console.log('[ImportConfiguration] Modal opened with prefix:', prefix);
|
||||||
resetState();
|
resetState();
|
||||||
fetchCollectionTasks();
|
fetchCollectionTasks();
|
||||||
}
|
}
|
||||||
}, [fetchCollectionTasks, open, prefix, resetState]);
|
}, [fetchCollectionTasks, open, prefix, resetState]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -259,111 +263,111 @@ export default function ImportConfiguration({
|
|||||||
form.setFieldsValue({ splitByLine: false });
|
form.setFieldsValue({ splitByLine: false });
|
||||||
setImportConfig((prev) => ({ ...prev, splitByLine: false }));
|
setImportConfig((prev) => ({ ...prev, splitByLine: false }));
|
||||||
}, [form, hasNonTextFile, importConfig.files, importConfig.splitByLine]);
|
}, [form, hasNonTextFile, importConfig.files, importConfig.splitByLine]);
|
||||||
|
|
||||||
// Separate effect for fetching collection tasks when source changes
|
// Separate effect for fetching collection tasks when source changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && importConfig.source === DataSource.COLLECTION) {
|
if (open && importConfig.source === DataSource.COLLECTION) {
|
||||||
fetchCollectionTasks();
|
fetchCollectionTasks();
|
||||||
}
|
}
|
||||||
}, [fetchCollectionTasks, importConfig.source, open]);
|
}, [fetchCollectionTasks, importConfig.source, open]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="导入数据"
|
title="导入数据"
|
||||||
open={open}
|
open={open}
|
||||||
width={600}
|
width={600}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
onClose();
|
onClose();
|
||||||
resetState();
|
resetState();
|
||||||
}}
|
}}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
<Button onClick={onClose}>取消</Button>
|
<Button onClick={onClose}>取消</Button>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
disabled={!importConfig?.files?.length && !importConfig.dataSource}
|
disabled={!importConfig?.files?.length && !importConfig.dataSource}
|
||||||
onClick={handleImportData}
|
onClick={handleImportData}
|
||||||
>
|
>
|
||||||
确定
|
确定
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
initialValues={importConfig || {}}
|
initialValues={importConfig || {}}
|
||||||
onValuesChange={(_, allValues) => setImportConfig(allValues)}
|
onValuesChange={(_, allValues) => setImportConfig(allValues)}
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="数据源"
|
label="数据源"
|
||||||
name="source"
|
name="source"
|
||||||
rules={[{ required: true, message: "请选择数据源" }]}
|
rules={[{ required: true, message: "请选择数据源" }]}
|
||||||
>
|
>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
buttonStyle="solid"
|
buttonStyle="solid"
|
||||||
options={availableSourceOptions}
|
options={availableSourceOptions}
|
||||||
optionType="button"
|
optionType="button"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{importConfig?.source === DataSource.COLLECTION && (
|
{importConfig?.source === DataSource.COLLECTION && (
|
||||||
<Form.Item name="dataSource" label="归集任务" required>
|
<Form.Item name="dataSource" label="归集任务" required>
|
||||||
<Select placeholder="请选择归集任务" options={collectionOptions} />
|
<Select placeholder="请选择归集任务" options={collectionOptions} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* obs import */}
|
{/* obs import */}
|
||||||
{importConfig?.source === DataSource.OBS && (
|
{importConfig?.source === DataSource.OBS && (
|
||||||
<div className="grid grid-cols-2 gap-3 p-4 bg-blue-50 rounded-lg">
|
<div className="grid grid-cols-2 gap-3 p-4 bg-blue-50 rounded-lg">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="endpoint"
|
name="endpoint"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="Endpoint"
|
label="Endpoint"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
className="h-8 text-xs"
|
className="h-8 text-xs"
|
||||||
placeholder="obs.cn-north-4.myhuaweicloud.com"
|
placeholder="obs.cn-north-4.myhuaweicloud.com"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="bucket"
|
name="bucket"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="Bucket"
|
label="Bucket"
|
||||||
>
|
>
|
||||||
<Input className="h-8 text-xs" placeholder="my-bucket" />
|
<Input className="h-8 text-xs" placeholder="my-bucket" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="accessKey"
|
name="accessKey"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="Access Key"
|
label="Access Key"
|
||||||
>
|
>
|
||||||
<Input className="h-8 text-xs" placeholder="Access Key" />
|
<Input className="h-8 text-xs" placeholder="Access Key" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="secretKey"
|
name="secretKey"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="Secret Key"
|
label="Secret Key"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
className="h-8 text-xs"
|
className="h-8 text-xs"
|
||||||
placeholder="Secret Key"
|
placeholder="Secret Key"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Local Upload Component */}
|
{/* Local Upload Component */}
|
||||||
{importConfig?.source === DataSource.UPLOAD && (
|
{importConfig?.source === DataSource.UPLOAD && (
|
||||||
<>
|
<>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="自动解压上传的压缩包"
|
label="自动解压上传的压缩包"
|
||||||
name="hasArchive"
|
name="hasArchive"
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{isTextDataset && (
|
{isTextDataset && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={
|
label={
|
||||||
@@ -386,10 +390,10 @@ export default function ImportConfiguration({
|
|||||||
<Switch disabled={hasNonTextFile} />
|
<Switch disabled={hasNonTextFile} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="上传文件"
|
label="上传文件"
|
||||||
name="files"
|
name="files"
|
||||||
valuePropName="fileList"
|
valuePropName="fileList"
|
||||||
getValueFromEvent={(
|
getValueFromEvent={(
|
||||||
event: { fileList?: UploadFile[] } | UploadFile[]
|
event: { fileList?: UploadFile[] } | UploadFile[]
|
||||||
) => {
|
) => {
|
||||||
@@ -398,69 +402,69 @@ export default function ImportConfiguration({
|
|||||||
}
|
}
|
||||||
return event?.fileList;
|
return event?.fileList;
|
||||||
}}
|
}}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请上传文件",
|
message: "请上传文件",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Dragger
|
<Dragger
|
||||||
className="w-full"
|
className="w-full"
|
||||||
beforeUpload={() => false}
|
beforeUpload={() => false}
|
||||||
multiple
|
multiple
|
||||||
>
|
>
|
||||||
<p className="ant-upload-drag-icon">
|
<p className="ant-upload-drag-icon">
|
||||||
<InboxOutlined />
|
<InboxOutlined />
|
||||||
</p>
|
</p>
|
||||||
<p className="ant-upload-text">本地文件上传</p>
|
<p className="ant-upload-text">本地文件上传</p>
|
||||||
<p className="ant-upload-hint">拖拽文件到此处或点击选择文件</p>
|
<p className="ant-upload-hint">拖拽文件到此处或点击选择文件</p>
|
||||||
</Dragger>
|
</Dragger>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Target Configuration */}
|
{/* Target Configuration */}
|
||||||
{importConfig?.target && importConfig?.target !== DataSource.UPLOAD && (
|
{importConfig?.target && importConfig?.target !== DataSource.UPLOAD && (
|
||||||
<div className="space-y-3 p-4 bg-blue-50 rounded-lg">
|
<div className="space-y-3 p-4 bg-blue-50 rounded-lg">
|
||||||
{importConfig?.target === DataSource.DATABASE && (
|
{importConfig?.target === DataSource.DATABASE && (
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="databaseType"
|
name="databaseType"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="数据库类型"
|
label="数据库类型"
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
className="w-full"
|
className="w-full"
|
||||||
options={[
|
options={[
|
||||||
{ label: "MySQL", value: "mysql" },
|
{ label: "MySQL", value: "mysql" },
|
||||||
{ label: "PostgreSQL", value: "postgresql" },
|
{ label: "PostgreSQL", value: "postgresql" },
|
||||||
{ label: "MongoDB", value: "mongodb" },
|
{ label: "MongoDB", value: "mongodb" },
|
||||||
]}
|
]}
|
||||||
></Select>
|
></Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="tableName"
|
name="tableName"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="表名"
|
label="表名"
|
||||||
>
|
>
|
||||||
<Input className="h-8 text-xs" placeholder="dataset_table" />
|
<Input className="h-8 text-xs" placeholder="dataset_table" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="connectionString"
|
name="connectionString"
|
||||||
rules={[{ required: true }]}
|
rules={[{ required: true }]}
|
||||||
label="连接字符串"
|
label="连接字符串"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
className="h-8 text-xs col-span-2"
|
className="h-8 text-xs col-span-2"
|
||||||
placeholder="数据库连接字符串"
|
placeholder="数据库连接字符串"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user