造輪子-省市區(qū)級聯(lián)選擇組件

小知識點:

  1. 如果你的文件是不需要編譯的最后文件名前加一個下劃線(_),比如_var.scss
    2.vue的屬性不能以data開頭妆丘,否則轉成默認屬性
    比如:datasource只能寫成source
    3.如果你在當前組件中使用了與你name相同的標簽,那么name就是你當前的組件

4.我們不知道要渲染的數(shù)據(jù)有多少層,我們該怎么用v-for遍歷车柠?
比如有些區(qū)下面有鎮(zhèn)竹祷,有的沒有塑陵,也就是說無法確定當前數(shù)據(jù)數(shù)組有幾層的情況下令花,那么我們可以通過遞歸組件讓組件(通過組件自己調用自己來實現(xiàn))
小案例

  • index.html
<div id="app" style="padding: 100px;">
    <g-cascader :source="source"></g-cascader>
</div>
<script>
let app = new Vue({
  el: '#app',
  data: {
    source: [
    {
        name: '浙江',
        children: [
            {
                name: '杭州',
                children: [
                    {name: '上城'},
                    {name: '下城'},
                    {name: '江干'}
                ]
            },{
                name: '嘉興',
                children: [
                    {name: '南湖'},
                    {name: '秀洲'},
                    {name: '嘉善'}
                ]
            }
        ]
    },
    {
        name: '福建',
        children: [
            {
                name: '福州',
                children: [
                    {name: '鼓樓'},
                    {name: '臺江'},
                    {name: '蒼山'}
                ]
            }
        ]
    }
]
   }
})
</script>
  • cascader.vue
<template>
    <div class="cascader">
        <div class="popover">
            <div v-for="item in source">
               <cascader-item :sourceItem="item"></cascader-item>
            </div>
        </div>
    </div>
</template>
<script>
import CascaderItem from './cascaderitem.vue'
export default {
    name: 'GuluCascader',
    props: {
        source: {
            type: Array
        }
    },
    components: {CascaderItem}
}
</script>
  • cascaderitem.vue
<template>
    <div class="cascader-item">
        {{sourceItem.name}}
        <gulu-cascader-item v-for="(item,index) in sourceItem.children" 
        v-if="sourceItem.children"
        :sourceItem="item"
        :key="index"   
        >
        </gulu-cascader-item>
    </div>
</template>
<script>
export default {
    name: 'GuluCascaderItem',
    props: {
        sourceItem: {
            type: Object
        }
    }
}
</script>

上面的代碼在index里使用了cascader.vue這個組件,這個組件接受父組件傳進來的source數(shù)組扮碧,
然后遍歷最開始的數(shù)組慎王,拿到數(shù)組里面第一層的每一項宏侍,之后通過遞歸組件cascaderitem.vue接受你拿到的數(shù)組第一層的每一項(對象)谅河,顯示你這每一項里面的name旧蛾,然后調用自己再次進行遍歷判斷你傳進來的sourceItem.children是否存在锨天,如果存在就繼續(xù)遍歷souceItem.children病袄,把拿到的新的對象item傳給souceItem,然后再次顯示這里面每一項的name基公,直到對應的item下面的children不存在為止轰豆。這里要注意遞歸組件自己本身要傳入的屬性酸休,在自己內部也要再寫一遍斑司,就像上面的:sourceItem="item"
以上面的案例為例但汞,一開始在cascader.vue組件里傳入的是最開始的數(shù)據(jù)私蕾,然后遍歷分別拿到的item是

{
    name: '浙江',
    children: [
        {
            name: '杭州',
            children: [
                {name: '上城'},
                {name: '下城'},
                {name: '江干'}
            ]
        },{
            name: '嘉興',
            children: [
                {name: '南湖'},
                {name: '秀洲'},
                {name: '嘉善'}
            ]
        }
    ]
},
{
    name: '福建',
    children: [
        {
            name: '福州',
            children: [
                {name: '鼓樓'},
                {name: '臺江'},
                {name: '蒼山'}
            ]
        }
    ]
}

