map.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <div id="map-container"></div>
  3. </template>
  4. <script setup>
  5. import { ref, onMounted, onUnmounted } from "vue";
  6. import AMapLoader from "@amap/amap-jsapi-loader";
  7. import eventBus from "@/api/eventBus";
  8. import { convertPointToArray } from "@/utils/index";
  9. import { deepClone } from "@/common/commonFun";
  10. const map = ref(null);
  11. const mouseTool = ref(null);
  12. const selectedArea = ref(null);
  13. const selectedPoints = ref([]);
  14. const allPoints = ref([]);
  15. const defalutAllPoints = ref([])
  16. const pointList = ref([])
  17. const mapEventType = ref('rectangle')
  18. // 自定义圆点样式
  19. const createMarkerContent = (color = 'rgba(0, 0, 0, 0.4)') => {
  20. return `
  21. <div style="
  22. width: 15px;
  23. height: 15px;
  24. background-color: ${color};
  25. border: 1px solid #fff;
  26. border-radius: 50%;
  27. ">
  28. </div>
  29. `;
  30. }
  31. //高亮样式
  32. const createMarkerImg = () => {
  33. return `
  34. <img style="width: 38px;height: 38px;" src="${require('@/assets/images/map/status/active-icon.png')}">
  35. `;
  36. }
  37. // 初始化地图
  38. const initMap = async () => {
  39. try {
  40. const AMap = await AMapLoader.load({
  41. key: "41769169f0f157e13a197e7eb0dd7b5b", // 替换为你的高德地图 Key
  42. version: "2.0", // SDK 版本
  43. plugins: ["AMap.MouseTool"], // 加载 MouseTool 插件
  44. }).then((AMap) => {
  45. // 创建地图实例
  46. map.value = new AMap.Map("map-container", {
  47. zoom: 18, // 初始缩放级别
  48. center: [113.6149629 ,23.58594117],
  49. layers: [
  50. // 卫星
  51. new AMap.TileLayer.Satellite(),
  52. ],
  53. });
  54. // 初始化 MouseTool
  55. mouseTool.value = new AMap.MouseTool(map.value);
  56. startRectangleSelection()
  57. // 监听框选完成事件
  58. mouseTool.value.on("draw", (event) => {
  59. if(mapEventType.value==='click') return
  60. const { obj} = event;
  61. if (obj instanceof AMap.Rectangle) {
  62. selectedArea.value = obj;
  63. map.value.remove(selectedArea.value);
  64. checkPointsInArea(); // 检查框选区域内的点位
  65. }
  66. });
  67. });
  68. } catch (error) {
  69. console.error("地图加载失败:", error);
  70. }
  71. };
  72. //重置数据
  73. const resetData = () =>{
  74. allPoints.value = defalutAllPoints.value
  75. allPoints.value.forEach(item =>{
  76. item.icon.setContent(createMarkerContent(item.color))
  77. item.icon.setOffset(new window.AMap.Pixel(-7, -7))
  78. item.type = 'defalutIcon'
  79. })
  80. }
  81. //初始化数据
  82. const initData = () =>{
  83. defalutAllPoints.value = []
  84. totalList.value.forEach(item => map.value.remove(item.icon))
  85. totalList.value = []
  86. allPoints.value.forEach(item =>{
  87. item.icon = new window.AMap.Marker({
  88. position: item.lngLat,
  89. map: map.value,
  90. content: createMarkerContent(item.color),
  91. offset: new window.AMap.Pixel(-7, -7),
  92. });
  93. item.type = 'defalutIcon'
  94. // 绑定点击事件
  95. item.icon.on('click', () => {
  96. mapEventType.value = 'click'
  97. if(item.type === 'defalutIcon'){
  98. item.icon.setContent(createMarkerImg());
  99. item.icon.setOffset(new window.AMap.Pixel(-18, -18))
  100. item.type = 'activeIcon'
  101. const arr = pointList.value.filter(ele => ele.id === item.id)
  102. if(arr.length===0){
  103. pointList.value.push(item)
  104. }
  105. }else{
  106. item.icon.setContent(createMarkerContent(item.color));
  107. item.icon.setOffset(new window.AMap.Pixel(-7, -7))
  108. item.type = 'defalutIcon'
  109. }
  110. setTimeout(()=>{
  111. mapEventType.value = 'draw'
  112. },100)
  113. });
  114. defalutAllPoints.value.push(item)
  115. })
  116. map.value.setFitView(null, false, [0, 0, 0, 0]);
  117. }
  118. // 开始框选
  119. const startRectangleSelection = () => {
  120. if (mouseTool.value) {
  121. mouseTool.value.rectangle({
  122. strokeColor: "#ffffff", // 框选区域边框颜色
  123. strokeOpacity: 1, // 边框透明度
  124. strokeWeight: 2, // 边框宽度
  125. fillColor: "#000000", // 填充颜色
  126. fillOpacity: 0.5, // 填充透明度
  127. }); // 启用矩形框选工具
  128. map.value.setDefaultCursor("crosshair");
  129. }
  130. };
  131. // 停止绘制矩形
  132. const stopDrawRectangle = () => {
  133. mouseTool.value.close(); // 关闭 mouseTool
  134. map.value.setDefaultCursor("default");
  135. };
  136. // 清除框选
  137. const clearSelection = () => {
  138. if (selectedArea.value) {
  139. map.value.remove(selectedArea.value); // 移除框选区域
  140. selectedArea.value = null;
  141. }
  142. resetData()
  143. selectedPoints.value = []; // 清空选中的点位
  144. };
  145. // 检查框选区域内的点位
  146. const checkPointsInArea = () => {
  147. if (!selectedArea.value) return;
  148. const bounds = selectedArea.value.getBounds(); // 获取框选区域的边界
  149. selectedPoints.value = allPoints.value.filter((point) => {
  150. return bounds.contains(point.lngLat); // 判断点位是否在框选区域内
  151. });
  152. selectedPoints.value.forEach(item =>{
  153. if(item.type==='defalutIcon'){
  154. item.icon.setContent(createMarkerImg())
  155. item.icon.setOffset(new window.AMap.Pixel(-18, -18))
  156. item.type = 'activeIcon'
  157. }else{
  158. item.icon.setContent(createMarkerContent(item.color))
  159. item.icon.setOffset(new window.AMap.Pixel(-7, -7))
  160. item.type = 'defalutIcon'
  161. }
  162. })
  163. };
  164. const speciesArr = ref([])
  165. const getList = () =>{
  166. const params = {
  167. farmId:sessionStorage.getItem('farmId'),
  168. regionId:sessionStorage.getItem('regionId')
  169. }
  170. VE_API.variety.pointList(params).then(res =>{
  171. allPoints.value = res.data.map(item =>{
  172. const bgColor = speciesArr.value.filter(ele =>ele.speciesItemId === item.speciesItemId)
  173. return{
  174. ...item,
  175. color:bgColor[0].color,
  176. lngLat:convertPointToArray(item.point)
  177. }
  178. })
  179. initData()
  180. })
  181. }
  182. const totalList = ref([])
  183. function getSelectPoint(){
  184. totalList.value = selectedPoints.value.concat(pointList.value)
  185. const arr = totalList.value.filter(item =>item.type==='activeIcon')
  186. return arr
  187. }
  188. defineExpose({getSelectPoint})
  189. function getSpeciesList(arr){
  190. speciesArr.value = arr
  191. getList()
  192. }
  193. // 组件挂载时初始化地图
  194. onMounted(() => {
  195. initMap()
  196. eventBus.on('handle:clear',clearSelection)
  197. eventBus.on('species:list',getSpeciesList)
  198. eventBus.on('handle:start',startRectangleSelection)
  199. eventBus.on('handle:end',stopDrawRectangle)
  200. });
  201. onUnmounted(()=>{
  202. map.value.destroy()// 销毁地图实例以释放资源
  203. eventBus.off('handle:clear',clearSelection)
  204. eventBus.off('species:list',getSpeciesList)
  205. eventBus.off('handle:start',startRectangleSelection)
  206. eventBus.off('handle:end',stopDrawRectangle)
  207. })
  208. </script>
  209. <style scoped>
  210. #map-container {
  211. width: 100%;
  212. height: 100%;
  213. }
  214. </style>