调用组件:
<template>
<div class="flex">
<div
v-for="(box, idx) in resource"
:key="idx"
class="c-select-item">
<select-box
v-model="box.current"
:data="box.data"
:level="box.level"
:title="box.title"
@on-child="pushChild"/>
</div>
</div>
</template>
<script>
import SelectBox from './select-box.vue'
export default {
components: {
SelectBox
},
data () {
return {
resource: [{
current: '',
data: [{ // 数据格式
id: 1,
title: 'APP',
check: false,
children: [{
title: '系统安全',
check: false,
id: 2
}]
}],
level: 1,
title: '省份'
}]
}
},
methods: {
pushChild (params) {
const { item, level } = params
const len = this.resource.length
if (level <= len - 1) {
this.resource.splice(level, len - level)
}
this.resource.push({
data: item.children,
current: '',
level: level + 1,
title: '全选'
})
this.resource[level - 1].current = item.id
}
}
}
</script>
<style scoped>
.c-select-item {
background-color: #fff;
border: solid 1px #D3D5E0;
border-radius: 3px;
}
.flex {
display: flex;
}
</style>
子组件:select-box.vue
<template>
<div class="c-select-box">
<div class="c-check-all flex active-switch">
<span class="is-active active-text">全</span>
<el-switch
v-model="all"
@change="selectAll"
class="switch-item"
/>
<div style="width: 120px">{{ title }}</div>
</div>
<div class="c-select-content">
<div
v-for="(item, i) in data"
:key="item.Id">
<div
v-if="item.children && item.children.length"
@click="clickCheckItem(item, i)"
class="c-check-item flex c-check-item-flex"
:class="[index == i ? 'c-check-item-active' : '']"
>
<div class="active-switch flex">
<span class="el-icon-check is-active" />
<el-switch
v-model="item.check"
class="switch-item"
@change="selectItem($event, item)"
/>
<div style="width: 120px">{{ item.title }}</div>
</div>
<i class="el-icon-arrow-right" />
</div>
<div
v-else
class="c-check-item flex">
<div class="active-switch flex">
<span class="el-icon-check is-active" />
<el-switch
v-model="item.check"
class="switch-item"
@change="selectItem($event, item)"
/>
<div style="width: 120px">{{ item.title }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SelectBox',
props: {
data: {
type: Array,
default: () => []
},
level: {
type: Number,
default: 0
},
title: {
type: String,
default: ''
}
},
computed: {
all () {
const len = this.data.filter((ret) => ret.check).length
return this.data.length === len
}
},
data () {
return {
index: -1,
result: []
}
},
methods: {
selectAll (e) {
this.data.forEach((item) => (item.check = e))
this.checkAllChild(this.data, this.all)
},
selectItem (e, item) {
// 选中某一个时,子集是跟当前同一状态,子集有一个不选时父级状态是不选
this.checkChild(e, item)
},
checkChild (e, list) {
if (list.children && list.children.length) {
list.children.forEach((child) => {
child.check = e
this.checkChild(e, child)
})
}
},
checkAllChild (list, check) {
// 递归把每一项都改变状态为全选的状态
list.forEach((item) => {
if (item.children && item.children.length) {
item.children.forEach((child) => {
child.check = check
this.checkAllChild(item.children, check)
})
}
})
},
itemIndeterminate (child) {
const hasChild = (meta) => {
return meta.children.reduce((sum, item) => {
let foundChilds = []
if (item.check) sum.push(item)
if (item.children) foundChilds = hasChild(item)
return sum.concat(foundChilds)
}, [])
}
const some = hasChild(child).length > 0
const every = child.children && child.children.every((ret) => ret.check)
return some && !every
},
computeChild (list, Vue) {
list.forEach((item) => {
if (item.children && item.children.length) {
const child = item.children
if (child.every((ret) => ret.check)) {
item.check = true
} else {
item.check = false
}
this.computeChild(child, Vue)
}
})
},
clickCheckItem (item, i) {
this.index = i
this.$emit('on-child', { item, level: this.level })
}
},
watch: {
data: {
handler (nVal) {
this.computeChild(nVal, this)
},
deep: true
}
},
mounted () {
this.computeChild(this.data, this)
}
}
</script>
<style lang="scss" scoped>
// @import "~assets/styles/mixin.styl"
.c-cataract {
display: block;
position: absolute;
top: 0;
left: 0;
z-index: 8;
cursor: pointer;
}
.c-select-content {
height: 166px;
overflow: auto;
}
.c-check-all {
height: 26px;
position: relative;
z-index: 9;
background-color: #f5f6fa;
padding-left: 10px;
border-bottom: 1px solid #d3d5e0;
font-weight: 700;
color: #0c1733;
.c-item-select {
width: 100%;
height: 100%;
}
}
.c-check-item-active {
background: #dce5ff;
.el-icon-arrow-right {
color: #4371ff;
}
}
.c-check-item {
margin: 0;
padding: 0 10px;
position: relative;
height: 36px;
line-height: 36px;
&:hover {
background-color: #dce5ff;
}
&.active {
.c-check-arrow {
color: #598fe6 !important;
}
}
.c-check-arrow {
float: right;
margin-top: 10px;
}
.c-item-checkbox {
width: 36px;
height: 36px;
}
}
.c-check-item-flex {
justify-content: space-between;
}
.switch-item {
padding-right: 8px;
}
.active-switch {
position: relative;
.is-active {
position: absolute;
top: 12px;
left: 2px;
z-index: 1;
color: #fff;
}
.active-text {
font-size: 12px;
line-height: 12px;
top: 8px;
left: 14px;
}
}
.flex {
display: flex;
align-items: center;
}
</style>