功能展示
- 正常情况下

- 展开查询条件也能够动态改变高度

- 数据过多或过少的情况下也能够满足自动撑满屏幕

代码实现
样式
- 首先要先自定义table样式, 用于覆盖antd 的默认样式
.ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
display: flex;
flex-direction: column;
}
.ant-table-container {
height: 100%;
display: flex;
flex-direction: column;
.ant-table-content {
height: 100%;
}
.ant-table-body {
position: relative;
flex: 1;
table {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
}
}
.ant-table {
flex: 1;
}
}
计算高度
import debounce from 'lodash/debounce';
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
interface ResizeObserverEntry {
contentRect: {
left: number;
top: number;
width: number;
height: number;
};
}
type ResizeObserverCallback = (
entries: ResizeObserverEntry[],
observer: ResizeObserver
) => void;
export declare class ResizeObserver {
constructor(callback: ResizeObserverCallback);
observe(target: Element, options?: any): void;
unobserve(target: Element): void;
disconnect(): void;
static toString(): string;
}
export interface ResizeObserverPolyfill {
new (callback: ResizeObserverCallback): ResizeObserver;
}
export interface PrivateWindow {
ResizeObserver: ResizeObserverPolyfill;
}
export type Simplify<T> = { [Key in keyof T]: T[Key] } & {};
export interface DebounceSettings {
debounceTime?: number;
enableDebounceLeadingCall?: boolean;
}
export type ParentSizeState = {
width: number;
height: number;
top: number;
left: number;
};
export type UseParentSizeConfig = {
initialSize?: Partial<ParentSizeState>;
resizeObserverPolyfill?: ResizeObserverPolyfill;
ignoreDimensions?: keyof ParentSizeState | (keyof ParentSizeState)[];
} & DebounceSettings;
type UseParentSizeResult<T extends HTMLElement = HTMLDivElement> =
ParentSizeState & {
parentRef: RefObject<T>;
resize: (state: ParentSizeState) => void;
};
const defaultIgnoreDimensions: UseParentSizeConfig['ignoreDimensions'] = [];
const defaultInitialSize: ParentSizeState = {
width: 0,
height: 0,
top: 0,
left: 0,
};
export default function useParentSize<T extends HTMLElement = HTMLDivElement>({
initialSize = defaultInitialSize,
debounceTime = 300,
ignoreDimensions = defaultIgnoreDimensions,
enableDebounceLeadingCall = true,
resizeObserverPolyfill,
}: UseParentSizeConfig = {}): UseParentSizeResult<T> {
const parentRef = useRef<T>(null);
const animationFrameID = useRef(0);
const [state, setState] = useState<ParentSizeState>({
...defaultInitialSize,
...initialSize,
});
const resize = useMemo(() => {
const normalized = Array.isArray(ignoreDimensions)
? ignoreDimensions
: [ignoreDimensions];
return debounce(
(incoming: ParentSizeState) => {
setState((existing) => {
const stateKeys = Object.keys(existing) as (keyof ParentSizeState)[];
const keysWithChanges = stateKeys.filter(
(key) => existing[key] !== incoming[key]
);
const shouldBail = keysWithChanges.every((key) =>
normalized.includes(key)
);
return shouldBail ? existing : incoming;
});
},
debounceTime,
{ leading: enableDebounceLeadingCall }
);
}, [debounceTime, enableDebounceLeadingCall, ignoreDimensions]);
useEffect(() => {
const LocalResizeObserver =
resizeObserverPolyfill ||
(window as unknown as PrivateWindow).ResizeObserver;
const observer = new LocalResizeObserver((entries) => {
entries.forEach((entry) => {
const { left, top, width, height } = entry?.contentRect ?? {};
animationFrameID.current = window.requestAnimationFrame(() => {
resize({ width, height, top, left });
});
});
});
if (parentRef.current) observer.observe(parentRef.current);
return () => {
window.cancelAnimationFrame(animationFrameID.current);
observer.disconnect();
resize.cancel();
};
}, [resize, resizeObserverPolyfill]);
return { parentRef, resize, ...state };
}
设置包裹容器ParentSize
import useParentSize, {
ParentSizeState,
UseParentSizeConfig,
} from '@/hooks/useParentSize';
import React from 'react';
export type ParentSizeProvidedProps = ParentSizeState & {
ref: HTMLDivElement | null;
resize: (state: ParentSizeState) => void;
};
export type ParentSizeProps = {
className?: string;
parentSizeStyles?: React.CSSProperties;
children: (args: ParentSizeProvidedProps) => React.ReactNode;
} & UseParentSizeConfig;
const defaultParentSizeStyles = { width: '100%', height: '100%' };
export default function ParentSize({
className,
children,
debounceTime,
ignoreDimensions,
initialSize,
parentSizeStyles = defaultParentSizeStyles,
enableDebounceLeadingCall = true,
resizeObserverPolyfill,
...restProps
}: ParentSizeProps &
Omit<React.HTMLAttributes<HTMLDivElement>, keyof ParentSizeProps>) {
const { parentRef, resize, ...dimensions } = useParentSize({
initialSize,
debounceTime,
ignoreDimensions,
enableDebounceLeadingCall,
resizeObserverPolyfill,
});
return (
<div
style={parentSizeStyles}
ref={parentRef}
className={className}
{...restProps}>
{children({
...dimensions,
ref: parentRef.current,
resize,
})}
</div>
);
}
示例代码使用
import { Table, Form, Input } from 'antd';
import './custom-table.less';
const dataSource = Array.from({ length: 52 }).map((_, i) => {
return {
key: i,
name: '胡彦斌' + i,
age: 3 + i,
address: '西湖区湖底公园1号' + i,
};
});
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
];
function App() {
return (
<div style={{ height: 'calc(100vh - 86px)' }}>
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
{/* 模拟查询 */}
<div className="search">
<Form name="basic" layout="inline">
<Form.Item label="Username" name="username">
<Input />
</Form.Item>
<Form.Item label="UserAge" name="userage">
<Input />
</Form.Item>
</Form>
</div>
<ParentSize>
{({ height }) => {
return (
<div style={{height: '100%'}}>
<Table
size="small"
rowKey="name"
bordered
dataSource={dataSource}
columns={columns}
// y 也可以通过dataSource的length来控制是否展示滚动条, 如数据量很小的情况,就无需显示滚动条
scroll={{ y: height - 32 }}
/>
</div>
)
}}
</ParentSize>
</div>
</div>
);
}
export default App;