map.vue 8.0 KB

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