微信小程序实现省市区三级联动

实现效果如图所示
在这里插入图片描述
js

const app = getApp()
const api = require('../api')
Page({
  /**
   * 页面的初始数据
   */
  data: {
    successCode:0,
    id:0,//正在编辑的地址的id,0表示新增
    detail:{//当前正在添加/编辑的地址的信息
      name: "",//收货人名 <string>
      phone: "",//收货人电话 <string>
      province: "",//省 <string>
      provinceCode: "",//省编码 <string>
      city: "",//市 <string>
      cityCode: "",//市编码 <string>
      area: "",//区 <string>
      areaCode: "",//区编码 <string>
      address: "",//详细地址 <string>
      isDefault: 0,//0非默认,1默认 <number>
    },
    disabled:false,
    provinceList:[],//从接口获取的省市区数据
    cityList:[],
    areaList:[],
    provinceNameList:[],//地区组件中展示的省市区数据
    cityNameList:[],
    areaNameList:[],
    provinceIndex:0,//被选中的省市区在数组中的index
    cityIndex:0,
    areaIndex:0,

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    if(options.id){
      this.setData({
        id:options.id
      })
    }
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    this.getInitRegion()
    wx.setNavigationBarTitle({
      title: '添加收货地址'
    })
    
  },

  //获取初始化的省市区信息
  /**
   * 获取初始化的省市区信息
   * 新增地址时,获取全部的省,第一个省下的市,第一个市下的区
   * 编辑地址时,获取全部的省,已选中的省下的市,已选中的市下的区
   */
  async getInitRegion(){
    try{
      const provinceList = await this.getRegionByCode('')
      const provinceCode = this.data.detail.provinceCode?this.data.detail.provinceCode:provinceList[0].cityCode
      const cityList  = await this.getRegionByCode(provinceCode)
      const cityCode =  this.data.detail.cityCode?this.data.detail.cityCode:cityList[0].cityCode
      const areaList = await this.getRegionByCode(cityCode)
      const provinceNameList = []
      const cityNameList = []
      const areaNameList = []
      provinceList.forEach((province,index)=>{
        provinceNameList.push(province.cityName)
        if(province.cityCode === this.data.detail.provinceCode){
          this.setData({
            provinceIndex:index
          })
        }
      })
      cityList.forEach((city,index)=>{
        cityNameList.push(city.cityName)
        if(city.cityCode === this.data.detail.cityCode){
          this.setData({
            cityIndex:index
          })
        }
      })
      areaList.forEach((area,index)=>{
        areaNameList.push(area.cityName)
        if(area.cityCode === this.data.detail.areaCode){
          this.setData({
            areaIndex:index
          })
        }
      })
      this.setData({
        provinceList,
        cityList,
        areaList,
        provinceNameList,
        cityNameList,
        areaNameList,
        regionNameList:[provinceNameList,cityNameList,areaNameList]
      })
      return new Promise((resolve)=>{
        resolve('success')
      })
      
    }catch(err){
      console.log(err)
      wx.showToast({
        title: err,
        icon:'none',
        duration:2000
      })
      
    }
  },
    //切换省市区划
    changeRangeColumn(e){
      console.log('切换省市区划',e)
      const index = e.detail.value
      switch(e.detail.column){
        case 0:
          this.handleChangeProvince(index)
          break;
        case 1:
          this.handleChangeCity(index)
          break;
        case 2:
          this.handleChangeArea(index)
          break;
        default:
          break
      }
    },
    //地区弹框,切换省
    async handleChangeProvince(provinceIndex){
      this.setData({
        provinceIndex:provinceIndex,
        cityIndex:0,
        areaIndex:0,
        disabled:true
      })
      try{
        const cityList  = await this.getRegionByCode(this.data.provinceList[provinceIndex].cityCode)
        const areaList = await this.getRegionByCode(cityList[0].cityCode)
        const cityNameList = []
        const areaNameList = []
        cityList.forEach((city,index)=>{
          cityNameList.push(city.cityName)
        })
        areaList.forEach((area,index)=>{
          areaNameList.push(area.cityName)
        })
        console.log('省切换完毕')
        this.setData({
          cityList,
          areaList,
          cityNameList,
          areaNameList,
          regionNameList:[this.data.regionNameList[0],cityNameList,areaNameList],
          disabled:false
        })
      }catch(err){
        console.log(err)
        wx.showToast({
          title: err,
          icon:'none',
          duration:2000
        })
      }
    },
    //地区弹框,切换市
    async handleChangeCity(cityIndex){
      this.setData({
        cityIndex:cityIndex,
        areaIndex:0
      })
      try{
        const areaNameList = []
        const areaList = await this.getRegionByCode(this.data.cityList[cityIndex].cityCode)
        areaList.forEach(area=>{
          areaNameList.push(area.cityName)
        })
        this.setData({
          areaList,
          areaNameList,
          regionNameList:[this.data.regionNameList[0],this.data.regionNameList[1],areaNameList],
        })
      }catch(err){
        console.log(err)
        wx.showToast({
          title: err,
          icon:'none',
          duration:2000
        })
      }
    },
    //地区弹框,切换区
    handleChangeArea(areaIndex){
      this.setData({
        areaIndex:areaIndex
      })
    },
    bindRegionChange(e){
      console.log('选择地址',e)
      console.log('选定地址时,data为',this.data)
      this.setData({
        detail:{
          ...this.data.detail,
          province:this.data.provinceList[this.data.provinceIndex].cityName,
          city:this.data.cityList[this.data.cityIndex].cityName,
          area:this.data.areaList[this.data.areaIndex].cityName,
          provinceCode:this.data.provinceList[this.data.provinceIndex].cityCode,
          cityCode:this.data.cityList[this.data.cityIndex].cityCode,
          areaCode:this.data.areaList[this.data.areaIndex].cityCode,
        }
      })
    },

  //切换新增/编辑地址时的默认状态
  changeDefault(e){
    const newDetail = this.data.detail
    newDetail.isDefault = e.detail.value?1:0
    this.setData({
      detail:newDetail
    })
  },
  //根据行政code获取下级区域
  getRegionByCode(code){
    return new Promise((resolve,reject)=>{
      wx.showLoading()
      api.getRegionListByCode({
        regionCode:code
      }).then(res=>{
        console.log('获取到省市区,code = '+code+',result = ',res)
        wx.hideLoading()
        if(res.code !== this.data.successCode){
          reject(res.msg)
        }
        if(res.data.length === 0){
          reject('未获取到省市区')
        }
        resolve(res.data)
      })
    })
  },
  bindInput(e){
    const newDetail = this.data.detail
    newDetail[e.currentTarget.dataset.name] = e.detail.value.trim()
    this.setData({
      detail:newDetail
    })
    console.log('detail',this.data.detail)
  },
  handleSubmit(e){
    console.log(e)
    const value = e.detail.value
    if(this.checkEmpty(value.name)){
      wx.showToast({
        title: '请填写收货人',
        icon: 'none',
        duration: 2000
      })
      return false
    }
    if(this.checkEmpty(value.phone)){
      wx.showToast({
        title: '请填写手机号码',
        icon: 'none',
        duration: 2000
      })
      return false
    }
    if(this.checkEmpty(value.region.length === 0)){
      wx.showToast({
        title: '请选择地区',
        icon: 'none',
        duration: 2000
      })
      return false
    }
    if(this.checkEmpty(this.data.detail.province) || this.checkEmpty(this.data.detail.city) || this.checkEmpty(this.data.detail.area)){
      wx.showToast({
        title: '请选择完整的地区',
        icon: 'none',
        duration: 2000
      })
      return false
    }
    if(this.checkEmpty(value.address)){
      wx.showToast({
        title: '请填写详细地址',
        icon: 'none',
        duration: 2000
      })
      return false
    }
    if(!this.checkPhone(value.phone)){
      wx.showToast({
        title: '请填写正确的手机号',
        icon: 'none',
        duration: 2000
      })
      return false
    }
    if(!this.checkEmpty(this.data.detail.id)){
      this.saveUpdateAddress(e.detail.value)
    }else{
      this.saveAddAddress(e.detail.value)
    }
  },
  //保存新增的地址
  saveAddAddress(value){
    wx.showLoading()
    api.addAddress({
      ...this.data.detail
    }).then(res=>{
      wx.hideLoading()
      if(res.code !== this.data.successCode){
        wx.showToast({
          title: res.msg,
          icon:'none',
          duration:2000
        })
        return
      }
      wx.showToast({
        title: '添加成功',
        icon:'none',
        duration:2000
      })
    })
  },
  //保存修改的地址
  saveUpdateAddress(value){
    wx.showLoading()
    api.updateAddress({
      ...this.data.detail
    }).then(res=>{
      wx.hideLoading()
      if(res.status !== this.data.successCode){
        wx.showToast({
          title: res.msg,
          icon:'none',
          duration:2000
        })
        return
      }
      wx.showToast({
        title: '修改成功',
        icon:'none',
        duration:2000
      })
    })
  },
  //非空检查
  checkEmpty(value){
    if (value === '' || value === null || typeof (value) == 'undefined') {
      return true
    }
    return false
  }
})

