条件限制没法类似vue2 mixins的写法
- vue3
- 因为ant design vue 4.1.2 版本 table自定义表头 cell传出来的参数中缺少列的基础属性,所以没法类似上述的写法
所以以下就手写一个表头拖拽以及行高改变的js方法
用法:
- 表格添加draggableTable类
- useDraggableTable放进使用的组件中执行,参数传递table的columns
- 尽可能配置每一列的宽度
待优化的点(行高调整测试过没什么毛病)
- 表头拖拽的鼠标与列宽的增长幅度不一致
- 打个比方 6列,每一列宽度100 但是表格长度1000 则剩下400会等比例分配,这个可能是造成的原因之一
- 尝试过改变dom的高度,而不用下面改变columns.width去改变宽度,失败,原因可能是因为cell的display为table-cell,宽度改不了,除非你改变了他display,但是会影响table的布局
- 尝试通过syncTableWidth函数去更新table,这个可以,但是你需要在下面方法中的onMounted执行他,他带来的问题是 你设置的宽度会因为他重新计算,会造成你设置的宽度不是你初始化的,所以看你需求来
目前这个实现了和官网表头拖拽一样的效果,就是鼠标和增幅不一致的问题而已
import { onMounted, onUnmounted, onUpdated } from 'vue';
export function useDraggableTable(columns) {
let resizingColumnIndex = null;
let resizingRowIndex = null;
let initialX = 0;
let initialY = 0;
let initialWidth = 0;
let initialHeight = 0;
let isResizing = false;
let tableWidth = 0;
// 是否是适配体验好一点的表头拖拽方式(鼠标与宽度的增减保持一定范围的一致),需要每个列宽都定义了
let isGoodAdapter = false;
// 重置所有状态参数
const resetParams = () => {
resizingColumnIndex = null;
resizingRowIndex = null;
initialX = 0;
initialY = 0;
initialWidth = 0;
initialHeight = 0;
isResizing = false;
};
// 添加调整大小的监听器
const addResizeListeners = () => {
const headers = document.querySelectorAll('.draggableTable .ant-table-thead th');
const rows = document.querySelectorAll('.draggableTable .ant-table-tbody tr');
// 为表头添加调整大小的手柄
headers.forEach((header, index) => {
addResizeHandle(header, index, startResizeColumn);
});
// 为表格行添加鼠标事件监听器
rows.forEach((row) => {
row.addEventListener('mouseenter', (e) => handleRowMouseEnter(e, row));
row.addEventListener('mouseleave', (e) => handleRowMouseLeave(e, row));
row.addEventListener('mousedown', (e) => handleRowMouseDown(e, row));
row.addEventListener('mousemove', (e) => handleRowMouseMove(e, row));
});
// 添加鼠标抬起事件监听器
document.addEventListener('mouseup', stopResize);
};
// 创建调整大小的手柄
const createResizeHandle = () => {
const handle = document.createElement('div');
handle.className = 'resize-handle';
handle.style.position = 'absolute';
handle.style.right = '0';
handle.style.top = '50%';
handle.style.transform = 'translateY(-50%)';
handle.style.width = '2px';
handle.style.height = '60%';
handle.style.cursor = 'col-resize';
handle.style.backgroundColor = '#e5e7eb';
handle.style.opacity = '0';
return handle;
};
// 添加调整大小的手柄到表头
const addResizeHandle = (header, index, callback) => {
const handle = createResizeHandle();
header.appendChild(handle);
handle.addEventListener('mousedown', (e) => callback(e, index));
header.addEventListener('mouseenter', showResizeHandle);
header.addEventListener('mouseleave', hideResizeHandle);
};
// 显示调整大小的手柄
const showResizeHandle = (e) => {
const handle = e.target.querySelector('.resize-handle');
handle.style.opacity = '1'; // 显示手柄
};
// 隐藏调整大小的手柄
const hideResizeHandle = (e) => {
const handle = e.target.querySelector('.resize-handle');
handle.style.opacity = '0'; // 隐藏手柄
};
// 开始调整列宽
const startResizeColumn = (e, columnIndex) => {
e.preventDefault(); // 阻止默认行为
resizingColumnIndex = columnIndex;
initialX = e.pageX;
// 获取初始宽度
const columnConfig = columns[columnIndex];
const headerElement = document.querySelector(`.draggableTable .ant-table-thead th:nth-child(${columnIndex + 1})`);
initialWidth = columnConfig.width ?? headerElement?.offsetWidth; // 默认50
// if (isGoodAdapter) {
// const headerElement = document.querySelector(`.draggableTable .ant-table-thead th:nth-child(${columnIndex + 1})`);
// if (headerElement) {
// initialWidth = headerElement.offsetWidth;
// }
// }
document.addEventListener('mousemove', doResizeColumn);
document.addEventListener('mouseup', stopResize);
isResizing = true; // 标记正在拖拽
};
// 调整列宽
const doResizeColumn = (e) => {
if (isResizing && resizingColumnIndex !== null) {
let newWidth = initialWidth + e.pageX - initialX;
// 确保新宽度不小于默认最小宽度
if (newWidth < 50) {
newWidth = 50;
}
// 同步更新表格宽度
// isGoodAdapter && syncTableWidth();
// 更新配置中的宽度
columns[resizingColumnIndex].width = newWidth;
}
};
// 同步表格宽度
const syncTableWidth = () => {
const totalWidth = columns.reduce((acc, col) => acc + col.width, 0);
const diff = tableWidth - totalWidth;
console.log('diff', diff, totalWidth, tableWidth);
// 如果总宽度大于表格宽度,平均分配剩余宽度
if (diff > 0) {
columns.forEach((col) => {
col.width += Math.round(diff / columns.length);
});
}
};
// 处理鼠标进入行
const handleRowMouseEnter = (e, row) => {
if (shouldShowRowHandle(e, row)) {
row.style.cursor = 'row-resize';
} else {
row.style.cursor = 'default';
}
};
// 处理鼠标离开行
const handleRowMouseLeave = (e, row) => {
row.style.cursor = 'default';
// 如果鼠标离开且没有处于调整状态,则隐藏手柄
if (!isResizing) {
row.style.cursor = 'default';
}
};
// 处理鼠标按下行
const handleRowMouseDown = (e, row) => {
e.preventDefault(); // 阻止默认行为
if (shouldShowRowHandle(e, row)) {
resizingRowIndex = Array.from(row.parentNode.children).indexOf(row);
const rowRect = row.getBoundingClientRect();
initialY = e.clientY - rowRect.top; // 使用 clientY 相对于行的偏移量
initialHeight = row.offsetHeight;
document.addEventListener('mousemove', doResizeRow);
document.addEventListener('mouseup', stopResize);
isResizing = true; // 标记正在拖拽
}
};
// 判断是否应该显示行调整手柄
const shouldShowRowHandle = (e, row) => {
const mouseY = e.clientY - row.getBoundingClientRect().top;
const curOffsetHeight = row.offsetHeight;
const threshold = 10;
return curOffsetHeight < 41 ? 41 - mouseY <= threshold : curOffsetHeight - mouseY <= threshold;
};
// 处理鼠标移动行
const handleRowMouseMove = (e, row) => {
if (shouldShowRowHandle(e, row)) {
row.style.cursor = 'row-resize';
} else {
row.style.cursor = 'default';
}
};
// 调整行高
const doResizeRow = (e) => {
if (isResizing && resizingRowIndex !== null) {
const resizingRow = document.querySelectorAll('.draggableTable .ant-table-tbody tr')[resizingRowIndex];
const rowRect = resizingRow.getBoundingClientRect();
const newHeight = initialHeight + (e.clientY - rowRect.top) - initialY;
// 更新 DOM 中的高度
resizingRow.style.height = `${newHeight}px`;
// 检查是否接近顶部或底部并显示手柄
if (shouldShowRowHandle(e, resizingRow)) {
resizingRow.style.cursor = 'row-resize';
} else {
resizingRow.style.cursor = 'default';
}
}
};
// 停止调整大小
const stopResize = () => {
document.removeEventListener('mousemove', doResizeColumn);
document.removeEventListener('mousemove', doResizeRow);
document.removeEventListener('mouseup', stopResize);
resetParams();
};
// 组件挂载时初始化
onMounted(() => {
setTimeout(() => {
tableWidth = document.querySelector('.draggableTable .ant-table').clientWidth;
isGoodAdapter = columns.every((col) => ![null, undefined, ''].includes(col.width));
console.log('列宽是否都已配置', isGoodAdapter);
addResizeListeners();
// syncTableWidth();
}, 1000);
});
// 组件更新时重置并重新添加监听器
onUpdated(() => {
resetParams();
stopResize();
addResizeListeners();
});
// 组件卸载时清除监听器
onUnmounted(() => {
stopResize();
});
}