上面兩個對象,之后分別調用遞歸組件把這兩個對象作為item依次傳進去懊纳,開始執(zhí)行cascaderitem.vue組件里的代碼嗤疯,以第一個浙江的為例茂缚,拿到{{sourceItem.name}}也就是浙江屋谭,然后執(zhí)行下面的代碼

<gulu-cascader-item v-for="(item,index) in sourceItem.children" 
        v-if="sourceItem.children"
        :sourceItem="item"
        :key="index"   
        >
        </gulu-cascader-item>

也就是把當前的組件里的代碼再執(zhí)行一遍桐磁,它先判斷sourceItem.children是否存在我擂,因為sourceItem.children是

{
            name: '杭州',
            children: [
                {name: '上城'},
                {name: '下城'},
                {name: '江干'}
            ]
        }
{
            name: '嘉興',
            children: [
                {name: '南湖'},
                {name: '秀洲'},
                {name: '嘉善'}
            ]
        }

所以再次對上面的對象也就是sourceItem.children再次進行遍歷看峻,得到的item分別為上面的兩個互妓,然后把這兩個賦值給sourceItem,也就是sourceItem分別是杭州和嘉興下面的對象霉猛,然后再分別使用這個組件惜浅,如此循環(huán)坛悉,最終得到下面結構

  1. 屬性不要以show開頭,因為以show開頭的都是函數(shù)
頁面渲染初步實現(xiàn)
  1. 正常情況下在已知有幾層數(shù)據(jù)的情況下的視圖渲染這里以上面的三層為例
<template>
    <div class="cascader">
        <div class="popover" @click="popoverVisibility = !popoverVisibility">
        </div>
        <div class="leave" v-if="popoverVisibility">
            <div class="leave1">
                <div v-for="item in source" @click="leave2 = item">
                    {{item.name}}
                </div>
            </div>
            <div class="leave2" v-for="item2 in selectLeave1"
            @click="leave3 = item2"
            >
                {{item2.name}}
            </div>
            <div class="leave3" v-for="item3 in selectLeave2">
                {{item3.name}}
            </div>

        </div>
    </div>
</template>
<script>
import CascaderItem from './cascaderitem.vue'
export default {
    name: 'GuluCascader',
    props: {
        source: {
            type: Array
        }
    },
    data(){
        return {
            popoverVisibility: false,
            leave2: null,
            leave3: null
        }
    },
    computed: {
        selectLeave1(){
            if(this.leave2){
                return this.leave2.children
            }
        },
        selectLeave2(){
            if(this.leave3){
                return this.leave3.children
            }
        }
    },
    components: {CascaderItem}
}
</script>

上面的代碼一開始通過點擊popover使leave顯示,leave里面分了三層div均践,遍歷第一層摩幔,點擊第一層里對應的內容或衡,比如浙江封断,把與浙江有關的數(shù)據(jù)添加到一個一開始為null的屬性里坡疼,然后通過計算屬性返回它里面的children,之后在第二層里遍歷它祖搓,同樣把當前下面的數(shù)據(jù)賦值給leave3拯欧,然后通過計算屬性返回這個數(shù)據(jù)下的children再次遍歷

  1. 改進:上面1的代碼雖然實現(xiàn)了我們的功能镐作,但是就像我們一開始說的我們根本不知道當前會有幾層數(shù)據(jù)该贾,所以我們還是需要通過遞歸的形式
  • cascaderitem.vue
<template>
    <div class="cascader-item">
        <div class="left">
            <div v-for="item in items" @click="leftSelected = item">
                {{item.name}}
            </div>
        </div>
        <div class="right" v-if="rightItems">
            <gulu-cascader-item :items="rightItems"></gulu-cascader-item>
        </div>
    </div>
</template>
<script>
export default {
    name: 'GuluCascaderItem',
    props: {
        items: {
            type: Array
        }
    },
    data(){
        return {
            leftSelected: null
        }
    },
    computed: {
        rightItems(){
            if(this.leftSelected && this.leftSelected.children){
                return this.leftSelected.children
            }else {
                return null
            }
        }
    }
}
</script>
  • cascader.vue
