EvaluationTrafficMap.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <template>
  2. <div class="map-wrapper">
  3. <div ref="mapContainer" class="map-container"></div>
  4. </div>
  5. </template>
  6. <script>
  7. import AMapLoader from '@amap/amap-jsapi-loader';
  8. export default {
  9. name: "EvaluationTrafficMap",
  10. props: {
  11. amapKey: { type: String, required: true },
  12. securityJsCode: { type: String, required: true },
  13. mode: {
  14. type: String,
  15. default: 'overview',
  16. validator: (val) => ['overview', 'singlePoint', 'trunkLine', 'area'].includes(val)
  17. }
  18. },
  19. data() {
  20. return {
  21. AMap: null,
  22. map: null,
  23. overlays: [], // 统一管理当前地图上的所有覆盖物
  24. };
  25. },
  26. watch: {
  27. // 监听模式变化,重新绘制地图元素
  28. mode(newMode) {
  29. this.updateMapDisplay();
  30. }
  31. },
  32. mounted() {
  33. this.initAMap();
  34. },
  35. beforeDestroy() {
  36. if (this.map) {
  37. this.map.destroy();
  38. this.map = null;
  39. }
  40. },
  41. methods: {
  42. async initAMap() {
  43. window._AMapSecurityConfig = { securityJsCode: this.securityJsCode };
  44. try {
  45. const AMap = await AMapLoader.load({
  46. key: this.amapKey,
  47. version: "2.0",
  48. });
  49. this.AMap = AMap;
  50. this.map = new AMap.Map(this.$refs.mapContainer, {
  51. zoom: 13,
  52. mapStyle: "amap://styles/darkblue",
  53. center: [116.66, 39.91], // 通州区
  54. });
  55. this.map.on('complete', () => {
  56. this.updateMapDisplay();
  57. });
  58. } catch (err) {
  59. console.error('地图加载失败:', err);
  60. }
  61. },
  62. clearOverlays() {
  63. if (this.map && this.overlays.length > 0) {
  64. this.map.remove(this.overlays);
  65. this.overlays = [];
  66. }
  67. },
  68. updateMapDisplay() {
  69. if (!this.map || !this.AMap) return;
  70. this.clearOverlays();
  71. switch (this.mode) {
  72. case 'overview':
  73. this.drawOverview();
  74. break;
  75. case 'singlePoint':
  76. this.drawSinglePoint();
  77. break;
  78. case 'trunkLine':
  79. this.drawTrunkLine();
  80. break;
  81. case 'area':
  82. this.drawArea();
  83. break;
  84. }
  85. },
  86. // 1. 绘制总览:简单的红黄绿圆点
  87. drawOverview() {
  88. const mockData = [
  89. { pos: [116.65, 39.90], color: '#00FF00' }, { pos: [116.66, 39.91], color: '#FF0000' },
  90. { pos: [116.67, 39.89], color: '#FFA500' }, { pos: [116.69, 39.92], color: '#00FF00' },
  91. { pos: [116.64, 39.93], color: '#FF0000' }, { pos: [116.70, 39.90], color: '#FFA500' },
  92. { pos: [116.68, 39.91], color: '#00FF00' }, { pos: [116.66, 39.93], color: '#FF0000' },
  93. ];
  94. mockData.forEach(item => {
  95. const marker = new this.AMap.Marker({
  96. position: item.pos,
  97. content: `<div style="width: 12px; height: 12px; background-color: ${item.color}; border-radius: 50%; box-shadow: 0 0 5px ${item.color}; border: 1px solid #fff;"></div>`,
  98. offset: new this.AMap.Pixel(-6, -6),
  99. });
  100. this.overlays.push(marker);
  101. });
  102. this.map.add(this.overlays);
  103. },
  104. // 2. 绘制评价监测-单点:带 A-F 字母和对应颜色的点
  105. drawSinglePoint() {
  106. const colors = { A: '#8fc31f', B: '#d7df23', C: '#fff200', D: '#f39c12', E: '#e74c3c', F: '#c0392b' };
  107. const mockData = [
  108. { pos: [116.63, 39.90], level: 'A' }, { pos: [116.65, 39.91], level: 'B' },
  109. { pos: [116.66, 39.89], level: 'C' }, { pos: [116.68, 39.88], level: 'D' },
  110. { pos: [116.70, 39.91], level: 'E' }, { pos: [116.71, 39.90], level: 'F' },
  111. { pos: [116.67, 39.90], level: 'B' }, { pos: [116.69, 39.89], level: 'C' },
  112. { pos: [116.65, 39.88], level: 'A' }, { pos: [116.69, 39.87], level: 'D' },
  113. ];
  114. mockData.forEach(item => {
  115. const marker = new this.AMap.Marker({
  116. position: item.pos,
  117. content: `
  118. <div style="width: 20px; height: 20px; background-color: ${colors[item.level]}; border-radius: 50%; display: flex; justify-content: center; align-items: center; color: #fff; font-weight: bold; font-size: 12px; border: 2px solid rgba(255,255,255,0.5);">
  119. ${item.level}
  120. </div>
  121. `,
  122. offset: new this.AMap.Pixel(-10, -10),
  123. });
  124. this.overlays.push(marker);
  125. });
  126. this.map.add(this.overlays);
  127. },
  128. // 3. 绘制评价监测-干线:绿色波段线段
  129. drawTrunkLine() {
  130. const lines = [
  131. [[116.65, 39.91], [116.69, 39.92]],
  132. [[116.64, 39.90], [116.70, 39.91]],
  133. [[116.65, 39.89], [116.70, 39.89]],
  134. [[116.66, 39.88], [116.68, 39.85]],
  135. ];
  136. lines.forEach(path => {
  137. // 画线
  138. const polyline = new this.AMap.Polyline({
  139. path: path,
  140. isOutline: true,
  141. outlineColor: '#fff',
  142. borderWeight: 1,
  143. strokeColor: "#a6ce39",
  144. strokeOpacity: 0.9,
  145. strokeWeight: 5,
  146. });
  147. this.overlays.push(polyline);
  148. // 在线段两端或中间画点作为路口标识
  149. path.forEach(pos => {
  150. const marker = new this.AMap.Marker({
  151. position: pos,
  152. content: `<div style="width: 10px; height: 10px; background-color: #fff; border-radius: 50%; border: 2px solid #a6ce39;"></div>`,
  153. offset: new this.AMap.Pixel(-5, -5),
  154. });
  155. this.overlays.push(marker);
  156. });
  157. });
  158. this.map.add(this.overlays);
  159. },
  160. // 4. 绘制评价监测-区域:半透明多边形 + 中心文字
  161. drawArea() {
  162. const areas = [
  163. {
  164. path: [[116.67, 39.94], [116.70, 39.94], [116.71, 39.92], [116.70, 39.91], [116.67, 39.91]],
  165. color: '#d35400', label: '6.2', center: [116.69, 39.925]
  166. },
  167. {
  168. path: [[116.68, 39.90], [116.72, 39.90], [116.72, 39.88], [116.68, 39.88]],
  169. color: '#c0392b', label: '4.5', center: [116.70, 39.89]
  170. },
  171. {
  172. path: [[116.67, 39.87], [116.71, 39.87], [116.71, 39.85], [116.67, 39.85]],
  173. color: '#c0392b', label: '4.8', center: [116.69, 39.86]
  174. }
  175. ];
  176. areas.forEach(area => {
  177. // 绘制多边形
  178. const polygon = new this.AMap.Polygon({
  179. path: area.path,
  180. fillColor: area.color,
  181. fillOpacity: 0.4,
  182. strokeColor: area.color,
  183. strokeWeight: 2,
  184. });
  185. this.overlays.push(polygon);
  186. // 绘制中心数字标识
  187. const textMarker = new this.AMap.Marker({
  188. position: area.center,
  189. content: `<div style="color: rgba(255,255,255,0.8); font-size: 24px; font-weight: bold; text-shadow: 1px 1px 2px #000;">${area.label}</div>`,
  190. offset: new this.AMap.Pixel(-15, -15),
  191. });
  192. this.overlays.push(textMarker);
  193. });
  194. // 画一个圆形的区域 (对应图中的绿色 9.1)
  195. const circle = new this.AMap.Circle({
  196. center: [116.64, 39.88],
  197. radius: 1800,
  198. fillColor: '#27ae60',
  199. fillOpacity: 0.4,
  200. strokeColor: '#27ae60',
  201. strokeWeight: 2,
  202. });
  203. this.overlays.push(circle);
  204. const circleText = new this.AMap.Marker({
  205. position: [116.64, 39.88],
  206. content: `<div style="color: rgba(255,255,255,0.8); font-size: 24px; font-weight: bold; text-shadow: 1px 1px 2px #000;">9.1</div>`,
  207. offset: new this.AMap.Pixel(-15, -15),
  208. });
  209. this.overlays.push(circleText);
  210. this.map.add(this.overlays);
  211. }
  212. }
  213. };
  214. </script>
  215. <style scoped>
  216. .map-wrapper {
  217. width: 100%;
  218. height: 100vh;
  219. position: relative;
  220. background: #010813;
  221. }
  222. .map-container {
  223. width: 100%;
  224. height: 100%;
  225. }
  226. /* 隐藏高德Logo等 */
  227. ::v-deep .amap-logo,
  228. ::v-deep .amap-copyright,
  229. ::v-deep .amap-copyright-logo {
  230. display: none !important;
  231. }
  232. </style>