简介:vue+elementui自定义无限层级条件配置。如图:
- 基础组件做无限递归(item.vue)
<div class="custom-menu">
<template v-for="(item, index) in treeData">
<div v-if="item.type === 'CD'" class="node" :class="treeData[index+1] ? 'node-style':''" :key="index" style="marginLeft:20px;marginTop:6px;">
<el-row :gutter="10">
<el-col :span="5">
<el-input v-model="item.field"></el-input>
</el-col>
<el-col :span="5">
<el-select v-model="item.op" v-show="item.field">
<el-option label="或" value="or"></el-option>
<el-option label="并" value="and"></el-option>
</el-select>
</el-col>
<el-col :span="12">
<div style="width:100%;">
<el-input v-model="item.val" v-show="item.field"></el-input>
</div>
</el-col>
<el-col :span="2">
<span style="lineHeight:32px;fontSize:16px;cursor:pointer;">
<i class="el-icon-close" @click="clearn(item.id)"></i>
</span>
</el-col>
</el-row>
</div>
<div v-else :key="index" class="node" :class="treeData[index+1] ? 'node-style':''" style="marginLeft:20px;marginTop:6px;">
<el-row :gutter="20">
<el-col :span="6">
<el-select v-model="item.op">
<el-option label="或" value="or"></el-option>
<el-option label="并" value="and"></el-option>
</el-select>
</el-col>
<el-col :span="18" class="btn-group">
<span @click="add('condit', item.id)">
<i class="el-icon-circle-plus-outline"></i>
添加条件
</span>
<span @click="add('group', item.id)">
<i class="el-icon-circle-plus-outline"></i>
添加组
</span>
<span @click="clearn(item.id)">
<i class="el-icon-delete"></i>
删除组
</span>
</el-col>
</el-row>
<template>
<ItemCompons :treeData='item.children' @addHandle="addHandle" @deleteItem="deleteItem"></ItemCompons>
</template>
</div>
</template>
</div>
</template>
<script>
export default {
name: 'ItemCompons',
props: {
treeData: {
type: Array,
default () {
return []
}
}
},
methods: {
add (type, id) {
this.$emit('addHandle', type, id)
},
clearn (id) {
this.$emit('deleteItem', id)
},
deleteItem (id) {
this.$emit('deleteItem', id)
},
addHandle (type, id) {
this.$emit('addHandle', type, id)
}
},
data () {
return {
}
}
}
</script>
<style lang="less">
.custom-menu{
.el-col{
border:1px solid transparent
}
}
.custom-menu {
.node:after {
border-top: none;
}
.node {
position: relative;
padding-left: 16px;
}
.node:before {
content: "";
left: -1px;
position: absolute;
right: auto;
border-width: 1px;
}
.node:after {
content: "";
left: 1px;
position: absolute;
right: auto;
border-width: 1px;
}
.node:before {
border-left: 1px dashed #bec0c5;
bottom: 0px;
height: 20px;
top: -4px;
width: 1px;
}
.node-style:before {
border-left: 1px dashed #bec0c5;
bottom: 0px;
height: calc(100% + 4px);
top: -4px;
width: 1px;
}
.node:after {
border-top: 1px dashed #bec0c5;
height: 20px;
top: 16px;
width: 15px;
}
}
</style>
- 初始化父级组件(slide.vue)
<div>
<el-row :gutter="20">
<el-col :span="6">
<el-select v-model="resultData.op">
<el-option label="或" value="or"></el-option>
<el-option label="并" value="and"></el-option>
</el-select>
</el-col>
<el-col :span="18" class="btn-group">
<span @click="add('condit')">
<i class="el-icon-circle-plus-outline"></i>
添加条件
</span>
<span @click="add('group')">
<i class="el-icon-circle-plus-outline"></i>
添加组
</span>
<span @click="clearn">
<i class="el-icon-delete"></i>
清空条件
</span>
<span>
<i class="el-icon-warning-outline"></i>
</span>
</el-col>
</el-row>
<ItemCompon :treeData="resultData.children" @addHandle="addHandle" @deleteItem="deleteItem"></ItemCompon>
</div>
</template>
<script>
import ItemCompon from './item'
import { nanoid } from 'nanoid'
export default {
name: 'SlideItem',
components: {
ItemCompon
},
props: {},
methods: {
clearn () {
this.resultData.children = []
},
deleteItem (id) {
const tempData = JSON.parse(JSON.stringify(this.resultData))
this.resultData = this.deepDelete(tempData, id)
},
deepDelete (obj, id) {
obj.children.map((item, index) => {
if (item.id === id) {
console.log(obj)
obj.children.splice(index, 1)
console.log(obj)
}
if (item.children) {
this.deepDelete(item, id)
}
return item
})
return obj
},
addHandle (type, id) {
let obj = {}
if (type === 'condit') {
obj = {
id: nanoid(),
type: 'CD',
field: '',
op: '',
val: '',
children: []
}
} else if (type === 'group') {
obj = {
id: nanoid(),
type: 'GP',
field: '',
op: '',
val: '',
children: [
{
id: nanoid(),
type: 'CD',
field: '',
op: '',
val: '',
children: []
}
]
}
}
this.deepMap(this.resultData, id, obj)
},
deepMap (obj, id, data) {
obj.children.map(item => {
if (item.id === id) {
item.children.push(data)
}
if (item.children) {
this.deepMap(item, id, data)
}
return item
})
},
add (type) {
if (type === 'condit') {
this.resultData.children.push({
id: nanoid(),
type: 'CD',
field: '',
op: '',
val: '',
children: []
})
} else if (type === 'group') {
this.resultData.children.push({
id: nanoid(),
type: 'GP',
field: '',
op: '',
val: '',
children: [
{
id: nanoid(),
type: 'CD',
field: '',
op: '',
val: '',
children: []
}
]
})
}
}
},
data () {
return {
resultData: {
op: 'and',
children: []
}
}
}
}
</script>
<style lang="less">
.btn-group{
text-align: right;
span{
font-size: 14px;
line-height: 32px;
padding-right: 15px;
cursor: pointer;
}
}
</style>
- 页面引用
// 引入
import SlideItem from './slide'
// 注册
components: {
SlideItem
}
// 使用
<SlideItem></SlideItem>