fix(练习): 修复嵌套滚动和底部空白问题

- Layout: 将 h-screen 改为 min-h-screen,移除嵌套滚动容器
- ProcessPracticePage: 底部区域从 fixed 改为 sticky,移除动态高度计算
- 使用 flex 布局管理页面结构,消除双滚动条和大片空白
- 清理未使用的 state 和 imports

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
ittoview
2026-03-01 16:31:02 +00:00
parent 08bd8dd4dc
commit 8f96865ebf
2 changed files with 8 additions and 33 deletions

View File

@@ -14,19 +14,19 @@ export function Layout({ children }: LayoutProps) {
return ( return (
<div className={clsx('min-h-screen', darkMode && 'dark')}> <div className={clsx('min-h-screen', darkMode && 'dark')}>
<div className="flex h-screen bg-gray-50 dark:bg-gray-900"> <div className="flex min-h-screen bg-gray-50 dark:bg-gray-900">
{/* 侧边栏 */} {/* 侧边栏 */}
<Sidebar /> <Sidebar />
{/* 主内容区 */} {/* 主内容区 */}
<div className="flex flex-1 flex-col overflow-hidden"> <div className="flex flex-1 flex-col">
{/* 顶部导航 */} {/* 顶部导航 */}
<Header /> <Header />
{/* 页面内容 */} {/* 页面内容 */}
<main <main
className={clsx( className={clsx(
'flex-1 overflow-y-auto p-6 transition-all duration-300', 'flex-1 p-6 transition-all duration-300',
sidebarOpen ? 'lg:ml-64' : 'lg:ml-20' sidebarOpen ? 'lg:ml-64' : 'lg:ml-20'
)} )}
> >

View File

@@ -9,14 +9,10 @@ import {
import { PracticeMatrix } from '@/components/practice/PracticeMatrix' import { PracticeMatrix } from '@/components/practice/PracticeMatrix'
import { InputArea } from '@/components/practice/InputArea' import { InputArea } from '@/components/practice/InputArea'
import { HintInfo } from '@/components/practice/HintInfo' import { HintInfo } from '@/components/practice/HintInfo'
import { useAppStore } from '@/stores/useAppStore'
import { clsx } from 'clsx'
type CharStatus = 'pending' | 'correct' | 'error' type CharStatus = 'pending' | 'correct' | 'error'
export default function ProcessPracticePage() { export default function ProcessPracticePage() {
const sidebarOpen = useAppStore((s) => s.sidebarOpen)
// 生成格子顺序 // 生成格子顺序
const [cellSequence] = useState<CellInfo[]>(() => generateCellSequence()) const [cellSequence] = useState<CellInfo[]>(() => generateCellSequence())
@@ -64,8 +60,6 @@ export default function ProcessPracticePage() {
} | null>(null) } | null>(null)
const [inputLocked, setInputLocked] = useState(false) const [inputLocked, setInputLocked] = useState(false)
const latestInputRef = useRef<string[]>([]) const latestInputRef = useRef<string[]>([])
const bottomAreaRef = useRef<HTMLDivElement>(null)
const [bottomHeight, setBottomHeight] = useState(0)
// 初始化输入框 // 初始化输入框
useEffect(() => { useEffect(() => {
@@ -96,19 +90,6 @@ export default function ProcessPracticePage() {
} }
}, [answeredCells, currentCellId]) }, [answeredCells, currentCellId])
// 动态计算底部固定区域高度
useEffect(() => {
const updateBottomHeight = () => {
if (bottomAreaRef.current) {
setBottomHeight(bottomAreaRef.current.offsetHeight)
}
}
updateBottomHeight()
window.addEventListener('resize', updateBottomHeight)
return () => window.removeEventListener('resize', updateBottomHeight)
}, [userInput.length]) // 当输入框数量变化时重新计算
// 切换到指定格子 // 切换到指定格子
const switchToCell = useCallback( const switchToCell = useCallback(
(cell: CellInfo) => { (cell: CellInfo) => {
@@ -371,7 +352,7 @@ export default function ProcessPracticePage() {
}, [cellSequence]) }, [cellSequence])
return ( return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900"> <div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex flex-col">
{/* 顶部进度条 */} {/* 顶部进度条 */}
<div className="sticky top-0 z-20 bg-white dark:bg-gray-800 shadow-sm"> <div className="sticky top-0 z-20 bg-white dark:bg-gray-800 shadow-sm">
<div className="max-w-7xl mx-auto px-4 py-3"> <div className="max-w-7xl mx-auto px-4 py-3">
@@ -405,7 +386,7 @@ export default function ProcessPracticePage() {
</div> </div>
{/* 主内容区域 */} {/* 主内容区域 */}
<div style={{ paddingBottom: `${bottomHeight}px` }}> <div className="flex-1 overflow-y-auto pb-10">
{/* 矩阵区域 */} {/* 矩阵区域 */}
<div className="max-w-7xl mx-auto py-4"> <div className="max-w-7xl mx-auto py-4">
<PracticeMatrix <PracticeMatrix
@@ -420,15 +401,9 @@ export default function ProcessPracticePage() {
</div> </div>
</div> </div>
{/* 底部固定区域 */} {/* 底部固定区域(粘附底部,参与文档流) */}
<div <div className="sticky bottom-0 bg-white/60 dark:bg-gray-800/60 backdrop-blur-md border-t border-gray-200 dark:border-gray-700 z-10">
ref={bottomAreaRef} <div className="max-w-7xl mx-auto px-6">
className={clsx(
'fixed bottom-0 left-0 right-0 bg-white/60 dark:bg-gray-800/60 backdrop-blur-md border-t border-gray-200 dark:border-gray-700 z-10 transition-all duration-300',
sidebarOpen ? 'lg:ml-64' : 'lg:ml-20'
)}
>
<div className="max-w-7xl mx-auto">
{/* 输入区域 */} {/* 输入区域 */}
<div className="py-4 border-b border-gray-200/50 dark:border-gray-700/50"> <div className="py-4 border-b border-gray-200/50 dark:border-gray-700/50">
<div className="flex items-center justify-center gap-3"> <div className="flex items-center justify-center gap-3">