完成大量美化
This commit is contained in:
242
frontend/src/Store/SideBarLeft/CharacterSlice.jsx
Normal file
242
frontend/src/Store/SideBarLeft/CharacterSlice.jsx
Normal file
@@ -0,0 +1,242 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
const useCharacterStore = create(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
// 状态
|
||||
characters: [], // 所有角色卡列表
|
||||
selectedCharacter: null, // 当前选中的角色
|
||||
isLoading: false,
|
||||
error: null,
|
||||
currentPage: 1, // 当前页码
|
||||
pageSize: 12, // 每页显示数量
|
||||
characterChats: {}, // 存储每个角色的聊天记录 {characterName: [chats]}
|
||||
|
||||
// 获取所有角色卡
|
||||
fetchCharacters: async () => {
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
const response = await fetch('/api/characters/');
|
||||
if (!response.ok) throw new Error('Failed to fetch characters');
|
||||
|
||||
const data = await response.json();
|
||||
set({
|
||||
characters: data,
|
||||
isLoading: false,
|
||||
currentPage: 1 // 重置到第一页
|
||||
});
|
||||
} catch (error) {
|
||||
set({
|
||||
error: error.message,
|
||||
isLoading: false
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 设置当前页码
|
||||
setCurrentPage: (page) => {
|
||||
set({ currentPage: page });
|
||||
},
|
||||
|
||||
// 设置每页显示数量
|
||||
setPageSize: (size) => {
|
||||
set({ pageSize: size, currentPage: 1 }); // 重置到第一页
|
||||
},
|
||||
|
||||
// 获取当前页的角色数据
|
||||
getCurrentPageCharacters: () => {
|
||||
const { characters, currentPage, pageSize } = get();
|
||||
const startIndex = (currentPage - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
return characters.slice(startIndex, endIndex);
|
||||
},
|
||||
|
||||
// 获取总页数
|
||||
getTotalPages: () => {
|
||||
const { characters, pageSize } = get();
|
||||
return Math.ceil(characters.length / pageSize);
|
||||
},
|
||||
|
||||
// 获取角色的聊天记录
|
||||
fetchCharacterChats: async (characterName) => {
|
||||
const { characterChats } = get();
|
||||
|
||||
// 如果已经缓存了,直接返回
|
||||
if (characterChats[characterName]) {
|
||||
return characterChats[characterName];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/characters/${encodeURIComponent(characterName)}/chats`);
|
||||
if (!response.ok) throw new Error('Failed to fetch chats');
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 更新缓存
|
||||
set((state) => ({
|
||||
characterChats: {
|
||||
...state.characterChats,
|
||||
[characterName]: data.chats || []
|
||||
}
|
||||
}));
|
||||
|
||||
return data.chats || [];
|
||||
} catch (error) {
|
||||
console.error('获取聊天记录失败:', error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
// 选择角色
|
||||
selectCharacter: (character) => {
|
||||
// 使用函数式更新避免不必要的重渲染
|
||||
set((state) => {
|
||||
// 如果选中的是同一个角色,不更新状态
|
||||
if (state.selectedCharacter?.id === character.id) {
|
||||
return state;
|
||||
}
|
||||
return { selectedCharacter: character };
|
||||
});
|
||||
},
|
||||
|
||||
// 创建角色
|
||||
createCharacter: async (characterData) => {
|
||||
try {
|
||||
const response = await fetch('/api/characters/', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(characterData)
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create character');
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 刷新列表
|
||||
await get().fetchCharacters();
|
||||
|
||||
return data.character;
|
||||
} catch (error) {
|
||||
set({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 更新角色
|
||||
updateCharacter: async (name, updates) => {
|
||||
try {
|
||||
const response = await fetch(`/api/characters/${encodeURIComponent(name)}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(updates)
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update character');
|
||||
|
||||
// 刷新列表
|
||||
await get().fetchCharacters();
|
||||
} catch (error) {
|
||||
set({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 删除角色
|
||||
deleteCharacter: async (name) => {
|
||||
try {
|
||||
const response = await fetch(`/api/characters/${encodeURIComponent(name)}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete character');
|
||||
|
||||
// 刷新列表
|
||||
await get().fetchCharacters();
|
||||
} catch (error) {
|
||||
set({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 上传头像
|
||||
uploadAvatar: async (name, file) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await fetch(`/api/characters/${encodeURIComponent(name)}/avatar`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to upload avatar');
|
||||
|
||||
// 刷新列表以更新头像路径
|
||||
await get().fetchCharacters();
|
||||
} catch (error) {
|
||||
set({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 导入角色卡
|
||||
importCharacter: async (file) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await fetch('/api/characters/import', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to import character');
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 刷新列表
|
||||
await get().fetchCharacters();
|
||||
|
||||
return data.character;
|
||||
} catch (error) {
|
||||
set({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 导出角色卡为 PNG
|
||||
exportCharacterAsPng: async (name) => {
|
||||
try {
|
||||
const response = await fetch(`/api/characters/${encodeURIComponent(name)}/export/png`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to export character');
|
||||
|
||||
// 下载文件
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${name}.png`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
} catch (error) {
|
||||
set({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: 'character-storage',
|
||||
partialize: (state) => ({
|
||||
selectedCharacter: state.selectedCharacter
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export default useCharacterStore;
|
||||
Reference in New Issue
Block a user