场景
业务需求做多级表头拖拽,具体要求如下:
没有子表头的父表头不可拖拽,第一个序号不可拖拽,父表头拖拽换位,同一个父表头下的子表头拖拽时 子表头换位,不同父表头下子表头拖拽时子表头不变两个父表头换位。
一、代码
1.页面部分
我这里是多级表头的数据表格。使用了Element UI库来构建表格组件,并结合了sortable.js插件来实现表格列的拖拽排序功能。
在模板部分,使用了<el-table>组件来展示表格数据,通过v-for指令循环渲染表头列和子列。每一列都设置了相应的属性,如label、align、sortable等,在表格中显示对应的数据页面代码如下:
<template>
<el-table :data="tableData" tooltip-effect="dark" class="financeTotalTwo financeTotal_spfcjhz"
style="width: 100%" height="100%" :size="GLOBAL.tableSize" ref="multipleTable">
<el-table-column type="index" width="80" label="序号" align="center"></el-table-column>
<el-table-column
v-for="(item, index) in titleList"
v-if="item.show"
:label="item.label"
:align="item.align"
:sortable="item.sortable"
:prop="item.prop"
:min-width="item.width"
:class-name="(item.prop == 'cityName' ||item.prop == 'tjDate' || item.prop == 'whHouseUseName' ||item.prop == 'blockName' || item.prop =='districtName' ) ? 'disableDrag':'allowDrag'"
:key="item.prop + index"
class="allowDrag"
>
<template slot-scope="scope">
<div>{{ scope.row[item.prop] }}</div>
</template>
<el-table-column
v-for="(zitem, zindex) in item.children"
v-if="zitem.show && item.children.length >0"
:label="zitem.label"
:align="zitem.align"
:sortable="zitem.sortable"
:prop="zitem.prop"
:min-width="zitem.width"
class-name="zallowDrag"
:class="'draggable'"
:key="zitem.prop + zindex"
>
<template slot-scope="scope">
<div class="finance_table_alignRight">{{ scope.row[zitem.prop] }}</div>
</template>
</el-table-column>
</el-table-column>
</el-table>
</template>
2.表格数据及拖拽处理部分
在脚本部分,定义了组件的数据对象,包括tableHeaderList、titleList和tableData。tableHeaderList用来存储表头的配置信息,titleList根据tableHeaderList筛选出有效的表头信息,tableData则是实际要展示的表格数据。
在组件的生命周期钩子函数中,调用了loadTitleList方法来初始化有效的表头列表,并通过setTimeout延迟执行columnDrop方法来初始化表格列的拖拽功能。
loadTitleList方法遍历tableHeaderList,根据show属性判断表头是否有效,若有效则将其添加到titleList中。
columnDrop方法初始化了表格列的拖拽功能,分别对父表头列和子表头列进行了绑定。在拖拽结束时,根据拖拽的起始索引和结束索引,对titleList进行相应的操作,包括表头交换和子表头交换:
<script>
import Sortable from 'sortablejs';
import { moveArr, strMapToObj} from './utils/utils'
export default {
name: "landSj",
components: {
},
data() {
return {
tableHeaderList: [
// 多级表头数据
{
"id":4,
"show":true,
"prop":"tjDate",
"label":"时间",
"width":130,
"sortable":false,
"children":[
],
"align":"center",
"width":130,
"template":true
},
{
"id":5,
"show":true,
"label":"供应信息",
"prop":'gy',
"sortable":false,
"children":[
{
"id":6,
"show":true,
"prop":"gyts",
"label":"供应套数",
"sortable":"gyts",
"align":"center",
"width":130,
"template":true
},
{
"id":7,
"show":true,
"prop":"gymj",
"label":"供应面积(㎡)",
"sortable":"gymj",
"align":"center",
"width":130,
"template":true
}
],
"align":"center",
"width":130,
"template":true
},
{
"id":11,
"show":true,
"label":"预测成交信息",
"sortable":false,
"prop":'yccj',
"children":[
{
"id":12,
"show":true,
"prop":"ysts",
"label":"预测成交套数",
"sortable":"ysts",
"align":"center",
"width":130,
"template":true
},
{
"id":13,
"show":true,
"prop":"ysmj",
"label":"预测面积(㎡)",
"sortable":"ysmj",
"align":"center",
"width":130,
"template":true
},
{
"id":14,
"show":true,
"prop":"yszj",
"label":"预测成交金额(万元)",
"sortable":"yszj",
"align":"center",
"width":130,
"template":true
},
{
"id":15,
"show":true,
"prop":"ysjj",
"label":"预测成交均价",
"sortable":"ysjj",
"align":"center",
"width":130,
"template":true
}
],
"align":"center",
"width":130,
"template":true
},
{
"id":21,
"show":true,
"label":"存量信息",
"sortable":false,
"prop":'cl',
"children":[
{
"id":22,
"show":true,
"prop":"clts",
"label":"存量套数",
"sortable":"clts",
"align":"center",
"width":130,
"template":true
},
{
"id":23,
"show":true,
"prop":"clmj",
"label":"存量面积(㎡)",
"sortable":"clmj",
"align":"center",
"width":130,
"template":true
},
{
"id":24,
"show":true,
"prop":"xzxclts",
"label":"限制性套数",
"sortable":"xzxclts",
"align":"center",
"width":130,
"template":true
},
{
"id":25,
"show":true,
"prop":"xzxclmj",
"label":"限制性面积(㎡)",
"sortable":"xzxclmj",
"align":"center",
"width":130,
"template":true
}
],
"align":"center",
"width":130,
"template":true
}
],
titleList:[],
tableData:[
{
"cityId": null,
"cityName": null,
"districtId": null,
"districtName": null,
"blockId": null,
"blockName": null,
"tjDate": "2023-01-01-2023-10-26",
"gyts": "30119",
"gymj": "4027187.55",
"qngyts": null,
"qngymj": null,
"ysts": "60022",
"ysmj": "7741787.72",
"yszj": "25548525",
"ysjj": "33001",
"qnysts": null,
"qnysmj": null,
"qnyszj": null,
"qnysjj": null,
"clts": "0",
"clmj": "0.00",
"xzxclts": "0",
"xzxclmj": "0.00"
}
]
};
},
created() {
},
mounted() {
this.loadTitleList();
setTimeout(() => {
this.columnDrop();
}, 100);
},
beforeUpdate(){
this.$nextTick(() => { //在数据加载完,重新渲染表格
if(this.$refs['multipleTable']){
this.$refs['multipleTable'].doLayout();
}
})
},
methods: {
/**
* 获取有效的表格
*/
loadTitleList(){
this.titleList = []
// 制造数据
for(var i = 0;i < this.tableHeaderList.length;i++){
if(this.tableHeaderList[i].show){
this.titleList.push(this.tableHeaderList[i]);
}
};
},
//表格列拖拽
columnDrop() {
//父表头列拖拽
const wrapperTr = document.querySelector('.el-table__header-wrapper tr')
this.sortable = Sortable.create(wrapperTr, {
handle:".allowDrag",
animation: 180,
delay: 0,
filter: function (evt) {
const element = evt.target;
// 判断元素是否具有 allowDrag 类名,如果有则可拖拽
return element.classList.contains('allowDrag');
},
onEnd: evt => {
if (evt.oldIndex === evt.newIndex) return;
//表头排序,第一列为序号,第二列是时间 ,因为 需求要求序号跟时间咧不参与拖拽 因此减1
moveArr(this.titleList, evt.newIndex -1, evt.oldIndex -1)
},
onMove(e) {
return e.related.className.indexOf('allowDrag') > 1;
}
})
//子表头列拖拽
const wrapperTrx = document.querySelector('.el-table__header-wrapper tr+tr')
this.sortablex = Sortable.create(wrapperTrx, {
handle:".zallowDrag",
animation: 180,
delay: 0,
onEnd: evt => {
if (evt.oldIndex === evt.newIndex) return;
// 计算子表头的起始索引和结束索引
let startIndex = 0;
for (let i = 1; i < this.titleList.length; i++) {
const childrenLength = this.titleList[i].children.length;
if (startIndex <= evt.oldIndex && evt.oldIndex < startIndex + childrenLength) {
const adjustedOldIndex = evt.oldIndex - startIndex;
const adjustedNewIndex = evt.newIndex - startIndex;
if (adjustedNewIndex >= childrenLength) {
// 超出当前拖拽父表头长度,不进行子表头操作 直接操作父表头交换操作
moveArr(this.titleList, i, i+1)
break;
}
// 判断是否在同一个子表头的children下切换
if (adjustedOldIndex >= 0 && adjustedOldIndex < childrenLength && adjustedNewIndex >= 0 && adjustedNewIndex < childrenLength) {
//'同一个父表头 子表头交换操作
moveArr(this.titleList[i].children, adjustedNewIndex, adjustedOldIndex);
break;
}else{
//非同一个父表头 只进行父表头操作
moveArr(this.titleList, i, i-1)
break;
}
}
startIndex += childrenLength;
}
},
onMove(e) {
return e.related.className.indexOf('allowDrag') > 1;
}
})
},
}
};
</script>
moveArr(arr, newIndex, oldIndex) {
arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0])
}