手寫一個Vue層聯(lián)選擇器

今天我們來手寫一個Cascader,也就是層聯(lián)選擇器,一般長下面這樣,通過點擊輸入框出來一個面板捷凄,可以層層選擇數(shù)據(jù),再次點擊輸入框或者外面围来,輸入框會消失跺涤。

image

我們先準備一份數(shù)據(jù)

 options: [{
          value: 'zhinan',
          label: '數(shù)碼',
          children: [{
            value: 'shejiyuanze',
            label: '手機',
            children: [{
              value: 'yizhi',
              label: '蘋果'
            }, {
              value: 'fankui',
              label: '華為'
            }, {
              value: 'xiaolv',
              label: '三星'
            }, {
              value: 'kekong',
              label: '小米'
            }]
          }, {
            value: '',
            label: '筆記本電腦',
            children: [{
              value: 'cexiangdaohang',
              label: 'Macbook Pro'
            }, {
              value: 'dingbudaohang',
              label: 'iMac'
            }]
          }]
        }, {
          value: 'zujian',
          label: '家電',
          children: [{
            value: 'basic',
            label: '空調(diào)',
            children: [{
              value: 'layout',
              label: '格力'
            }, {
              value: 'color',
              label: '美的'
            }]
          }, 
          {
            value: 'basic',
            label: '洗衣機',
            children: [{
              value: 'layout',
              label: '西門子'
            }, {
              value: 'color',
              label: '松下'
            }]
          },]
        }
      ]

然后創(chuàng)建一個Cascader.vue文件

<template>
  <div class="container" v-click-outside="close">
    <div class="input" @click="toggle"></div>
    <div class="content" v-if="visiable">
      <div class="content-left">
        <div v-for="(item,index) in options" :key="index">
          <div @click="select(item)">{{item.label}}</div>
        </div>
      </div>
      <div class="content-right" v-if="list.length>0">
        <div v-for="(item,index) in list" :key="index">
          <div>{{item.label}}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import clickOutside from "../../directives/outside";
export default {
  props: {
    options: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      visiable: false,
      selected: []
    };
  },
  computed: {
    list() {
      return this.selected.children ? this.selected.children : [];
    }
  },
  methods: {
    toggle() {
      this.visiable = !this.visiable;
    },
    select(item) {
      console.log(item);
      this.selected = item;
    },
    close() {
      this.visiable = false;
    }
  },
  directives: {
    clickOutside: clickOutside
  },
  mounted() {}
};
</script>

可以看到匈睁,我們通過一個props來接收要展示的數(shù)據(jù),用v-for循環(huán)展示這些數(shù)據(jù)桶错,通過計算屬性計算出點擊左側(cè)面板后右側(cè)面板要顯示的數(shù)據(jù)航唆,通過visiable屬性搭配v-if指令控制面板的顯示和隱藏,同時這里我們使用一個自定義指令v-click-outside來實現(xiàn)點擊面板外面關(guān)閉面板,代碼如下

const clickOutside = {
    inserted: function (el, binding) {
        function hide(e) {
            if (el === e.target || el.contains(e.target)) {
                return
            }
            binding.value()
        }
        el._hide = hide
        document.addEventListener('click', el._hide)

    },
    unbind(el) {
        document.removeEventListener(el._hide)
    }
}

export default clickOutside

我們在頁面組件引入Cascader,將數(shù)據(jù)傳給他,這時頁面長這樣

image

我們點擊數(shù)碼院刁,出現(xiàn)手機 筆記本電腦這沒毛病糯钙,但我們點擊手機的時候,并沒有出現(xiàn)下一層退腥。任岸。。因為我們的dom結(jié)構(gòu)只寫了兩個div狡刘,也就是class名為content-leftcontent-right的這兩個享潜,想要顯示第三層的話,就得再增加div嗅蔬,但你們只看到了第二層剑按,而我看到了第五層。事實上我們不知道到底有多少層澜术,所以我們得用Vue的遞歸組件艺蝴,我們再創(chuàng)建一個CascaderItem.vue的文件,來循環(huán)右側(cè)數(shù)據(jù)

<template>
    <div class="panel">
      <div class="content-left">
        <div v-for="(item,index) in options" :key="index">
          <div @click="select(item,index)" :class="{'selected':selectIndex==index}">{{item.label}}</div>
        </div>
      </div>
      <div class="content-right" v-if="list.length>0">
          <CascaderItem :options='list'></CascaderItem>
      </div>
    </div>
</template>

<script>
export default {
  name:'CascaderItem',
  props: {
    options: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      selected: [],
      selectIndex:null
    };
  },
  computed: {
    list() {
      return this.selected.children ? this.selected.children : [];
    }
  },
  methods: {
    select(item,index) {
      console.log(item);
      this.selected = item;
      this.selectIndex = index
    },
  },
 
  mounted() {}
};
</script>

image

