《省市区三级联动开发指南:数据接口设计与下拉菜单联动实现》

效果图示意

代码截图示意

实现需求描述

好的,我重新为您总结一份专注于三级联动核心逻辑的笔记:

省市区三级联动笔记

1. 核心数据结构

@State range: string[][] = [['北京'], ['北京市'], ['东城区']]  // TextPicker数据源
@State selected: number[] = [0, 0, 0]                        // 控制高亮位置
@State values: string[] = ['北京', '北京市', '东城区']         // 实际选中的值
@State timeID: number = 0                                    // 防抖定时器

2. 联动触发机制

TextPicker的onChange事件

.onChange((value) => {
  const _pname = value[0] // 当前高亮的省份
  const _cname = value[1] // 当前高亮的城市  
  const _aname = value[2] // 当前高亮的地区
  
  // 防抖处理
  clearTimeout(this.timeID)
  this.timeID = setTimeout(async () => {
    // 三级联动逻辑
  }, 300)
})

3. 三级联动核心逻辑

第一级:省份变更联动

if (this.values[0] !== value[0]) {
  // 1. 更新省份显示值
  this.values[0] = _pname
  
  // 2. 重新获取城市数据
  const city = await this.req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent(this.values[0])}`)
  this.range[1] = (JSON.parse(city.result.toString()) as IResponse).list
  this.values[1] = this.range[1][0] // 重置为第一个城市
  
  // 3. 重新获取地区数据
  const area = await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.values[0])}&cname=${encodeURIComponent(this.values[1])}`)
  this.range[2] = (JSON.parse(area.result.toString()) as IResponse).list
  this.values[2] = this.range[2][0] // 重置为第一个地区
  
  // 4. 重置下级高亮位置
  this.selected[1] = 0 // 城市高亮重置
  this.selected[2] = 0 // 地区高亮重置
  return
}

第二级:城市变更联动

