feat: 新增主页沉浸模式开关

This commit is contained in:
JSR
2026-03-27 20:55:04 +08:00
parent 86f0c722bc
commit f83e8905e1
2 changed files with 84 additions and 29 deletions
+43 -10
View File
@@ -14,6 +14,7 @@ function MainContent(): React.JSX.Element {
const { currentTheme } = useTheme() const { currentTheme } = useTheme()
const [messageApi, contextHolder] = message.useMessage() const [messageApi, contextHolder] = message.useMessage()
const { isIosDevice, isAndroidDevice, defaultPortraitMode } = useMemo(getMobileDeviceInfo, []) const { isIosDevice, isAndroidDevice, defaultPortraitMode } = useMemo(getMobileDeviceInfo, [])
const [immersiveMode, setImmersiveMode] = useState(false)
useEffect(() => { useEffect(() => {
const api = (window as any).api const api = (window as any).api
@@ -27,6 +28,11 @@ function MainContent(): React.JSX.Element {
const currentPath = location.pathname === "/" ? "/home" : location.pathname const currentPath = location.pathname === "/" ? "/home" : location.pathname
const targetPath = route === "/" ? "/home" : route const targetPath = route === "/" ? "/home" : route
if (immersiveMode && !targetPath.startsWith("/home")) {
navigate("/", { replace: true })
return
}
if (currentPath !== targetPath) { if (currentPath !== targetPath) {
navigate(route) navigate(route)
} }
@@ -44,7 +50,7 @@ function MainContent(): React.JSX.Element {
disposed = true disposed = true
if (unlisten) unlisten() if (unlisten) unlisten()
} }
}, [navigate, location.pathname]) }, [navigate, location.pathname, immersiveMode])
const [wizardVisible, setWizardVisible] = useState(false) const [wizardVisible, setWizardVisible] = useState(false)
const [permission, setPermission] = useState<"admin" | "points" | "view">("view") const [permission, setPermission] = useState<"admin" | "points" | "view">("view")
@@ -302,6 +308,7 @@ function MainContent(): React.JSX.Element {
const onMenuChange = (v: string) => { const onMenuChange = (v: string) => {
const key = String(v) const key = String(v)
if (immersiveMode && key !== "home") return
if (key === "home") navigate("/") if (key === "home") navigate("/")
if (key === "students") navigate("/students") if (key === "students") navigate("/students")
if (key === "score") navigate("/score") if (key === "score") navigate("/score")
@@ -315,6 +322,7 @@ function MainContent(): React.JSX.Element {
} }
const toggleSidebar = () => { const toggleSidebar = () => {
if (immersiveMode) return
if (isPortraitMode && sidebarCollapsed) { if (isPortraitMode && sidebarCollapsed) {
setFloatingSidebarExpanded((prev) => !prev) setFloatingSidebarExpanded((prev) => !prev)
return return
@@ -322,6 +330,26 @@ function MainContent(): React.JSX.Element {
setSidebarCollapsed((prev) => !prev) setSidebarCollapsed((prev) => !prev)
} }
useEffect(() => {
if (!immersiveMode) return
if (location.pathname !== "/" && !location.pathname.startsWith("/home")) {
navigate("/", { replace: true })
}
}, [immersiveMode, location.pathname, navigate])
const toggleImmersiveMode = () => {
setImmersiveMode((prev) => {
const next = !prev
if (next) {
setFloatingSidebarExpanded(false)
if (location.pathname !== "/" && !location.pathname.startsWith("/home")) {
navigate("/", { replace: true })
}
}
return next
})
}
const isDark = currentTheme?.mode === "dark" const isDark = currentTheme?.mode === "dark"
const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9" const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9"
@@ -338,15 +366,17 @@ function MainContent(): React.JSX.Element {
> >
{contextHolder} {contextHolder}
<Layout style={{ height: "100%", flexDirection: "row", overflow: "hidden" }}> <Layout style={{ height: "100%", flexDirection: "row", overflow: "hidden" }}>
<Sidebar {!immersiveMode && (
activeMenu={activeMenu} <Sidebar
permission={permission} activeMenu={activeMenu}
onMenuChange={onMenuChange} permission={permission}
collapsed={sidebarCollapsed} onMenuChange={onMenuChange}
floatingExpand={isPortraitMode} collapsed={sidebarCollapsed}
floatingExpanded={floatingSidebarExpanded} floatingExpand={isPortraitMode}
onFloatingExpandedChange={setFloatingSidebarExpanded} floatingExpanded={floatingSidebarExpanded}
/> onFloatingExpandedChange={setFloatingSidebarExpanded}
/>
)}
<ContentArea <ContentArea
permission={permission} permission={permission}
hasAnyPassword={hasAnyPassword} hasAnyPassword={hasAnyPassword}
@@ -358,6 +388,9 @@ function MainContent(): React.JSX.Element {
floatingExpand={isPortraitMode} floatingExpand={isPortraitMode}
floatingExpanded={floatingSidebarExpanded} floatingExpanded={floatingSidebarExpanded}
onToggleSidebar={toggleSidebar} onToggleSidebar={toggleSidebar}
immersiveMode={immersiveMode}
isHomePage={activeMenu === "home"}
onToggleImmersiveMode={toggleImmersiveMode}
/> />
<OOBE visible={wizardVisible} onComplete={() => setWizardVisible(false)} /> <OOBE visible={wizardVisible} onComplete={() => setWizardVisible(false)} />
+41 -19
View File
@@ -1,6 +1,11 @@
import React, { Suspense, lazy, useEffect } from "react" import React, { Suspense, lazy, useEffect } from "react"
import { Layout, Space, Button, Tag, Spin } from "antd" import { Layout, Space, Button, Tag, Spin } from "antd"
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons" import {
MenuFoldOutlined,
MenuUnfoldOutlined,
FullscreenOutlined,
FullscreenExitOutlined,
} from "@ant-design/icons"
import { Routes, Route, Navigate } from "react-router-dom" import { Routes, Route, Navigate } from "react-router-dom"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { WindowControls } from "./WindowControls" import { WindowControls } from "./WindowControls"
@@ -58,6 +63,9 @@ interface ContentAreaProps {
floatingExpand: boolean floatingExpand: boolean
floatingExpanded: boolean floatingExpanded: boolean
onToggleSidebar: () => void onToggleSidebar: () => void
immersiveMode: boolean
isHomePage: boolean
onToggleImmersiveMode: () => void
} }
export function ContentArea({ export function ContentArea({
@@ -71,6 +79,9 @@ export function ContentArea({
floatingExpand, floatingExpand,
floatingExpanded, floatingExpanded,
onToggleSidebar, onToggleSidebar,
immersiveMode,
isHomePage,
onToggleImmersiveMode,
}: ContentAreaProps): React.JSX.Element { }: ContentAreaProps): React.JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
@@ -144,26 +155,28 @@ export function ContentArea({
} as React.CSSProperties } as React.CSSProperties
} }
> >
<Button {!immersiveMode && (
type="text" <Button
size="small" type="text"
onClick={onToggleSidebar} size="small"
icon={ onClick={onToggleSidebar}
floatingExpand && sidebarCollapsed ? ( icon={
floatingExpanded ? ( floatingExpand && sidebarCollapsed ? (
<MenuFoldOutlined /> floatingExpanded ? (
) : ( <MenuFoldOutlined />
) : (
<MenuUnfoldOutlined />
)
) : sidebarCollapsed ? (
<MenuUnfoldOutlined /> <MenuUnfoldOutlined />
) : (
<MenuFoldOutlined />
) )
) : sidebarCollapsed ? ( }
<MenuUnfoldOutlined /> title={sidebarCollapsed ? "展开导航栏" : "收起导航栏"}
) : ( style={{ width: "32px", height: "32px" }}
<MenuFoldOutlined /> />
) )}
}
title={sidebarCollapsed ? "展开导航栏" : "收起导航栏"}
style={{ width: "32px", height: "32px" }}
/>
</div> </div>
<div <div
data-tauri-drag-region data-tauri-drag-region
@@ -186,6 +199,15 @@ export function ContentArea({
} }
> >
<Space size="small"> <Space size="small">
{(immersiveMode || isHomePage) && (
<Button
size="small"
type={immersiveMode ? "primary" : "default"}
icon={immersiveMode ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
onClick={onToggleImmersiveMode}
title={immersiveMode ? "退出沉浸模式" : "进入沉浸模式"}
/>
)}
{permissionTag} {permissionTag}
{hasAnyPassword && ( {hasAnyPassword && (
<> <>