找回密码
 立即注册

QQ登录

只需一步,快速开始

微信小程序综合交流

关注:51

所属分类: 微信开发 微信小程序综合交流

微信小程序用户交流版块,希望同学们积极发言,不过大家不要恶意发布广告哦!

[经验分享] 微信小程序-省市区县三级联动选择器

[复制链接]
查看: 423|回复: 2
最佳答案
0 

15

主题

15

帖子

209

积分

新人求带

积分
209
 楼主| 发表于 2017-8-9 15:09:28 | 显示全部楼层 |阅读模式
项目地址:https://github.com/leesonp/littleAPP.git

怎么使用呢?

只需在要用到的类中导入模板模板,引入对应后缀的文件,做一些简单必要操作即可。
以上传的Demo为例:
  1. // modelTest.js
  2. var model = require('../../model/model.js')

  3. var show = false;
  4. var item = {};

  5. Page({
  6.   data: {
  7.     item: {
  8.       show: show
  9.     }
  10.   },
  11.    //生命周期函数--监听页面初次渲染完成
  12.   onReady: function (e) {
  13.     var that = this;
  14.     //请求数据
  15.     model.updateAreaData(that, 0, e);
  16.   },
  17.   //点击选择城市按钮显示picker-view
  18.   translate: function (e) {
  19.     model.animationEvents(this, 0, true,400);  
  20.   },
  21.   //隐藏picker-view
  22.   hiddenFloatView: function (e) {
  23.     model.animationEvents(this, 200, false,400);
  24.   },
  25.   //滑动事件
  26.   bindChange: function (e) {
  27.     model.updateAreaData(this, 1, e);
  28.     item = this.data.item;
  29.     this.setData({
  30.       province: item.provinces[item.value[0]].name,
  31.       city: item.citys[item.value[1]].name,
  32.       county: item.countys[item.value[2]].name
  33.     });
  34.   },
  35.   onReachBottom: function (){
  36.   },
  37.   nono: function (){}
  38. })
复制代码
  1. <!--modelTest.wxml-->
  2. <import src="../../model/model.wxml"/>
  3. <view class="infoText">{{province}} {{city}} {{county}}</view>
  4. <button class="animation-button" bindtap="translate">选择城市</button>
  5. <template is="areaData" data="{{...item}}"/>
复制代码
  1. /* modelTest.wxss */
  2. @import '../../model/model.wxss'
复制代码

在model.js文件中我就暴露了两个接口,引入模板后,全部代码就这些。是不是感觉舒服很多?
代码简洁、逻辑清晰,是封装给我们带来的好处,希望没试过封装的同学可以尝试下。

-- END --

------------------------------ 分割线 ------------------------------
2017.07.17 更新
本人之前一直从事iOS开发,最近无项目闲来无事研究了下微信小程序。
我没有像从新学一门语言一样从头到尾看一遍文档,我个人感觉小程序类似前端开发,正好之前对前端也略有涉猎,所以直接就拿我之前iOS的项目的某个模块练手,不懂的再去查阅官方文档。
因为有个创建红包的界面,UITableView的某个cell点击会弹出选择区域和时间选择器,下面就给大家讲一下小程序版是怎样的一种操作。
02729e793.gif
图1 iOS版

在iOS中我用的是UIPickView,体验还OK。那在小程序中选什么控件呢?查阅官方文档,有两种滑动选择器。
5d3b3fb5.jpeg
图2 官方列出的两种滑动选择器

picker:从底部弹起的滚动选择器,现支持三种选择器,通过mode来区分,分别是普通选择器,时间选择器,日期选择器,默认是普通选择器。因为是固定的,直接pass掉了。但是最开始我以为只有这一种,然后想去百度找找有没有自定义的。结果发现几乎网上没有很好的区域选择器的方案。我就再回过头来查阅官方文档发现还有个picker-view。

picker-view:嵌入页面的滚动选择器。官方的示例咋一看还是时间选择器,以为也是定死的呢。但是看代码就可以发现,其实跟iOS的写法逻辑相似。

既然相似我们就可以着手写代码了。但是开始写之前首先我们要拿到省市区县的数据。网上搜索下中国行政区域可以找到很多,我选了这个。下载下来是txt文件的Json数据。那怎么导入工程呢? 有两种方法:

