You've already forked DataMate
Compare commits
2 Commits
1fd70085e8
...
accaa47a83
| Author | SHA1 | Date | |
|---|---|---|---|
| accaa47a83 | |||
| 98d2ef1aa5 |
@@ -4,6 +4,7 @@ const TopLoadingBar = () => {
|
|||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
const intervalRef = useRef(null);
|
const intervalRef = useRef(null);
|
||||||
|
const timeoutRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 监听全局事件
|
// 监听全局事件
|
||||||
@@ -33,8 +34,13 @@ const TopLoadingBar = () => {
|
|||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current);
|
||||||
intervalRef.current = null;
|
intervalRef.current = null;
|
||||||
}
|
}
|
||||||
|
// 清除旧的timeout
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
timeoutRef.current = null;
|
||||||
|
}
|
||||||
setProgress(100);
|
setProgress(100);
|
||||||
setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
setIsVisible(false);
|
setIsVisible(false);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
}, 300);
|
}, 300);
|
||||||
@@ -49,6 +55,9 @@ const TopLoadingBar = () => {
|
|||||||
if (intervalRef.current) {
|
if (intervalRef.current) {
|
||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current);
|
||||||
}
|
}
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
window.removeEventListener("loading:show", handleShow);
|
window.removeEventListener("loading:show", handleShow);
|
||||||
window.removeEventListener("loading:hide", handleHide);
|
window.removeEventListener("loading:hide", handleHide);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -151,6 +151,15 @@ export default function AgentPage() {
|
|||||||
const [isTyping, setIsTyping] = useState(false);
|
const [isTyping, setIsTyping] = useState(false);
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
const inputRef = useRef<any>(null);
|
const inputRef = useRef<any>(null);
|
||||||
|
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
@@ -174,8 +183,13 @@ export default function AgentPage() {
|
|||||||
setInputValue("");
|
setInputValue("");
|
||||||
setIsTyping(true);
|
setIsTyping(true);
|
||||||
|
|
||||||
|
// 清理旧的 timeout
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟AI响应
|
// 模拟AI响应
|
||||||
setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
const response = generateResponse(content);
|
const response = generateResponse(content);
|
||||||
const assistantMessage: Message = {
|
const assistantMessage: Message = {
|
||||||
id: (Date.now() + 1).toString(),
|
id: (Date.now() + 1).toString(),
|
||||||
|
|||||||
@@ -78,7 +78,11 @@ export default function DatasetCreate() {
|
|||||||
onValuesChange={handleValuesChange}
|
onValuesChange={handleValuesChange}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<BasicInformation data={newDataset} setData={setNewDataset} />
|
<BasicInformation
|
||||||
|
data={newDataset}
|
||||||
|
setData={setNewDataset}
|
||||||
|
hidden={["dataSource"]}
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 justify-end p-6 border-top">
|
<div className="flex gap-2 justify-end p-6 border-top">
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default function BasicInformation({
|
|||||||
|
|
||||||
// 获取归集任务
|
// 获取归集任务
|
||||||
const fetchCollectionTasks = useCallback(async () => {
|
const fetchCollectionTasks = useCallback(async () => {
|
||||||
|
if (hidden.includes("dataSource")) return;
|
||||||
try {
|
try {
|
||||||
const res = await queryTasksUsingGet({ page: 0, size: 100 });
|
const res = await queryTasksUsingGet({ page: 0, size: 100 });
|
||||||
const tasks = Array.isArray(res?.data?.content)
|
const tasks = Array.isArray(res?.data?.content)
|
||||||
@@ -52,7 +53,7 @@ export default function BasicInformation({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching collection tasks:", error);
|
console.error("Error fetching collection tasks:", error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [hidden]);
|
||||||
|
|
||||||
const fetchParentDatasets = useCallback(async () => {
|
const fetchParentDatasets = useCallback(async () => {
|
||||||
if (hidden.includes("parentDatasetId")) return;
|
if (hidden.includes("parentDatasetId")) return;
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [currentStep, setCurrentStep] = useState(0);
|
const [currentStep, setCurrentStep] = useState(0);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const [selectedFilesMap, setSelectedFilesMap] = useState({});
|
const [selectedFilesMap, setSelectedFilesMap] = useState({});
|
||||||
|
|
||||||
// 定义分块选项
|
// 定义分块选项
|
||||||
const sliceOptions = [
|
const sliceOptions = [
|
||||||
{ label: "默认分块", value: "DEFAULT_CHUNK" },
|
{ label: "默认分块", value: "DEFAULT_CHUNK" },
|
||||||
{ label: "按章节分块", value: "CHAPTER_CHUNK" },
|
{ label: "按句子分块", value: "SENTENCE_CHUNK" },
|
||||||
{ label: "按段落分块", value: "PARAGRAPH_CHUNK" },
|
{ label: "按段落分块", value: "PARAGRAPH_CHUNK" },
|
||||||
{ label: "固定长度分块", value: "FIXED_LENGTH_CHUNK" },
|
{ label: "固定长度分块", value: "LENGTH_CHUNK" },
|
||||||
{ label: "自定义分隔符分块", value: "CUSTOM_SEPARATOR_CHUNK" },
|
{ label: "自定义分隔符分块", value: "CUSTOM_SEPARATOR_CHUNK" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -57,12 +58,7 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// 获取已选择文件总数
|
// 获取已选择文件总数
|
||||||
const getSelectedFilesCount = () => {
|
const getSelectedFilesCount = () => Object.keys(selectedFilesMap).length;
|
||||||
return Object.values(selectedFilesMap).reduce(
|
|
||||||
(total, ids) => total + ids.length,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
// 验证当前步骤
|
// 验证当前步骤
|
||||||
@@ -112,15 +108,29 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAddData = async () => {
|
const handleAddData = async () => {
|
||||||
|
if (isSubmitting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (getSelectedFilesCount() === 0) {
|
if (getSelectedFilesCount() === 0) {
|
||||||
message.warning("请至少选择一个文件");
|
message.warning("请至少选择一个文件");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
const uploadMessageKey = "kb-add-files";
|
||||||
|
message.open({
|
||||||
|
type: "loading",
|
||||||
|
content: "正在上传,请稍候...",
|
||||||
|
key: uploadMessageKey,
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
// 构造符合API要求的请求数据
|
// 构造符合API要求的请求数据
|
||||||
const requestData = {
|
const requestData = {
|
||||||
files: Object.values(selectedFilesMap),
|
files: Object.values(selectedFilesMap).map((file) => ({
|
||||||
|
id: String(file.id),
|
||||||
|
fileName: file.fileName,
|
||||||
|
})),
|
||||||
processType: newKB.processType,
|
processType: newKB.processType,
|
||||||
chunkSize: Number(newKB.chunkSize), // 确保是数字类型
|
chunkSize: Number(newKB.chunkSize), // 确保是数字类型
|
||||||
overlapSize: Number(newKB.overlapSize), // 确保是数字类型
|
overlapSize: Number(newKB.overlapSize), // 确保是数字类型
|
||||||
@@ -132,12 +142,20 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
// 先通知父组件刷新数据(确保刷新发生在重置前)
|
// 先通知父组件刷新数据(确保刷新发生在重置前)
|
||||||
onDataAdded?.();
|
onDataAdded?.();
|
||||||
|
|
||||||
message.success("数据添加成功");
|
message.success({
|
||||||
|
content: "数据添加成功",
|
||||||
|
key: uploadMessageKey,
|
||||||
|
});
|
||||||
// 重置状态
|
// 重置状态
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error("数据添加失败,请重试");
|
message.error({
|
||||||
|
content: "数据添加失败,请重试",
|
||||||
|
key: "kb-add-files",
|
||||||
|
});
|
||||||
console.error("添加文件失败:", error);
|
console.error("添加文件失败:", error);
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -221,10 +239,12 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
footer={
|
footer={
|
||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
{currentStep === 0 && (
|
{currentStep === 0 && (
|
||||||
<Button onClick={handleModalCancel}>取消</Button>
|
<Button onClick={handleModalCancel} disabled={isSubmitting}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
{currentStep > 0 && (
|
{currentStep > 0 && (
|
||||||
<Button disabled={false} onClick={handlePrev}>
|
<Button disabled={isSubmitting} onClick={handlePrev}>
|
||||||
上一步
|
上一步
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -235,14 +255,20 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
Object.keys(selectedFilesMap).length === 0 ||
|
Object.keys(selectedFilesMap).length === 0 ||
|
||||||
!newKB.chunkSize ||
|
!newKB.chunkSize ||
|
||||||
!newKB.overlapSize ||
|
!newKB.overlapSize ||
|
||||||
!newKB.processType
|
!newKB.processType ||
|
||||||
|
isSubmitting
|
||||||
}
|
}
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button type="primary" onClick={handleAddData}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={handleAddData}
|
||||||
|
loading={isSubmitting}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
确认上传
|
确认上传
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { Button, Card, Progress, Badge, Tabs } from "antd";
|
import { Button, Card, Progress, Badge, Tabs } from "antd";
|
||||||
import {
|
import {
|
||||||
GitBranch,
|
GitBranch,
|
||||||
@@ -272,6 +272,19 @@ export default function OrchestrationPage() {
|
|||||||
);
|
);
|
||||||
const [showWorkflowEditor, setShowWorkflowEditor] = useState(false);
|
const [showWorkflowEditor, setShowWorkflowEditor] = useState(false);
|
||||||
|
|
||||||
|
// 使用 useRef 保存 interval 引用
|
||||||
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
// 组件卸载时清理 interval
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const startNewFlow = () => {
|
const startNewFlow = () => {
|
||||||
setShowWorkflowEditor(true);
|
setShowWorkflowEditor(true);
|
||||||
};
|
};
|
||||||
@@ -310,8 +323,14 @@ export default function OrchestrationPage() {
|
|||||||
|
|
||||||
setExecutions([newExecution, ...executions]);
|
setExecutions([newExecution, ...executions]);
|
||||||
|
|
||||||
|
// 清理旧的 interval
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟执行进度
|
// 模拟执行进度
|
||||||
const interval = setInterval(() => {
|
intervalRef.current = setInterval(() => {
|
||||||
setExecutions((prev) =>
|
setExecutions((prev) =>
|
||||||
prev.map((exec) => {
|
prev.map((exec) => {
|
||||||
if (exec.id === newExecution.id) {
|
if (exec.id === newExecution.id) {
|
||||||
@@ -319,6 +338,11 @@ export default function OrchestrationPage() {
|
|||||||
exec.progress + Math.random() * 10,
|
exec.progress + Math.random() * 10,
|
||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
// 进度完成时清理 interval
|
||||||
|
if (newProgress >= 100 && intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...exec,
|
...exec,
|
||||||
progress: newProgress,
|
progress: newProgress,
|
||||||
@@ -334,8 +358,6 @@ export default function OrchestrationPage() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
setTimeout(() => clearInterval(interval), 10000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusIcon = (status: string) => {
|
const getStatusIcon = (status: string) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useRef } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Select,
|
Select,
|
||||||
@@ -24,6 +24,7 @@ export default function InstructionTemplateCreate() {
|
|||||||
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
|
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
|
||||||
const [variables, setVariables] = useState<string[]>([]);
|
const [variables, setVariables] = useState<string[]>([]);
|
||||||
const variableInputRef = useRef<Input | null>(null);
|
const variableInputRef = useRef<Input | null>(null);
|
||||||
|
const testTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
@@ -70,12 +71,27 @@ export default function InstructionTemplateCreate() {
|
|||||||
setVariables(variables.filter((_, i) => i !== index));
|
setVariables(variables.filter((_, i) => i !== index));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 组件卸载时清理 timeout
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (testTimeoutRef.current) {
|
||||||
|
clearTimeout(testTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 测试模板
|
// 测试模板
|
||||||
const handleTestTemplate = async () => {
|
const handleTestTemplate = async () => {
|
||||||
const values = form.getFieldsValue();
|
const values = form.getFieldsValue();
|
||||||
if (!values.prompt || !values.testInput) return;
|
if (!values.prompt || !values.testInput) return;
|
||||||
|
|
||||||
|
// 清理旧的 timeout
|
||||||
|
if (testTimeoutRef.current) {
|
||||||
|
clearTimeout(testTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
setIsTestingTemplate(true);
|
setIsTestingTemplate(true);
|
||||||
setTimeout(() => {
|
testTimeoutRef.current = setTimeout(() => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
"testOutput",
|
"testOutput",
|
||||||
`基于输入"${values.testInput}"生成的测试结果:
|
`基于输入"${values.testInput}"生成的测试结果:
|
||||||
|
|||||||
Reference in New Issue
Block a user