因为之前的头部置顶 1.0 存在一些问题。当头部上方还须要置顶一些其余货色时,比方导出案件、筛选框、搜寻框、题目等等时,之前的代码不够灵便,没有设置置顶的偏移量。当页面内关上抽屉反复利用 table 时,也会存在问题,因为里面的滚动曾经让头部置顶了,关上抽屉时,table 的 head 仍然在,并且会遮挡数据。
因而重写了置顶的 js 代码,通过监听对应 dom 的滚动,而不是监听 window。
// 判断是否呈现滚动条
function hasScrolled(el: HTMLElement, direction = 'vertical') {if (direction === 'vertical') {return el.scrollHeight > el.clientHeight;} else if (direction === 'horizontal') {return el.scrollWidth > el.clientWidth;}
}
// 找到有滚动条的 dom
function findScrollDom(el: HTMLElement) {if (!el) return null;
const isScrolled = hasScrolled(el);
if (isScrolled) {return el;}
const pEl = el.parentElement;
if (pEl) {return findScrollDom(pEl);
} else {return null;}
}
function useListenerScroll() {const containerRef = ref();
const isFixed = ref(false);
const width = ref('100%'); // 置顶容易的宽度
const top = ref('51px'); // 默认的定位高度,导航栏高度
const titleHeight = ref(0); // head 上放的 title 或其余操作的容器高度
const initEvent = async () => {await nextTick();
// 如果 table 组件为空,递归调用
if (!containerRef.value) {initEvent();
return;
}
// 找到滚动容器
const scrollDom = findScrollDom(containerRef.value);
if (scrollDom) {const titleDom = scrollDom.getElementsByClassName('table-title-content');
// 获取 title 内容高度
if (titleDom[0]) titleHeight.value = titleDom[0].clientHeight;
else titleHeight.value = 0;
scrollDom.addEventListener('scroll', listenerScroll);
top.value = scrollDom.offsetTop + 'px';
}
};
function listenerScroll(event: any) {
const $el = event.target;
if ($el) {
const scrollTop = $el.scrollTop;
const fixedDom = $el.getElementsByClassName('table-content');
if (fixedDom.length) {const dom = fixedDom[0];
const domTop = dom.offsetTop - titleHeight.value - 51;
// 滚动高度大于设定高度时,置顶头部
if (scrollTop > domTop) {isFixed.value = true;}
if (scrollTop < domTop) {
isFixed.value = false;
width.value = dom.clientWidth + 'px';
}
}
}
}
return {
top,
width,
isFixed,
containerRef,
initEvent,
};
}
<template>
<div ref="containerRef">
<div :style="fixedHeaderStyle">
<div class="table-title-content" v-if="$slots['w1-title'] || title">
<span v-if="title">{{title}}</span>
<slot name="title" />
</div>
<div>
<a-table
:pagination="false"
v-bind="$attrs"
:data-source="[]"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data" />
</template>
</a-table>
</div>
</div>
<a-table
class="table-content"
v-bind="$attrs"
:dataSource="dataSource"
v-loading="loading"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"> </slot>
</template>
</a-table>
</div>
</template>
<script lang="ts">
import {computed, defineComponent, onMounted} from 'vue';
import {isFunction} from '/@/utils/is';
import useListenerScroll from './hooks/useListenerScroll';
export default defineComponent({
props: {
dataSource: {type: Array,},
loading: {type: Boolean,},
title: {type: String,},
},
setup(props) {const { isFixed, containerRef, initEvent, width, top} = useListenerScroll();
const fixedHeaderStyle = computed(() => ({
position: isFixed.value ? 'fixed' : 'relative',
top: isFixed.value ? top.value : 0,
zIndex: isFixed.value ? 19 : 0,
width: width.value,
}));
onMounted(() => {initEvent();
});
return {
isFixed,
containerRef,
fixedHeaderStyle,
};
},
});
</script>