方法一: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{ 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>