vue手动制作地址选择器

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

 

posted @ 2017-10-13 15:48  zph前端  阅读(4551)  评论(2编辑  收藏  举报