<template>
    <div class="cascader">
        <div class="popover" @click="popoverVisibility = !popoverVisibility">
        </div>
        <div class="leave" v-if="popoverVisibility">
            <cascader-item :items="source"></cascader-item>
        </div>
    </div>
</template>
<script>
import CascaderItem from './cascaderitem.vue'
export default {
    name: 'GuluCascader',
    props: {
        source: {
            type: Array
        }
    },
    data(){
        return {
            popoverVisibility: false,
        }
    },
    components: {CascaderItem}
}
</script>

上面的代碼就是每次分為左邊區(qū)域跟右邊區(qū)域,然后你下次在你的右邊里再次顯示你的左邊逞力,依次類推寇荧,不斷的在右側調用你這個組件揩抡,也就是不斷的在右側顯示你的左側

讓用戶可以自定義高度

通過給cascader.vue傳入一個height峦嗤,然后在它中的props里聲明這個height寻仗,然后再在cascader.vue中把這個height傳給cascader-items.vue,在這里面設置style為你的height

  • index.html
<g-cascader :source="source" height="200px"></g-cascader>
  • cascader.vue
<div class="cascader">
  <cascader-item :items="source" :style="{height}" :height="height"></cascader-item>
</div>

import CascaderItem from './cascader-items.vue'
 props: {
        source: {
            type: Array
        },
        height: {
            type: String
        }
    },
  • cascader-items.vue
<template>
    <div class="cascader-item">
        <div class="left">
            <div class="label" v-for="item in items" @click="leftSelected = item">
                {{item.name}}
                <icon name="right" v-if="item.children"></icon>
            </div>
        </div>
        <div class="right" v-if="rightItems">
            <gulu-cascader-item :items="rightItems" :style="{height}" :height="height"></gulu-cascader-item>
        </div>
    </div>