1.在utils文件夹下新建一个js文件,写一个函数把txt里的json数据复制粘贴进去,然后在需要的类目下调用这个函数接收返回值就行了。
  1. //area.js
  2. function getAreaInfo(callBack){
  3. var str =["此处粘贴json数据":... ];//因数据量太大就不在此处全部展示,知道是个数组就行了。

  4. callBack(str);
  5. }
  6. module.exports.getAreaInfo = getAreaInfo;
  7. ------------------------------ 分割线 ------------------------------
  8. //index.js
  9. var area = require('../../utils/area.js')
  10. ...
  11. Page({
  12.   data: { },
  13. onLoad: function (options) {
  14.     var that = this;
  15.     //获取省市区县数据
  16.     area.getAreaInfo(function (arr) {
  17.     });

  18.   }
  19. })
  20. ...
复制代码

2.写一个接口调用,我自己就是写的一个PHP接口。
  1. //php代码
  2. $filename = "ChinaArea.txt";//网络上下载的文件(PHP或Java需要配置环境才能请求到数据)。
  3. $json_string = file_get_contents($filename);
  4. echo print_r($json_string,true);
复制代码

个人建议还是不要把数据源直接写在项目,因为小程序包是有大小限制的如若超过是无法提交的所以建议写接口,或者拿别人现成的接口。

话不多说直接贴代码吧。(代码已做必要注释)
  1. <!--index.wxml-->

  2.   <view class="infoText">{{province}} {{city}} {{county}}</view>

  3.   <view class="aaaa" >
  4.   <button class="animation-button" bindtap="translate">选择城市</button>
  5.   </view>

  6.   <view class="animation-element-wrapper" animation="{{animation}}" style="visibility:{{show ? 'visible':'hidden'}}" bindtap="hiddenFloatView" data-id="444">
  7.      <view class="animation-element" catchtap="nono">
  8.         <text class="left-bt" catchtap="hiddenFloatView" data-id="555">取消</text>
  9.         <text class="right-bt" catchtap="hiddenFloatView" data-id="666">确定</text>
  10.           <view class="line"></view>

  11.         <picker-view indicator-style = "height: 50rpx;" value="{{value}}" bindchange="bindChange" catchtap="nono">
  12.         <!--省-->
  13.         <picker-view-column>
  14.            <view wx:for="{{provinces}}" wx:key="" >
  15.              {{item.name}}
  16.           </view>
  17.         </picker-view-column>
  18.         <!--地级市-->
  19.         <picker-view-column>
  20.           <view wx:for="{{citys}}" wx:key="" >
  21.             {{item.name}}
  22.           </view>
  23.         </picker-view-column>
  24.         <!--区县-->
  25.         <picker-view-column>
  26.           <view wx:for="{{countys}}" wx:key="" >
  27.             {{item.name}}
  28.           </view>
  29.         </picker-view-column>
  30.         </picker-view>
  31.     </view>
  32.   </view>
复制代码
  1. /**index.wxss**/
  2. page{
  3.   background-color: rgba(255, 255, 255, 1);
  4. }

  5. .infoText{
  6.     margin-top: 20rpx;
  7.     text-align: center;
  8.     width: 100%;
  9.     justify-content: center;
  10. }

  11. picker-view{
  12.   background-color: white;
  13.   padding: 0;
  14.   width: 100%;
  15.   height: 380rpx;
  16.   bottom: 0;
  17.   position: fixed;
  18. }

  19. picker-view-column view{
  20.   vertical-align:middle;
  21.   font-size: 28rpx;
  22.   line-height: 28rpx;
  23.   height: 100%;
  24.   display: flex;
  25.   align-items: center;
  26.   justify-content: center;
  27. }

  28. /* ----------------------------------------- */

  29. .animation-element-wrapper {
  30.   display: flex;  
  31.   position: fixed;
  32.   left: 0;
  33.   top:0;
  34.   height: 100%;
  35.   width: 100%;
  36.   background-color: rgba(0, 0, 0, 0.6);
  37. }
  38. .animation-element {
  39.   display: flex;
  40.   position: fixed;
  41.   width: 100%;
  42.   height: 470rpx;
  43.   bottom: 0;
  44.   background-color: rgba(255, 255, 255, 1);
  45. }

  46. .animation-button {
  47.   top:20rpx;
  48.   width: 290rpx;
  49.   height: 100rpx;  
  50.   align-items:center;
  51. }


  52. text{
  53.   color: #999999;
  54.   display: inline-flex;  
  55.   position: fixed;
  56.   margin-top: 20rpx;
  57.   height: 50rpx;
  58.   text-align: center;
  59.   line-height: 50rpx;
  60.   font-size: 34rpx;
  61.   font-family: Arial, Helvetica, sans-serif;
  62. }

  63. .left-bt{
  64.   left: 30rpx;
  65. }
  66. .right-bt {
  67.   right: 30rpx;
  68. }

  69. .line{
  70.   display: block;
  71.   position: fixed;
  72.   height: 1rpx;
  73.   width: 100%;
  74.   margin-top: 89rpx;
  75.   background-color: #eeeeee;
  76. }
