前段时间项目里有一个树状单位结构的需求,第一反应就是用elementui里的cascader 级联选择器来做。
本来以为是通过接口直接获取到所有单位树后配置一下就可以了,看了需求文档后发现原来是要通过动态加载的方式来获取。
需求:
1.进入页面之后根据当前的单位Id调用第一个接口来获取第一级的单位,并默认展示。
2.如果当前单位有下级单位则显示箭头,点击调用第二个接口获取下级单位。
很快啊,看着elementui官方文档,写了一个简单的demo,如下:
<template>
<div class="main">
<el-cascader :props="props"></el-cascader>
</div>
</template>
<script>
export default {
data(){
return {
placeholder:"",
defaultName:"",
props:{
label:'text',
value:'id',
lazy: true,
lazyLoad:(node, resolve) =>{
if(node.level == 0 ){
this.queryTree(node, resolve)
}else {
this.queryMoreTree(node, resolve)
}
},
}
}
},
methods:{
//获取第一级单位
queryTree(node, resolve){
setTimeout(() => {
let res = [{id:"1",text:"北京",hasChildren:true}];
res.forEach(item => {
return {
value:item.id,
label:item.text,
leaf:item.leaf
}
})
console.log(res)
resolve(res)
}, 500);
},
//获取下级单位
queryMoreTree(node, resolve){
setTimeout(() => {
let res = [
{id:"1-1",text:"昌平区",hasChildren:true},
{id:"1-2",text:"东城区",hasChildren:true},
{id:"1-3",text:"朝阳区",hasChildren:true},
{id:"1-4",text:"沙河",hasChildren:false}
]
res.forEach(item => {
item.leaf = !item.hasChildren;
return {
value:item.id,
label:item.text,
leaf:!item.hasChildren
}
})
console.log(res)
resolve(res)
}, 500);
}
}
}
</script>
因为只是个demo,所以我用了settimeout来模拟异步获取数据。
我们返回的数据中有一个hasChildren的字段,为true时表示这个单位下是有子单位的。然后在这个地方我被折磨了好久!
在return的对象中,leaf是判断当前节点是否有下级的字段,当leaf为false时是有下级,为true时没有下级。leaf也就是控制每个节点是否显示箭头的的属性。
然后我就想当然的给leaf属性赋值为 item.!haschildren,但是!不生效!尝试着直接赋值 true,不生效!
最后没办法了,把官方文档里的例子拷过来,去看看为什么它的就可以控制。
props:{
lazy: true,
lazyLoad (node, resolve) {
const { level } = node;
setTimeout(() => {
const nodes = Array.from({ length: level + 1 })
.map(item => ({
value: ++id,
label: `选项${id}`,
leaf: level >= 2
}));
// 通过调用resolve将子节点数据返回,通知组件数据加载完成
console.log(nodes) //在这里输出查看节点
resolve(nodes);
}, 1000);
}
}
上边是官方案例中的写法,我在输出了nodes后发现
他的对象里是有leaf属性的。
然后我就尝试了一下给我自己拿到数据中添加这个属性:
//获取第一级单位
queryTree(node, resolve){
setTimeout(() => {
let res = [{id:"1",text:"北京",hasChildren:true}];
res.forEach(item => {
item.leaf = !item.hasChildren //给item添加leaf属性
return {
value:item.id,
label:item.text,
leaf:item.leaf
}
})
resolve(res)
}, 500);
},
//获取下级单位
queryMoreTree(node, resolve){
setTimeout(() => {
let res = [
{id:"1-1",text:"昌平区",hasChildren:true},
{id:"1-2",text:"东城区",hasChildren:true},
{id:"1-3",text:"朝阳区",hasChildren:true},
{id:"1-4",text:"沙河",hasChildren:false}
]
res.forEach(item => {
item.leaf = !item.hasChildren //给item添加leaf属性
return {
value:item.id,
label:item.text,
leaf:item.leaf
}
})
resolve(res)
}, 500);
}
成功了!
接下来还有几个问题:
1.假如昌平区下还有数个子单位,但是我现在需要直接选中昌平区。
2.选择完单位后,input框里会显示从北京开始一直到选中单位的所有路径。而我只想显示当前选中单位。
3.选择完单位后需要将选中的单位id覆盖掉当前的单位id,并将级联选择器下拉框隐藏掉。
4.进入页面后,input框里需要默认显示第一级中的第一个单位。
前两个问题其实很好解决,官方文档给出了明确的方法。
第三个问题有一个选中选项后的方法,我们在这里边进行处理。
第四个默认显示的问题,我尝试着用 v-model 给cascader绑定默认值,但是不生效。
百度了好几种方法,但是我没看懂…
最后灵机一动,用了一个取巧的方法。 上完整版代码:
<template>
<div class="main">
<el-cascader
:props="props"
:show-all-levels="false"
:placeholder = "placeholder"
@change="handleChange"
change-on-select
ref="cascader"
>
</el-cascader>
</div>
</template>
<script>
export default {
data(){
return {
placeholder:"",
props:{
label:'text',
value:'id',
lazy: true,
lazyLoad:(node, resolve) =>{
if(node.level == 0 ){
this.queryTree(node, resolve)
}else {
if(!node.leaf){ //当本节点有子单位时才调用接口
this.queryMoreTree(node, resolve)
}
}
}
}
}
},
methods:{
//选中单位之后的操作
handleChange(item) {
this.id = item[item.length - 1];
this.$refs.cascader.dropDownVisible = false
},
//获取第一级单位
queryTree(node, resolve){
//此处要将本单位id作为参数提交
setTimeout(() => {
let res = [{id:"1",text:"北京",hasChildren:true}];
this.placeholder = res[0].text
res.forEach(item => {
item.leaf = !item.hasChildren
return {
value:item.id,
label:item.text,
leaf:item.leaf
}
})
console.log(res)
resolve(res)
}, 500);
},
//获取下级单位
queryMoreTree(node, resolve){
//注意此处要将node.value,也就是点击的节点单位的id,作为查询被点击单位下级单位的参数提交
setTimeout(() => {
let res = [
{id:"1-1",text:"昌平区",hasChildren:true},
{id:"1-2",text:"东城区",hasChildren:true},
{id:"1-3",text:"朝阳区",hasChildren:true},
{id:"1-4",text:"沙河",hasChildren:false}
]
res.forEach(item => {
item.leaf = !item.hasChildren;
return {
value:item.id,
label:item.text,
leaf:item.leaf
}
})
resolve(res)
}, 500);
}
}
}
</script>
<style lang = "less" scoped>
::placeholder{
color:rgb(69, 68, 102)
}
</style>
show-all-levels = ‘false’ 只显示当前选中单位。
change-on-select 属性可以让你选择每个节点的单位。
@change是在选中某节点后触发,在这里我们将当前选定单位的id覆盖原单位id。并且给级联选择器一个ref值,在选中某单位后将选择器下拉框隐藏。
最后我给级联选择器动态绑定了一个placeholder占位符,将第一次获取到的数据中首个单位的名字赋值给它。然后在css中将占位符的字体颜色设置为选中后的颜色。
效果图: