原创

微信小程序自定义数据实现级联省市区组件功能

温馨提示:
本文最后更新于 2025年07月16日,已超过 58 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

在微信小程序中,省市区三级联动是常见的功能需求。一种轻量、高效且不依赖第三方组件的实现方式是使用自定义组件和本地 JSON 数据来驱动联动逻辑。本文将从数据准备、组件设计、逻辑处理三方面全面讲解如何实现该功能。


效果图

file

✨ 一、准备数据源

我们用一份结构清晰的 JSON 文件来定义所有省、市、区信息,例如 regions.json

[
  { "id": "11", "name": "北京市", "parent": "0" },
  { "id": "1101", "name": "北京市市辖区", "parent": "11" },
  { "id": "110101", "name": "东城区", "parent": "1101" },
  // ...
  { "id": "31", "name": "上海市", "parent": "0" },
  { "id": "3101", "name": "上海市市辖区", "parent": "31" },
  { "id": "310101", "name": "黄浦区", "parent": "3101" }
]
`
  • id:每一条的唯一编码
  • name:显示名称
  • parent:关联层级,上级编码为 0 表示顶级省份

保存到项目 data/regions.json 中,便于后续调用。


🛠 二、自定义组件设计

components/area-picker/ 下创建 area-picker 组件,包含:

  • area-picker.json

    {
      "component": true
    }
    
  • area-picker.wxml

    <!-- 三列 Picker 用于省市区选择 -->
    <view class="area-picker">
      <picker mode="selector" range="{{provinceList}}" value="{{provinceIndex}}" bindchange="onProvinceChange">
        <view>{{provinceList[provinceIndex]}}</view>
      </picker>
      <picker mode="selector" range="{{cityList}}" value="{{cityIndex}}" bindchange="onCityChange">
        <view>{{cityList[cityIndex]}}</view>
      </picker>
      <picker mode="selector" range="{{districtList}}" value="{{districtIndex}}" bindchange="onDistrictChange">
        <view>{{districtList[districtIndex]}}</view>
      </picker>
    </view>
    
  • area-picker.wxss

    .area-picker {
      display: flex;
      justify-content: space-between;
    }
    picker {
      flex: 1;
      margin: 0 5px;
    }
    
  • area-picker.js

    import regions from '../../data/regions.json';
    
    Component({
      properties: {
        value: {
          type: Object,
          value: { provinceId: '', cityId: '', districtId: '' }
        }
      },
      data: {
        provinceList: [],
        cityList: [],
        districtList: [],
        provinceIndex: 0,
        cityIndex: 0,
        districtIndex: 0
      },
      lifetimes: {
        attached() {
          this.initData();
        }
      },
      methods: {
        initData() {
          const provinces = regions.filter(r => r.parent === '0');
          const provinceList = provinces.map(r => r.name);
          this.setData({ regionData: regions, provinceList });
          this.updateCities(0);
        },
        updateCities(provIdx) {
          const provinces = this.data.regionData.filter(r => r.parent === '0');
          const seleProv = provinces[provIdx];
          const cities = this.data.regionData.filter(r => r.parent === seleProv.id);
          const cityList = cities.map(r => r.name);
          this.setData({ cityList, cityIndex: 0 });
          this.updateDistricts(cities, 0);
        },
        updateDistricts(cities, cityIdx) {
          if (!cities.length) return this.setData({ districtList: [] });
          const seleCity = cities[cityIdx];
          const districts = this.data.regionData.filter(r => r.parent === seleCity.id);
          const districtList = districts.map(r => r.name);
          this.setData({ districtList, districtIndex: 0 });
        },
        onProvinceChange(e) {
          const index = e.detail.value;
          this.setData({ provinceIndex: index });
          this.updateCities(index);
          this.triggerPick();
        },
        onCityChange(e) {
          const index = e.detail.value;
          const provs = this.data.regionData.filter(r => r.parent === '0');
          const selProv = provs[this.data.provinceIndex];
          const cities = this.data.regionData.filter(r => r.parent === selProv.id);
          this.setData({ cityIndex: index });
          this.updateDistricts(cities, index);
          this.triggerPick();
        },
        onDistrictChange(e) {
          const index = e.detail.value;
          this.setData({ districtIndex: index });
          this.triggerPick();
        },
        triggerPick() {
          const provs = this.data.regionData.filter(r => r.parent === '0');
          const selProv = provs[this.data.provinceIndex];
          const cities = this.data.regionData.filter(r => r.parent === selProv.id);
          const selCity = cities[this.data.cityIndex];
          const districts = this.data.regionData.filter(r => r.parent === selCity.id);
          const selDistrict = districts[this.data.districtIndex] || {};
          this.triggerEvent('change', {
            provinceId: selProv.id,
            provinceName: selProv.name,
            cityId: selCity.id,
            cityName: selCity.name,
            districtId: selDistrict.id || '',
            districtName: selDistrict.name || ''
          });
        }
      }
    });
    

💡 三、实际页面中的调用示例

在页面 pages/index/index.wxml 调用组件:

<area-picker value="{{selectedArea}}" bindchange="onAreaChange" />
<text>当前选择:{{selectedArea.provinceName}} - {{selectedArea.cityName}} - {{selectedArea.districtName}}</text>

index.js 中:

Page({
  data: {
    selectedArea: {
      provinceId: '', cityId: '', districtId: '',
      provinceName: '', cityName: '', districtName: ''
    }
  },
  onAreaChange(e) {
    this.setData({ selectedArea: e.detail });
  }
});

效果图

file
file


✅ 小结

  1. 自定义 JSON 数据源:避免调用接口,加载快,操作自由。
  2. 组件封装:分离视图和业务,复用更高。
  3. 联动逻辑:根据父节点变更动态更新下级列表。

通过以上方法,即可实现一个轻量、可维护、适配线下环境的省市区三级联动组件。欢迎分享与交流!


正文到此结束
本文目录