|
|
@@ -1,188 +1,13 @@
|
|
|
<template>
|
|
|
- <!-- 仅微信小程序端 -->
|
|
|
- <!-- #ifdef MP-WEIXIN -->
|
|
|
- <view class="wx-map-draw-area">
|
|
|
- <up-navbar title="地图绘制" @leftClick="navigateBackOrHome()" :fixed="false"></up-navbar>
|
|
|
- <view class="flex1 ov-hd">
|
|
|
- <map class="wx-map" :latitude="center[0]" :longitude="center[1]" enable-rotate :scale="zoomToScale(zoom)"
|
|
|
- :polygons="wxPolygons" :polyline="wxPolylines" :show-location="false" @tap="onWxMapTap"
|
|
|
- enable-satellite />
|
|
|
-
|
|
|
- </view>
|
|
|
- <view class="wx-toolbar">
|
|
|
- <u-button size="small" type="primary" @click="startOrFinish">
|
|
|
- {{ isDrawing ? '完成' : '开始绘制' }}
|
|
|
- </u-button>
|
|
|
- <u-button size="small" class="mt-8" @click="undoPoint" :disabled="wxPoints.length === 0">撤销</u-button>
|
|
|
- <u-button size="small" class="mt-8" type="warning" @click="clearAll"
|
|
|
- :disabled="wxPoints.length === 0">清除</u-button>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="wx-info" v-if="showInfo">
|
|
|
- <text>点数:{{ wxPoints.length }}</text>
|
|
|
- <text class="ml-12">面积:{{ formattedWxArea }}</text>
|
|
|
- <text class="ml-12" v-if="isClosed">(已闭合)</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <!-- #endif -->
|
|
|
-
|
|
|
- <!-- 非微信端兜底提示(可选) -->
|
|
|
- <!-- #ifndef MP-WEIXIN -->
|
|
|
- <view class="map-draw-area__unsupported">
|
|
|
- <text>该组件仅支持微信小程序端绘制。</text>
|
|
|
- </view>
|
|
|
- <!-- #endif -->
|
|
|
+ <div>
|
|
|
+ <web-view src="https://dm.yujin.shuziyunyao.com/trainpage/pages/amap-draw/index" @message="messageChanged"></web-view>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
-
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue'
|
|
|
-
|
|
|
-type LatLng = [number, number]
|
|
|
-type WxPoint = { latitude: number; longitude: number }
|
|
|
-
|
|
|
-const props = defineProps({
|
|
|
- center: { type: Array as () => LatLng, default: () => [23.1291, 113.2644] },
|
|
|
- zoom: { type: Number, default: 13 },
|
|
|
- showInfo: { type: Boolean, default: true },
|
|
|
-})
|
|
|
-
|
|
|
-const emits = defineEmits<{
|
|
|
- (e: 'change', payload: { area: number; latlngs: LatLng[]; closed: boolean }): void
|
|
|
-}>()
|
|
|
-
|
|
|
-// 绘制状态
|
|
|
-const wxPoints = ref<WxPoint[]>([])
|
|
|
-const isDrawing = ref(false)
|
|
|
-const isClosed = ref(false)
|
|
|
-const wxArea = ref(0)
|
|
|
-
|
|
|
-// 可视化:多边形(填充)、折线(路径)
|
|
|
-const wxPolygons = computed<any[]>(() => {
|
|
|
- if (wxPoints.value.length < 3) return []
|
|
|
- return [
|
|
|
- {
|
|
|
- points: wxPoints.value,
|
|
|
- strokeColor: '#18A058',
|
|
|
- strokeWidth: 2,
|
|
|
- fillColor: '#18A05833',
|
|
|
- zIndex: 2,
|
|
|
- },
|
|
|
- ]
|
|
|
-})
|
|
|
-
|
|
|
-const wxPolylines = computed<any[]>(() => {
|
|
|
- if (wxPoints.value.length < 2) return []
|
|
|
- const lines: any[] = [
|
|
|
- { points: wxPoints.value, color: '#18A058', width: 2, dottedLine: false, zIndex: 3 },
|
|
|
- ]
|
|
|
- if (wxPoints.value.length >= 2) {
|
|
|
- const first = wxPoints.value[0]
|
|
|
- const last = wxPoints.value[wxPoints.value.length - 1]
|
|
|
- lines.push({ points: [last, first], color: '#18A058', width: 2, dottedLine: true, zIndex: 3 })
|
|
|
- }
|
|
|
- return lines
|
|
|
-})
|
|
|
-
|
|
|
-const formattedWxArea = computed(() => formatArea(wxArea.value))
|
|
|
-
|
|
|
-function onWxMapTap(e: any) {
|
|
|
- if (!isDrawing.value) return
|
|
|
- const { latitude, longitude } = e?.detail || {}
|
|
|
- if (typeof latitude === 'number' && typeof longitude === 'number') {
|
|
|
- wxPoints.value = [...wxPoints.value, { latitude, longitude }]
|
|
|
- computeWxArea()
|
|
|
- emitChange()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function startOrFinish() {
|
|
|
- if (!isDrawing.value) {
|
|
|
- isDrawing.value = true
|
|
|
- isClosed.value = false
|
|
|
- wxPoints.value = []
|
|
|
- wxArea.value = 0
|
|
|
- } else {
|
|
|
- isDrawing.value = false
|
|
|
- isClosed.value = wxPoints.value.length >= 3
|
|
|
- computeWxArea()
|
|
|
- emitChange()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function undoPoint() {
|
|
|
- if (wxPoints.value.length === 0) return
|
|
|
- wxPoints.value = wxPoints.value.slice(0, -1)
|
|
|
- computeWxArea()
|
|
|
- emitChange()
|
|
|
-}
|
|
|
-
|
|
|
-function clearAll() {
|
|
|
- wxPoints.value = []
|
|
|
- wxArea.value = 0
|
|
|
- isDrawing.value = false
|
|
|
- isClosed.value = false
|
|
|
- emitChange()
|
|
|
-}
|
|
|
-
|
|
|
-function emitChange() {
|
|
|
- emits('change', {
|
|
|
- area: wxArea.value,
|
|
|
- latlngs: wxPoints.value.map((p) => [p.latitude, p.longitude]) as LatLng[],
|
|
|
- closed: isClosed.value,
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-function computeWxArea() {
|
|
|
- if (wxPoints.value.length < 3) {
|
|
|
- wxArea.value = 0
|
|
|
- return
|
|
|
- }
|
|
|
- wxArea.value = calcSphericalPolygonAreaLngLat(
|
|
|
- wxPoints.value.map((p) => [p.longitude, p.latitude])
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-// 球面多边形面积(近似,WGS84)输入 [lng, lat]
|
|
|
-function calcSphericalPolygonAreaLngLat(coords: [number, number][]): number {
|
|
|
- const n = coords.length
|
|
|
- if (n < 3) return 0
|
|
|
- const rad = (d: number) => (d * Math.PI) / 180
|
|
|
- const R = 6378137
|
|
|
- let area = 0
|
|
|
- for (let i = 0; i < n; i++) {
|
|
|
- const lower = coords[(i + n - 1) % n]
|
|
|
- const middle = coords[i]
|
|
|
- const upper = coords[(i + 1) % n]
|
|
|
- area += (rad(upper[0]) - rad(lower[0])) * Math.sin(rad(middle[1]))
|
|
|
- }
|
|
|
- area = (area * R * R) / 2
|
|
|
- return Math.abs(area)
|
|
|
-}
|
|
|
-
|
|
|
-function zoomToScale(zoom: number) {
|
|
|
- const z = Math.max(3, Math.min(20, Math.round(zoom)))
|
|
|
- return z
|
|
|
-}
|
|
|
-
|
|
|
-function formatArea(m2: number): string {
|
|
|
- if (!m2) return '0 m²'
|
|
|
- if (m2 < 100000) return `${m2.toFixed(2)} m²`
|
|
|
- const km2 = m2 / 1_000_000
|
|
|
- return `${km2.toFixed(4)} km²`
|
|
|
-}
|
|
|
-
|
|
|
-defineExpose({ startOrFinish, undoPoint, clearAll })
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped lang="scss">
|
|
|
-.wx-map-draw-area {
|
|
|
- width: 100%;
|
|
|
- height: 100vh;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
-}
|
|
|
-.wx-map {
|
|
|
- width: 750rpx;
|
|
|
- height: 100%;
|
|
|
-}
|
|
|
-</style>
|
|
|
+ const messageChanged = (e: any) => {
|
|
|
+ console.log('message changed', e);
|
|
|
+ // 数组最后一项
|
|
|
+ const lastItem = e.detail.data[e.detail.data.length - 1];
|
|
|
+ uni.$emit('mapAreaData', lastItem);
|
|
|
+ };
|
|
|
+</script>
|