DistributionMap.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <template>
  2. <div ref="containerRef" class="map-container">
  3. <div ref="mapRef" id="YztMap" class="map-container" :style="{ width: width, height: height }"></div>
  4. <div class="level-box">
  5. <i class="left-decoration" />
  6. <div class="box">
  7. <div v-show="!hideBox" class="box2">
  8. <div class="box-title">{{ legendTitle }}</div>
  9. <div v-for="(item, index) in legend" :key="index" class="box-item">
  10. <div class="checked-box" @click="handleClick(item)">
  11. <i :class="!!item.checked ? 'checked' : 'unchecked'" />
  12. <div class="text">{{ item.name }}</div>
  13. </div>
  14. <div class="line" :style="{ backgroundColor: item.color }" />
  15. </div>
  16. </div>
  17. <div class="btn" @click="hideBox = !hideBox">{{ hideBox ? '展开' : '隐藏' }}</div>
  18. </div>
  19. <i class="right-decoration" />
  20. </div>
  21. </div>
  22. </template>
  23. <script setup lang="ts">
  24. import 'ol/ol.css';
  25. import mmJson from '@/assets/json/mm.json';
  26. import { olMap } from '@/utils/olMap/olMap';
  27. import { deepClone } from '@/utils';
  28. interface Props {
  29. activeMap: string;
  30. points?: [];
  31. distributionData?: [];
  32. legendTitle?: string;
  33. }
  34. const containerScale = inject('containerScale');
  35. const props = withDefaults(defineProps<Props>(), {
  36. legendTitle: '风险等级'
  37. });
  38. const emits = defineEmits(['update:drawing', 'selectGraphics']);
  39. const mapRef = ref(null);
  40. const mapState = reactive({
  41. center: [110.925175, 22],
  42. zoom: 9.5,
  43. minZoom: 6,
  44. maxZoom: 16,
  45. isThreeDimensional: false,
  46. // 是否显示比例尺
  47. showScale: true
  48. });
  49. const containerRef = ref();
  50. const width = ref('100%');
  51. const height = ref('100%');
  52. let hideBox = ref(false);
  53. let yztMap, map;
  54. // 监听地图类型变化
  55. watch(
  56. () => props.activeMap,
  57. () => {
  58. if (!map) return;
  59. const id = props.activeMap === 'imageMap' ? ['YZT1640925052482', 'YZT1695608158269'] : ['YZT1708679726700', 'YZT1695608158269'];
  60. map.replaceLayers(id);
  61. }
  62. );
  63. watch(
  64. () => props.points,
  65. () => {
  66. if (props.points) {
  67. map.addMarker(props.points);
  68. }
  69. },
  70. {
  71. deep: true
  72. }
  73. );
  74. let legend = ref([]);
  75. // 更新分布图数据
  76. const updateMask = () => {
  77. if (!map || !map.createMask) return;
  78. const data = [];
  79. legend.value.forEach((item) => {
  80. if (!!item.checked) {
  81. data.push(item);
  82. }
  83. });
  84. map.createMask(data);
  85. };
  86. watch(
  87. () => props.distributionData,
  88. () => {
  89. const data = deepClone(props.distributionData);
  90. data.forEach((item) => {
  91. item.checked = true;
  92. });
  93. legend.value = data;
  94. updateMask();
  95. },
  96. {
  97. deep: true
  98. }
  99. );
  100. const init = () => {
  101. const id = props.activeMap === 'imageMap' ? ['YZT1640925052482', 'YZT1695608158269'] : ['YZT1708679726700', 'YZT1695608158269'];
  102. map = new olMap({
  103. dom: mapRef.value,
  104. id: id,
  105. center: mapState.center,
  106. zoom: mapState.zoom,
  107. minZoom: mapState.minZoom,
  108. maxZoom: mapState.maxZoom,
  109. // 加载完成事件
  110. onLoadCompleted: (yMap) => {
  111. yztMap = yMap;
  112. map.createVecByJson(mmJson, '茂名市');
  113. handleResize();
  114. map.addMarker(props.points);
  115. map.createMask(legend.value);
  116. }
  117. });
  118. };
  119. const addMarker = (points) => {
  120. map.addMarker(points);
  121. };
  122. const clearMarker = () => {
  123. map.clearMarker();
  124. };
  125. const handleClick = (item) => {
  126. item.checked = !item.checked;
  127. updateMask();
  128. };
  129. defineExpose({ addMarker, clearMarker });
  130. const handleResize = () => {
  131. let containerScaleData = !!containerScale ? containerScale() : {
  132. scaleX: 1,
  133. scaleY: 1
  134. };
  135. const containerWidth = containerRef.value.clientWidth * containerScaleData.scaleX;
  136. const containerHeight = containerRef.value.clientHeight * containerScaleData.scaleY;
  137. width.value = containerWidth + 'px';
  138. height.value = containerHeight + 'px';
  139. yztMap.updateSize();
  140. };
  141. // 加载事件
  142. onMounted(() => {
  143. init();
  144. window.addEventListener('resize', handleResize);
  145. });
  146. // 卸载事件
  147. onUnmounted(() => {
  148. window.removeEventListener('resize', handleResize);
  149. });
  150. </script>
  151. <style scoped>
  152. .map-container {
  153. width: 100%;
  154. height: 100%;
  155. position: relative;
  156. box-sizing: border-box;
  157. .level-box {
  158. position: absolute;
  159. bottom: 20px;
  160. left: 20px;
  161. display: flex;
  162. align-items: center;
  163. .left-decoration {
  164. display: inline-block;
  165. width: 6px;
  166. height: 58px;
  167. background: url('@/assets/images/censusDataAnalysis/decoration1.png') no-repeat;
  168. background-size: 100% 100%;
  169. }
  170. .right-decoration {
  171. display: inline-block;
  172. width: 5px;
  173. height: 58px;
  174. background: url('@/assets/images/censusDataAnalysis/decoration2.png') no-repeat;
  175. background-size: 100% 100%;
  176. }
  177. .box {
  178. //padding: 0 3px 0 13px;
  179. height: 53px;
  180. background: url('@/assets/images/censusDataAnalysis/box3.png') no-repeat;
  181. background-size: 100% 100%;
  182. display: flex;
  183. align-items: center;
  184. color: #ffffff;
  185. .box-title {
  186. width: 38px;
  187. font-size: 14px;
  188. letter-spacing: 3px;
  189. margin-right: 9px;
  190. margin-left: 10px;
  191. }
  192. .box-item {
  193. .checked-box {
  194. display: flex;
  195. align-items: center;
  196. margin-bottom: 3px;
  197. cursor: pointer;
  198. .checked {
  199. display: inline-block;
  200. width: 12px;
  201. height: 12px;
  202. background: url('@/assets/images/censusDataAnalysis/checked2.png') no-repeat;
  203. background-size: 100% 100%;
  204. margin-right: 5px;
  205. }
  206. .unchecked {
  207. display: inline-block;
  208. width: 12px;
  209. height: 12px;
  210. background: url('@/assets/images/censusDataAnalysis/checked1.png') no-repeat;
  211. background-size: 100% 100%;
  212. margin-right: 5px;
  213. }
  214. .text {
  215. font-size: 12px;
  216. }
  217. }
  218. .line {
  219. width: 70px;
  220. height: 9px;
  221. }
  222. }
  223. .box2 {
  224. margin-right: 22px;
  225. display: flex;
  226. align-items: center;
  227. }
  228. .btn {
  229. width: 20px;
  230. height: 49px;
  231. font-size: 12px;
  232. background: url('@/assets/images/censusDataAnalysis/btn2.png') no-repeat;
  233. background-size: 100% 100%;
  234. text-align: center;
  235. display: flex;
  236. align-items: center;
  237. cursor: pointer;
  238. }
  239. }
  240. }
  241. }
  242. </style>