核心功能:
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;