farmInfo copy.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <template>
  2. <custom-header name="农场信息" bgColor="#f2f3f5"></custom-header>
  3. <div class="farm-details-page">
  4. <div class="map-wrap info-card">
  5. <div class="map-area" ref="mapContainer"></div>
  6. <div class="map-text" @click="handleEditMap">点击编辑地块</div>
  7. </div>
  8. <div class="info-box info-card">
  9. <div class="section-header">
  10. <div class="line-title">基本信息</div>
  11. <div class="edit-btn-box">
  12. <div class="edit-btn" @click="handleAddVariety">新增品种</div>
  13. <div class="edit-btn" @click="handleEditFarmInfo">编辑信息</div>
  14. </div>
  15. </div>
  16. <div class="info-list">
  17. <div class="info-row">
  18. <span class="info-label">农场名称:</span>
  19. <span class="info-value">{{ farmInfo.subjectName }}</span>
  20. </div>
  21. <div class="info-row">
  22. <span class="info-label">联系人:</span>
  23. <span class="info-value">{{ farmInfo.contactName }}</span>
  24. </div>
  25. <div class="info-row center-row">
  26. <span class="info-label">联系电话:</span>
  27. <span class="info-value">{{ farmInfo.contactPhone }}</span>
  28. </div>
  29. <div class="info-row">
  30. <span class="info-label">种植面积:</span>
  31. <span class="info-value">{{ farmInfo.farmArea }}亩</span>
  32. </div>
  33. <div class="info-row">
  34. <span class="info-label">农场位置:</span>
  35. <span class="info-value">{{ farmInfo.farmAddress }}</span>
  36. </div>
  37. <div class="info-row">
  38. <span class="info-label">种植品种:</span>
  39. <div class="info-value crop-tags">
  40. <span v-for="crop in farmInfo.regionList" :key="crop.regionId" class="crop-tag">
  41. {{ crop.regionName }}
  42. </span>
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. <div class="info-box info-card">
  48. <div class="section-header">
  49. <div class="line-title">农场设施</div>
  50. <div class="edit-btn" @click="handleEditFarmFacility">编辑信息</div>
  51. </div>
  52. <div class="info-list">
  53. <div class="info-row">
  54. <span class="info-label">灌溉方式:</span>
  55. <div class="info-value crop-tags">
  56. <span v-for="method in basicFarmInfo.irrigation" :key="method.code" class="crop-tag">
  57. {{ method.name }}
  58. </span>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. <FarmInfoPopup ref="farmInfoPopupRef" :farmId="route.query.subjectId" @success="fetchFarmSubjectDetail" />
  65. </template>
  66. <script setup>
  67. import { ref, onMounted, nextTick } from "vue";
  68. import customHeader from "@/components/customHeader.vue";
  69. import { useRouter, useRoute } from "vue-router";
  70. import DrawRegionMap from "@/views/old_mini/interactionList/map/drawRegionMap.js";
  71. import * as util from "@/common/ol_common.js";
  72. import { ElMessage } from "element-plus";
  73. import { useStore } from "vuex";
  74. import FarmInfoPopup from "@/views/old_mini/home/components/farmInfoPopup.vue";
  75. const router = useRouter();
  76. const route = useRoute();
  77. const store = useStore();
  78. const farmInfo = ref({});
  79. const mapContainer = ref(null);
  80. const drawRegionMap = new DrawRegionMap();
  81. const initMap = () => {
  82. if (!mapContainer.value) return;
  83. if (drawRegionMap.kmap) return;
  84. const info = farmInfo.value || {};
  85. drawRegionMap.initMap(info.farmLocation, mapContainer.value, false, true, false);
  86. // 回显农场区域,多边形 WKT 数组(有就画,没有就不画)
  87. let geometryArr = [];
  88. // 优先使用 regionList 里的 geom:有值才渲染
  89. if (Array.isArray(info.regionList) && info.regionList.length > 0) {
  90. info.regionList.forEach((item) => {
  91. if (item?.geom) {
  92. geometryArr.push(item?.geom)
  93. }
  94. });
  95. }
  96. if (geometryArr.length) {
  97. // 农场信息页:地块用绿色展示;第三参留空表示面积仍按地块自动计算
  98. drawRegionMap.setAreaGeometry(Array.from(geometryArr), true, undefined, {
  99. fill: "rgba(0, 57, 44, 0.5)",
  100. stroke: "#18AA8B",
  101. });
  102. }
  103. // 在区域中心落一个点位(使用与勾画页相同的图标)
  104. try {
  105. const geom = util.wktCastGeom(info.farmLocation);
  106. if (geom && typeof geom.getFirstCoordinate === "function") {
  107. const coord = geom.getFirstCoordinate();
  108. drawRegionMap.setMapPoint(coord);
  109. }
  110. } catch (e) {
  111. // 解析失败则忽略点位
  112. }
  113. };
  114. const destroyMap = () => {
  115. if (drawRegionMap && drawRegionMap.kmap && drawRegionMap.destroyMap) {
  116. drawRegionMap.destroyMap();
  117. }
  118. };
  119. onMounted(() => {
  120. fetchFarmSubjectDetail();
  121. fetchBasicFarmFormData();
  122. });
  123. function fetchFarmSubjectDetail() {
  124. VE_API.basic_farm.fetchFarmSubjectDetail({ subjectId: route.query.subjectId }).then(({ data }) => {
  125. farmInfo.value = data || {};
  126. nextTick(() => {
  127. destroyMap();
  128. initMap();
  129. });
  130. });
  131. }
  132. const basicFarmInfo = ref({});
  133. function fetchBasicFarmFormData() {
  134. VE_API.basic_farm.fetchBasicFarmFormData({ subjectId: route.query.subjectId }).then(({ data, code }) => {
  135. if (code === 0) {
  136. basicFarmInfo.value = {
  137. ...data,
  138. irrigation: data.irrigationMethods.filter(item => item.selected),
  139. }
  140. }
  141. });
  142. }
  143. const farmInfoPopupRef = ref(null);
  144. const handleEditFarmInfo = () => {
  145. // // 回显地块:存到 polygonData,创建页会优先使用这里的数据
  146. // if (data.geomWkt) {
  147. // const polygonData = {
  148. // geometryArr: [data.geomWkt],
  149. // mianji: data.mianji,
  150. // isConfirmed: true,
  151. // };
  152. // store.commit("home/SET_FARM_POLYGON", polygonData);
  153. // } else {
  154. // store.commit("home/SET_FARM_POLYGON", null);
  155. // }
  156. // const params = {
  157. // ...farmInfo.value,
  158. // name: farmInfo.value.subjectName,
  159. // fzr: farmInfo.value.contactName,
  160. // tel: farmInfo.value.contactPhone,
  161. // mianji: farmInfo.value.farmArea,
  162. // address: farmInfo.value.farmAddress,
  163. // };
  164. // // 回显其他表单字段
  165. // store.commit("home/SET_EDIT_FARM_DATA", params);
  166. // // 带上 from=details,创建页提交/取消后能正确返回
  167. // router.push(`/create_farm?type=edit&farmId=${route.query.subjectId}&from=details`);
  168. farmInfoPopupRef.value.handleShow();
  169. };
  170. const handleEditFarmFacility = () => {
  171. router.push(`/prescription?subjectId=${route.query.subjectId}`);
  172. };
  173. // 点击编辑地块
  174. const handleEditMap = () => {
  175. const mapCenter = farmInfo.value.farmLocation || undefined;
  176. if (farmInfo.value.regionList.length) {
  177. // type=view 进入查看态;rangeWkt 用于回显多个地块
  178. router.push({
  179. path: "/draw_area",
  180. query: {
  181. type: "viewOnly",
  182. subjectId: route.query.subjectId
  183. },
  184. });
  185. } else {
  186. ElMessage.warning("暂无种植作物,无法编辑地块");
  187. }
  188. };
  189. const handleAddVariety = () => {
  190. router.push(`/interaction?addVariety=true&subjectId=${route.query.subjectId}`);
  191. };
  192. </script>
  193. <style lang="scss" scoped>
  194. .farm-details-page {
  195. background: #f2f3f5;
  196. height: calc(100vh - 40px);
  197. padding: 0 12px;
  198. overflow: auto;
  199. .line-title {
  200. position: relative;
  201. padding-left: 14px;
  202. font-size: 16px;
  203. &::before {
  204. content: "";
  205. position: absolute;
  206. left: 5px;
  207. top: 50%;
  208. transform: translateY(-50%);
  209. width: 4px;
  210. height: 15px;
  211. background: #2199f8;
  212. border-radius: 20px;
  213. }
  214. }
  215. .info-card{
  216. margin-top: 12px;
  217. background: #fff;
  218. border-radius: 6px;
  219. padding: 10px;
  220. }
  221. .map-wrap {
  222. position: relative;
  223. .map-area {
  224. width: 100%;
  225. height: 142px;
  226. clip-path: inset(0px round 5px);
  227. }
  228. .map-text {
  229. position: absolute;
  230. right: 13px;
  231. bottom: 16px;
  232. font-size: 12px;
  233. color: #ffffff;
  234. background: rgba(0, 0, 0, 0.5);
  235. padding: 8px 12px;
  236. border-radius: 25px;
  237. border: 1px solid rgba(255, 255, 255, 0.5);
  238. }
  239. }
  240. .info-box {
  241. margin-top: 12px;
  242. .section-header {
  243. display: flex;
  244. align-items: center;
  245. justify-content: space-between;
  246. }
  247. .edit-btn-box {
  248. display: flex;
  249. align-items: center;
  250. gap: 8px;
  251. }
  252. .edit-btn {
  253. padding: 4px 12px;
  254. font-size: 12px;
  255. color: #86909c;
  256. border-radius: 999px;
  257. border: 0.5px solid #d0d3d8;
  258. background-color: #ffffff;
  259. }
  260. .info-list {
  261. margin-top: 10px;
  262. margin-left: 5px;
  263. .info-row {
  264. display: flex;
  265. align-items: flex-start;
  266. margin-bottom: 6px;
  267. &.center-row {
  268. align-items: center;
  269. }
  270. .info-label {
  271. min-width: 80px;
  272. color: #4E5969;
  273. }
  274. .crop-tags {
  275. display: flex;
  276. align-items: center;
  277. flex-wrap: wrap;
  278. gap: 6px;
  279. .crop-tag {
  280. padding: 2px 8px;
  281. background: #E8F3FF;
  282. color: #2199f8;
  283. border-radius: 2px;
  284. font-size: 12px;
  285. }
  286. }
  287. .problem-tags {
  288. display: flex;
  289. flex-wrap: wrap;
  290. gap: 6px;
  291. .problem-tag {
  292. padding: 2px 8px;
  293. background: rgba(58, 173, 148, 0.1);
  294. color: #3AAD94;
  295. border-radius: 2px;
  296. font-size: 13px;
  297. }
  298. }
  299. .device-value {
  300. width: 100%;
  301. }
  302. .device-box {
  303. padding: 6px;
  304. border-radius: 4px;
  305. border: 0.5px solid rgba(33, 153, 248, 0.2);
  306. display: grid;
  307. grid-template-columns: repeat(2, minmax(0, 1fr));
  308. column-gap: 12px;
  309. row-gap: 4px;
  310. .device-item {
  311. display: flex;
  312. align-items: center;
  313. justify-content: space-between;
  314. padding: 4px 0;
  315. min-width: 0;
  316. gap: 8px;
  317. font-size: 13px;
  318. color: #1D2129;
  319. .device-count {
  320. padding: 2px 8px;
  321. background: #E8F3FF;
  322. color: #2199f8;
  323. border-radius: 2px;
  324. }
  325. }
  326. }
  327. }
  328. .info-row-column {
  329. display: flex;
  330. flex-direction: column;
  331. gap: 4px;
  332. }
  333. }
  334. }
  335. }
  336. </style>