</template>
<script>
import Icon from './icon.vue'
export default {
    name: 'GuluCascaderItem',
    props: {
        items: {
            type: Array
        },
        height: {
            type: String
        }
    },
重新點擊第一層的時候后面的第三層的層級隱藏掉

實現(xiàn)方法:通過傳一個selected數(shù)組,和一個level層級硝烂,默認selected是一個空數(shù)組滞谢,level為0狮杨,開始遞歸的時候level的值就等于level+1(也就是右側的層級是在左側的基礎上加1)橄教,然后點擊每一項的時候在selceted數(shù)組里的第level項的值為你點擊的這一項的item(這樣就可以保證每一層級在數(shù)組里只有一個值护蝶,不會每點擊一個添加一個)持灰,并且每次點擊的時候都把這個數(shù)組里的第level+1和之后的項全部刪掉搅方,然后通過子組件觸發(fā)父組件的@update:selected事件把一個新的深拷貝的selected傳給最外的父組件

  • demo.vue
<template>
    <div>
        <div style="padding: 20px;">
            <g-cascader :source="source" height="200px" :selected="selected"
                        @update:selected="selected = $event"
            ></g-cascader>
        </div>
    </div>
</template>
<script>
data(){
            return {
                source: [
                    {
                        name: '浙江',
                        children: [
                            {
                                name: '杭州',
                                children: [
                                    {name: '上城'},
                                    {name: '下城'},
                                    {name: '江干'}
                                ]
                            },{
                                name: '嘉興',
                                children: [
                                    {name: '南湖'},
                                    {name: '秀洲'},
                                    {name: '嘉善'}
                                ]
                            }
                        ]
                    },
                    {
                        name: '福建',
                        children: [
                            {
                                name: '福州',
                                children: [
                                    {name: '鼓樓'},
                                    {name: '臺江'},
                                    {name: '蒼山'}
                                ]
                            }
                        ]
                    }
                ],
                selected: []
            }
        },
</script>
  • cascader.vue
<div class="popover" v-if="popoverVisibility">
            <cascader-item :items="source" :style="{height}" :height="height" :selected="selected" :level="level"
            @update:selected="onUpdateSelected"
            ></cascader-item>

<script>
import CascaderItem from './cascader-items.vue'
props: {
        selected: {
            type: Array,
            default: []
        },
        level: {
            type: Number,
            default: 0
        }
    },
computed: {
        result(){
            return this.selected.map(item=>{return item.name}).join('/')
        }
    },
    components: {CascaderItem},
    methods: {
        onUpdateSelected(val){
            this.$emit('update:selected',val)
        }
    }
</script>
  • cascader-item.vue
<template>
    <div class="cascader-item">
        <div class="left">
            <div class="label" v-for="item in items" @click="onSelected(item)" >
                {{item.name}}
                <icon name="right" v-if="item.children"></icon>
            </div>
        </div>
        <div class="right" v-if="rightItems">
            <gulu-cascader-item :items="rightItems" :style="{height}" :height="height" :selected="selected" :level="level+1"
              @update:selected="onUpdateSelected"
            ></gulu-cascader-item>
        </div>
    </div>
</template>
<script>
import Icon from './icon.vue'
export default {
    name: 'GuluCascaderItem',
    props: {
        items: {
            type: Array
        },
        height: {
            type: String
        },
        selected: {
            type: Array,
            default: ()=>{return []}
        },
        level: {
            type: Number,
            default: 0
        }
    },
    data(){
        return {

        }
    },
    computed: {
        rightItems(){
            let currentSelected= this.selected[this.level]
            if(currentSelected && currentSelected.children){
                return currentSelected.children
            }else {
                return null
            }
        }
    },
    components: {
        Icon
    },
    methods: {
        onSelected(item){
            let copy = JSON.parse(JSON.stringify(this.selected))
            //之所以寫copy[this.level]是為了你點擊當前層的每一個都讓數(shù)組里只保留一個
            //而不是點一個就往數(shù)組里加一個涛漂,如果不寫的話你點杭州數(shù)組里有一個杭州匈仗,再點福建
            //數(shù)組就會變成['杭州','福建']可這兩個屬于同一層悠轩,我們統(tǒng)一層只想保留一個
            copy[this.level]= item
            copy.splice(this.level+1)
            this.$emit('update:selected',copy)
        },
        onUpdateSelected(val){
            this.$emit('update:selected',val)
        }
    }
}
實現(xiàn)動態(tài)數(shù)據(jù)層級選擇

首先從github上引入一個數(shù)據(jù)庫https://github.com/eduosi/district/blob/master/district-full.csv火架,然后把它通過JSON轉化工具轉化成JSON格式何鸡,創(chuàng)建一個本地db.js骡男,通過封裝一個promise實現(xiàn)傳入一個id獲取到相應的子級,然后通過外層傳入一個loadData函數(shù)拾稳,通過loadData獲取到你點擊的item熊赖,然后拿到對應的id震鹉,調用你的promise传趾,成功后通過一個回調渲染我們的頁面

  • demo.vue
<template>
    <div>
        <div style="padding: 20px;">
            <g-cascader :source.sync="source" height="200px" :selected.sync="selected"
             :loadData="loadData"
            ></g-cascader>
        </div>
    </div>
</template>
data(){
            return {
                source: [

                ],
                selected: [],
            }
        },
        methods: {
            loadData(node,fn){
                let {id}=node
                this.ajax(id).then((result)=>{
                    fn(result)
                })
            },
            ajax(id=0){
                return new Promise((resolve,reject)=>{
                    let result = db.filter(item=>item.parent_id === id)
                    result.map(node=>{
                        //如果數(shù)據(jù)庫里有對應的對象的id等于當前節(jié)點的id浆兰,說明當前節(jié)點有children
                        if(db.filter(item=>item.parent_id === node.id).length > 0){
                            node.isLeaf = false
                        }else{
                            node.isLeaf = true
                        }

                    })
                    setTimeout(()=>{
                        resolve(result)
                    },300)
                })
            },
        },
        created() {

            this.ajax().then((result)=>{
                this.source = result
            })
        }
    }
  • cascader.vue
<div class="popover" v-if="popoverVisibility">
            <cascader-item :items="source" :style="{height}" :height="height" :selected="selected" :level="level"
<!--一直把這個loadData傳給cascader-item-->
           @update:selected="onUpdateSelected" :loadData="loadData"
            ></cascader-item>
        </div>

