擼個(gè)微信小程序的省市區(qū)選擇器

起因

微信小程序雖然已經(jīng)有現(xiàn)成的封裝好的省市區(qū)選擇器給開發(fā)者使用摊溶,然鵝不幸的是,微信地址庫(kù)的數(shù)據(jù)和公司用的地址庫(kù)數(shù)據(jù)很難一一對(duì)上玖雁,那就只能擼起袖子自己寫個(gè)組件了更扁。

最終效果

最終效果

思維導(dǎo)圖

思維導(dǎo)圖

主要代碼

組件 region-picker.js

/* region-picker.js */
import area from '本地 json 數(shù)據(jù)';
Component({
  properties: {
    showRegion: {
      type: Boolean,
      observer: function(newVal, oldVal) {
        this.setData({
          dialog: newVal,
        });
      },
    },
    regionValue: {
      type: Array,
      value: [],
      observer: function(newVal, oldVal) {
        if (newVal.length > 0) {
          let select = -1;
          for (let i = newVal.length - 1; i >= 0; i--) {
            if (newVal[i].id !== '') {
              select = i;
              break;
            }
          }
          // 除最低級(jí)別區(qū)(select = 2)以外,需要獲取當(dāng)前級(jí)別下一級(jí)的數(shù)據(jù)
          this.setData({
            ['region.tabs']: newVal,
            ['region.select']: select < 2 ? select+1 : select,
          }, () => {
            this.setData({
              area: this.getChildArea(select < 2 ? select+1 : select),
            });
          });
        }
      },
    },
  },
  data: {
    dialog: false,
    area: area,
    region: {
      tabs: [
        {
          name: '請(qǐng)選擇',
          id: '',
        },
        {
          name: '請(qǐng)選擇',
          id: '',
        },
        {
          name: '請(qǐng)選擇',
          id: '',
        },
      ],
      select: 0,
    },
  },
  methods: {
    // 關(guān)閉 picker 觸發(fā)的方法
    emitHideRegion: function() {
      if (this.data.region.tabs[2].id === '') {
        wx.showToast({
          title: '請(qǐng)選擇所在地',
          icon: 'none',
          duration: 2000,
        });
        return false;
      }
      let myEventDetail = {}; // detail對(duì)象赫冬,提供給事件監(jiān)聽函數(shù)
      let myEventOption = {}; // 觸發(fā)事件的選項(xiàng)
      this.setData({
        dialog: !this.data.dialog,
      });
      myEventDetail = {
        showRegion: this.data.dialog,
        regionValue: this.data.region.tabs,
      };
      this.triggerEvent('myevent', myEventDetail, myEventOption);
    },
    bindRegionChange: function(e) {
      // 獲取當(dāng)前選中項(xiàng)的name和id并賦值給data中的數(shù)據(jù)
      let id ='region.tabs[' + this.data.region.select + '].id';
      let name ='region.tabs[' + this.data.region.select + '].name';
      this.setData({
        [id]: e.target.dataset.id,
        [name]: e.target.dataset.name,
      });
      // 除了三級(jí)以外的需要獲取對(duì)應(yīng)子選項(xiàng)
      if (this.data.region.select < 2) {
        this.setData({
          ['region.select']: ++this.data.region.select,
        }, () => {
          // 獲取子選項(xiàng)
          this.setData({
            area: this.getChildArea(this.data.region.select),
          });
        });
      } else {
        // 三級(jí)選項(xiàng)選擇完畢關(guān)閉省市區(qū)選擇器
        this.emitHideRegion();
      }
    },
    getChildArea: function(level) {
      let _id = '';
      // 默認(rèn)取完整的數(shù)據(jù)
      let _area = area;
      // 根據(jù)層級(jí)取當(dāng)前層級(jí)下的數(shù)據(jù)
      for (let i = 0; i < level; i++) {
        _id = this.data.region.tabs[i].id;
        for (let j = 0; j < _area.length; j++) {
          if (_area[j].id === _id) {
            _area = _area[j]._child;
            break;
          }
        }
      }
      return _area;
    },
    // 省市區(qū)tab切換
    changeRegionLevel: function(e) {
      let level = e.target.dataset.level;
      // 三級(jí)選項(xiàng)的tab點(diǎn)擊無效果
      if (level === 2) return false;
      // 當(dāng)前選中tab和級(jí)別小于當(dāng)前選中tab的狀態(tài)都置為初始化狀態(tài)
      for (let i = level; i < 3; i++) {
        let string = 'region.tabs['+ i +']';
        this.setData({
          [string]: {
            name: '請(qǐng)選擇',
            id: '',
          },
        });
      }
      this.setData({
        ['region.select']: level,
      });
      this.setData({
        area: this.getChildArea(level),
      });
    },
  },
});

組件 region-picker.wxml

/* region-picker.wxml */
<view class="free-dialog {{dialog ? 'free-dialog--show' : ''}}">
    <view class="free-dialog__mask" bindtap="emitHideRegion"></view>
    <view class="free-dialog__container">
        <view class="free-dialog__container__header">
            <view>選擇所在地區(qū)</view>
            <image
                src="自行替換36rpx*36rpx的x圖標(biāo)"
                class="close"
                bindtap="emitHideRegion">
            </image>
        </view>
        <view class="free-dialog__container__content">
            <view class="free-content {{isIphoneX ? 'ipx' : ''}}">
                <view class="free-content__tabs">
                    <view
                        class="free-content__tabs__tab {{region.select === index ? 'select' : ''}}"
                        wx:for="{{region.tabs}}"
                        wx:key="{{index}}"
                        wx:if="{{index <= region.select}}"
                        data-level="{{index}}"
                        bindtap="changeRegionLevel">
                        {{item.name}}
                    </view>
                </view>
                <scroll-view scroll-y class="free-content__scroll">
                    <view
                        class="free-content__scroll__item"
                        wx:for="{{area}}"
                        wx:key="id"
                        data-id="{{item.id}}"
                        data-name="{{item.name}}"
                        bindtap="bindRegionChange">
                        {{item.name}}
                    </view>
                </scroll-view>
            </view>
        </view>
    </view>