复制代码
  1. //index.js
  2. //获取应用实例
  3. var area = require('../../utils/area.js')

  4. var areaInfo = [];//所有省市区县数据

  5. var provinces = [];//省

  6. var citys = [];//城市

  7. var countys = [];//区县

  8. var index = [0, 0, 0];

  9. var cellId;

  10. var t = 0;
  11. var show = false;
  12. var moveY = 200;

  13. Page({
  14.   data: {
  15.     show: show,
  16.     provinces: provinces,
  17.     citys: citys,
  18.     countys: countys,
  19.     value: [0, 0, 0]
  20.   },
  21.   //滑动事件
  22.   bindChange: function (e) {
  23.     var val = e.detail.value

  24.     //判断滑动的是第几个column
  25.     //若省份column做了滑动则定位到地级市和区县第一位
  26.     if (index[0] != val[0]) {
  27.       val[1] = 0;
  28.       val[2] = 0;
  29.       getCityArr(val[0], this);//获取地级市数据
  30.       getCountyInfo(val[0], val[1], this);//获取区县数据
  31.     } else {    //若省份column未做滑动,地级市做了滑动则定位区县第一位
  32.       if (index[1] != val[1]) {
  33.         val[2] = 0;
  34.         getCountyInfo(val[0], val[1], this);//获取区县数据
  35.       }
  36.     }
  37.     index = val;

  38.     console.log(index + " => " + val);

  39.     //更新数据
  40.     this.setData({
  41.       value: [val[0], val[1], val[2]],
  42.       province: provinces[val[0]].name,
  43.       city: citys[val[1]].name,
  44.       county: countys[val[2]].name
  45.     })

  46.   },
  47.   onLoad: function (options) {
  48.     cellId = options.cellId;
  49.     var that = this;
  50.     var date = new Date()
  51.     console.log(date.getFullYear() + "年" + (date.getMonth() + 1) + "月" + date.getDate() + "日");

  52.     //获取省市区县数据
  53.     area.getAreaInfo(function (arr) {
  54.       areaInfo = arr;
  55.       //获取省份数据
  56.       getProvinceData(that);
  57.     });

  58.   },
  59.   // ------------------- 分割线 --------------------
  60.   onReady: function () {
  61.     this.animation = wx.createAnimation({
  62.       transformOrigin: "50% 50%",
  63.       duration: 0,
  64.       timingFunction: "ease",
  65.       delay: 0
  66.     }
  67.     )
  68.     this.animation.translateY(200 + 'vh').step();
  69.     this.setData({
  70.       animation: this.animation.export(),
  71.       show: show
  72.     })
  73.   },
  74.   //移动按钮点击事件
  75.   translate: function (e) {
  76.     if (t == 0) {
  77.       moveY = 0;
  78.       show = false;
  79.       t = 1;
  80.     } else {
  81.       moveY = 200;
  82.       show = true;
  83.       t = 0;
  84.     }
  85.     // this.animation.translate(arr[0], arr[1]).step();
  86.     animationEvents(this,moveY, show);

  87.   },
  88.   //隐藏弹窗浮层
  89.   hiddenFloatView(e){
  90.     console.log(e);
  91.     moveY = 200;
  92.     show = true;
  93.     t = 0;
  94.     animationEvents(this,moveY, show);

  95.   },
  96.   //页面滑至底部事件
  97.   onReachBottom: function () {
  98.     // Do something when page reach bottom.
  99.   }
  100. })

  101. //动画事件
  102. function animationEvents(that,moveY,show){
  103.   console.log("moveY:" + moveY + "\nshow:" + show);
  104.   that.animation = wx.createAnimation({
  105.     transformOrigin: "50% 50%",
  106.     duration: 400,
  107.     timingFunction: "ease",
  108.     delay: 0
  109.   }
  110.   )
  111.   that.animation.translateY(moveY + 'vh').step()

  112.   that.setData({
  113.     animation: that.animation.export(),
  114.     show: show
  115.   })

  116. }

  117. // ---------------- 分割线 ----------------

  118. //获取省份数据
  119. function getProvinceData(that) {
  120.   var s;
  121.   provinces = [];
  122.   var num = 0;
  123.   for (var i = 0; i < areaInfo.length; i++) {
  124.     s = areaInfo[i];
  125.     if (s.di == "00" && s.xian == "00") {
  126.       provinces[num] = s;
  127.       num++;
  128.     }
  129.   }
  130.   that.setData({
  131.     provinces: provinces
  132.   })

  133.   //初始化调一次
  134.   getCityArr(0, that);
  135.   getCountyInfo(0, 0, that);
  136.   that.setData({
  137.     province: "北京市",
  138.     city: "市辖区",
  139.     county: "东城区",
  140.   })

  141. }

  142. // 获取地级市数据
  143. function getCityArr(count, that) {
  144.   var c;
  145.   citys = [];
  146.   var num = 0;
  147.   for (var i = 0; i < areaInfo.length; i++) {
  148.     c = areaInfo[i];
  149.     if (c.xian == "00" && c.sheng == provinces[count].sheng && c.di != "00") {
  150.       citys[num] = c;
  151.       num++;
  152.     }
  153.   }
  154.   if (citys.length == 0) {
  155.     citys[0] = { name: '' };
  156.   }

  157.   that.setData({
  158.     city: "",
  159.     citys: citys,
  160.     value: [count, 0, 0]
  161.   })
  162. }

  163. // 获取区县数据
  164. function getCountyInfo(column0, column1, that) {
  165.   var c;
  166.   countys = [];
  167.   var num = 0;
  168.   for (var i = 0; i < areaInfo.length; i++) {
  169.     c = areaInfo[i];
  170.     if (c.xian != "00" && c.sheng == provinces[column0].sheng && c.di == citys[column1].di) {
  171.       countys[num] = c;
  172.       num++;
  173.     }
  174.   }
  175.   if(countys.length == 0){
  176.     countys[0] = {name:''};
  177.   }
  178.   that.setData({
  179.     county: "",
  180.     countys: countys,
  181.     value: [column0, column1, 0]
  182.   })
  183. }
