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 (
<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 />
{/* 主内容区 */}
<div className="flex flex-1 flex-col overflow-hidden">
<div className="flex flex-1 flex-col">
{/* 顶部导航 */}
<Header />
{/* 页面内容 */}
<main
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'
)}
>

View File

@@ -9,14 +9,10 @@ import {
import { PracticeMatrix } from '@/components/practice/PracticeMatrix'
import { InputArea } from '@/components/practice/InputArea'
import { HintInfo } from '@/components/practice/HintInfo'
import { useAppStore } from '@/stores/useAppStore'
import { clsx } from 'clsx'
type CharStatus = 'pending' | 'correct' | 'error'
export default function ProcessPracticePage() {
const sidebarOpen = useAppStore((s) => s.sidebarOpen)
// 生成格子顺序
const [cellSequence] = useState<CellInfo[]>(() => generateCellSequence())
@@ -64,8 +60,6 @@ export default function ProcessPracticePage() {
} | null>(null)
const [inputLocked, setInputLocked] = useState(false)
const latestInputRef = useRef<string[]>([])
const bottomAreaRef = useRef<HTMLDivElement>(null)
const [bottomHeight, setBottomHeight] = useState(0)
// 初始化输入框
useEffect(() => {
@@ -96,19 +90,6 @@ export default function ProcessPracticePage() {
}
}, [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(
(cell: CellInfo) => {
@@ -371,7 +352,7 @@ export default function ProcessPracticePage() {
}, [cellSequence])
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="max-w-7xl mx-auto px-4 py-3">
@@ -405,7 +386,7 @@ export default function ProcessPracticePage() {
</div>
{/* 主内容区域 */}
<div style={{ paddingBottom: `${bottomHeight}px` }}>
<div className="flex-1 overflow-y-auto pb-10">
{/* 矩阵区域 */}
<div className="max-w-7xl mx-auto py-4">
<PracticeMatrix
@@ -420,15 +401,9 @@ export default function ProcessPracticePage() {
</div>
</div>
{/* 底部固定区域 */}
<div
ref={bottomAreaRef}
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="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">
<div className="max-w-7xl mx-auto px-6">
{/* 输入区域 */}
<div className="py-4 border-b border-gray-200/50 dark:border-gray-700/50">
<div className="flex items-center justify-center gap-3">