</view>

組件 region-picker.wxss

/* region-picker.wxss */
.free-dialog__mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  background: rgba(0, 0, 0, 0.7);
  display: none;
}
.free-dialog__container {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  background: #F1F1F1;
  transform: translateY(150%);
  transition: all 0.4s ease;
  z-index: 11;
}
.free-dialog--show .free-dialog__container {
  transform: translateY(0);
}
.free-dialog--show .free-dialog__mask {
  display: block;
}
.free-dialog__container__header {
  padding: 24rpx 30rpx;
  text-align: center;
  background: white;
}
.free-dialog__container__header .close {
  position:absolute;
  right:30rpx;
  top:31rpx;
  width:36rpx;
  height:36rpx;
}
.free-content {
  background: white;
  border-bottom: 40rpx solid white;
}
.free-content.ipx {
  border-bottom: 72rpx solid white;
}
.free-content__tabs__tab {
  display: inline-block;
  padding: 12rpx 46rpx;
  font-size: 32rpx;
  color: #333;
  border-bottom: 4rpx solid white;
}
.free-content__tabs__tab.select {
  border-color: #FA263C;
}
.free-content__scroll {
  padding: 0 40rpx;
  height: 480rpx;
  box-sizing: border-box;
}
.free-content__scroll__item {
  margin-top: 40rpx;
  height: 40rpx;
  line-height: 40rpx;
  font-size: 28rpx;
  color: #333;
}

頁(yè)面的 WXML

/* 頁(yè)面的 WXML */
<view bindtap="chooseRegion">請(qǐng)選擇</view>
<view>
    <text wx:if="{{regionValue[0].id}}">{{regionValue[0].name}}</text>
    <text wx:if="{{regionValue[1].id}}">{{regionValue[1].name}}</text>
    <text wx:if="{{regionValue[2].id}}">{{regionValue[2].name}}</text>
</view>
...
<region-picker
    region-value="{{regionValue}}"
    show-region="{{showRegion}}"
    bind:myevent="emitHideRegion">
</region-picker>

頁(yè)面的 js

/* 頁(yè)面的 js */
Page({
  data: {
    regionValue: [],
    showRegion: false,
  },
  chooseRegion: function() {
    this.setData({
      showRegion: true,
    });
  },
  emitHideRegion: function(e) {
    this.setData({
      showRegion: e.detail.showRegion,
      regionValue: e.detail.regionValue,
    });
  },
});

總結(jié)

需要注意下的是浓镜,最低級(jí)別區(qū)級(jí)別是個(gè)特殊的臨界點(diǎn),因?yàn)閰^(qū)后面沒有更低級(jí)別劲厌,所以不需要獲取下一級(jí)別的數(shù)據(jù)膛薛,也不能觸發(fā) tab 事件。

然后父組件傳遞子自組件的值补鼻,如果后期父組件變更了這個(gè)值哄啄,子組件可以在響應(yīng)函數(shù) observer 里監(jiān)聽到值的變化。

我本次使用的本地省市區(qū) JSON 數(shù)據(jù)格式為:

/* area.js */
module.exports = [{
    id: '...',
    name: '...',
    _child: [{
        id: '...',
        name: '...',
        _child: [{
            id: '...',
            name: '...'
        }, ...]
    }, ...]
}, ...]

寫的不是特別好风范,也希望能幫助到有需要的人吧咨跌,有疑問戳微信小程序官方文檔,沒有什么比官方文檔更靠譜的了硼婿!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锌半,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寇漫,更是在濱河造成了極大的恐慌刊殉,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件州胳,死亡現(xiàn)場(chǎng)離奇詭異记焊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)栓撞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門遍膜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓤湘,你說我怎么就攤上這事捌归。” “怎么了岭粤?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)特笋。 經(jīng)常有香客問我剃浇,道長(zhǎng)巾兆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任虎囚,我火速辦了婚禮角塑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淘讥。我一直安慰自己圃伶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布蒲列。 她就那樣靜靜地躺著窒朋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝗岖。 梳的紋絲不亂的頭發(fā)上侥猩,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音抵赢,去河邊找鬼欺劳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛铅鲤,可吹牛的內(nèi)容都是我干的划提。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼邢享,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鹏往!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驼仪,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤掸犬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后绪爸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體湾碎,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年奠货,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了介褥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡递惋,死狀恐怖柔滔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萍虽,我是刑警寧澤睛廊,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站杉编,受9級(jí)特大地震影響超全,放射性物質(zhì)發(fā)生泄漏咆霜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一嘶朱、第九天 我趴在偏房一處隱蔽的房頂上張望蛾坯。 院中可真熱鬧,春花似錦疏遏、人聲如沸脉课。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)倘零。三九已至,卻和暖如春宝当,著一層夾襖步出監(jiān)牢的瞬間视事,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工庆揩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俐东,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓订晌,卻偏偏與公主長(zhǎng)得像虏辫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锈拨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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