first-update
This commit is contained in:
14
easy-dataset-main/hooks/useDebounce.js
Normal file
14
easy-dataset-main/hooks/useDebounce.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function useDebounce(value, delay = 500) {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [value, delay]);
|
||||
return debouncedValue;
|
||||
}
|
||||
57
easy-dataset-main/hooks/useFileProcessingStatus.js
Normal file
57
easy-dataset-main/hooks/useFileProcessingStatus.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
// 存储文件处理状态的共享对象
|
||||
const fileProcessingSubscribers = {
|
||||
value: false,
|
||||
listeners: new Set()
|
||||
};
|
||||
|
||||
// 存储文件任务信息的共享对象
|
||||
const fileTaskSubscribers = {
|
||||
value: null,
|
||||
listeners: new Set()
|
||||
};
|
||||
|
||||
/**
|
||||
* 自定义hook,用于在组件间共享文件处理任务的状态
|
||||
*/
|
||||
export default function useFileProcessingStatus() {
|
||||
const [taskFileProcessing, setTaskFileProcessing] = useState(fileProcessingSubscribers.value);
|
||||
const [task, setTask] = useState(fileTaskSubscribers.value);
|
||||
|
||||
useEffect(() => {
|
||||
// 添加当前组件为订阅者
|
||||
const updateProcessingState = newValue => setTaskFileProcessing(newValue);
|
||||
const updateTaskState = newTask => setTask(newTask);
|
||||
|
||||
fileProcessingSubscribers.listeners.add(updateProcessingState);
|
||||
fileTaskSubscribers.listeners.add(updateTaskState);
|
||||
|
||||
// 组件卸载时清理
|
||||
return () => {
|
||||
fileProcessingSubscribers.listeners.delete(updateProcessingState);
|
||||
fileTaskSubscribers.listeners.delete(updateTaskState);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 共享的setState函数
|
||||
const setSharedFileProcessing = newValue => {
|
||||
fileProcessingSubscribers.value = newValue;
|
||||
// 通知所有订阅者
|
||||
fileProcessingSubscribers.listeners.forEach(listener => listener(newValue));
|
||||
};
|
||||
|
||||
// 共享的setTask函数
|
||||
const setSharedTask = newTask => {
|
||||
fileTaskSubscribers.value = newTask;
|
||||
// 通知所有订阅者
|
||||
fileTaskSubscribers.listeners.forEach(listener => listener(newTask));
|
||||
};
|
||||
|
||||
return {
|
||||
taskFileProcessing,
|
||||
task,
|
||||
setTaskFileProcessing: setSharedFileProcessing,
|
||||
setTask: setSharedTask
|
||||
};
|
||||
}
|
||||
135
easy-dataset-main/hooks/useGenerateDataset.js
Normal file
135
easy-dataset-main/hooks/useGenerateDataset.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import { useCallback } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import i18n from '@/lib/i18n';
|
||||
import axios from 'axios';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
import { selectedModelInfoAtom } from '@/lib/store';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function useGenerateDataset() {
|
||||
const model = useAtomValue(selectedModelInfoAtom);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const generateSingleDataset = useCallback(
|
||||
async ({ projectId, questionId, questionInfo, imageId, imageName }) => {
|
||||
// 获取模型参数
|
||||
if (!model) {
|
||||
toast.error(t('models.configNotFound'));
|
||||
return null;
|
||||
}
|
||||
|
||||
// 判断是否为图片问题
|
||||
const isImageQuestion = !!imageId;
|
||||
|
||||
// 调用API生成数据集
|
||||
const currentLanguage = i18n.language === 'zh-CN' ? '中文' : 'en';
|
||||
|
||||
if (isImageQuestion) {
|
||||
// 图片问题:调用图片数据集生成接口
|
||||
toast.promise(
|
||||
axios.post(`/api/projects/${projectId}/images/datasets`, {
|
||||
imageName,
|
||||
question: { question: questionInfo, id: questionId },
|
||||
model,
|
||||
language: currentLanguage
|
||||
}),
|
||||
{
|
||||
loading: t('datasets.generating'),
|
||||
description: `图片:【${imageName}】\n问题:【${questionInfo}】`,
|
||||
position: 'top-right',
|
||||
success: data => {
|
||||
return '生成数据集成功';
|
||||
},
|
||||
error: error => {
|
||||
return t('datasets.generateFailed', { error: error.response?.data?.error });
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 文本问题:调用普通数据集生成接口
|
||||
toast.promise(
|
||||
axios.post(`/api/projects/${projectId}/datasets`, {
|
||||
questionId,
|
||||
model,
|
||||
language: currentLanguage
|
||||
}),
|
||||
{
|
||||
loading: t('datasets.generating'),
|
||||
description: `问题:【${questionInfo}】`,
|
||||
position: 'top-right',
|
||||
success: data => {
|
||||
return '生成数据集成功';
|
||||
},
|
||||
error: error => {
|
||||
return t('datasets.generateFailed', { error: error.response?.data?.error });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
[model, t]
|
||||
);
|
||||
|
||||
const generateMultipleDataset = useCallback(
|
||||
async (projectId, questions) => {
|
||||
let completed = 0;
|
||||
const total = questions.length;
|
||||
// 显示带进度的Loading
|
||||
const loadingToastId = toast.loading(`正在处理请求 (${completed}/${total})...`, { position: 'top-right' });
|
||||
|
||||
// 处理每个请求
|
||||
const processRequest = async question => {
|
||||
try {
|
||||
const isImageQuestion = !!question.imageId;
|
||||
let response;
|
||||
|
||||
if (isImageQuestion) {
|
||||
// 图片问题
|
||||
response = await axios.post(`/api/projects/${projectId}/images/datasets`, {
|
||||
imageName: question.imageName,
|
||||
question,
|
||||
model,
|
||||
language: i18n.language === 'zh-CN' ? '中文' : 'en'
|
||||
});
|
||||
} else {
|
||||
// 文本问题
|
||||
response = await axios.post(`/api/projects/${projectId}/datasets`, {
|
||||
questionId: question.id,
|
||||
model,
|
||||
language: i18n.language === 'zh-CN' ? '中文' : 'en'
|
||||
});
|
||||
}
|
||||
|
||||
const data = response.data;
|
||||
completed++;
|
||||
toast.success(`${question.question} 完成`, { position: 'top-right' });
|
||||
toast.loading(`正在处理请求 (${completed}/${total})...`, { id: loadingToastId });
|
||||
return data;
|
||||
} catch (error) {
|
||||
completed++;
|
||||
toast.error(`${question.question} 失败`, {
|
||||
description: error.message,
|
||||
position: 'top-right'
|
||||
});
|
||||
toast.loading(`正在处理请求 (${completed}/${total})...`, { id: loadingToastId });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const results = await Promise.allSettled(questions.map(req => processRequest(req)));
|
||||
// 全部完成后更新Loading为完成状态
|
||||
toast.success(`全部请求处理完成 (成功: ${results.filter(r => r.status === 'fulfilled').length}/${total})`, {
|
||||
id: loadingToastId,
|
||||
position: 'top-right'
|
||||
});
|
||||
return results;
|
||||
} catch {
|
||||
// Promise.allSettled不会进入catch,这里只是保险
|
||||
}
|
||||
},
|
||||
[model, t]
|
||||
);
|
||||
|
||||
return { generateSingleDataset, generateMultipleDataset };
|
||||
}
|
||||
406
easy-dataset-main/hooks/useModelPlayground.js
Normal file
406
easy-dataset-main/hooks/useModelPlayground.js
Normal file
@@ -0,0 +1,406 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useAtomValue } from 'jotai/index';
|
||||
import { modelConfigListAtom } from '@/lib/store';
|
||||
|
||||
export default function useModelPlayground(projectId, defaultModelId = null) {
|
||||
// 状态管理
|
||||
const [selectedModels, setSelectedModels] = useState(defaultModelId ? [defaultModelId] : []);
|
||||
const [loading, setLoading] = useState({});
|
||||
const [userInput, setUserInput] = useState('');
|
||||
const [conversations, setConversations] = useState({});
|
||||
const [error, setError] = useState(null);
|
||||
const [outputMode, setOutputMode] = useState('normal'); // 'normal' 或 'streaming'
|
||||
const [uploadedImage, setUploadedImage] = useState(null); // 存储上传的图片Base64
|
||||
|
||||
const availableModels = useAtomValue(modelConfigListAtom);
|
||||
|
||||
// 初始化会话状态
|
||||
useEffect(() => {
|
||||
if (selectedModels.length > 0) {
|
||||
const initialConversations = {};
|
||||
selectedModels.forEach(modelId => {
|
||||
if (!conversations[modelId]) {
|
||||
initialConversations[modelId] = [];
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(initialConversations).length > 0) {
|
||||
setConversations(prev => ({
|
||||
...prev,
|
||||
...initialConversations
|
||||
}));
|
||||
}
|
||||
}
|
||||
}, [selectedModels]);
|
||||
|
||||
// 处理模型选择
|
||||
const handleModelSelection = event => {
|
||||
const {
|
||||
target: { value }
|
||||
} = event;
|
||||
|
||||
// 限制最多选择 3 个模型
|
||||
const selectedValues = typeof value === 'string' ? value.split(',') : value;
|
||||
const limitedSelection = selectedValues.slice(0, 3);
|
||||
|
||||
setSelectedModels(limitedSelection);
|
||||
};
|
||||
|
||||
// 处理用户输入
|
||||
const handleInputChange = e => {
|
||||
setUserInput(e.target.value);
|
||||
};
|
||||
|
||||
// 处理图片上传
|
||||
const handleImageUpload = e => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setUploadedImage(reader.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除已上传的图片
|
||||
const handleRemoveImage = () => {
|
||||
setUploadedImage(null);
|
||||
};
|
||||
|
||||
// 处理输出模式切换
|
||||
const handleOutputModeChange = event => {
|
||||
setOutputMode(event.target.value);
|
||||
};
|
||||
|
||||
// 发送消息给所有选中的模型
|
||||
const handleSendMessage = async () => {
|
||||
if (!userInput.trim() || Object.values(loading).some(value => value) || selectedModels.length === 0) return;
|
||||
|
||||
// 获取用户输入
|
||||
const input = userInput.trim();
|
||||
setUserInput('');
|
||||
|
||||
// 获取图片(如果有的话)
|
||||
const image = uploadedImage;
|
||||
setUploadedImage(null); // 清除图片
|
||||
|
||||
// 更新所有选中模型的对话
|
||||
const updatedConversations = { ...conversations };
|
||||
selectedModels.forEach(modelId => {
|
||||
if (!updatedConversations[modelId]) {
|
||||
updatedConversations[modelId] = [];
|
||||
}
|
||||
// 检查是否有图片并且当前模型是视觉模型
|
||||
const model = availableModels.find(m => m.id === modelId);
|
||||
const isVisionModel = model && model.type === 'vision';
|
||||
|
||||
if (isVisionModel && image) {
|
||||
// 如果是视觉模型并且有图片,使用复合格式
|
||||
updatedConversations[modelId].push({
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: input || '请描述这个图片' },
|
||||
{ type: 'image_url', image_url: { url: image } }
|
||||
]
|
||||
});
|
||||
} else {
|
||||
// 其他情况使用纯文本
|
||||
updatedConversations[modelId].push({
|
||||
role: 'user',
|
||||
content: input
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setConversations(updatedConversations);
|
||||
|
||||
// 为每个模型设置独立的加载状态
|
||||
const updatedLoading = {};
|
||||
selectedModels.forEach(modelId => {
|
||||
updatedLoading[modelId] = true;
|
||||
});
|
||||
setLoading(updatedLoading);
|
||||
|
||||
// 为每个模型单独发送请求
|
||||
selectedModels.forEach(async modelId => {
|
||||
const model = availableModels.find(m => m.id === modelId);
|
||||
if (!model) {
|
||||
// 模型配置不存在
|
||||
const modelConversation = [...(updatedConversations[modelId] || [])];
|
||||
|
||||
// 更新对话状态
|
||||
setConversations(prev => ({
|
||||
...prev,
|
||||
[modelId]: [...modelConversation, { role: 'error', content: '模型配置不存在' }]
|
||||
}));
|
||||
|
||||
// 更新加载状态
|
||||
setLoading(prev => ({ ...prev, [modelId]: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查是否是视觉模型且有图片
|
||||
const isVisionModel = model.type === 'vision';
|
||||
|
||||
// 构建请求消息
|
||||
let requestMessages = [...updatedConversations[modelId]]; // 复制当前消息历史
|
||||
|
||||
// 如果是vision模型并且有图片,将最后一条用户消息替换为包含图片的消息
|
||||
if (isVisionModel && image && requestMessages.length > 0) {
|
||||
// 找到最后一条用户消息
|
||||
const lastUserMsgIndex = requestMessages.length - 1;
|
||||
// 替换为包含图片的消息
|
||||
requestMessages[lastUserMsgIndex] = {
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: input || '请描述这个图片' },
|
||||
{ type: 'image_url', image_url: { url: image } }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// 根据输出模式选择不同的处理方式
|
||||
if (outputMode === 'streaming') {
|
||||
// 流式输出处理
|
||||
// 先添加一个空的助手回复,用于后续流式更新
|
||||
setConversations(prev => {
|
||||
const modelConversation = [...(prev[modelId] || [])];
|
||||
return {
|
||||
...prev,
|
||||
[modelId]: [
|
||||
...modelConversation,
|
||||
{
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
isStreaming: true,
|
||||
thinking: '', // 添加推理过程字段
|
||||
showThinking: true // 默认显示推理过程
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
const response = await fetch(`/api/projects/${projectId}/playground/chat/stream`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
messages: requestMessages
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let accumulatedContent = '';
|
||||
|
||||
// 状态变量,用于跟踪是否正在处理思维链
|
||||
let isInThinking = false;
|
||||
let currentThinking = '';
|
||||
let currentContent = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
// 解码收到的数据块
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
|
||||
// 处理当前数据块
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const char = chunk[i];
|
||||
|
||||
// 检测开始标签 <think>
|
||||
if (i + 6 <= chunk.length && chunk.substring(i, i + 7) === '<think>') {
|
||||
isInThinking = true;
|
||||
i += 6; // 跳过标签
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测结束标签 </think>
|
||||
if (i + 7 <= chunk.length && chunk.substring(i, i + 8) === '</think>') {
|
||||
isInThinking = false;
|
||||
i += 7; // 跳过标签
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据当前状态添加到对应内容中
|
||||
if (isInThinking) {
|
||||
currentThinking += char;
|
||||
} else {
|
||||
currentContent += char;
|
||||
}
|
||||
}
|
||||
|
||||
// 累积全部内容以便最终处理
|
||||
accumulatedContent += chunk;
|
||||
|
||||
// 更新对话内容
|
||||
setConversations(prev => {
|
||||
const modelConversation = [...prev[modelId]];
|
||||
const lastIndex = modelConversation.length - 1;
|
||||
|
||||
// 更新最后一条消息的内容,包括思维链
|
||||
modelConversation[lastIndex] = {
|
||||
...modelConversation[lastIndex],
|
||||
content: currentContent,
|
||||
thinking: currentThinking,
|
||||
showThinking: currentThinking.length > 0 // 只要有思维链内容就显示
|
||||
};
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[modelId]: modelConversation
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 完成流式传输,移除流式标记
|
||||
// 使用刚刚实时跟踪的 currentThinking 和 currentContent作为最终的思维链和内容
|
||||
let finalThinking = currentThinking;
|
||||
let finalAnswer = currentContent;
|
||||
|
||||
// 如果到流结束时还在思维链中,确保解析完整的思维链内容
|
||||
if (isInThinking) {
|
||||
console.log('警告: 流结束时仍在思维链中,可能有标签不完整');
|
||||
isInThinking = false;
|
||||
}
|
||||
|
||||
setConversations(prev => {
|
||||
const modelConversation = [...prev[modelId]];
|
||||
const lastIndex = modelConversation.length - 1;
|
||||
|
||||
// 更新最后一条消息,移除流式标记
|
||||
modelConversation[lastIndex] = {
|
||||
role: 'assistant',
|
||||
content: finalAnswer,
|
||||
thinking: finalThinking,
|
||||
showThinking: finalThinking ? true : false,
|
||||
isStreaming: false
|
||||
};
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[modelId]: modelConversation
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// 普通输出处理
|
||||
const response = await fetch(`/api/projects/${projectId}/playground/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: {
|
||||
...model,
|
||||
extra_body: { enable_thinking: true } // 启用思考链
|
||||
},
|
||||
messages: requestMessages
|
||||
})
|
||||
});
|
||||
|
||||
// 获取响应数据
|
||||
const data = await response.json();
|
||||
|
||||
// 独立更新此模型的对话状态
|
||||
setConversations(prev => {
|
||||
const modelConversation = [...(prev[modelId] || [])];
|
||||
|
||||
if (response.ok) {
|
||||
// 处理可能包含思考链的内容
|
||||
let thinking = '';
|
||||
let content = data.response;
|
||||
|
||||
// 检查是否包含思考链
|
||||
if (content && content.includes('<think>')) {
|
||||
const thinkParts = content.split(/<think>(.*?)<\/think>/s);
|
||||
if (thinkParts.length >= 3) {
|
||||
thinking = thinkParts[1] || '';
|
||||
// 移除思考链部分,只保留最终回答
|
||||
content = thinkParts.filter((_, i) => i % 2 === 0).join('');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[modelId]: [
|
||||
...modelConversation,
|
||||
{
|
||||
role: 'assistant',
|
||||
content: content,
|
||||
thinking: thinking,
|
||||
showThinking: thinking ? true : false
|
||||
}
|
||||
]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
[modelId]: [...modelConversation, { role: 'error', content: `错误: ${data.error || '请求失败'}` }]
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`请求模型 ${model.name} 失败:`, error);
|
||||
|
||||
// 独立更新此模型的对话状态 - 添加错误消息
|
||||
setConversations(prev => {
|
||||
const modelConversation = [...(prev[modelId] || [])];
|
||||
return {
|
||||
...prev,
|
||||
[modelId]: [...modelConversation, { role: 'error', content: `错误: ${error.message}` }]
|
||||
};
|
||||
});
|
||||
} finally {
|
||||
// 更新此模型的加载状态
|
||||
setLoading(prev => ({ ...prev, [modelId]: false }));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 清空所有对话
|
||||
const handleClearConversations = () => {
|
||||
const clearedConversations = {};
|
||||
selectedModels.forEach(modelId => {
|
||||
clearedConversations[modelId] = [];
|
||||
});
|
||||
setConversations(clearedConversations);
|
||||
setLoading({});
|
||||
};
|
||||
|
||||
// 获取模型名称
|
||||
const getModelName = modelId => {
|
||||
const model = availableModels.find(m => m.id === modelId);
|
||||
return model ? `${model.provider}: ${model.name}` : modelId;
|
||||
};
|
||||
|
||||
return {
|
||||
availableModels,
|
||||
selectedModels,
|
||||
loading,
|
||||
userInput,
|
||||
conversations,
|
||||
error,
|
||||
outputMode,
|
||||
uploadedImage,
|
||||
handleModelSelection,
|
||||
handleInputChange,
|
||||
handleImageUpload,
|
||||
handleRemoveImage,
|
||||
handleSendMessage,
|
||||
handleClearConversations,
|
||||
handleOutputModeChange,
|
||||
getModelName
|
||||
};
|
||||
}
|
||||
73
easy-dataset-main/hooks/useSnackbar.js
Normal file
73
easy-dataset-main/hooks/useSnackbar.js
Normal file
@@ -0,0 +1,73 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Snackbar, Alert } from '@mui/material';
|
||||
|
||||
export const useSnackbar = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [severity, setSeverity] = useState('info');
|
||||
|
||||
const showMessage = useCallback((newMessage, newSeverity = 'info') => {
|
||||
setMessage(newMessage);
|
||||
setSeverity(newSeverity);
|
||||
setOpen(true);
|
||||
}, []);
|
||||
|
||||
const showSuccess = useCallback(
|
||||
message => {
|
||||
showMessage(message, 'success');
|
||||
},
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
const showError = useCallback(
|
||||
message => {
|
||||
showMessage(message, 'error');
|
||||
},
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
const showInfo = useCallback(
|
||||
message => {
|
||||
showMessage(message, 'info');
|
||||
},
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
const showWarning = useCallback(
|
||||
message => {
|
||||
showMessage(message, 'warning');
|
||||
},
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false);
|
||||
}, []);
|
||||
|
||||
const SnackbarComponent = useCallback(
|
||||
() => (
|
||||
<Snackbar
|
||||
open={open}
|
||||
autoHideDuration={2000}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
||||
>
|
||||
<Alert onClose={handleClose} severity={severity}>
|
||||
{message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
),
|
||||
[open, message, severity, handleClose]
|
||||
);
|
||||
|
||||
return {
|
||||
showMessage,
|
||||
showSuccess,
|
||||
showError,
|
||||
showInfo,
|
||||
showWarning,
|
||||
SnackbarComponent
|
||||
};
|
||||
};
|
||||
63
easy-dataset-main/hooks/useTaskSettings.js
Normal file
63
easy-dataset-main/hooks/useTaskSettings.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DEFAULT_SETTINGS } from '@/constant/setting';
|
||||
|
||||
export default function useTaskSettings(projectId) {
|
||||
const { t } = useTranslation();
|
||||
const [taskSettings, setTaskSettings] = useState({
|
||||
...DEFAULT_SETTINGS
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchTaskSettings() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch(`/api/projects/${projectId}/tasks`);
|
||||
if (!response.ok) {
|
||||
throw new Error(t('settings.fetchTasksFailed'));
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 如果没有配置,使用默认值
|
||||
if (Object.keys(data).length === 0) {
|
||||
setTaskSettings({
|
||||
...DEFAULT_SETTINGS
|
||||
});
|
||||
} else {
|
||||
// 确保所有默认值都被正确设置,特别是数字类型的字段
|
||||
const mergedSettings = {
|
||||
...DEFAULT_SETTINGS,
|
||||
...data
|
||||
};
|
||||
|
||||
// 确保 multiTurnRounds 是数字类型
|
||||
if (mergedSettings.multiTurnRounds !== undefined) {
|
||||
mergedSettings.multiTurnRounds = Number(mergedSettings.multiTurnRounds);
|
||||
}
|
||||
|
||||
setTaskSettings(mergedSettings);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取任务配置出错:', error);
|
||||
setError(error.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchTaskSettings();
|
||||
}, [projectId, t]);
|
||||
|
||||
return {
|
||||
taskSettings,
|
||||
setTaskSettings,
|
||||
loading,
|
||||
error,
|
||||
success,
|
||||
setSuccess
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user