vue 移动端 级联选择器支持多选父子关联 、父关联子不关联、还有父子 都不关联。外加一个单选
vue移动端级联选择器
组件
<template>
<div>
<van-action-sheet @click-overlay="sureSubmit" :value="showInfo" :closeable="false" :title="titleInfo">
<transition name="fade">
<div class="container-searchPanel background-color-white">
<div class="wrapper1">
<!-- <form action="/">
<van-search
v-model="value"
show-action
placeholder="请输入搜索关键词"
@clear="clearInfo"
@search="onSearch"
@cancel="close"
>
<template #label>
<van-dropdown-menu>
<van-dropdown-item
v-model="searchListValue"
:options="[
{ text: '查询全部', value: '查询全部' },
{ text: '当页查询', value: '当页查询' }
]"
/>
</van-dropdown-menu>
</template>
<template #left-icon>
<van-icon name="bm-systemSearch" class="icon-size-16 color-text-secondary" @click="onSearch" />
</template>
</van-search>
</form> -->
<!-- 面包屑 -->
<ul class="category-wrapper">
<!-- <span v-if="!isre" class="none">全部</span> -->
<li
v-for="(item, index) in navList"
:key="index"
class="category-item"
@click="navListClick(item, index, navList)"
>
<van-icon v-if="index > 0" name="arrow" />
<span v-if="index == navList.length - 1">
{{ item.name }}
</span>
<span class="active" v-else>
{{ item.name }}
</span>
</li>
<!-- <text v-else class="active">全部</text> -->
</ul>
<!-- 数据展示 -->
<div class="infoShow" v-if="data.length > 0">
<div class="item-p" v-for="(item, index) in data" :key="index">
<van-cell
:title="item.name"
:is-link="item.childs && item.childs.length > 0"
@click="checkedMethods(item, index)"
>
<template #title>
<span style="" class="select_group">
<van-icon v-if="item.checked" name="arrow" class="van-icon-bm-zhengque" />
</span>
{{ item.name }}
</template>
<template #right-icon>
<van-icon
@click.stop="clickChild(item, index, data)"
v-if="item.childs && item.childs.length > 0"
name="arrow"
class="van-icon-bm-xiaji_line iconSize"
/>
</template>
</van-cell>
</div>
<div>
<van-button round class="vanbtn" type="primary" @click="sureSubmit" block>确认</van-button>
</div>
<!-- <div style="height:100px"></div> -->
</div>
<div class="Nomore" v-else>
<van-empty description="暂无更多" />
</div>
</div>
</div>
</transition>
</van-action-sheet>
</div>
</template>
<script>
export default {
name: 'ProdTreeNew',
data() {
return {
searchListValue: '查询全部',
navList: [],
data: [],
value: '',
selectInfo: [],
fatherCheckout: false,
dataEcho: [],
// 承载数据
newData: []
}
},
props: {
titleInfo: {
type: String,
default: '产品树'
},
// 接口
methodsFunc: {
type: Function,
required: true
},
showInfo: {
type: Boolean,
default: false
},
showEnable: {
// 是否显示第三层禁用项
type: Boolean,
default: false
},
isAdd: {
// 是否是新增页面 新增页面不展示禁用项
type: Boolean,
default: false
},
// 隐藏最后一层
hidePart: {
type: Boolean,
default: false
},
// 只显示三层
isThree: {
type: Boolean,
default: false
},
infoValue: {
type: Array,
default: () => []
},
/**
//
* 多选
父子关联 0
只关联父(勾选父级显示), 子不关联 1 但如果子集 有值 ,父级取消选中 那么对应的父级下面子集 全部取消选中
父子完全不关联 2
* **/
isNoMultiplechoice: {
type: String,
default: '0'
},
// 只关联父(勾选父级显示), 子不关联 3 但如果子集 有值 ,父级取消选中 那么对应的父级下面子集 不会取消选中
SubsetnotSelected: {
type: Boolean,
default: false
},
// 开启单选
openRadioselection: {
type: Boolean,
default: false
}
},
created() {
this.productTree()
},
watch: {
// 由于 值都是一样的
infoValue: {
handler(newValue, oldValue) {
this.$nextTick(() => {
if (newValue && newValue.length > 0) {
if (!this.openRadioselection) {
if (this.isNoMultiplechoice === '0' || this.isNoMultiplechoice === '1') {
this.dataEcho = [
...new Set([
...this.flatten(
newValue.map(item => {
return [item.oneId, item.secondId, item.threeId, item.fourId]
})
)
])
].map(ite => {
this.breadthQuery(this.navList[0].data, ite)
})
} else if (this.isNoMultiplechoice === '2') {
newValue.map(ite => {
console.log(ite, 'ite')
this.breadthQuery(this.navList[0].data, ite.id || ite.oneId)
})
}
// 打印输出
} else {
// 多选
newValue.map(ite => {
console.log(ite, 'ite')
this.breadthQuery(this.navList[0].data, ite.id || ite.oneId)
})
}
}
console.log(newValue)
})
},
deep: true,
immediate: true
}
},
methods: {
// 通过id 遍历 查询匹配的值 并且返回对象
breadthQuery(tree, id) {
var stark = []
stark = stark.concat(tree)
while (stark.length) {
var temp = stark.shift()
if (temp.childs) {
stark = stark.concat(temp.childs)
}
if (temp.id === id) {
temp.checked = true
return temp
}
}
},
// 二维转换一维数组
flatten(arr) {
return [].concat(...arr.map(x => (Array.isArray(x) ? this.flatten(x) : x)))
},
// 接口 ----------------------处理格式
productTree() {
this.methodsFunc().then(res => {
this.fnItemTreeArea(res.data)
})
},
fnItemTreeArea(data) {
let list = this.fn(data)
if (this.isAdd) {
list = this.fn1(list)
}
if (this.showEnable) {
list = this.filter1(list)
} else {
list = this.filter(list)
}
// list 是最终的数据源 上面写了那么多 是因为 我们业务要过滤各种情况下的数据(如果你们业务没有可以删除)
//list 的数据结构 就是 tree 大家可以直接套
this.data = list
this.navList = [{ name: '全部', value: '1', data: this.data }]
},
fn(arr = []) {
return arr.map(item => {
item.disabled = item.node.isDelete === '1'
item.checked = false
if (item.childs && item.childs.length) {
item.childs = this.fn(item.childs)
}
return {
...item
}
})
},
fn1(arr = []) {
// 判断改归属地是否禁用,禁用了不展示
return arr.filter(item => item.node.enable === '1')
},
filter1(arr = []) {
return arr.filter(item => {
// 用于判断 最底层是否被禁用了 如果禁用了 就不显示
if (item.node.type !== '4') {
return item.childs && item.childs.length
}
return true
})
},
filter(arr = []) {
return arr.filter(item => {
if (!this.hidePart && !this.isThree) {
if (!item.disabled && item.childs && item.childs.length) {
item.childs = this.filter(item.childs)
}
}
// 用于判断 最底层是否被禁用了 如果禁用了 就不显示
if (item.node.type !== '4') {
return !item.disabled && item.childs && item.childs.length
} else {
return !item.disabled
}
})
},
// 接口 ----------------------处理格式
// --------------提交 方法
sureSubmit() {
if (!this.openRadioselection) {
// 过滤树形结构 checkout 为true的
if (this.isNoMultiplechoice === '0') {
const a = this.filterMenuList(this.navList[0].data)
const b = this.traverse(a).map((item, index) => {
const obj = {
oneId: item[0].id,
oneContent: item[0].name,
secondId: item[1].id,
secondName: item[1].name,
threeId: item[2].id,
threeName: item[2].name,
fourId: item[3].id,
fourName: item[3].name
}
console.log(obj)
return {
...obj
}
})
this.navList.splice(1)
this.data = this.navList[0].data
this.$emit('sureSubmit', b) // 提交数据
// this.fn(this.navList[0].data) // 将数据变成false
// this.close()
} else if (this.isNoMultiplechoice === '1') {
const copyData = JSON.parse(JSON.stringify(this.navList[0].data))
this.newData = []
this.filterCheckoutTrue(copyData)
const listSigle = this.newData
.map(item => {
const aa = this.findIdList(this.navList[0].data, item.id, item)
console.log(aa, 'aa')
return aa
})
.map(ite => {
const obj = {
oneId: ite[0].infoItem.id,
oneContent: ite[0].infoItem.name,
secondId: ite[1] ? ite[1].infoItem.id : '',
secondName: ite[1] ? ite[1].infoItem.name : '',
threeId: ite[2] ? ite[2].infoItem.id : '',
threeName: ite[2] ? ite[2].infoItem.name : '',
fourId: ite[3] ? ite[3].infoItem.id : '',
fourName: ite[3] ? ite[3].infoItem.name : ''
}
this.preProcessData(obj)
return {
...obj
}
})
this.navList.splice(1)
this.data = this.navList[0].data
this.$emit('sureSubmit', listSigle) // 提交数据
} else if (this.isNoMultiplechoice === '2') {
const copyData = JSON.parse(JSON.stringify(this.navList[0].data))
this.newData = []
this.filterCheckoutTrue(copyData)
const listSigle = this.newData
.map(item => {
const aa = this.findIdList(this.navList[0].data, item.id, item)
console.log(aa, 'aa')
return aa
})
.map(ite => {
const obj = {
oneId: ite[0].infoItem.id,
oneContent: ite[0].infoItem.name,
secondId: ite[1] ? ite[1].infoItem.id : '',
secondName: ite[1] ? ite[1].infoItem.name : '',
threeId: ite[2] ? ite[2].infoItem.id : '',
threeName: ite[2] ? ite[2].infoItem.name : '',
fourId: ite[3] ? ite[3].infoItem.id : '',
fourName: ite[3] ? ite[3].infoItem.name : ''
}
this.preProcessData(obj)
return {
...obj
}
})
this.navList.splice(1)
this.data = this.navList[0].data
this.$emit(
'sureSubmit',
this.newData.map(ite => {
return {
...ite,
oneContent: ite.name,
oneId: ite.id
}
}), // 当前自己勾选的数据
listSigle // 含有勾选的父级数据
) // 提交数据
}
// this.fn(this.navList[0].data) // 将数据变成false
// this.close()
} else {
// 单选
const copyData = JSON.parse(JSON.stringify(this.navList[0].data))
this.newData = []
this.filterCheckoutTrue(copyData)
const listSigle = this.newData
.map(item => {
const aa = this.findIdList(this.navList[0].data, item.id, item)
console.log(aa, 'aa')
return aa
})
.map(ite => {
const obj = {
oneId: ite[0].infoItem.id,
oneContent: ite[0].infoItem.name,
secondId: ite[1] ? ite[1].infoItem.id : '',
secondName: ite[1] ? ite[1].infoItem.name : '',
threeId: ite[2] ? ite[2].infoItem.id : '',
threeName: ite[2] ? ite[2].infoItem.name : '',
fourId: ite[3] ? ite[3].infoItem.id : '',
fourName: ite[3] ? ite[3].infoItem.name : ''
}
this.preProcessData(obj)
return {
...obj
}
})
this.navList.splice(1)
this.data = this.navList[0].data
this.$emit(
'sureSubmit',
this.newData.map(ite => {
return {
...ite,
oneContent: ite.name,
oneId: ite.id
}
}), // 当前自己勾选的数据
listSigle // 含有勾选的父级数据
) // 提交数据
}
this.fn(this.navList[0].data) // 将数据变成false
this.close()
},
// 判断对象里面有 'undefined' null ''
isEmpty(obj) {
if (typeof obj === 'undefined' || obj === null || obj === '') return true
return false
},
preProcessData(formData) {
Object.keys(formData).forEach(item => {
if (this.isEmpty(formData[item])) {
delete formData[item]
}
})
return formData
},
// 递归查询为true 的
filterCheckoutTrue(data) {
data.forEach(item => {
if (item.childs) {
this.filterCheckoutTrue(item.childs)
delete item.childs
if (item.checked === true) {
this.newData.push(item)
}
} else {
if (item.checked === true) {
this.newData.push(item)
}
}
})
},
// ------------------
// 将tree 转换二维数组
traverse(tree, path = [], result = []) {
if (!tree) return []
for (const data of tree) {
path.push(data)
const isLeaf = !data.childs || !data.childs.length
isLeaf ? result.push([...path]) : this.traverse(data.childs, path, result)
path.pop()
}
return result
},
// -----------------
// 处理格式
filterMenuList(list) {
const copyList = JSON.parse(JSON.stringify(list))
const filterList = list => {
if (list instanceof Array && list.length > 0) {
const temp = list.filter(item => item.checked === true)
temp.forEach(e => {
if (e.childs) {
e.childs = filterList(e.childs)
}
})
return temp
}
}
return filterList(copyList)
},
clearInfo() {},
onSearch() {},
close() {
this.$emit('closepop') // 通知父组件改变。
},
navListClick(item, index) {
if (index === this.navList.length - 1) {
return
}
if (item.name === '全部') {
this.navList.splice(1)
this.data = this.navList[0].data
} else {
// 点击某一层级树
if (this.navList.length - 1 > index) {
this.navList.splice(index + 1)
}
this.data = this.navList[index].data
}
},
checkedMethods(item) {
if (!this.openRadioselection) {
if (this.isNoMultiplechoice === '0') {
if (item.node.type === '1') {
// 如果选择第一级
item.checked = !item.checked
this.fatherCheckout = item.checked
this.sonInfo(item.childs)
} else {
this.findIdList(this.navList[0].data, item.id, item)
this.fatherCheckout = item.checked
if (item.childs) {
this.sonInfo(item.childs)
}
}
} else if (this.isNoMultiplechoice === '1') {
this.findIdList(this.navList[0].data, item.id, item)
if (item.childs && !this.SubsetnotSelected) {
this.sonInfo(item.childs)
}
// this.fatherCheckout = item.checked
} else if (this.isNoMultiplechoice === '2') {
item.checked = !item.checked
// this.fatherCheckout = item.checked
}
} else {
this.breadthQuerySelect(this.navList[0].data, item.id)
this.sureSubmit()
}
},
// 通过id 遍历 查询匹配的值 并且返回对象
breadthQuerySelect(tree, id) {
var stark = []
stark = stark.concat(tree)
while (stark.length) {
var temp = stark.shift()
if (temp.childs) {
stark = stark.concat(temp.childs)
}
if (temp.id === id) {
temp.checked = true
} else {
temp.checked = false
}
}
},
sonInfo(arr) {
return arr.map(item => {
if (item.node) {
item.disabled = item.node.isDelete === '1'
if (this.fatherCheckout === false) {
item.checked = false
} else {
item.checked = !item.checked
}
}
if (item.childs && item.childs.length > 0) {
item.childs = this.sonInfo(item.childs)
} else {
item.childs = []
}
return {
...item
}
})
},
clickChild(item) {
if (!item.childs || item.childs.length === 0) {
// Toast('暂无更多')
return
}
this.data = item.childs
this.navList.push({ name: item.name, value: item.id, data: item.childs })
},
// 获取 父级
findIdList(data2, id, itemInfo, children = 'childs', level = 0) {
var arrRes = []
const obj = {
id: 0,
[children]: data2
}
const rev = (data, id, level) => {
if (!data || !data[children] || !data[children].length) {
return
}
for (var i = 0; i < data[children].length; i++) {
const item = data[children][i]
if (item.id === id) {
arrRes.unshift({ level, activeId: item.id, infoItem: item })
rev(obj, data.id, 0)
break
} else if (item[children] && item[children].length > 0) {
// 如果有子集,则把子集作为参数重新执行本方法
rev(item, id, level + 1)
}
}
}
rev(obj, id, level)
itemInfo.checked = !itemInfo.checked
const a = this.navList[this.navList.length - 1].data.some(it => it.checked)
arrRes.slice(0, arrRes.length - 1).forEach(ite => {
if (a) {
ite.infoItem.checked = true
} else {
ite.infoItem.checked = false
}
})
return arrRes
}
}
}
</script>
<style lang="scss" scoped>
.container-searchPanel {
width: 100%;
height: 100%;
// margin-top: 200px;
-webkit-overflow-scrolling: touch;
word-break: break-all;
.wrapper {
height: 100%;
width: 100%;
}
.category-wrapper {
height: 50px;
width: 100%;
padding-left: 10px;
background-color: rgb(237, 239, 247);
white-space: nowrap;
overflow-y: hidden;
overflow-x: auto;
&::-webkit-scrollbar {
display: none;
}
.category-item {
display: inline-block;
font-size: 14px;
height: 30px;
padding: 0 14px;
background-color: #fff;
border-radius: 30px;
line-height: 30px;
margin: 10px 8px 0 0;
min-width: 66px;
text-align: center;
color: #666666;
&.active {
color: #4297ed;
}
}
}
}
.background-nav {
width: 100%;
background: rgb(247, 247, 247);
// height: 40px;
// white-space: nowrap;
// background: yellow;
// overflow: scroll;
// white-space: nowrap;
.inline-item {
// display: block;
line-height: 40px;
white-space: nowrap;
// background: yellow;
overflow: scroll;
// -webkit-overflow-scrolling: touch; //解决ios滑动慢的问题
padding-left: 12px;
// background: red;
// display: inline-block;
// 导航栏项-无状态
.none {
color: #666666;
display: inline-block;
}
}
}
.infoShow {
height: calc(100vh - 150px);
overflow-x: hidden;
overflow-y: scroll;
.item-p {
border-bottom: 1px solid #ccc;
.van-cell {
padding-left: 5px;
}
}
.vanbtn {
position: fixed;
bottom: 4px;
}
}
// .infoShow::-webkit-scrollbar {
// display: none;
// }
// 导航栏项-启用状态
.active {
color: #4297ed !important;
}
// / 导航栏项样式
// .inline-item {
// }
.iconSize {
font-size: 24px;
}
.Nomore {
text-align: center;
margin-top: 20px;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.select_group {
display: inline-block;
width: 10px;
margin-right: 10px;
color: #ed3458;
// margin: 12px 0px;
}
::v-deep .van-dropdown-menu__bar {
height: 33px;
background-color: #edeff7;
box-shadow: none;
}
::v-deep .van-dropdown-menu__title {
padding-left: 0px;
// margin-right: 10px;
}
::v-deep .van-search .van-cell {
padding-left: 13px;
}
.van-cell__title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
父组件使用
<!-- Created by Tanking. -->
<!-- 事故事件 -->
<template>
<div class="container container-supports">
<div v-for="(item, index) in data" :key="index">{{ item }}</div>
<span class="title" @click="aaShow">事故调查</span>
<prodTreeNew
isAdd
:showInfo="show"
isNoMultiplechoice="0"
@closepop="show = false"
:methodsFunc="productTree"
:infoValue="dataCopy"
@sureSubmit="sureSubmit"
></prodTreeNew>
</div>
</div>
</template>
<script>
// import { productTree } from '@/api/plan'
import { chooseAreaInfo } from '@/api/safetyCheck'
export default {
name: 'EventManagement',
data() {
return {
show: false,
data: [], //页面回显
dataCopy: [], //数据回显
dataCopy1: [] //数据回显
}
},
computed: {
productTree() {
return chooseAreaInfo
}
},
methods: {
aaShow(info) {
this.show = true
},
sureSubmit(info, infoList) {
console.log(info)
console.log(infoList)
this.dataCopy = info
this.data = info.map(ite => {
let a = [ite.oneContent, ite.secondName, ite.threeName, ite.fourName].filter(item => item)
return a.join('-')
})
}
}
}
</script>
更多推荐
所有评论(0)