From 224c89f4dd4b1b586136723164825e4ed6a4bcf2 Mon Sep 17 00:00:00 2001 From: JSR Date: Wed, 18 Mar 2026 21:05:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A8=AA=E7=AB=96=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=88=87=E6=8D=A2=E5=B9=B6=E6=94=AF=E6=8C=81=E4=BE=A7?= =?UTF-8?q?=E6=A0=8F=E6=82=AC=E6=B5=AE=E5=B1=95=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 37 +++++++++- src/components/ContentArea.tsx | 11 +++ src/components/Sidebar.tsx | 131 +++++++++++++++++++++++++-------- 3 files changed, 146 insertions(+), 33 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 38a99c2..c44d7c9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -51,6 +51,8 @@ function MainContent(): React.JSX.Element { const [authVisible, setAuthVisible] = useState(false) const [authPassword, setAuthPassword] = useState("") const [authLoading, setAuthLoading] = useState(false) + const [isPortraitMode, setIsPortraitMode] = useState(false) + const [sidebarCollapsed, setSidebarCollapsed] = useState(false) const activeMenu = useMemo(() => { const p = location.pathname @@ -127,6 +129,30 @@ function MainContent(): React.JSX.Element { if (key === "settings") navigate("/settings") } + const toggleOrientationMode = async () => { + const api = (window as any).api + if (!api) return + + const nextPortraitMode = !isPortraitMode + try { + const maximized = await api.windowIsMaximized() + if (maximized) { + await api.windowMaximize() + } + if (nextPortraitMode) { + await api.windowResize(800, 1000) + setSidebarCollapsed(true) + } else { + await api.windowResize(1180, 680) + setSidebarCollapsed(false) + } + setIsPortraitMode(nextPortraitMode) + } catch (error) { + messageApi.error(t("common.error")) + console.error("Failed to toggle orientation mode:", error) + } + } + const isDark = currentTheme?.mode === "dark" const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9" @@ -141,12 +167,21 @@ function MainContent(): React.JSX.Element { > {contextHolder} - + setAuthVisible(true)} onLogout={logout} + isPortraitMode={isPortraitMode} + onToggleOrientation={toggleOrientationMode} /> setWizardVisible(false)} /> diff --git a/src/components/ContentArea.tsx b/src/components/ContentArea.tsx index 059ec76..698fc75 100644 --- a/src/components/ContentArea.tsx +++ b/src/components/ContentArea.tsx @@ -45,6 +45,8 @@ interface ContentAreaProps { hasAnyPassword: boolean onAuthClick: () => void onLogout: () => void + isPortraitMode: boolean + onToggleOrientation: () => void } export function ContentArea({ @@ -52,6 +54,8 @@ export function ContentArea({ hasAnyPassword, onAuthClick, onLogout, + isPortraitMode, + onToggleOrientation, }: ContentAreaProps): React.JSX.Element { const { t } = useTranslation() @@ -132,6 +136,13 @@ export function ContentArea({ } > + {permissionTag} {hasAnyPassword && ( <> diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 84b9e8e..73bf6b5 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -22,6 +22,9 @@ interface SidebarProps { activeMenu: string permission: "admin" | "points" | "view" onMenuChange: (value: string) => void + collapsed: boolean + floatingExpand: boolean + onCollapsedChange: (collapsed: boolean) => void } interface DbStatus { @@ -30,9 +33,16 @@ interface DbStatus { error?: string } -export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): React.JSX.Element { +export function Sidebar({ + activeMenu, + permission, + onMenuChange, + collapsed, + floatingExpand, + onCollapsedChange, +}: SidebarProps): React.JSX.Element { const { t } = useTranslation() - const [collapsed, setCollapsed] = useState(false) + const [floatingExpanded, setFloatingExpanded] = useState(false) const [dbStatus, setDbStatus] = useState({ type: "sqlite", connected: true }) const [syncLoading, setSyncLoading] = useState(false) const [messageApi, contextHolder] = message.useMessage() @@ -95,6 +105,12 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): const [forceSyncLoading, setForceSyncLoading] = useState(false) + useEffect(() => { + if (!floatingExpand || !collapsed) { + setFloatingExpanded(false) + } + }, [floatingExpand, collapsed]) + const handleForceSync = async () => { if (!(window as any).api) return @@ -175,25 +191,15 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): }, ] - return ( - + const showFloatingPanel = floatingExpand && collapsed && floatingExpanded + + const renderSidebarBody = (isCollapsedView: boolean, isFloatingPanel = false) => ( + <>
setCollapsed((prev) => !prev)} - icon={collapsed ? : } - style={{ - position: "absolute", - top: "8px", - right: "8px", - WebkitAppRegion: "no-drag", + onClick={() => { + if (floatingExpand && collapsed) { + setFloatingExpanded((prev) => !prev) + return + } + onCollapsedChange(!collapsed) }} + icon={ + floatingExpand && collapsed + ? isFloatingPanel + ? + : + : isCollapsedView + ? + : + } + style={ + { + position: "absolute", + top: "8px", + right: "8px", + WebkitAppRegion: "no-drag", + } as React.CSSProperties + } /> logo - {!collapsed && ( + {!isCollapsedView && ( <>

onMenuChange(key)} + onClick={({ key }) => { + onMenuChange(key) + if (floatingExpand && collapsed) { + setFloatingExpanded(false) + } + }} style={{ width: "100%", border: "none", @@ -263,7 +290,7 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): />

- {!collapsed && dbStatus.type === "postgresql" && ( + {!isCollapsedView && dbStatus.type === "postgresql" && ( - {contextHolder}
@@ -324,6 +350,47 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): )} + + ) + + return ( + + {contextHolder} + {renderSidebarBody(collapsed)} + + {showFloatingPanel && ( +
+ {renderSidebarBody(false, true)} +
+ )}
) }