好了 現(xiàn)在我們好像能展示多層數(shù)據(jù)了瘪板,但這時我們發(fā)現(xiàn)一個問題
image

我們這時點擊家電吴趴,雖然第二層跟著變了,但第三層沒有變侮攀。因為目前的寫法第三層數(shù)據(jù)是由第二層數(shù)據(jù)點擊產(chǎn)生的锣枝。那么如何解決這個問題呢?我們用在Cascader文件中聲明一個數(shù)組 selectData 來存放我們選中的數(shù)據(jù)兰英,為此我們還需要聲明一個變量level來表示當前層數(shù)

Cascader.vue

<template>
  <div class="container" v-click-outside="close">
    <div class="input" @click="toggle">{{inputVal}}</div>
    <div class="content" v-if="visiable">
      <CascaderItem :options='options' :selectData='selectData' :level='0' @change='change'></CascaderItem>
    </div>
  </div>
</template>

<script>

export default {
 ...
  data() {
    return {
     ...
      selectData:[]
    };
  },
 ...
  computed: {
     inputVal(){
       return this.selectData.map(item=>item.label).join('/')
     }
  },
  methods: {
    change(value,index){
      this.selectData[index] = value
      this.selectData.splice(index,1,value) //觸發(fā)更新
    },
   
  },
 
};
</script>

CascaderItem.vue


<template>
    <div class="panel">
      <div class="content-left">
        <div v-for="(item,index) in options" :key="index">
          <div @click="select(item,index)" :class="{'selected':selected==item}">{{item.label}}</div>
        </div>
      </div>
      <div class="content-right" v-if="list && list.length>0">
          <CascaderItem :options='list' :selectData='selectData' :level='level+1' @change='change' ></CascaderItem>
      </div>
    </div>
</template>

<script>

export default {
    ..
  props: {
   ..
     selectData: {
      type: Array,
      default: () => []
    },
    level:{
        type:Number
    }
  },
 
  computed: {
    list() {
      return this.selectData[this.level] && this.selectData[this.level].children 
    }
  },
  methods: {
    select(item,index) {
      this.selected = item;
      this.selectIndex = index
      this.selectData.splice(this.level+1)
      this.$emit('change',item,this.level)
    },
    change(item,index){
      this.$emit('change',item,index)
    }
  },
};
</script>

可以看到撇叁,點擊的時候我們把selectData下一層之后的數(shù)據(jù)都刪除了,這就符合了我們的邏輯畦贸,我們的右側(cè)數(shù)據(jù)此時就可以通過selectDatalevel來得到了陨闹。這里我們要主要的是我們需要將level傳給遞歸組件,并且+1,因為每遞歸一次薄坏,他的層數(shù)就會加一趋厉,此外,我們需要監(jiān)聽遞歸組件emit出來的事件胶坠,然后再傳遞出去君账。
我們再稍作修改,將選擇的數(shù)據(jù)傳遞出去

props:{
...
 value: {
      type: Array,
      default: () => []
    }
}
 change(){
 ...
      let selectArr = this.selectData.map(item => item.value);
      console.log(selectArr);
      this.$emit("input", selectArr);
      }

這時沈善,我們的靜態(tài)層聯(lián)基本就完成了


image

接下來我們來寫異步的乡数。椭蹄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市净赴,隨后出現(xiàn)的幾起案子绳矩,更是在濱河造成了極大的恐慌,老刑警劉巖玖翅,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翼馆,死亡現(xiàn)場離奇詭異,居然都是意外死亡烧栋,警方通過查閱死者的電腦和手機写妥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來审姓,“玉大人珍特,你說我怎么就攤上這事∧拢” “怎么了扎筒?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酬姆。 經(jīng)常有香客問我嗜桌,道長,這世上最難降的妖魔是什么辞色? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任骨宠,我火速辦了婚禮,結(jié)果婚禮上相满,老公的妹妹穿的比我還像新娘层亿。我一直安慰自己,他們只是感情好立美,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布匿又。 她就那樣靜靜地躺著,像睡著了一般建蹄。 火紅的嫁衣襯著肌膚如雪碌更。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天洞慎,我揣著相機與錄音痛单,去河邊找鬼。 笑死劲腿,一個胖子當著我的面吹牛旭绒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼快压,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了垃瞧?” 一聲冷哼從身側(cè)響起蔫劣,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎个从,沒想到半個月后脉幢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡嗦锐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年嫌松,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奕污。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡萎羔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碳默,到底是詐尸還是另有隱情贾陷,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布嘱根,位于F島的核電站髓废,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏该抒。R本人自食惡果不足惜慌洪,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凑保。 院中可真熱鬧冈爹,春花似錦、人聲如沸愉适。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽维咸。三九已至剂买,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間癌蓖,已是汗流浹背瞬哼。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留租副,地道東北人坐慰。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像用僧,于是被迫代替她去往敵國和親结胀。 傳聞我的和親對象是個殘疾皇子赞咙,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容