useTableScroll.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import type { Ref } from 'vue'
  2. export function useTableScroll(
  3. elScrollBarH: Ref<HTMLElement | null>,
  4. tableContent: Ref<HTMLElement | null>,
  5. tableContainer: Ref<HTMLElement | null>,
  6. tableHeaderRef: Ref<HTMLElement | null>,
  7. isFixed: Ref<boolean>,
  8. ) {
  9. /**
  10. * @description: 设置滚动条的位置
  11. * 这里调整的是滚动条的包裹容器,他相对于表格容器来定位,
  12. * 而内部的实际的滚动条相对于这个容器使用tranlate来调整位置
  13. * 所以在调回的时候,直接把left设置为0,仍然能保持滚动条的相对位置
  14. * @param {*} state true:固定在可视区域,false:固定在表格容器内
  15. * @return {*}
  16. */
  17. const setScrollPos = (state: boolean) => {
  18. if (state) {
  19. if (!elScrollBarH.value || !tableContent.value) return
  20. elScrollBarH.value.style.position = 'fixed'
  21. // 这里去找表格的content区域,把他相对于视口的left值设置给滚动条的容器
  22. elScrollBarH.value.style.left =
  23. tableContent.value?.getBoundingClientRect().left + 'px'
  24. } else {
  25. elScrollBarH.value!.style.left = 0 + 'px'
  26. elScrollBarH.value!.style.position = 'absolute'
  27. }
  28. }
  29. /**
  30. * @description: 观察表格是否在可视区域,设置滚动条的对应位置
  31. * @param {*} entries 观察实例,包含当前观察的元素的状态
  32. * @return {*}
  33. */
  34. const obScroll = (entries: IntersectionObserverEntry[]) => {
  35. setScrollPos(entries[0].intersectionRatio > 0)
  36. }
  37. /**
  38. * @description: 判定当前横向滑动条是否在表格内
  39. * 滑动距离加可见区域高度不大于表格总高度,则说明在表格内
  40. * @return 是否在表格内
  41. */
  42. const isInTable = () => {
  43. if (!tableContent.value || !tableContainer.value) return false
  44. const { scrollTop, offsetHeight, scrollHeight } =
  45. tableContainer.value as HTMLElement
  46. let result = scrollTop + offsetHeight < scrollHeight
  47. isFixed.value = result
  48. return result
  49. }
  50. /**
  51. * @description: 初始化滑动条
  52. */
  53. const initScroll = () => {
  54. let sc = document.querySelector('.el-scrollbar__bar') as HTMLElement
  55. let header = document.querySelector('.el-table__header-wrapper')
  56. if (sc) {
  57. elScrollBarH.value = sc
  58. }
  59. if (header) {
  60. tableHeaderRef.value = header as HTMLElement
  61. }
  62. }
  63. /**
  64. * @description: 设置横向滑动条的位置和表头的位置
  65. * 只有滑动位置到达表格内部时,才需要固定横向滑动条,否则让滚动条回到原来的初始位置,即表格的底部
  66. * 只有滚动条超出表头的原本位置时,才固定表头
  67. */
  68. const setScrollAndHeader = () => {
  69. setScrollPos(isInTable())
  70. if (tableHeaderRef.value) {
  71. const { scrollTop } = tableContainer.value as HTMLElement
  72. let contentOffsetTop = tableContent.value!.offsetTop // 父容器距离顶部的高度
  73. let contentScrollHeight = tableContent.value!.scrollHeight // 整个父容器的高度
  74. // 超出表头原本位置时且小于整个表的高度,则固定表头
  75. if (
  76. scrollTop >= contentOffsetTop &&
  77. scrollTop <= contentOffsetTop + contentScrollHeight
  78. ) {
  79. tableHeaderRef.value.style.position = 'fixed'
  80. tableHeaderRef.value.style.top = `${tableContainer.value?.getBoundingClientRect().top}px`
  81. tableHeaderRef.value.style.zIndex = '666'
  82. } else {
  83. // 还原
  84. // 不设置top是因为在static下,top无效
  85. tableHeaderRef.value.style.position = 'static'
  86. tableHeaderRef.value.style.zIndex = '1'
  87. }
  88. }
  89. }
  90. const findScrollParent = (el: HTMLElement): HTMLElement | Window => {
  91. let parent = el.parentElement
  92. while (parent) {
  93. const style = window.getComputedStyle(parent)
  94. const overflowY = style.overflowY
  95. // 判断标准:
  96. // 1. 是可见容器(高度不为0)
  97. // 2. 包含滚动条(内容溢出或强制显示)
  98. if (
  99. parent.clientHeight > 0 &&
  100. (overflowY === 'scroll' ||
  101. overflowY === 'auto' ||
  102. (overflowY === 'visible' &&
  103. parent.scrollHeight > parent.clientHeight))
  104. ) {
  105. return parent
  106. }
  107. parent = parent.parentElement
  108. }
  109. // 未找到则返回window
  110. return window
  111. }
  112. return {
  113. initScroll,
  114. setScrollAndHeader,
  115. obScroll,
  116. setScrollPos,
  117. findScrollParent,
  118. }
  119. }