From 8849b547ce5043b026cd77cd2bcf5758e14133b3 Mon Sep 17 00:00:00 2001
From: hzm <934585316@qq.com>
Date: Thu, 12 Mar 2026 21:53:11 +0800
Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E8=A7=A3=E5=86=B3=E7=A7=BB?=
=?UTF-8?q?=E5=8A=A8=E7=AB=AF=20Dialog=20=E6=BB=9A=E5=8A=A8=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/hooks/useBodyScrollLock.js | 18 ++++++++++++++++++
components/ui/dialog.jsx | 29 ++++++++++++++++++++++++++++-
components/ui/drawer.jsx | 29 +++--------------------------
3 files changed, 49 insertions(+), 27 deletions(-)
create mode 100644 app/hooks/useBodyScrollLock.js
diff --git a/app/hooks/useBodyScrollLock.js b/app/hooks/useBodyScrollLock.js
new file mode 100644
index 0000000..4b76395
--- /dev/null
+++ b/app/hooks/useBodyScrollLock.js
@@ -0,0 +1,18 @@
+import { useEffect } from "react";
+
+export function useBodyScrollLock(open) {
+ useEffect(() => {
+ if (!open) return;
+
+ const scrollY = window.scrollY;
+
+ document.body.style.position = "fixed";
+ document.body.style.top = `-${scrollY}px`;
+
+ return () => {
+ document.body.style.position = "";
+ document.body.style.top = "";
+ window.scrollTo(0, scrollY);
+ };
+ }, [open]);
+}
\ No newline at end of file
diff --git a/components/ui/dialog.jsx b/components/ui/dialog.jsx
index 1038cfa..eb8f219 100644
--- a/components/ui/dialog.jsx
+++ b/components/ui/dialog.jsx
@@ -6,11 +6,38 @@ import { Dialog as DialogPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import {CloseIcon} from "@/app/components/Icons";
+import { useBodyScrollLock } from "../../app/hooks/useBodyScrollLock";
function Dialog({
+ open: openProp,
+ defaultOpen,
+ onOpenChange,
...props
}) {
- return ;
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
+ const isControlled = openProp !== undefined;
+ const currentOpen = isControlled ? openProp : uncontrolledOpen;
+
+ // 使用全局 hook 统一处理 body 滚动锁定 & 恢复,避免弹窗打开时页面跳到顶部
+ useBodyScrollLock(currentOpen);
+
+ const handleOpenChange = React.useCallback(
+ (next) => {
+ if (!isControlled) setUncontrolledOpen(next);
+ onOpenChange?.(next);
+ },
+ [isControlled, onOpenChange]
+ );
+
+ return (
+
+ );
}
function DialogTrigger({
diff --git a/components/ui/drawer.jsx b/components/ui/drawer.jsx
index e9c15f8..b06ffb6 100644
--- a/components/ui/drawer.jsx
+++ b/components/ui/drawer.jsx
@@ -4,6 +4,7 @@ import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul"
import { cn } from "@/lib/utils"
+import { useBodyScrollLock } from "../../app/hooks/useBodyScrollLock"
const DrawerScrollLockContext = React.createContext(null)
@@ -12,36 +13,12 @@ const DrawerScrollLockContext = React.createContext(null)
* 既锁定滚动又保留视觉位置;overlay 上 ontouchmove preventDefault 防止背景触摸滚动。
*/
function useScrollLock(open) {
- const savedScrollYRef = React.useRef(0)
const onOverlayTouchMove = React.useCallback((e) => {
e.preventDefault()
}, [])
- React.useEffect(() => {
- if (!open || typeof document === "undefined") return
- const scrollY = window.scrollY ?? window.pageYOffset
- savedScrollYRef.current = scrollY
- const prev = {
- position: document.body.style.position,
- top: document.body.style.top,
- left: document.body.style.left,
- right: document.body.style.right,
- width: document.body.style.width,
- }
- document.body.style.position = "fixed"
- document.body.style.top = `-${scrollY}px`
- document.body.style.left = "0"
- document.body.style.right = "0"
- document.body.style.width = "100%"
- return () => {
- document.body.style.position = prev.position
- document.body.style.top = prev.top
- document.body.style.left = prev.left
- document.body.style.right = prev.right
- document.body.style.width = prev.width
- window.scrollTo(0, savedScrollYRef.current)
- }
- }, [open])
+ // 统一使用 app 级 hook 处理 body 滚动锁定 & 恢复,避免多处实现导致位移/跳顶问题
+ useBodyScrollLock(open)
return React.useMemo(
() => (open ? { onTouchMove: onOverlayTouchMove } : null),