selectLocation.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <template>
  2. <div class="select-location">
  3. <custom-header :name="headerName"></custom-header>
  4. <div class="select-location-content">
  5. <div class="search-bar">
  6. <location-search
  7. class="location-search"
  8. :user-location="userLocation"
  9. @change="handleLocationChange"
  10. />
  11. </div>
  12. <div class="map-container" ref="mapContainer"></div>
  13. <div class="locate-btn" @click="handleLocate">
  14. <img class="locate-icon" src="@/assets/img/map/map-icon.png" alt="" />
  15. </div>
  16. </div>
  17. <div class="custom-bottom-fixed-btns">
  18. <div class="bottom-btn primary-btn" @click="handleSubmit">确认点位</div>
  19. </div>
  20. <add-zone-info-popup
  21. v-if="fromAgriAlbum"
  22. v-model:show="showZoneInfoPopup"
  23. :county-code="pendingZoneLocation?.adcode"
  24. :point-location="pendingZoneLocation?.point || ''"
  25. @confirm="handleZoneInfoConfirm"
  26. />
  27. </div>
  28. </template>
  29. <script setup>
  30. import { onMounted, onUnmounted, ref } from "vue";
  31. import { useRouter, useRoute } from "vue-router";
  32. import { useStore } from "vuex";
  33. import { ElMessage } from "element-plus";
  34. import customHeader from "@/components/customHeader.vue";
  35. import locationSearch from "@/components/pageComponents/locationSearch.vue";
  36. import SelectLocationMap, { mapLocation, POINT_ICON } from "./map/selectLocationMap.js";
  37. import { convertPointToArray } from "@/utils/index";
  38. import addZoneInfoPopup from "@/views/old_mini/agri_file/components/addZoneInfoPopup.vue";
  39. const SESSION_KEY_RESIDENT = "ENTRY_RESIDENT_LOCATION";
  40. const SESSION_KEY_GROWTH = "GROWTH_REPORT_LOCATION";
  41. const DEFAULT_POINT = "POINT(113.6142086995688 23.585836479509055)";
  42. const MAP_KEY = "CZLBZ-LJICQ-R4A5J-BN62X-YXCRJ-GNBUT";
  43. const router = useRouter();
  44. const route = useRoute();
  45. const store = useStore();
  46. const mapContainer = ref(null);
  47. const isPlantPoint = route.query.pointType === "plant";
  48. const fromGrowthReport = route.query.from === "growth_report";
  49. const fromService = route.query.from === "entry_service";
  50. const fromAgriAlbum = route.query.from === "agri_album";
  51. const headerName = fromGrowthReport ? "切换位置" : fromService ? "选择位置" : "选择种植点位";
  52. const sessionKey = fromGrowthReport ? SESSION_KEY_GROWTH : SESSION_KEY_RESIDENT;
  53. const iconSrc =
  54. route.query.iconType && POINT_ICON[route.query.iconType]
  55. ? POINT_ICON[route.query.iconType]
  56. : isPlantPoint
  57. ? POINT_ICON.plant
  58. : POINT_ICON.resident;
  59. const userLocation = ref(
  60. store.state.home.miniUserLocation || localStorage.getItem("MINI_USER_LOCATION") || "113.61702297075017,23.584863449735067"
  61. );
  62. /** 搜索选中时带出的区县编码 */
  63. const selectedAdcode = ref("");
  64. /** 搜索选中时带出的地址文案 */
  65. const selectedAddress = ref("");
  66. const showZoneInfoPopup = ref(false);
  67. const pendingZoneLocation = ref(null);
  68. const selectLocationMap = new SelectLocationMap({
  69. iconSrc,
  70. onMapClick: () => {
  71. selectedAdcode.value = "";
  72. selectedAddress.value = "";
  73. },
  74. });
  75. function getDefaultPoint() {
  76. if (route.query.mapCenter) return route.query.mapCenter;
  77. return (
  78. localStorage.getItem("MINI_USER_LOCATION_POINT") ||
  79. store.state.home.miniUserLocationPoint ||
  80. DEFAULT_POINT
  81. );
  82. }
  83. function toPointWkt(coordinate) {
  84. const lng = Number(coordinate[0]);
  85. const lat = Number(coordinate[1]);
  86. return `POINT(${lng} ${lat})`;
  87. }
  88. async function fetchLocationInfoByCoordinate(coordinate) {
  89. if (!coordinate?.length) return { adcode: "", address: "" };
  90. try {
  91. const { result } = await VE_API.old_mini_map.location({
  92. key: MAP_KEY,
  93. location: `${coordinate[1]},${coordinate[0]}`,
  94. });
  95. const adcode = result?.ad_info?.adcode;
  96. const address =
  97. result?.formatted_addresses?.recommend ||
  98. result?.address ||
  99. (result?.address_component
  100. ? `${result.address_component.city || ""}${result.address_component.district || ""}`
  101. : "");
  102. return {
  103. adcode: adcode != null && adcode !== "" ? String(adcode) : "",
  104. address: address || "",
  105. };
  106. } catch {
  107. return { adcode: "", address: "" };
  108. }
  109. }
  110. onMounted(() => {
  111. const point = getDefaultPoint();
  112. selectLocationMap.initMap(point, mapContainer.value);
  113. });
  114. onUnmounted(() => {
  115. selectLocationMap.clearLayer();
  116. });
  117. const handleLocationChange = (payload) => {
  118. if (!payload?.coordinateArray) {
  119. selectedAdcode.value = "";
  120. selectedAddress.value = "";
  121. return;
  122. }
  123. selectedAdcode.value = payload.adcode ? String(payload.adcode) : "";
  124. selectedAddress.value = payload.address || payload.pointAddress || "";
  125. selectLocationMap.setMapPosition(payload.coordinateArray);
  126. };
  127. const handleLocate = () => {
  128. selectedAdcode.value = "";
  129. selectedAddress.value = "";
  130. const point =
  131. localStorage.getItem("MINI_USER_LOCATION_POINT") ||
  132. store.state.home.miniUserLocationPoint ||
  133. DEFAULT_POINT;
  134. const coordinate = convertPointToArray(point).map(Number);
  135. selectLocationMap.setMapPosition(coordinate);
  136. };
  137. const handleSubmit = async () => {
  138. const coordinate = mapLocation.data;
  139. if (!coordinate) {
  140. ElMessage.warning(
  141. fromGrowthReport || fromService ? "请选择位置" : isPlantPoint ? "请选择种植点位" : "请选择常驻点位"
  142. );
  143. return;
  144. }
  145. const point = toPointWkt(coordinate);
  146. let adcode = selectedAdcode.value;
  147. let address = selectedAddress.value;
  148. if (!adcode || !address) {
  149. const info = await fetchLocationInfoByCoordinate(coordinate);
  150. if (!adcode) adcode = info.adcode;
  151. if (!address) address = info.address;
  152. }
  153. const payload = {
  154. point,
  155. coordinate: [Number(coordinate[0]), Number(coordinate[1])],
  156. adcode: adcode || "",
  157. address: address || "",
  158. };
  159. if (fromAgriAlbum) {
  160. payload.zoneType = route.query.zoneType || "region";
  161. pendingZoneLocation.value = payload;
  162. showZoneInfoPopup.value = true;
  163. return;
  164. }
  165. sessionStorage.setItem(sessionKey, JSON.stringify(payload));
  166. router.back();
  167. };
  168. const handleZoneInfoConfirm = () => {
  169. pendingZoneLocation.value = null;
  170. router.back();
  171. };
  172. </script>
  173. <style lang="scss" scoped>
  174. .select-location {
  175. width: 100%;
  176. height: 100vh;
  177. overflow: hidden;
  178. background: #fff;
  179. display: flex;
  180. flex-direction: column;
  181. .select-location-content {
  182. position: relative;
  183. flex: 1;
  184. overflow: hidden;
  185. .search-bar {
  186. position: absolute;
  187. top: 12px;
  188. left: 12px;
  189. right: 12px;
  190. z-index: 2;
  191. }
  192. .map-container {
  193. width: 100%;
  194. height: 100%;
  195. }
  196. .locate-btn {
  197. position: absolute;
  198. right: 12px;
  199. bottom: 40px;
  200. z-index: 2;
  201. width: 32px;
  202. height: 32px;
  203. display: flex;
  204. align-items: center;
  205. justify-content: center;
  206. background: #fff;
  207. border-radius: 8px;
  208. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  209. .locate-icon {
  210. width: 16px;
  211. height: 18px;
  212. }
  213. }
  214. }
  215. .custom-bottom-fixed-btns {
  216. position: relative;
  217. background: #fff;
  218. box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.4);
  219. .bottom-btn {
  220. padding: 0 30px;
  221. height: 40px;
  222. line-height: 40px;
  223. font-size: 14px;
  224. border-radius: 25px;
  225. }
  226. .primary-btn {
  227. background: #2199f8;
  228. color: #fff;
  229. }
  230. }
  231. }
  232. </style>