实现效果

请添加图片描述

实现思路

  1. 自定义表头,在标题后面加两个标签,分别用来显示拖拽图标(cursor: col-resize),和蓝色标记线(有的时候鼠标移动过程中不一定会在表内,这个时候不显示图标,只显示蓝色标记线)
  2. 给所有列增加一个初始宽度,表格 scroll 的 x 设置为100%
  3. 点击拖拽图标后:显示对应的蓝色标记线,监听鼠标的移动和释放
  4. 鼠标移动的时候同步更新列宽
  5. 鼠标释放的时候移除监听

实现代码

html

<a-table
    :scroll="{ x: '100%' }"
    :columns="columns"
    :data-source="dataSource"
>
    <template #headerCell="{ column }">
        <div class="resizable-header">
            <span style="overflow: hidden; white-space: nowrap; text-overflow: ellipsis">{{ column.title }}</span>
            <span class="resizer" @mousedown="(e) => onMouseDown(e, column)"></span>
            <span v-if="isResizing && resizingColumnKey === column.key" class="resizer-line"></span>
        </div>
    </template>
</a-table>

ts

const columns = ref<Array<any>>([]);
const isResizing = ref(false);
const resizingColumnKey = ref<string>('');

// 设置列字段,列宽、固定列
watch(
   () => headerColumns.value,
   () => {
	   columns.value = headerColumns.value.map((obj: HeaderColumns) => {
		    if (item.name === 'code') {
		        obj = { ...obj, width: 150, fixed: true };
		    } else if (item.name === 'seq') {
		        obj = { ...obj, width: 50, fixed: true };
		    } else if (item.name === 'operation') {
		        obj = { ...obj, width: 200, fixed: 'right' };
		    } else {
		        obj = { ...obj, width: (obj.title || '').length * 20 + 18 };
		    }
		    return obj;
		});
   },
   {
        immediate: true,
        deep: true,
    },
);

// 点击拖拽设置列宽
const onMouseDown = (event: MouseEvent, column: any) => {
    const startX = event.clientX; // 获取鼠标初始位置
    const startWidth = column.width || 0; // 获取初始宽度
    isResizing.value = true;
    resizingColumnKey.value = column.key;
    
	let animationFrameId: any; // 为了让移动更丝滑

    const mouseMoveHandler = (e: MouseEvent) => {
        if (!isResizing.value || !resizingColumnKey.value) return;

        if (animationFrameId) {
           cancelAnimationFrame(animationFrameId);
        }

        animationFrameId = requestAnimationFrame(() => {
            const newWidth = Math.max(50, startWidth + (e.clientX - startX));
            if (newWidth !== column.width) {
                column.width = newWidth;
            }
        });
    };

    const mouseUpHandler = () => {
        isResizing.value = false;
        resizingColumnKey.value = '';
        document.removeEventListener('mousemove', mouseMoveHandler);
        document.removeEventListener('mouseup', mouseUpHandler);
        if (animationFrameId) {
            cancelAnimationFrame(animationFrameId);
        }
    };

    document.addEventListener('mousemove', mouseMoveHandler);
    document.addEventListener('mouseup', mouseUpHandler);
};

css

:deep(.ant-table-cell-ellipsis.ant-table-cell-fix-left-last .ant-table-cell-content) {
    overflow: visible;
}

.resizable-header {
    position: relative;
    display: flex;
    align-items: center;

    .resizer {
        position: absolute;
        top: 0;
        right: -12px;
        z-index: 2;
        width: 6px;
        height: 100%;
        cursor: col-resize;
    }

    .resizer-line {
        position: absolute;
        top: -8px;
        right: -8px;
        bottom: -8px;
        width: 1px;
        background-color: #3388FF;
    }
}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