From de8af343161546d43a16bcd476477f4e08d7a5cf Mon Sep 17 00:00:00 2001 From: JSR Date: Fri, 27 Mar 2026 21:08:54 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B2=89=E6=B5=B8?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E6=90=9C=E7=B4=A2=E9=94=AE=E7=9B=98=E4=B8=8E?= =?UTF-8?q?=E4=B8=8B=E6=8B=89=E5=B1=82=E7=BA=A7=E6=98=BE=E7=A4=BA=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Home.tsx b/src/components/Home.tsx index 0cd52af..4caf53d 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -117,6 +117,7 @@ export const Home: React.FC = ({ const scrollContainerRef = useRef(null) const groupRefs = useRef>({}) const searchAreaRef = useRef(null) + const immersiveToolbarRef = useRef(null) const [selectedStudent, setSelectedStudent] = useState(null) const [batchMode, setBatchMode] = useState(false) @@ -376,6 +377,10 @@ export const Home: React.FC = ({ setSearchKeyword((prev) => `${prev}${keyValue}`) } + const getImmersivePopupContainer = useCallback((triggerNode: HTMLElement) => { + return immersiveToolbarRef.current ?? triggerNode.parentElement ?? document.body + }, []) + const getDisplayText = (name: string) => { if (!name) return "" return name.length > 2 ? name.substring(name.length - 2) : name @@ -2469,6 +2474,8 @@ export const Home: React.FC = ({ {immersiveMode && (
= ({ WebkitBackdropFilter: "blur(16px)", boxShadow: "0 8px 30px rgba(0, 0, 0, 0.16)", padding: "10px", - overflow: "hidden", + overflow: "visible", }} >
= ({ setLayoutType(v as LayoutType)} + getPopupContainer={getImmersivePopupContainer} style={{ width: 126, flexShrink: 0 }} options={[ { value: "grouped", label: t("home.layoutBy.grouped") }, From e6d1e03238432f03b4d688939e22fe7579404fb9 Mon Sep 17 00:00:00 2001 From: JSR Date: Fri, 27 Mar 2026 21:36:48 +0800 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=E7=B2=BE=E7=AE=80=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E6=96=87=E6=A1=88=E6=8C=89=E9=92=AE=E6=96=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/i18n/locales/zh-CN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index a6b1188..f82b8fb 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -423,7 +423,7 @@ "operationTitle": "积分操作:{{name}}", "submitOperation": "提交操作", "operationTitleBatch": "积分操作:已选 {{count}} 人", - "undoLastAction": "撤销上一步", + "undoLastAction": "撤销", "undoLastHint": "撤销上一条:{{name}} {{delta}}", "undoUnavailable": "当前没有可撤销的积分操作", "undoLastSuccess": "已撤销上一步积分操作", @@ -592,7 +592,7 @@ }, "rewardExchange": { "title": "奖励兑换", - "enterMode": "进入奖励兑换模式", + "enterMode": "奖励兑换", "exitMode": "退出奖励兑换模式", "modeHint": "当前为奖励兑换模式,点击学生卡可选择可兑换奖励。", "normalHint": "当前显示学生原始积分,点击按钮后将显示可兑换积分。", From 56d56e34ab3940c20def1489e6fc7ec2d81020f4 Mon Sep 17 00:00:00 2001 From: JSR Date: Fri, 27 Mar 2026 21:47:47 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=B2=89?= =?UTF-8?q?=E6=B5=B8=E6=A8=A1=E5=BC=8F=E5=88=87=E6=8D=A2=E8=BF=87=E6=B8=A1?= =?UTF-8?q?=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 57 +++++++++++++++++++++++++++++++++-------- src/assets/main.css | 42 ++++++++++++++++++++++++++++++ src/components/Home.tsx | 50 ++++++++++++++++++------------------ 3 files changed, 114 insertions(+), 35 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 55bb376..f5415fc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,8 @@ import { ContentArea } from "./components/ContentArea" import { OOBE } from "./components/OOBE/OOBE" import { ThemeProvider, useTheme } from "./contexts/ThemeContext" +const IMMERSIVE_TRANSITION_MS = 260 + function MainContent(): React.JSX.Element { const { t } = useTranslation() const navigate = useNavigate() @@ -15,6 +17,8 @@ function MainContent(): React.JSX.Element { const [messageApi, contextHolder] = message.useMessage() const { isIosDevice, isAndroidDevice, defaultPortraitMode } = useMemo(getMobileDeviceInfo, []) const [immersiveMode, setImmersiveMode] = useState(false) + const [sidebarVisible, setSidebarVisible] = useState(true) + const sidebarTransitionTimerRef = useRef(null) useEffect(() => { const api = (window as any).api @@ -350,6 +354,30 @@ function MainContent(): React.JSX.Element { }) } + useEffect(() => { + if (sidebarTransitionTimerRef.current) { + window.clearTimeout(sidebarTransitionTimerRef.current) + sidebarTransitionTimerRef.current = null + } + + if (!immersiveMode) { + setSidebarVisible(true) + return + } + + sidebarTransitionTimerRef.current = window.setTimeout(() => { + setSidebarVisible(false) + sidebarTransitionTimerRef.current = null + }, IMMERSIVE_TRANSITION_MS) + + return () => { + if (sidebarTransitionTimerRef.current) { + window.clearTimeout(sidebarTransitionTimerRef.current) + sidebarTransitionTimerRef.current = null + } + } + }, [immersiveMode]) + const isDark = currentTheme?.mode === "dark" const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9" @@ -366,16 +394,25 @@ function MainContent(): React.JSX.Element { > {contextHolder} - {!immersiveMode && ( - + {sidebarVisible && ( +
+ +
)} = ({ )} - {immersiveMode && ( -
+
= ({
)}
-
- )} +
{isPortraitMode ? ( Date: Fri, 27 Mar 2026 21:49:01 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E5=BA=95=E9=83=A8=E7=81=B0=E8=89=B2=E7=A9=BA=E9=9A=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/main.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/assets/main.css b/src/assets/main.css index d3b3279..79996a0 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -228,8 +228,12 @@ html.platform-macos #root { .ss-immersive-sidebar { --ss-sidebar-width: 200px; + height: 100%; + display: flex; + flex-shrink: 0; width: var(--ss-sidebar-width); overflow: hidden; + background: var(--ss-sidebar-bg); opacity: 1; transform: translateX(0); transition: @@ -239,6 +243,10 @@ html.platform-macos #root { will-change: width, opacity, transform; } +.ss-immersive-sidebar .ant-layout-sider { + height: 100% !important; +} + .ss-immersive-sidebar.is-hidden { width: 0; opacity: 0; From 8ee3729596cdaba0d8373ac684670a1874b3ebf0 Mon Sep 17 00:00:00 2001 From: JSR Date: Fri, 27 Mar 2026 21:49:48 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=80=80=E5=87=BA?= =?UTF-8?q?=E6=B2=89=E6=B5=B8=E6=A8=A1=E5=BC=8F=E5=AF=BC=E8=88=AA=E6=A0=8F?= =?UTF-8?q?=E6=97=A0=E8=BF=87=E6=B8=A1=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 66 +++++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 48 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f5415fc..b8637d5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,8 +7,6 @@ import { ContentArea } from "./components/ContentArea" import { OOBE } from "./components/OOBE/OOBE" import { ThemeProvider, useTheme } from "./contexts/ThemeContext" -const IMMERSIVE_TRANSITION_MS = 260 - function MainContent(): React.JSX.Element { const { t } = useTranslation() const navigate = useNavigate() @@ -17,8 +15,6 @@ function MainContent(): React.JSX.Element { const [messageApi, contextHolder] = message.useMessage() const { isIosDevice, isAndroidDevice, defaultPortraitMode } = useMemo(getMobileDeviceInfo, []) const [immersiveMode, setImmersiveMode] = useState(false) - const [sidebarVisible, setSidebarVisible] = useState(true) - const sidebarTransitionTimerRef = useRef(null) useEffect(() => { const api = (window as any).api @@ -354,30 +350,6 @@ function MainContent(): React.JSX.Element { }) } - useEffect(() => { - if (sidebarTransitionTimerRef.current) { - window.clearTimeout(sidebarTransitionTimerRef.current) - sidebarTransitionTimerRef.current = null - } - - if (!immersiveMode) { - setSidebarVisible(true) - return - } - - sidebarTransitionTimerRef.current = window.setTimeout(() => { - setSidebarVisible(false) - sidebarTransitionTimerRef.current = null - }, IMMERSIVE_TRANSITION_MS) - - return () => { - if (sidebarTransitionTimerRef.current) { - window.clearTimeout(sidebarTransitionTimerRef.current) - sidebarTransitionTimerRef.current = null - } - } - }, [immersiveMode]) - const isDark = currentTheme?.mode === "dark" const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9" @@ -394,26 +366,24 @@ function MainContent(): React.JSX.Element { > {contextHolder} - {sidebarVisible && ( -
- -
- )} +
+ +
Date: Fri, 27 Mar 2026 21:53:04 +0800 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=E6=B2=89=E6=B5=B8=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=BA=95=E6=A0=8F=E9=95=BF=E5=BA=A6=E5=8F=98=E5=8C=96?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=9E=E5=BC=B9=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/main.css | 5 +++-- src/components/Home.tsx | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/assets/main.css b/src/assets/main.css index 79996a0..c889ddc 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -258,9 +258,10 @@ html.platform-macos #root { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); transition: + width 420ms cubic-bezier(0.2, 1.2, 0.26, 1), opacity 200ms ease, - transform 260ms cubic-bezier(0.22, 1, 0.36, 1); - will-change: opacity, transform; + transform 320ms cubic-bezier(0.2, 1.2, 0.26, 1); + will-change: width, opacity, transform; } .ss-immersive-toolbar.is-hidden { diff --git a/src/components/Home.tsx b/src/components/Home.tsx index be0c2fb..a6a1a99 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -118,6 +118,8 @@ export const Home: React.FC = ({ const groupRefs = useRef>({}) const searchAreaRef = useRef(null) const immersiveToolbarRef = useRef(null) + const immersiveToolbarContentRef = useRef(null) + const [immersiveToolbarWidth, setImmersiveToolbarWidth] = useState(null) const [selectedStudent, setSelectedStudent] = useState(null) const [batchMode, setBatchMode] = useState(false) @@ -321,6 +323,33 @@ export const Home: React.FC = ({ return () => document.removeEventListener("mousedown", onDocumentClick) }, []) + useEffect(() => { + if (!immersiveMode) return + const contentEl = immersiveToolbarContentRef.current + if (!contentEl) return + + let frameId: number | null = null + const updateToolbarWidth = () => { + if (frameId !== null) cancelAnimationFrame(frameId) + frameId = requestAnimationFrame(() => { + const contentWidth = Math.ceil(contentEl.getBoundingClientRect().width) + const nextWidth = contentWidth + 20 + setImmersiveToolbarWidth((prev) => (prev === nextWidth ? prev : nextWidth)) + }) + } + + updateToolbarWidth() + const observer = new ResizeObserver(updateToolbarWidth) + observer.observe(contentEl) + window.addEventListener("resize", updateToolbarWidth) + + return () => { + observer.disconnect() + window.removeEventListener("resize", updateToolbarWidth) + if (frameId !== null) cancelAnimationFrame(frameId) + } + }, [immersiveMode]) + useEffect(() => { return () => { if (longPressTimerRef.current !== null) { @@ -2481,9 +2510,8 @@ export const Home: React.FC = ({ position: "fixed", left: "50%", bottom: isPortraitMode ? "12px" : "16px", - transform: "translateX(-50%)", zIndex: 1100, - width: "max-content", + width: immersiveToolbarWidth ? `${immersiveToolbarWidth}px` : "max-content", maxWidth: "calc(100vw - 20px)", borderRadius: "999px", border: "1px solid color-mix(in srgb, var(--ss-border-color) 80%, transparent)", @@ -2497,7 +2525,10 @@ export const Home: React.FC = ({ }} >
{ + searchAreaRef.current = node + immersiveToolbarContentRef.current = node + }} style={{ display: "flex", alignItems: "center",