低版本的antd table组件 官网文档上有可伸缩列功能,但是用起来有很明显的bug,无法直接拿来用;接下来给大家提供一个手写的拖拽方案:
1.首先,写好v-table组件,这里最重要的就是table组件中的‘components’属性,官方对component的描述是"覆盖默认的 table 元素",意思就是对table进行重写
2.然后用计算属性去定义这个components,这里的“ResizeHeader”就是重写table组件的一个方法,这里面还引入了‘customHeaderCell, ResizeColumnProvide’这两个方法后面会介绍
import ResizeHeader, { customHeaderCell, ResizeColumnProvide } from './header'
computed: {
components() {
return {
header: {
cell: ResizeHeader,
},
}
},
}
mixins: [ResizeColumnProvide],
3.我们创建一个header.js 文件 用来放ResizeHeader方法
//重构table组件的核心渲染函数
const ResizeHeader = (h, props, children) => {
const { key, column, ...restProps } = props;
let content = [].concat(children);
if (column?.resizable) {
const handlerVNode = h(DragHandler, {
props: {
width: column.width,
minWidth: Math.min(column.width, 100),
column: column
}
}, []);
content = content.concat(
handlerVNode
);
}
return h("th", {
key,
props: { column },
...Object.assign({}, restProps, { class: (restProps.class || "") + " nio-header-th" })
}, content);
};
4.ResizeHeader方法里面“DragHandler”组件是对table组件重写后的一些拖拽操作的事件监听处理
const events = {
start: "mousedown",
move: "mousemove",
stop: "mouseup",
};
//对table组件进行重构的核心方法组件
const DragHandler = {
name: "nio-drag-handler",
//获取混入的方法(重新设置表头宽度)
inject: {
onResizeColumn: {
default: noop
}
},
props: {
column: { type: Object, required: true },
minWidth: { type: Number, required: true }
},
data() {
return {
dragging: false,
thWidth: 0,// 列头的宽度,用于计算
startX: 0,
moveEvent: {
remove: noop
},
stopEvent: {
remove: noop
}
};
},
//销毁监听
beforeDestroy() {
this.removeEvents();
},
methods: {
//鼠标开始拖拽
handleMouseDown(e) {
e.stopPropagation();
e.preventDefault();
this.handleStart(e);
},
handleStart(e) {
this.dragging = true;
this.removeEvents();
this.thWidth = this.$el.parentNode.getBoundingClientRect().width;
if (e instanceof MouseEvent && e.which !== 1) {
return;
}
if (e.stopPropagation) e.stopPropagation();
this.startX = e.pageX;
this.moveEvent = addEventListenerWrap(document.documentElement, events.move, this.handleMove);
this.stopEvent = addEventListenerWrap(document.documentElement, events.stop, this.handleStop);
},
handleMove(e) {
this.updateWidth(e);
},
handleStop(e) {
this.dragging = false;
this.updateWidth(e);
this.removeEvents();
},
updateWidth(e) {
let pageX = e.pageX;
const tmpDeltaX = this.startX - pageX;
let w = Math.max(this.thWidth - tmpDeltaX, this.minWidth);
w = Math.min(w, Infinity);
this.onResizeColumn(w, this.column);
},
removeEvents() {
this.moveEvent.remove();
this.stopEvent.remove();
}
},
//渲染div 绑定拖拽
render(h) {
const line = h("div", { class: "nio-drag-handler-line" }, []);
return h("div", { class: "nio-drag-handler", on: { mousedown: this.handleMouseDown } }, [line]);
}
};
//监听鼠标移动的方法
function addEventListenerWrap(target, eventType, cb) {
if (target && target.addEventListener) {
target.addEventListener(eventType, cb);
}
return {
remove: () => {
if (target && target.removeEventListener) {
target.removeEventListener(eventType, cb);
}
},
};
}
5.DragHandler组件中 最重要的方法是“onResizeColumn”方法,废话不多说 上代码
重点:ResizeColumnProvide 方法要以mixins混入的方式 引入到table组件的页面中,这样“DragHandler”就能用到this.onResizeColumn()方法,对列的宽度进行改变
//在table组件中混入的一个方法 provide => onResizeColumn
//目的是在table重写中(DragHandler方法)
//可以用到构造的onResizeColumn方法(重新设置表头的宽度)
export const ResizeColumnProvide = {
name: "ResizeColumnProvideMixin",
provide() {
return {
onResizeColumn: this.onResizeColumn
};
},
data() {
const onResizeColumn = throttle2(this._resizeColumn, 30);
return {
columnsKey: "columns", //这个属性是table组件表头的集合名称 :columns="columns"
moving:false, //这个属性是为了在拖拽时 阻止表头触发排序的功能
onResizeColumn
};
},
methods: {
_resizeColumn(w, column) {
const columns = this[this.columnsKey];
this.moving = true
let time = setTimeout(() => {
this.moving = false
clearTimeout(time)
}, 2000);
//在拖拽时找到对应表头 重新设置表头的宽度
//这就是为什么表头column要绑定customHeaderCell的原因
const matchedIndex = columns.findIndex(item => item.key === column.key);
columns[matchedIndex].width = w;
}
}
};
6.上面的方法中的‘throttle2’是对拖拽的一个节流操作
//拖拽的节流方法
const throttle2 = (fn, delay) => {
let valid = true;
return (...args) => {
if (!valid) {
return;
}
valid = false;
setTimeout(() => {
fn(...args);
valid = true;
}, delay);
};
};
7.在第一步中 table组件 ,这里用到这个方法 是因为antd的table组件在用components属性进行重写时,无法获取到column(对应的表头数据),但是在拖拽的操作中,又需要拿到对应的表头数据,这里只能用table内置的customHeaderCell方法,在生成表头数据的时候,给每个表头数据绑定customHeaderCell方法
//table组件内置的一个方法
export function customHeaderCell(column) {
return Object.assign({
column: column
});
}
//给column绑定 不绑定的话ResizeHeader方法中的参数props无法获取到column信息
//这里的list 就是表头的集合
this.columns = list.map((item) => Object.assign({}, item, { customHeaderCell }))
8.最后附加上css样式吧
.nio-header-th {
position: relative;
isolation: isolate;
}
.nio-drag-handler {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 2px;
z-index: 1;
border: 1px dashed #eee;
&:hover,
&:focus {
cursor: col-resize;
border: 1px dashed #0bd9d9;
}
}