wxml:



  <view class="add-page">
    <form catchsubmit="handleSubmit" >
      <view class="address-block">
        <view class="address-info">
          <view class="address-title" >收货人</view>:
          <view class="address-content">
            <input 
            name="name" 
            value="{{detail.name}}" 
            placeholder="请输入名称" 
            placeholder-style="color:#999999;" 
            maxlength="20"
            bindinput="bindInput"
            data-name="name"
            />
          </view>
        </view>
        <view class="address-info">
          <view class="address-title">手机号码</view>:
          <view class="address-content">
            <input 
              name="phone"  
              value="{{detail.phone}}" 
              placeholder="请输入手机号" 
              placeholder-style="color:#999999;"
              maxlength="11" 
              type="number"
              bindinput="bindInput"
              data-name="phone"
             />
          </view>
        </view>
        <view class="address-info">
          <view class="address-title">选择地区</view>:
          <view class="address-content address-region">
            <picker class="" 
            mode="multiSelector" 
            range="{{[provinceNameList,cityNameList,areaNameList]}}" 
            bindchange="bindRegionChange" 
            value="{{[provinceIndex,cityIndex,areaIndex]}}" 
            name="region"
            bindcolumnchange="changeRangeColumn"
            disabled="{{disabled}}"
            >
             <span class="address-region-name one-line" wx:if="{{detail.province}}">{{detail.province}} {{detail.city}} {{detail.area}}</span>
             <span class="address-region-name one-line placeholder" wx:else>请选择</span>
             <span class="arrow-right"></span>
            </picker>
          </view>
        </view>
        <view class="address-info address-detail">
          <view class="address-title">详细地址</view>:
          <view class="address-content">  
            <textarea class="address-detail-text" bindblur="bindTextAreaBlur" name="address" value="{{detail.address}}" placeholder="请输入详细地址" placeholder-style="color:#999999;" maxlength="150"  bindinput="bindInput"
            data-name="address"/>
          </view>
        </view>
        <view class="address-info">
          <span>设为默认地址</span>
          <switch checked="{{detail.isDefault}}" bindchange="changeDefault" color="{{iconColor}}" />
        </view>
      </view>
      <view class="bottom-btn">
        <button class="btn-primary" type="primary" formType="submit">保存</button>
      </view>
    </form> 
  </view>

