一、功能概述與實現步驟
1.1 功能需求
顯示地圖并固定中心點標記
繪制服務區域多邊形邊界
實時檢測拖拽后位置是否在服務區內
提供位置確認和超出范圍提示功能
1.2 實現步驟分解
第一步:初始化地圖基礎配置
創建Map組件并設置基本屬性
定義服務區域多邊形坐標
設置地圖初始中心點
第二步:實現地圖交互邏輯
監聽地圖拖拽事件
獲取拖拽后中心點坐標
判斷坐標是否在服務區內
第三步:實現覆蓋層UI
固定中心點標記
位置信息顯示面板
操作按鈕(確認/返回服務區)
二、分步驟代碼實現
2.1 第一步:地圖基礎配置
<template><view class="map-container"><mapid="map"style="width: 100%; height: 80vh":latitude="center.latitude":longitude="center.longitude":polygons="polygons"@regionchange="handleMapDrag":show-location="true"><!-- 覆蓋層將在第三步添加 --></map></view> </template><script setup> import { ref, onMounted } from "vue";// 服務區域邊界坐標 const serviceAreaPolygon = [{ latitude: 34.808, longitude: 113.55 },{ latitude: 34.805, longitude: 113.58 },// ...其他坐標點{ latitude: 34.808, longitude: 113.55 } // 閉合多邊形 ];// 中心點位置 const center = ref({latitude: 34.747, longitude: 113.625 });// 多邊形配置 const polygons = ref([{points: serviceAreaPolygon,strokeWidth: 2,strokeColor: "#1E90FF",fillColor: "#1E90FF22" }]);// 初始化地圖上下文 const mapContext = ref(null); onMounted(() => {mapContext.value = uni.createMapContext("map"); }); </script>
2.2 第二步:地圖交互邏輯實現
// 當前坐標點 const currentPos = ref({ ...center.value }); // 是否在服務區內 const isInServiceArea = ref(true);// 地圖拖拽事件處理 const handleMapDrag = (e) => {if (e.type === "end") {mapContext.value.getCenterLocation({success: (res) => {currentPos.value = {latitude: res.latitude,longitude: res.longitude};// 判斷是否在服務區內isInServiceArea.value = isPointInPolygon(currentPos.value,serviceAreaPolygon);}});} };// 射線法判斷點是否在多邊形內 function isPointInPolygon(point, polygon) {const { latitude, longitude } = point;let inside = false;for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {const xi = polygon[i].longitude, yi = polygon[i].latitude;const xj = polygon[j].longitude, yj = polygon[j].latitude;const intersect = ((yi > latitude) !== (yj > latitude))&& (longitude < (xj - xi) * (latitude - yi) / (yj - yi) + xi);if (intersect) inside = !inside;}return inside; }
2.3 第三步:覆蓋層UI實現
<!-- 在map標簽內添加 --> <cover-view class="center-marker"></cover-view><cover-view class="info-box" :class="{ 'out-of-range': !isInServiceArea }"><cover-view v-if="isInServiceArea">當前位置在服務區域內</cover-view><cover-view v-else class="error">當前選擇位置超出服務區域</cover-view><cover-view class="coords">緯度: {{ currentPos.latitude.toFixed(6) }} 經度: {{ currentPos.longitude.toFixed(6) }}</cover-view><cover-view v-if="!isInServiceArea" class="recenter-btn" @tap="centerToServiceArea">查看最近的服務區域</cover-view><cover-view v-else class="confirm-btn" @tap="confirmLocation">確定上車位置</cover-view> </cover-view>
2.4 第四步:業務功能完善
// 返回服務區中心 const centerToServiceArea = () => {const center = getPolygonCenter(serviceAreaPolygon);currentPos.value = { ...center };isInServiceArea.value = true;mapContext.value.moveToLocation({latitude: center.latitude,longitude: center.longitude}); };// 計算多邊形中心點 function getPolygonCenter(polygon) {let latSum = 0, lngSum = 0;polygon.forEach(point => {latSum += point.latitude;lngSum += point.longitude;});return {latitude: latSum / polygon.length,longitude: lngSum / polygon.length}; }// 確認位置 const confirmLocation = () => {uni.showToast({title: `位置已確認: ${currentPos.value.latitude.toFixed(6)}, ${currentPos.value.longitude.toFixed(6)}`,icon: "none"});// 實際業務中可以觸發回調或跳轉 };
三、完整實現代碼
<template><view class="map-container"><mapid="map"style="width: 100%; height: 80vh":latitude="center.latitude":longitude="center.longitude":polygons="polygons"@regionchange="handleMapDrag":show-location="true"><cover-view class="center-marker"></cover-view><cover-view class="info-box" :class="{ 'out-of-range': !isInServiceArea }"><cover-view v-if="isInServiceArea">當前位置在服務區域內</cover-view><cover-view v-else class="error">當前選擇位置超出服務區域</cover-view><cover-view class="coords">緯度: {{ currentPos.latitude.toFixed(6) }} 經度: {{ currentPos.longitude.toFixed(6) }}</cover-view><cover-view v-if="!isInServiceArea" class="recenter-btn" @tap="centerToServiceArea">查看最近的服務區域</cover-view><cover-view v-else class="confirm-btn" @tap="confirmLocation">確定上車位置</cover-view></cover-view></map></view> </template><script setup> import { ref, onMounted } from "vue";// 服務區域邊界 const serviceAreaPolygon = [{ latitude: 34.808, longitude: 113.55 },{ latitude: 34.805, longitude: 113.58 },{ latitude: 34.79, longitude: 113.61 },{ latitude: 34.765, longitude: 113.625 },{ latitude: 34.735, longitude: 113.62 },{ latitude: 34.71, longitude: 113.6 },{ latitude: 34.7, longitude: 113.57 },{ latitude: 34.715, longitude: 113.54 },{ latitude: 34.75, longitude: 113.53 },{ latitude: 34.808, longitude: 113.55 } ];const center = ref(getPolygonCenter(serviceAreaPolygon)); const currentPos = ref({ ...center.value }); const isInServiceArea = ref(true); const mapContext = ref(null);const polygons = ref([{points: serviceAreaPolygon,strokeWidth: 2,strokeColor: "#1E90FF",fillColor: "#1E90FF22" }]);onMounted(() => {mapContext.value = uni.createMapContext("map"); });const handleMapDrag = (e) => {if (e.type === "end") {mapContext.value.getCenterLocation({success: (res) => {currentPos.value = {latitude: res.latitude,longitude: res.longitude};isInServiceArea.value = isPointInPolygon(currentPos.value,serviceAreaPolygon);}});} };function isPointInPolygon(point, polygon) {const { latitude, longitude } = point;let inside = false;for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {const xi = polygon[i].longitude, yi = polygon[i].latitude;const xj = polygon[j].longitude, yj = polygon[j].latitude;const intersect = ((yi > latitude) !== (yj > latitude))&& (longitude < (xj - xi) * (latitude - yi) / (yj - yi) + xi);if (intersect) inside = !inside;}return inside; }function getPolygonCenter(polygon) {let latSum = 0, lngSum = 0;polygon.forEach(point => {latSum += point.latitude;lngSum += point.longitude;});return {latitude: latSum / polygon.length,longitude: lngSum / polygon.length}; }const centerToServiceArea = () => {const center = getPolygonCenter(serviceAreaPolygon);currentPos.value = { ...center };isInServiceArea.value = true;mapContext.value.moveToLocation({latitude: center.latitude,longitude: center.longitude}); };const confirmLocation = () => {uni.showToast({title: `位置已確認: ${currentPos.value.latitude.toFixed(6)}, ${currentPos.value.longitude.toFixed(6)}`,icon: "none"}); }; </script><style scoped> .map-container {width: 100%;height: 100vh;position: relative; }.center-marker {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 20px;height: 20px;background-color: #5ca7fc;border-radius: 50%;border: 2px solid white;z-index: 999; }.info-box {position: absolute;top: 20%;left: 50%;transform: translateX(-50%);background: rgba(255, 255, 255, 0.9);padding: 12px 16px;border-radius: 8px;width: 80%;box-shadow: 0 2px 8px rgba(0,0,0,0.1); }.info-box.out-of-range {background: rgba(255, 240, 240, 0.9); }.coords {font-size: 12px;color: #666;margin: 8px 0; }.error {color: #f56c6c;font-weight: bold; }.recenter-btn, .confirm-btn {margin-top: 10px;padding: 8px 12px;border-radius: 4px;text-align: center;font-size: 14px; }.recenter-btn {background: #606266;color: white; }.confirm-btn {background: #409eff;color: white; } </style>
四、總結
本文分步驟詳細講解了如何使用Uni-app實現地圖位置選擇功能,從基礎配置到完整實現,重點介紹了:
地圖基礎配置方法
多邊形區域繪制與判斷
交互邏輯的實現
覆蓋層UI的開發技巧
.moveToLocation移動api 只有在真機才能實現,微信開發者工具不支持
可直接復制完整代碼到單頁測試運行,歡迎補充問題