Skip to content

Commit 56c2a46

Browse files
committed
feat: 优化手机设置页入口并新增子页面进入动画
1 parent 76341d4 commit 56c2a46

4 files changed

Lines changed: 266 additions & 52 deletions

File tree

src/App.tsx

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Layout, Modal, Input, message, ConfigProvider, theme as antTheme } from "antd"
2+
import { HomeOutlined, SettingOutlined } from "@ant-design/icons"
23
import { useEffect, useMemo, useRef, useState } from "react"
34
import { HashRouter, useLocation, useNavigate, Routes, Route } from "react-router-dom"
45
import { useTranslation } from "react-i18next"
@@ -352,6 +353,7 @@ function MainContent(): React.JSX.Element {
352353

353354
const isDark = currentTheme?.mode === "dark"
354355
const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9"
356+
const showMobileBottomNav = isPortraitMode && !immersiveMode
355357

356358
return (
357359
<ConfigProvider
@@ -365,25 +367,27 @@ function MainContent(): React.JSX.Element {
365367
}}
366368
>
367369
{contextHolder}
368-
<Layout style={{ height: "100%", flexDirection: "row", overflow: "hidden" }}>
369-
<div
370-
className={`ss-immersive-sidebar ${immersiveMode ? "is-hidden" : "is-visible"}`}
371-
style={
372-
{
373-
"--ss-sidebar-width": `${sidebarCollapsed ? 64 : 200}px`,
374-
} as React.CSSProperties
375-
}
376-
>
377-
<Sidebar
378-
activeMenu={activeMenu}
379-
permission={permission}
380-
onMenuChange={onMenuChange}
381-
collapsed={sidebarCollapsed}
382-
floatingExpand={isPortraitMode}
383-
floatingExpanded={floatingSidebarExpanded}
384-
onFloatingExpandedChange={setFloatingSidebarExpanded}
385-
/>
386-
</div>
370+
<Layout style={{ height: "100%", flexDirection: "row", overflow: "hidden", position: "relative" }}>
371+
{!isPortraitMode && (
372+
<div
373+
className={`ss-immersive-sidebar ${immersiveMode ? "is-hidden" : "is-visible"}`}
374+
style={
375+
{
376+
"--ss-sidebar-width": `${sidebarCollapsed ? 64 : 200}px`,
377+
} as React.CSSProperties
378+
}
379+
>
380+
<Sidebar
381+
activeMenu={activeMenu}
382+
permission={permission}
383+
onMenuChange={onMenuChange}
384+
collapsed={sidebarCollapsed}
385+
floatingExpand={isPortraitMode}
386+
floatingExpanded={floatingSidebarExpanded}
387+
onFloatingExpandedChange={setFloatingSidebarExpanded}
388+
/>
389+
</div>
390+
)}
387391
<ContentArea
388392
permission={permission}
389393
hasAnyPassword={hasAnyPassword}
@@ -398,7 +402,70 @@ function MainContent(): React.JSX.Element {
398402
immersiveMode={immersiveMode}
399403
isHomePage={activeMenu === "home"}
400404
onToggleImmersiveMode={toggleImmersiveMode}
405+
showSidebarToggle={!isPortraitMode}
406+
bottomInset={showMobileBottomNav ? 84 : 0}
401407
/>
408+
{showMobileBottomNav && (
409+
<div
410+
style={{
411+
position: "fixed",
412+
left: 0,
413+
right: 0,
414+
bottom: 0,
415+
background: "var(--ss-header-bg)",
416+
borderTop: "1px solid var(--ss-border-color)",
417+
zIndex: 1400,
418+
paddingBottom: "env(safe-area-inset-bottom, 0px)",
419+
}}
420+
>
421+
<div style={{ display: "flex", height: "60px" }}>
422+
<button
423+
type="button"
424+
onClick={() => onMenuChange("home")}
425+
style={{
426+
flex: 1,
427+
border: "none",
428+
background: "transparent",
429+
color:
430+
activeMenu === "home"
431+
? "var(--ant-color-primary)"
432+
: "var(--ss-text-secondary, var(--ss-text-main))",
433+
display: "flex",
434+
flexDirection: "column",
435+
alignItems: "center",
436+
justifyContent: "center",
437+
gap: "2px",
438+
fontSize: "12px",
439+
}}
440+
>
441+
<HomeOutlined style={{ fontSize: "18px" }} />
442+
<span>{t("sidebar.home")}</span>
443+
</button>
444+
<button
445+
type="button"
446+
onClick={() => onMenuChange("settings")}
447+
style={{
448+
flex: 1,
449+
border: "none",
450+
background: "transparent",
451+
color:
452+
activeMenu === "home"
453+
? "var(--ss-text-secondary, var(--ss-text-main))"
454+
: "var(--ant-color-primary)",
455+
display: "flex",
456+
flexDirection: "column",
457+
alignItems: "center",
458+
justifyContent: "center",
459+
gap: "2px",
460+
fontSize: "12px",
461+
}}
462+
>
463+
<SettingOutlined style={{ fontSize: "18px" }} />
464+
<span>{t("sidebar.settings")}</span>
465+
</button>
466+
</div>
467+
</div>
468+
)}
402469

403470
<OOBE visible={wizardVisible} onComplete={() => setWizardVisible(false)} />
404471

src/assets/main.css

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,74 @@ html.platform-macos #root {
270270
pointer-events: none;
271271
}
272272

273+
.ss-settings-mobile-nav-list {
274+
display: flex;
275+
flex-direction: column;
276+
}
277+
278+
.ss-settings-mobile-nav-item.ant-btn {
279+
min-height: 48px;
280+
width: 100%;
281+
border-radius: 0;
282+
justify-content: space-between;
283+
align-items: center;
284+
padding: 12px 16px;
285+
color: var(--ss-text-main);
286+
border-bottom: 1px solid var(--ss-divider, rgba(5, 5, 5, 0.06));
287+
}
288+
289+
.ss-settings-mobile-nav-item.ant-btn:last-child {
290+
border-bottom: none;
291+
}
292+
293+
.ss-settings-mobile-nav-item.ant-btn:hover,
294+
.ss-settings-mobile-nav-item.ant-btn:focus-visible {
295+
background: var(--ss-item-hover, rgba(0, 0, 0, 0.04));
296+
}
297+
298+
.ss-settings-mobile-nav-item.ant-btn.is-active {
299+
color: var(--ant-color-primary, #1677ff);
300+
background: color-mix(in srgb, var(--ant-color-primary, #1677ff) 10%, transparent);
301+
}
302+
303+
.ss-settings-mobile-nav-item.ant-btn[disabled] {
304+
color: var(--ss-text-secondary);
305+
opacity: 0.65;
306+
}
307+
308+
.ss-settings-mobile-nav-item-label {
309+
font-size: 15px;
310+
font-weight: 500;
311+
}
312+
313+
.ss-settings-mobile-nav-item-arrow {
314+
font-size: 12px;
315+
}
316+
317+
.ss-route-page {
318+
min-height: 100%;
319+
}
320+
321+
.ss-route-page.is-subpage-enter {
322+
animation: ss-subpage-enter 240ms cubic-bezier(0.22, 1, 0.36, 1);
323+
}
324+
325+
@keyframes ss-subpage-enter {
326+
from {
327+
opacity: 0;
328+
transform: translate3d(20px, 0, 0);
329+
}
330+
to {
331+
opacity: 1;
332+
transform: translate3d(0, 0, 0);
333+
}
334+
}
335+
273336
@media (prefers-reduced-motion: reduce) {
274337
.ss-immersive-sidebar,
275-
.ss-immersive-toolbar {
338+
.ss-immersive-toolbar,
339+
.ss-route-page.is-subpage-enter {
276340
transition: none !important;
341+
animation: none !important;
277342
}
278343
}

src/components/ContentArea.tsx

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
FullscreenOutlined,
77
FullscreenExitOutlined,
88
} from "@ant-design/icons"
9-
import { Routes, Route, Navigate } from "react-router-dom"
9+
import { Routes, Route, Navigate, useLocation } from "react-router-dom"
1010
import { useTranslation } from "react-i18next"
1111
import { WindowControls } from "./WindowControls"
1212
import appLogo from "../assets/logoHD.svg"
@@ -67,6 +67,8 @@ interface ContentAreaProps {
6767
immersiveMode: boolean
6868
isHomePage: boolean
6969
onToggleImmersiveMode: () => void
70+
showSidebarToggle?: boolean
71+
bottomInset?: number
7072
}
7173

7274
export function ContentArea({
@@ -83,8 +85,13 @@ export function ContentArea({
8385
immersiveMode,
8486
isHomePage,
8587
onToggleImmersiveMode,
88+
showSidebarToggle = true,
89+
bottomInset = 0,
8690
}: ContentAreaProps): React.JSX.Element {
8791
const { t } = useTranslation()
92+
const location = useLocation()
93+
const isSubPage = location.pathname !== "/" && !location.pathname.startsWith("/home")
94+
const shouldAnimateSubPage = isPortraitMode && isSubPage
8895

8996
useEffect(() => {
9097
let cancelled = false
@@ -156,7 +163,7 @@ export function ContentArea({
156163
} as React.CSSProperties
157164
}
158165
>
159-
{!immersiveMode && (
166+
{!immersiveMode && showSidebarToggle && (
160167
<Button
161168
type="text"
162169
size="small"
@@ -246,7 +253,14 @@ export function ContentArea({
246253
{showWindowControls && <WindowControls />}
247254
</div>
248255

249-
<Content style={{ flex: 1, overflowY: "auto", overflowX: "hidden" }}>
256+
<Content
257+
style={{
258+
flex: 1,
259+
overflowY: "auto",
260+
overflowX: "hidden",
261+
paddingBottom: bottomInset ? `${bottomInset}px` : 0,
262+
}}
263+
>
250264
<Suspense
251265
fallback={
252266
<div
@@ -263,34 +277,47 @@ export function ContentArea({
263277
</div>
264278
}
265279
>
266-
<Routes>
267-
<Route
268-
path="/"
269-
element={
270-
<Home
271-
canEdit={permission === "admin" || permission === "points"}
272-
isPortraitMode={isPortraitMode}
273-
immersiveMode={immersiveMode}
274-
/>
275-
}
276-
/>
277-
<Route path="/students" element={<StudentManager canEdit={permission === "admin"} />} />
278-
<Route
279-
path="/score"
280-
element={<ScoreManager canEdit={permission === "admin" || permission === "points"} />}
281-
/>
282-
<Route path="/boards" element={<BoardManager canManage={permission === "admin"} />} />
283-
<Route path="/leaderboard" element={<Leaderboard />} />
284-
<Route path="/settlements" element={<SettlementHistory />} />
285-
<Route path="/reasons" element={<ReasonManager canEdit={permission === "admin"} />} />
286-
<Route path="/auto-score" element={<AutoScoreManager />} />
287-
<Route
288-
path="/reward-settings"
289-
element={<RewardSettings canEdit={permission === "admin"} />}
290-
/>
291-
<Route path="/settings" element={<Settings permission={permission} />} />
292-
<Route path="*" element={<Navigate to="/" replace />} />
293-
</Routes>
280+
<div
281+
key={location.pathname}
282+
className={`ss-route-page${shouldAnimateSubPage ? " is-subpage-enter" : ""}`}
283+
>
284+
<Routes>
285+
<Route
286+
path="/"
287+
element={
288+
<Home
289+
canEdit={permission === "admin" || permission === "points"}
290+
isPortraitMode={isPortraitMode}
291+
immersiveMode={immersiveMode}
292+
/>
293+
}
294+
/>
295+
<Route
296+
path="/students"
297+
element={<StudentManager canEdit={permission === "admin"} />}
298+
/>
299+
<Route
300+
path="/score"
301+
element={
302+
<ScoreManager canEdit={permission === "admin" || permission === "points"} />
303+
}
304+
/>
305+
<Route path="/boards" element={<BoardManager canManage={permission === "admin"} />} />
306+
<Route path="/leaderboard" element={<Leaderboard />} />
307+
<Route path="/settlements" element={<SettlementHistory />} />
308+
<Route path="/reasons" element={<ReasonManager canEdit={permission === "admin"} />} />
309+
<Route path="/auto-score" element={<AutoScoreManager />} />
310+
<Route
311+
path="/reward-settings"
312+
element={<RewardSettings canEdit={permission === "admin"} />}
313+
/>
314+
<Route
315+
path="/settings"
316+
element={<Settings permission={permission} mobileNavigationEnabled={isPortraitMode} />}
317+
/>
318+
<Route path="*" element={<Navigate to="/" replace />} />
319+
</Routes>
320+
</div>
294321
</Suspense>
295322
</Content>
296323
</Layout>

0 commit comments

Comments
 (0)