项目中需实现以下这种效果:级联数据,表格横向排列,数据之间相互联动。现有UI组件无法满足此需求,只能撸起袖子加油干!!
实现效果如下
开发前先准备一个树形数据 treeData.js
const treeData = [
{
areaName: "江苏",
areaCode: "100",
checked: false,
indeterminate: false,
order: 1,
children: [
{
areaName: "无锡",
areaCode: "1001",
parentCode: "100",
checked: false,
indeterminate: false,
order: 1,
children: [
{
areaName: "鼓楼区",
areaCode: "10011",
parentCode: "1001",
checked: false,
indeterminate: false,
order: 1,
children: [],
},
{
areaName: "玄武区",
areaCode: "10012",
parentCode: "1001",
checked: false,
indeterminate: false,
order: 2,
children: [],
},
{
areaName: "秦港区",
areaCode: "10013",
parentCode: "1001",
checked: false,
indeterminate: false,
order: 3,
children: [],
},
{
areaName: "浦口区",
areaCode: "10014",
parentCode: "1001",
checked: false,
indeterminate: false,
order: 4,
children: [],
},
],
},
{
areaName: "南京",
areaCode: "1002",
parentCode: "100",
checked: false,
indeterminate: false,
order: 1,
children: [
{
areaName: "鼓楼区2",
areaCode: "10021",
parentCode: "1002",
checked: false,
indeterminate: false,
order: 1,
children: [],
},
{
areaName: "玄武区2",
areaCode: "10022",
parentCode: "1002",
checked: false,
indeterminate: false,
order: 2,
children: [],
},
{
areaName: "秦港区2",
areaCode: "10023",
parentCode: "1002",
checked: false,
indeterminate: false,
order: 3,
children: [],
},
{
areaName: "浦口区2",
areaCode: "10024",
parentCode: "1002",
checked: false,
indeterminate: false,
order: 4,
children: [],
},
],
},
],
},
{
areaName: "浙江",
areaCode: "200",
checked: false,
indeterminate: false,
order: 1,
children: [
{
areaName: "杭州",
areaCode: "2001",
parentCode: "200",
checked: false,
indeterminate: false,
order: 1,
children: [
{
areaName: "鼓楼区1",
areaCode: "20011",
parentCode: "2001",
checked: false,
indeterminate: false,
order: 1,
children: [],
},
{
areaName: "玄武区1",
areaCode: "20012",
parentCode: "2001",
checked: false,
indeterminate: false,
order: 2,
children: [],
},
{
areaName: "秦港区1",
areaCode: "20013",
parentCode: "2001",
checked: false,
indeterminate: false,
order: 3,
children: [],
},
{
areaName: "浦口区1",
areaCode: "20014",
parentCode: "2001",
checked: false,
indeterminate: false,
order: 4,
children: [],
},
],
},
{
areaName: "宁波",
areaCode: "2002",
parentCode: "200",
checked: false,
indeterminate: false,
order: 1,
children: [
{
areaName: "鼓楼区2",
areaCode: "20021",
parentCode: "2002",
checked: false,
indeterminate: false,
order: 1,
children: [],
},
{
areaName: "玄武区2",
areaCode: "20022",
parentCode: "2002",
checked: false,
indeterminate: false,
order: 2,
children: [],
},
{
areaName: "秦港区2",
areaCode: "20023",
parentCode: "2002",
checked: false,
indeterminate: false,
order: 3,
children: [],
},
{
areaName: "浦口区2",
areaCode: "20024",
parentCode: "2002",
checked: false,
indeterminate: false,
order: 4,
children: [],
},
],
},
],
},
];
export default treeData;
完整代码
具体的每一个细节我都在代码注释了,这里就不单独抽出来赘述了,详细的分析请看大屏幕…
<template>
<a-modal
:title="addConfig.title"
:showSubmit="false"
:visible="addConfig.visible"
@ok="handleAddOk"
@cancel="handleCancel"
>
<div class="container_box">
<div class="container_header">
<div v-for="item in 3" :key="item" class="header_item">
<div class="title"><span>名称</span></div>
<div class="title"><span>排序</span></div>
</div>
</div>
<div class="container_body">
<div class="body_item" v-for="(list, index) in listData" :key="index">
<div
class="item_list"
:style="{
backgroundColor: item.clickStatus
? 'rgba(19,194,194,.3)'
: '#ffffff',
}"
v-for="item in list"
:key="item.areaCode"
@click.capture="handleClickAreaItem(item)"
>
<div class="item">
<a-checkbox
:indeterminate="item.indeterminate"
v-model="item.checked"
@change="onCheckAllChange($event, item)"
>
{{ item.areaName }}
</a-checkbox>
</div>
<div class="item">
<a-input v-model="item.order" placeholder="排序" />
</div>
</div>
</div>
</div>
</div>
</a-modal>
</template>
<script>
import treeData from "./treeData";
let filterData = [];
export default {
name: "addArea",
props: {
addConfig: Object,
},
data() {
return {
listData: [],
};
},
mounted() {
this.handleTreeData(treeData, 1);
// 初始化数据
treeData[0].clickStatus = true;
treeData[0].children[0].clickStatus = true;
this.listData = [
treeData,
treeData[0].children,
treeData[0].children[0].children,
];
},
methods: {
// 点击每一个item 显示选中状态并进行数据联动
handleClickAreaItem(item) {
if (item.level === 3) return;
let list = [];
if (item.parentCode) {
// 点击第二或第三级
this.handleFilterData(treeData, item.parentCode);
list = filterData[0]?.children;
} else {
// 点击第一级
list = treeData;
// 切换第一级时 第二级默认选择第一个
let arr = treeData.filter((lis) => lis.areaCode === item.areaCode);
arr[0]?.children.forEach((lis, index) => {
if (index === 0) {
lis.clickStatus = true;
} else {
lis.clickStatus = false;
}
});
}
list.forEach((lis) => {
if (lis.areaCode === item.areaCode) {
lis.clickStatus = true;
// 子级数据跟随联动
if (item.level === 1) {
this.listData[1] = lis.children;
this.listData[2] = lis.children[0].children;
} else if (item.level === 2) {
this.listData[2] = lis.children;
}
} else {
lis.clickStatus = false;
}
});
},
handleTreeData(list, levelIndex) {
list.forEach((lis) => {
lis.clickStatus = false;
// 动态标记每层层级
lis.level = levelIndex;
if (lis.children && lis.children.length !== 0)
this.handleTreeData(lis.children, levelIndex + 1);
});
},
handleAddOk(e) {
e && e.preventDefault();
},
handleCancel() {
this.$emit("cancel");
},
// 向下处理树形选中/未选中
handleDownTreeCheck(arr, checked) {
arr.forEach((iii) => {
iii.indeterminate = false;
iii.checked = checked;
if (iii.children && iii.children.length !== 0)
this.handleDownTreeCheck(iii.children, checked);
});
},
// 向上处理树形选中/未选中
handleUpTreeCheck(item) {
this.handleFilterData(treeData, item.parentCode);
let list = filterData[0];
let status1 = list.children.some((ii) => ii.checked);
let status2 = list.children.every((ii) => ii.checked);
let statusIn = list.children.some((ii) => ii.indeterminate);
// 当不全选中且子集有勾选中 或者 子集有indeterminate状态为true
list.indeterminate = status1 || statusIn;
// 当全选中的时候 indeterminate 状态 为false
if (status2) list.indeterminate = false;
list.checked = status2;
// 层层向上处理
if (list.parentCode) this.handleUpTreeCheck(list);
},
onCheckAllChange(e, item) {
item.indeterminate = false;
filterData = [];
// 当前点击的该项中有父级的情况下 比如当前点击项为第二、三....
if (item.parentCode) this.handleFilterData(treeData, item.parentCode);
// 当前点击的该项中存在父级数据情况下 比如当前点击项为第二、三.... 则向上处理元素状态
if (filterData.length > 0) this.handleUpTreeCheck(item);
// 当前点击的该项中存在子集数据情况下 则向下处理元素状态
if (item.children && item.children.length > 0)
this.handleDownTreeCheck(item.children, e.target.checked);
},
handleFilterData(list, parentCode) {
// 过滤数据
list.forEach((lis) => {
if (lis.areaCode === parentCode) {
filterData = [];
filterData.push(lis);
}
if (lis.children && lis.children.length !== 0)
this.handleFilterData(lis.children, parentCode);
});
},
},
};
</script>
<style lang="less" scoped>
/deep/.ant-modal {
width: 840px !important;
.ant-modal-close {
color: #fff;
.ant-modal-close-x {
height: 47px;
line-height: 47px;
}
}
.ant-modal-header {
padding: 12px 20px;
background-color: #02c7b5;
.ant-modal-title {
font-size: 16px;
font-weight: 500;
color: #ffffff;
}
}
.ant-modal-body {
// padding: 24px 27px 6px;
}
.container_box {
width: 100%;
height: auto;
display: flex;
flex-direction: column;
align-items: center;
.container_header {
display: flex;
align-items: center;
width: 100%;
background-color: #f4f7fb !important;
border-radius: 6px 6px 0px 0px;
.header_item {
flex: 4;
height: 42px;
line-height: 42px;
display: flex;
align-items: center;
.title {
flex: 6;
text-align: center;
font-size: 14px;
font-weight: 400;
color: #242525;
}
}
}
.container_body {
display: flex;
width: 100%;
.body_item {
flex: 4;
flex-direction: column;
.item_list {
display: flex;
align-items: center;
margin-top: 10px;
.item {
flex: 6;
text-align: center;
font-size: 14px;
font-weight: 400;
color: #242525;
.ant-input {
width: 86px !important;
}
}
}
&:nth-child(2) {
border-left: 2px solid #f1f1f1;
border-right: 2px solid #f1f1f1;
}
}
}
}
}
</style>