props: {
        loadData: {
            type: Function
        }
    },
    computed: {
        result(){
            return this.selected.map(item=>{return item.name}).join('/')
        }
    },
    components: {CascaderItem},
    methods: {
        onUpdateSelected(val){
            this.$emit('update:selected',val)
            //因為你每次點擊后都會把當前這個后面的都刪掉,所以當前這個就是數(shù)組的最后一個也就是val[val.length-1]
            let lastVal = val[val.length-1]
            //當前的數(shù)據(jù)如果是第一層的話直接可以通過item.id===id拿到蜕便,如果是第二層就會是一個二維數(shù)組轿腺,所以你需要分別針對有沒有children設置不同的函數(shù)獲取它們對應的值
            let simplest = (children,id)=>{
                return children.filter(item=>item.id === id)[0]
            }
            let complex = (children,id)=>{
                let noChildren = []
                let hasChildren = []
                children.forEach(item=>{
                    if(item.children){
                        hasChildren.push(item)
                    }else{
                        noChildren.push(item)
                    }
                })
              //沒有children的只需要使用simplest就可以拿到當前的
                let found = simplest(noChildren,id)
                if(found){
                    return found
                }else{
                    // 如果是有children我們先把它當做沒children的找一遍,然后再對它里面
                    // 的children的每一項使用complex方法找一遍
                    found = simplest(hasChildren, id)
                    if(found){
                        return found
                    }else{
                        for(let i = 0;i<hasChildren.length;i++){
                            found = complex(hasChildren[i].children,id)
                            if(found){
                                return found
                            }
                        }
                        return undefined
                    }
                }
            }
            let updateSource = (result)=>{
                //source是props所以需要深拷貝
                let copy = JSON.parse(JSON.stringify(this.source))
              //拿到你點擊元素的數(shù)據(jù)
                let toUpdate = complex(copy,lastVal.id)
                //給當前元素下面添加children值為你回調中獲取的當前點擊元素下的子元素
                toUpdate.children = result
                //觸發(fā)一個update:source將拷貝后的source傳出去
                this.$emit('update:source',copy)
            }
          //如果最后一個的isLeaf是false并且this.loadData存在就去獲取數(shù)據(jù)
            if(!lastVal.isLeaf && this.loadData){
            
  //將你點擊的元素和updateSource這個回調傳進去
             this.loadData(lastVal,updateSource)
            }
        }
    }
  • cascader-items.vue
<template>
    <div class="cascader-item">
        <div class="left">
            <div class="label" v-for="item in items" @click="onSelected(item)" >
                <span class="name">{{item.name}}</span>
                <icon name="right" v-if="rightArrowVisible(item)"></icon>
            </div>
        </div>
        <div class="right" v-if="rightItems">
            <gulu-cascader-item :items="rightItems" :style="{height}" :height="height" :selected="selected" :level="level+1"
              @update:selected="onUpdateSelected" :loadData="loadData"
            ></gulu-cascader-item>
        </div>
    </div>
</template>
props: {
        items: {
            type: Array
        },
        height: {
            type: String
        },
        selected: {
            type: Array,
            default: ()=>{return []}
        },
        level: {
            type: Number,
            default: 0
        },
        loadData: {
            type: Function
        }
    },
computed: {
        rightItems(){
            if(this.selected[this.level]){
                let selected = this.items.filter((item)=>item.name === this.selected[this.level].name)
                if(selected && selected[0].children&&selected[0].children.length > 0){
                    return selected[0].children
                }
            }
        }
    },
    components: {
        Icon
    },
methods: {
rightArrowVisible(item){
//如果this.loadData存在的話也就是用動態(tài)數(shù)據(jù)坏平,那么就是!item.isLeaf的時候顯示箭頭功茴,否則就是item下有children顯示
            return this.loadData ? !item.isLeaf : item.children
        }
}
點擊外側關閉顯示層

方法1:點擊的時候添加一個docuemnt事件監(jiān)聽坎穿,因為會有冒泡玲昧,所以事件監(jiān)聽需要在this.$nextTick下寫,然后給這個事件監(jiān)聽的函數(shù)傳一個原生參數(shù)吕漂,拿到e.target根據(jù)它判斷屬不屬于cascader里面惶凝,如果屬于就什么都不干苍鲜,否則就關閉

