antdesignvue表格实现伸缩列并限制最大最小宽度和限制操作
- 先看效果动图:
代码部分:
<template>
<a-table :rowKey="(record,index)=>index" :columns="columns" :components="components" :data-source="tableData" :scroll="{ x: scrollX }">
<template v-slot:action>
<a href="javascript:;">Delete</a>
</template>
</a-table>
</template>
<script>
import Vue from "vue";
import VueDraggableResizable from "vue-draggable-resizable";
Vue.component("vue-draggable-resizable", VueDraggableResizable);
export default {
name: "App",
data() {
return {
columns: [
{
title: "项目名称",
dataIndex: "name",
key: "name",
width: 150,
fixed: "left",
ellipsis: true // 伸缩列过小时省略号显示
},
{
title: "项目类型",
dataIndex: "type",
key: "type",
width: 150,
ellipsis: true
},
{
title: "项目编号",
dataIndex: "num",
key: "num",
width: 150,
ellipsis: true
},
{
title: "项目状态",
dataIndex: "status",
key: "status",
width: 100,
ellipsis: true
},
{
title: "备注1",
dataIndex: "commet1",
key: "commet1",
width: 200,
ellipsis: true
},
{
title: "备注2",
dataIndex: "commet2",
key: "commet2",
width: 200,
ellipsis: true
},
{
title: "备注3",
dataIndex: "commet3",
key: "commet3",
width: 200,
ellipsis: true
},
{
title: "备注4",
dataIndex: "commet4",
key: "commet4",
width: 200,
ellipsis: true
},
{
title: "操作",
dataIndex: "action",
key: "action",
scopedSlots: {
customRender: "action" },
width: 200,
fixed: "right",
},
],
tableData: [
{
name: "我是项目1111",
type: "我是项目类型啊啊",
num: "666-ffffa",
status: "稳定中",
commet1: "我是备注啊啊啊啊",
commet2: "我是备注啊啊啊啊",
commet3: "我是备注啊啊啊啊",
commet4: "我是备注啊啊啊啊",
},
{
name: "我是项目1111",
type: "我是项目类型啊啊",
num: "666-ffffa",
status: "稳定中",
commet1: "我是备注啊啊啊啊",
commet2: "我是备注啊啊啊啊",
commet3: "我是备注啊啊啊啊",
commet4: "我是备注啊啊啊啊",
},
{
name: "我是项目1111",
type: "我是项目类型啊啊",
num: "666-ffffa",
status: "稳定中",
commet1: "我是备注啊啊啊啊",
commet2: "我是备注啊啊啊啊",
commet3: "我是备注啊啊啊啊",
commet4: "我是备注啊啊啊啊",
},
],
};
},
computed: {
// 动态获取scrollX,防止数组固定列的大小变化
scrollX() {
return this.columns.reduce((preVal, curVal) => {
return preVal + curVal.width
}, 0)
}
},
created() {
this.initDrag(this.columns);
},
methods: {
initDrag(columns) {
const draggingMap = {
};
columns.forEach((col) => {
draggingMap[col.key] = col.width;
});
const draggingState = Vue.observable(draggingMap);
// resizeableTitle要小写,不然会有报错
const resizeableTitle = (h, props, children) => {
let thDom = null;
const {
key, ...restProps } = props;
const col = columns.find((col) => {
const k = col.dataIndex || col.key;
return k === key;
});
// col.key === 'action'限制操作列不可伸缩
if (!col.width || col.key === 'action') {
return <th {
...restProps}>{
children}</th>;
}
const onDrag = (x) => {
draggingState[key] = 0;
let width = Math.max(x, 100) // 此处100为伸缩时的最小宽度
width = Math.min(width, 400) // 此处400为伸缩时的最大宽度
col.width = width
};
const onDragstop = () => {
draggingState[key] = thDom.getBoundingClientRect().width;
};
return (
<th
{
...restProps}
v-ant-ref={
(r) => (thDom = r)}
width={
draggingState[key]}
class="resize-table-th"
>
{
children}
<vue-draggable-resizable
key={
col.key}
class="table-draggable-handle"
w={
10}
x={
col.width || draggingState[key]}
z={
1}
axis="x"
draggable={
true}
resizable={
false}
onDragging={
onDrag}
onDragstop={
onDragstop}
></vue-draggable-resizable>
</th>
);
};
this.components = {
header: {
cell: resizeableTitle,
},
};
},
},
};
</script>
<style lang="less" scoped>
// 这里的deep必须,不然样式无法穿透内部
/deep/.resize-table-th {
position: relative;
.table-draggable-handle {
height: 100% !important;
bottom: 0;
left: auto !important;
right: 0px;
cursor: col-resize;
touch-action: none;
position: absolute;
transform: none !important;
&:hover {
background: #1890ff;
}
&::after {
content: "|";
position: absolute;
top: 15px;
left: 3px;
color: #ccc;
}
}
}
</style>
下面新增了拖动表头的功能
需要安装sortablejs $ npm install sortablejs --save
代码如下:
<template>
<div class="box">
<a-table
:rowKey="(record, index) => index"
:columns="columns"
:components="components"
:data-source="tableData"
:scroll="{ x: scrollX, y: 100 }"
>
<template v-slot:action>
<a href="javascript:;">Delete</a>
</template>
</a-table>
</div>
</template>
<script>
import Sortable from 'sortablejs'
import Vue from "vue";
import VueDraggableResizable from "vue-draggable-resizable";
Vue.component("vue-draggable-resizable", VueDraggableResizable);
export default {
name: "App",
data() {
return {
columns: [
{
title: "项目名称",
dataIndex: "name",
key: "name",
width: 150,
fixed: "left",
ellipsis: true, // 伸缩列过小时省略号显示
},
{
title: "项目类型",
dataIndex: "type",
key: "type",
width: 150,
ellipsis: true,
},
{
title: "项目编号",
dataIndex: "num",
key: "num",
width: 150,
ellipsis: true,
},
{
title: "项目状态",
dataIndex: "status",
key: "status",
width: 100,
ellipsis: true,
},
{
title: "备注1",
dataIndex: "commet1",
key: "commet1",
width: 200,
ellipsis: true,
},
{
title: "备注2",
dataIndex: "commet2",
key: "commet2",
width: 200,
ellipsis: true,
},
{
title: "备注3",
dataIndex: "commet3",
key: "commet3",
width: 200,
ellipsis: true,
},
{
title: "备注4",
dataIndex: "commet4",
key: "commet4",
width: 200,
ellipsis: true,
},
{
title: "操作",
dataIndex: "action",
key: "action",
scopedSlots: { customRender: "action" },
width: 200,
fixed: "right",
},
],
tableData: [
{
name: "我是项目1111",
type: "我是项目类型啊啊",
num: "666-ffffa",
status: "稳定中",
commet1: "我是备注啊啊啊啊",
commet2: "我是备注啊啊啊啊",
commet3: "我是备注啊啊啊啊",
commet4: "我是备注啊啊啊啊",
},
{
name: "我是项目1111",
type: "我是项目类型啊啊",
num: "666-ffffa",
status: "稳定中",
commet1: "我是备注啊啊啊啊",
commet2: "我是备注啊啊啊啊",
commet3: "我是备注啊啊啊啊",
commet4: "我是备注啊啊啊啊",
},
{
name: "我是项目1111",
type: "我是项目类型啊啊",
num: "666-ffffa",
status: "稳定中",
commet1: "我是备注啊啊啊啊",
commet2: "我是备注啊啊啊啊",
commet3: "我是备注啊啊啊啊",
commet4: "我是备注啊啊啊啊",
},
],
};
},
computed: {
// 动态获取scrollX,防止数组固定列的大小变化
scrollX() {
return this.columns.reduce((preVal, curVal) => {
return preVal + curVal.width;
}, 0);
},
},
created() {
this.initDrag(this.columns);
},
mounted() {
this.columnDrop()
},
methods: {
initDrag(columns) {
const draggingMap = {};
columns.forEach((col) => {
draggingMap[col.key] = col.width;
});
const draggingState = Vue.observable(draggingMap);
const resizeableTitle = (h, props, children) => {
let thDom = null;
const { key, ...restProps } = props;
const col = columns.find((col) => {
const k = col.dataIndex || col.key;
return k === key;
});
// col.key === 'action'限制操作列不可伸缩
if (!col.width || col.key === "action") {
return <th {...restProps}>{children}</th>;
}
const onDrag = (x) => {
draggingState[key] = 0;
let width = Math.max(x, 100); // 此处100为伸缩时的最小宽度
width = Math.min(width, 400); // 此处400为伸缩时的最大宽度
col.width = width;
};
const onDragstop = () => {
draggingState[key] = thDom.getBoundingClientRect().width;
};
return (
<th
{...restProps}
v-ant-ref={(r) => (thDom = r)}
width={draggingState[key]}
class="resize-table-th"
>
{children}
<vue-draggable-resizable
key={col.key}
class="table-draggable-handle"
w={10}
x={col.width || draggingState[key]}
z={1}
axis="x"
draggable={true}
resizable={false}
onDragging={onDrag}
onDragstop={onDragstop}
></vue-draggable-resizable>
</th>
);
};
this.components = {
header: {
cell: resizeableTitle,
},
};
},
//列拖拽
columnDrop() {
var _this = this;
const wrapperTr = document.querySelector(".ant-table-wrapper tr");
this.sortable = Sortable.create(wrapperTr, {
handle: ".ant-table-header-column", // 防止拉伸时触发拖拽
animation: 180,
delay: 0,
//之前调用onEnd方法会出现表格DOM不更新以及表头对不上的情况所以更换为onUpdate方法
onUpdate: function (evt) {
//修改items数据顺序
var newIndex = evt.newIndex;
var oldIndex = evt.oldIndex;
const newItem = wrapperTr.children[newIndex];
const oldItem = wrapperTr.children[oldIndex];
// 先删除移动的节点
wrapperTr.removeChild(newItem);
// 再插入移动的节点到原有节点,还原了移动的操作
if (newIndex > oldIndex) {
wrapperTr.insertBefore(newItem, oldItem);
} else {
wrapperTr.insertBefore(newItem, oldItem.nextSibling);
}
// 更新items数组
var item = _this.columns.splice(oldIndex, 1);
_this.columns.splice(newIndex, 0, item[0]);
// 下一个tick就会走patch更新
// 如果需要缓存表头,比如缓存到vuex中
//每次更新调取保存用户接口
// _this.saveColumns("columnChange", _this.columns);
},
});
},
},
};
</script>
<style lang="less" scoped>
.box {
padding: 20px;
}
/deep/.resize-table-th {
position: relative;
.table-draggable-handle {
height: 100% !important;
bottom: 0;
left: auto !important;
right: 0px;
cursor: col-resize;
touch-action: none;
position: absolute;
transform: none !important;
&:hover {
background: #1890ff;
}
&::after {
content: "|";
position: absolute;
top: 15px;
left: 3px;
color: #ccc;
}
}
}
</style>
有疑问欢迎一起讨论啊~