if (this.values[1] !== value[1]) {
  // 1. 更新城市显示值
  this.values[1] = _cname
  
  // 2. 重新获取地区数据
  const area = await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.values[0])}&cname=${encodeURIComponent(this.values[1])}`)
  this.range[2] = (JSON.parse(area.result.toString()) as IResponse).list
  this.values[2] = this.range[2][0] // 重置为第一个地区
  
  // 3. 重置地区高亮位置
  this.selected[2] = 0
  return
}

第三级:地区变更

// 直接更新地区显示值
this.values[2] = _aname

4. 联动机制关键点

数据流控制

  • range → TextPicker显示的数据源
  • values → 页面实际显示的值
  • selected → TextPicker高亮位置控制

级联更新策略

  1. 省份变更:清空城市和地区数据,重新加载,重置高亮位置
  2. 城市变更:清空地区数据,重新加载,重置地区高亮位置
  3. 地区变更:仅更新显示值

防抖机制

clearTimeout(this.timeID)
this.timeID = setTimeout(async () => {
  // 联动逻辑
}, 300)
  • 避免用户快速滑动时频繁请求接口
  • 300ms延迟确保操作稳定

5. API接口调用模式

接口参数传递

  • 省份接口:/api/province
  • 城市接口:/api/city?pname=${省份名}
  • 地区接口:/api/area?pname=${省份名}&cname=${城市名}

数据更新流程

  1. 用户滑动 → onChange触发
  2. 判断哪一级发生变化
  3. 清空下级数据并重新请求
  4. 更新显示值和高亮位置

6. 联动逻辑总结

核心思想:上级变更时,下级数据必须重新获取并重置为第一项

实现要点

  • 使用this.values[0] !== value[0]判断是否真正发生变化
  • 上级变更时,下级数据用this.range[x] = 新数据更新
  • 下级显示值用this.values[x] = this.range[x][0]重置为第一项
  • 下级高亮位置用this.selected[x] = 0重置

这种设计确保了三级数据的级联更新和用户体验的流畅性。

相关接口

全部代码

import http from '@ohos.net.http';
interface IResponse {
  message: string
  list: string[]
}
@Entry
@Component
struct Day03_06_AreaChange {
  @State message: string = '居住地选择';
  @State range: string[][] = [['北京'], ['北京市'], ['东城区']]// 在滑动组件中显示的数据 //index [  [0][0], [1][0] ,[2][0]   ]
  @State selected: number[] = [0, 0, 0]//// 控制滑动组件中数据的高亮
  @State values: string[] = ['北京', '北京市', '东城区'] // 这部分信息的目的是渲染到页面上
  req: http.HttpRequest = http.createHttp() // 请求对象
  @State showSheet: boolean = false // 半模态的显示
  @State timeID: number = 0 // 定时器的id

  // 初始化数据的加载显示
  async aboutToAppear() {
    //省份
    const res1 = await this.req.request(`https://hmajax.itheima.net/api/province`)
    this.range[0] = (JSON.parse(res1.result.toString()) as IResponse).list //默认第一个省份
    this.values[0] = this.range[0][0] //页面数据初始时 省份 显示第一个数据
    // 城市
    const res2 = await this.req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent(this.range[0][0])}`)
    this.range[1] = (JSON.parse(res2.result.toString()) as IResponse).list
    AlertDialog.show({ message: JSON.stringify(this.range[1]) })
    this.values[1] = this.range[1][0] //页面数据初始时 城市 显示第一个数据
    //   地区
    const res3 = await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.range[0][0])}&cname=${encodeURIComponent(this.range[1][0])}`)
    this.range[2] = (JSON.parse(res3.result.toString()) as IResponse).list
    this.values[2] = this.range[2][0] //页面数据初始时 地区 显示第一个数据
  }

  build() {
    Column({ space: 10 }) {
      // 标题
      Text(this.message)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)
        .width('100%')
        .margin({ bottom: 20 })
      //页面显示的地址数据
      Row({ space: 10 }) {
        Text('居住地:')
          .fontWeight(FontWeight.Bold)
        Text(this.values.join('/'))
          .layoutWeight(1)
          .fontColor(Color.Gray)
          .onClick(() => {
            this.showSheet = true
          })
        Image($r('app.media.ic_public_arrow_right'))
          .width(20)
      }

      Divider()
      Blank()


    }
    .height('100%')
    .width('100%')
    .alignItems(HorizontalAlign.Start)
    .padding(20)
    .bindSheet($$this.showSheet, this.areaSheet(), {
      height: 300
    })
  }

  @Builder
  areaSheet() {
    Column() {
      // TextPicker 级联组件  滑动选择文本内容的组件
      TextPicker({
        range: this.range,//在滑动组件中显示的数据
        selected: $$this.selected,//控制滑动组件中数据的高亮
      })
        .canLoop(false)
        //onChange事件 的形参 返回数组 数组中显示的是滑动的高亮数据 ['北京', '北京市', '东城区']
        .onChange((value) => {
          console.log(`123`, value)
          const _pname = value[0] //省份 高亮数据
          const _cname = value[1] //城市 高亮数据
          const _aname = value[2] //地区 高亮数据
          // 先清理一下定时器
          clearTimeout(this.timeID)
          this.timeID = setTimeout(async () => {
            // 处理业务
            //  1.判断 滑动后高亮的省份 和页面上显示的地址的省份 是否一致,
            //   如果不一致,进if判断
            if (this.values[0] !== value[0]) {
              //   1.1 高亮的省份 赋值给 页面显示地址的省份
              this.values[0] = _pname
              //   1.2 滑动省份时  城市和地区联动
              //  城市的联动
              const city =
                await this.req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent(this.values[0])}`)
              this.range[1] = (JSON.parse(city.result.toString()) as IResponse).list //滑动省份时 对应省份下面的所有城市数据
              this.values[1] = this.range[1][0] //滑动省份时 对应省份下面的城市数据中  页面上的地址显示第一个城市
              // 地区的联动
              const area =
                await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.values[0])}&cname=${encodeURIComponent(this.values[1])}`)
              this.range[2] = (JSON.parse(area.result.toString()) as IResponse).list //滑动省份时 选择对应城市下面的 所有地区的数据
              this.values[2] = this.range[2][0] //滑动省份时 选择对应城市下面的 所有地区数据中   页面上的地址显示第一个地区

              // 1.3 切换省份时 城市和地区第一个高亮
              this.selected[1] = 0 //城市 高亮
              this.selected[2] = 0 //地区 高亮
              return
            }
            //   2.判断滑动后高亮的地址 和页面上显示地址的城市 是否一致
            //   如果不一致,进if判断
            if (this.values[1] !== value[1]) {
              //2.1 高亮的城市 赋值给页面上显示地址的城市
              this.values[1] = _cname
              //2.2 滑动城市时  地区联动
              const area =
                await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.values[0])}&cname=${encodeURIComponent(this.values[1])}`)
              this.range[2] = (JSON.parse(area.result.toString()) as IResponse).list //滑动城市时 对应城市下面的所有地区数据
              this.values[2] = this.range[2][0] //滑动城市时 选择对应地区下面的  页面上的地址显示第一个地区
              //2.3 切换城市时  地区第一个高亮
              this.selected[2] = 0 // 地区 高亮
              return
            }
            // 3.滑动高亮的地区 赋值给页面上显示地址中的地区
            this.values[2] = _aname
          }, 300)
        })
    }
  }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值