import type { Ref } from 'vue' export function useTableScroll( elScrollBarH: Ref, tableContent: Ref, tableContainer: Ref, tableHeaderRef: Ref, isFixed: Ref, ) { /** * @description: 设置滚动条的位置 * 这里调整的是滚动条的包裹容器,他相对于表格容器来定位, * 而内部的实际的滚动条相对于这个容器使用tranlate来调整位置 * 所以在调回的时候,直接把left设置为0,仍然能保持滚动条的相对位置 * @param {*} state true:固定在可视区域,false:固定在表格容器内 * @return {*} */ const setScrollPos = (state: boolean) => { if (state) { if (!elScrollBarH.value || !tableContent.value) return elScrollBarH.value.style.position = 'fixed' // 这里去找表格的content区域,把他相对于视口的left值设置给滚动条的容器 elScrollBarH.value.style.left = tableContent.value?.getBoundingClientRect().left + 'px' } else { elScrollBarH.value!.style.left = 0 + 'px' elScrollBarH.value!.style.position = 'absolute' } } /** * @description: 观察表格是否在可视区域,设置滚动条的对应位置 * @param {*} entries 观察实例,包含当前观察的元素的状态 * @return {*} */ const obScroll = (entries: IntersectionObserverEntry[]) => { setScrollPos(entries[0].intersectionRatio > 0) } /** * @description: 判定当前横向滑动条是否在表格内 * 滑动距离加可见区域高度不大于表格总高度,则说明在表格内 * @return 是否在表格内 */ const isInTable = () => { if (!tableContent.value || !tableContainer.value) return false const { scrollTop, offsetHeight, scrollHeight } = tableContainer.value as HTMLElement let result = scrollTop + offsetHeight < scrollHeight isFixed.value = result return result } /** * @description: 初始化滑动条 */ const initScroll = () => { let sc = document.querySelector('.el-scrollbar__bar') as HTMLElement let header = document.querySelector('.el-table__header-wrapper') if (sc) { elScrollBarH.value = sc } if (header) { tableHeaderRef.value = header as HTMLElement } } /** * @description: 设置横向滑动条的位置和表头的位置 * 只有滑动位置到达表格内部时,才需要固定横向滑动条,否则让滚动条回到原来的初始位置,即表格的底部 * 只有滚动条超出表头的原本位置时,才固定表头 */ const setScrollAndHeader = () => { setScrollPos(isInTable()) if (tableHeaderRef.value) { const { scrollTop } = tableContainer.value as HTMLElement let contentOffsetTop = tableContent.value!.offsetTop // 父容器距离顶部的高度 let contentScrollHeight = tableContent.value!.scrollHeight // 整个父容器的高度 // 超出表头原本位置时且小于整个表的高度,则固定表头 if ( scrollTop >= contentOffsetTop && scrollTop <= contentOffsetTop + contentScrollHeight ) { tableHeaderRef.value.style.position = 'fixed' tableHeaderRef.value.style.top = `${tableContainer.value?.getBoundingClientRect().top}px` tableHeaderRef.value.style.zIndex = '666' } else { // 还原 // 不设置top是因为在static下,top无效 tableHeaderRef.value.style.position = 'static' tableHeaderRef.value.style.zIndex = '1' } } } const findScrollParent = (el: HTMLElement): HTMLElement | Window => { let parent = el.parentElement while (parent) { const style = window.getComputedStyle(parent) const overflowY = style.overflowY // 判断标准: // 1. 是可见容器(高度不为0) // 2. 包含滚动条(内容溢出或强制显示) if ( parent.clientHeight > 0 && (overflowY === 'scroll' || overflowY === 'auto' || (overflowY === 'visible' && parent.scrollHeight > parent.clientHeight)) ) { return parent } parent = parent.parentElement } // 未找到则返回window return window } return { initScroll, setScrollAndHeader, obScroll, setScrollPos, findScrollParent, } }