Sfoglia il codice sorgente

feat:添加当前农场切换点位和地块,修改头部样式

wangsisi 17 ore fa
parent
commit
b2f4644ce7

BIN
src/assets/img/common/header-icon-1.png


BIN
src/assets/img/common/header-icon-2.png


BIN
src/assets/img/common/header-icon-3.png


+ 11 - 1
src/components/gardenList.vue

@@ -153,12 +153,22 @@ function selectFarmFromList(data) {
     return selectDefaultFarm(data);
 }
 
+function resolveFarmPointWkt(farm) {
+    if (farm.geom_wkt && /^POINT\s*\(/i.test(String(farm.geom_wkt).trim())) {
+        return farm.geom_wkt;
+    }
+    if (farm.farm_location && /^POINT\s*\(/i.test(String(farm.farm_location).trim())) {
+        return farm.farm_location;
+    }
+    return farm.geom_wkt || "";
+}
+
 function normalizeFarmList(data) {
     return (data || []).map((farm) => ({
         ...farm,
         id: farm.farm_id,
         name: farm.farm_name,
-        wkt: farm.geom_wkt,
+        wkt: resolveFarmPointWkt(farm),
         rawAddress: farm.farm_address || farm.city || "",
         stats: mapStats(),
         isCurrent: false

+ 54 - 51
src/components/weatherInfo.vue

@@ -11,6 +11,12 @@
                                 :src="activeGarden === 'current' ? require('@/assets/img/common/farm-active.png') : require('@/assets/img/common/farm.png')"
                                 alt="">
                             <span class="current-name van-ellipsis">{{ farmName }}</span>
+                            <el-icon
+                                v-if="activeGarden === 'current'"
+                                class="farm-edit-icon"
+                            >
+                                <Edit />
+                            </el-icon>
                         </div>
                         <img class="title-block" v-show="activeGarden === 'current'"
                             src="@/assets/img/common/title-block.png" alt="">
@@ -59,11 +65,14 @@
 
                 <div v-if="!hasWeather" class="report-tabs">
                     <div
-                        v-for="item in reportTabs"
+                        v-for="(item, index) in reportTabs"
                         :key="item.key"
                         class="report-tab-item"
                         @click="emit('reportTabClick', item)"
-                    >{{ item.label }}</div>
+                    >
+                        <img class="report-tab-icon" :src="getReportTabIcon(index)" alt="" />
+                        <span class="report-tab-label">{{ item.label }}</span>
+                    </div>
                 </div>
             </div>
             <!-- <div class="weather-icon" v-else>
@@ -77,14 +86,6 @@
             </div>
             <weather-chart class="weather-chart" :weather-data="weatherData"></weather-chart>
         </div>
-        <div
-            v-if="!hasWeather && activeGarden === 'current'"
-            class="report-maintain-btn"
-            @click="handleFarmInfoMaintain"
-        >
-            <img class="report-maintain-icon" src="@/assets/img/common/info.png" alt="" />
-            <span>{{ t("weather.farmInfoMaintain") }}</span>
-        </div>
         <!-- 农场筛选 -->
         <div class="farm-filter" v-show="activeGarden === 'list'">
             <div class="filter-l">
@@ -109,7 +110,7 @@ import { ref, onActivated, computed } from "vue";
 import weatherChart from "./weatherChart.vue";
 import { useRouter } from "vue-router";
 import { useStore } from "vuex";
-import { Search } from '@element-plus/icons-vue';
+import { Edit, Search } from '@element-plus/icons-vue';
 import { convertPointToArray } from "@/utils/index";
 import { useI18n } from "@/i18n";
 
@@ -132,9 +133,9 @@ const props = defineProps({
     reportTabs: {
         type: Array,
         default: () => [
-            { key: "historyRisk", label: "历史风险报告" },
-            { key: "soilImprovement", label: "土壤改良" },
-            { key: "rotationAdvice", label: "轮作建议" },
+            { key: "historyRisk", label: "历史气象灾害" },
+            { key: "soilImprovement", label: "土壤问题研判" },
+            { key: "rotationAdvice", label: "种植轮作建议" },
         ],
     },
     from: {
@@ -158,8 +159,16 @@ const typeOptions = ref([{
     value: '1'
 }]);
 
+const REPORT_TAB_ICONS = [
+    require("@/assets/img/common/header-icon-1.png"),
+    require("@/assets/img/common/header-icon-2.png"),
+    require("@/assets/img/common/header-icon-3.png"),
+];
+
+const getReportTabIcon = (index) => REPORT_TAB_ICONS[index] || REPORT_TAB_ICONS[0];
+
 // 定义emit事件
-const emit = defineEmits(['weatherExpanded', 'changeGarden', 'changeGardenTab', 'closeTabMask', 'reportTabClick', 'farmInfoMaintain']);
+const emit = defineEmits(['weatherExpanded', 'changeGarden', 'changeGardenTab', 'closeTabMask', 'reportTabClick']);
 const router = useRouter();
 
 const isExpanded = ref(false);
@@ -206,11 +215,6 @@ const handleGardenClick = (type) => {
     emit("changeGardenTab", type);
 };
 
-const handleFarmInfoMaintain = () => {
-    if (!farmId.value) return;
-    emit('farmInfoMaintain', farmId.value);
-};
-
 const locationName = ref("");
 const weatherData = ref(null);
 const currentWeather = ref({ temp: "--", text: "--", iconDay: "" });
@@ -351,30 +355,6 @@ defineExpose({
     &.no-weather {
         height: auto;
         position: relative;
-
-        .report-maintain-btn {
-            position: fixed;
-            right: -5px;
-            bottom: 10px;
-            z-index: 13;
-            display: flex;
-            align-items: center;
-            gap: 4px;
-            padding: 7px 10px;
-            background: #2199F8;
-            color: #fff;
-            border-radius: 20px 0 0 20px;
-            font-size: 11px;
-            line-height: 1;
-            white-space: nowrap;
-            box-sizing: border-box;
-
-            .report-maintain-icon {
-                width: 14px;
-                height: 14px;
-                filter: brightness(0) invert(1);
-            }
-        }
     }
 
     &.no-farm {
@@ -520,19 +500,33 @@ defineExpose({
                 width: 100%;
                 display: flex;
                 align-items: center;
-                gap: 4px;
-                padding: 10px 9px 6px;
+                gap: 7px;
+                padding: 8px 10px 10px;
                 background: #fff;
                 box-sizing: border-box;
 
                 .report-tab-item {
-                    padding: 5px 7px;
-                    text-align: center;
+                    flex: 1;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    gap: 5px;
+                    padding: 5px 8px;
                     font-size: 12px;
-                    color: #777777;
-                    background: rgba(255, 255, 255, 0.1);
+                    color: #5A5A5A;
                     border: 0.5px solid rgba(180, 180, 180, 0.4);
-                    border-radius: 2px;
+                    border-radius: 4px;
+
+                    .report-tab-icon {
+                        width: 14px;
+                        height: 14px;
+                    }
+
+                    .report-tab-label {
+                        white-space: nowrap;
+                        overflow: hidden;
+                        text-overflow: ellipsis;
+                    }
                 }
             }
         }
@@ -567,6 +561,15 @@ defineExpose({
             // background: linear-gradient(180deg, #89CBFF 0%, #2199F8 100%);
             &.left-item {
                 padding-left: 10px;
+                justify-content: flex-start;
+
+                .farm-edit-icon {
+                    width: 16px;
+                    height: 16px;
+                    color: #2199F8;
+                    flex-shrink: 0;
+                    margin-left: 2px;
+                }
             }
 
             &.right-item {

+ 2 - 1
src/views/old_mini/agri_file/index.vue

@@ -6,7 +6,8 @@
         <div class="agri-file-header" :style="activeGardenTab === 'current' ? headerMotionStyle : undefined">
             <weather-info ref="weatherInfoRef" :hasWeather="false" from="agri_file" class="weather-info"
                 @weatherExpanded="weatherExpanded" @changeGarden="changeGarden" @changeGardenTab="changeGardenTab"
-                @reportTabClick="handleReportTabClick" :isGarden="true" :gardenId="defaultGardenId" />
+                @reportTabClick="handleReportTabClick" :isGarden="true"
+                :gardenId="defaultGardenId" />
         </div>
         <!-- 农场列表 -->
         <div v-show="activeGardenTab === 'list'">

+ 1 - 5
src/views/old_mini/agri_record/index.vue

@@ -7,7 +7,7 @@
         <div class="agri-record-header">
             <weather-info ref="weatherInfoRef" :hasWeather="false" from="agri_record" class="weather-info"
                 @weatherExpanded="weatherExpanded" @changeGarden="changeGarden" @changeGardenTab="changeGardenTab"
-                @reportTabClick="handleReportTabClick" @farmInfoMaintain="handleFarmInfoMaintain" :isGarden="true"
+                @reportTabClick="handleReportTabClick" :isGarden="true"
                 :gardenId="defaultGardenId" />
         </div>
         <!-- 农场列表 -->
@@ -109,10 +109,6 @@ const handleReportTabClick = (item) => {
     }
 };
 
-const handleFarmInfoMaintain = (farmId) => {
-    router.push(`/create_farm?type=edit&farmId=${farmId}&from=agri_record`);
-};
-
 const handleGardenLoaded = ({ hasFarm }) => {
     weatherInfoRef.value?.setGardenLoaded?.(hasFarm);
 };

+ 48 - 10
src/views/old_mini/growth_report/growthReportMap.js

@@ -3,8 +3,11 @@ import * as util from "@/common/ol_common.js";
 import config from "@/api/config.js";
 import Style from "ol/style/Style";
 import Icon from "ol/style/Icon";
+import Fill from "ol/style/Fill";
+import Stroke from "ol/style/Stroke";
 import { Point } from 'ol/geom';
 import Feature from "ol/Feature";
+import WKT from "ol/format/WKT.js";
 import { reactive } from "vue";
 
 export let mapLocation = reactive({
@@ -78,19 +81,54 @@ class IndexMap {
     this.kmap.polygonLayer.source.clear();
   }
 
+  createFarmPolygonStyle() {
+    return new Style({
+      fill: new Fill({
+        color: "rgba(19, 162, 127, 0.25)",
+      }),
+      stroke: new Stroke({
+        color: "#13a27f",
+        width: 2,
+      }),
+    });
+  }
+
   setAreaGeometry(geometryArr) {
-    this.clearLayer()
-    let that = this
-    geometryArr.map(item => {
-      that.kmap.setLayerWkt(item)
-    })
-    this.fitView()
+    this.clearLayer();
+    const format = new WKT();
+    const mapProjection = this.kmap.map.getView().getProjection();
+    geometryArr.forEach((item) => {
+      const geometry = format.readGeometry(item, {
+        dataProjection: "EPSG:4326",
+        featureProjection: mapProjection,
+      });
+      const feature = new Feature({ geometry });
+      feature.setStyle(this.createFarmPolygonStyle());
+      this.kmap.polygonLayer.source.addFeature(feature);
+    });
+    this.scheduleFitView();
+  }
+
+  fitView(options = {}) {
+    const map = this.kmap?.map;
+    if (!map) return;
+
+    map.updateSize();
+    const extent = this.kmap.polygonLayer.source.getExtent();
+    if (!extent || !isFinite(extent[0])) return;
+
+    map.getView().fit(extent, {
+      duration: options.duration ?? 0,
+      padding: options.padding ?? [120, 48, 48, 48],
+      maxZoom: options.maxZoom ?? 18,
+    });
   }
 
-  fitView(){
-    let extent = this.kmap.polygonLayer.source.getExtent()
-    // 地图自适应到区域可视范围
-    this.kmap.getView().fit(extent, { duration: 500, padding: [100, 100, 100, 100] });
+  scheduleFitView(options = {}) {
+    const run = () => this.fitView(options);
+    requestAnimationFrame(() => {
+      setTimeout(run, 50);
+    });
   }
 }
 

+ 60 - 6
src/views/old_mini/growth_report/index.vue

@@ -52,6 +52,38 @@ import * as util from "@/common/ol_common.js";
 
 const DEFAULT_FARM_POINT = "POINT(113.6142086995688 23.585836479509055)";
 
+function isPointWkt(value) {
+    return typeof value === "string" && /^POINT\s*\(/i.test(value.trim());
+}
+
+function isPolygonWkt(value) {
+    return typeof value === "string" && /^(MULTI)?POLYGON\s*\(/i.test(value.trim());
+}
+
+function resolveFarmPolygonWkt(farm) {
+    if (!farm || typeof farm === "string") return null;
+    const polygon = farm.farm_polygon;
+    return isPolygonWkt(polygon) ? polygon : null;
+}
+
+function resolveFarmLocationWkt(farm, polygonWkt) {
+    if (typeof farm === "string" && isPointWkt(farm)) return farm;
+    if (!farm || typeof farm === "string") return null;
+
+    const candidates = [farm.wkt, farm.geom_wkt, farm.farm_location];
+    for (const candidate of candidates) {
+        if (isPointWkt(candidate)) return candidate;
+    }
+
+    if (polygonWkt) {
+        const geom = util.wktCastGeom(polygonWkt);
+        const extent = geom.getExtent();
+        return `POINT(${(extent[0] + extent[2]) / 2} ${(extent[1] + extent[3]) / 2})`;
+    }
+
+    return null;
+}
+
 const store = useStore();
 const { t, locale } = useI18n();
 const route = useRoute();
@@ -116,16 +148,33 @@ const currentFarmVariety = ref(null);
 const mapContainer = ref(null);
 const growthReportMap = new GrowthReportMap();
 
-const syncMapByFarm = async (wkt) => {
-    const location = DEFAULT_FARM_POINT;
+const syncMapByFarm = async (farm) => {
+    const polygonWkt = resolveFarmPolygonWkt(farm);
+    const location = resolveFarmLocationWkt(farm, polygonWkt) || DEFAULT_FARM_POINT;
+
     await nextTick();
     if (!mapContainer.value) return;
+
     if (growthReportMap.kmap) {
-        const coordinate = util.wktCastGeom(location).getFirstCoordinate();
-        growthReportMap.setMapPosition(coordinate);
+        growthReportMap.kmap.map?.updateSize?.();
+        if (polygonWkt) {
+            growthReportMap.setAreaGeometry([polygonWkt]);
+            const coordinate = util.wktCastGeom(location).getFirstCoordinate();
+            growthReportMap.setMapPoint(coordinate);
+            growthReportMap.scheduleFitView();
+        } else {
+            growthReportMap.clearLayer();
+            const coordinate = util.wktCastGeom(location).getFirstCoordinate();
+            growthReportMap.setMapPosition(coordinate);
+        }
         return;
     }
+
     growthReportMap.initMap(location, mapContainer.value);
+    if (polygonWkt) {
+        growthReportMap.setAreaGeometry([polygonWkt]);
+        growthReportMap.scheduleFitView();
+    }
 };
 
 const initGrowthReportMap = async () => {
@@ -164,7 +213,12 @@ const changeGardenTab = (tab) => {
     if (tab !== "current") {
         panelExpandProgress.value = 0;
         panelViewType.value = "risk";
+        return;
     }
+    nextTick(() => {
+        growthReportMap.kmap?.map?.updateSize?.();
+        growthReportMap.scheduleFitView?.();
+    });
 };
 
 const handleGardenLoaded = ({ hasFarm }) => {
@@ -173,7 +227,7 @@ const handleGardenLoaded = ({ hasFarm }) => {
 
 const handleGardenSelected = (garden) => {
     selectedGardenId.value = garden?.id ?? null;
-    syncMapByFarm(garden?.wkt);
+    syncMapByFarm(garden);
     weatherInfoRef.value?.setSelectedGarden?.(garden);
 };
 
@@ -183,7 +237,7 @@ const changeGarden = (data) => {
     selectedGardenId.value = data.id;
     currentFarmName.value = data.name ?? "";
     currentFarmVariety.value = data.farm_variety ?? null;
-    syncMapByFarm(data.wkt);
+    syncMapByFarm(data);
 };
 
 onActivated(async () => {