diff --git a/frontend-react/src/App.jsx b/frontend-react/src/App.jsx index 9e6922d..d4d7f64 100644 --- a/frontend-react/src/App.jsx +++ b/frontend-react/src/App.jsx @@ -14,9 +14,7 @@ function App() { {/* 主内容容器 */}
{/* 左侧栏 - 预设面板 */} -
-
{/* 中间栏:聊天框 */}
@@ -24,9 +22,7 @@ function App() {
{/* 右侧栏 */} -
-
); diff --git a/frontend-react/src/Store/Slices/SideBarLeftSlice.jsx b/frontend-react/src/Store/Slices/SideBarLeftSlice.jsx new file mode 100644 index 0000000..47bd478 --- /dev/null +++ b/frontend-react/src/Store/Slices/SideBarLeftSlice.jsx @@ -0,0 +1,16 @@ +import { create } from 'zustand'; + +const useSideBarLeftStore = create((set) => ({ + activeTab: 'gallery', + + tabs: [ + { id: 'gallery', label: '🖼️ 画廊' }, + { id: 'api', label: '🔌 API' }, + { id: 'presets', label: '📋 预设' }, + { id: 'worldbook', label: '🌍 世界书' } + ], + + setActiveTab: (tab) => set({ activeTab: tab }) +})); + +export default useSideBarLeftStore; diff --git a/frontend-react/src/Store/Slices/SideBarRightSlice.jsx b/frontend-react/src/Store/Slices/SideBarRightSlice.jsx new file mode 100644 index 0000000..15b11e0 --- /dev/null +++ b/frontend-react/src/Store/Slices/SideBarRightSlice.jsx @@ -0,0 +1,34 @@ +import { create } from 'zustand'; + +const useSideBarRightStore = create((set) => ({ + selectedTabs: ['dice', 'macros'], + + allTabs: [ + { id: 'dice', label: '🎲 骰子与工具', component: null }, + { id: 'debug', label: '🔍 上下文调试', component: null }, + { id: 'macros', label: '🔧 快捷宏', component: null }, + { id: 'table', label: '📊 动态表格', component: null } + ], + + handleTabClick: (tabId) => set((state) => { + if (state.selectedTabs.includes(tabId)) { + // 如果已选中,则取消选中 + return { selectedTabs: state.selectedTabs.filter(id => id !== tabId) }; + } else if (state.selectedTabs.length < 2) { + // 如果未选中且少于2个,则添加 + return { selectedTabs: [...state.selectedTabs, tabId] }; + } else { + // 如果已有2个,则替换最早选中的 + return { selectedTabs: [...state.selectedTabs.slice(1), tabId] }; + } + }), + + // 设置特定标签的组件 + setTabComponent: (tabId, component) => set((state) => ({ + allTabs: state.allTabs.map(tab => + tab.id === tabId ? { ...tab, component } : tab + ) + })) +})); + +export default useSideBarRightStore; diff --git a/frontend-react/src/Store/indexStore.jsx b/frontend-react/src/Store/indexStore.jsx index 9c9a612..08e14dd 100644 --- a/frontend-react/src/Store/indexStore.jsx +++ b/frontend-react/src/Store/indexStore.jsx @@ -1,2 +1,5 @@ // frontend-react/src/store/index.js -export { default as useRoleSelectorStore } from './roleSelectorStore'; +export { default as useRoleSelectorStore } from './Slices/RoleSelectorSlice'; +export { default as useSideBarLeftStore } from './Slices/SideBarLeftSlice'; +export { default as useSideBarRightStore } from './Slices/SideBarRightSlice'; +export { default as useChatBoxStore } from './Slices/ChatBoxSlice'; \ No newline at end of file diff --git a/frontend-react/src/components/RoleSelector/RoleSelector.css b/frontend-react/src/components/RoleSelector/RoleSelector.css index f93bd5c..40b12dc 100644 --- a/frontend-react/src/components/RoleSelector/RoleSelector.css +++ b/frontend-react/src/components/RoleSelector/RoleSelector.css @@ -145,7 +145,7 @@ flex-direction: column; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); overflow: hidden; - min-height: 140px; + height: 200px; /* 设置固定高度 */ position: relative; /* 添加微妙的边框效果 */ border: 1px solid rgba(0, 0, 0, 0.05); @@ -167,7 +167,8 @@ display: flex; justify-content: space-between; align-items: center; - flex: 1; + flex: 0 0 auto; /* 不再自动伸缩,使用固定高度 */ + height: 60px; /* 设置固定高度 */ } .role-header .role-name { @@ -177,12 +178,16 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + max-width: 80%; /* 限制名称最大宽度 */ } + .role-item.active .role-header .role-name { color: #667eea; + font-size: 16px; /* 激活状态时字体稍大 */ } + .role-actions { display: flex; gap: 4px; @@ -191,8 +196,10 @@ right: 12px; opacity: 0; transition: opacity 0.2s ease; + z-index: 10; /* 确保操作按钮在最上层 */ } + .role-item:hover .role-actions { opacity: 1; } @@ -227,13 +234,17 @@ border-top: 1px solid #f0f0f0; padding: 8px 0; background-color: #fafbfc; - max-height: 140px; + height: 120px; /* 设置固定高度 */ overflow-y: auto; /* 自定义滚动条样式 */ scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; + flex: 1; /* 让聊天列表占据剩余空间 */ + display: flex; + flex-direction: column; } + /* Webkit浏览器滚动条样式 */ .chat-list::-webkit-scrollbar { width: 4px; diff --git a/frontend-react/src/components/SideBarLeft/SideBarLeft.css b/frontend-react/src/components/SideBarLeft/SideBarLeft.css index 9fa5163..df75a84 100644 --- a/frontend-react/src/components/SideBarLeft/SideBarLeft.css +++ b/frontend-react/src/components/SideBarLeft/SideBarLeft.css @@ -1,42 +1,91 @@ -/* frontend-react/src/components/SideBarLeft/SideBarLeft.css */ .sidebar-left { width: 250px; height: 100%; display: flex; flex-direction: column; overflow: hidden; + background-color: #ffffff; + box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05); + border-right: 1px solid #e8e8e8; + transition: width 0.3s ease; } .sidebar-tabs { display: flex; - border-bottom: 1px solid #e0e0e0; + border-bottom: 1px solid #e8e8e8; + background-color: #fafafa; + padding: 0 10px; } .tab-button { flex: 1; - padding: 10px 5px; + padding: 12px 5px; background: none; border: none; cursor: pointer; - transition: background-color 0.3s; + transition: all 0.3s ease; font-size: 14px; + color: #555; + position: relative; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; } .tab-button:hover { - background-color: #f5f5f5; + background-color: #f0f0f0; + color: #333; } .tab-button.active { - border-bottom: 2px solid #4a90e2; - font-weight: bold; + color: #4a90e2; + font-weight: 600; +} + +.tab-button.active::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background-color: #4a90e2; + border-radius: 3px 3px 0 0; } .sidebar-content { flex: 1; overflow-y: auto; - padding: 10px; + padding: 15px; } .tab-content { - height: 100%; + flex: 1; + overflow-y: auto; + padding: 15px; + border-bottom: 1px solid #f0f0f0; +} + + +/* 自定义滚动条样式 */ +.sidebar-content::-webkit-scrollbar, +.tab-content::-webkit-scrollbar { + width: 6px; +} + +.sidebar-content::-webkit-scrollbar-track, +.tab-content::-webkit-scrollbar-track { + background: #f1f1f1; +} + +.sidebar-content::-webkit-scrollbar-thumb, +.tab-content::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.sidebar-content::-webkit-scrollbar-thumb:hover, +.tab-content::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; } diff --git a/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx b/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx index fd7011b..2f83edd 100644 --- a/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx +++ b/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx @@ -1,40 +1,30 @@ // frontend-react/src/components/SideBarLeft/SideBarLeft.jsx -import React, { useState } from 'react'; +import React from 'react'; import './SideBarLeft.css'; +import { useSideBarLeftStore } from '../../Store/indexStore'; +import useSideBarRightStore from '../../Store/Slices/SideBarLeftSlice'; const SideBarLeft = () => { - const [activeTab, setActiveTab] = useState('gallery'); - - const handleTabChange = (tab) => { - setActiveTab(tab); - }; + const { activeTab, tabs, setActiveTab } = useSideBarLeftStore(); return (
- - - + {tabs.map(tab => ( + + ))}
{activeTab === 'gallery' &&
画廊内容
} - {activeTab === 'config' &&
配置内容
} + {activeTab === 'api' &&
API配置内容
} + {activeTab === 'presets' &&
预设配置内容
} {activeTab === 'worldbook' &&
世界书内容
}
diff --git a/frontend-react/src/components/SideBarRight/SideBarRight.css b/frontend-react/src/components/SideBarRight/SideBarRight.css index 9b934c7..9cfbcfa 100644 --- a/frontend-react/src/components/SideBarRight/SideBarRight.css +++ b/frontend-react/src/components/SideBarRight/SideBarRight.css @@ -1,56 +1,102 @@ -/* frontend-react/src/components/SideBarRight/SideBarRight.css */ .sidebar-right { width: 300px; height: 100%; display: flex; flex-direction: column; overflow: hidden; -} - -.right-top, .right-bottom { - display: flex; - flex-direction: column; - overflow: hidden; -} - -.right-top { - flex: 1; -} - -.right-bottom { - height: 200px; + background-color: #ffffff; + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.05); + border-left: 1px solid #e8e8e8; + transition: width 0.3s ease; } .sidebar-tabs { display: flex; - border-bottom: 1px solid #e0e0e0; + border-bottom: 1px solid #e8e8e8; + flex-shrink: 0; + background-color: #fafafa; + padding: 0 10px; } .tab-button { flex: 1; - padding: 10px 5px; + padding: 12px 5px; background: none; border: none; cursor: pointer; - transition: background-color 0.3s; + transition: all 0.3s ease; font-size: 14px; + color: #555; + position: relative; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; } .tab-button:hover { - background-color: #f5f5f5; + background-color: #f0f0f0; + color: #333; } .tab-button.active { - border-bottom: 2px solid #4a90e2; - font-weight: bold; + color: #4a90e2; + font-weight: 600; +} + +.tab-button.active::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background-color: #4a90e2; + border-radius: 3px 3px 0 0; } .sidebar-content { flex: 1; overflow-y: auto; - padding: 10px; + display: flex; + flex-direction: column; } .tab-content { - height: 100%; + flex: 1; + overflow-y: auto; + padding: 15px; + border-bottom: 1px solid #f0f0f0; } + + +.tab-content:last-child { + border-bottom: none; +} + +.tab-content.full-height { + flex: 1; +} + +/* 自定义滚动条样式 */ +.sidebar-content::-webkit-scrollbar, +.tab-content::-webkit-scrollbar { + width: 6px; +} + +.sidebar-content::-webkit-scrollbar-track, +.tab-content::-webkit-scrollbar-track { + background: #f1f1f1; +} + +.sidebar-content::-webkit-scrollbar-thumb, +.tab-content::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.sidebar-content::-webkit-scrollbar-thumb:hover, +.tab-content::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + diff --git a/frontend-react/src/components/SideBarRight/SideBarRight.jsx b/frontend-react/src/components/SideBarRight/SideBarRight.jsx index b82a305..ff480ba 100644 --- a/frontend-react/src/components/SideBarRight/SideBarRight.jsx +++ b/frontend-react/src/components/SideBarRight/SideBarRight.jsx @@ -1,65 +1,41 @@ -// frontend-react/src/components/SideBarRight/SideBarRight.jsx -import React, { useState } from 'react'; +import React, { useEffect } from 'react'; import './SideBarRight.css'; import DicePanel from '../DicePanel/DicePanel'; import ImageDisplay from '../ImageDisplay/ImageDisplay'; +import useSideBarRightStore from '../../Store/Slices/SideBarRightSlice'; const SideBarRight = () => { - const [topActiveTab, setTopActiveTab] = useState('dice'); - const [bottomActiveTab, setBottomActiveTab] = useState('macros'); + const { selectedTabs, allTabs, handleTabClick, setTabComponent } = useSideBarRightStore(); - const handleTopTabChange = (tab) => { - setTopActiveTab(tab); - }; - - const handleBottomTabChange = (tab) => { - setBottomActiveTab(tab); - }; + // 设置标签组件 + useEffect(() => { + setTabComponent('dice', DicePanel); + // 可以在这里设置其他标签的组件 + }, [setTabComponent]); return (
-
-
+
+ {allTabs.map(tab => ( - -
- -
- {topActiveTab === 'dice' && } - {topActiveTab === 'debug' &&
上下文调试内容
} -
+ ))}
-
-
- - -
- -
- {bottomActiveTab === 'macros' &&
快捷宏内容
} - {bottomActiveTab === 'table' &&
动态表格内容
} -
+
+ {selectedTabs.map(tabId => { + const tab = allTabs.find(t => t.id === tabId); + return ( +
+ {tab.component ? :
{tab.label}内容
} +
+ ); + })}
); diff --git a/frontend-react/src/components/ToolBar/ToolBar.jsx b/frontend-react/src/components/ToolBar/ToolBar.jsx index 0555cc1..e1a65fc 100644 --- a/frontend-react/src/components/ToolBar/ToolBar.jsx +++ b/frontend-react/src/components/ToolBar/ToolBar.jsx @@ -65,7 +65,7 @@ const Toolbar = () => {
handlePanelToggle('currentRole')} > 👤 @@ -82,11 +82,11 @@ const Toolbar = () => {
handlePanelToggle('role')} > 🎭 - 全局世界书 + 角色管理
{truncateText(getDisplayText())} diff --git a/frontend-react/src/index.css b/frontend-react/src/index.css index ec6c2f2..9d5de85 100644 --- a/frontend-react/src/index.css +++ b/frontend-react/src/index.css @@ -1,76 +1,21 @@ -/* frontend-react/src/index.css */ -html, body { - margin: 0; - padding: 0; - height: 100vh; - width: 100%; -} - -.app { - height: 100%; - display: flex; - flex-direction: column; -} - -#root { - height: 100%; - width: 100%; - display: flex; - flex-direction: column; -} - -/* 重置样式 */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html, body, .app { - height: 100%; - width: 100%; - overflow: hidden; -} - -.app { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - overflow: hidden; -} - -/* 主内容区域 */ -.main-container { - flex: 1; - display: flex; - overflow: hidden; -} - /* 左侧栏 */ .sidebar-left { - width: 250px; + width: 22.5%; /* 修改为30% */ height: 100%; overflow: hidden; } /* 中间聊天区域 */ .chat-area { - flex: 1; + flex: 55%; /* 修改为0.4 */ height: 100%; display: flex; - flex-direction: column; overflow: hidden; } /* 右侧栏 */ .sidebar-right { - width: 300px; + width: 22.5%; /* 修改为30% */ height: 100%; overflow: hidden; } - -.toolbar { - height: 60px; - flex-shrink: 0; -}