vue3+elementuiplus的table表格动态高度

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>
相关推荐
打野赵怀真19 分钟前
前端资源发布路径怎么实现非覆盖式发布(平滑升级)?
前端·javascript
顾林海29 分钟前
Flutter Dart 流程控制语句详解
android·前端·flutter
tech_zjf30 分钟前
装饰器:给你的代码穿上品如的衣服
前端·typescript·代码规范
xiejianxin52031 分钟前
如何封装axios和取消重复请求
前端·javascript
parade岁月31 分钟前
从学习ts的三斜线指令到项目中声明类型的最佳实践
前端·javascript
狼性书生34 分钟前
electron + vue3 + vite 渲染进程与渲染进程之间的消息端口通信
前端·javascript·electron
阿里巴巴P8资深技术专家34 分钟前
使用vue3.0+electron搭建桌面应用并打包exe
前端·javascript·vue.js
coder_leon38 分钟前
Vite打包优化实践:从分包到性能提升
前端
shmily_yyA38 分钟前
【2025】Electron 基础一 (目录及主进程解析)
前端·javascript·electron
吞吞071140 分钟前
浅谈前端性能指标、性能监控、以及搭建性能优化体系
前端