Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/src/components/CardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,10 @@ function CardView<T extends BaseCardDataType>(props: CardViewProps<T>) {
<div className="grid grid-cols-2 gap-4 py-3">
{item?.statistics?.map((stat, idx) => (
<div key={idx}>
<div className="text-sm text-gray-500">
<div className="text-sm text-gray-500 overflow-hidden whitespace-nowrap text-ellipsis w-full">
{stat?.label}:
</div>
<div className="text-base font-semibold text-gray-900">
<div className="text-base font-semibold text-gray-900 overflow-hidden whitespace-nowrap text-ellipsis w-full">
{stat?.value}
</div>
</div>
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/hooks/useFetchData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ import { useState, useRef, useEffect, useCallback } from "react";
import { useDebouncedEffect } from "./useDebouncedEffect";
import Loading from "@/utils/loading";
import { App } from "antd";
import { AnyObject } from "antd/es/_util/type";

export default function useFetchData<T>(
fetchFunc: (params?: any) => Promise<any>,
mapDataFunc: (data: AnyObject) => T = (data) => data as T,
mapDataFunc: (data: Partial<T>) => T = (data) => data as T,
pollingInterval: number = 30000, // 默认30秒轮询一次
autoRefresh: boolean = true,
additionalPollingFuncs: (() => Promise<any>)[] = [], // 额外的轮询函数
Expand Down
185 changes: 185 additions & 0 deletions frontend/src/hooks/useSliceUpload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { TaskItem } from "@/pages/DataManagement/dataset.model";
import { calculateSHA256, checkIsFilesExist } from "@/utils/file.util";
import { App } from "antd";
import { useRef, useState } from "react";

export function useFileSliceUpload(
{
preUpload,
uploadChunk,
cancelUpload,
}: {
preUpload: (id: string, params: any) => Promise<{ data: number }>;
uploadChunk: (id: string, formData: FormData, config: any) => Promise<any>;
cancelUpload: ((reqId: number) => Promise<any>) | null;
},
showTaskCenter = true // 上传时是否显示任务中心
) {
const { message } = App.useApp();
const [taskList, setTaskList] = useState<TaskItem[]>([]);
const taskListRef = useRef<TaskItem[]>([]); // 用于固定任务顺序

const createTask = (detail: any = {}) => {
const { dataset } = detail;
const title = `上传数据集: ${dataset.name} `;
const controller = new AbortController();
const task: TaskItem = {
key: dataset.id,
title,
percent: 0,
reqId: -1,
controller,
size: 0,
updateEvent: detail.updateEvent,
};
taskListRef.current = [task, ...taskListRef.current];

setTaskList(taskListRef.current);
return task;
};

const updateTaskList = (task: TaskItem) => {
taskListRef.current = taskListRef.current.map((item) =>
item.key === task.key ? task : item
);
setTaskList(taskListRef.current);
};

const removeTask = (task: TaskItem) => {
const { key } = task;
taskListRef.current = taskListRef.current.filter(
(item) => item.key !== key
);
setTaskList(taskListRef.current);
if (task.isCancel && task.cancelFn) {
task.cancelFn();
}
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));
if (showTaskCenter) {
window.dispatchEvent(
new CustomEvent("show:task-popover", { detail: { show: false } })
);
}
};

async function buildFormData({ file, reqId, i, j }) {
const formData = new FormData();
const { slices, name, size } = file;
const checkSum = await calculateSHA256(slices[j]);
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);
formData.append("fileSize", size.toString());
formData.append("totalChunkNum", slices.length.toString());
formData.append("checkSumHex", checkSum);
return formData;
}

async function uploadSlice(task: TaskItem, fileInfo) {
if (!task) {
return;
}
const { reqId, key } = task;
const { loaded, i, j, files, totalSize } = fileInfo;
const formData = await buildFormData({
file: files[i],
i,
j,
reqId,
});

let newTask = { ...task };
await uploadChunk(key, formData, {
onUploadProgress: (e) => {
const loadedSize = loaded + e.loaded;
const curPercent = Number((loadedSize / totalSize) * 100).toFixed(2);

newTask = {
...newTask,
...taskListRef.current.find((item) => item.key === key),
size: loadedSize,
percent: curPercent >= 100 ? 99.99 : curPercent,
};
updateTaskList(newTask);
},
});
}

async function uploadFile({ task, files, totalSize }) {
const { data: reqId } = await preUpload(task.key, {
totalFileNum: files.length,
totalSize,
datasetId: task.key,
});

const newTask: TaskItem = {
...task,
reqId,
isCancel: false,
cancelFn: () => {
task.controller.abort();
cancelUpload?.(reqId);
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));
},
};
updateTaskList(newTask);
if (showTaskCenter) {
window.dispatchEvent(
new CustomEvent("show:task-popover", { detail: { show: true } })
);
}
// // 更新数据状态
if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent));

