React + Antd 全高度滚动模板(可直接复用)
1. FullHeightLayout.module.less
.pageRoot {
height: 100%;
min-height: 0;
}
.grid {
display: flex;
flex-direction: column;
gap: 24px;
width: 100%;
height: auto;
min-height: 0;
}
@media (min-width: 1200px) {
.grid {
display: grid;
grid-template-columns: repeat(24, 1fr);
grid-template-rows: auto minmax(0, 1fr);
gap: 24px;
height: 100%;
min-height: 0;
}
}
.gridItem {
width: 100%;
height: auto;
min-height: 0;
min-width: 0;
}
@media (min-width: 1200px) {
.gridItem {
height: 100%;
min-height: 0;
}
}
.sectionCard {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.sectionInner {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tabsWrap {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tabsWrap :global(.ant-tabs) {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
.tabsWrap :global(.ant-tabs-content-holder) {
flex: 1;
min-height: 0;
overflow: hidden;
}
.tabsWrap :global(.ant-tabs-content) {
height: 100%;
min-height: 0;
}
.tabsWrap :global(.ant-tabs-tabpane) {
height: 100%;
min-height: 0;
}
.listPane {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.listHeader {
flex-shrink: 0;
padding-bottom: 12px;
}
.listBody {
flex: 1;
min-height: 0;
height: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.listBody :global(.ant-spin-nested-loading) {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
.listBody :global(.ant-spin-container) {
height: 100%;
min-height: 0;
overflow: auto;
}
.fillList {
height: 100%;
}
.tablePane {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tableBody {
flex: 1;
min-height: 0;
overflow: hidden;
}
.tableBody :global(.ant-table-wrapper),
.tableBody :global(.ant-spin-nested-loading),
.tableBody :global(.ant-spin-container),
.tableBody :global(.ant-table),
.tableBody :global(.ant-table-container) {
height: 100%;
min-height: 0;
}
.tableBody :global(.ant-table-body) {
overflow: auto !important;
}
2. FullHeightLayoutDemo.tsx
import React from 'react';
import { Card, Input, List, Table, Tabs } from 'antd';
import type { TabsProps, TableColumnsType } from 'antd';
import styles from './FullHeightLayout.module.less';
type RowData = {
id: number;
name: string;
idNo: string;
};
const mockList: RowData[] = Array.from({ length: 30 }).map((_, i) => ({
id: i + 1,
name: `考生-${i + 1}`,
idNo: `5002********${String(i).padStart(4, '0')}`,
}));
const mockCols: TableColumnsType<RowData> = [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '姓名', dataIndex: 'name' },
{ title: '证件号', dataIndex: 'idNo' },
];
const ListPane: React.FC = () => {
return (
<div className={styles.listPane}>
<div className={styles.listHeader}>
<Input.Search placeholder="考生查询" enterButton />
</div>
<div className={styles.listBody}>
<List
className={styles.fillList}
dataSource={mockList}
renderItem={(item) => (
<List.Item key={item.id}>
<Card style={{ width: '100%' }}>
{item.name} - {item.idNo}
</Card>
</List.Item>
)}
/>
</div>
</div>
);
};
const TablePane: React.FC = () => {
return (
<div className={styles.tablePane}>
<div className={styles.tableBody}>
<Table<RowData>
rowKey="id"
columns={mockCols}
dataSource={mockList}
pagination={{ pageSize: 10 }}
/>
</div>
</div>
);
};
const tabsItems: TabsProps['items'] = [
{ key: 'list', label: '列表', children: <ListPane /> },
{ key: 'table', label: '表格', children: <TablePane /> },
{ key: 'other', label: '其他', children: <div style={{ height: '100%' }}>内容</div> },
];
const FullHeightLayoutDemo: React.FC = () => {
return (
<div className={styles.pageRoot}>
<div className={styles.grid}>
<div className={styles.gridItem} style={{ gridColumn: 'span 18' }}>
<Card className={styles.sectionCard} styles={{ body: { height: '100%', padding: 12 } }}>
<div className={styles.sectionInner}>上半区内容</div>
</Card>
</div>
<div className={styles.gridItem} style={{ gridColumn: 'span 6' }}>
<Card className={styles.sectionCard} styles={{ body: { height: '100%', padding: 12 } }}>
<div className={styles.sectionInner}>上半区右侧</div>
</Card>
</div>
<div className={styles.gridItem} style={{ gridColumn: 'span 12' }}>
<Card className={styles.sectionCard} styles={{ body: { height: '100%', padding: 12 } }}>
<div className={styles.tabsWrap}>
<Tabs items={tabsItems} />
</div>
</Card>
</div>
<div className={styles.gridItem} style={{ gridColumn: 'span 6' }}>
<Card className={styles.sectionCard} styles={{ body: { height: '100%', padding: 12 } }}>
<div className={styles.sectionInner}>左侧模块</div>
</Card>
</div>
<div className={styles.gridItem} style={{ gridColumn: 'span 6' }}>
<Card className={styles.sectionCard} styles={{ body: { height: '100%', padding: 12 } }}>
<div className={styles.sectionInner}>右侧模块</div>
</Card>
</div>
</div>
</div>
);
};
export default FullHeightLayoutDemo;
3. 日常修复检查清单(复制保存)
- Grid 第二行若要内部滚动,优先
minmax(0, 1fr)。
flex/grid 子项中,承担滚动链路的层补 min-height: 0。
- 横向溢出时对应补
min-width: 0。
overflow: auto 放在真实内容层,不要只放最外层壳子。
- Antd 组件要检查中间层:
ant-tabs-content-holder、ant-spin-container、ant-table-body。
- 避免
margin-top: -xx 这类负 margin 干扰滚动与可视区。