|
@@ -0,0 +1,110 @@
|
|
|
|
|
+// 工具函数:获取 .el-table__body-wrapper 下的滚动容器
|
|
|
|
|
+function getBodyWrapper(el) {
|
|
|
|
|
+ return el.querySelector('.el-table__body-wrapper');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 创建滚动条并绑定事件,返回清理函数
|
|
|
|
|
+function setupScrollSync(el) {
|
|
|
|
|
+ const table = el; // el 就是容器元素
|
|
|
|
|
+ if (!table) return null;
|
|
|
|
|
+
|
|
|
|
|
+ const outerWidth = table.offsetWidth;
|
|
|
|
|
+ // 取第一个内部 table 的宽度作为内容宽度
|
|
|
|
|
+ const innerTable = table.querySelector('table');
|
|
|
|
|
+ const contentWidth = innerTable ? innerTable.offsetWidth : outerWidth;
|
|
|
|
|
+
|
|
|
|
|
+ const bodyWrapper = getBodyWrapper(table);
|
|
|
|
|
+ if (!bodyWrapper) {
|
|
|
|
|
+ console.warn('未找到 .el-table__body-wrapper,无法创建滚动同步');
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 隐藏原生横向滚动条,但保留纵向滚动条
|
|
|
|
|
+ bodyWrapper.style.overflowX = 'hidden';
|
|
|
|
|
+ bodyWrapper.style.overflowY = 'auto';
|
|
|
|
|
+
|
|
|
|
|
+ // 创建自定义样式标签来隐藏横向滚动条
|
|
|
|
|
+ const styleId = 'v-scroll-sync-hide-x-scrollbar';
|
|
|
|
|
+ let styleEl = document.getElementById(styleId);
|
|
|
|
|
+ if (!styleEl) {
|
|
|
|
|
+ styleEl = document.createElement('style');
|
|
|
|
|
+ styleEl.id = styleId;
|
|
|
|
|
+ styleEl.textContent = `
|
|
|
|
|
+ .el-table__body-wrapper.v-scroll-sync-hide-x::-webkit-scrollbar {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ height: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ .el-table__body-wrapper.v-scroll-sync-hide-x::-webkit-scrollbar-track {
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ }
|
|
|
|
|
+ .el-table__body-wrapper.v-scroll-sync-hide-x::-webkit-scrollbar-thumb {
|
|
|
|
|
+ background: #d9d9d9;
|
|
|
|
|
+ border-radius: 3px;
|
|
|
|
|
+ }
|
|
|
|
|
+ `;
|
|
|
|
|
+ document.head.appendChild(styleEl);
|
|
|
|
|
+ }
|
|
|
|
|
+ bodyWrapper.classList.add('v-scroll-sync-hide-x');
|
|
|
|
|
+ // 避免重复创建
|
|
|
|
|
+ const existingDiv = table.querySelector('.v-scroll-sync-bar');
|
|
|
|
|
+ if (existingDiv) existingDiv.remove();
|
|
|
|
|
+
|
|
|
|
|
+ const div = document.createElement('div');
|
|
|
|
|
+ div.className = 'v-scroll-sync-bar';
|
|
|
|
|
+ div.style.width = outerWidth + 'px';
|
|
|
|
|
+ div.style.height = '12px';
|
|
|
|
|
+ div.style.overflowX = 'auto';
|
|
|
|
|
+ div.innerHTML = `<div style="width:${contentWidth}px; height:1px;"></div>`;
|
|
|
|
|
+
|
|
|
|
|
+ const onScroll = () => {
|
|
|
|
|
+ bodyWrapper.scrollLeft = div.scrollLeft;
|
|
|
|
|
+ };
|
|
|
|
|
+ div.addEventListener('scroll', onScroll);
|
|
|
|
|
+
|
|
|
|
|
+ const tableContainer = table.querySelector('.el-table');
|
|
|
|
|
+
|
|
|
|
|
+ // 在tableContainer后面插入div(作为兄弟元素)
|
|
|
|
|
+ tableContainer.insertAdjacentElement('afterend', div);
|
|
|
|
|
+
|
|
|
|
|
+ // table.appendChild(div);
|
|
|
|
|
+
|
|
|
|
|
+ // 返回清理函数和事件引用
|
|
|
|
|
+ return {
|
|
|
|
|
+ div,
|
|
|
|
|
+ bodyWrapper,
|
|
|
|
|
+ handler: onScroll,
|
|
|
|
|
+ destroy() {
|
|
|
|
|
+ div.removeEventListener('scroll', onScroll);
|
|
|
|
|
+ if (div.parentNode) div.parentNode.removeChild(div);
|
|
|
|
|
+ // 移除添加的class
|
|
|
|
|
+ bodyWrapper.classList.remove('v-scroll-sync-hide-x');
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Vue 2 自定义指令
|
|
|
|
|
+const tableScrollSync = {
|
|
|
|
|
+ bind(el) {
|
|
|
|
|
+ // bind 时可能未插入文档,先不做操作,统一在 inserted 中处理
|
|
|
|
|
+ el._scrollSyncData = null;
|
|
|
|
|
+ },
|
|
|
|
|
+ inserted(el) {
|
|
|
|
|
+ // 元素插入 DOM 后初始化
|
|
|
|
|
+ el._scrollSyncData = setupScrollSync(el);
|
|
|
|
|
+ },
|
|
|
|
|
+ update(el) {
|
|
|
|
|
+ // 当表格数据变化导致内容宽度改变时(如切换分页、动态列),重新初始化
|
|
|
|
|
+ if (el._scrollSyncData) {
|
|
|
|
|
+ el._scrollSyncData.destroy();
|
|
|
|
|
+ }
|
|
|
|
|
+ el._scrollSyncData = setupScrollSync(el);
|
|
|
|
|
+ },
|
|
|
|
|
+ unbind(el) {
|
|
|
|
|
+ // 指令解绑时销毁
|
|
|
|
|
+ if (el._scrollSyncData) {
|
|
|
|
|
+ el._scrollSyncData.destroy();
|
|
|
|
|
+ el._scrollSyncData = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+export default tableScrollSync;
|