let loaded = 0;
for (let i = 0; i < files.length; i++) {
const { slices } = files[i];
for (let j = 0; j < slices.length; j++) {
await uploadSlice(newTask, {
loaded,
i,
j,
files,
totalSize,
});
loaded += slices[j].size;
}
}
removeTask(newTask);
}

const handleUpload = async ({ task, files }) => {
const isErrorFile = await checkIsFilesExist(files);
if (isErrorFile) {
message.error("文件被修改或删除,请重新选择文件上传");
removeTask({
...task,
isCancel: false,
...taskListRef.current.find((item) => item.key === task.key),
});
return;
}

try {
const totalSize = files.reduce((acc, file) => acc + file.size, 0);
await uploadFile({ task, files, totalSize });
} catch (err) {
console.error(err);
message.error("文件上传失败,请稍后重试");
removeTask({
...task,
isCancel: true,
...taskListRef.current.find((item) => item.key === task.key),
});
}
};

return {
taskList,
createTask,
removeTask,
handleUpload,
};
}
28 changes: 13 additions & 15 deletions frontend/src/mock/mock-apis.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,15 @@ const MockAPI = {
batchEvaluationUsingPost: "/evaluation/batch-evaluate", // 批量评测

// 知识生成接口
queryKnowledgeBasesUsingPost: "/knowledge/bases", // 获取知识库列表
createKnowledgeBaseUsingPost: "/knowledge/bases/create", // 创建知识库
queryKnowledgeBaseByIdUsingGet: "/knowledge/bases/:baseId", // 根据ID获取知识库详情
updateKnowledgeBaseByIdUsingPut: "/knowledge/bases/:baseId", // 更新知识库
deleteKnowledgeBaseByIdUsingDelete: "/knowledge/bases/:baseId", // 删除知识库
queryKnowledgeGenerationTasksUsingPost: "/knowledge/tasks", // 获取知识生成任务列表
createKnowledgeGenerationTaskUsingPost: "/knowledge/tasks/create", // 创建知识生成任务
queryKnowledgeGenerationTaskByIdUsingGet: "/knowledge/tasks/:taskId", // 根据ID获取知识生成任务详情
updateKnowledgeGenerationTaskByIdUsingPut: "/knowledge/tasks/:taskId", // 更新知识生成任务
deleteKnowledgeGenerationTaskByIdUsingDelete: "/knowledge/tasks/:taskId", // 删除知识生成任务
executeKnowledgeGenerationTaskByIdUsingPost:
"/knowledge/tasks/:taskId/execute", // 执行知识生成任务
stopKnowledgeGenerationTaskByIdUsingPost: "/knowledge/tasks/:taskId/stop", // 停止知识生成任务
queryKnowledgeStatisticsUsingGet: "/knowledge/statistics", // 获取知识生成
queryKnowledgeBasesUsingPost: "/knowledge-base/list", // 获取知识库列表
createKnowledgeBaseUsingPost: "/knowledge-base/create", // 创建知识库
queryKnowledgeBaseByIdUsingGet: "/knowledge-base/:baseId", // 根据ID获取知识库详情
updateKnowledgeBaseByIdUsingPut: "/knowledge-base/:baseId", // 更新知识库
deleteKnowledgeBaseByIdUsingDelete: "/knowledge-base/:baseId", // 删除知识库
queryKnowledgeGenerationTasksUsingPost: "/knowledge-base/tasks", // 获取知识生成任务列表
addKnowledgeGenerationFilesUsingPost: "/knowledge-base/:baseId/files", // 添加文件到知识库
queryKnowledgeGenerationFilesByIdUsingGet: "/knowledge-base/:baseId/files/:fileId", // 根据ID获取知识生成文件详情
deleteKnowledgeGenerationTaskByIdUsingDelete: "/knowledge-base/:baseId/files", // 删除知识生成文件

// 算子市场
queryOperatorsUsingPost: "/operators/list", // 获取算子列表
Expand All @@ -137,6 +132,10 @@ const MockAPI = {
createOperatorUsingPost: "/operators/create", // 创建算子
updateOperatorByIdUsingPut: "/operators/:operatorId", // 更新算子
uploadOperatorUsingPost: "/operators/upload", // 上传算子
uploadFileChunkUsingPost: "/operators/upload/chunk", // 上传切片
preUploadOperatorUsingPost: "/operators/upload/pre-upload", // 预上传文件
cancelUploadOperatorUsingPut: "/operators/upload/cancel-upload", // 取消上传

createLabelUsingPost: "/operators/labels", // 创建算子标签
queryLabelsUsingGet: "/labels", // 获取算子标签列表
deleteLabelsUsingDelete: "/labels", // 删除算子标签
Expand All @@ -151,7 +150,6 @@ const MockAPI = {
createModelUsingPost: "/models/create", // 创建模型
updateModelUsingPut: "/models/:id", // 更新模型
deleteModelUsingDelete: "/models/:id", // 删除模型

};

module.exports = addMockPrefix("/api", MockAPI);
Loading