重构路由架构,修复导致的前端出错

This commit is contained in:
2026-04-06 00:52:04 +08:00
parent 7a62139683
commit e8dedb5ec4
12 changed files with 2055 additions and 1102 deletions

View File

@@ -3,46 +3,46 @@ import { subscribeWithSelector } from 'zustand/middleware';
const useChatBoxStore = create(
subscribeWithSelector((set, get) => ({
// 聊天历史消息列表
messages: [],
// 聊天历史消息列表
messages: [],
// 用户名称
userName: '',
// 用户名称
userName: '',
// 角色名称
characterName: '',
// 角色名称
characterName: '',
// 当前选中的角色
currentRole: null,
// 当前选中的角色
currentRole: null,
// 当前选中的聊天
currentChat: null,
// 当前选中的聊天
currentChat: null,
// 是否正在加载
isLoading: false,
// 是否正在加载
isLoading: false,
// 是否正在生成
isGenerating: false,
// 是否正在生成
isGenerating: false,
// 错误信息
error: null,
// 错误信息
error: null,
// 设置消息列表
setMessages: (messages) => set({ messages }),
// 设置消息列表
setMessages: (messages) => set({ messages }),
// 设置用户名称
setUserName: (userName) => set({ userName }),
// 设置用户名称
setUserName: (userName) => set({ userName }),
// 设置角色名称
setCharacterName: (characterName) => set({ characterName }),
// 设置角色名称
setCharacterName: (characterName) => set({ characterName }),
// 设置当前角色
setCurrentRole: (role) => set({ currentRole: role }),
// 设置当前角色
setCurrentRole: (role) => set({ currentRole: role }),
// 设置当前聊天
setCurrentChat: (chat) => set({ currentChat: chat }),
// 设置当前聊天
setCurrentChat: (chat) => set({ currentChat: chat }),
// 同时设置角色和聊天
// 同时设置角色和聊天
setChatBoxRoleAndChat: (role, chat) => {
console.log('[ChatBoxStore] setChatBoxRoleAndChat called with:', { role, chat });
set({
@@ -51,99 +51,207 @@ const useChatBoxStore = create(
});
},
// 设置生成状态
setIsGenerating: (status) => set({ isGenerating: status }),
// 设置生成状态
setIsGenerating: (status) => set({ isGenerating: status }),
// 发送消息
sendMessage: async (content) => {
const { messages, userName, characterName, currentRole, currentChat } = get();
sendMessage: async (content) => {
const { messages, userName, characterName, currentRole, currentChat } = get();
set({
isGenerating: true,
messages: [...messages, {
id: Date.now(),
floor: messages.length + 1,
mes: content,
is_user: true
}]
});
set({
isGenerating: true,
messages: [...messages, {
id: Date.now(),
floor: messages.length + 1,
mes: content,
is_user: true
}]
});
try {
const response = await fetch(`/api/chats/${encodeURIComponent(currentRole)}/${encodeURIComponent(currentChat)}/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
floor: messages.length + 1,
mes: content,
is_user: true
})
});
try {
const response = await fetch('/api/chat_box/send_message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
role_name: currentRole,
chat_name: currentChat,
message: content
})
});
if (!response.ok) {
throw new Error('Failed to send message');
}
if (!response.ok) {
throw new Error('Failed to send message');
}
const data = await response.json();
set((state) => ({
messages: [...state.messages, {
id: Date.now(),
floor: state.messages.length + 1,
mes: data.response,
is_user: false
}],
isGenerating: false
}));
} catch (error) {
set({
error: error.message,
isGenerating: false
});
}
},
const data = await response.json();
set((state) => ({
messages: [...state.messages, {
id: Date.now(),
floor: state.messages.length + 1,
mes: data.response,
is_user: false
}],
isGenerating: false
}));
} catch (error) {
set({
error: error.message,
isGenerating: false
});
}
},
// 终止生成
stopGeneration: () => set({ isGenerating: false }),
// 终止生成
stopGeneration: () => set({ isGenerating: false }),
// 加载聊天历史
fetchChatHistory: async (roleName, chatName) => {
set({ isLoading: true, error: null });
try {
const response = await fetch(`/api/chat_box/get_chat_history?role_name=${encodeURIComponent(roleName)}&chat_name=${encodeURIComponent(chatName)}`);
if (!response.ok) {
throw new Error('Failed to fetch chat history');
}
const data = await response.json();
// 修改数据处理逻辑适配API返回的数据结构
set({
messages: data || [], // 直接使用返回的数组
userName: 'User', // 固定用户名
characterName: roleName || 'Assistant', // 使用角色名作为角色名称
isLoading: false
});
} catch (error) {
set({
error: error.message,
isLoading: false
});
}
},
// 加载聊天历史
fetchChatHistory: async (roleName, chatName) => {
set({ isLoading: true, error: null });
try {
const response = await fetch(`/api/chats/${encodeURIComponent(roleName)}/${encodeURIComponent(chatName)}`);
if (!response.ok) {
throw new Error('Failed to fetch chat history');
}
const data = await response.json();
set({
messages: data.messages || [],
userName: data.metadata?.user_name || 'User',
characterName: data.metadata?.character_name || roleName || 'Assistant',
isLoading: false
});
} catch (error) {
set({
error: error.message,
isLoading: false
});
}
},
// 清空聊天历史
clearChatHistory: () => set({
messages: [],
userName: '',
characterName: '',
error: null
}),
// 清空聊天历史
clearChatHistory: () => set({
messages: [],
userName: '',
characterName: '',
error: null
}),
// 更新特定消息的内容
updateMessage: (id, content) => set((state) => ({
messages: state.messages.map((msg) =>
msg.id === id ? { ...msg, content } : msg
)
})),
// 更新特定消息的内容
updateMessage: async (floor, content) => {
const { currentRole, currentChat } = get();
try {
const response = await fetch(`/api/chats/${encodeURIComponent(currentRole)}/${encodeURIComponent(currentChat)}/messages/${floor}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ mes: content })
});
if (!response.ok) {
throw new Error('Failed to update message');
}
set((state) => ({
messages: state.messages.map((msg) =>
msg.floor === floor ? { ...msg, mes: content } : msg
)
}));
} catch (error) {
set({ error: error.message });
}
},
// 删除特定消息
deleteMessage: async (floor) => {
const { currentRole, currentChat } = get();
try {
const response = await fetch(`/api/chats/${encodeURIComponent(currentRole)}/${encodeURIComponent(currentChat)}/messages/${floor}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('Failed to delete message');
}
set((state) => ({
messages: state.messages.filter((msg) => msg.floor !== floor)
}));
} catch (error) {
set({ error: error.message });
}
},
// 创建新聊天
createChat: async (roleName, chatName, metadata = {}) => {
try {
const response = await fetch(`/api/chats/${encodeURIComponent(roleName)}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_name: chatName,
metadata: {
user_name: 'User',
character_name: roleName,
...metadata
}
})
});
if (!response.ok) {
throw new Error('Failed to create chat');
}
return await response.json();
} catch (error) {
set({ error: error.message });
throw error;
}
},
// 更新聊天元数据
updateChatMetadata: async (roleName, chatName, metadata) => {
try {
const response = await fetch(`/api/chats/${encodeURIComponent(roleName)}/${encodeURIComponent(chatName)}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ metadata })
});
if (!response.ok) {
throw new Error('Failed to update chat metadata');
}
return await response.json();
} catch (error) {
set({ error: error.message });
throw error;
}
},
// 删除聊天
deleteChat: async (roleName, chatName) => {
try {
const response = await fetch(`/api/chats/${encodeURIComponent(roleName)}/${encodeURIComponent(chatName)}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('Failed to delete chat');
}
return await response.json();
} catch (error) {
set({ error: error.message });
throw error;
}
}
}))
);
@@ -151,20 +259,17 @@ const useChatBoxStore = create(
useChatBoxStore.subscribe(
(state) => ({ role: state.currentRole, chat: state.currentChat }),
({ role, chat }, prev) => {
// 只有当角色或聊天发生变化时才处理
if (role !== prev.role || chat !== prev.chat) {
// 确保角色和聊天都存在且不为null
if (role && chat) {
useChatBoxStore.getState().fetchChatHistory(role, chat);
} else {
useChatBoxStore.getState().clearChatHistory();
}
}
// 只有当角色或聊天发生变化时才处理
if (role !== prev.role || chat !== prev.chat) {
// 确保角色和聊天都存在且不为null
if (role && chat) {
useChatBoxStore.getState().fetchChatHistory(role, chat);
} else {
useChatBoxStore.getState().clearChatHistory();
}
}
},
{ equalityFn: (a, b) => a.role === b.role && a.chat === b.chat }
);
export default useChatBoxStore;

View File

@@ -100,14 +100,16 @@ const usePresetStore = create((set, get) => ({
fetchPresets: async () => {
set({ isLoadingPresets: true });
try {
const response = await fetch('/api/presets/list');
const response = await fetch('/api/presets');
const data = await response.json();
// 转换为预设对象数组
const presetList = data.presets.map(name => ({
id: name,
name,
parameters: {} // 参数将在选择预设时加载
const presetList = data.presets.map(preset => ({
id: preset.name,
name: preset.name,
description: preset.description,
component_count: preset.component_count,
temperature: preset.temperature
}));
set({ presets: presetList, isLoadingPresets: false });
@@ -150,14 +152,19 @@ const usePresetStore = create((set, get) => ({
// 处理预设组件
let components = [];
if (presetData.prompts && Array.isArray(presetData.prompts)) {
// 获取当前角色的prompt_order
const currentOrder = presetData.prompt_order && presetData.prompt_order.length > 0
// 获取当前角色的prompt_order,添加更严格的检查
const currentOrder = (presetData.prompt_order &&
Array.isArray(presetData.prompt_order) &&
presetData.prompt_order.length > 0 &&
presetData.prompt_order[0] &&
presetData.prompt_order[0].order &&
Array.isArray(presetData.prompt_order[0].order))
? presetData.prompt_order[0].order
: [];
// 根据prompt_order排序组件
components = presetData.prompts.map(prompt => {
const orderItem = currentOrder.find(item => item.identifier === prompt.identifier);
const orderItem = currentOrder.find(item => item && item.identifier === prompt.identifier);
return {
...prompt,
enabled: orderItem ? orderItem.enabled : true,
@@ -168,13 +175,14 @@ const usePresetStore = create((set, get) => ({
// 如果有prompt_order按照它排序
if (currentOrder.length > 0) {
components.sort((a, b) => {
const indexA = currentOrder.findIndex(item => item.identifier === a.identifier);
const indexB = currentOrder.findIndex(item => item.identifier === b.identifier);
const indexA = currentOrder.findIndex(item => item && item.identifier === a.identifier);
const indexB = currentOrder.findIndex(item => item && item.identifier === b.identifier);
return indexA - indexB;
});
}
}
// 更新状态,确保参数容器展开
set({
selectedPreset: presetId,
@@ -187,8 +195,6 @@ const usePresetStore = create((set, get) => ({
}
},
// 更新参数
updateParameter: ({ name, value }) => set((state) => ({
parameters: { ...state.parameters, [name]: value }
@@ -200,25 +206,97 @@ const usePresetStore = create((set, get) => ({
})),
// 保存当前设置为预设
saveCurrentAsPreset: ({ name }) => set((state) => {
const newPreset = {
id: `preset_${Date.now()}`,
name,
parameters: { ...state.parameters },
promptComponents: [...state.promptComponents]
};
return {
presets: [...state.presets, newPreset],
selectedPreset: newPreset.id
};
}),
saveCurrentAsPreset: async ({ name }) => {
const state = get();
try {
// 构建预设数据
const presetData = {
...state.parameters,
prompts: state.promptComponents.map(component => ({
identifier: component.identifier,
name: component.name,
content: component.content || '',
role: component.role,
system_prompt: component.system_prompt,
marker: component.marker
})),
prompt_order: [{
order: state.promptComponents.map(component => ({
identifier: component.identifier,
enabled: component.enabled !== false
}))
}]
};
// 发送到后端
const response = await fetch('/api/presets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
preset_name: name,
...presetData
})
});
if (!response.ok) {
throw new Error('Failed to save preset');
}
const result = await response.json();
// 添加到本地预设列表
const newPreset = {
id: name,
name,
description: '',
component_count: state.promptComponents.length,
temperature: state.parameters.temperature
};
set((state) => ({
presets: [...state.presets, newPreset],
selectedPreset: name
}));
return result;
} catch (error) {
console.error('Failed to save preset:', error);
throw error;
}
},
// 编辑预设名称
editPresetName: (presetId, newName) => set((state) => ({
presets: state.presets.map(preset =>
preset.id === presetId ? { ...preset, name: newName } : preset
)
})),
editPresetName: async (presetId, newName) => {
try {
const response = await fetch(`/api/presets/${presetId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: newName
})
});
if (!response.ok) {
throw new Error('Failed to update preset name');
}
// 更新本地状态
set((state) => ({
presets: state.presets.map(preset =>
preset.id === presetId ? { ...preset, name: newName } : preset
)
}));
return await response.json();
} catch (error) {
console.error('Failed to update preset name:', error);
throw error;
}
},
// 切换参数设置折叠状态
toggleParametersExpanded: () => set((state) => ({

View File

@@ -3,7 +3,7 @@ import { create } from 'zustand';
// 异步获取角色数据
const fetchRoleData = async () => {
try {
const response = await fetch('/api/tool_bar/get_all_role_and_chat', {
const response = await fetch('/api/chats', {
method: 'GET',
headers: {
'Cache-Control': 'no-cache',
@@ -12,7 +12,19 @@ const fetchRoleData = async () => {
}
});
const data = await response.json();
return data;
// 转换数据格式以适应前端需求
const roleData = {};
if (data.chats && Array.isArray(data.chats)) {
data.chats.forEach(chat => {
if (!roleData[chat.role_name]) {
roleData[chat.role_name] = [];
}
roleData[chat.role_name].push(chat.chat_name);
});
}
return roleData;
} catch (error) {
console.error('获取角色数据失败:', error);
throw error;
@@ -66,52 +78,92 @@ const useRoleSelectorStore = create((set, get) => ({
// 同时更新角色和聊天
setSelectedRoleAndChat: (role, chat) => set({ selectedRole: role, selectedChat: chat }),
handleRenameRole: (oldName, newName) => {
// 处理角色重命名
handleRenameRole: async (oldName, newName) => {
const { roleData, selectedRole } = get();
if (newName && newName !== oldName) {
const newRoleData = { ...roleData };
const chats = newRoleData[oldName];
delete newRoleData[oldName];
newRoleData[newName] = chats;
try {
// 获取该角色下的所有聊天
const chats = roleData[oldName] || [];
if (selectedRole === oldName) {
set({
roleData: newRoleData,
selectedRole: newName,
editingRole: null
});
} else {
set({
roleData: newRoleData,
editingRole: null
});
// 为每个聊天更新元数据中的角色名称
for (const chatName of chats) {
await fetch(`/api/chats/${encodeURIComponent(oldName)}/${encodeURIComponent(chatName)}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
metadata: { character_name: newName }
})
});
}
// 更新本地状态
const newRoleData = { ...roleData };
newRoleData[newName] = chats;
delete newRoleData[oldName];
if (selectedRole === oldName) {
set({
roleData: newRoleData,
selectedRole: newName,
editingRole: null
});
} else {
set({
roleData: newRoleData,
editingRole: null
});
}
} catch (error) {
console.error('重命名角色失败:', error);
set({ editingRole: null });
}
} else {
set({ editingRole: null });
}
},
handleRenameChat: (oldName, newName) => {
// 处理聊天重命名
handleRenameChat: async (oldName, newName) => {
const { roleData, selectedRole, selectedChat } = get();
if (newName && newName !== oldName) {
const newRoleData = { ...roleData };
const chatIndex = newRoleData[selectedRole].indexOf(oldName);
if (chatIndex !== -1) {
newRoleData[selectedRole][chatIndex] = newName;
try {
// 更新后端聊天名称
await fetch(`/api/chats/${encodeURIComponent(selectedRole)}/${encodeURIComponent(oldName)}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
metadata: { chat_name: newName }
})
});
if (selectedChat === oldName) {
set({
roleData: newRoleData,
selectedChat: newName,
editingChat: null
});
// 更新本地状态
const newRoleData = { ...roleData };
const chatIndex = newRoleData[selectedRole].indexOf(oldName);
if (chatIndex !== -1) {
newRoleData[selectedRole][chatIndex] = newName;
if (selectedChat === oldName) {
set({
roleData: newRoleData,
selectedChat: newName,
editingChat: null
});
} else {
set({
roleData: newRoleData,
editingChat: null
});
}
} else {
set({
roleData: newRoleData,
editingChat: null
});
set({ editingChat: null });
}
} else {
} catch (error) {
console.error('重命名聊天失败:', error);
set({ editingChat: null });
}
} else {
@@ -119,34 +171,27 @@ const useRoleSelectorStore = create((set, get) => ({
}
},
confirmDelete: () => {
// 确认删除
confirmDelete: async () => {
const { roleData, selectedRole, selectedChat, showDeleteConfirm, deleteType } = get();
const newRoleData = { ...roleData };
if (deleteType === 'role') {
delete newRoleData[showDeleteConfirm];
if (selectedRole === showDeleteConfirm) {
set({
roleData: newRoleData,
selectedRole: null,
selectedChat: null,
showDeleteConfirm: null,
deleteType: null
});
} else {
set({
roleData: newRoleData,
showDeleteConfirm: null,
deleteType: null
});
}
} else if (deleteType === 'chat') {
const chatIndex = newRoleData[selectedRole].indexOf(showDeleteConfirm);
if (chatIndex !== -1) {
newRoleData[selectedRole].splice(chatIndex, 1);
if (selectedChat === showDeleteConfirm) {
try {
if (deleteType === 'role') {
// 删除角色下的所有聊天
const chats = roleData[showDeleteConfirm] || [];
for (const chatName of chats) {
await fetch(`/api/chats/${encodeURIComponent(showDeleteConfirm)}/${encodeURIComponent(chatName)}`, {
method: 'DELETE'
});
}
const newRoleData = { ...roleData };
delete newRoleData[showDeleteConfirm];
if (selectedRole === showDeleteConfirm) {
set({
roleData: newRoleData,
selectedRole: null,
selectedChat: null,
showDeleteConfirm: null,
deleteType: null
@@ -158,41 +203,116 @@ const useRoleSelectorStore = create((set, get) => ({
deleteType: null
});
}
} else {
set({
showDeleteConfirm: null,
deleteType: null
} else if (deleteType === 'chat') {
// 删除单个聊天
await fetch(`/api/chats/${encodeURIComponent(selectedRole)}/${encodeURIComponent(showDeleteConfirm)}`, {
method: 'DELETE'
});
const newRoleData = { ...roleData };
const chatIndex = newRoleData[selectedRole].indexOf(showDeleteConfirm);
if (chatIndex !== -1) {
newRoleData[selectedRole].splice(chatIndex, 1);
if (selectedChat === showDeleteConfirm) {
set({
roleData: newRoleData,
selectedChat: null,
showDeleteConfirm: null,
deleteType: null
});
} else {
set({
roleData: newRoleData,
showDeleteConfirm: null,
deleteType: null
});
}
} else {
set({
showDeleteConfirm: null,
deleteType: null
});
}
}
} catch (error) {
console.error('删除失败:', error);
set({
showDeleteConfirm: null,
deleteType: null
});
}
},
// 取消删除
cancelDelete: () => set({ showDeleteConfirm: null, deleteType: null }),
handleAddRole: () => {
// 添加新角色
handleAddRole: async () => {
const { roleData } = get();
const newRole = '新角色';
const newRoleData = { ...roleData };
newRoleData[newRole] = [];
set({
roleData: newRoleData,
editingRole: newRole
});
try {
// 创建新角色(通过创建一个默认聊天)
await fetch(`/api/chats/${encodeURIComponent(newRole)}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_name: '默认聊天',
metadata: {
user_name: 'User',
character_name: newRole
}
})
});
const newRoleData = { ...roleData };
newRoleData[newRole] = ['默认聊天'];
set({
roleData: newRoleData,
editingRole: newRole
});
} catch (error) {
console.error('添加角色失败:', error);
}
},
handleAddChat: () => {
// 添加新聊天
handleAddChat: async () => {
const { roleData, selectedRole } = get();
if (!selectedRole) return;
const newChat = '新聊天';
const newRoleData = { ...roleData };
newRoleData[selectedRole].push(newChat);
set({
roleData: newRoleData,
editingChat: newChat
});
try {
await fetch(`/api/chats/${encodeURIComponent(selectedRole)}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_name: newChat,
metadata: {
user_name: 'User',
character_name: selectedRole
}
})
});
const newRoleData = { ...roleData };
newRoleData[selectedRole].push(newChat);
set({
roleData: newRoleData,
editingChat: newChat
});
} catch (error) {
console.error('添加聊天失败:', error);
}
},
// 重置面板状态
resetPanel: () => set({
hoveredRole: null,
clickedRole: null,