darwArea.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <template>
  2. <div class="edit-map">
  3. <custom-header :name="viewOnly ? '查看区域' : '勾选地块'"></custom-header>
  4. <div class="variety-tabs" v-if="varietyTabs.length > 0">
  5. <div
  6. v-for="(v, index) in varietyTabs"
  7. :key="index"
  8. class="variety-tab"
  9. :class="{ 'variety-tab--active': activeVariety === index }"
  10. @click="handleVarietyClick(v, index)"
  11. >
  12. {{ v.regionName }}
  13. </div>
  14. </div>
  15. <div class="edit-map-content">
  16. <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
  17. <div class="map-container" ref="mapContainer"></div>
  18. <div class="edit-map-footer">
  19. <div class="footer-back" @click="goBack">
  20. <img class="back-icon" src="@/assets/img/home/go-back.png" alt="" />
  21. </div>
  22. <div class="edit-map-footer-btn" v-if="!viewOnly">
  23. <!-- <div class="edit-map-footer-btn confirm-btn-box" v-if="!viewOnly"> -->
  24. <div class="btn-reset" @click="resetPolygon">重置区域</div>
  25. <div class="btn-confirm" @click="confirm">确认</div>
  26. <!-- <div class="btn-confirm" @click="confirm">编辑区域</div> -->
  27. </div>
  28. </div>
  29. </div>
  30. <save-region-success-popup
  31. v-if="!viewOnly"
  32. v-model:show="showSuccessPopup"
  33. :title="`${activeVarietyName} 区域已保存成功`"
  34. :has-next="varietyTabs.length > 0 && activeVariety < varietyTabs.length - 1"
  35. @know="handleKnow"
  36. @next="handleSelectNextVariety"
  37. />
  38. </div>
  39. </template>
  40. <script setup>
  41. import customHeader from "@/components/customHeader.vue";
  42. import SaveRegionSuccessPopup from "@/components/popup/saveRegionSuccessPopup.vue";
  43. import { ref, computed, onMounted, onActivated, onDeactivated } from "vue";
  44. import { useStore } from "vuex";
  45. import DrawRegionMap from "../../interactionList/map/drawRegionMap.js";
  46. import { useRouter, useRoute } from "vue-router";
  47. import { convertPointToArray } from "@/utils/index";
  48. import { ElMessage, ElMessageBox } from "element-plus";
  49. const store = useStore();
  50. const router = useRouter();
  51. const route = useRoute();
  52. const mapContainer = ref(null);
  53. const drawRegionMap = new DrawRegionMap();
  54. const showSuccessPopup = ref(false);
  55. // 是否从第一个品种开始的引导流程(需要全部品种都确认后再退出)
  56. const isGuidedFlow = ref(false);
  57. const type = ref(null);
  58. const varietyTabs = ref([]);
  59. const activeVariety = ref(0);
  60. const farmIdData = ref(null);
  61. const gardenId = ref(store.state.home.gardenId);
  62. const getVarietyTabs = async () => {
  63. if (!gardenId.value) return;
  64. try {
  65. const res = await VE_API.monitor.listRegionsBySubjectId({
  66. subjectId: gardenId.value,
  67. });
  68. varietyTabs.value = res.data || [];
  69. if (varietyTabs.value.length > 0) {
  70. handleVarietyClick(varietyTabs.value[0], 0);
  71. }
  72. } catch (error) {
  73. console.error("获取主体分区列表失败:", error);
  74. }
  75. };
  76. const handleVarietyClick = (tab, index) => {
  77. activeVariety.value = index;
  78. farmIdData.value = tab.farmId;
  79. };
  80. const activeVarietyName = computed(() => {
  81. const current = varietyTabs.value[activeVariety.value];
  82. return current?.regionName || "";
  83. });
  84. const viewOnly = computed(() => route.query.viewOnly === "1" || route.query.viewOnly === "true");
  85. onMounted(() => {
  86. type.value = route.query.type;
  87. const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
  88. const editable = !viewOnly.value;
  89. const showPoint = !viewOnly.value;
  90. drawRegionMap.initMap(point, mapContainer.value, editable, true, showPoint);
  91. // 初始化品种 tabs
  92. getVarietyTabs();
  93. // 地图初始化之后(比如 initPreviewMap 里)
  94. // const regions = [
  95. // {
  96. // geometry:
  97. // "MULTIPOLYGON(((113.61674040430906 23.586573370597367,113.61586610436014 23.585922976493354,113.61710291900188 23.58486741952544,113.61770000158238 23.585651090473736,113.61674040430906 23.586573370597367)))",
  98. // status: "unresolved", // 未解决(蓝色)
  99. // },
  100. // {
  101. // geometry:
  102. // "MULTIPOLYGON(((113.61516640298626 23.588441931082958,113.61445736699218 23.58799411906573,113.61572616841707 23.586954554834552,113.61642987338976 23.588180707433526,113.61516640298626 23.588441931082958)))",
  103. // status: "resolved", // 已解决(灰色)
  104. // },
  105. // ];
  106. // drawRegionMap.setStatusRegions(regions);
  107. });
  108. onActivated(() => {
  109. const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
  110. // 从编辑态进入仅查看时,需重新初始化为不可编辑
  111. if (viewOnly.value && drawRegionMap.kmap && drawRegionMap.editable) {
  112. drawRegionMap.destroyMap();
  113. drawRegionMap.initMap(point, mapContainer.value, false, true, false);
  114. }
  115. // 从仅查看进入勾画(编辑)时,需重新初始化为可编辑
  116. if (!viewOnly.value && drawRegionMap.kmap && !drawRegionMap.editable) {
  117. drawRegionMap.destroyMap();
  118. drawRegionMap.initMap(point, mapContainer.value, true, true, true);
  119. }
  120. // 先绘制地块
  121. const polygonData = route.query.polygonData;
  122. const rawRangeWkt = route.query.rangeWkt;
  123. const rangeWkt = rawRangeWkt ? decodeURIComponent(rawRangeWkt) : null;
  124. if (rangeWkt) {
  125. let regions = [];
  126. try {
  127. const parsed = JSON.parse(rangeWkt);
  128. if (parsed && Array.isArray(parsed.geometryArr)) {
  129. regions = parsed.geometryArr.map((item) => ({
  130. geometry: item,
  131. status: "unresolved",
  132. updatedTime: route.query.updatedTime,
  133. }));
  134. } else if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
  135. regions = [{ geometry: rangeWkt.trim(), status: "unresolved", updatedTime: route.query.updatedTime }];
  136. }
  137. } catch (_) {
  138. if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
  139. regions = [{ geometry: rangeWkt.trim(), status: "unresolved", updatedTime: route.query.updatedTime }];
  140. }
  141. }
  142. if (regions.length) {
  143. drawRegionMap.setStatusRegions(regions);
  144. if (viewOnly.value && drawRegionMap.fitAllRegions) {
  145. drawRegionMap.fitAllRegions();
  146. }
  147. }
  148. }
  149. if (!viewOnly.value && polygonData) {
  150. drawRegionMap.setAreaGeometry(JSON.parse(polygonData)?.geometryArr);
  151. }
  152. // 查看模式下已通过 fitAllRegions 适配;编辑模式再设置地图中心
  153. if (!viewOnly.value) {
  154. drawRegionMap.setMapPosition(convertPointToArray(point));
  155. }
  156. });
  157. onDeactivated(() => {
  158. activeVariety.value = 0;
  159. drawRegionMap.clearLayer()
  160. })
  161. const goBack = () => {
  162. // drawRegionMap.clearLayer()
  163. router.back()
  164. };
  165. const resetPolygon = () => {
  166. ElMessageBox.confirm(
  167. "确认要重置当前地块吗?重置后将恢复到进入页面时的区域形状。",
  168. "重置确认",
  169. {
  170. confirmButtonText: "确认重置",
  171. cancelButtonText: "取消",
  172. type: "warning",
  173. }
  174. )
  175. .then(() => {
  176. // 只清空当前可编辑多边形,不影响只读状态图层
  177. if (drawRegionMap.kmap && drawRegionMap.kmap.polygonLayer && drawRegionMap.kmap.polygonLayer.source) {
  178. drawRegionMap.kmap.polygonLayer.source.clear();
  179. }
  180. // 使用进入页面时路由上带的 polygonData 作为“初始形状”进行回显
  181. const polygonDataStr = route.query.polygonData;
  182. if (polygonDataStr) {
  183. try {
  184. const parsed = JSON.parse(polygonDataStr);
  185. if (parsed && Array.isArray(parsed.geometryArr) && parsed.geometryArr.length) {
  186. drawRegionMap.setAreaGeometry(parsed.geometryArr);
  187. }
  188. } catch (_) {
  189. // 解析失败则不做处理,仅相当于清空重画
  190. }
  191. }
  192. ElMessage.success("地块已重置");
  193. })
  194. .catch(() => {
  195. // 用户取消重置,不做任何操作
  196. });
  197. };
  198. const confirm = () => {
  199. // const polygonData = drawRegionMap.getAreaGeometry();
  200. // sessionStorage.setItem("drawRegionPolygonData", JSON.stringify(polygonData));
  201. // 如果当前是第一个品种,则开启完整引导流程:每次确认都弹成功提示,直到最后一个才退出
  202. if (activeVariety.value === 0) {
  203. isGuidedFlow.value = true;
  204. showSuccessPopup.value = true;
  205. return;
  206. }
  207. // 已经在引导流程中(从第一个品种开始勾选),后续品种确认也只弹成功提示,不直接跳走
  208. if (isGuidedFlow.value) {
  209. showSuccessPopup.value = true;
  210. return;
  211. }
  212. // 非引导流程(例如直接编辑单个非第一个品种),确认后直接返回上一页
  213. router.back();
  214. };
  215. const handleKnow = () => {
  216. showSuccessPopup.value = false;
  217. const isLastVariety =
  218. varietyTabs.value.length > 0 && activeVariety.value === varietyTabs.value.length - 1;
  219. // 在引导流程中且还没到最后一个品种时,“我知道了”只关闭弹窗,不跳走
  220. if (isGuidedFlow.value && !isLastVariety) {
  221. return;
  222. }
  223. // 到达最后一个品种或本身就不是引导流程,才真正完成并返回
  224. isGuidedFlow.value = false;
  225. router.back();
  226. };
  227. const handleSelectNextVariety = () => {
  228. if (varietyTabs.value.length === 0) {
  229. handleKnow();
  230. return;
  231. }
  232. const nextIndex = activeVariety.value + 1;
  233. if (nextIndex < varietyTabs.value.length) {
  234. const nextTab = varietyTabs.value[nextIndex];
  235. // 勾画当前品种完成后,先用统一的 clearLayer 方法重置图层,再切换到下一个品种
  236. if (drawRegionMap && typeof drawRegionMap.clearLayer === "function") {
  237. drawRegionMap.clearLayer();
  238. }
  239. handleVarietyClick(nextTab, nextIndex);
  240. showSuccessPopup.value = false;
  241. } else {
  242. // 如果已经是最后一个品种,则直接返回上一页
  243. handleKnow();
  244. }
  245. };
  246. </script>
  247. <style lang="scss" scoped>
  248. .edit-map {
  249. width: 100%;
  250. height: 100vh;
  251. overflow: hidden;
  252. .variety-tabs {
  253. display: flex;
  254. align-items: center;
  255. gap: 8px;
  256. padding: 10px 12px 0;
  257. overflow-x: auto;
  258. overflow-y: hidden;
  259. flex-wrap: nowrap;
  260. -webkit-overflow-scrolling: touch;
  261. .variety-tab {
  262. padding: 4px 12px;
  263. border-radius: 2px;
  264. color: #767676;
  265. background: #fff;
  266. border: 0.5px solid rgba(174, 174, 174, 0.8);
  267. white-space: nowrap;
  268. }
  269. .variety-tab--active {
  270. background: #2199F8;
  271. color: #ffffff;
  272. }
  273. }
  274. .edit-map-content {
  275. width: 100%;
  276. margin-top: 10px;
  277. height: calc(100% - 58px);
  278. position: relative;
  279. .edit-map-tip {
  280. position: absolute;
  281. top: 23px;
  282. left: calc(50% - 256px / 2);
  283. z-index: 1;
  284. font-size: 12px;
  285. color: #fff;
  286. padding: 9px 20px;
  287. background: rgba(0, 0, 0, 0.5);
  288. border-radius: 20px;
  289. }
  290. .map-container {
  291. width: 100%;
  292. height: 100%;
  293. }
  294. .edit-map-footer {
  295. position: absolute;
  296. bottom: 110px;
  297. left: 12px;
  298. width: calc(100% - 24px);
  299. display: flex;
  300. flex-direction: column;
  301. align-items: flex-end;
  302. .footer-back {
  303. padding: 6px 7px 9px;
  304. background: #fff;
  305. border-radius: 8px;
  306. margin-bottom: 12px;
  307. .back-icon {
  308. width: 20px;
  309. height: 18px;
  310. }
  311. }
  312. .edit-map-footer-btn {
  313. display: flex;
  314. justify-content: space-between;
  315. align-items: center;
  316. width: 100%;
  317. position: fixed;
  318. bottom: 0;
  319. left: 0;
  320. background: #fff;
  321. padding: 10px 12px 20px 12px;
  322. box-sizing: border-box;
  323. &.confirm-btn-box{
  324. justify-content: center;
  325. }
  326. div {
  327. width: 100px;
  328. text-align: center;
  329. color: #666666;
  330. font-size: 14px;
  331. padding: 8px 0;
  332. border-radius: 25px;
  333. border: 0.5px solid rgba(153, 153, 153, 0.5);
  334. }
  335. .btn-confirm {
  336. background: #2199F8;
  337. color: #fff;
  338. border: 1px solid #2199F8;
  339. }
  340. }
  341. }
  342. }
  343. }
  344. </style>