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>
相关推荐
拖孩13 分钟前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
用户3600555790014 分钟前
Kestrel_VLLM - 13 · 为什么排除 MoE FFN Tiling 与 Tensor Core
前端
流烟默18 分钟前
SSE 实战:在 SpringBoot + Vue 项目中零依赖实现轻量级实时消息推送
vue.js·spring boot·后端·sse
Hilaku23 分钟前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员
奇牙coding32 分钟前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
小羊没烦恼!38 分钟前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
程序员老赵40 分钟前
Docker 部署 Filestash:轻松搭建浏览器文件管理平台
前端·docker·开源
夏天要喝冰可乐42 分钟前
给 23 万字修仙小说造个阅读站:浏览器内打包 EPUB 分页阅读 + 三主题实战
前端·产品·全栈
茶底世界之下1 小时前
林间步道要不要加暗角?用 Awaking 把强度收在20%
前端·ios·图片资源
执子念的飞鱼1 小时前
纯前端预览 275 种文件格式是怎么做到的
前端·vue.js·react.js