微信小程序-省市區(qū)縣三級聯(lián)動選擇器

項目地址:https://github.com/leesonp/littleAPP.git

2018.10.25 更新

很多人反映彈出選擇器控件時,不滑動直接點擊“確定”按鈕沒有值返回顯示。其實默認坐標是[0,0,0]的,只是沒有調(diào)用更新數(shù)據(jù)而已。現(xiàn)已更正。

2017.07.21 17:30 更新
之前版本是從頭寫到尾沒有封裝的,有的同學可能看的一臉懵逼,昨天我用微信官方提供的WXML模板(template)對代碼進行了封裝,在模板中定義代碼片段,通過封裝以后我們就可以在不同的地方調(diào)用了。
Demo已上傳到github。

文件組成:
1.模板類(model)
2.舊版未封裝類 (index)
3.新版調(diào)用類 (modelTest)
4.數(shù)據(jù)源 (area.js)

怎么使用呢?
只需在要用到的類中導入模板模板,引入對應后綴的文件,做一些簡單必要操作即可。
以上傳的Demo為例:

// modelTest.js
var model = require('../../model/model.js')

var show = false;
var item = {};

Page({
  data: {
    item: {
      show: show
    }
  },
   //生命周期函數(shù)--監(jiān)聽頁面初次渲染完成
  onReady: function (e) {
    var that = this;
    //請求數(shù)據(jù)
    model.updateAreaData(that, 0, e);
  },
  //點擊選擇城市按鈕顯示picker-view
  translate: function (e) {
    model.animationEvents(this, 0, true,400);  
  },
  //隱藏picker-view
  hiddenFloatView: function (e) {
    model.animationEvents(this, 200, false,400);
  },
  //滑動事件
  bindChange: function (e) {
    model.updateAreaData(this, 1, e);
    item = this.data.item;
    this.setData({
      province: item.provinces[item.value[0]].name,
      city: item.citys[item.value[1]].name,
      county: item.countys[item.value[2]].name
    });
  },
  onReachBottom: function (){
  },
  nono: function (){}
})
<!--modelTest.wxml-->
<import src="../../model/model.wxml"/>
<view class="infoText">{{province}} {{city}} {{county}}</view>
<button class="animation-button" bindtap="translate">選擇城市</button>
<template is="areaData" data="{{...item}}"/>
/* modelTest.wxss */
@import '../../model/model.wxss'

在model.js文件中我就暴露了兩個接口,引入模板后,全部代碼就這些。是不是感覺舒服很多?
代碼簡潔、邏輯清晰,是封裝給我們帶來的好處,希望沒試過封裝的同學可以嘗試下。

-- END --

------------------------------ 分割線 ------------------------------
2017.07.17 更新
本人之前一直從事iOS開發(fā),最近無項目閑來無事研究了下微信小程序。
我沒有像從新學一門語言一樣從頭到尾看一遍文檔,我個人感覺小程序類似前端開發(fā),正好之前對前端也略有涉獵,所以直接就拿我之前iOS的項目的某個模塊練手,不懂的再去查閱官方文檔。
因為有個創(chuàng)建紅包的界面,UITableView的某個cell點擊會彈出選擇區(qū)域和時間選擇器,下面就給大家講一下小程序版是怎樣的一種操作。

圖1 iOS版

在iOS中我用的是UIPickView,體驗還OK。那在小程序中選什么控件呢?查閱官方文檔,有兩種滑動選擇器

圖2 官方列出的兩種滑動選擇器

picker:從底部彈起的滾動選擇器,現(xiàn)支持三種選擇器,通過mode來區(qū)分,分別是普通選擇器,時間選擇器,日期選擇器,默認是普通選擇器。因為是固定的,直接pass掉了。但是最開始我以為只有這一種,然后想去百度找找有沒有自定義的。結(jié)果發(fā)現(xiàn)幾乎網(wǎng)上沒有很好的區(qū)域選擇器的方案。我就再回過頭來查閱官方文檔發(fā)現(xiàn)還有個picker-view。
picker-view:嵌入頁面的滾動選擇器。官方的示例咋一看還是時間選擇器,以為也是定死的呢。但是看代碼就可以發(fā)現(xiàn),其實跟iOS的寫法邏輯相似。
既然相似我們就可以著手寫代碼了。但是開始寫之前首先我們要拿到省市區(qū)縣的數(shù)據(jù)。網(wǎng)上搜索下中國行政區(qū)域可以找到很多,我選了這個。下載下來是txt文件的Json數(shù)據(jù)。那怎么導入工程呢? 有兩種方法:
1.在utils文件夾下新建一個js文件,寫一個函數(shù)把txt里的json數(shù)據(jù)復制粘貼進去,然后在需要的類目下調(diào)用這個函數(shù)接收返回值就行了。

