地图模块实现流程操作指南
地图页面

一、模块概述
地图模块(PageShopList)实现门店浏览和选择功能,为用户提供一个直观的门店列表,支持地图展示区域折叠、门店卡片展示、门店选择和确认等操作。该模块在点餐流程中起到"选择取餐/配送门店"的关键作用。
二、模块职责
| 职责 | 说明 |
|---|---|
| 地图展示 | 可折叠的地图区域,显示门店标记 |
| 门店列表 | 展示所有门店的详细信息卡片 |
| 门店选择 | 点击卡片高亮选中,支持切换 |
| 状态展示 | 门店营业状态标识(营业中/休息中) |
| 确认跳转 | 底部确认按钮,保存选择并返回 |
三、数据模型
门店数据模型(ShopModel.ets)
typescript
export class ShopDetail {
id: string = ''
name: string = ''
addr: string = ''
latitude: number = 0 // WGS84坐标
longitude: number = 0
time: string = '' // 营业时间
status: string = '' // 营业状态
phone: string = '' // 联系电话
}
本项目预置了5家"甜蜜时光"门店数据(南京地区):
- 新街口旗舰店(营业中)
- 河西万达店(营业中)
- 仙林大学城店(营业中)
- 江宁百家湖店(休息中)
- 大行宫文创店(营业中)
四、实现流程
步骤1:创建页面结构
typescript
@Component
export struct PageShopList {
@LocalStorageLink('chooseShop') chooseShop: string = ''
@State shopList: ShopDetail[] = shopListData
@State preChooseShop: string = '' // 预选门店(确认前)
@State isMapExpanded: boolean = true // 地图展开状态
aboutToAppear(): void {
this.preChooseShop = this.chooseShop // 恢复上次选择
}
build() {
NavDestination() {
Column() {
// 地图区域
// 折叠/展开切换
// 门店列表
// 底部确认按钮
}
}
.title('选择门店')
}
}
步骤2:实现可折叠地图区域
typescript
Stack() {
Column() {
Text('🗺️').fontSize(60)
Text('门店分布地图').fontSize(14)
Row() {
ForEach(this.shopList, (shop: ShopDetail) => {
Text('📍').fontSize(20)
.onClick(() => { this.preChooseShop = shop.name })
})
}
}
.backgroundColor('#E8F4E8')
.borderRadius(16)
}
.height(this.isMapExpanded ? 220 : 0)
.animation({ duration: 300, curve: Curve.EaseInOut })
设计要点:
- 使用
animation属性实现折叠/展开的平滑过渡动画 - 地图区域使用模拟展示(实际项目可集成 MapKit)
步骤3:实现门店卡片列表
使用 ShopCard 公共组件渲染每个门店:
typescript
ForEach(this.shopList, (shop: ShopDetail) => {
ShopCard({
shop: shop,
isSelected: this.preChooseShop === shop.name,
onSelect: (s: ShopDetail) => {
this.preChooseShop = s.name
},
onCall: (s: ShopDetail) => {
// 调用电话(实际项目可使用 call.makeCall)
}
})
})
步骤4:ShopCard组件实现
typescript
@Component
export struct ShopCard {
shop: ShopDetail = new ShopDetail('', '', '', 0, 0, '', '')
isSelected: boolean = false
build() {
Column() {
Row() {
Text('🏪').fontSize(28)
Column() {
Text(this.shop.name)
Text(this.shop.addr)
}
if (this.isSelected) {
Text('✓').fontColor('#FF6B35') // 选中标记
}
}
Row() {
Text(`🕐 ${this.shop.time}`)
Text(this.shop.status)
.fontColor(this.shop.status === '营业中' ? '#4CAF50' : '#FF4444')
}
}
}
}
状态颜色方案:
- 营业中:绿色文字
#4CAF50+ 浅绿背景#E8F5E9 - 休息中:红色文字
#FF4444+ 浅红背景#FFEBEE
步骤5:确认选择并返回
typescript
Button('确认选择,去下单')
.backgroundColor('#FF6B35')
.onClick(() => {
if (this.preChooseShop.length > 0) {
this.chooseShop = this.preChooseShop // 更新LocalStorage
RouterModule.pop(NavStackMap.MAIN_STACK) // 返回上一页
}
})
数据流向:
用户选择门店 → preChooseShop (临时状态)
→ 点击确认 → chooseShop (LocalStorageLink)
→ RouterModule.pop → 美食页面自动更新门店显示
五、坐标转换工具(MapUtil)
在实际地图集成中,需要将WGS84坐标转换为GCJ02坐标(国内地图坐标系):
typescript
export class MapUtil {
static wgs84ToGcj02(lat: number, lng: number): number[] {
// WGS84 → GCJ02 坐标转换算法
// 返回 [转换后纬度, 转换后经度]
}
}
使用场景:门店坐标存储在WGS84格式,显示在高德/华为地图时需要转换为GCJ02。
六、注意事项
preChooseShop使用@State管理,仅在确认后才写入@LocalStorageLink- 地图折叠动画使用
animation({ duration: 300, curve: Curve.EaseInOut }) - 门店电话号码可选填,为空时隐藏拨号按钮
- 实际项目中可集成
@kit.MapKit实现真实地图展示和路径规划 - 从美食页面进入门店选择后,返回时美食页面自动读取更新后的
chooseShop值