方法一:4級地址選擇器(基於elementui Cascader 級聯選擇器) 推薦
效果圖:
組件源碼:
<template> <div class="select-city" ref="selectCity"> <el-cascader :options="options2" @change="change" v-model="selCity" :props="props" ></el-cascader> </div> </template> <style> .select-city .el-input{ width: 350px; } </style> <script> import addressData from 'common/json/class4new.json' export default { props: { value: { required: true }, getCityName: { } }, data() { return { options2: addressData, props: { label: 'name', value: 'id', children: 'children' }, selCity: [] } }, watch: { value (val) { this.init() } }, created() { // 組件剛載入並不會觸發watch value }, methods: { init() { let el = this.$refs.selectCity if (!this.value) { if (this.selCity.length) { this.selCity = [] el.getElementsByClassName('el-cascader__label')[0].innerHTML = '' el.getElementsByClassName('el-input__inner')[0].setAttribute('placeholder', '請選擇') } } else { if (this.selCity.length===0 || this.selCity[3] !== this.value) { this.selCity[0] = this.value.substr(0, 2) + '0000' this.selCity[1] = this.value.substr(0, 4) + '00000000' this.selCity[2] = this.value.substr(0, 6) + '000000' this.selCity[3] = this.value let name = this.getNode().join('<span>/</span>') el.getElementsByClassName('el-cascader__label')[0].innerHTML = name el.getElementsByClassName('el-input__inner')[0].setAttribute('placeholder', '') } } }, change(val) { // 只有選完了,才會將數據返回給父組件 this.$emit('input', val[3]) this.returnCityName() }, returnCityName() { if (typeof this.getCityName === 'function') { this.getCityName(this.getNode().join('')) } }, getNode() { let name = [] this.options2.filter(v => { if (name[0]) return if (v.id===this.selCity[0]) { name.push(v.name) v.children.filter(v => { if (name[1]>0) return if (v.id===this.selCity[1]) { name.push(v.name) v.children.filter(v => { if (name[2]>0) return if (v.id===this.selCity[2]) { name.push(v.name) v.children.filter(v => { if (name[3]>0) return if (v.id===this.selCity[3]) { name.push(v.name) return false } }) } }) } }) } }) return name } } } </script>
方法二:4級地址選擇器(基於elementui select選擇器 )
適用環境: PC
開發過程中遇到的問題:
1. 自定義組件如何做到雙向數據綁定
2. 自定義組件在剛加載完畢,會執行一次created和mounted,當組件上綁定的v-model變化時候,也就是做編輯的時候,觸發的watch監聽的value方法
3.這個程序剛好是一個自循環,一旦更新了地址組件綁定的值立刻就觸發this.$emit,把執行結果返回至父組件。這邊需要visible-change(下拉框出現/隱藏時觸發, 出現則為 true,隱藏則為 false),來組件地址組件數據的初始化,導致向父組件傳遞錯誤數據
小小的程序,把我折騰了好幾天,仔細想想,兩個2原因:1.缺少自己寫組件的經歷導致對vue的很多api不熟悉 2.缺乏獨自面對困難的恆心
效果圖:
地址組件代碼:
<style> .block .el-select{display: block;} .block .el-select{margin-bottom: 20px} </style> <template> <div> <div :class="{block: block}"> <el-select v-model="proviceCode" popper-class="tab-select" placeholder="請選擇省" @change="proviceChange" @visible-change="vChange($event, 'provice')"> <el-option v-for="(item, index) in provice" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> <el-select v-model="cityCode" popper-class="tab-select" placeholder="請選擇市" @change="cityChange" @visible-change="vChange($event, 'city')"> <el-option v-for="(item, index) in city" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> <el-select v-model="areaCode" popper-class="tab-select" placeholder="請選擇區或縣" @change="areaChange" @visible-change="vChange($event, 'area')"> <el-option v-for="(item, index) in area" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> <el-select v-model="villageCode" popper-class="tab-select" placeholder="請選擇鄉" @change="villageChange" @visible-change="vChange($event, 'village')"> <el-option v-for="(item, index) in village" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> <input type="hidden" :value="value"> </div> </div> </template> <script> import address from 'common/json/class4new.json' export default { props: { block: { type: Boolean, default: false }, value: { required: false } }, data() { return { provice: address, city: [], area: [], village: [], proviceCode: '', cityCode: '', areaCode: '', villageCode: '', isOpen: { provice: false, city: false, area: false, village: false } } }, watch: { value (val) { if (val.code !== this.villageCode) { this.init() } } }, created() { this.villageCode = this.value.code this.init() }, mounted () { this.$root.eventHub.$on("reset-addressSelect", () => { this.city = [] this.area = [] this.village = [] this.proviceCode = '' this.cityCode = '' this.areaCode = '' this.villageCode = '' }) }, methods: { vChange(val, type) { this.isOpen[type] = val }, init() { let code = this.value.code; if (code) { let v = code.toString() this.proviceCode = v.substr(0,2) + '0000' this.cityCode = v.substr(0,4)+'00000000' this.areaCode = v.substr(0,6)+'000000' this.villageCode = v this.proviceChange(this.proviceCode).then(_ => { this.cityChange(this.cityCode).then(_ => { this.areaChange(this.areaCode) }) }) } else { this.city = [] this.area = [] this.village = [] this.proviceCode = '' this.cityCode = '' this.areaCode = '' this.villageCode = '' } }, proviceChange(id) { return new Promise((resolve, reject) => { if (this.isOpen.provice) { this.city = [] this.area = [] this.village = [] this.cityCode = '' this.areaCode = '' this.villageCode = '' } this.provice.filter(v => { if (v.id === id) { this.city = v.children resolve() return false } }) }) }, cityChange(id) { return new Promise((resolve, reject) => { if (this.isOpen.city) { this.area = [] this.village = [] this.areaCode = '' this.villageCode = '' } this.city.filter(v => { if (v.id === id) { this.area = v.children resolve() return false } }) }) }, areaChange(id) { return new Promise((resolve, reject) => { if (this.isOpen.area) { this.village = [] this.villageCode = '' } this.area.filter(v => { if (v.id === id) { this.village = v.children resolve() return false } }) }) }, villageChange(id) { var text = [] this.provice.filter(v => { if (v.id === this.proviceCode) { text.push(v.name) return false } }) this.city.filter(v => { if (v.id === this.cityCode) { text.push(v.name) return false } }) this.area.filter(v => { if (v.id === this.areaCode) { text.push(v.name) return false } }) this.village.filter(v => { if (v.id === id) { text.push(v.name) return false } }) this.$emit('input', { text: text.join(''), code: id }) } } } </script>