<template>
    <div class="cascader" ref="a" >
        <div class="trigger" @click="toggle">
            {{result || '&nbsp;'}}
        </div>
        <div class="popover" v-if="popoverVisibility">
            <cascader-item :items="source" :style="{height}" :height="height" :selected="selected" :level="level"
            @update:selected="onUpdateSelected" :loadData="loadData"
            ></cascader-item>
        </div>
    </div>
</template>
documentClik(e){
            let cascader = this.$refs.a
            if(cascader.contains(e.target)){
                return
            }else{
                this.close()
            }
        },
        close(){
            this.popoverVisibility = false
            document.removeEventListener('click',this.toggle)
        },
        open(){
            this.popoverVisibility = true
            this.$nextTick(()=>{
                document.addEventListener('click',this.documentClik)
            })
        },
        toggle(){
            if(this.popoverVisibility){
                this.close()
            }else{
                this.open()
            }
        }

方法2:通過自定義指令給cascader最外層添加一個click-outside指令,實現(xiàn)點擊組件外關閉

  • click-outside.js
export default {
    // 當被綁定的元素插入到 DOM 中時……
    bind: function (el, binding, vnode) {
        document.addEventListener('click',(e)=>{
            let {target} =e
            if(el === target || el.contains(target)){
                return
            }
            //拿到你指令中傳入的值坯屿,這里是close函數(shù)
            binding.value()
        })
    }
}
  • cascader.vue
<div class="cascader" v-click-outside="close">

</div>
import clickOutside from './click-outside.js'
directives: {clickOutside}
close(){
            this.popoverVisibility = false
        },
        open(){
            this.popoverVisibility = true
        },
        toggle(){
            if(this.popoverVisibility){
                this.close()
            }else{
                this.open()
            }
        }

問題:上面的寫法,如果有多個cascader就會有多個監(jiān)聽器
解決辦法:讓頁面剛加載的時候就添加一個監(jiān)聽事件隔节,聲明一個空數(shù)組怎诫,每次綁定指令的時候給這個數(shù)組里添加一個對象{el:el,callback:callback}幻妓,然后在最開始的監(jiān)聽事件里遍歷這個數(shù)組肉津,看看數(shù)組里面的每一項中是否有el===e.target或者el.containes(e.target)舱沧,如果有就直接return熟吏,否則就調用這一項的callback

  • click-outside.js
let onClickDocument = (e)=>{
    let {target} = e
    arr.forEach(item=>{
        if(item.el === target || item.el.contains(target)){
            return
        }
        item.callback()
    })
}
document.addEventListener('click',onClickDocument)
let arr = []
export default {
    // 當被綁定的元素插入到 DOM 中時……
    bind: function (el, binding, vnode) {
        arr.push({el,callback:binding.value})
    }
}
實現(xiàn)點擊數(shù)據(jù)未渲染完成時的loading狀態(tài)

實現(xiàn)方法:通過拿到在cascader里給cascader-item傳入一個loadingItem屬性悍引,一開始是一個空對象趣斤,然后點擊當前層級的某一項拿到selected數(shù)組里的最后一項賦值給這個loadingItem浓领,通過點擊的當前item.name等不等于loadingItem.name來判斷是否顯示loading联贩,如果相等就顯示否則就顯示箭頭,然后數(shù)據(jù)獲取成功在updateSource 中將loadingItem變?yōu)榭諏ο?/p>

  • cascader.vue
<div class="popover" v-if="popoverVisibility">
            <cascader-item :items="source" :style="{height}" :height="height" :selected="selected" :level="level"
            @update:selected="onUpdateSelected" :loadData="loadData" :loadItem="loadItem"
            ></cascader-item>
        </div>
data(){
        return {
            loadItem: {}
        }
    },
