index.uvue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <template>
  2. <view class="map-page">
  3. <!-- 地图组件 -->
  4. <map-view
  5. ref="mapViewRef"
  6. :latitude="latitude"
  7. :longitude="longitude"
  8. :zoom="zoom"
  9. :markers="markers"
  10. :show-location="true"
  11. @markerTap="handleMarkerTap"
  12. @locationUpdate="handleLocationUpdate"
  13. @layerChange="handleLayerChange"
  14. @layerChangeError="handleLayerChangeError"
  15. />
  16. <!-- 操作面板 -->
  17. <view class="operation-panel">
  18. <view class="panel-btn" @click="handleAddMarker">
  19. <text class="panel-btn-text">添加标记</text>
  20. </view>
  21. <view class="panel-btn" @click="handleAddRoute">
  22. <text class="panel-btn-text">绘制路线</text>
  23. </view>
  24. <view class="panel-btn" @click="handleAddCircle">
  25. <text class="panel-btn-text">绘制圆形</text>
  26. </view>
  27. <view class="panel-btn" @click="handleClear">
  28. <text class="panel-btn-text">清除覆盖物</text>
  29. </view>
  30. </view>
  31. </view>
  32. </template>
  33. <script setup lang="uts">
  34. import { ref, type ComponentPublicInstance } from 'vue'
  35. import type { MapMarker, MapLayerType } from '../../../types/map'
  36. // 地图引用(使用 ComponentPublicInstance 类型)
  37. const mapViewRef = ref<ComponentPublicInstance | null>(null)
  38. // 地图中心点
  39. const latitude = ref<number>(32.556211)
  40. const longitude = ref<number>(111.494811)
  41. // 地图缩放级别(3-18,数字越大越详细)
  42. const zoom = ref<number>(13)
  43. // 标记点列表(初始为空)
  44. const markers = ref<MapMarker[]>([])
  45. // 路线数据类型
  46. type RouteData = {
  47. points: any[]
  48. color: string
  49. width: number
  50. name: string
  51. }
  52. // 路线列表
  53. const routes = ref<RouteData[]>([])
  54. // 圆形数据类型
  55. type CircleData = {
  56. latitude: number
  57. longitude: number
  58. radius: number
  59. strokeColor: string
  60. fillColor: string
  61. name: string
  62. }
  63. // 圆形列表
  64. const circles = ref<CircleData[]>([])
  65. /**
  66. * 圆形半径(单位:米,可修改)
  67. * 示例:
  68. * - 1000 = 1公里
  69. * - 5000 = 5公里
  70. * - 10000 = 10公里
  71. * - 20000 = 20公里
  72. */
  73. const circleRadius = ref<number>(1000)
  74. // 标记点点击
  75. const handleMarkerTap = (e: any): void => {
  76. console.log('标记点击事件:', e)
  77. uni.showToast({
  78. title: '标记被点击',
  79. icon: 'none'
  80. })
  81. }
  82. // 位置更新
  83. const handleLocationUpdate = (location: any): void => {
  84. // 提取属性(any 类型不能直接访问属性)
  85. const locationObj = location as UTSJSONObject
  86. const lat = locationObj['latitude'] as number
  87. const lon = locationObj['longitude'] as number
  88. latitude.value = lat
  89. longitude.value = lon
  90. }
  91. // 模拟接口请求,获取标记点数据
  92. const fetchMarkers = async (): Promise<MapMarker[]> => {
  93. // 模拟网络延迟
  94. await new Promise<void>((resolve) => {
  95. setTimeout(() => {
  96. resolve()
  97. }, 800)
  98. })
  99. // 在地图中心点周围生成50个随机标记点
  100. const mockMarkers: MapMarker[] = []
  101. const baseId = markers.value.length
  102. // 项目类型列表
  103. const projectTypes = ['水库', '河流', '水闸', '泵站', '灌区', '堤防', '水文站']
  104. for (let i = 0; i < 50; i++) {
  105. // 在中心点周围0.1度范围内随机生成(约11公里范围)
  106. const randomLat = latitude.value + (Math.random() - 0.5) * 0.1
  107. const randomLng = longitude.value + (Math.random() - 0.5) * 0.1
  108. const projectType = projectTypes[Math.floor(Math.random() * projectTypes.length)]
  109. mockMarkers.push({
  110. id: baseId + i + 1,
  111. latitude: randomLat,
  112. longitude: randomLng,
  113. title: `${projectType}${i + 1}`,
  114. iconPath: '/static/images/map/project.png',
  115. width: 32,
  116. height: 32
  117. })
  118. }
  119. return mockMarkers
  120. }
  121. // 模拟接口请求,获取路线数据
  122. const fetchRoutes = async (): Promise<RouteData[]> => {
  123. // 模拟网络延迟
  124. await new Promise<void>((resolve) => {
  125. setTimeout(() => {
  126. resolve()
  127. }, 600)
  128. })
  129. // 生成3条路线的模拟数据
  130. const mockRoutes: RouteData[] = []
  131. // 路线1:东西向路线(红色)
  132. const route1Points: any[] = []
  133. const startLat1 = latitude.value + 0.02
  134. for (let i = 0; i < 10; i++) {
  135. const lng = longitude.value - 0.03 + i * 0.006
  136. const lat = startLat1 + Math.sin(i * 0.5) * 0.005
  137. route1Points.push({
  138. latitude: lat,
  139. longitude: lng
  140. })
  141. }
  142. mockRoutes.push({
  143. points: route1Points,
  144. color: '#FF5733',
  145. width: 4,
  146. name: '主干道路线'
  147. })
  148. // 路线2:南北向路线(蓝色)
  149. const route2Points: any[] = []
  150. const startLng2 = longitude.value - 0.02
  151. for (let i = 0; i < 8; i++) {
  152. const lat = latitude.value - 0.02 + i * 0.005
  153. const lng = startLng2 + Math.cos(i * 0.6) * 0.008
  154. route2Points.push({
  155. latitude: lat,
  156. longitude: lng
  157. })
  158. }
  159. mockRoutes.push({
  160. points: route2Points,
  161. color: '#3498DB',
  162. width: 5,
  163. name: '河流路线'
  164. })
  165. // 路线3:环形路线(绿色)
  166. const route3Points: any[] = []
  167. const centerLat = latitude.value - 0.01
  168. const centerLng = longitude.value + 0.01
  169. const radius = 0.015
  170. for (let i = 0; i <= 12; i++) {
  171. const angle = i * Math.PI / 6
  172. const lat = centerLat + radius * Math.sin(angle)
  173. const lng = centerLng + radius * Math.cos(angle)
  174. route3Points.push({
  175. latitude: lat,
  176. longitude: lng
  177. })
  178. }
  179. mockRoutes.push({
  180. points: route3Points,
  181. color: '#27AE60',
  182. width: 3,
  183. name: '环形巡查路线'
  184. })
  185. return mockRoutes
  186. }
  187. // 添加标记
  188. const handleAddMarker = async (): Promise<void> => {
  189. try {
  190. uni.showLoading({
  191. title: '加载中...'
  192. })
  193. // 模拟接口请求
  194. const newMarkers = await fetchMarkers()
  195. // 添加到列表
  196. for (let i = 0; i < newMarkers.length; i++) {
  197. markers.value.push(newMarkers[i])
  198. }
  199. // 批量添加到地图
  200. if (mapViewRef.value != null) {
  201. mapViewRef.value.$callMethod('addMarkers', newMarkers)
  202. }
  203. uni.hideLoading()
  204. uni.showToast({
  205. title: `成功添加${newMarkers.length}个标记点`,
  206. icon: 'success'
  207. })
  208. } catch (e: any) {
  209. uni.hideLoading()
  210. uni.showToast({
  211. title: '加载失败',
  212. icon: 'none'
  213. })
  214. }
  215. }
  216. // 绘制路线
  217. const handleAddRoute = async (): Promise<void> => {
  218. try {
  219. uni.showLoading({
  220. title: '加载中...'
  221. })
  222. // 模拟接口请求
  223. const newRoutes = await fetchRoutes()
  224. // 添加到列表
  225. for (let i = 0; i < newRoutes.length; i++) {
  226. routes.value.push(newRoutes[i])
  227. }
  228. // 批量绘制路线
  229. if (mapViewRef.value != null) {
  230. for (let i = 0; i < newRoutes.length; i++) {
  231. const route = newRoutes[i]
  232. mapViewRef.value.$callMethod('addPolyline', route.points, route.color, route.width)
  233. }
  234. }
  235. uni.hideLoading()
  236. uni.showToast({
  237. title: `成功绘制${newRoutes.length}条路线`,
  238. icon: 'success'
  239. })
  240. } catch (e: any) {
  241. uni.hideLoading()
  242. uni.showToast({
  243. title: '加载失败',
  244. icon: 'none'
  245. })
  246. }
  247. }
  248. // 绘制圆形
  249. const handleAddCircle = (): void => {
  250. if (mapViewRef.value != null) {
  251. // 以地图中心点为圆心绘制圆形
  252. const circle: CircleData = {
  253. latitude: latitude.value,
  254. longitude: longitude.value,
  255. radius: circleRadius.value,
  256. strokeColor: 'rgb(12, 113, 247)',
  257. fillColor: 'rgb(12, 113, 247)',
  258. name: `圆形-${circles.value.length + 1}`
  259. }
  260. // 添加到列表
  261. circles.value.push(circle)
  262. // 绘制到地图
  263. mapViewRef.value.$callMethod(
  264. 'addCircle',
  265. circle.latitude,
  266. circle.longitude,
  267. circle.radius,
  268. circle.strokeColor,
  269. circle.fillColor
  270. )
  271. const radiusKm = circleRadius.value / 1000
  272. }
  273. }
  274. // 清除覆盖物
  275. const handleClear = (): void => {
  276. if (mapViewRef.value != null) {
  277. // 清除覆盖物(路线、圆形、多边形)
  278. mapViewRef.value.$callMethod('clearOverlays')
  279. // 清除标记点
  280. mapViewRef.value.$callMethod('clearMarkers')
  281. // 清空本地列表
  282. markers.value = []
  283. routes.value = []
  284. circles.value = []
  285. uni.showToast({
  286. title: '清除成功',
  287. icon: 'success'
  288. })
  289. }
  290. }
  291. // 图层切换成功事件
  292. const handleLayerChange = (layerType: MapLayerType): void => {
  293. console.log('图层切换成功:', layerType)
  294. // 可以在这里执行图层切换后的额外逻辑
  295. // 例如:刷新地图标记、重新加载数据等
  296. }
  297. // 图层切换失败事件
  298. const handleLayerChangeError = (error: string): void => {
  299. console.error('图层切换失败:', error)
  300. // 可以在这里执行错误处理逻辑
  301. // 例如:记录错误日志、上报监控等
  302. }
  303. </script>
  304. <style lang="scss">
  305. .map-page {
  306. position: relative;
  307. width: 100%;
  308. height: 100%;
  309. }
  310. .operation-panel {
  311. position: absolute;
  312. left: 20rpx;
  313. bottom: 30rpx;
  314. flex-direction: column;
  315. }
  316. .panel-btn {
  317. margin-bottom: 15rpx;
  318. padding: 0 30rpx;
  319. height: 70rpx;
  320. background-color: #ffffff;
  321. border-radius: 8rpx;
  322. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
  323. justify-content: center;
  324. align-items: center;
  325. &-text {
  326. font-size: 26rpx;
  327. color: #333333;
  328. }
  329. }
  330. </style>