uniapp省市区三级联动简书

admin 104 0
本文基于uniapp实现省市区三级联动功能,核心采用picker组件联动机制,通过本地JSON数据源高效加载各级区域信息,实现过程中,监听省份选择事件动态加载对应城市数据,再监听城市选择事件动态加载区县数据,形成完整联动链路,同时优化数据加载逻辑,采用异步缓存策略减少重复请求,确保切换流畅性,最终实现用户交互友好、数据准确的省市区三级选择功能,适用于各类地址选择场景,为uniapp开发者提供可直接复用的实践方案。

Uniapp 省市区三级联动实战:从零教你轻松搞定地址选择功能

在电商、物流、表单填写等业务场景中,省市区三级联动选择器是高频需求组件,本文将基于Uniapp框架,结合实际开发经验,详细讲解如何实现一个流畅、易用的省市区三级联动组件,涵盖数据获取、组件联动、交互优化等关键环节,适合初学者快速上手。

前置准备:明确需求与技术方案

需求拆解

在开始实现之前,我们需要明确核心需求点:

  1. 三级联动机制:实现省→市→区的级联选择,选择省级后动态加载对应市级数据,选择市级后动态加载对应区级数据;
  2. 数据准确性:采用权威的省市区数据源,确保地址信息准确无误;
  3. 交互友好性:支持默认选中当前地区(可选)、加载状态提示、滚动选择体验优化;
  4. 跨平台兼容:确保组件能在App、H5、小程序等多端正常运行。

技术选型

根据需求特点,我们选择以下技术方案:

  • 组件选择:使用Uniapp原生picker组件,它支持多列联动且具有良好的多端兼容性;
  • 数据源:采用开源的china-area-data数据集,包含全国省市区层级数据,JSON格式便于解析;
  • 数据管理:将数据存放在本地静态资源中,避免接口请求,提升加载速度和用户体验;
  • 组件封装:将三级联动功能封装为可复用的Vue组件,提高代码复用性。

数据准备:获取并整理省市区数据

数据源下载

china-area-data官方仓库(或国内镜像源)下载最新版JSON数据,该数据集包含三个层级:

  • province_list:省级数据(如 "北京市": "110000");
  • city_list:市级数据(如 "北京市": "110100",对应省级代码 "110000");
  • area_list:区级数据(如 "东城区": "110101",对应市级代码 "110100")。

数据存放

将下载的JSON文件存放在Uniapp项目的static目录下(如static/area-data/),确保打包后能被正确访问,建议在项目中创建专门的资源目录,便于管理和维护。

数据格式化

原始数据是"名称-代码"键值对格式,需要转换为picker组件所需的数组格式([{text: '北京市', value: '110000'}]),我们封装一个工具函数formatAreaData.js来处理数据转换:

// utils/formatAreaData.js
export function formatAreaData(data) {
  return Object.keys(data).map(key => ({
    text: key,
    value: data[key]
  }))
}
// 加载本地JSON数据
export function loadAreaData() {
  return new Promise((resolve) => {
    // 通过uni.request读取本地静态文件(H5端可用import,但多端兼容用request)
    uni.request({
      url: '/static/area-data/china-area-data.json',
      success: (res) => {
        const { province_list, city_list, area_list } = res.data
        resolve({
          provinces: formatAreaData(province_list),
          cities: formatAreaData(city_list),
          areas: formatAreaData(area_list)
        })
      },
      fail: () => {
        console.error('省市区数据加载失败')
        resolve({ provinces: [], cities: [], areas: [] })
      }
    })
  })
}

注意事项

  • 确保JSON文件路径正确,特别是在不同平台(H5、App、小程序)上的访问方式可能不同;
  • 考虑数据更新机制,可以定期检查并更新数据源;
  • 对于大型项目,可以考虑将数据按需加载,减少初始包体积。

组件实现:三级联动核心代码

组件结构设计

创建area-picker.vue组件,包含以下部分:

  • 模板:使用picker组件绑定三列数据,监听选择变化事件;
  • 逻辑:处理数据加载、选中状态管理、动态更新子级数据;
  • 样式:优化picker高度、字体大小等视觉体验。

完整代码实现

<!-- components/area-picker.vue -->
<template>
  <view class="area-picker">
    <picker
      mode="multiSelector"
      :range="columns"
      :value="indexArray"
      @change="handleChange"
      @columnchange="handleColumnChange"
      :disabled="disabled"
    >
      <view class="picker-input" :class="{ 'placeholder': !selectedText }">
        {{ selectedText || placeholder }}
      </view>
    </picker>
  </view>
</template>
<script>
import { loadAreaData } from '@/utils/formatAreaData'
export default {
  name: 'AreaPicker',
  props: {
    // 默认选中地区(格式:['北京市', '110000', '北京市', '110100', '东城区', '110101'])
    defaultValue: {
      type: Array,
      default: () => []
    },
    // 占位文本
    placeholder: {
      type: String,
      default: '请选择省/市/区'
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      columns: [[], [], []], // 三列数据(省、市、区)
      indexArray: [0, 0, 0], // 三列选中索引
      selectedData: [], // 选中数据([省, 市, 区])
      areaInfo: { // 存储原始数据,通过code查询
        provinces: [],
        cities: {},
        areas: {}
      }
    }
  },
  computed: {
    // 选中后的展示文本
    selectedText() {
      return this.selectedData.length === 3 ? this.selectedData.join(' ') : ''
    }
  },
  async mounted() {
    await this.initAreaData()
    if (this.defaultValue.length) {
      this.setDefaultValues()
    }
  },
  methods: {
    // 初始化数据
    async initAreaData() {
      const { provinces, cities, areas } = await loadAreaData()
      this.areaInfo.provinces = provinces
      this.areaInfo.cities = cities
      this.areaInfo.areas = areas
      this.columns[0] = provinces
      // 默认加载第一列省份对应的城市
      if (provinces.length) {
        this.updateCities(provinces[0].value)
      }
    },
    // 设置默认选中值
    setDefaultValues() {
      const [provinceName, provinceCode, cityName, cityCode, areaName, areaCode] = this.defaultValue
      const provinceIndex = this.areaInfo.provinces.findIndex(p => p.value === provinceCode)
      if (provinceIndex !== -1) {
        this.indexArray[0] = provinceIndex
        this.updateCities(provinceCode)
        this.$nextTick(() => {
          const cityIndex = this.areaInfo.cities[provinceCode].findIndex(c => c.value === cityCode)
          if (cityIndex !== -1) {
            this.indexArray[1] = cityIndex
            this.updateAreas(cityCode)
            this.$nextTick(() => {
              const areaIndex = this.areaInfo.areas[cityCode].findIndex(a => a.value === areaCode)
              if (areaIndex !== -1) {
                this.indexArray[2] = areaIndex
                this.updateSelectedData()
              }
            })
          }
        })
      }
    },
    // 更新城市数据
    updateCities(provinceCode) {
      const cities = this.areaInfo.cities[provinceCode] || []
      this.columns[1

标签: #联动简书