diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 60e3510..29e925a 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -1,5 +1,5 @@ import { useLocation, useNavigate } from 'react-router-dom'; -import { Book, Bot, Clock, Code, Cpu, House, Network, Puzzle, Settings } from 'lucide-react'; +import { Book, Clock, Code, House, Network, Puzzle, Settings } from 'lucide-react'; import { useI18n } from '../../i18n'; import { NAV_ITEMS, normalizeWorkspacePath } from '../../router/routes'; @@ -9,8 +9,6 @@ const MENU_MARKS: Record = { '/home': House, '/knowledge': Book, '/channels': Network, - '/agents': Bot, - '/models': Cpu, '/skills': Puzzle, '/cron': Clock, // '/scripts': Code, diff --git a/src/i18n/messages.ts b/src/i18n/messages.ts index 49612da..840ce67 100644 --- a/src/i18n/messages.ts +++ b/src/i18n/messages.ts @@ -416,6 +416,8 @@ export const messages: I18nMessages = { systemSettings: 'System Settings', account: 'Account', general: 'General', + agents: 'Agents', + models: 'Models', }, account: { title: 'Account Settings', @@ -892,6 +894,8 @@ export const messages: I18nMessages = { systemSettings: '系统设置', account: '账号', general: '通用', + agents: 'Agents', + models: 'Models', }, account: { title: '账号设置', @@ -1368,6 +1372,8 @@ export const messages: I18nMessages = { systemSettings: 'システム設定', account: 'アカウント', general: '一般', + agents: 'Agents', + models: 'Models', }, account: { title: 'アカウント設定', diff --git a/src/pages/Setting/components/SettingMenu.tsx b/src/pages/Setting/components/SettingMenu.tsx index 2a6cf7a..8ec0a05 100644 --- a/src/pages/Setting/components/SettingMenu.tsx +++ b/src/pages/Setting/components/SettingMenu.tsx @@ -1,7 +1,13 @@ import { useI18n } from '../../../i18n'; -import { Settings, User } from 'lucide-react'; +import { Bot, Cpu, Settings, User } from 'lucide-react'; -export type SettingView = 'account' | 'general'; +const SETTING_VIEWS = ['general', 'account', 'models', 'agents'] as const; + +export type SettingView = (typeof SETTING_VIEWS)[number]; + +export function isSettingView(value: string | null): value is SettingView { + return value !== null && SETTING_VIEWS.includes(value as SettingView); +} type SettingMenuProps = { currentView: SettingView; @@ -10,8 +16,12 @@ type SettingMenuProps = { const MENU_ITEMS: Array<{ id: SettingView; - labelKey: 'settings.menu.account' | 'settings.menu.general'; - Icon: typeof User | typeof Settings; + labelKey: + | 'settings.menu.account' + | 'settings.menu.general' + | 'settings.menu.models' + | 'settings.menu.agents'; + Icon: typeof User | typeof Settings | typeof Cpu | typeof Bot; }> = [ { id: 'general', @@ -23,6 +33,16 @@ const MENU_ITEMS: Array<{ labelKey: 'settings.menu.account', Icon: User, }, + { + id: 'models', + labelKey: 'settings.menu.models', + Icon: Cpu, + }, + { + id: 'agents', + labelKey: 'settings.menu.agents', + Icon: Bot, + }, ]; export default function SettingMenu({ currentView, onChange }: SettingMenuProps) { diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx index 1fdc0a7..9483e4d 100644 --- a/src/pages/Setting/index.tsx +++ b/src/pages/Setting/index.tsx @@ -1,18 +1,41 @@ -import { useState } from 'react'; +import { Suspense, lazy, useEffect } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { useI18n } from '../../i18n'; import { updateLanguage, updateThemeMode, useSettingsStore } from '../../stores'; import type { LanguageCode, ThemeMode } from '../../types/runtime'; import AccountSettingsPanel from './components/AccountSettingsPanel'; import GeneralSettingsPanel from './components/GeneralSettingsPanel'; -import SettingMenu, { type SettingView } from './components/SettingMenu'; +import SettingMenu, { isSettingView, type SettingView } from './components/SettingMenu'; import { useGatewaySettingState } from './useGatewaySettingState'; import { useSettingUpdateState } from './useSettingUpdateState'; +const AgentsPage = lazy(() => import('../Agents')); +const ModelsPage = lazy(() => import('../Models')); + export default function SettingPage() { - const [currentView, setCurrentView] = useState('general'); + const location = useLocation(); + const navigate = useNavigate(); + const { t } = useI18n(); const themeMode = useSettingsStore((state) => state.themeMode); const language = useSettingsStore((state) => state.language); const updateState = useSettingUpdateState(); const gatewayState = useGatewaySettingState(); + const rawView = new URLSearchParams(location.search).get('view'); + const currentView: SettingView = isSettingView(rawView) ? rawView : 'general'; + + useEffect(() => { + if (isSettingView(rawView)) return; + + const searchParams = new URLSearchParams(location.search); + searchParams.set('view', 'general'); + navigate( + { + pathname: location.pathname, + search: `?${searchParams.toString()}`, + }, + { replace: true }, + ); + }, [location.pathname, location.search, navigate, rawView]); const handleThemeChange = async (nextTheme: ThemeMode) => { await updateThemeMode(nextTheme); @@ -22,24 +45,69 @@ export default function SettingPage() { await updateLanguage(nextLanguage); }; + const handleViewChange = (nextView: SettingView) => { + if (nextView === currentView) return; + + const searchParams = new URLSearchParams(location.search); + searchParams.set('view', nextView); + navigate({ + pathname: location.pathname, + search: `?${searchParams.toString()}`, + }); + }; + + const loadingFallback = ( +
+ {t('common.loading')} +
+ ); + + const renderContent = () => { + switch (currentView) { + case 'account': + return ( +
+ +
+ ); + case 'models': + return ( +
+ + + +
+ ); + case 'agents': + return ( +
+ + + +
+ ); + case 'general': + default: + return ( +
+ +
+ ); + } + }; + return (
- + -
- {currentView === 'account' ? ( - - ) : ( - - )} -
+ {renderContent()}
); } diff --git a/src/router/index.tsx b/src/router/index.tsx index 378cbbf..f877fb9 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -8,8 +8,6 @@ import { onAuthLogout } from './auth-session'; const HomePage = lazy(() => import('../pages/Home')); const ChannelsPage = lazy(() => import('../pages/Channels')); -const AgentsPage = lazy(() => import('../pages/Agents')); -const ModelsPage = lazy(() => import('../pages/Models')); const SkillsPage = lazy(() => import('../pages/Skills')); const CronPage = lazy(() => import('../pages/Cron')); const ScriptsPage = lazy(() => import('../pages/Scripts')); @@ -32,6 +30,10 @@ function renderLazyPage(element: ReactNode) { ); } +function renderSettingViewRedirect(view: 'agents' | 'models') { + return ; +} + function AuthLogoutRedirector() { const location = useLocation(); const navigate = useNavigate(); @@ -65,8 +67,8 @@ export function AppRouter() { }> )} /> )} /> - )} /> - )} /> + + )} /> )} /> )} /> diff --git a/src/router/routes.ts b/src/router/routes.ts index a546a0b..cb52227 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -32,8 +32,6 @@ export const NAV_ITEMS: NavItem[] = [ { path: '/home', labelKey: 'sidebar.home' }, { path: '/knowledge', labelKey: 'sidebar.knowledge' }, { path: '/channels', labelKey: 'sidebar.channels' }, - { path: '/agents', labelKey: 'sidebar.agents' }, - { path: '/models', labelKey: 'sidebar.models' }, { path: '/skills', labelKey: 'sidebar.skills' }, { path: '/cron', labelKey: 'sidebar.cron' }, // { path: '/scripts', labelKey: 'sidebar.scripts' }, @@ -44,13 +42,14 @@ export function normalizeWorkspacePath(pathname: string): WorkspacePath { switch (pathname) { case '/knowledge': case '/channels': - case '/agents': - case '/models': case '/skills': case '/cron': case '/scripts': case '/setting': return pathname; + case '/agents': + case '/models': + return '/setting'; case '/home': default: return DEFAULT_PATH;