wwf
7 小时以前 a1d7e81859f554f3a53680cc35f0f49bf1f77098
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
<!-- 地图组件:基于百度地图GL实现 -->
<!-- TODO @super:还存在两个没解决的小bug,一个是修改手动定位时一次加载 不知道为何定位点在地图左上角 调了半天没解决 第二个是检索地址确定定位的功能参照百度的文档没也搞好 回头再解决一下 -->
<template>
  <div v-if="props.isWrite">
    <el-form ref="form" label-width="120px">
      <el-form-item label="定位位置:">
        <el-select
          class="w-full"
          v-model="state.address"
          clearable
          filterable
          remote
          reserve-keyword
          placeholder="可输入地址查询经纬度"
          :remote-method="autoSearch"
          @change="handleAddressSelect"
          :loading="state.loading"
        >
          <el-option
            v-for="item in state.mapAddrOptions"
            :key="item.value"
            :label="item.name"
            :value="item.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="设备地图:">
        <!-- TODO @super:这里看看 unocss 哈 -->
        <div id="bdMap" class="mapContainer"></div>
      </el-form-item>
    </el-form>
  </div>
  <div v-else>
    <el-descriptions :column="2" border :labelStyle="{ 'font-weight': 'bold' }">
      <el-descriptions-item label="设备位置:">{{ state.address }}</el-descriptions-item>
    </el-descriptions>
    <div id="bdMap" class="mapContainer"></div>
  </div>
</template>
 
<script setup lang="ts">
import { reactive, onMounted } from 'vue'
import { propTypes } from '@/utils/propTypes'
 
// 扩展 Window 接口以包含百度地图 GL API
declare global {
  interface Window {
    BMapGL: any
    initBaiduMap: () => void
  }
}
 
const emits = defineEmits(['locateChange', 'update:center'])
const state = reactive({
  lonLat: '', // 经度,纬度
  address: '',
  loading: false,
  latitude: '', // 纬度
  longitude: '', // 经度
  map: null as any, // 地图对象
  mapAddrOptions: [] as any[],
  mapMarker: null as any, // 标记对象
  geocoder: null as any,
  autoComplete: null as any,
  tips: [] // 搜索提示
})
 
const props = defineProps({
  clickMap: propTypes.bool.def(false),
  isWrite: propTypes.bool.def(false),
  center: propTypes.string.def('')
})
 
/** 加载百度地图 */
const loadMap = () => {
  state.address = ''
  state.latitude = ''
  state.longitude = ''
 
  // 创建百度地图 API 脚本,动态加载
  const script = document.createElement('script')
  script.src = `https://api.map.baidu.com/api?v=1.0&type=webgl&ak=${
    import.meta.env.VITE_BAIDU_MAP_KEY
  }&callback=initBaiduMap`
  document.body.appendChild(script)
 
  // 定义全局回调函数
  window.initBaiduMap = () => {
    initMap()
    initGeocoder()
    initAutoComplete()
 
    // TODO @super:这里加一行注释
    if (props.clickMap) {
      state.map.addEventListener('click', (e: any) => {
        console.log(e)
        const point = e.latlng
        console.log(point)
        state.lonLat = point.lng + ',' + point.lat
        console.log(state.lonLat)
        regeoCode(state.lonLat)
      })
    }
 
    // TODO @super:这里加一行注释
    if (props.center) {
      regeoCode(props.center)
    }
  }
}
 
/** 初始化地图 */
const initMap = () => {
  const mapId = 'bdMap'
  state.map = new window.BMapGL.Map(mapId)
  // TODO @super:这个是默认的哇?
  state.map.centerAndZoom(new window.BMapGL.Point(116.404, 39.915), 11)
  state.map.enableScrollWheelZoom()
  state.map.disableDoubleClickZoom()
 
  // 添加地图控件
  state.map.addControl(new window.BMapGL.NavigationControl())
  state.map.addControl(new window.BMapGL.ScaleControl())
  state.map.addControl(new window.BMapGL.ZoomControl())
}
 
/** 初始化地理编码器 */
const initGeocoder = () => {
  state.geocoder = new window.BMapGL.Geocoder()
}
 
/** 初始化自动完成 */
const initAutoComplete = () => {
  state.autoComplete = new window.BMapGL.Autocomplete({
    input: 'searchInput',
    location: state.map
  })
}
 
/**
 * 搜索地址
 * @param queryValue 搜索关键词
 */
const autoSearch = (queryValue: string) => {
  if (!queryValue) {
    state.mapAddrOptions = []
    return
  }
 
  state.loading = true
 
  // 使用百度地图地点检索服务
  const localSearch = new window.BMapGL.LocalSearch(state.map, {
    onSearchComplete: (results: any) => {
      state.loading = false
      const temp: any[] = []
 
      if (results && results.getPoi) {
        const pois = results.getPoi()
        pois.forEach((p: any) => {
          const point = p.point
          if (point && point.lng && point.lat) {
            temp.push({
              name: p.title,
              value: point.lng + ',' + point.lat
            })
          }
        })
      }
 
      state.mapAddrOptions = temp
    }
  })
 
  localSearch.search(queryValue)
}
 
/**
 * 处理地址选择
 * @param value 选中的地址值
 */
const handleAddressSelect = (value: string) => {
  if (value) {
    regeoCode(value)
  }
}
 
/**
 * 添加标记点
 * @param lnglat 经纬度数组
 */
// TODO @super:拼写;尽量不要有 idea 绿色提醒哈
const setMarker = (lnglat: any) => {
  if (!lnglat) return
 
  // 如果点标记已存在则先移除原点
  if (state.mapMarker !== null) {
    state.map.removeOverlay(state.mapMarker)
    state.lonLat = ''
  }
 
  // 创建新的标记点
  const point = new window.BMapGL.Point(lnglat[0], lnglat[1])
  state.mapMarker = new window.BMapGL.Marker(point)
 
  // 添加点标记到地图
  state.map.addOverlay(state.mapMarker)
  state.map.centerAndZoom(point, 16)
}
 
/**
 * 经纬度转化为地址、添加标记点
 * @param lonLat 经度,纬度字符串
 */
// TODO @super:拼写;尽量不要有 idea 绿色提醒哈
const regeoCode = (lonLat: string) => {
  if (!lonLat) return
 
  // TODO @super:拼写;尽量不要有 idea 绿色提醒哈
  const lnglat = lonLat.split(',')
  if (lnglat.length !== 2) return
 
  state.longitude = lnglat[0]
  state.latitude = lnglat[1]
 
  // 通知父组件位置变更
  emits('locateChange', lnglat)
  emits('update:center', lonLat)
 
  // 先将地图中心点设置到目标位置
  const point = new window.BMapGL.Point(lnglat[0], lnglat[1])
  state.map.centerAndZoom(point, 16)
 
  // 再设置标记并获取地址
  setMarker(lnglat)
  getAddress(lnglat)
}
 
// TODO @super:lnglat 拼写
/**
 * 根据经纬度获取地址信息
 *
 * @param lnglat 经纬度数组
 */
const getAddress = (lnglat: any) => {
  const point = new window.BMapGL.Point(lnglat[0], lnglat[1])
 
  state.geocoder.getLocation(point, (result: any) => {
    if (result && result.address) {
      state.address = result.address
    }
  })
}
 
/** 显式暴露方法,使其可以被父组件访问 */
defineExpose({ regeoCode })
 
onMounted(() => {
  loadMap()
})
</script>
 
<style scoped>
.mapContainer {
  width: 100%;
  height: 400px;
}
</style>