//area.js
function getAreaInfo(callBack){
var str =["此處粘貼json數(shù)據(jù)":... ];//因數(shù)據(jù)量太大就不在此處全部展示,知道是個數(shù)組就行了。

callBack(str);
}
module.exports.getAreaInfo = getAreaInfo;
------------------------------ 分割線 ------------------------------
//index.js
var area = require('../../utils/area.js')
...
Page({
  data: { },
onLoad: function (options) {
    var that = this;
    //獲取省市區(qū)縣數(shù)據(jù)
    area.getAreaInfo(function (arr) {
    });

  }
})
...

2.寫一個接口調(diào)用,我自己就是寫的一個PHP接口。

//php代碼
$filename = "ChinaArea.txt";//網(wǎng)絡(luò)上下載的文件(PHP或Java需要配置環(huán)境才能請求到數(shù)據(jù))。
$json_string = file_get_contents($filename);
echo print_r($json_string,true);

個人建議還是不要把數(shù)據(jù)源直接寫在項目,因為小程序包是有大小限制的如若超過是無法提交的所以建議寫接口,或者拿別人現(xiàn)成的接口。

話不多說直接貼代碼吧。(代碼已做必要注釋)

<!--index.wxml-->

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

  <view class="aaaa" >
  <button class="animation-button" bindtap="translate">選擇城市</button>
  </view>
      
  <view class="animation-element-wrapper" animation="{{animation}}" style="visibility:{{show ? 'visible':'hidden'}}" bindtap="hiddenFloatView" data-id="444">
     <view class="animation-element" catchtap="nono">
        <text class="left-bt" catchtap="hiddenFloatView" data-id="555">取消</text>
        <text class="right-bt" catchtap="hiddenFloatView" data-id="666">確定</text>
          <view class="line"></view> 

        <picker-view indicator-style = "height: 50rpx;" value="{{value}}" bindchange="bindChange" catchtap="nono">
        <!--省-->
        <picker-view-column>
           <view wx:for="{{provinces}}" wx:key="" >
             {{item.name}}
          </view>
        </picker-view-column>
        <!--地級市-->
        <picker-view-column>
          <view wx:for="{{citys}}" wx:key="" >
            {{item.name}}
          </view>
        </picker-view-column>
        <!--區(qū)縣-->
        <picker-view-column>
          <view wx:for="{{countys}}" wx:key="" >
            {{item.name}}
          </view>
        </picker-view-column>
        </picker-view>
    </view>
  </view>
/**index.wxss**/
page{
  background-color: rgba(255, 255, 255, 1); 
}

.infoText{
    margin-top: 20rpx;
    text-align: center;
    width: 100%;
    justify-content: center; 
}

picker-view{
  background-color: white;
  padding: 0;
  width: 100%; 
  height: 380rpx;
  bottom: 0;
  position: fixed;
}

