table表格流体高度
1、前提
了解自定义指令、hooks
2、核心思路
通过自定义指令(new ResizeObserver)监听表格变化,然后通过hooks去更新表格高度。
3、核心代码
src/directives/resize.ts
TypeScript
// import { debounce } from '@/utils';
import { type Directive } from "vue";
const resizeDirective: Directive = {
mounted(el, binding) {
// update 要高效,否则会导致 ResizeObserver loop completed with undelivered notifications.
// 这里通过 setTimeout 延迟实际更新到下个 tick
let update = (entry: ResizeObserverEntry) => {
setTimeout(() => {
binding.value(entry);
}, 0);
};
if (binding.arg) {
try {
let delay = Number.parseInt(binding.arg);
console.log(delay);
// update = debounce(binding.value, delay);
} catch (error) {
console.log(error);
}
}
// 创建 ResizeObserver 实例
const resizeObserver = (el.__resizeObserver__ = new ResizeObserver((entries) => {
// 当目标元素的大小发生变化时,执行回调函数
update(entries[0]);
}));
// 开始监听目标元素的大小变化
resizeObserver.observe(el);
},
unmounted(el) {
el.__resizeObserver__.disconnect();
},
};
export default resizeDirective;
src/hooks/useTableConfig.ts
TypeScript
import { type TableInstance } from "element-plus";
/** 获取表格的基本配置 */
export const useTableConfig = <T>(padding: number = 20) => {
const tableLoading = ref<boolean>(false);
const pageData = ref<T[]>([]);
const total = ref<number>(0);
const tableRef = ref<TableInstance | null>(null);
const selectedTableIds = ref<string[]>([]);
/**表格高度 */
const tableFluidHeight = ref<number>(0);
const tableResize = (rect: DOMRectReadOnly) => {
tableFluidHeight.value = rect.height - padding * 2;
};
return {
tableLoading,
pageData,
total,
tableRef,
selectedTableIds,
tableFluidHeight,
tableResize,
};
};
这里padding:20的原因是

src/components/BaseTableSearchContainer/index.vue
html
<template>
<div class="flex flex-col h-full">
<slot name="search" />
<div ref="centerRef" v-resize="onResize" class="flex-1 overflow-auto">
<div :class="centerClass" class="p-20px bg-light-50">
<slot name="table" />
</div>
</div>
<div style="background: white">
<slot name="pagination" />
</div>
</div>
</template>
<script setup lang="ts">
import { useTemplateRef } from "vue";
defineProps({ centerClass: { type: String, default: "" } });
const emit = defineEmits(["sizeChanged"]);
const centerRef = useTemplateRef<HTMLElement>("centerRef");
const onResize = (e: ResizeObserverEntry) => {
// console.log("resize", e, centerRef.value, centerRef.value?.getBoundingClientRect().height);
emit("sizeChanged", e.contentRect);
};
</script>
<style lang="scss" scoped></style>
使用:
html
<template>
<div class="app-container">
<BaseTableSearchContainer @size-changed="tableResize">
<template #search>
<TBSearchContainer :is-show-toggle="true">
<template #left> </template>
<template #right> </template>
</TBSearchContainer>
</template>
<template #table>
<el-table
ref="tableRef"
v-loading="tableLoading"
:data="pageData"
highlight-current-row
row-key="Id"
border
:height="tableFluidHeight"
style="text-align: center; flex: 1"
@select="handleTableSelect"
@select-all="handleTableSelect"
>
</el-table>
</template>
<template #pagination>
<Pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageIndex"
v-model:limit="queryParams.pageSize"
@pagination="handleGetTableList"
/>
</template>
</BaseTableSearchContainer>
</div>
</template>
<script setup lang="ts">
const {
tableLoading,
pageData,
total,
tableRef,
tableFluidHeight,
tableResize,
} = useTableConfig<MoItemPackItem>();
</script>
<style scoped lang="scss"></style>