tableScrollSync.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // 工具函数:获取 .el-table__body-wrapper 下的滚动容器
  2. function getBodyWrapper(el) {
  3. return el.querySelector('.el-table__body-wrapper');
  4. }
  5. // 创建滚动条并绑定事件,返回清理函数
  6. function setupScrollSync(el) {
  7. const table = el; // el 就是容器元素
  8. if (!table) return null;
  9. const outerWidth = table.offsetWidth;
  10. // 取第一个内部 table 的宽度作为内容宽度
  11. const innerTable = table.querySelector('table');
  12. const contentWidth = innerTable ? innerTable.offsetWidth : outerWidth;
  13. const bodyWrapper = getBodyWrapper(table);
  14. if (!bodyWrapper) {
  15. console.warn('未找到 .el-table__body-wrapper,无法创建滚动同步');
  16. return null;
  17. }
  18. // 隐藏原生横向滚动条,但保留纵向滚动条
  19. bodyWrapper.style.overflowX = 'hidden';
  20. bodyWrapper.style.overflowY = 'auto';
  21. // 创建自定义样式标签来隐藏横向滚动条
  22. const styleId = 'v-scroll-sync-hide-x-scrollbar';
  23. let styleEl = document.getElementById(styleId);
  24. if (!styleEl) {
  25. styleEl = document.createElement('style');
  26. styleEl.id = styleId;
  27. styleEl.textContent = `
  28. .el-table__body-wrapper.v-scroll-sync-hide-x::-webkit-scrollbar {
  29. width: 6px;
  30. height: 0;
  31. }
  32. .el-table__body-wrapper.v-scroll-sync-hide-x::-webkit-scrollbar-track {
  33. background: transparent;
  34. }
  35. .el-table__body-wrapper.v-scroll-sync-hide-x::-webkit-scrollbar-thumb {
  36. background: #d9d9d9;
  37. border-radius: 3px;
  38. }
  39. `;
  40. document.head.appendChild(styleEl);
  41. }
  42. bodyWrapper.classList.add('v-scroll-sync-hide-x');
  43. // 避免重复创建
  44. const existingDiv = table.querySelector('.v-scroll-sync-bar');
  45. if (existingDiv) existingDiv.remove();
  46. const div = document.createElement('div');
  47. div.className = 'v-scroll-sync-bar';
  48. div.style.width = outerWidth + 'px';
  49. div.style.height = '12px';
  50. div.style.overflowX = 'auto';
  51. div.innerHTML = `<div style="width:${contentWidth}px; height:1px;"></div>`;
  52. const onScroll = () => {
  53. bodyWrapper.scrollLeft = div.scrollLeft;
  54. };
  55. div.addEventListener('scroll', onScroll);
  56. const tableContainer = table.querySelector('.el-table');
  57. // 在tableContainer后面插入div(作为兄弟元素)
  58. tableContainer.insertAdjacentElement('afterend', div);
  59. // table.appendChild(div);
  60. // 返回清理函数和事件引用
  61. return {
  62. div,
  63. bodyWrapper,
  64. handler: onScroll,
  65. destroy() {
  66. div.removeEventListener('scroll', onScroll);
  67. if (div.parentNode) div.parentNode.removeChild(div);
  68. // 移除添加的class
  69. bodyWrapper.classList.remove('v-scroll-sync-hide-x');
  70. }
  71. };
  72. }
  73. // Vue 2 自定义指令
  74. const tableScrollSync = {
  75. bind(el) {
  76. // bind 时可能未插入文档,先不做操作,统一在 inserted 中处理
  77. el._scrollSyncData = null;
  78. },
  79. inserted(el) {
  80. // 元素插入 DOM 后初始化
  81. el._scrollSyncData = setupScrollSync(el);
  82. },
  83. update(el) {
  84. // 当表格数据变化导致内容宽度改变时(如切换分页、动态列),重新初始化
  85. if (el._scrollSyncData) {
  86. el._scrollSyncData.destroy();
  87. }
  88. el._scrollSyncData = setupScrollSync(el);
  89. },
  90. unbind(el) {
  91. // 指令解绑时销毁
  92. if (el._scrollSyncData) {
  93. el._scrollSyncData.destroy();
  94. el._scrollSyncData = null;
  95. }
  96. }
  97. };
  98. export default tableScrollSync;