methods: {
        onUpdateSelected(val){
            let lastVal = val[val.length-1]
            this.loadItem = lastVal   
            let updateSource = (result)=>{
                this.loadItem={}
            } 
  • cascader-item.vue
<div class="label" v-for="(item,index) in items" @click="onSelected(item,index)" :class="{active: currentItem === index}">
                <span class="name">{{item.name}}</span>
                <div class="icons" v-if="rightArrowVisible(item)">
                    <template v-if="loadItem.name === item.name">
                        <icon name="loading" class="loading"></icon>
                    </template>
                    <template v-else>
                        <icon name="right" class="next"></icon>
                    </template>
                </div>

            </div>
<div class="right" v-if="rightItems">
            <gulu-cascader-item :items="rightItems" :style="{height}" :height="height" :selected="selected" :level="level+1"
              @update:selected="onUpdateSelected" :loadData="loadData" :loadItem="loadItem"
            ></gulu-cascader-item>
        </div>
props: {
  loadItem: {
            type: Object
        }
}
實現(xiàn)點擊選中座菠,展開后仍然是對應的項添加選中狀態(tài)

實現(xiàn):首先要將我們可選擇的各省市區(qū)原來使用的v-if改為v-show浴滴,因為這樣的話才會不會去重新渲染升略,然后通過你之前點擊對應項把當前的項添加到selected里品嚣,然后通過當前的name來找selected里是否有這個name來判斷是否要添加這個選中的類翰撑,因為我們的selected里的item是對象,而對象沒法使用indexOf眶诈,所以這里我們單獨把selected里的name添加到一個新的數(shù)組里涨醋,之后判斷這個數(shù)組里是否存在我們當前項的name

  • cascader-items.vue
<template>
  <div class="left">
    <div class="label" v-for="item in items" @click="onSelected(item)"
                 :class="{active: selectedName.indexOf(item.name) > -1}"
            >
      <span class="name">{{item.name}}</span>
    </div>
  </div>
</template>
data(){
        return {
            selectedName: []
        }
    },
methods: {
  onSelected(item){
            let copy = JSON.parse(JSON.stringify(this.selected))
            //之所以寫copy[this.level]是為了你點擊當前層的每一個都讓數(shù)組里只保留一個
            //而不是點一個就往數(shù)組里加一個,如果不寫的話你點杭州數(shù)組里有一個杭州逝撬,再點福建
            //數(shù)組就會變成['杭州','福建']可這兩個屬于同一層浴骂,我們統(tǒng)一層只想保留一個
            copy[this.level]= item
            copy.splice(this.level+1)
            this.selectedName = copy.map(item1=>{
                //這時候this.selectedName=['杭州']
                return item1.name
            })
            this.$emit('update:selected',copy)
        },
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宪潮,隨后出現(xiàn)的幾起案子趣苏,更是在濱河造成了極大的恐慌,老刑警劉巖愧膀,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谣光,居然都是意外死亡檩淋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門萄金,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蟀悦,“玉大人,你說我怎么就攤上這事氧敢∪崭辏” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵孙乖,是天一觀的道長浙炼。 經常有香客問我,道長唯袄,這世上最難降的妖魔是什么弯屈? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮恋拷,結果婚禮上资厉,老公的妹妹穿的比我還像新娘。我一直安慰自己蔬顾,他們只是感情好宴偿,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诀豁,像睡著了一般窄刘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舷胜,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天都哭,我揣著相機與錄音,去河邊找鬼逞带。 笑死欺矫,一個胖子當著我的面吹牛,可吹牛的內容都是我干的展氓。 我是一名探鬼主播穆趴,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遇汞!你這毒婦竟也來了未妹?” 一聲冷哼從身側響起簿废,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎络它,沒想到半個月后族檬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡化戳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年单料,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片点楼。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡扫尖,死狀恐怖,靈堂內的尸體忽然破棺而出掠廓,到底是詐尸還是另有隱情换怖,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布蟀瞧,位于F島的核電站沉颂,受9級特大地震影響,放射性物質發(fā)生泄漏悦污。R本人自食惡果不足惜兆览,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塞关。 院中可真熱鬧抬探,春花似錦、人聲如沸帆赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椰于。三九已至怠益,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘾婿,已是汗流浹背蜻牢。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偏陪,地道東北人抢呆。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像笛谦,于是被迫代替她去往敵國和親抱虐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容