From d2f428ec864bc6b2ed5298b3f44fbb26566e3c94 Mon Sep 17 00:00:00 2001 From: fish Date: Mon, 6 Jul 2026 12:57:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E6=A0=8F=E5=BA=95=E9=83=A8?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E7=99=BB=E5=BD=95=E7=94=A8=E6=88=B7=E5=90=8D?= =?UTF-8?q?=20+=20=E6=9A=97=E5=A4=9C=E6=A8=A1=E5=BC=8F=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- serve/frontend/src/components/Layout.tsx | 103 +++++++++++++++++------ 1 file changed, 78 insertions(+), 25 deletions(-) diff --git a/serve/frontend/src/components/Layout.tsx b/serve/frontend/src/components/Layout.tsx index ce6a2d5..a1d891a 100644 --- a/serve/frontend/src/components/Layout.tsx +++ b/serve/frontend/src/components/Layout.tsx @@ -1,5 +1,6 @@ -import { NavLink, Outlet } from 'react-router-dom' -import { useQuery } from '@tanstack/react-query' +import { useState } from 'react' +import { NavLink, Outlet, useNavigate } from 'react-router-dom' +import { useQuery, useQueryClient } from '@tanstack/react-query' import { motion } from 'framer-motion' import { ToastContainer } from '@/components/Toast' import { AlertToastContainer } from '@/components/AlertToast' @@ -10,7 +11,6 @@ import { StockAnalysisBubble } from '@/components/stock-analysis/StockAnalysisBu import { useSettings, usePreferences, - useVersion, } from '@/lib/useSharedQueries' import { QK } from '@/lib/queryKeys' import { @@ -25,6 +25,10 @@ import { Layers3, Landmark, BookOpenCheck, + Moon, + Sun, + User, + LogOut, } from 'lucide-react' import { Logo } from './Logo' import { api } from '@/lib/api' @@ -76,14 +80,42 @@ function AIConfigBadge({ configured, model }: { configured?: boolean; model?: st export function Layout() { // ===== 共享 hooks (替代内联 useQuery) ===== const { data: settingsState } = useSettings() - const { data: versionData } = useVersion() const { data: prefs } = usePreferences() const { data: analysisMenus } = useQuery({ queryKey: QK.analysisMenus, queryFn: api.analysisMenus, }) + const { data: authData } = useQuery({ + queryKey: ['auth-status'], + queryFn: api.authStatus, + refetchInterval: 60_000, + }) - const version = versionData?.version + const username = authData?.username + const navigate = useNavigate() + const qc = useQueryClient() + const [darkMode, setDarkMode] = useState(() => { + if (typeof document === 'undefined') return true + try { + const saved = localStorage.getItem('theme') + if (saved === 'light' || saved === 'dark') return saved === 'dark' + } catch {} + return document.documentElement.classList.contains('dark') + }) + + // 切换暗夜模式 + const toggleDark = () => { + const next = !darkMode + setDarkMode(next) + document.documentElement.classList.toggle('dark', next) + try { localStorage.setItem('theme', next ? 'dark' : 'light') } catch {} + } + + const handleLogout = async () => { + try { await api.authLogout() } catch {} + qc.invalidateQueries({ queryKey: ['auth-status'] }) + navigate('/login', { replace: true }) + } // 合并内置页面 + 可见的扩展分析菜单 const analysisNav = (analysisMenus?.items ?? []) @@ -161,26 +193,47 @@ export function Layout() { ))} -
- - cn( - 'flex items-center justify-between gap-3 px-3 py-2 rounded-btn text-sm transition-colors duration-150 ease-smooth', - isActive - ? 'bg-elevated text-foreground font-medium' - : 'text-foreground/80 hover:bg-elevated hover:text-foreground', - ) - } - > - - - 设置 - - - {version ?? ''} - - +
+ {/* 登录用户 */} + {username && ( +
+ + {username} + +
+ )} + + {/* 暗夜模式 + 设置 */} +
+ + + cn( + 'flex items-center justify-center rounded-btn p-1.5 text-xs transition-colors', + isActive + ? 'bg-accent/10 text-accent' + : 'text-foreground/70 hover:bg-elevated hover:text-foreground', + ) + } + title="设置" + > + + +