一、最近遇到产品提的一个需求,需求如下:
1.这是基本的样式:
2.每个字段的可选项默认只显示一行,点击右侧的“更多”按钮,可以展开全部可选项。在点击收起可回到一行。
3.同一字段所勾选的项之间是“或”的关系,不同字段所勾选的项之间是“且”的关系
4.勾选了某个字段的某个可选项后,要立刻根据刚勾选的选项,更新其它字段的可选项,禁用掉无效的可选项。
5.被勾选的筛选项,显示在顶部“筛选条件”栏,相同字段的显示在同一个框内,中间用逗号隔开。筛选条件框的顺序与下方筛选字段的顺序一致。
同时后端会返回存在关联的可选项的情况,如:
['螺纹钢','@6','HRB40EE','宏承'],
['螺纹钢','@6','HRB40EE','冷钢'],
['螺纹钢','@6','HRB40CC','泰天'],
['螺纹钢','@6','HRB40CC','沙钢'],
简单总结一下:点击规格@6后,查询规格@6下存在的材质和企业,将不存在的选项禁用。
二、解决思路如下:
1.首先点击规格@6时将规格记录为一开始点击的字段,用firstRange保存,不显示禁用选项;将后端返回的可选项情况用screenTable字段保存做为查询的目录。
2.当点击@6时,根据目录screenTable查询规格@6时存在的情况,将不存在的材质和企业的选项禁用,查询到材质HRB40EE,HRB4000,HRB50EE,将材质HRB500禁用,查询到企业冷钢,中天,沙钢,将萍钢等其他选项禁用
3.当点击材质HRB40EE时,查询到企业宏承,冷钢,再次将泰天等其他选项禁用
4.当同时点击材质HRB40EE和HRB40CC时,查询到企业宏承冷钢,泰天,沙钢,将其他选项禁用
5.当取消材质HRB40CC的勾选时,回到步骤3,同时清空与材质HRB40CC相关的企业的勾选
6.当材质取消勾选时,如何判断它下级的企业是否需要取消勾选呢,这就要通过已勾选的和需要禁止的项去比较,从已勾选的选项中排除需禁用的
三、代码如下:
<template>
<div class="content">
<el-dialog
title="标的筛选"
:visible.sync="dialogVisible"
width="720px"
:footer="false"
>
<div class="action-area">
<div class="left-area">
<template v-if="groupList.length>0">
选择已添加范围:
<el-select v-model="range" placeholder="请选择" @change="selectChange" style="">
<el-option
v-for="item in rangeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</div>
<div class="right-area">
<el-button size="mini" @click="resetSelect">重置筛选</el-button>
<el-button size="mini" @click="delRange">移除当前范围</el-button>
</div>
</div>
<div class="condition-area">
筛选条件:
<span class="condition-item" v-for="(item,index) in groupList" :key="index">
{{item.label}}:
<template v-if="item.checkList.length>0">
{{item.checkList.toString()}}
</template>
</span>
</div>
<div class="select-area">
<div class="select-wrap" v-for="(item,index) in groupList" :key="index">
<div class="title">{{item.label}}:</div>
<div class="select-content" :style="{height:item.show?'auto':'35px'}">
<el-checkbox-group v-model="item.checkList" @change="checkboxChange($event,item.label)">
<el-checkbox :label="val" v-for="(val) in item.skuList" :key="val" @change="minboxChange( $event,item.label)" :disabled="item.abledList.indexOf(val)==-1"></el-checkbox>
</el-checkbox-group>
</div>
<div class="more" @click="item.show = !item.show">{{item.show?'收起':'更多'}}</div>
</div>
</div>
<div class="tip-area" v-if="groupList.length==0">
请在左侧‘目录-范围’中选中范围并添加。
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small">取 消</el-button>
<el-button type="primary" @click="submitForm('ruleForm')" size="small">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
data(){
return{
dialogVisible:true,
show:true,
range:'',
describes:['品种','规格','材质','企业'],
rangeList:[
{
label:'螺纹钢',
value:'123',
describes:['品种','规格','材质','企业'],
attribute:[
{
label:'品种',
skuList:['螺纹钢'],
show:true,
checkList:[],//已经点击的
abledList:[],//允许被点击的
},
{
label:'规格',
skuList:['@6','@7','@8','@9','@10','@11','@12','@13','@14','@15'],
show:true,
checkList:[],//已经点击的
abledList:[],//允许被点击的
},
{
label:'材质',
skuList:['HRB40EE','HRB40CC','HRB41BB','HRB304E'],
show:true,
checkList:[],
abledList:[],//允许被点击的
},
{
label:'企业',
skuList:['宏承','泰天','冷钢','沙钢'],
show:true,
checkList:[],
abledList:[],//允许被点击的
}
]
},
{ label:'罗盘',
value:'113',
attribute:[
{
label:'尺寸',
skuList:['小','中','大','中大'],
show:true,
checkList:[]
},
{
label:'材质',
skuList:['铜线','金属','塑料','聚乙烯'],
show:true,
checkList:[]
},
]
}
],
groupList:[],//当前展示的范围分组数据
screenTable:[//筛选模板,记录存在的数据,根据它来筛选
['螺纹钢','@6','HRB40EE','宏承'],
['螺纹钢','@6','HRB40EE','冷钢'],
['螺纹钢','@6','HRB40CC','泰天'],
['螺纹钢','@6','HRB40CC','沙钢'],
],
screenGroup:[],//每次筛选后都缩小可选择的范围
screenItem:[],//记录点击的顺序,到最后一个点击筛选项时不再过滤区间
firstRange:'',//开始点击的筛选项,点击后去筛选其他两个项可勾选的项
}
},
watch:{
range(newValue,oldValue){
console.log(newValue,oldValue);
this.rangeList.forEach(item=>{//先保存选中的数据再更新区域
if(oldValue==item.value){
item.attribute = this.groupList;
}
})
this.rangeList.forEach(item=>{
if(newValue==item.value){
this.groupList = item.attribute;
}
})
},
groupList:{
handler:function(newValue,oldValue){
let flag = true;
newValue.forEach(item=>{
if(item.checkList.length>0){//只要有一个勾选了就false
flag=false;
}
})
if(flag){
this.firstRange = '';//都没有勾选就初始化最开始选择的项
this.groupList.forEach(item=>{
item.abledList = item.skuList;
})
this.screenItem = [];
}
},
deep:true
}
},
methods:{
selectChange(e){
console.log('e',e);
},
resetSelect(){//清除当前选中
this.groupList.forEach(item=>{
item.checkList = [];
})
},
delRange(){//删除范围
console.log('rangeList',this.rangeList);
if(this.rangeList.length==0){//移除完
return;
}
let idx = -1;
this.rangeList.forEach((item,index)=>{
if(item.value==this.range){
idx = index;
}
})
console.log('idx',idx);
if(idx==(this.rangeList.length-1)){//删除的是最后一个
this.groupList = this.rangeList[idx-1]?this.rangeList[idx-1].attribute:[];//选中上一个
this.range = this.rangeList[idx-1]?this.rangeList[idx-1].value:'';//选中上一个
//this.rangeList.splice(idx,1);//删除当前范围
this.rangeList.pop();
}else{
this.groupList = this.rangeList[idx+1]?this.rangeList[idx+1].attribute:[];//选中下一个
this.range = this.rangeList[idx+1]?this.rangeList[idx+1].value:'';//选中上一个
this.rangeList.splice(idx,1);//删除当前范围
}
},
checkboxChange(e,data){
console.log('e,data',e,data);
if(this.screenItem.indexOf(data)==-1){
this.screenItem.push(data);
}
// if(this.firstRange=='' || this.firstRange==data){
// this.firstRange = data;
// }
this.firstRange = data;
if( this.screenItem.length==this.groupList.length && data == this.screenItem[this.screenItem.length-1]){
return;
}
//if(this.screenItem.length<this.groupList.length){
this.screenGroup = this.screenTable;
let list = [];
this.groupList.forEach(item=>{
list.push(item.checkList);
})
console.log('list',list);
console.log('describes',this.describes);
const idx = this.screenItem.indexOf(data);
const alist = this.screenItem.slice(0,idx+1);//不允许下级过滤上级
//const alist = this.screenItem;//
console.log('alist',alist);//&& alist.indexOf(this.describes[index])!=-1
list.forEach((item,index)=>{//根据已勾选的选项去按照筛选表去过滤
if(item.length>0 && index==this.describes.indexOf(data)){
//console.log('筛选',item);
this.screenGroup = this.screenGroup.filter((atem,i)=>{
return item.indexOf(atem[index])!=-1;
});
}
})
console.log('screenGroup',this.screenGroup);
//|| alist.indexOf(this.describes[index])!=-1
this.groupList.forEach((item,index)=>{
if(item.label==data ){//当前点击的行不过滤或者当前点击项之前的点击项不过滤
//|| alist.indexOf(this.describes[index])!=-1
}else if(item.label!==this.firstRange){
item.abledList = [...new Set(this.getIndexList(this.screenGroup,index))];//去除重复项,获取不可勾选的项
item.checkList = item.checkList.filter(val=>item.abledList.indexOf(val)!=-1);//从已勾选的数据中去除不能勾选的
}else{
item.abledList = item.skuList;
}
})
console.log('过滤后',this.groupList);
//}
console.log('e',e,data);
},
getIndexList(data,index){//获取二维数组每项第几个索引的集合
let list = [];
data.forEach(item=>{
list.push(item[index]);
})
return list
},
minboxChange(e,label){//获取是取消还是勾选
console.log(e,label);
}
},
mounted(){
this.range = this.rangeList[0].value;
this.groupList = this.rangeList[0].attribute;
this.groupList.forEach(item=>{
item.abledList = item.skuList;
})
}
}
</script>
<style lang="less" scoped>
.action-area{
display: flex;
justify-content: space-between;
//font-size: 12px;
padding: 10px;
border-bottom: 1px solid #dee2ed;
.left-area{
/deep/ .el-select{
.el-input__inner{
height: 32px;
line-height: 32px;
}
.el-input__suffix .el-input__icon{
line-height: 32px;
}
}
}
.right-area{
}
}
.bar{
border: 1px solid #dee2ed;
width: 350px;
height: 400px;
white-space: pre-line;
font-size: 12px;
}
/deep/ .el-dialog__header {
background-color: #f2f2f2;
border-bottom: 1px solid #dee2ed;
.el-dialog__title{
font-size: 14px;
}
}
/deep/ .el-dialog__body{
padding: 0;
margin-bottom: 80px;
}
/deep/ .el-dialog__footer{
padding-left:0 ;
padding-right: 0;
}
.dialog-footer{
border-top: 1px solid #dee2ed;
display: block;
padding: 10px;
bttton{
}
}
.select-area{
margin: 0 auto;
.select-wrap{
display: flex;
font-size: 12px;
border-bottom: 1px solid #dee2ed;
padding: 20px 20px 20px 20px;
//align-items: ;
.title{
width: 36px;
line-height: 30px;
}
.select-content{
flex: 1;
line-height: 30px;
overflow: hidden;
/deep/ .el-checkbox-group .el-checkbox{
margin-right: 18px;
}
}
.more{
width: 40px;
line-height: 24px;
height: 24px;
border-radius: 4px;
background-color: #409eff;
color: #fff;
text-align: center;
cursor: pointer;
}
}
}
.condition-area{
display: flex;
flex-wrap: wrap;
border-bottom: 1px solid #dee2ed;
padding: 10px 20px 10px 20px;
.condition-item{
font-size: 12px;
margin-right: 6px;
padding: 2px 16px;
border: 1px solid #409eff;
border-radius: 2px;
color: #409eff;
}
}
.tip-area{
padding: 120px 0;
text-align: center;
}
</style>