目录
- 引言
- [1 搭建布局](#1 搭建布局)
- [2 创建变量](#2 创建变量)
- [3 初始化站点](#3 初始化站点)
- [4 点选站点时联动](#4 点选站点时联动)
- [5 数据绑定](#5 数据绑定)
- 最终效果
- 完整代码
- 总结
引言
在《加油站小程序实战教程08动态获取城市和站点信息》中,我们已经介绍了如何动态的读取城市及站点信息显示到地图上。站点信息读取到后,如果在地图点选站点信息的时候,需要在地图的下方显示站点的详细信息,本篇我们介绍一下具体的实现过程。
1 搭建布局
要显示详情信息,需要先用布局组件搭建布局。通常我们是使用普通容器、文本、图标几个布局组件来搭建页面。
在JSX组件下边先添加普通容器,整体内容是分为两行,第一行显示站点的名称、营业状态、更多网点。第二行显示站点的地址,有两个图标,一个可以拨打电话,一个可以导航
每一行都可以用一个普通容器进行占位,具体结构如下
第一个普通容器里边放置三个文本组件,用来显示站点名称、营业状态和更多网点
组件默认是垂直排列的,为了让他在一行显示,可以设置普通容器的样式,设置为横向排列
第一个文本我们用来显示站点名称,设置文本的样式为加粗
第二个文本显示营业状态,设置一个淡绿色的背景色
将文本颜色设置为绿色
设置字号和内边距
再设置一点圆角
设置第三个文本的边框,设置为红色
文本颜色也设置为红色
第二个普通容器,放置文本和两个图标组件
将两个图标组件又放到普通容器里边是为了实现两端对齐的效果,外层普通容器设置对齐方式为端对齐
2 创建变量
布局搭建好了之后,就需要定义变量用来绑定数据。先创建一个文本变量,定义为stationName,用来获取地图点选的站点名称
然后创建一个内置表查询变量,用来根据站点名称查询站点信息
查询条件设置为站点名称等于我们的stationName
3 初始化站点
当地图加载完毕后,我们会进行距离计算,将最近的站点的名称赋值给我们的stationName
bash
const calculateNearestStation =async (stations) => {
let nearest = null;
let minDistance = Infinity;
stations.forEach((station) => {
const distance = calculateDistance(userLocation, station.location);
if (distance < minDistance) {
minDistance = distance;
nearest = station;
}
});
$w.page.dataset.state.stationName = nearest.name
await $w.staion.trigger()
setNearestStation(nearest);
};
在计算最近距离站点的方法里我们添加了赋值语句,这样就得到了站点信息,内置表查询需要调用trigger方法才可以获取到值
4 点选站点时联动
除了初始化需要显示站点详情外,当在地图点选站点的时候也要进行联动,找到点选的方法,给站点名称进行赋值
bash
marker.on("click", async () => {
infoWindow.open(mapInstance, marker.getPosition());
// 更新站点名称并触发事件
$w.page.dataset.state.stationName = station.name;
await $w.staion.trigger();
});
5 数据绑定
完成了点选和初始化工作后,我们现在就已经得到了站点的数据,就需要给文本组件绑定一下文本内容,从变量里依次绑定
先绑定站点名称
接着绑定营业状态
接着绑定营业地址
电话的图标,我们可以设置点击事件,方法选择拨打电话
绑定为站点的电话字段
导航图标绑定地图导航方法
绑定站点的经纬度
最终效果
地图加载完毕后会显示最近的站点,当点击站点的时候信息会重新切换
完整代码
bash
import React, { useRef, useEffect, useState } from "react";
export default function CitySwitcherMap(props) {
const { style } = props;
const mapContainerRef = useRef(null);
const [selectedCity, setSelectedCity] = useState("");
const [defaultCityLocation, setDefaultCityLocation] = useState([116.397428, 39.90923]); // 默认北京
const [mapInstance, setMapInstance] = useState(null);
const [userLocation, setUserLocation] = useState([116.397428, 39.90923]); // 默认用户位置为北京
const [nearestStation, setNearestStation] = useState(null);
const [cities, setCities] = useState([]); // 存储城市数据
useEffect(() => {
// 模拟触发 API 并获取数据
const fetchCityStations = async () => {
try {
// 调用你的 API 获取城市数据
const result = await $w.getCityStation.trigger();
console.log("城市数据",result)
if (result) {
setCities(result); // 更新城市数据
} else {
console.error("获取城市数据失败");
}
} catch (error) {
console.error("API 调用失败", error);
}
};
fetchCityStations();
}, []); // 只在组件加载时触发一次
useEffect(() => {
const loadAMap = async () => {
if (!window.AMap) {
const script = document.createElement("script");
script.src = "https://webapi.amap.com/maps?v=2.0&key=6bd36e95532ee2800d841762601420f5";
script.async = true;
script.onload = () => {
initMap();
};
document.body.appendChild(script);
} else {
initMap();
}
};
const initMap = () => {
if (window.AMap) {
const map = new window.AMap.Map(mapContainerRef.current, {
center: defaultCityLocation,
zoom: 12,
mapStyle: "amap://styles/normal",
});
setMapInstance(map);
}
};
loadAMap();
}, []);
useEffect(() => {
if (mapInstance) {
const selectedCityData = cities.find((city) => city.name === selectedCity);
if (selectedCityData) {
mapInstance.setCenter(selectedCityData.location);
addMarkers(selectedCityData.stations);
calculateNearestStation(selectedCityData.stations);
}
}
}, [selectedCity, mapInstance, cities]);
const addMarkers = (stations) => {
mapInstance.clearMap();
stations.forEach((station) => {
const marker = new window.AMap.Marker({
position: station.location,
map: mapInstance,
});
const distance = calculateDistance(userLocation, station.location);
const infoWindow = new window.AMap.InfoWindow({
content: `<div style="padding: 10px; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<div><strong>${station.name}</strong></div>
<div style="color: #666;">距当前站点 ${distance.toFixed(1)} km</div>
</div>`,
offset: new window.AMap.Pixel(0, -40), // 调整偏移,避免遮挡标记
});
marker.on("click", async () => {
infoWindow.open(mapInstance, marker.getPosition());
// 更新站点名称并触发事件
$w.page.dataset.state.stationName = station.name;
await $w.staion.trigger();
});
if (nearestStation && nearestStation.name === station.name) {
infoWindow.open(mapInstance, marker.getPosition());
}
});
};
const calculateNearestStation =async (stations) => {
let nearest = null;
let minDistance = Infinity;
stations.forEach((station) => {
const distance = calculateDistance(userLocation, station.location);
if (distance < minDistance) {
minDistance = distance;
nearest = station;
}
});
$w.page.dataset.state.stationName = nearest.name
await $w.staion.trigger()
setNearestStation(nearest);
};
const calculateDistance = (start, end) => {
const startLngLat = new window.AMap.LngLat(start[0], start[1]);
const endLngLat = new window.AMap.LngLat(end[0], end[1]);
return startLngLat.distance(endLngLat) / 1000; // 返回公里数
};
const getUserLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
setUserLocation([longitude, latitude]);
fetchCityNameFromCoords(longitude, latitude);
},
() => {
setUserLocation([111.75199, 40.84211]); // 呼和浩特
setSelectedCity("呼和浩特市");
}
);
}
};
const fetchCityNameFromCoords = async (lng, lat) => {
try {
const response = await fetch(
`https://restapi.amap.com/v3/geocode/regeo?key=154efad008972a03ecac218a844959a9&location=${lng},${lat}`
);
const data = await response.json();
if (data.status === "1" && data.regeocode) {
const cityName = data.regeocode.addressComponent.city || data.regeocode.addressComponent.province;
setSelectedCity(cityName);
}
} catch {
console.error("获取城市名称失败");
}
};
useEffect(() => {
getUserLocation();
}, []);
return (
<div>
<div style={{ marginBottom: "10px", zIndex: 999 }}>
<select
value={selectedCity}
onChange={(e) => setSelectedCity(e.target.value)}
style={{
padding: "8px",
fontSize: "14px",
borderRadius: "4px",
border: "1px solid #ccc",
}}
>
<option value="">请选择城市</option>
{cities.map((city) => (
<option key={city.name} value={city.name}>
{city.name}
</option>
))}
</select>
</div>
<div
ref={mapContainerRef}
style={{
position: "relative",
width: "100%",
height: "500px",
marginTop: "0px",
...style,
}}
/>
</div>
);
}
总结
本篇我们介绍了站点详情的展示,需要理解变量的作用,如何在地图组件中重新更新变量的内容。页面布局的搭建,以及变量绑定的方法。