Skip to content

核心功能:

javascript
export function getElementTopPositionInParent(element) {
  if (!element || !element.parentElement) {
    throw new Error("Element or parent element is not defined.");
  }

  const rect = element.getBoundingClientRect();
  const parentRect = element.parentElement.getBoundingClientRect();

  const yPositionInParent = rect.top - parentRect.top;

  return yPositionInParent;
}

export function getComputedCssVal(el, property, reNumber = true) {
  const originVal = getComputedStyle(el).getPropertyValue(property) ?? "";
  return reNumber
    ? Number(originVal.replace(/([0-9]*)(.*)/g, "$1"))
    : originVal;
}

export function useTableHeight(tableEle, offsetHeight = 0) {
  const parentEl = tableEle?.parentNode;

  const height = parentEl?.offsetHeight || 0; // 屏幕可见区域高度
  const borderTopH = getComputedCssVal(parentEl, "border-top-width");
  const borderBottomH = getComputedCssVal(parentEl, "border-bottom-width");
  const paddingTopH = getComputedCssVal(parentEl, "padding-top");
  const paddingBottomH = getComputedCssVal(parentEl, "padding-bottom");
  return (
    height -
    offsetHeight -
    borderTopH -
    borderBottomH -
    paddingTopH -
    paddingBottomH
  );
}

// methods中
methods: {
calculateTableHeight() {
			// 分页组件高度
      const PAGINATION_HEIGHT = 52;
      const { showPagination, total } = this;
      const tableContainer = this.$refs.tableContainer;

      if (!tableContainer) return;

      const paginationOffset =
        showPagination && total > 0 ? PAGINATION_HEIGHT : 0;
      const containerTopOffset = getElementTopPositionInParent(tableContainer);
      const parentPadding = getComputedCssVal(
        tableContainer.parentNode,
        "padding-top"
      );

      this.tableHeight = useTableHeight(
        tableContainer,
        containerTopOffset + parentPadding + paginationOffset
      );
    },
    
    initObserver() {
      try {
        this.observer = new ResizeObserver((entries) => {
          this.calculateTableHeight();
        });

        const tableElement = this.$refs.tableContainer;
        if (tableElement) {
          this.observer.observe(tableElement);
        }
      } catch (error) {
        console.error("Failed to initialize ResizeObserver:", error);
      }
    },
  }  
    mounted() {
    this.calculateTableHeight();
    this.$nextTick(() => {
      this.initObserver();
    });
  },
  beforeDestroy() {
    this.observer?.disconnect();
    this.observer = null;
  },

整体表格组件封装:

javascript
<template>
  <div class="table-container" ref="tableContainer">
    <el-table
      v-loading="loading"
      v-bind="$attrs"
      v-on="$listeners"
      size="mini"
      :height="tableHeight"
    >
      <el-table-column
        v-for="(column, index) in normalizedColumns"
        :key="column.prop || index"
        v-bind="column"
      >
        <template #default="scope" v-if="column.render">
          <render-cell
            :render="column.render"
            :row="scope.row"
            :index="scope.$index"
            v-bind="scope"
          />
        </template>
      </el-table-column>
    </el-table>
    <pagination
      v-if="showPagination"
      v-show="total > 0"
      :total="total"
      :page.sync="currentPage"
      :pager-count="5"
      :limit.sync="pageSize"
      @pagination="handlePagination"
    />
  </div>
</template>

<script>
import Pagination from "@/components/Pagination";
import {
  useTableHeight,
  getElementTopPositionInParent,
  getComputedCssVal,
} from "./utils";

const RenderCell = {
  functional: true,
  props: {
    render: Function,
    row: Object,
    index: Number,
  },
  render: (h, ctx) => {
    const params = {
      row: ctx.props.row,
      index: ctx.props.index,
      ...ctx.data,
    };
    return ctx.props.render(h, params);
  },
};

export default {
  name: "Table",
  components: {
    Pagination,
    RenderCell,
  },
  props: {
    columns: {
      type: Array,
      required: true,
      default: () => [],
    },
    loading: {
      type: Boolean,
      default: false,
    },
    // 分页相关属性
    showPagination: {
      type: Boolean,
      default: true,
    },
    total: {
      type: Number,
      default: 0,
    },
    page: {
      type: Number,
      default: 1,
    },
    limit: {
      type: Number,
      default: 10,
    },
  },
  data() {
    return {
      tableHeight: null,
      observer: null,
    };
  },
  computed: {
    currentPage: {
      get: function () {
        return this.page;
      },
      set(v) {
        this.$emit("update:page", v);
      },
    },
    pageSize: {
      get: function () {
        return this.limit;
      },
      set(v) {
        this.$emit("update:limit", v);
      },
    },
    normalizedColumns() {
      return this.columns
        .filter((col) => !('show' in col) || col?.show())
        .map((column) => {
          // 处理序号列的特殊逻辑
          if (column.type === "index" && !column.index) {
            return {
              ...column,
              index: (index) =>
                index + 1 + (this.currentPage - 1) * this.pageSize,
            };
          }
          return column;
        });
    },
  },
  mounted() {
    this.calculateTableHeight();
    this.$nextTick(() => {
      this.initObserver();
    });
  },
  beforeDestroy() {
    this.observer?.disconnect();
    this.observer = null;
  },
  methods: {
    initObserver() {
      try {
        this.observer = new ResizeObserver((entries) => {
          this.calculateTableHeight();
        });

        const tableElement = this.$refs.tableContainer;
        if (tableElement) {
          this.observer.observe(tableElement);
        }
      } catch (error) {
        console.error("Failed to initialize ResizeObserver:", error);
      }
    },
    handlePagination(pagination) {
      this.$emit("pagination", pagination);
    },
    calculateTableHeight() {
      const PAGINATION_HEIGHT = 52;
      const { showPagination, total } = this;
      const tableContainer = this.$refs.tableContainer;

      if (!tableContainer) return;

      const paginationOffset =
        showPagination && total > 0 ? PAGINATION_HEIGHT : 0;
      const containerTopOffset = getElementTopPositionInParent(tableContainer);
      const parentPadding = getComputedCssVal(
        tableContainer.parentNode,
        "padding-top"
      );

      this.tableHeight = useTableHeight(
        tableContainer,
        containerTopOffset + parentPadding + paginationOffset
      );
    },
  },
};
</script>

<style lang="scss" scoped>
.table-container {
  width: 100%;
  overflow-y: hidden;
}
</style>

npm待发布:自适应高度functional;

Released under the MIT License.