picker-view-column view{
  vertical-align:middle; 
  font-size: 28rpx;
  line-height: 28rpx;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ----------------------------------------- */

.animation-element-wrapper {
  display: flex;  
  position: fixed;
  left: 0;
  top:0;
  height: 100%;
  width: 100%;
  background-color: rgba(0, 0, 0, 0.6);
}
.animation-element {
  display: flex;
  position: fixed;
  width: 100%;
  height: 470rpx;
  bottom: 0;
  background-color: rgba(255, 255, 255, 1);
}

.animation-button {
  top:20rpx;
  width: 290rpx;
  height: 100rpx;  
  align-items:center;
}


text{
  color: #999999;
  display: inline-flex;  
  position: fixed;
  margin-top: 20rpx;
  height: 50rpx;
  text-align: center;
  line-height: 50rpx;
  font-size: 34rpx;
  font-family: Arial, Helvetica, sans-serif;
}

.left-bt{
  left: 30rpx;
}
.right-bt {
  right: 30rpx;
}

.line{
  display: block;
  position: fixed;
  height: 1rpx;
  width: 100%;
  margin-top: 89rpx; 
  background-color: #eeeeee;
}
//index.js
//獲取應用實例
var area = require('../../utils/area.js')

var areaInfo = [];//所有省市區(qū)縣數(shù)據(jù)

var provinces = [];//省

var citys = [];//城市

var countys = [];//區(qū)縣

var index = [0, 0, 0];

var cellId;

var t = 0;
var show = false;
var moveY = 200;

Page({
  data: {
    show: show,
    provinces: provinces,
    citys: citys,
    countys: countys,
    value: [0, 0, 0]
  },
  //滑動事件
  bindChange: function (e) {
    var val = e.detail.value

    //判斷滑動的是第幾個column
    //若省份column做了滑動則定位到地級市和區(qū)縣第一位
    if (index[0] != val[0]) {
      val[1] = 0;
      val[2] = 0;
      getCityArr(val[0], this);//獲取地級市數(shù)據(jù)
      getCountyInfo(val[0], val[1], this);//獲取區(qū)縣數(shù)據(jù)
    } else {    //若省份column未做滑動,地級市做了滑動則定位區(qū)縣第一位
      if (index[1] != val[1]) {
        val[2] = 0;
        getCountyInfo(val[0], val[1], this);//獲取區(qū)縣數(shù)據(jù)
      }
    }
    index = val;

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

    //更新數(shù)據(jù)
    this.setData({
      value: [val[0], val[1], val[2]],
      province: provinces[val[0]].name,
      city: citys[val[1]].name,
      county: countys[val[2]].name
    })

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

    //獲取省市區(qū)縣數(shù)據(jù)
    area.getAreaInfo(function (arr) {
      areaInfo = arr;
      //獲取省份數(shù)據(jù)
      getProvinceData(that);
    });

  },
  // ------------------- 分割線 --------------------
  onReady: function () {
    this.animation = wx.createAnimation({
      transformOrigin: "50% 50%",
      duration: 0,
      timingFunction: "ease",
      delay: 0
    }
    )
    this.animation.translateY(200 + 'vh').step();
    this.setData({
      animation: this.animation.export(),
      show: show
    })
  },
  //移動按鈕點擊事件
  translate: function (e) {
    if (t == 0) {
      moveY = 0;
      show = false;
      t = 1;
    } else {
      moveY = 200;
      show = true;
      t = 0;
    }
    // this.animation.translate(arr[0], arr[1]).step();
    animationEvents(this,moveY, show);
    
  },
  //隱藏彈窗浮層
  hiddenFloatView(e){
    console.log(e);
    moveY = 200;
    show = true;
    t = 0;
    animationEvents(this,moveY, show);

  },
  //頁面滑至底部事件
  onReachBottom: function () {
    // Do something when page reach bottom.
  }
})

//動畫事件
function animationEvents(that,moveY,show){
  console.log("moveY:" + moveY + "\nshow:" + show);
  that.animation = wx.createAnimation({
    transformOrigin: "50% 50%",
    duration: 400,
    timingFunction: "ease",
    delay: 0
  }
  )
  that.animation.translateY(moveY + 'vh').step()

  that.setData({
    animation: that.animation.export(),
    show: show
  })

}

// ---------------- 分割線 ---------------- 

//獲取省份數(shù)據(jù)
function getProvinceData(that) {
  var s;
  provinces = [];
  var num = 0;
  for (var i = 0; i < areaInfo.length; i++) {
    s = areaInfo[i];
    if (s.di == "00" && s.xian == "00") {
      provinces[num] = s;
      num++;
    }
  }
  that.setData({
    provinces: provinces
  })

  //初始化調(diào)一次
  getCityArr(0, that);
  getCountyInfo(0, 0, that);
  that.setData({
    province: "北京市",
    city: "市轄區(qū)",
    county: "東城區(qū)",
  })

}

// 獲取地級市數(shù)據(jù)
function getCityArr(count, that) {
  var c;
  citys = [];
  var num = 0;
  for (var i = 0; i < areaInfo.length; i++) {
    c = areaInfo[i];
    if (c.xian == "00" && c.sheng == provinces[count].sheng && c.di != "00") {
      citys[num] = c;
      num++;
    }
  }
  if (citys.length == 0) {
    citys[0] = { name: '' };
  }

  that.setData({
    city: "",
    citys: citys,
    value: [count, 0, 0]
  })
}

// 獲取區(qū)縣數(shù)據(jù)
function getCountyInfo(column0, column1, that) {
  var c;
  countys = [];
  var num = 0;
  for (var i = 0; i < areaInfo.length; i++) {
    c = areaInfo[i];
    if (c.xian != "00" && c.sheng == provinces[column0].sheng && c.di == citys[column1].di) {
      countys[num] = c;
      num++;
    }
  }
  if(countys.length == 0){
    countys[0] = {name:''};
  }
  that.setData({
    county: "",
    countys: countys,
    value: [column0, column1, 0]
  })
}

最終效果:

3. 最終效果

本文的難點主要集中在滑動的是哪一列、數(shù)據(jù)的處理、每次滑動每一列數(shù)據(jù)的更新數(shù)組的定位:
1.滑動第一列: 第二列和第三列位置都置零;更新數(shù)組citys和countys。
2.滑動第二列: 第一列不動,第三列置零;更新數(shù)組countys。
3.滑動第三列: 第一列和第二列都不動;數(shù)據(jù)不用更新,改變val[2]即可。

處理不好的話體驗會很不好,會有閃跳的感覺。

項目地址:https://github.com/leesonp/littleAPP.git

這是我第一次寫文章,寫的不好或者有紕漏的地方,希望大家多多包容、指正,謝謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容