Forráskód Böngészése

feat:对接分区管理页面接口和修改作物档案接口和农事记录

wangsisi 2 napja
szülő
commit
c686e85ef0

+ 6 - 1
src/api/modules/basic_farm.js

@@ -66,5 +66,10 @@ module.exports = {
     saveProblemZoneImage: {
         url: config.base_dev_url + "farm_problem_zone_image/saveByProblemZoneId",
         type: "post",
-    }
+    },
+    //根据农场主体ID查询其下“已配置”的问题分区列表,
+    fetchProblemZoneList: {
+        url: config.base_dev_url + "farm_subject/listRegionsAndProblemZonesMapBySubjectId",
+        type: "get",
+    },
 }

+ 25 - 5
src/components/pageComponents/ArchivesFarmTimeLine.vue

@@ -164,6 +164,11 @@ const props = defineProps({
         type: [Number, String],
         default: null,
     },
+    // 问题分区ID
+    problemZoneId: {
+        type: [Number, String],
+        default: null,
+    },
 });
 
 const farmWorkType = {
@@ -249,9 +254,12 @@ let resizeObserver = null;
 const isEmpty = ref(false);
 // 标记是否正在请求数据,防止重复请求
 const isRequesting = ref(false);
-// 记录上一次请求的 farmId,避免相同 farmId 重复请求
+// 记录上一次请求的 regionId + problemZoneId,避免相同参数重复请求
 const lastRequestedFarmId = ref(null);
 
+const farmWorkPlanScopeKey = () =>
+    JSON.stringify([props.regionId ?? null, props.problemZoneId ?? null]);
+
 // 获取当前季节
 const getCurrentSeason = () => {
     const month = new Date().getMonth() + 1; // 1-12
@@ -587,11 +595,10 @@ const handleRowClick = (item) => {
 // 获取农事规划数据
 const getFarmWorkPlan = () => {
     if (!props.farmId) return;
-    // 如果正在请求,或者 regionId 与上次请求的相同,直接返回,防止重复请求
-    if (isRequesting.value || lastRequestedFarmId.value === props.regionId) return;
-    // 设置请求标志和记录 regionId
+    const scopeKey = farmWorkPlanScopeKey();
+    if (isRequesting.value || lastRequestedFarmId.value === scopeKey) return;
     isRequesting.value = true;
-    lastRequestedFarmId.value = props.regionId;
+    lastRequestedFarmId.value = scopeKey;
     // 更新时间戳,确保key变化,触发DOM重新渲染
     uniqueTimestamp.value = Date.now();
     // 重置测量高度,等待重新测量
@@ -608,6 +615,7 @@ const getFarmWorkPlan = () => {
     const params = {
         farmId: props.farmId,
         regionId: props.regionId,
+        problemZoneId: props.problemZoneId,
     }
     if (props.pageType === 'agri_record') {
         params.containerId = props.containerId;
@@ -825,6 +833,18 @@ watch(
     { immediate: true }
 );
 
+watch(
+    () => props.problemZoneId,
+    (val, oldVal) => {
+        if (!props.farmId) return;
+        if (val !== oldVal) {
+            lastRequestedFarmId.value = null;
+        }
+        isInitialLoad.value = true;
+        updateFarmWorkPlan();
+    },
+);
+
 const handleStatusDetail = (fw) => {
     // 跳转前记录当前滚动位置
     saveTimelineScrollTop();

+ 58 - 139
src/components/popup/confirmDrawTypePopup.vue

@@ -4,29 +4,31 @@
         <div class="type-list">
             <div
                 v-for="item in typeOptions"
-                :key="item.value"
+                :key="item.code"
                 class="type-item"
-                :class="{ 'type-item--active': selectedType === item.value }"
-                @click="handleTypeClick(item.value)"
+                :class="{ 'type-item--active': selectedType === item.code }"
+                @click="handleTypeClick(item)"
             >
-                {{ item.label }}
+                {{ item.name }}
             </div>
         </div>
 
-        <div class="popup-sub-title">请确认 具体勾画类别</div>
-        <div class="category-list">
-            <div
-                v-for="(item, index) in currentCategoryOptions"
-                :key="`${selectedType}-${index}`"
-                class="category-item"
-                :class="{ 'category-item--active': selectedCategory === item }"
-                @click="selectedCategory = item"
-            >
-                {{ item.regionName || item.problemZoneTypeName }}
+        <template v-if="selectedType !== 'DORMANCY'">
+            <div class="popup-sub-title">请确认 具体勾画类别</div>
+            <div class="category-list">
+                <div
+                    v-for="(item, index) in currentCategoryOptions"
+                    :key="`${selectedType}-${index}`"
+                    class="category-item"
+                    :class="{ 'category-item--active': selectedCategory === index }"
+                    @click="selectedCategory = index"
+                >
+                    {{ item.regionName || item.problemZoneTypeName }}
+                </div>
             </div>
-        </div>
+        </template>
 
-        <textarea v-if="selectedType !== 'abnormal'" v-model="remark" class="remark-input" maxlength="200"
+        <textarea v-if="selectedType !== 'ABNORMAL'" v-model="remark" class="remark-input" maxlength="200"
             placeholder="添加备注"></textarea>
 
         <div class="confirm-btn" @click="handleConfirm">确认</div>
@@ -39,7 +41,12 @@
             <el-progress class="upload-progress" :percentage="uploadPercentage" :stroke-width="10" :format="format" />
         </div>
         <div class="upload-box">
-            <upload :maxCount="10" :initImgArr="initImgArr" @handleUpload="handleUploadSuccess" />
+            <upload
+                ref="abnormalUploadRef"
+                :maxCount="10"
+                :initImgArr="initImgArr"
+                @handleUpload="handleUploadSuccess"
+            />
         </div>
         <textarea v-model="remark" class="remark-input" maxlength="200" placeholder="添加备注"></textarea>
         <div class="confirm-btn" :class="{ 'confirm-btn-loading': confirmUploadLoading }" @click="handleConfirmUpload">
@@ -58,110 +65,26 @@ const emit = defineEmits(["confirm"]);
 
 const showValue = ref(false);
 
-const normalizeCategoryItems = (arr) => {
-    const list = Array.isArray(arr)
-        ? arr
-        : arr && typeof arr === "object"
-          ? Object.values(arr)
-          : [];
-    return list.map((t) => ({
-        ...t,
-        regionName:
-            t.regionName ||
-            t.problemZoneTypeName ||
-            t.name ||
-            t.title ||
-            t.label ||
-            t.typeName ||
-            t.zoneName ||
-            "",
-    }));
-};
+const typeOptions = ref([])
+const currentCategoryOptions = ref([]);
+const tempCategoryOptions = ref({});
 
-const TAB_TYPE_VALUES = new Set(["variety", "abnormal", "environment", "sleep"]);
-
-const PROBLEM_TYPE_KEYS = ["abnormal", "environment", "sleep"];
-
-/**
- * 接口 problemZone 分组的 code 常与前端 Tab(sleep/abnormal…)不一致,按名称与常见别名归一化
- */
-const resolveCanonicalProblemZoneCode = (g) => {
-    if (!g) return "";
-    const code = (g.code ?? "").toString().trim();
-    const lower = code.toLowerCase();
-    const name = (g.name ?? "").toString();
-    if (name.includes("异常") || lower === "abnormal" || lower.includes("abnormal")) {
-        return "abnormal";
-    }
-    if (name.includes("环境") || lower === "environment" || lower.includes("environment")) {
-        return "environment";
-    }
-    if (
-        name.includes("休眠") ||
-        lower === "sleep" ||
-        lower.includes("sleep") ||
-        lower.includes("dormant") ||
-        lower.includes("dormancy") ||
-        lower.includes("hibern")
-    ) {
-        return "sleep";
-    }
-    return "";
-};
-
-/** 地图页选中的大类 code → 弹窗内四选一 Tab 的 value */
-const normalizeDrawerRegionType = (activeCode, problemZoneList) => {
-    if (!activeCode || activeCode === "variety") return "variety";
-    if (TAB_TYPE_VALUES.has(activeCode)) return activeCode;
-    const hit = (problemZoneList || []).find((x) => x && String(x.code) === String(activeCode));
-    if (hit) {
-        const c = resolveCanonicalProblemZoneCode(hit);
-        if (c) return c;
+const openPopup = (payload) => {
+    tempCategoryOptions.value = payload;
+    remark.value = payload.remark || "";
+    // remark.value = "";
+    typeOptions.value = payload.regionInfo.problemZoneList;
+
+    if(payload.activeRegionType === 'variety'){
+        currentCategoryOptions.value = payload.regionInfo.regionList;
+    }else{
+        const arr = payload.regionInfo.problemZoneList.filter(item => item.code === payload.activeRegionType);
+        currentCategoryOptions.value = arr[0].children;
     }
-    return activeCode;
-};
 
-const buildCategoryMap = (payload) => {
-    const map = {
-        variety: normalizeCategoryItems(payload.regionList),
-        abnormal: [],
-        environment: [],
-        sleep: [],
-    };
-    (payload.problemZoneList || []).forEach((g) => {
-        const canonical = resolveCanonicalProblemZoneCode(g);
-        if (!canonical || !PROBLEM_TYPE_KEYS.includes(canonical)) return;
-        const rawChildren = g.children ?? g.childList ?? g.problemZoneItemList ?? [];
-        const normalized = normalizeCategoryItems(rawChildren);
-        map[canonical] = [...map[canonical], ...normalized];
-    });
-    if (Array.isArray(payload.varietyTabs) && payload.activeRegionType) {
-        const nt = normalizeDrawerRegionType(payload.activeRegionType, payload.problemZoneList);
-        if (TAB_TYPE_VALUES.has(nt)) {
-            map[nt] = normalizeCategoryItems(payload.varietyTabs);
-        }
-    }
-    const fall = payload.fallbackProblemCategories || {};
-    PROBLEM_TYPE_KEYS.forEach((key) => {
-        if (!Array.isArray(map[key]) || map[key].length === 0) {
-            map[key] = normalizeCategoryItems(fall[key]);
-        }
-    });
-    return map;
-};
+    selectedType.value = payload.activeRegionType;
+    selectedCategory.value = payload.activeVarietyIndex;
 
-const openPopup = (payload) => {
-    remark.value = "";
-    categoryMap.value = buildCategoryMap(payload);
-    const drawerType = normalizeDrawerRegionType(
-        payload.activeRegionType,
-        payload.problemZoneList
-    );
-    selectedType.value = TAB_TYPE_VALUES.has(drawerType) ? drawerType : "variety";
-    const list = categoryMap.value[selectedType.value] || [];
-    const idx = Number(payload.activeVarietyIndex);
-    selectedCategory.value =
-        list[Number.isFinite(idx) ? idx : 0] ?? list[0] ?? null;
     showValue.value = true;
 };
 
@@ -174,30 +97,19 @@ defineExpose({
     close,
 });
 
-const typeOptions = [
-    { label: "品种区", value: "variety" },
-    { label: "异常问题区", value: "abnormal" },
-    { label: "环境问题区", value: "environment" },
-    { label: "休眠区", value: "sleep" },
-];
-
-const categoryMap = ref({
-    variety: [],
-    abnormal: [],
-    environment: [],
-    sleep: [],
-});
 
 const selectedType = ref("");
 const selectedCategory = ref(null);
 const remark = ref("");
 
-const currentCategoryOptions = computed(() => categoryMap.value[selectedType.value] || []);
-
-const handleTypeClick = (value) => {
-    selectedType.value = value;
-    const nextList = categoryMap.value[value] || [];
-    selectedCategory.value = nextList[0] ?? null;
+const handleTypeClick = (item) => {
+    selectedType.value = item.code;
+    if(item.code === 'variety'){
+        currentCategoryOptions.value = tempCategoryOptions.value.regionInfo.regionList;
+    }else{
+        currentCategoryOptions.value = item.children;
+    }
+    selectedCategory.value = 0;
 };
 
 const handleConfirm = () => {
@@ -208,12 +120,12 @@ const handleConfirm = () => {
 
     const payload = {
         type: selectedType.value,
-        category: selectedCategory.value,
+        category: currentCategoryOptions.value[selectedCategory.value],
         remark: remark.value.trim(),
     };
 
     // 异常问题区:先打开上传弹窗,再发出 confirm
-    if (selectedType.value === "abnormal") {
+    if (selectedType.value === "ABNORMAL") {
         pendingConfirmPayload.value = payload;
         initImgArr.value = [];
         uploadData.value = [];
@@ -252,6 +164,7 @@ const format = () => {
 const initImgArr = ref([]);
 const uploadData = ref([]);
 const confirmUploadLoading = ref(false);
+const abnormalUploadRef = ref(null);
 
 const handleUploadSuccess = (data) => {
     uploadData.value = data?.imgArr || [];
@@ -272,11 +185,17 @@ const handleConfirmUpload = async () => {
         if (pendingConfirmPayload.value) {
             pendingConfirmPayload.value.remark = remark.value.trim();
         }
+        const images = [...uploadData.value];
+        abnormalUploadRef.value?.uploadReset();
+        initImgArr.value = [];
+        uploadData.value = [];
+        totalUploadCount.value = 0;
+        uploadedSuccessCount.value = 0;
         showUploadProgressPopup.value = false;
         pendingConfirmPayload.value = null;
         emit("confirm", {
             ...(payload || {}),
-            images: uploadData.value, // 预留字段,父组件当前逻辑不受影响
+            images, // 预留字段,父组件当前逻辑不受影响
         });
     } finally {
         confirmUploadLoading.value = false;

+ 18 - 15
src/views/old_mini/agri_record/index.vue

@@ -16,7 +16,7 @@
             <div class="variety-tabs" v-if="varietyTabs.length > 0">
                 <div v-for="(v, index) in varietyTabs" :key="index" class="variety-tab"
                     :class="{ 'variety-tab--active': activeVariety === index }" @click="handleVarietyClick(v, index)">
-                    {{ v.regionName }}
+                    {{ v.regionName || v.problemZoneTypeName }}
                 </div>
             </div>
             <template v-if="!varietyTabs.length">
@@ -32,7 +32,7 @@
                 <img class="example-img" src="@/assets/img/monitor/example.png" alt="">
             </template>
             <div class="archives-time-line-content">
-                <archives-farm-time-line :farmId="farmIdData" :regionId="regionData" :containerId="containerData"
+                <archives-farm-time-line :farmId="farmIdData" :problemZoneId="currentVariety?.id" :regionId="regionData" :containerId="containerData"
                     pageType="agri_record" :typeId="currentVariety?.typeId"></archives-farm-time-line>
             </div>
         </div>
@@ -85,10 +85,15 @@ const getVarietyTabs = async (isShowPopup = true) => {
         return;
     }
     try {
-        const res = await VE_API.monitor.listRegionsBySubjectId({
+        // const res = await VE_API.monitor.listRegionsBySubjectId({
+        //     subjectId: gardenId.value,
+        // });
+        const res = await VE_API.basic_farm.fetchProblemZoneList({
             subjectId: gardenId.value,
         });
-        varietyTabs.value = res.data || []
+        if(res.data && res.data.regionList && res.data.problemZones){
+            varietyTabs.value = res.data.regionList.concat(res.data.problemZones)
+        }
         if (varietyTabs.value.length > 0) {
             handleVarietyClick(varietyTabs.value[activeVariety.value || 0], activeVariety.value || 0)
             if (isShowPopup && !showSelectRegionPopup.value && agriExecutePopupRef.value) {
@@ -104,15 +109,17 @@ const farmIdData = ref(null);
 const containerData = ref(null);
 const regionData = ref(null);
 const titlePopup = ref("");
-const currentVariety = ref(null);
+const currentVariety = ref({});
 const showSelectRegionPopup = ref(false);
 const handleVarietyClick = (tab, index) => {
     activeVariety.value = index;
-    farmIdData.value = tab.farmId;
-    containerData.value = tab.containerId;
-    regionData.value = tab.regionId;
+    if(tab.regionId){
+        farmIdData.value = tab.farmId;
+        containerData.value = tab.containerId;
+        regionData.value = tab.regionId;
+    }
     currentVariety.value = tab;
-    if (tab.lastViewTime == null) {
+    if (tab.lastViewTime == null && tab.regionId) {
         titlePopup.value = `勾选 ${tab.regionName} 区域`;
         showSelectRegionPopup.value = true;
         VE_API.basic_farm.updateLastViewTime({
@@ -317,13 +324,11 @@ const changeGarden = ({ id }) => {
 
         .variety-tabs {
             display: flex;
+            flex-wrap: wrap;
             align-items: center;
+            align-content: flex-start;
             gap: 8px;
             margin: 10px 0;
-            overflow-x: auto;
-            overflow-y: hidden;
-            flex-wrap: nowrap;
-            -webkit-overflow-scrolling: touch;
 
             .variety-tab {
                 padding: 4px 12px;
@@ -331,7 +336,6 @@ const changeGarden = ({ id }) => {
                 color: #767676;
                 background: #fff;
                 white-space: nowrap;
-                flex-shrink: 0;
             }
 
             .variety-tab--active {
@@ -339,7 +343,6 @@ const changeGarden = ({ id }) => {
                 color: #ffffff;
             }
         }
-
         .archives-time-line-content {
             margin-top: 10px;
             height: calc(100% - 70px);

+ 53 - 7
src/views/old_mini/interactionList/map/drawRegionMap.js

@@ -107,8 +107,9 @@ class DrawRegionMap {
      * @param {boolean} editable 是否允许绘制/编辑地块
      * @param {boolean} movable 是否允许拖动/缩放地图
      * @param {boolean} showPoint 是否显示初始点位图标
+     * @param {Function} onDrawEnd 绘制闭环回调(drawend),可选
      */
-    initMap(location, target, editable = true, movable = true, showPoint = true) {
+    initMap(location, target, editable = true, movable = true, showPoint = true, onDrawEnd) {
         let level = 16;
         let coordinate = util.wktCastGeom(location).getFirstCoordinate();
         this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 8, 22);
@@ -128,8 +129,14 @@ class DrawRegionMap {
         // 仅在 editable 为 true 时开启绘制/编辑能力(用于勾画页面)
         if (editable) {
             this.kmap.initDraw((e) => {
-                // drawend事件:绘制结束后的处理(支持绘制多个地块)
-            })
+                if (typeof onDrawEnd === "function") {
+                    try {
+                        onDrawEnd(e);
+                    } catch (_) {
+                        // 回调失败不影响地图继续使用
+                    }
+                }
+            });
             this.kmap.startDraw()
             this.kmap.modifyDraw()
         }
@@ -149,8 +156,10 @@ class DrawRegionMap {
      * @param {string[]} geometryArr 多边形 WKT 数组
      * @param {boolean} needFitView 是否自动缩放视图
      * @param {string|number} areaText 显示的面积(单位:亩),可选
+     * @param {{ fill?: string, stroke?: string }} [readonlyAreaStyle] 只读模式下覆盖填充/描边色;不传则仍用 Map.drawStyleColors 或默认
+     * @param {{ badgeText: string, discoveryDate: string, badgeBackground?: string }} [growthOverlay] 只读模式下异常区标签(配色与勾画页一致)
      */
-    setAreaGeometry(geometryArr, needFitView = false, areaText) {
+    setAreaGeometry(geometryArr, needFitView = false, areaText, readonlyAreaStyle, growthOverlay) {
         // 兜底保护:geometryArr 可能为 undefined/null 或空数组
         if (!Array.isArray(geometryArr) || geometryArr.length === 0) return;
         // 地图实例或图层尚未初始化时也直接返回,避免报错
@@ -168,9 +177,15 @@ class DrawRegionMap {
             let f = new Feature({ geometry: geometry })
             // 只读模式下,为多边形单独设置样式:仅填充+边框 + 面积文本,不显示可拖动的顶点小圆点
             if (!this.editable) {
-                // 查看模式下单块区域展示:按当前大类颜色展示(颜色由页面侧 applyRegionStyles 写入 Map.drawStyleColors)
-                const fillColor = KMap.Map?.drawStyleColors?.fill ?? "rgba(0, 57, 44, 0.5)";
-                const strokeColor = KMap.Map?.drawStyleColors?.stroke ?? "#18AA8B";
+                // 查看模式下单块区域展示:优先 readonlyAreaStyle,其次 Map.drawStyleColors,再默认
+                const fillColor =
+                    readonlyAreaStyle?.fill ??
+                    KMap.Map?.drawStyleColors?.fill ??
+                    "rgba(0, 57, 44, 0.5)";
+                const strokeColor =
+                    readonlyAreaStyle?.stroke ??
+                    KMap.Map?.drawStyleColors?.stroke ??
+                    "#18AA8B";
                 const styles = [
                     new Style({
                         fill: new Fill({
@@ -204,6 +219,10 @@ class DrawRegionMap {
                         }
                     }
                 }
+                const hasGrowth =
+                    growthOverlay &&
+                    growthOverlay.badgeText &&
+                    growthOverlay.discoveryDate;
                 if (textValue) {
                     styles.push(
                         new Style({
@@ -211,6 +230,31 @@ class DrawRegionMap {
                                 text: textValue,
                                 font: "15px sans-serif",
                                 fill: new Fill({ color: "#ffffff" }),
+                                offsetY: hasGrowth ? 14 : 0,
+                            }),
+                        })
+                    );
+                }
+                if (hasGrowth) {
+                    styles.push(
+                        new Style({
+                            text: new Text({
+                                text: growthOverlay.badgeText,
+                                font: "bold 13px sans-serif",
+                                fill: new Fill({ color: "#ffffff" }),
+                                backgroundFill: new Fill({
+                                    color: growthOverlay.badgeBackground || "#FF7F00",
+                                }),
+                                padding: [4, 10, 4, 10],
+                                offsetY: -40,
+                            }),
+                        }),
+                        new Style({
+                            text: new Text({
+                                text: `发现时间:${growthOverlay.discoveryDate}`,
+                                font: "12px sans-serif",
+                                fill: new Fill({ color: "#ffffff" }),
+                                offsetY: -16,
                             }),
                         })
                     );
@@ -261,11 +305,13 @@ class DrawRegionMap {
 
     getAreaGeometry() {
         const features = this.kmap.getLayerFeatures()
+        console.log(features, 'features');
         let geometryArr = []
         let area = 0
         const format = new WKT()
         // 获取图层上的Polygon,转成WKT用于回显
         features.forEach(item => {
+            console.log(item, 'item');
             // 使用 writeGeometry 而不是 writeFeature,因为 setLayerWkt 期望的是几何体的 WKT
             const geometry = item.getGeometry()
             geometryArr.push(format.writeGeometry(geometry, {

+ 24 - 21
src/views/old_mini/monitor/index.vue

@@ -12,10 +12,7 @@
                 <div class="line-title" @click="handlePage">作物档案</div>
                 <div class="header-right">
                     <div class="add-variety-btn" v-if="varietyTabs.length > 0" @click="handleAddVariety">
-                        <el-icon size="12">
-                            <Plus />
-                        </el-icon>
-                        <span>新增品种</span>
+                        <span>分区管理</span>
                     </div>
                     <el-date-picker style="width: 110px" v-model="date" type="year" placeholder="全部日期" />
                 </div>
@@ -24,7 +21,7 @@
             <div class="variety-tabs" v-if="varietyTabs.length > 0">
                 <div v-for="(v, index) in varietyTabs" :key="index" class="variety-tab"
                     :class="{ 'variety-tab--active': activeVariety === index }" @click="handleVarietyClick(v, index)">
-                    {{ v.regionName }}
+                    {{ v.regionName || v.problemZoneTypeName }}
                 </div>
             </div>
             <template v-if="!varietyTabs.length">
@@ -53,7 +50,7 @@
                     <img src="@/assets/img/monitor/report-icon.png" alt="" class="report-icon" />
                 </div> -->
                 <div class="time-line">
-                    <archives-farm-time-line key="monitor" :farmId="farmIdData" :regionId="regionData"></archives-farm-time-line>
+                    <archives-farm-time-line :problemZoneId="currentVariety?.id" :farmId="farmIdData" :regionId="regionData"></archives-farm-time-line>
                 </div>
             </div>
         </div>
@@ -99,10 +96,15 @@ const getVarietyTabs = async (isShowPopup = true) => {
         return;
     }
     try {
-        const res = await VE_API.monitor.listRegionsBySubjectId({
+        const res = await VE_API.basic_farm.fetchProblemZoneList({
             subjectId: gardenId.value,
         });
-        varietyTabs.value = res.data || []
+        // const res = await VE_API.monitor.listRegionsBySubjectId({
+        //     subjectId: gardenId.value,
+        // });
+        if(res.data && res.data.regionList && res.data.problemZones){
+            varietyTabs.value = res.data.regionList.concat(res.data.problemZones)
+        }
         if (varietyTabs.value.length > 0) {
             handleVarietyClick(varietyTabs.value[activeVariety.value || 0], activeVariety.value || 0)
             if (isShowPopup && !showSelectRegionPopup.value && agriExecutePopupRef.value) {
@@ -117,13 +119,15 @@ const getVarietyTabs = async (isShowPopup = true) => {
 const farmIdData = ref(null);
 const regionData = ref(null);
 const titlePopup = ref("");
-const currentVariety = ref(null);
+const currentVariety = ref({});
 const handleVarietyClick = (tab, index) => {
     activeVariety.value = index;
-    farmIdData.value = tab.farmId;
-    regionData.value = tab.regionId;
+    if(tab.regionId){
+        farmIdData.value = tab.farmId;
+        regionData.value = tab.regionId;
+    }
     currentVariety.value = tab;
-    if (tab.lastViewTime == null) {
+    if (tab.lastViewTime == null && tab.regionId) {
         titlePopup.value = `勾选 ${tab.regionName} 区域`;
         showSelectRegionPopup.value = true;
         VE_API.basic_farm.updateLastViewTime({
@@ -139,7 +143,8 @@ const date = ref(new Date());
 const showSelectRegionPopup = ref(false);
 
 const handleAddVariety = () => {
-    router.push("/interaction?addVariety=true&subjectId=" + gardenId.value);
+    // router.push("/interaction?addVariety=true&subjectId=" + gardenId.value);
+    router.push(`/draw_area?subjectId=${gardenId.value}&type=viewOnly`);
 };
 
 const handleSkipSelectRegion = () => {
@@ -348,22 +353,21 @@ function handleReportClick() {
                     align-items: center;
                     gap: 3px;
                     padding: 4px 10px;
-                    border: 1px solid #DCDCDC;
-                    border-radius: 3px;
-                    background: #fff;
+                    border: 1px solid #2199F8;
+                    border-radius: 2px;
+                    color: #2199F8;
+                    background: rgba(33, 153, 248, 0.1);
                 }
             }
         }
 
         .variety-tabs {
             display: flex;
+            flex-wrap: wrap;
             align-items: center;
+            align-content: flex-start;
             gap: 8px;
             margin: 10px 0;
-            overflow-x: auto;
-            overflow-y: hidden;
-            flex-wrap: nowrap;
-            -webkit-overflow-scrolling: touch;
 
             .variety-tab {
                 padding: 4px 12px;
@@ -371,7 +375,6 @@ function handleReportClick() {
                 color: #767676;
                 background: #fff;
                 white-space: nowrap;
-                flex-shrink: 0;
             }
 
             .variety-tab--active {

+ 212 - 77
src/views/old_mini/monitor/subPages/darwArea.vue

@@ -8,7 +8,7 @@
                 {{ item.name }}
             </div>
         </div>
-        <div class="variety-tabs" v-if="varietyTabs.length > 0">
+        <div class="variety-tabs" v-if="varietyTabs.length > 0 && activeRegionType !== 'DORMANCY'">
             <div v-for="(v, index) in varietyTabs" :key="index" class="variety-tab"
                 :class="{ 'variety-tab--active': activeVariety === index }" @click="handleVarietyClick(v, index)">
                 {{ v.regionName || v.problemZoneTypeName }}
@@ -40,7 +40,6 @@
 <script setup>
 import customHeader from "@/components/customHeader.vue";
 import { ref, computed, onActivated, onDeactivated, nextTick } from "vue";
-import { useStore } from "vuex";
 import DrawRegionMap from "../../interactionList/map/drawRegionMap.js";
 import { Map as KMapMap } from "@/utils/ol-map/KMap";
 import { useRouter, useRoute } from "vue-router";
@@ -54,12 +53,20 @@ import { Point } from "ol/geom";
 import * as proj from "ol/proj";
 import { getArea } from "ol/sphere.js";
 
-const store = useStore();
 const router = useRouter();
 const route = useRoute();
 const mapContainer = ref(null);
 const drawRegionMap = new DrawRegionMap();
 
+// handleVarietyClick 内部有延迟渲染(setTimeout),切换查看/编辑或重建地图时必须取消,避免旧回调在新实例上再次 addFeature 造成叠加
+const pendingGeomRenderTimer = ref(null);
+const clearPendingGeomRenderTimer = () => {
+    if (pendingGeomRenderTimer.value) {
+        clearTimeout(pendingGeomRenderTimer.value);
+        pendingGeomRenderTimer.value = null;
+    }
+};
+
 const confirmDrawTypePopupRef = ref(null);
 const selectedDrawTypeMeta = ref({
     type: "",
@@ -79,14 +86,22 @@ const varietyTabs = ref([]);
 const activeVariety = ref(0);
 const regionGeom = ref(null);
 
-const categoryMap = {
-    variety: [],
-    abnormal: [{ regionName: "病害" }, { regionName: "虫害" }, { regionName: "长势过快" }, { regionName: "长势过慢" }],
-    environment: [{ regionName: "渍水不畅" }, { regionName: "密不透风" }, { regionName: "高温灼伤" }, { regionName: "低温冻害" }],
-    sleep: [],
+
+/** 切换「大类」Tab 时子类列表完全替换,regionsDraftByIndex 按下标缓存会与新区块列表错位;保存成功后也应以接口为准 */
+const clearDraftIndexOnly = () => {
+    regionsDraftByIndex.value = {};
+};
+
+const clearAllRegionDrafts = () => {
+    regionsDraftByKey.value = {};
+    regionsDraftByIndex.value = {};
 };
 
 const handleRegionTypeClick = (item) => {
+    const prevMajor = activeRegionType.value;
+    if (item.code !== prevMajor) {
+        clearDraftIndexOnly();
+    }
     activeRegionType.value = item.code;
     activeVariety.value = 0;
     if (item.code === "variety") {
@@ -148,11 +163,35 @@ const createPolygonStyleFunc = (fillColor, strokeColor) => {
         geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"));
         let area = getArea(geom);
         area = (area + area / 2) / 1000;
+        const growth = getAbnormalGrowthOverlayMeta();
+        if (growth) {
+            styles.push(
+                new Style({
+                    text: new Text({
+                        text: growth.badgeText,
+                        font: "bold 13px sans-serif",
+                        fill: new Fill({ color: "#ffffff" }),
+                        backgroundFill: new Fill({ color: growth.badgeBackground || ABNORMAL_BADGE_BG_GROWTH }),
+                        padding: [4, 10, 4, 10],
+                        offsetY: -40,
+                    }),
+                }),
+                new Style({
+                    text: new Text({
+                        text: `发现时间:${growth.discoveryDate}`,
+                        font: "12px sans-serif",
+                        fill: new Fill({ color: "#ffffff" }),
+                        offsetY: -16,
+                    }),
+                })
+            );
+        }
         const areaValStyle = new Style({
             text: new Text({
                 font: "16px sans-serif",
                 text: area.toFixed(2) + "亩",
                 fill: new Fill({ color: "#fff" }),
+                offsetY: growth ? 14 : 0,
             }),
         });
         styles.push(fillStyle, areaValStyle);
@@ -169,17 +208,25 @@ const applyRegionStyles = () => {
     let fillColor = [0, 0, 0, 0.5];
     let strokeColor = "#2199F8";
 
-    if (activeRegionType.value === "variety") {
+    const styleKind = getCanonicalRegionTypeForStyles();
+    if (styleKind === "variety") {
         lineColor = "#18AA8B";
         vertexColor = "#18AA8B";
         fillColor = [0, 57, 44, 0.5];
         strokeColor = "#18AA8B";
-    } else if (activeRegionType.value === "abnormal") {
-        lineColor = "#E03131";
-        vertexColor = "#E03131";
-        fillColor = [100, 0, 0, 0.5];
-        strokeColor = "#E03131";
-    } else if (activeRegionType.value === "environment") {
+    } else if (styleKind === "ABNORMAL") {
+        if(activeVariety.value < 2){
+            lineColor = "#E03131";
+            vertexColor = "#E03131";
+            fillColor = [100, 0, 0, 0.5];
+            strokeColor = "#E03131";
+        }else{
+            lineColor = "#FF7300";
+            vertexColor = "#FF7300";
+            fillColor = [124, 46, 0, 0.5];
+            strokeColor = "#FF7300";
+        }
+    } else if (styleKind === "ENVIRONMENT") {
         lineColor = "#FDCF7F";
         vertexColor = "#FDCF7F";
         fillColor = [151, 96, 0, 0.5];
@@ -309,7 +356,43 @@ const renderAllVarietyRegionsReadonlyOnly = () => {
     drawRegionMap.setStatusRegions(regions);
 };
 
+/**
+ * 用户“闭环(drawend)”后,把当前绘制结果自动写入草稿,避免切换 tab 时被清掉。
+ * 注意:getAreaGeometry 可能包含多块,这里以“最新一块”为准(最后一个 feature)
+ */
+const syncClosedDrawToDraft = () => {
+    const tab = varietyTabs.value?.[activeVariety.value];
+    if (!tab) return;
+    const polygonData = drawRegionMap.getAreaGeometry?.();
+    const geometryArr = polygonData?.geometryArr;
+    if (!Array.isArray(geometryArr) || geometryArr.length === 0) return;
+    const geom = String(geometryArr[geometryArr.length - 1] || "").trim();
+    if (!isValidGeom(geom)) return;
+
+    const key = draftKeyFromParts(activeRegionType.value, tab);
+    if (key) {
+        const prev = regionsDraftByKey.value[key] || {};
+        regionsDraftByKey.value[key] = {
+            ...prev,
+            geomArr: [geom],
+            geom,
+        };
+    }
+    regionsDraftByIndex.value[activeVariety.value] = { geomArr: [geom], geom };
+    regionGeom.value = geom;
+};
+
 const handleVarietyClick = (tab, index) => {
+    // 取消上一次延迟渲染,避免快速切换 tab / 切换编辑态时重复追加要素
+    clearPendingGeomRenderTimer();
+
+    // 若存在“未闭环”的绘制草图(Draw 的 sketch 在 overlay 上),切换时需要清掉;已闭环的要素会走 drawend 写入草稿
+    const draw = drawRegionMap.kmap?.draw;
+    const hasUnclosedSketch = !!(draw && (draw.sketchFeature_ || draw.sketchCoords_ || draw.sketchLine_));
+    if (hasUnclosedSketch) {
+        drawRegionMap.abortOngoingDrawSketch?.();
+    }
+
     activeVariety.value = index;
     // 取值优先级:
     // 1) 草稿(regionsDraftByKey / regionsDraftByIndex)
@@ -343,9 +426,11 @@ const handleVarietyClick = (tab, index) => {
     // 切换时先清空当前可编辑图层,避免叠加
     drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
     if (geomArr.length > 0) {
-        setTimeout(() => {
+        pendingGeomRenderTimer.value = setTimeout(() => {
+            // 再兜底清一次,避免异步回调在 destroy/init 后仍执行导致重复追加
+            drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
             // 切换小类仅负责“显示”,不主动缩放;缩放留到点击“编辑”时触发
-            drawRegionMap.setAreaGeometry(geomArr, false);
+            drawRegionMap.setAreaGeometry(geomArr, false, undefined, undefined, getAbnormalGrowthOverlayMeta());
             // 需要 fit 时,确保视野包含当前渲染的所有地块(只读层 + 当前层)
             drawRegionMap.fitAllRegions?.();
         }, 50);
@@ -355,26 +440,6 @@ const handleVarietyClick = (tab, index) => {
 const viewOnly = computed(() => route.query.type === "viewOnly");
 
 const point = ref(null);
-const farmVarietyList = ref([]);
-async function fetchFarmSubjectDetail() {
-    const subjectId = route.query?.subjectId;
-    if (!subjectId) return;
-
-    const { data } = await VE_API.basic_farm.fetchFarmSubjectDetail({ subjectId });
-
-
-    if (data?.regionList?.length) {
-        if (!hasAppliedInitialVariety.value && route.query?.varietyId) {
-            activeVariety.value = resolveInitialVarietyIndex(data?.regionList);
-            hasAppliedInitialVariety.value = true;
-        }
-
-        point.value = data.farmLocation;
-        // farmVarietyList.value = data.regionList;
-        // varietyTabs.value = data.regionList;
-        // handleVarietyClick(varietyTabs.value[activeVariety.value], activeVariety.value);
-    }
-}
 
 const regionTypeTabs = ref([]);
 const activeRegionType = ref("variety");
@@ -398,30 +463,55 @@ async function fetchRegionInfo() {
     }
 }
 
-const resolveProblemZoneGroupKind = (g) => {
-    if (!g) return "";
-    const code = (g.code ?? "").toString().trim().toLowerCase();
-    const name = (g.name ?? "").toString();
-    if (name.includes("异常") || code === "abnormal" || code.includes("abnormal")) return "abnormal";
-    if (name.includes("环境") || code === "environment" || code.includes("environment")) return "environment";
-    if (
-        name.includes("休眠") ||
-        code === "sleep" ||
-        code.includes("sleep") ||
-        code.includes("dormant") ||
-        code.includes("dormancy") ||
-        code.includes("hibern")
-    ) {
-        return "sleep";
-    }
-    return "";
+/** 样式用的大类:与接口 tab.code 解耦(避免 ABNORMAL / 数字 code 等导致勾画色落到默认灰) */
+const getCanonicalRegionTypeForStyles = () => {
+    const raw = activeRegionType.value;
+    if (raw === "variety") return "variety";
+    const tabs = regionTypeTabs.value || [];
+    const item = tabs.find((t) => String(t?.code) === String(raw));
+    if (item) {
+        const kind = item.code;
+        if (kind) return kind;
+    }
+    return "SLEEP";
+};
+
+const ABNORMAL_BADGE_BG_DISEASE_PEST = "#E32A28";
+const ABNORMAL_BADGE_BG_GROWTH = "#F76F00";
+
+/** 异常区小类(长势/病害/虫害等)闭合地块后在多边形内展示标签与发现日期(查看态 setAreaGeometry 同步使用) */
+const getAbnormalGrowthOverlayMeta = () => {
+    if (getCanonicalRegionTypeForStyles() !== "ABNORMAL") return null;
+    const tab = varietyTabs.value?.[activeVariety.value];
+    if (!tab) return null;
+    const name = (tab.problemZoneTypeName || tab.regionName || "").toString();
+    let badgeText = "";
+    let badgeBackground = ABNORMAL_BADGE_BG_GROWTH;
+    if (name.includes("病害")) {
+        badgeText = "新增病害";
+        badgeBackground = ABNORMAL_BADGE_BG_DISEASE_PEST;
+    } else if (name.includes("虫害")) {
+        badgeText = "新增虫害";
+        badgeBackground = ABNORMAL_BADGE_BG_DISEASE_PEST;
+    } else if (name.includes("过慢")) {
+        badgeText = "新增长势过慢";
+    } else if (name.includes("过快")) {
+        badgeText = "新增长势过快";
+    } else {
+        return null;
+    }
+    const now = new Date();
+    const y = now.getFullYear();
+    const m = String(now.getMonth() + 1).padStart(2, "0");
+    const d = String(now.getDate()).padStart(2, "0");
+    return { badgeText, discoveryDate: `${y}.${m}.${d}`, badgeBackground };
 };
 
 const findProblemZoneGroupByDrawType = (majorType) => {
     if (!majorType || majorType === "variety") return null;
     const list = regionInfo.value?.problemZoneList || [];
     return (
-        list.find((x) => resolveProblemZoneGroupKind(x) === majorType) ||
+        list.find((x) => x.code === majorType) ||
         list.find((x) => String(x?.code) === String(majorType))
     );
 };
@@ -481,7 +571,7 @@ const buildProblemZoneListForSubmit = () => {
     const subjectId = route.query.subjectId;
     const groups = [];
     raw.forEach((group) => {
-        const canonical = resolveProblemZoneGroupKind(group);
+        const canonical = group.code;
         const children = (group.children || [])
             .map((child) => {
                 if (!canonical) return null;
@@ -514,6 +604,7 @@ const buildProblemZoneListForSubmit = () => {
 onActivated(async () => {
     activeVariety.value = 0;
     hasAppliedInitialVariety.value = false;
+    clearPendingGeomRenderTimer();
     // keep-alive 场景下再次进入前先销毁旧地图实例,避免重复 init 导致图层状态错位
     if (drawRegionMap.kmap) {
         drawRegionMap.abortOngoingDrawSketch?.();
@@ -522,9 +613,8 @@ onActivated(async () => {
 
     type.value = route.query.type;
     const editable = !viewOnly.value;
-    // await fetchFarmSubjectDetail();
     await fetchRegionInfo();
-    drawRegionMap.initMap(point.value, mapContainer.value, editable, true, true);
+    drawRegionMap.initMap(point.value, mapContainer.value, editable, true, true, syncClosedDrawToDraft);
     applyRegionStyles();
 
     // 首次进入时,fetch 阶段可能先于地图初始化触发了 tab 渲染;
@@ -537,7 +627,7 @@ onActivated(async () => {
     // 从编辑态进入仅查看时,需重新初始化为不可编辑
     if (viewOnly.value && drawRegionMap.kmap && drawRegionMap.editable) {
         drawRegionMap.destroyMap();
-        drawRegionMap.initMap(point.value, mapContainer.value, false, true, false);
+        drawRegionMap.initMap(point.value, mapContainer.value, false, true, false, syncClosedDrawToDraft);
         applyRegionStyles();
         if (varietyTabs.value.length > 0 && varietyTabs.value[activeVariety.value]) {
             handleVarietyClick(varietyTabs.value[activeVariety.value], activeVariety.value);
@@ -547,7 +637,7 @@ onActivated(async () => {
     // 从仅查看进入勾画(编辑)时,需重新初始化为可编辑
     if (!viewOnly.value && drawRegionMap.kmap && !drawRegionMap.editable) {
         drawRegionMap.destroyMap();
-        drawRegionMap.initMap(point.value, mapContainer.value, true, true, true);
+        drawRegionMap.initMap(point.value, mapContainer.value, true, true, true, syncClosedDrawToDraft);
         applyRegionStyles();
         if (varietyTabs.value.length > 0 && varietyTabs.value[activeVariety.value]) {
             handleVarietyClick(varietyTabs.value[activeVariety.value], activeVariety.value);
@@ -562,6 +652,7 @@ onActivated(async () => {
 
 onDeactivated(() => {
     activeVariety.value = 0;
+    clearPendingGeomRenderTimer();
     // 离开页面时中止未完成绘制并销毁地图,确保下次进入是干净实例
     drawRegionMap.abortOngoingDrawSketch?.();
     drawRegionMap.destroyMap?.();
@@ -598,7 +689,13 @@ const resetPolygon = () => {
                 try {
                     const parsed = JSON.parse(polygonDataStr);
                     if (parsed && Array.isArray(parsed.geometryArr) && parsed.geometryArr.length) {
-                        drawRegionMap.setAreaGeometry(parsed.geometryArr);
+                        drawRegionMap.setAreaGeometry(
+                            parsed.geometryArr,
+                            false,
+                            undefined,
+                            undefined,
+                            getAbnormalGrowthOverlayMeta()
+                        );
                     }
                 } catch (_) {
                     // 解析失败则不做处理,仅相当于清空重画
@@ -628,6 +725,24 @@ const submitRegions = async () => {
         };
         const res = await VE_API.basic_farm.ediRegionZone(params);
         if (res?.code === 0) {
+            if(selectedDrawTypeMeta.value.type === 'ABNORMAL'){
+                let problemZoneId = null;
+                if(params.problemZoneList.length){
+                    res.data[0].problemZoneList.forEach(element => {
+                        if(element.code === selectedDrawTypeMeta.value.type){
+                            element.children.forEach(item => {
+                                if(item.problemZoneTypeName === selectedDrawTypeMeta.value.category.problemZoneTypeName){
+                                    problemZoneId = item.id;
+                                }
+                            });
+                        }
+                    });
+                }
+                VE_API.basic_farm.saveProblemZoneImage({
+                    problemZoneId,
+                    imagePathList: selectedDrawTypeMeta.value.images,
+                })
+            }
             return true;
         }
         ElMessage.error(res?.msg || "保存失败");
@@ -651,6 +766,7 @@ const confirmArea = async (drawMeta) => {
         return;
     }
     const polygonData = drawRegionMap.getAreaGeometry?.();
+    console.log(polygonData, 'polygonData');
     const geometryArr = polygonData?.geometryArr;
     if (!Array.isArray(geometryArr) || geometryArr.length === 0) {
         ElMessage.warning("请先勾画地块后再确认");
@@ -687,6 +803,8 @@ const confirmArea = async (drawMeta) => {
     // 保存成功后:不跳走,刷新当前页并回显最新数据,并切换为查看态
     try {
         await fetchRegionInfo();
+        // 接口已是最新几何与 id,清空草稿避免旧 key/下标回退遮挡 geomItems
+        clearAllRegionDrafts();
 
         // 恢复当前大类下的子类 tab(fetchRegionInfo 默认会填充品种区)
         if (activeRegionType.value === "variety") {
@@ -732,18 +850,13 @@ const openConfirmDrawTypePopup = () => {
         ElMessage.warning("请先勾画地块后再确认");
         return;
     }
+
+    const remark = varietyTabs?.value?.[activeVariety.value]?.geomItems?.[0]?.remark;
     confirmDrawTypePopupRef.value.openPopup({
-        varietyTabs: varietyTabs.value,
-        regionList: regionInfo.value?.regionList ?? [],
-        problemZoneList: regionInfo.value?.problemZoneList ?? [],
-        /** 接口未返回子类时与地图页本地 categoryMap 一致,避免弹窗内切换大类无选项 */
-        fallbackProblemCategories: {
-            abnormal: categoryMap.abnormal,
-            environment: categoryMap.environment,
-            sleep: categoryMap.sleep,
-        },
+        regionInfo: regionInfo.value,
         activeRegionType: activeRegionType.value,
         activeVarietyIndex: activeVariety.value,
+        remark: activeRegionType.value === 'variety' ? '' : remark,
     });
 };
 
@@ -752,15 +865,17 @@ const handleConfirmDrawType = async (payload) => {
     await confirmArea(payload);
 };
 
-const handleEditRegion = () => {
+const handleEditRegion = async () => {
     // 从查看态切换到可勾画编辑态:移除查看标记(所有大类共用)
     const nextQuery = { ...route.query };
     delete nextQuery.type;
     delete nextQuery.viewOnly;
-    router.replace({ query: nextQuery });
+    clearPendingGeomRenderTimer();
+    await router.replace({ query: nextQuery });
 
     nextTick(() => {
         if (drawRegionMap.kmap) {
+            drawRegionMap.abortOngoingDrawSketch?.();
             drawRegionMap.destroyMap();
         }
 
@@ -769,7 +884,7 @@ const handleEditRegion = () => {
         // - 只显示「品种区」地块作为底图参考(只读)
         // - 不显示其它问题区地块
         if (activeRegionType.value !== "variety") {
-            drawRegionMap.initMap(point.value, mapContainer.value, true, true, true);
+            drawRegionMap.initMap(point.value, mapContainer.value, true, true, true, syncClosedDrawToDraft);
             applyRegionStyles();
             // 清空当前可编辑图层,避免残留
             drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
@@ -786,7 +901,14 @@ const handleEditRegion = () => {
             regionGeom.value =
                 currentGeomArr.length > 1 ? JSON.stringify(currentGeomArr) : currentGeomArr[0] || "";
             if (currentGeomArr.length > 0) {
-                drawRegionMap.setAreaGeometry(currentGeomArr, true);
+                drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
+                drawRegionMap.setAreaGeometry(
+                    currentGeomArr,
+                    true,
+                    undefined,
+                    undefined,
+                    getAbnormalGrowthOverlayMeta()
+                );
             } else {
                 // 仍自适应到品种区底图
                 drawRegionMap.fitAllRegions?.();
@@ -794,7 +916,7 @@ const handleEditRegion = () => {
             return;
         }
 
-        drawRegionMap.initMap(point.value, mapContainer.value, true, true, true);
+        drawRegionMap.initMap(point.value, mapContainer.value, true, true, true, syncClosedDrawToDraft);
         applyRegionStyles();
 
         // 切到编辑态后:统一走一遍当前 tab 的点击逻辑,确保所有有地块的品种都能显示
@@ -813,13 +935,26 @@ const handleEditRegion = () => {
                       })();
             drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
             if (geomArr.length > 0) {
-                drawRegionMap.setAreaGeometry(geomArr, true);
+                drawRegionMap.setAreaGeometry(
+                    geomArr,
+                    true,
+                    undefined,
+                    undefined,
+                    getAbnormalGrowthOverlayMeta()
+                );
             }
         } else {
             renderReadonlyVarietyRegions(activeVariety.value);
             const fallbackGeom = regionGeom.value;
             if (isValidGeom(fallbackGeom)) {
-                drawRegionMap.setAreaGeometry([fallbackGeom], true);
+                drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
+                drawRegionMap.setAreaGeometry(
+                    [fallbackGeom],
+                    true,
+                    undefined,
+                    undefined,
+                    getAbnormalGrowthOverlayMeta()
+                );
             }
         }
         // 编辑态下:保持缩放在当前要编辑地块,不再强制 fitAllRegions 覆盖

+ 5 - 2
src/views/old_mini/monitor/subPages/farmInfo.vue

@@ -129,8 +129,11 @@ const initMap = () => {
     }
 
     if (geometryArr.length) {
-        // 这里不做自适应缩放,保持以中心点为主,避免点位偏上
-        drawRegionMap.setAreaGeometry(Array.from(geometryArr), true);
+        // 农场信息页:地块用绿色展示;第三参留空表示面积仍按地块自动计算
+        drawRegionMap.setAreaGeometry(Array.from(geometryArr), true, undefined, {
+            fill: "rgba(0, 57, 44, 0.5)",
+            stroke: "#18AA8B",
+        });
     }
 
     // 在区域中心落一个点位(使用与勾画页相同的图标)