完成大量美化,zustand迁移,动态表格修复
This commit is contained in:
130
frontend/src/Store/SideBarLeft/CharacterCardUISlice.jsx
Normal file
130
frontend/src/Store/SideBarLeft/CharacterCardUISlice.jsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
/**
|
||||
* CharacterCard UI 状态 Store
|
||||
* 管理角色卡列表的 UI 交互状态(非持久化)
|
||||
*/
|
||||
const useCharacterCardUIStore = create((set) => ({
|
||||
// ==================== 状态 ====================
|
||||
|
||||
// 筛选标签
|
||||
filterTag: '',
|
||||
|
||||
// 是否处于编辑模式
|
||||
isEditing: false,
|
||||
|
||||
// 编辑表单数据
|
||||
editForm: null,
|
||||
|
||||
// 当前页码
|
||||
currentPage: 1,
|
||||
|
||||
// 每页显示数量
|
||||
pageSize: 12,
|
||||
|
||||
// ==================== Actions ====================
|
||||
|
||||
/**
|
||||
* 设置筛选标签
|
||||
* @param {string} tag - 标签名
|
||||
*/
|
||||
setFilterTag: (tag) => {
|
||||
set({ filterTag: tag, currentPage: 1 }); // 重置页码
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空筛选
|
||||
*/
|
||||
clearFilter: () => {
|
||||
set({ filterTag: '', currentPage: 1 });
|
||||
},
|
||||
|
||||
/**
|
||||
* 进入编辑模式
|
||||
* @param {Object} character - 角色数据
|
||||
*/
|
||||
startEditing: (character) => {
|
||||
set({
|
||||
isEditing: true,
|
||||
editForm: {
|
||||
name: character.name,
|
||||
description: character.description || '',
|
||||
personality: character.personality || '',
|
||||
scenario: character.scenario || '',
|
||||
first_mes: character.first_mes || '',
|
||||
mes_example: character.mes_example || '',
|
||||
categories: character.categories || [],
|
||||
tags: character.tags || []
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 退出编辑模式
|
||||
*/
|
||||
cancelEditing: () => {
|
||||
set({
|
||||
isEditing: false,
|
||||
editForm: null
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新编辑表单字段
|
||||
* @param {string} field - 字段名
|
||||
* @param {*} value - 字段值
|
||||
*/
|
||||
updateEditForm: (field, value) => {
|
||||
set((state) => ({
|
||||
editForm: {
|
||||
...state.editForm,
|
||||
[field]: value
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置页码
|
||||
* @param {number} page - 页码
|
||||
*/
|
||||
setCurrentPage: (page) => {
|
||||
set({ currentPage: page });
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置每页显示数量
|
||||
* @param {number} size - 每页数量
|
||||
*/
|
||||
setPageSize: (size) => {
|
||||
set({ pageSize: size, currentPage: 1 }); // 重置页码
|
||||
},
|
||||
|
||||
/**
|
||||
* 下一页
|
||||
*/
|
||||
nextPage: () => {
|
||||
set((state) => ({ currentPage: state.currentPage + 1 }));
|
||||
},
|
||||
|
||||
/**
|
||||
* 上一页
|
||||
*/
|
||||
prevPage: () => {
|
||||
set((state) => ({ currentPage: Math.max(1, state.currentPage - 1) }));
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置所有 UI 状态
|
||||
*/
|
||||
reset: () => {
|
||||
set({
|
||||
filterTag: '',
|
||||
isEditing: false,
|
||||
editForm: null,
|
||||
currentPage: 1,
|
||||
pageSize: 12
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
export default useCharacterCardUIStore;
|
||||
@@ -28,6 +28,10 @@ const usePresetStore = create((set, get) => ({
|
||||
// 参数设置折叠状态
|
||||
isParametersExpanded: true,
|
||||
|
||||
// 分页状态
|
||||
currentPage: 1,
|
||||
pageSize: 8,
|
||||
|
||||
// 预设组件列表
|
||||
promptComponents: [
|
||||
{
|
||||
@@ -129,17 +133,22 @@ const usePresetStore = create((set, get) => ({
|
||||
// 记录原始数据用于调试
|
||||
console.log('从后端获取的预设数据:', presetData);
|
||||
|
||||
// 提取参数并更新状态,确保所有参数都有默认值
|
||||
// 提取参数并更新状态,支持内部结构和SillyTavern结构
|
||||
const parameters = {
|
||||
temperature: presetData.temperature !== undefined ? presetData.temperature : 1.0,
|
||||
frequency_penalty: presetData.frequency_penalty !== undefined ? presetData.frequency_penalty : 0.0,
|
||||
presence_penalty: presetData.presence_penalty !== undefined ? presetData.presence_penalty : 0.0,
|
||||
top_p: presetData.top_p !== undefined ? presetData.top_p : 1.0,
|
||||
top_k: presetData.top_k !== undefined ? presetData.top_k : 0,
|
||||
frequency_penalty: presetData.frequency_penalty !== undefined ? presetData.frequency_penalty :
|
||||
(presetData.frequencyPenalty !== undefined ? presetData.frequencyPenalty : 0.0),
|
||||
presence_penalty: presetData.presence_penalty !== undefined ? presetData.presence_penalty :
|
||||
(presetData.presencePenalty !== undefined ? presetData.presencePenalty : 0.0),
|
||||
top_p: presetData.top_p !== undefined ? presetData.top_p :
|
||||
(presetData.topP !== undefined ? presetData.topP : 1.0),
|
||||
top_k: presetData.top_k !== undefined ? presetData.top_k :
|
||||
(presetData.topK !== undefined ? presetData.topK : 0),
|
||||
max_context: presetData.openai_max_context !== undefined ? presetData.openai_max_context :
|
||||
(presetData.max_context !== undefined ? presetData.max_context : 1000000),
|
||||
max_tokens: presetData.openai_max_tokens !== undefined ? presetData.openai_max_tokens :
|
||||
(presetData.max_tokens !== undefined ? presetData.max_tokens : 30000),
|
||||
(presetData.max_tokens !== undefined ? presetData.max_tokens :
|
||||
(presetData.maxLength !== undefined ? presetData.maxLength : 30000)),
|
||||
max_context_unlocked: presetData.max_context_unlocked !== undefined ? presetData.max_context_unlocked : false,
|
||||
stream_openai: presetData.stream_openai !== undefined ? presetData.stream_openai : true,
|
||||
seed: presetData.seed !== undefined ? presetData.seed : -1,
|
||||
@@ -149,9 +158,30 @@ const usePresetStore = create((set, get) => ({
|
||||
// 记录映射后的参数用于调试
|
||||
console.log('映射后的参数:', parameters);
|
||||
|
||||
// 处理预设组件
|
||||
// 处理预设组件 - 支持内部结构和SillyTavern结构
|
||||
let components = [];
|
||||
if (presetData.prompts && Array.isArray(presetData.prompts)) {
|
||||
|
||||
// 优先使用内部结构的 entries
|
||||
if (presetData.entries && Array.isArray(presetData.entries)) {
|
||||
components = presetData.entries.map(entry => ({
|
||||
identifier: entry.identifier,
|
||||
name: entry.name,
|
||||
content: entry.content || '',
|
||||
enabled: entry.enabled !== false,
|
||||
role: entry.role === 'system' ? 0 : entry.role === 'user' ? 1 : 2,
|
||||
system_prompt: entry.role === 'system',
|
||||
marker: entry.isSystemNode || false
|
||||
}));
|
||||
|
||||
// 按 order 排序
|
||||
components.sort((a, b) => {
|
||||
const orderA = presetData.entries.find(e => e.identifier === a.identifier)?.order || 0;
|
||||
const orderB = presetData.entries.find(e => e.identifier === b.identifier)?.order || 0;
|
||||
return orderA - orderB;
|
||||
});
|
||||
}
|
||||
// 兼容SillyTavern结构的 prompts
|
||||
else if (presetData.prompts && Array.isArray(presetData.prompts)) {
|
||||
// 获取当前角色的prompt_order,添加更严格的检查
|
||||
const currentOrder = (presetData.prompt_order &&
|
||||
Array.isArray(presetData.prompt_order) &&
|
||||
@@ -182,7 +212,6 @@ const usePresetStore = create((set, get) => ({
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新状态,确保参数容器展开
|
||||
set({
|
||||
selectedPreset: presetId,
|
||||
@@ -209,23 +238,31 @@ const usePresetStore = create((set, get) => ({
|
||||
saveCurrentAsPreset: async ({ name }) => {
|
||||
const state = get();
|
||||
try {
|
||||
// 构建预设数据
|
||||
// 构建预设数据 - 使用内部专有结构
|
||||
const presetData = {
|
||||
...state.parameters,
|
||||
prompts: state.promptComponents.map(component => ({
|
||||
// GenerationPreset 部分 - 采样参数
|
||||
id: `preset_${Date.now()}`,
|
||||
name: name,
|
||||
temperature: state.parameters.temperature,
|
||||
topP: state.parameters.top_p,
|
||||
topK: state.parameters.top_k,
|
||||
frequencyPenalty: state.parameters.frequency_penalty,
|
||||
presencePenalty: state.parameters.presence_penalty,
|
||||
maxLength: state.parameters.max_tokens,
|
||||
isDefault: false,
|
||||
|
||||
// PromptPresetView 部分 - prompt组件列表
|
||||
characterId: 'global', // 全局预设,不绑定特定角色
|
||||
entries: state.promptComponents.map((component, index) => ({
|
||||
identifier: component.identifier,
|
||||
name: component.name,
|
||||
enabled: component.enabled !== false,
|
||||
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
|
||||
}))
|
||||
}]
|
||||
order: index,
|
||||
role: component.role === 0 ? 'system' : component.role === 1 ? 'user' : 'ai',
|
||||
tokenCount: component.content ? component.content.length : 0,
|
||||
isSystemNode: component.marker || false
|
||||
}))
|
||||
};
|
||||
|
||||
// 发送到后端
|
||||
@@ -234,10 +271,7 @@ const usePresetStore = create((set, get) => ({
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
preset_name: name,
|
||||
...presetData
|
||||
})
|
||||
body: JSON.stringify(presetData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -248,7 +282,7 @@ const usePresetStore = create((set, get) => ({
|
||||
|
||||
// 添加到本地预设列表
|
||||
const newPreset = {
|
||||
id: name,
|
||||
id: presetData.id,
|
||||
name,
|
||||
description: '',
|
||||
component_count: state.promptComponents.length,
|
||||
@@ -257,7 +291,7 @@ const usePresetStore = create((set, get) => ({
|
||||
|
||||
set((state) => ({
|
||||
presets: [...state.presets, newPreset],
|
||||
selectedPreset: name
|
||||
selectedPreset: presetData.id
|
||||
}));
|
||||
|
||||
return result;
|
||||
@@ -350,6 +384,26 @@ const usePresetStore = create((set, get) => ({
|
||||
identifier: component.identifier,
|
||||
enabled: component.enabled !== false
|
||||
}));
|
||||
},
|
||||
|
||||
// 设置当前页
|
||||
setCurrentPage: (page) => set({ currentPage: page }),
|
||||
|
||||
// 设置每页数量
|
||||
setPageSize: (size) => set({ pageSize: size, currentPage: 1 }),
|
||||
|
||||
// 获取当前页的预设列表
|
||||
getCurrentPagePresets: () => {
|
||||
const { presets, currentPage, pageSize } = get();
|
||||
const startIndex = (currentPage - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
return presets.slice(startIndex, endIndex);
|
||||
},
|
||||
|
||||
// 获取总页数
|
||||
getTotalPages: () => {
|
||||
const { presets, pageSize } = get();
|
||||
return Math.ceil(presets.length / pageSize);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ export { default as useApiConfigStore } from './ApiConfigSlice';
|
||||
export { default as usePresetStore } from './PresetSlice';
|
||||
export { default as useWorldBookStore } from './WorldBookSlice';
|
||||
export { default as useCharacterStore } from './CharacterSlice';
|
||||
export { default as useCharacterCardUIStore } from './CharacterCardUISlice';
|
||||
|
||||
Reference in New Issue
Block a user