wxss:

page{
  padding-bottom: 200rpx;
}
/*添加地址start*/
.add-page{
  width: 100%;
  font-size: 28rpx;
  
}
.address-block{
  padding-left: 40rpx;
  padding-right: 51rpx;
  background-color: #ffffff;
}
.address-info{
  width: 100%;
  display: flex;
  justify-content: space-between;
  height: 98rpx;
  line-height: 98rpx;
  border-bottom: 1rpx solid #f1f1f1;
}
.address-info:last-child{
  border-bottom:none;
}
.address-info input{
  height: 100%;
}
.address-title{
  flex: 0 0 130rpx;
  text-align:justify;
}
.address-title:after{
  display:inline-block;
  content:'';
  width:100%;
}
.address-content{
  flex: 1 1 auto;
  height: 100%;
  padding-left: 35rpx;
}
.address-region{
  display: inline-flex;
  justify-content: space-between;
  position: relative;
}
.address-region-name{
  width: 450rpx;
}
.address-region .arrow-right{
  display: inline-block;
  position: absolute;
  right: 0;
  top: 40rpx;
}
.address-detail{
  height: 226rpx;
  padding-top: 40rpx;
  line-height: 41rpx;
}
.address-detail-text{
  width: 100%;
  height: 100%;
}

/*添加地址end*/

