11import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded" ;
2- import { Avatar , Link } from "@mui/material" ;
2+ import KeyboardArrowUpRoundedIcon from "@mui/icons-material/KeyboardArrowUpRounded" ;
3+ import { Avatar , Fab , Fade , Link } from "@mui/material" ;
34import AppBar from "@mui/material/AppBar" ;
45import Chip from "@mui/material/Chip" ;
56import IconButton from "@mui/material/IconButton" ;
@@ -9,12 +10,13 @@ import Tooltip from "@mui/material/Tooltip";
910import Typography from "@mui/material/Typography" ;
1011import { DashboardLayout } from "@toolpad/core/DashboardLayout" ;
1112import { PageContainer } from "@toolpad/core/PageContainer" ;
13+ import { useEffect , useState } from "react" ;
1214import { useTranslation } from "react-i18next" ;
1315import { Outlet , useLocation , useNavigate } from "react-router-dom" ;
1416import AddModal from "@/components/AddModal" ;
1517import { SearchBox } from "@/components/SearchBox" ;
1618import { Toolbars } from "@/components/Toolbar" ;
17- import { saveScrollPosition } from "@/utils/scroll" ;
19+ import { saveScrollPosition , scrollToTop } from "@/utils/scroll" ;
1820
1921/**
2022 * 侧边栏底部信息组件
@@ -122,6 +124,52 @@ const Header = () => (
122124 </ AppBar >
123125) ;
124126
127+ const BackToTopButton = ( ) => {
128+ const { t } = useTranslation ( ) ;
129+ const location = useLocation ( ) ;
130+ const [ visible , setVisible ] = useState ( false ) ;
131+
132+ useEffect ( ( ) => {
133+ const container = document . querySelector < HTMLElement > ( "main" ) ;
134+ if ( ! container ) return ;
135+
136+ const updateVisible = ( ) => {
137+ setVisible ( container . scrollTop > 320 ) ;
138+ } ;
139+
140+ updateVisible ( ) ;
141+ container . addEventListener ( "scroll" , updateVisible , { passive : true } ) ;
142+
143+ return ( ) => {
144+ container . removeEventListener ( "scroll" , updateVisible ) ;
145+ } ;
146+ } , [ ] ) ;
147+
148+ const label = t ( "components.AppLayout.backToTop" , "返回顶部" ) ;
149+
150+ return (
151+ < Fade in = { visible } unmountOnExit >
152+ < Tooltip title = { label } enterDelay = { 1000 } >
153+ < Fab
154+ color = "primary"
155+ size = "small"
156+ aria-label = { label }
157+ className = "print:hidden"
158+ onClick = { ( ) => scrollToTop ( location . pathname ) }
159+ sx = { {
160+ position : "fixed" ,
161+ right : { xs : 16 , sm : 24 } ,
162+ bottom : { xs : 16 , sm : 24 } ,
163+ zIndex : 40 ,
164+ } }
165+ >
166+ < KeyboardArrowUpRoundedIcon />
167+ </ Fab >
168+ </ Tooltip >
169+ </ Fade >
170+ ) ;
171+ } ;
172+
125173/**
126174 * 应用主布局组件
127175 * 集成侧边栏、顶部工具栏、页面容器等,支持自定义标题、国际化和响应式布局。
@@ -157,6 +205,7 @@ export const Layout: React.FC = () => {
157205 ) : (
158206 < Outlet />
159207 ) }
208+ < BackToTopButton />
160209 </ DashboardLayout >
161210 </ >
162211 ) ;
0 commit comments