省市区联动
上代码:
import http from '@ohos.net.http';
interface IResponse {
message: string
list: string[]
}
@Entry
@Component
struct AreaChange {
@State range: string[][] = [[], [], []] //TextPicker的values数组
@State selected: number[] = [0, 0, 0] //TextPicker的index数组
@State values: string[] = ['北京', '北京市', '东城区'] //选中后用于展示的数组
req: http.HttpRequest = http.createHttp()
@State showSheet: boolean = false //半模态框
timeId: number = -1 //防抖处理的延时器Id
async aboutToAppear() {
//进入半模态框后需要马上发起请求存入range数组中先进行展示
const res1 = await this.req.request("https://hmajax.itheima.net/api/province")
const provinces = (JSON.parse(res1.result as string) as IResponse).list
const res2 = await this.req.request("https://hmajax.itheima.net/api/city?pname=" + encodeURIComponent(provinces[0]))
const citys = (JSON.parse(res2.result as string) as IResponse).list
const res3 = await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(provinces[0])}&cname=${encodeURIComponent(citys[0])}`)
const areas = (JSON.parse(res3.result as string) as IResponse).list
this.range[0] = provinces
this.range[1] = citys
this.range[2] = areas
}
build() {
Column({ space: 10 }) {
Text('居住地选择')
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
.width('100%').fontSize(30).margin({ bottom: 20 })
Row({ space: 10 }) {
Text('居住地:')
.fontWeight(FontWeight.Bold)
Text(this.values.join('/'))
.layoutWeight(1)
.fontColor(Color.Gray)
.onClick(() => {
this.showSheet = true
})
}
Divider()
Blank()
}
.height('100%').width('100%').padding(20)
.alignItems(HorizontalAlign.Start)
.bindSheet($$this.showSheet, this.areaSheet(), { height: 300 })
}
@Builder
areaSheet() {
Column() {
TextPicker({
range: this.range, //['北京', '北京市', '东城区']
selected: $$this.selected, //[0, 0, 0]
})
.canLoop(false)// 不要循环
.onChange((values) => {
//进行防抖处理,防止滚动期间多次发送请求
clearTimeout(this.timeId)
this.timeId = setTimeout(async () => {
//当省份被改变
if (this.values[0] !== values[0]) {
const res2 = await this.req.request("https://hmajax.itheima.net/api/city?pname=" + encodeURIComponent(values[0]))
const citys = (JSON.parse(res2.result as string) as IResponse).list
const res3 = await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(values[0])}&cname=${encodeURIComponent(citys[0])}`)
const areas = (JSON.parse(res3.result as string) as IResponse).list
this.range[1] = citys //发送请求拿到省份,存入range数组中
this.range[2] = areas //根据省份发送请求,拿到对应的城市,存入range数组中
//当省份发生变化需要把城市置零,当城市发生变化需要把地区置零
this.selected[1] = 0
this.selected[2] = 0
//内容页面展示需要同步
this.values[0] = values[0]
this.values[1] = this.range[1][0]
this.values[2] = this.range[2][0]
//当城市被改变
} else if (this.values[1] !== values[1]) {
const res3 = await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(values[0])}&cname=${encodeURIComponent(values[1])}`)
const areas = (JSON.parse(res3.result as string) as IResponse).list
this.range[2] = areas //根据省份和城市发送请求,拿到对应地区,存入range数组中
//当城市发生变化需要把地区置零
this.selected[2] = 0
//内容页面展示需要同步
this.values[1] = values[1]
this.values[2] = this.range[2][0]
//当地区被改变
} else if (this.values[2] !== values[2]) {
//内容页面展示需要同步
this.values[2] = values[2]
}
}, 1000)
})
}
}
}
另外,当我们在开发中需要完成以下类似的联动业务,例如:通讯录,城市联动等:
List列表联动字母索引表是怎么实现的呢?
咱们分两步来做:
- 滚动 List,同步选中对应的AlphabetIndexer
.onScrollIndex((firstIndex: number) => {
this.selectedIndex = firstIndex
})
- 选中AlphabetIndexer的区域,同步滚动List
AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })
.width(20)
.onSelect((index: number) => {
this.listScroller.scrollToIndex(index)
})
具体代码如下:
interface BKCityContent {
initial: string
cityNameList: string[]
}
@Entry
@Component
struct ListPageBK {
@State selectedIndex: number = 0;
@State isShow: boolean = false
hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
cityContentList: BKCityContent[] = [
{
initial: 'A',
cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
},
{
initial: 'B',
cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
},
{
initial: 'C',
cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
},
{
initial: 'D',
cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
},
{
initial: 'E',
cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
},
{
initial: 'F',
cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
},
{
initial: 'G',
cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
},
{
initial: 'H',
cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
},
{
initial: 'J',
cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
},
{
initial: 'K',
cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
},
{
initial: 'L',
cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
},
{
initial: 'M',
cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
},
{
initial: 'N',
cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
},
{
initial: 'P',
cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
},
{
initial: 'Q',
cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
},
{
initial: 'R',
cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
},
{
initial: 'S',
cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
},
{
initial: 'T',
cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
},
{
initial: 'W',
cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
},
{
initial: 'X',
cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
},
{
initial: 'Y',
cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
},
{
initial: 'Z',
cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
}
]
alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]
listScroller: Scroller = new Scroller();
build() {
Column() {
Button('显示城市选择')
.onClick(() => this.isShow = true)
.bindContentCover(this.isShow, this.ContentCoverBuilder())
}
.width('100%')
.height('100%')
.backgroundColor('#f8f8f8')
}
@Builder
ContentCoverBuilder() {
Stack({ alignContent: Alignment.End }) {
Column() {
// 顶部
this.TopBuilder();
// 列表
this.ListBuilder();
}
.backgroundColor(Color.White)
// 导航
this.AlphabetBuilder()
}
}
@Builder
AlphabetBuilder() {
AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })
.width(20)
.onSelect((index: number) => {
this.listScroller.scrollToIndex(index)
})
}
@Builder
ListBuilder() {
List({ space: 30, scroller: this.listScroller }) {
// 历史
this.LocationListItemBuilder()
// 热门
this.HotListItemBuilder()
// A-B的区域
this.LetterListItemBuilder()
}
.divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
.width('100%')
.layoutWeight(1)
.sticky(StickyStyle.Header)
.onScrollIndex((firstIndex: number) => {
this.selectedIndex = firstIndex
})
}
@Builder
LetterListItemBuilder() {
// A-B的区域
ForEach(this.cityContentList, (item: BKCityContent) => {
ListItemGroup({ header: this.ListItemGroupHeaderBuilder(item.initial) }) {
ForEach(item.cityNameList, (city: string) => {
ListItem() {
Text(city)
.width('100%')
.padding({ left: 20 })
}
.width('100%')
.height(50)
.backgroundColor(Color.White)
})
}
.padding({ bottom: 20 })
.divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
})
}
@Builder
ListItemGroupHeaderBuilder(title: string) {
Text(title)
.padding({ left: 20, bottom: 15, top: 20 })
.fontSize(14)
.fontColor(Color.Gray)
.backgroundColor('#f8f8f8')
.width('100%')
}
@Builder
HotListItemBuilder() {
// 热门
ListItem() {
Column({ space: 10 }) {
Text('热门城市')
.alignSelf(ItemAlign.Start)
.fontColor(Color.Gray)
.fontSize(14)
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.hotCitys, (item: string) => {
Text(item)
.height(25)
.backgroundColor(Color.White)
.width('25%')
.margin({ bottom: 10 })
})
}
.padding({ left: 20, right: 20 })
}
.width('100%')
.padding({ left: 20, right: 20, bottom: 10 })
}
}
@Builder
LocationListItemBuilder() {
ListItem() {
Column({ space: 15 }) {
// 定位地址
Row() {
Text('北京')
Text() {
ImageSpan($r('app.media.ic_public_location_fill_blue'))
.width(20)
Span('开启定位')
}
}
.width('100%')
.padding({ top: 10, bottom: 10, right: 20, left: 20 })
.justifyContent(FlexAlign.SpaceBetween)
.backgroundColor(Color.White)
// 历史
Column({ space: 10 }) {
Text('历史')
.fontColor(Color.Gray)
.alignSelf(ItemAlign.Start)
.fontSize(14)
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.historyCitys, (city: string, index: number) => {
Text(city)
.height(25)
.backgroundColor(Color.White)
.width('25%')
.margin({ bottom: 10 })
})
}
.padding({ left: 20, right: 20 })
}
.width('100%')
.padding({ left: 20, right: 20 })
}
}
.padding({ top: 20 })
}
@Builder
TopBuilder() {
Column() {
// X + 输入框
Row({ space: 20 }) {
Image($r('app.media.ic_public_cancel'))
.width(30)
.fillColor(Color.Gray)
Row({ space: 5 }) {
Image($r('app.media.ic_public_search'))
.width(18)
Text('请输入城市名称')
.layoutWeight(1)
}
.height(50)
.border({ width: .5, color: Color.Gray, radius: 5 })
.padding({ left: 5 })
.layoutWeight(1)
.shadow({
radius: 20,
color: '#f6f6f7'
})
}
.padding({
left: 15,
right: 15,
top: 15
})
// 国内城市
Column() {
Text('国内城市')
.fontSize(15)
.fontWeight(800)
.padding(5)
Row()
.width(20)
.height(2)
.backgroundColor('#0094ff')
.borderRadius(2)
}
}
.width('100%')
.backgroundColor(Color.White)
.height(100)
.border({
width: { bottom: 4 },
color: '#f6f6f7',
})
}
}