测试数据:api.js

const provinceList = 
   [
    {cityCode: "370000", cityName: "山东省"},
    {cityCode: "140000", cityName: "山西省"},
    {cityCode: "410000", cityName: "河南省"},
    {cityCode: "130000", cityName: "河北省"},
  ]
  const cityList = {
    '370000':[
      {cityCode: "370100", cityName: "济南市"},
      {cityCode: "370200", cityName: "青岛市"}
    ],
    '140000':[
      {cityCode: "140100", cityName: "太原市"},
      {cityCode: "140200", cityName: "大同市"},
      {cityCode: "140300", cityName: "阳泉市"}
    ],
    '130000':[
      {cityCode: "130100", cityName: "石家庄市"}
    ],
    '410000':[
      {cityCode: "410100", cityName: "郑州市"},
      {cityCode: "410200", cityName: "洛阳市"},
      {cityCode: "410300", cityName: "开封市"},
      {cityCode: "410300", cityName: "平顶山市"}
    ],
    '370100':[
      {cityCode: "370101", cityName: "济南区县-1"},
      {cityCode: "370102", cityName: "济南区县-2"}
    ],
    '370200':[
      {cityCode: "370201", cityName: "青岛区县-1"},
      {cityCode: "370202", cityName: "青岛区县-2"}
    ],
    '140100':[
      {cityCode: "140101", cityName: "太原区县-1"},
      {cityCode: "140102", cityName: "太原区县-2"},
      {cityCode: "140103", cityName: "太原区县-3"},
      {cityCode: "140104", cityName: "太原区县-4"}
    ],
    '140200':[
      {cityCode: "140201", cityName: "大同区县-1"},
      {cityCode: "140202", cityName: "大同区县-2"}
    ],
    '140300':[
      {cityCode: "140301", cityName: "阳泉区县-1"},
      {cityCode: "140302", cityName: "阳泉区县-2"},
      {cityCode: "140303", cityName: "阳泉区县-3"}
    ],
    '130100':[
      {cityCode: "130101", cityName: "石家庄区县-1"},
      {cityCode: "130102", cityName: "石家庄区县-2"},
      {cityCode: "130103", cityName: "石家庄区县-3"},
      {cityCode: "130104", cityName: "石家庄区县-4"},
    ],
    '410100':[
      {cityCode: "410101", cityName: "郑州区县-1"},
      {cityCode: "410102", cityName: "郑州区县-2"},
      {cityCode: "410103", cityName: "郑州区县-3"},
    ],
    '410200':[
      {cityCode: "410201", cityName: "洛阳区县-1"},
      {cityCode: "410202", cityName: "洛阳区县-2"}
    ],
    '410300':[
      {cityCode: "370100", cityName: "开封区县-1"}
    ],
    '410400':[
      {cityCode: "410401", cityName: "平顶山区县-1"},
      {cityCode: "410402", cityName: "平顶山区县-2"},
      {cityCode: "410403", cityName: "平顶山区县-3"},
      {cityCode: "410404", cityName: "平顶山区县-4"},
    ],
  }

module.exports = {
  //新增地址
  addAddress(params){
    return new Promise(resolve=>{
      resolve({
        code:0,
        msg:'success',
        data:{}
      })
    })

  },
  //获取省市区联动
  getRegionListByCode(params){
    console.log('cityCode',params)
    let result={
      code:0,
      msg:'success',
      data:[]
    }
    if(params.regionCode == ''){
      result={
        ...result,
        data:provinceList
      }
    }else{
      result={
        ...result,
        data:cityList[params.regionCode]
      }
      
    }
    return new Promise(resolve=>{
      resolve(result)
    })
  }
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值