我们一个常见的效果是表头固定,随着滚动条在顶部固定,而自带的fixed只是给table设置一个高度,一点也不是我们想要的效果
查看效果
准备
增加定义
export interface Fixed {
header?: boolean; // 头部固定(默认表头)
onHeaderFixed?: () => void; // 头部固定触发事件
onHeaderUnfixed?: () => void; // 头部不固定触发事件
footer?: boolean; // 底部固定(默认取分页)
onFooterFixed?: () => void; // 底部固定触发事件
onFooterUnfixed?: () => void; // 底部不固定触发事件
}
props
fixed: {
type: Object as PropType<Fixed>,
default: () => ({}),
},
useFixed实现
function useFixed(fixed: Fixed = {}, elProTable: Ref) {
const getScrollElement = (el: any, root = window) => {
let node = el;
const overflowScrollReg = /scroll|auto/i;
const rtv = [];
while (
node &&
node.tagName !== "HTML" &&
node.nodeType === 1 &&
node !== root
) {
const { overflowY } = window.getComputedStyle(node);
if (overflowScrollReg.test(overflowY)) {
if (node.tagName !== "BODY") {
rtv.push(node);
}
// see: https://github.com/youzan/vant/issues/3823
const { overflowY: htmlOverflowY } = window.getComputedStyle(
node.parentNode
);
if (overflowScrollReg.test(htmlOverflowY)) {
rtv.push(node);
}
}
node = node.parentNode;
}
// 需要返回最近的一个,el-main也是overflow:auto 但是没有滚动条,只有app-main才有滚动条
return rtv.length ? rtv[rtv.length - 1] : root;
};
const getElementStyle = (
element: HTMLElement,
styleKey: keyof CSSStyleDeclaration
) => window.getComputedStyle(element)[styleKey];
(fixed.header || fixed.footer) &&
onMounted(() => {
const el = elProTable.value;
const tableElement = el.querySelector(".el-table");
const paginationElement = el.querySelector(".el-pagination");
const tableHeaderElement = el.querySelector(".el-table__header-wrapper");
const tableBodyElement = el.querySelector(".el-table__body-wrapper");
const tableInnerWrapperElement = el.querySelector(
".el-table__inner-wrapper"
);
const scrollElement = getScrollElement(el);
let ticking = false;
const onScroll = () => {
if (!ticking) {
requestAnimationFrame(() => {
const headerHeight = tableHeaderElement.clientHeight;
const rect = el.querySelector(".el-table").getBoundingClientRect();
const maxZIndex = Array.from(
tableElement.querySelectorAll("*")
).reduce((maxZIndex, element) => {
const zIndex = getElementStyle(element as HTMLElement, "zIndex");
return Math.max(
maxZIndex as number,
parseInt(zIndex as string) || 0
);
}, 0);
const bodyWidth = getElementStyle(tableBodyElement, "width");
if (fixed.header && tableHeaderElement) {
if (rect.top <= 0 && rect.bottom >= headerHeight) {
fixed.onHeaderFixed && fixed.onHeaderFixed();
// console.log("fixed-header", bodyWidth);
tableHeaderElement.style.position = "fixed";
tableHeaderElement.style.zIndex = `${maxZIndex}`;
tableHeaderElement.style.top = 0 + "px";
tableHeaderElement.style.transition = "top .3s";
tableHeaderElement.style.width = bodyWidth;
tableInnerWrapperElement.style.marginTop =
tableHeaderElement.offsetHeight + "px";
} else {
fixed.onHeaderUnfixed && fixed.onHeaderUnfixed();
tableHeaderElement.style.width = "auto";
tableInnerWrapperElement.style.marginTop = 0;
tableHeaderElement.style.position = "static";
}
}
// console.log("rect.top", rect.top, rect.bottom);
if (fixed.footer && paginationElement) {
if (
rect.top <=
scrollElement.innerHeight -
tableHeaderElement.offsetHeight -
paginationElement.offsetHeight &&
rect.bottom >= scrollElement.innerHeight + 12
) {
fixed.onFooterFixed && fixed.onFooterFixed();
paginationElement.style.position = "fixed";
paginationElement.style.padding = "12px 0";
paginationElement.style.background = "#fff";
paginationElement.style.zIndex = `${maxZIndex}`;
paginationElement.style.bottom = 0;
paginationElement.style.transition = "bottom .3s";
paginationElement.style.width = bodyWidth;
tableInnerWrapperElement.style.marginBottom =
paginationElement.offsetHeight + "px";
} else {
fixed.onFooterUnfixed && fixed.onFooterUnfixed();
paginationElement.style.width = "auto";
tableInnerWrapperElement.style.marginBottom = 0;
paginationElement.style.position = "static";
}
}
ticking = false;
});
}
ticking = true;
};
// scrollElementOnScroll
scrollElement.addEventListener("scroll", onScroll, false);
onUnmounted(() => {
scrollElement.removeEventListener("scroll", onScroll, false);
});
// tableElementResizeObserver
const observer = new ResizeObserver(() => {
// console.log("resize");
onScroll();
});
observer.observe(tableElement);
onUnmounted(() => {
observer.disconnect();
});
});
}
调用
<h2>支持表头固定、尾固定</h2>
<el-pro-table
:data="tableData2"
:fixed="{ header: true, footer: true }"
:pagination="{ total: 100 }"
>
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
<el-table-column prop="name" label="Name" />
<el-table-column prop="state" label="State" />
<el-table-column prop="city" label="City" />
<el-table-column prop="address" label="Address" width="600" />
<el-table-column prop="zip" label="Zip" />
<el-table-column label="Operations">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="handleClick">
编辑
</el-button>
<el-button v-if="row.status === 2" link type="primary" size="small">
删除
</el-button>
</template>
</el-table-column>
</el-pro-table>