复制代码

最终效果:
23a7a.gif

本文的难点主要集中在滑动的是哪一列、数据的处理、每次滑动每一列数据的更新数组的定位:
1.滑动第一列: 第二列和第三列位置都置零;更新数组citys和countys。
2.滑动第二列: 第一列不动,第三列置零;更新数组countys。
3.滑动第三列: 第一列和第二列都不动;数据不用更新,改变val[2]即可。

处理不好的话体验会很不好,会有闪跳的感觉。
回复

使用道具 举报

最佳答案
1 

5

主题

56

帖子

1393

积分

专家路上

积分
1393
发表于 2017-8-9 16:54:46 | 显示全部楼层
这个组件真心不错,我已经用上了。也很好用。
里面有一点小遗憾就是目前直辖市只有两级(市-区),实际上直辖市的区是地级市啊。
另外就是港澳台,直接就只有一级。
回复 支持 反对

使用道具 举报

最佳答案
0 

0

主题

25

帖子

315

积分

略知一二

积分
315
发表于 2017-8-23 11:21:29 | 显示全部楼层
鹅鹅鹅鹅鹅鹅饿鹅鹅鹅鹅鹅鹅饿
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


易域网-您身边的域名管家

henkuai.com是专业的第三方微信开发者平台,为生态而生。


本站为第三方微信开发者平台,非腾讯官方网站。

天津市滨海新区
中新生态城中成大道生态建设公寓9号楼3层301

欢迎来这里一起喝喝茶,
聊聊你的产品。

微信公众号gongzhongkaifa

工作日12小时内回复。

广告推广
zhongcong@henkuai.com

工作日12小时内回复。

市场合作
songchang@henkuai.com