فهرست منبع

feat:修改农事详情页面内容

wangsisi 1 روز پیش
والد
کامیت
dee804df56

+ 3 - 1
src/components/pageComponents/ArchivesFarmTimeLine.vue

@@ -888,12 +888,14 @@ const handleStatusDetail = (fw) => {
     saveTimelineScrollTop();
     emits('card-click');
     if (props.pageType === 'agri_plan') {
+        console.log(fw);
         if (fw?.sourceData && fw?.id) {
             router.push({
                 path: "/agricultural_detail",
                 query: {
                     id: fw?.id,
-                    content: fw?.content
+                    title: fw?.title,
+                    content: fw?.content,
                 },
             });
         }

+ 32 - 8
src/views/old_mini/home/subPages/prescriptionPage.vue

@@ -16,7 +16,7 @@
                         <div class="tips">灌溉方式确认药肥最佳配比</div>
                         <div class="item-checkbox">
                             <div class="tag-group add-tag-group">
-                                <div class="tag-item" :class="{ self: item.custom === true, selected: item.selected }"
+                                <div class="tag-item" :class="{ self: item.custom === true && item.selected, selected: item.selected }"
                                     @click="handleSelect('irrigationMethods', idx, item.custom)"
                                     v-for="(item, idx) in basicFarmFormData.irrigationMethods" :key="'ce-' + idx">
                                     <span class="text">{{ item.name }}</span>
@@ -35,8 +35,7 @@
                         <div class="item-checkbox">
                             <el-radio-group v-model="basicForm.needZbCode" @change="handleNeedZbChange">
                                 <el-radio-button v-for="item in (basicFarmFormData.needZbOptions || [])"
-                                    :key="item.code || item.value"
-                                    :label="item.name || item.label" :value="String(item.code ?? item.value)" />
+                                    :key="item.code" :label="item.name" :value="item.code" />
                             </el-radio-group>
                         </div>
                     </div>
@@ -88,9 +87,12 @@ const getBasicFarmFormData = () => {
         basicFarmFormData.value = data;
 
         if (Array.isArray(basicFarmFormData.value.needZbOptions)) {
-            const selectedExpert = basicFarmFormData.value.needZbOptions.find(item => item.selected);
-            if (selectedExpert) {
-                basicForm.value.needZbCode = String(selectedExpert.code ?? selectedExpert.value);
+            const needZbOptions = basicFarmFormData.value.needZbOptions;
+            const selectedExpert = needZbOptions.find(item => item.selected);
+            const defaultOption = selectedExpert || needZbOptions[0];
+            if (defaultOption) {
+                basicForm.value.needZbCode = defaultOption.code;
+                handleNeedZbChange(defaultOption.code);
             }
         }
     }).finally(() => {
@@ -135,7 +137,29 @@ function handleSelect(type, index, custom) {
         popupInputVal.value = basicFarmFormData.value[type][index].name;
         currentEditIndex.value = index;
     }
-    basicFarmFormData.value[type][index].selected = !basicFarmFormData.value[type][index].selected;
+    const optionList = basicFarmFormData.value[type] || [];
+    const currentOption = optionList[index];
+    if (!currentOption) {
+        return;
+    }
+
+    const isNoneOption = String(currentOption.name ?? "").trim() === "无";
+    if (isNoneOption) {
+        const nextSelected = !currentOption.selected;
+        optionList.forEach((item, idx) => {
+            item.selected = nextSelected ? idx === index : false;
+        });
+        return;
+    }
+
+    currentOption.selected = !currentOption.selected;
+    if (currentOption.selected) {
+        optionList.forEach((item, idx) => {
+            if (idx !== index && String(item.name ?? "").trim() === "无") {
+                item.selected = false;
+            }
+        });
+    }
 }
 
 function handleNeedZbChange(selectedCode) {
@@ -211,7 +235,7 @@ const goBack = () => {
 
 function handleSubmit() {
     const irrigationMethods = (basicFarmFormData.value.irrigationMethods || [])
-        .filter(item => item.selected || item.custom)
+        .filter(item => item.selected)
         .map(item => item.code || item.id);
     const draftData = {
         irrigationMethods: irrigationMethods.map(code => Number(code)),

+ 12 - 3
src/views/old_mini/interactionList/components/examplePopup.vue

@@ -7,6 +7,8 @@
                     :show-indicators="false"
                     v-model="activeIndex"
                     :autoplay="0"
+                    :loop="false"
+                    @change="handleSwipeChange"
                     indicator-color="#2199F8"
                 >
                     <SwipeItem v-for="img in images" :key="img?.exampleImageUrl">
@@ -88,7 +90,9 @@ watch(
     () => [props.show, props.startIndex],
     ([show, start]) => {
         if (show) {
-            activeIndex.value = start || 0;
+            const lastIndex = Math.max(props.images.length - 1, 0);
+            const normalizedIndex = Math.min(Math.max(start || 0, 0), lastIndex);
+            activeIndex.value = normalizedIndex;
             if (exampleSwipeRef.value) {
                 exampleSwipeRef.value.swipeTo(activeIndex.value);
             }
@@ -98,15 +102,20 @@ watch(
 
 const prevExample = () => {
     if (!props.images.length) return;
-    activeIndex.value = (activeIndex.value - 1 + props.images.length) % props.images.length;
+    activeIndex.value = Math.max(activeIndex.value - 1, 0);
     exampleSwipeRef.value && exampleSwipeRef.value.swipeTo(activeIndex.value);
 };
 
 const nextExample = () => {
     if (!props.images.length) return;
-    activeIndex.value = (activeIndex.value + 1) % props.images.length;
+    const lastIndex = props.images.length - 1;
+    activeIndex.value = Math.min(activeIndex.value + 1, lastIndex);
     exampleSwipeRef.value && exampleSwipeRef.value.swipeTo(activeIndex.value);
 };
+
+const handleSwipeChange = (index) => {
+    activeIndex.value = Number(index) || 0;
+};
 </script>
 
 <style scoped lang="scss">

+ 673 - 66
src/views/old_mini/interactionList/drawRegion.vue

@@ -1,118 +1,558 @@
 <template>
     <div class="edit-map">
         <custom-header :name="viewOnly ? '查看区域' : '勾画区域'"></custom-header>
+        <div class="region-type-tabs">
+            <div v-for="item in regionTypeTabs" :key="item.code" class="region-type-tab"
+                :class="{ 'region-type-tab--active': activeRegionType === item.code }">
+                {{ item.name }}
+            </div>
+        </div>
+        <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 }">
+                {{ v.regionName || v.problemZoneTypeName }}
+            </div>
+        </div>
         <div class="edit-map-content">
             <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
             <div class="map-container" ref="mapContainer"></div>
-            <div class="edit-map-footer">
+            <div class="edit-map-footer" :style="{ 'bottom': activeRegionType !== 'DORMANCY' ? '85px' : '59px' }">
                 <div class="footer-back" @click="goBack">
                     <img class="back-icon" src="@/assets/img/home/go-back.png" alt="" />
                 </div>
                 <div class="edit-map-footer-btn" v-if="!viewOnly">
                     <div class="btn-delete" @click="deletePolygon">删除地块</div>
-                    <div class="btn-cancel" @click="goBack">取消</div>
-                    <div class="btn-confirm" @click="confirm">确认</div>
+                    <!-- <div class="btn-cancel" @click="goBack">取消</div> -->
+                    <div class="btn-confirm" @click="confirm">确认区域</div>
                 </div>
             </div>
         </div>
+
+        <popup v-model:show="showAbnormalTypePopup" round closeable class="abnormal-popup"
+            :close-on-click-overlay="true">
+            <div class="abnormal-popup-content">
+                <div class="abnormal-popup-title">请选择异常类型</div>
+                <el-select v-model="selectedAbnormalType" class="abnormal-type-select" placeholder="请选择类型" size="large">
+                    <el-option v-for="item in abnormalTypeOptions" :key="item.value" :label="item.name"
+                        :value="item.value" />
+                </el-select>
+                <div class="abnormal-popup-confirm" @click="handleConfirmUpload">确认上传</div>
+            </div>
+        </popup>
     </div>
 </template>
 
 <script setup>
 import customHeader from "@/components/customHeader.vue";
 import { ref, computed, onMounted, onActivated, onDeactivated } from "vue";
+import { Popup } from "vant";
 import DrawRegionMap from "./map/drawRegionMap.js";
+import { Map as KMapMap } from "@/utils/ol-map/KMap";
 import { useRouter, useRoute } from "vue-router";
 import { convertPointToArray } from "@/utils/index";
 import { ElMessage, ElMessageBox } from "element-plus";
+import Style from "ol/style/Style";
+import { Fill, Stroke, Circle, Text } from "ol/style.js";
+import { Point } from "ol/geom";
+import * as proj from "ol/proj";
+import { getArea } from "ol/sphere.js";
 
 const router = useRouter();
 const route = useRoute();
 const mapContainer = ref(null);
 const drawRegionMap = new DrawRegionMap();
+const DEFAULT_MAP_CENTER = "POINT (113.6142086995688 23.585836479509055)";
 
 const type = ref(null);
 const viewOnly = computed(() => route.query.viewOnly === "1" || route.query.viewOnly === "true");
+const showDataMode = computed(() => route.query.showData === "1" || route.query.showData === "true");
+const showAbnormalTypePopup = ref(false);
+const selectedAbnormalType = ref("");
+const abnormalTypeOptions = [{
+    name: "病害",
+    value: "DISEASE"
+}, {
+    name: "虫害",
+    value: "PEST"
+}, {
+    name: "不确定",
+    value: "UNCERTAIN"
+}];
 
 onMounted(() => {
     type.value = route.query.type;
-    const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
+    const point = route.query.mapCenter || DEFAULT_MAP_CENTER;
     const editable = !viewOnly.value;
     const showPoint = !viewOnly.value;
     drawRegionMap.initMap(point, mapContainer.value, editable, true, showPoint);
+    applyRegionStyles();
+});
+
+onActivated(async () => {
+    const point = route.query.mapCenter || DEFAULT_MAP_CENTER;
+    await fetchRegionInfo();
+
+    syncMapEditableState(point);
+    const polygonData = route.query.polygonData;
+    const rawRangeWkt = route.query.rangeWkt;
+    const rangeWkt = rawRangeWkt ? decodeURIComponent(rawRangeWkt) : null;
+    const shouldRenderFromApi = showDataMode.value && !rangeWkt && !polygonData;
+
+    applyRegionStyles();
+    if (shouldRenderFromApi) {
+        renderRegionsFromApi();
+    }
+    if (rangeWkt) {
+        renderRegionsFromRouteRangeWkt(rangeWkt);
+    }
+    restoreEditablePolygonFromRoute(polygonData);
+    updateMapCenterForEditMode(point);
 
-    // 地图初始化之后(比如 initPreviewMap 里)
-    // const regions = [
-    //     {
-    //         geometry:
-    //             "MULTIPOLYGON(((113.61674040430906 23.586573370597367,113.61586610436014 23.585922976493354,113.61710291900188 23.58486741952544,113.61770000158238 23.585651090473736,113.61674040430906 23.586573370597367)))",
-    //         status: "unresolved", // 未解决(蓝色)
-    //     },
-    //     {
-    //         geometry:
-    //             "MULTIPOLYGON(((113.61516640298626 23.588441931082958,113.61445736699218 23.58799411906573,113.61572616841707 23.586954554834552,113.61642987338976 23.588180707433526,113.61516640298626 23.588441931082958)))",
-    //         status: "resolved", // 已解决(灰色)
-    //     },
-    // ];
-
-    // drawRegionMap.setStatusRegions(regions);
+    applyRegionStyles();
 });
 
-onActivated(() => {
-    const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
+const regionTypeTabs = ref([]);
+const activeRegionType = ref("variety");
+const regionInfo = ref([]);
+const formatDateToYmdDash = (raw) => {
+    const sourceDate = raw ? new Date(raw) : new Date();
+    const resolvedDate = Number.isNaN(sourceDate.getTime()) ? new Date() : sourceDate;
+    const y = resolvedDate.getFullYear();
+    const m = String(resolvedDate.getMonth() + 1).padStart(2, "0");
+    const d = String(resolvedDate.getDate()).padStart(2, "0");
+    return `${y}-${m}-${d}`;
+};
+
+const isValidGeomWkt = (geom) => {
+    const text = String(geom || "").trim();
+    return text.length > 10 && (text.includes("POLYGON") || text.includes("MULTIPOLYGON"));
+};
+
+const isTreatmentOrControlledStatus = (status) => {
+    const normalizedStatus = String(status || "").trim();
+    return normalizedStatus === "2" || normalizedStatus === "3";
+};
+
+/**
+ * 将不同入口(接口回显/路由透传)的几何数据统一映射为地图组件可消费的数据结构,
+ * 避免散落在各处拼装导致字段不一致。
+ */
+const buildRegionBasePayload = (
+    geometry,
+    regionStyleKind,
+    reproductiveName,
+    updatedTime,
+    handleStatus,
+    lockStyleType = "",
+    label = ""
+) => {
+    if (!isValidGeomWkt(geometry)) return null;
+    const hasTreatmentStatus = isTreatmentOrControlledStatus(handleStatus);
+    const shouldUseLockedDiseaseStyle = regionStyleKind === "ABNORMAL" && hasTreatmentStatus && !!lockStyleType;
+    return {
+        geometry: String(geometry).trim(),
+        status: "unresolved",
+        regionStyleKind,
+        reproductiveName,
+        updatedTime,
+        displayMode: shouldUseLockedDiseaseStyle ? "lockedDisease" : "",
+        handleStatus: hasTreatmentStatus ? String(handleStatus).trim() : "",
+        lockStyleType: shouldUseLockedDiseaseStyle ? lockStyleType : "",
+        label,
+    };
+};
+
+const buildAbnormalLabel = (name, handleStatus) => {
+    const status = String(handleStatus || "").trim();
+    const isTreatingOrControlled = isTreatmentOrControlledStatus(status);
+    const prefix = name.includes("虫害")
+        ? "虫害"
+        : name.includes("病害")
+            ? "病害"
+            : name.includes("过快")
+                ? "长势过快"
+                : name.includes("过慢")
+                    ? "长势过慢"
+                    : "";
+    if (!prefix) return "";
+    if (isTreatingOrControlled) {
+        return status === "3" ? `${prefix}已控制` : `${prefix}治疗中`;
+    }
+    return `新增${prefix}`;
+};
+
+const buildShowDataRegionsFromApi = () => {
+    const currentTab = (varietyTabs.value || [])[activeVariety.value] || {};
+    if (!currentTab) return [];
+    const styleKind = getCanonicalRegionTypeForStyles();
+    const reproductiveName = String(currentTab.problemZoneTypeName || currentTab.regionName || "");
+    const isAbnormal = styleKind === "ABNORMAL";
+    const geomItems = Array.isArray(currentTab.geomItems) ? currentTab.geomItems : [];
+    const regions = [];
+
+    geomItems.forEach((item) => {
+        const geometry = String(item?.geomWkt || "").trim();
+        if (!isValidGeomWkt(geometry)) return;
+        const handleStatus = String(item?.handleStatus ?? "").trim();
+        const hasTreatmentStatus = isTreatmentOrControlledStatus(handleStatus);
+        const isGrowthAbnormal = reproductiveName.includes("过慢") || reproductiveName.includes("过快");
+        const region = buildRegionBasePayload(
+            geometry,
+            styleKind,
+            reproductiveName,
+            formatDateToYmdDash(item?.createTime || currentTab?.createTime),
+            handleStatus,
+            isAbnormal && hasTreatmentStatus
+                ? (handleStatus === "3" ? "controlled" : (isGrowthAbnormal ? "growthTreating" : "diseaseTreating"))
+                : "",
+            isAbnormal ? buildAbnormalLabel(reproductiveName, handleStatus) : ""
+        );
+        if (region) regions.push(region);
+    });
+
+    if (regions.length > 0) return regions;
+    const fallbackGeom = String(currentTab.geom || "").trim();
+    if (!isValidGeomWkt(fallbackGeom)) return [];
+    const fallbackRegion = buildRegionBasePayload(
+        fallbackGeom,
+        styleKind,
+        reproductiveName,
+        formatDateToYmdDash(currentTab?.createTime),
+        "",
+        "",
+        isAbnormal ? buildAbnormalLabel(reproductiveName, "") : ""
+    );
+    return fallbackRegion ? [fallbackRegion] : [];
+};
 
-    // 从编辑态进入仅查看时,需重新初始化为不可编辑
-    if (viewOnly.value && drawRegionMap.kmap && drawRegionMap.editable) {
-        drawRegionMap.destroyMap();
-        drawRegionMap.initMap(point, mapContainer.value, false, true, false);
+const fitAllRegionsWhenViewOnly = () => {
+    if (viewOnly.value && drawRegionMap.fitAllRegions) {
+        drawRegionMap.fitAllRegions();
     }
+};
+
+const renderRegionsFromApi = () => {
+    const apiRegions = buildShowDataRegionsFromApi();
+    if (!apiRegions.length) return;
+    drawRegionMap.setStatusRegions(apiRegions);
+    fitAllRegionsWhenViewOnly();
+};
 
-    // 从仅查看进入勾画(编辑)时,需重新初始化为可编辑
-    if (!viewOnly.value && drawRegionMap.kmap && !drawRegionMap.editable) {
-        drawRegionMap.destroyMap();
-        drawRegionMap.initMap(point, mapContainer.value, true, true, true);
+/**
+ * 路由可能传 JSON(geometryArr)或单条 WKT 字符串,这里统一转换为 regions。
+ * 该函数只负责“解析+映射”,不关心地图渲染,便于后续复用和测试。
+ */
+const buildRegionsFromRouteRangeWkt = (rangeWkt) => {
+    const regionStyleKind = getCanonicalRegionTypeForStyles();
+    const currentVarietyTab = (varietyTabs.value || [])[activeVariety.value] || {};
+    // 异常区名称有时不会从路由透传,补充当前 tab 兜底,避免标签回退为“病害治疗中”
+    const reproductiveName = String(
+        route.query.reproductiveName ||
+        route.query.problemZoneTypeName ||
+        currentVarietyTab.problemZoneTypeName ||
+        currentVarietyTab.regionName ||
+        ""
+    );
+    const isAbnormal = regionStyleKind === "ABNORMAL";
+    const routeHandleStatus = String(route.query.handleStatus || "").trim();
+    const hasTreatmentStatus = isTreatmentOrControlledStatus(routeHandleStatus);
+    const isGrowthAbnormal =
+        isAbnormal && (reproductiveName.includes("长势") || reproductiveName.includes("过慢") || reproductiveName.includes("过快"));
+    const abnormalLabel =
+        !isAbnormal
+            ? ""
+            : reproductiveName.includes("病害")
+                ? "新增病害"
+                : reproductiveName.includes("虫害")
+                    ? "新增虫害"
+                    : reproductiveName.includes("过慢")
+                        ? "新增长势过慢"
+                        : reproductiveName.includes("过快")
+                            ? "新增长势过快"
+                            : "";
+    const lockStyleType = !isAbnormal || !hasTreatmentStatus
+        ? ""
+        : (isGrowthAbnormal ? "growthTreating" : "diseaseTreating");
+
+    const toRegionPayload = (geometryText) => buildRegionBasePayload(
+        geometryText,
+        regionStyleKind,
+        reproductiveName,
+        route.query.updatedTime,
+        routeHandleStatus,
+        lockStyleType,
+        abnormalLabel
+    );
+
+    try {
+        const parsed = JSON.parse(rangeWkt);
+        if (parsed && Array.isArray(parsed.geometryArr)) {
+            return parsed.geometryArr
+                .map((item) => toRegionPayload(String(item || "").trim()))
+                .filter(Boolean);
+        }
+    } catch (_) {
+        // 非 JSON 透传时,按单条 WKT 处理
     }
 
-    // 先绘制地块
-    const polygonData = route.query.polygonData;
-    const rawRangeWkt = route.query.rangeWkt;
-    const rangeWkt = rawRangeWkt ? decodeURIComponent(rawRangeWkt) : null;
+    if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
+        const region = toRegionPayload(rangeWkt.trim());
+        return region ? [region] : [];
+    }
+    return [];
+};
 
-    if (rangeWkt) {
-        let regions = [];
-        try {
-            const parsed = JSON.parse(rangeWkt);
-            if (parsed && Array.isArray(parsed.geometryArr)) {
-                regions = parsed.geometryArr.map((item) => ({
-                    geometry: item,
-                    status: "unresolved",
-                    reproductiveName: route.query.reproductiveName,
-                    updatedTime: route.query.updatedTime,
-                }));
-            } else if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
-                regions = [{ geometry: rangeWkt.trim(), status: "unresolved", reproductiveName: route.query.reproductiveName, updatedTime: route.query.updatedTime }];
+const renderRegionsFromRouteRangeWkt = (rangeWkt) => {
+    const regions = buildRegionsFromRouteRangeWkt(rangeWkt);
+    if (!regions.length) return;
+    drawRegionMap.setStatusRegions(regions);
+    fitAllRegionsWhenViewOnly();
+};
+
+const restoreEditablePolygonFromRoute = (polygonData) => {
+    if (viewOnly.value || !polygonData) return;
+    drawRegionMap.setAreaGeometry(
+        JSON.parse(polygonData)?.geometryArr,
+        false,
+        undefined,
+        undefined,
+        getAbnormalGrowthOverlayMeta()
+    );
+};
+
+const updateMapCenterForEditMode = (point) => {
+    // 查看模式通常会通过 fitAllRegions 自适应,不主动重置中心,避免打断用户缩放视角
+    if (viewOnly.value) return;
+    drawRegionMap.setMapPosition(convertPointToArray(point));
+};
+
+/**
+ * 页面从 keep-alive 恢复时,如果查看态和编辑态发生切换,需要重建地图实例。
+ * 仅在状态不一致时重建,避免每次激活都销毁重建带来的闪烁和性能开销。
+ */
+const syncMapEditableState = (point) => {
+    if (!drawRegionMap.kmap) return;
+    const shouldEditable = !viewOnly.value;
+    if (drawRegionMap.editable === shouldEditable) return;
+    drawRegionMap.destroyMap();
+    drawRegionMap.initMap(point, mapContainer.value, shouldEditable, true, shouldEditable);
+};
+
+async function fetchRegionInfo() {
+    const subjectId = route.query.subjectId || localStorage.getItem('selectedFarmId');
+    const { data } = await VE_API.basic_farm.fetchRegionInfo({ subjectId });
+    if (data && data.length > 0) {
+        regionInfo.value = data[0] || [];
+        regionTypeTabs.value = regionInfo.value.problemZoneList || [];
+        regionTypeTabs.value.unshift({ name: "品种区", code: "variety" });
+        varietyTabs.value = regionInfo.value.regionList || [];
+        activeVariety.value = 0;
+
+        if (route.query.firstAct) {
+            activeRegionType.value = route.query.firstAct;
+            if (route.query.firstAct === "variety") {
+                varietyTabs.value = regionInfo.value.regionList || [];
+                const index = Number(route.query.secondAct);
+                activeVariety.value = Number.isInteger(index) && index >= 0 ? index : 0;
+                return;
             }
-        } catch (_) {
-            if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
-                regions = [{ geometry: rangeWkt.trim(), status: "unresolved", reproductiveName: route.query.reproductiveName, updatedTime: route.query.updatedTime }];
+            const matchedGroup = (regionInfo.value.problemZoneList || []).find(
+                (item) => String(item?.code) === String(route.query.firstAct)
+            );
+            if (matchedGroup) {
+                varietyTabs.value = matchedGroup.children || [];
+                const index = Number(route.query.secondAct);
+                activeVariety.value = Number.isInteger(index) && index >= 0 ? index : 0;
             }
         }
-        if (regions.length) {
-            drawRegionMap.setStatusRegions(regions);
-            if (viewOnly.value && drawRegionMap.fitAllRegions) {
-                drawRegionMap.fitAllRegions();
-            }
+        if (activeVariety.value >= varietyTabs.value.length) {
+            activeVariety.value = 0;
         }
     }
-    if (!viewOnly.value && polygonData) {
-        drawRegionMap.setAreaGeometry(JSON.parse(polygonData)?.geometryArr);
+}
+
+const varietyTabs = ref([]);
+const activeVariety = ref(0);
+
+/** 样式用的大类:与接口 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";
+};
 
-    // 查看模式下已通过 fitAllRegions 适配;编辑模式再设置地图中心
-    if (!viewOnly.value) {
-        drawRegionMap.setMapPosition(convertPointToArray(point));
+const ABNORMAL_BADGE_BG_DISEASE_PEST = "#E32A28";
+const ABNORMAL_BADGE_BG_GROWTH = "#F76F00";
+
+/** 异常区小类(长势/病害/虫害等)闭合地块后在多边形内展示标签与发现日期 */
+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 createPolygonStyleFunc = (fillColor, strokeColor) => {
+    return (feature) => {
+        const styles = [];
+        const coord = feature.getGeometry().getCoordinates()[0];
+        for (let i = 0; i < coord[0].length - 1; i++) {
+            if (i % 2) {
+                styles.push(
+                    new Style({
+                        geometry: new Point(coord[0][i]),
+                        image: new Circle({
+                            radius: 4,
+                            fill: new Fill({
+                                color: strokeColor,
+                            }),
+                            stroke: new Stroke({
+                                color: "#fff",
+                                width: 1,
+                            }),
+                        }),
+                    })
+                );
+            } else {
+                styles.push(
+                    new Style({
+                        geometry: new Point(coord[0][i]),
+                        image: new Circle({
+                            radius: 6,
+                            fill: new Fill({
+                                color: "#fff",
+                            }),
+                        }),
+                    })
+                );
+            }
+        }
+        const fillStyle = new Style({
+            fill: new Fill({
+                color: fillColor,
+            }),
+            stroke: new Stroke({
+                color: strokeColor,
+                width: 2,
+            }),
+        });
+        let geom = feature.getGeometry().clone();
+        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);
+        return styles;
+    };
+};
+
+const applyRegionStyles = () => {
+    const kmap = drawRegionMap.kmap;
+    if (!kmap) return;
+
+    let lineColor = "#2199F8";
+    let vertexColor = "#2199F8";
+    let fillColor = [0, 0, 0, 0.5];
+    let strokeColor = "#2199F8";
+
+    const styleKind = getCanonicalRegionTypeForStyles();
+    if (styleKind === "variety") {
+        lineColor = "#18AA8B";
+        vertexColor = "#18AA8B";
+        fillColor = [0, 57, 44, 0.5];
+        strokeColor = "#18AA8B";
+    } 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];
+        strokeColor = "#FDCF7F";
+    } else {
+        lineColor = "#A6A6A6";
+        vertexColor = "#A6A6A6";
+        fillColor = [166, 166, 166, 0.25];
+        strokeColor = "#A6A6A6";
+    }
+
+    KMapMap.drawStyleColors = {
+        line: lineColor,
+        vertex: vertexColor,
+        fill: fillColor,
+        stroke: strokeColor,
+    };
+
+    kmap.polygonStyle = createPolygonStyleFunc(fillColor, strokeColor);
+    if (kmap.polygonLayer?.layer && typeof kmap.polygonLayer.layer.setStyle === "function") {
+        kmap.polygonLayer.layer.setStyle(kmap.polygonStyle);
+    }
+};
 
 onDeactivated(() => {
     drawRegionMap.clearLayer()
@@ -132,19 +572,97 @@ const deletePolygon = () => {
             type: 'warning',
         }
     ).then(() => {
-        drawRegionMap.deleteCurrentPolygon();
+        drawRegionMap.abortOngoingDrawSketch();
+        if (drawRegionMap.kmap && drawRegionMap.kmap.polygonLayer && drawRegionMap.kmap.polygonLayer.source) {
+            drawRegionMap.kmap.polygonLayer.source.clear();
+        }
         ElMessage.success("地块已删除");
-        sessionStorage.removeItem("drawRegionPolygonData");
     }).catch(() => {
         // 用户取消删除,不做任何操作
     });
 };
 
-const confirm = () => {
+const saveAndBack = () => {
     const polygonData = drawRegionMap.getAreaGeometry();
+    const subjectId = route.query.subjectId || localStorage.getItem("selectedFarmId") || "";
+    const geometryArr = Array.isArray(polygonData?.geometryArr)
+        ? polygonData.geometryArr
+            .map((g) => (g == null ? "" : String(g).trim()))
+            .filter((g) => g.length > 10)
+        : [];
+    const geom = geometryArr[0] || "";
+
+    const currentGroup = (regionTypeTabs.value || []).find(
+        (item) => String(item?.code) === String(activeRegionType.value)
+    );
+    const currentTab = (varietyTabs.value || [])[activeVariety.value] || {};
+
+    const regionList =
+        activeRegionType.value === "variety" && geom
+            ? [
+                {
+                    regionId: currentTab.regionId ?? currentTab.id ?? "",
+                    typeId: currentTab.typeId ?? "",
+                    regionName: currentTab.regionName ?? currentTab.problemZoneTypeName ?? "",
+                    geom,
+                },
+            ]
+            : [];
+
+    const problemZoneList =
+        activeRegionType.value !== "variety" && geom
+            ? [
+                {
+                    name: currentGroup?.name ?? "",
+                    code: currentGroup?.code ?? activeRegionType.value,
+                    children: [
+                        {
+                            farmSubjectId: subjectId,
+                            handleStatus: 1,
+                            createTime: new Date().toISOString(),
+                            problemZoneTypeId: currentTab.problemZoneTypeId ?? currentTab.typeId ?? "",
+                            problemZoneTypeName: currentTab.problemZoneTypeName ?? currentTab.regionName ?? "",
+                            parentName: currentGroup?.name ?? "",
+                            parentCode: currentGroup?.code ?? activeRegionType.value,
+                            geomItems: [{ geomWkt: geom, remark: "" }],
+                        },
+                    ],
+                },
+            ]
+            : [];
+
+    const params = {
+        subjectId,
+        regionList,
+        problemZoneList,
+    };
+
     sessionStorage.setItem("drawRegionPolygonData", JSON.stringify(polygonData));
+    sessionStorage.setItem("drawRegionSubmitParams", JSON.stringify(params));
+    if (selectedAbnormalType.value) {
+        sessionStorage.setItem("drawRegionAbnormalType", selectedAbnormalType.value);
+    } else {
+        sessionStorage.removeItem("drawRegionAbnormalType");
+    }
     router.back();
 };
+
+const handleConfirmUpload = () => {
+    if (!selectedAbnormalType.value) {
+        ElMessage.warning("请选择异常类型");
+        return;
+    }
+    showAbnormalTypePopup.value = false;
+    saveAndBack();
+};
+
+const confirm = () => {
+    if (getCanonicalRegionTypeForStyles() === "ABNORMAL") {
+        showAbnormalTypePopup.value = true;
+        return;
+    }
+    saveAndBack();
+};
 </script>
 
 <style lang="scss" scoped>
@@ -153,9 +671,59 @@ const confirm = () => {
     height: 100vh;
     overflow: hidden;
 
+    .region-type-tabs {
+        display: flex;
+        align-items: center;
+        background: #f4f4f4;
+        margin: 10px 10px 0;
+        padding: 3px;
+        border-radius: 4px;
+        box-sizing: border-box;
+
+        .region-type-tab {
+            flex: 1;
+            text-align: center;
+            padding: 5px 0;
+            color: #767676;
+        }
+
+        .region-type-tab--active {
+            background: #ffffff;
+            border-radius: 4px;
+            color: #0D0D0D;
+        }
+    }
+
+    .variety-tabs {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 10px 12px 0;
+        overflow-x: auto;
+        overflow-y: hidden;
+        flex-wrap: nowrap;
+        -webkit-overflow-scrolling: touch;
+
+        .variety-tab {
+            padding: 4px 12px;
+            border-radius: 2px;
+            color: #575757;
+            background: #F4F4F4;
+            border: 1px solid transparent;
+            white-space: nowrap;
+        }
+
+        .variety-tab--active {
+            background: rgba(33, 153, 248, 0.1);
+            color: #2199F8;
+            border: 1px solid #2199F8;
+        }
+    }
+
     .edit-map-content {
         width: 100%;
-        height: 100%;
+        height: calc(100% - 96px);
+        margin-top: 10px;
         position: relative;
 
         .edit-map-tip {
@@ -177,7 +745,7 @@ const confirm = () => {
 
         .edit-map-footer {
             position: absolute;
-            bottom: 80px;
+            bottom: 85px;
             left: 12px;
             width: calc(100% - 24px);
             display: flex;
@@ -188,7 +756,7 @@ const confirm = () => {
                 padding: 6px 7px 9px;
                 background: #fff;
                 border-radius: 8px;
-                margin-bottom: 12px;
+                margin-bottom: 30px;
 
                 .back-icon {
                     width: 20px;
@@ -228,4 +796,43 @@ const confirm = () => {
         }
     }
 }
+
+.abnormal-popup {
+    width: 100%;
+
+    ::v-deep {
+        .van-popup__close-icon {
+            color: #000;
+        }
+    }
+
+    .abnormal-popup-content {
+        background: linear-gradient(180deg, #d9ecff 0%, #ffffff 60%);
+        border-radius: 18px;
+        padding: 24px 16px;
+        box-sizing: border-box;
+        position: relative;
+
+        .abnormal-popup-title {
+            font-size: 16px;
+            margin-bottom: 12px;
+        }
+
+        .abnormal-type-select {
+            width: 100%;
+        }
+
+        .abnormal-popup-confirm {
+            margin-top: 24px;
+            border-radius: 25px;
+            padding: 8px;
+            background: #2199F8;
+            color: #fff;
+            font-size: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+    }
+}
 </style>

+ 231 - 0
src/views/old_mini/interactionList/drawRegion1.vue

@@ -0,0 +1,231 @@
+<template>
+    <div class="edit-map">
+        <custom-header :name="viewOnly ? '查看区域' : '勾画区域'"></custom-header>
+        <div class="edit-map-content">
+            <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
+            <div class="map-container" ref="mapContainer"></div>
+            <div class="edit-map-footer">
+                <div class="footer-back" @click="goBack">
+                    <img class="back-icon" src="@/assets/img/home/go-back.png" alt="" />
+                </div>
+                <div class="edit-map-footer-btn" v-if="!viewOnly">
+                    <div class="btn-delete" @click="deletePolygon">删除地块</div>
+                    <div class="btn-cancel" @click="goBack">取消</div>
+                    <div class="btn-confirm" @click="confirm">确认</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import customHeader from "@/components/customHeader.vue";
+import { ref, computed, onMounted, onActivated, onDeactivated } from "vue";
+import DrawRegionMap from "./map/drawRegionMap.js";
+import { useRouter, useRoute } from "vue-router";
+import { convertPointToArray } from "@/utils/index";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+const router = useRouter();
+const route = useRoute();
+const mapContainer = ref(null);
+const drawRegionMap = new DrawRegionMap();
+
+const type = ref(null);
+const viewOnly = computed(() => route.query.viewOnly === "1" || route.query.viewOnly === "true");
+
+onMounted(() => {
+    type.value = route.query.type;
+    const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
+    const editable = !viewOnly.value;
+    const showPoint = !viewOnly.value;
+    drawRegionMap.initMap(point, mapContainer.value, editable, true, showPoint);
+
+    // 地图初始化之后(比如 initPreviewMap 里)
+    // const regions = [
+    //     {
+    //         geometry:
+    //             "MULTIPOLYGON(((113.61674040430906 23.586573370597367,113.61586610436014 23.585922976493354,113.61710291900188 23.58486741952544,113.61770000158238 23.585651090473736,113.61674040430906 23.586573370597367)))",
+    //         status: "unresolved", // 未解决(蓝色)
+    //     },
+    //     {
+    //         geometry:
+    //             "MULTIPOLYGON(((113.61516640298626 23.588441931082958,113.61445736699218 23.58799411906573,113.61572616841707 23.586954554834552,113.61642987338976 23.588180707433526,113.61516640298626 23.588441931082958)))",
+    //         status: "resolved", // 已解决(灰色)
+    //     },
+    // ];
+
+    // drawRegionMap.setStatusRegions(regions);
+});
+
+onActivated(() => {
+    const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
+
+    // 从编辑态进入仅查看时,需重新初始化为不可编辑
+    if (viewOnly.value && drawRegionMap.kmap && drawRegionMap.editable) {
+        drawRegionMap.destroyMap();
+        drawRegionMap.initMap(point, mapContainer.value, false, true, false);
+    }
+
+    // 从仅查看进入勾画(编辑)时,需重新初始化为可编辑
+    if (!viewOnly.value && drawRegionMap.kmap && !drawRegionMap.editable) {
+        drawRegionMap.destroyMap();
+        drawRegionMap.initMap(point, mapContainer.value, true, true, true);
+    }
+
+    // 先绘制地块
+    const polygonData = route.query.polygonData;
+    const rawRangeWkt = route.query.rangeWkt;
+    const rangeWkt = rawRangeWkt ? decodeURIComponent(rawRangeWkt) : null;
+
+    if (rangeWkt) {
+        let regions = [];
+        try {
+            const parsed = JSON.parse(rangeWkt);
+            if (parsed && Array.isArray(parsed.geometryArr)) {
+                regions = parsed.geometryArr.map((item) => ({
+                    geometry: item,
+                    status: "unresolved",
+                    reproductiveName: route.query.reproductiveName,
+                    updatedTime: route.query.updatedTime,
+                }));
+            } else if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
+                regions = [{ geometry: rangeWkt.trim(), status: "unresolved", reproductiveName: route.query.reproductiveName, updatedTime: route.query.updatedTime }];
+            }
+        } catch (_) {
+            if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
+                regions = [{ geometry: rangeWkt.trim(), status: "unresolved", reproductiveName: route.query.reproductiveName, updatedTime: route.query.updatedTime }];
+            }
+        }
+        if (regions.length) {
+            drawRegionMap.setStatusRegions(regions);
+            if (viewOnly.value && drawRegionMap.fitAllRegions) {
+                drawRegionMap.fitAllRegions();
+            }
+        }
+    }
+    if (!viewOnly.value && polygonData) {
+        drawRegionMap.setAreaGeometry(JSON.parse(polygonData)?.geometryArr);
+    }
+
+    // 查看模式下已通过 fitAllRegions 适配;编辑模式再设置地图中心
+    if (!viewOnly.value) {
+        drawRegionMap.setMapPosition(convertPointToArray(point));
+    }
+});
+
+onDeactivated(() => {
+    drawRegionMap.clearLayer()
+})
+const goBack = () => {
+    // drawRegionMap.clearLayer()
+    router.back()
+};
+
+const deletePolygon = () => {
+    ElMessageBox.confirm(
+        '确认要删除当前地块吗?删除后可以重新勾画。',
+        '删除确认',
+        {
+            confirmButtonText: '确认删除',
+            cancelButtonText: '取消',
+            type: 'warning',
+        }
+    ).then(() => {
+        drawRegionMap.deleteCurrentPolygon();
+        ElMessage.success("地块已删除");
+        sessionStorage.removeItem("drawRegionPolygonData");
+    }).catch(() => {
+        // 用户取消删除,不做任何操作
+    });
+};
+
+const confirm = () => {
+    const polygonData = drawRegionMap.getAreaGeometry();
+    sessionStorage.setItem("drawRegionPolygonData", JSON.stringify(polygonData));
+    router.back();
+};
+</script>
+
+<style lang="scss" scoped>
+.edit-map {
+    width: 100%;
+    height: 100vh;
+    overflow: hidden;
+
+    .edit-map-content {
+        width: 100%;
+        height: 100%;
+        position: relative;
+
+        .edit-map-tip {
+            position: absolute;
+            top: 23px;
+            left: calc(50% - 256px / 2);
+            z-index: 1;
+            font-size: 12px;
+            color: #fff;
+            padding: 9px 20px;
+            background: rgba(0, 0, 0, 0.5);
+            border-radius: 20px;
+        }
+
+        .map-container {
+            width: 100%;
+            height: 100%;
+        }
+
+        .edit-map-footer {
+            position: absolute;
+            bottom: 80px;
+            left: 12px;
+            width: calc(100% - 24px);
+            display: flex;
+            flex-direction: column;
+            align-items: flex-end;
+
+            .footer-back {
+                padding: 6px 7px 9px;
+                background: #fff;
+                border-radius: 8px;
+                margin-bottom: 12px;
+
+                .back-icon {
+                    width: 20px;
+                    height: 18px;
+                }
+            }
+
+            .edit-map-footer-btn {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                width: 100%;
+                gap: 8px;
+
+                div {
+                    flex: 1;
+                    max-width: 100px;
+                    text-align: center;
+                    color: #666666;
+                    font-size: 14px;
+                    padding: 8px 0;
+                    border-radius: 25px;
+                    background: #fff;
+                }
+
+                .btn-delete {
+                    background: #ff4d4f;
+                    color: #fff;
+                }
+
+                .btn-confirm {
+                    background: #000;
+                    background-image: linear-gradient(180deg, #76c3ff 0%, #2199f8 100%);
+                    color: #fff;
+                }
+            }
+        }
+    }
+}
+</style>

+ 0 - 668
src/views/old_mini/interactionList/drawRegion2.vue

@@ -1,668 +0,0 @@
-<template>
-    <div class="edit-map">
-        <custom-header :name="viewOnly ? '查看区域' : '勾画区域'"></custom-header>
-        <div class="region-type-tabs">
-            <div v-for="item in regionTypeTabs" :key="item.code" class="region-type-tab"
-                :class="{ 'region-type-tab--active': activeRegionType === item.code }">
-                {{ item.name }}
-            </div>
-        </div>
-        <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 }">
-                {{ v.regionName || v.problemZoneTypeName }}
-            </div>
-        </div>
-        <div class="edit-map-content">
-            <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
-            <div class="map-container" ref="mapContainer"></div>
-            <div class="edit-map-footer" :style="{ 'bottom': activeRegionType !== 'DORMANCY' ? '85px' : '59px' }">
-                <div class="footer-back" @click="goBack">
-                    <img class="back-icon" src="@/assets/img/home/go-back.png" alt="" />
-                </div>
-                <div class="edit-map-footer-btn" v-if="!viewOnly">
-                    <div class="btn-delete" @click="deletePolygon">删除地块</div>
-                    <!-- <div class="btn-cancel" @click="goBack">取消</div> -->
-                    <div class="btn-confirm" @click="confirm">确认区域</div>
-                </div>
-            </div>
-        </div>
-
-        <popup v-model:show="showAbnormalTypePopup" round closeable class="abnormal-popup"
-            :close-on-click-overlay="true">
-            <div class="abnormal-popup-content">
-                <div class="abnormal-popup-title">请选择异常类型</div>
-                <el-select v-model="selectedAbnormalType" class="abnormal-type-select" placeholder="请选择类型" size="large">
-                    <el-option v-for="item in abnormalTypeOptions" :key="item.value" :label="item.name"
-                        :value="item.value" />
-                </el-select>
-                <div class="abnormal-popup-confirm" @click="handleConfirmUpload">确认上传</div>
-            </div>
-        </popup>
-    </div>
-</template>
-
-<script setup>
-import customHeader from "@/components/customHeader.vue";
-import { ref, computed, onMounted, onActivated, onDeactivated } from "vue";
-import { Popup } from "vant";
-import DrawRegionMap from "./map/drawRegionMap.js";
-import { Map as KMapMap } from "@/utils/ol-map/KMap";
-import { useRouter, useRoute } from "vue-router";
-import { convertPointToArray } from "@/utils/index";
-import { ElMessage, ElMessageBox } from "element-plus";
-import Style from "ol/style/Style";
-import { Fill, Stroke, Circle, Text } from "ol/style.js";
-import { Point } from "ol/geom";
-import * as proj from "ol/proj";
-import { getArea } from "ol/sphere.js";
-
-const router = useRouter();
-const route = useRoute();
-const mapContainer = ref(null);
-const drawRegionMap = new DrawRegionMap();
-
-const type = ref(null);
-const viewOnly = computed(() => route.query.viewOnly === "1" || route.query.viewOnly === "true");
-const showAbnormalTypePopup = ref(false);
-const selectedAbnormalType = ref("");
-const abnormalTypeOptions = [{
-    name: "病害",
-    value: "DISEASE"
-}, {
-    name: "虫害",
-    value: "PEST"
-}, {
-    name: "不确定",
-    value: "UNCERTAIN"
-}];
-
-onMounted(() => {
-    type.value = route.query.type;
-    const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
-    const editable = !viewOnly.value;
-    const showPoint = !viewOnly.value;
-    drawRegionMap.initMap(point, mapContainer.value, editable, true, showPoint);
-    applyRegionStyles();
-
-    // 地图初始化之后(比如 initPreviewMap 里)
-    // const regions = [
-    //     {
-    //         geometry:
-    //             "MULTIPOLYGON(((113.61674040430906 23.586573370597367,113.61586610436014 23.585922976493354,113.61710291900188 23.58486741952544,113.61770000158238 23.585651090473736,113.61674040430906 23.586573370597367)))",
-    //         status: "unresolved", // 未解决(蓝色)
-    //     },
-    //     {
-    //         geometry:
-    //             "MULTIPOLYGON(((113.61516640298626 23.588441931082958,113.61445736699218 23.58799411906573,113.61572616841707 23.586954554834552,113.61642987338976 23.588180707433526,113.61516640298626 23.588441931082958)))",
-    //         status: "resolved", // 已解决(灰色)
-    //     },
-    // ];
-
-    // drawRegionMap.setStatusRegions(regions);
-});
-
-onActivated(async () => {
-    const point = route.query.mapCenter || "POINT (113.6142086995688 23.585836479509055)";
-    await fetchRegionInfo();
-
-    // 从编辑态进入仅查看时,需重新初始化为不可编辑
-    if (viewOnly.value && drawRegionMap.kmap && drawRegionMap.editable) {
-        drawRegionMap.destroyMap();
-        drawRegionMap.initMap(point, mapContainer.value, false, true, false);
-    }
-
-    // 从仅查看进入勾画(编辑)时,需重新初始化为可编辑
-    if (!viewOnly.value && drawRegionMap.kmap && !drawRegionMap.editable) {
-        drawRegionMap.destroyMap();
-        drawRegionMap.initMap(point, mapContainer.value, true, true, true);
-    }
-
-    applyRegionStyles();
-
-    // 先绘制地块
-    const polygonData = route.query.polygonData;
-    const rawRangeWkt = route.query.rangeWkt;
-    const rangeWkt = rawRangeWkt ? decodeURIComponent(rawRangeWkt) : null;
-
-    if (rangeWkt) {
-        let regions = [];
-        try {
-            const parsed = JSON.parse(rangeWkt);
-            if (parsed && Array.isArray(parsed.geometryArr)) {
-                regions = parsed.geometryArr.map((item) => ({
-                    geometry: item,
-                    status: "unresolved",
-                    reproductiveName: route.query.reproductiveName,
-                    updatedTime: route.query.updatedTime,
-                }));
-            } else if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
-                regions = [{ geometry: rangeWkt.trim(), status: "unresolved", reproductiveName: route.query.reproductiveName, updatedTime: route.query.updatedTime }];
-            }
-        } catch (_) {
-            if (typeof rangeWkt === "string" && rangeWkt.trim().length > 10) {
-                regions = [{ geometry: rangeWkt.trim(), status: "unresolved", reproductiveName: route.query.reproductiveName, updatedTime: route.query.updatedTime }];
-            }
-        }
-        if (regions.length) {
-            drawRegionMap.setStatusRegions(regions);
-            if (viewOnly.value && drawRegionMap.fitAllRegions) {
-                drawRegionMap.fitAllRegions();
-            }
-        }
-    }
-    if (!viewOnly.value && polygonData) {
-        drawRegionMap.setAreaGeometry(
-            JSON.parse(polygonData)?.geometryArr,
-            false,
-            undefined,
-            undefined,
-            getAbnormalGrowthOverlayMeta()
-        );
-    }
-
-    // 查看模式下已通过 fitAllRegions 适配;编辑模式再设置地图中心
-    if (!viewOnly.value) {
-        drawRegionMap.setMapPosition(convertPointToArray(point));
-    }
-
-    applyRegionStyles();
-});
-
-const regionTypeTabs = ref([]);
-const activeRegionType = ref("variety");
-const regionInfo = ref([]);
-async function fetchRegionInfo() {
-    const subjectId = route.query.subjectId || localStorage.getItem('selectedFarmId');
-    const { data } = await VE_API.basic_farm.fetchRegionInfo({ subjectId });
-    if (data && data.length > 0) {
-        regionInfo.value = data[0] || [];
-        regionTypeTabs.value = regionInfo.value.problemZoneList || [];
-        regionTypeTabs.value.unshift({ name: "品种区", code: "variety" });
-        varietyTabs.value = regionInfo.value.regionList || [];
-        activeVariety.value = 0;
-
-        if (route.query.firstAct) {
-            activeRegionType.value = route.query.firstAct;
-            if (route.query.firstAct === "variety") {
-                varietyTabs.value = regionInfo.value.regionList || [];
-                activeVariety.value = 0;
-                return;
-            }
-            const matchedGroup = (regionInfo.value.problemZoneList || []).find(
-                (item) => String(item?.code) === String(route.query.firstAct)
-            );
-            if (matchedGroup) {
-                varietyTabs.value = matchedGroup.children || [];
-                activeVariety.value = 0;
-            }
-        }
-    }
-}
-
-const varietyTabs = ref([]);
-const activeVariety = ref(0);
-
-/** 样式用的大类:与接口 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";
-
-/** 异常区小类(长势/病害/虫害等)闭合地块后在多边形内展示标签与发现日期 */
-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 createPolygonStyleFunc = (fillColor, strokeColor) => {
-    return (feature) => {
-        const styles = [];
-        const coord = feature.getGeometry().getCoordinates()[0];
-        for (let i = 0; i < coord[0].length - 1; i++) {
-            if (i % 2) {
-                styles.push(
-                    new Style({
-                        geometry: new Point(coord[0][i]),
-                        image: new Circle({
-                            radius: 4,
-                            fill: new Fill({
-                                color: strokeColor,
-                            }),
-                            stroke: new Stroke({
-                                color: "#fff",
-                                width: 1,
-                            }),
-                        }),
-                    })
-                );
-            } else {
-                styles.push(
-                    new Style({
-                        geometry: new Point(coord[0][i]),
-                        image: new Circle({
-                            radius: 6,
-                            fill: new Fill({
-                                color: "#fff",
-                            }),
-                        }),
-                    })
-                );
-            }
-        }
-        const fillStyle = new Style({
-            fill: new Fill({
-                color: fillColor,
-            }),
-            stroke: new Stroke({
-                color: strokeColor,
-                width: 2,
-            }),
-        });
-        let geom = feature.getGeometry().clone();
-        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);
-        return styles;
-    };
-};
-
-const applyRegionStyles = () => {
-    const kmap = drawRegionMap.kmap;
-    if (!kmap) return;
-
-    let lineColor = "#2199F8";
-    let vertexColor = "#2199F8";
-    let fillColor = [0, 0, 0, 0.5];
-    let strokeColor = "#2199F8";
-
-    const styleKind = getCanonicalRegionTypeForStyles();
-    if (styleKind === "variety") {
-        lineColor = "#18AA8B";
-        vertexColor = "#18AA8B";
-        fillColor = [0, 57, 44, 0.5];
-        strokeColor = "#18AA8B";
-    } 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];
-        strokeColor = "#FDCF7F";
-    } else {
-        lineColor = "#A6A6A6";
-        vertexColor = "#A6A6A6";
-        fillColor = [166, 166, 166, 0.25];
-        strokeColor = "#A6A6A6";
-    }
-
-    KMapMap.drawStyleColors = {
-        line: lineColor,
-        vertex: vertexColor,
-        fill: fillColor,
-        stroke: strokeColor,
-    };
-
-    kmap.polygonStyle = createPolygonStyleFunc(fillColor, strokeColor);
-    if (kmap.polygonLayer?.layer && typeof kmap.polygonLayer.layer.setStyle === "function") {
-        kmap.polygonLayer.layer.setStyle(kmap.polygonStyle);
-    }
-};
-
-onDeactivated(() => {
-    drawRegionMap.clearLayer()
-})
-const goBack = () => {
-    // drawRegionMap.clearLayer()
-    router.back()
-};
-
-const deletePolygon = () => {
-    ElMessageBox.confirm(
-        '确认要删除当前地块吗?删除后可以重新勾画。',
-        '删除确认',
-        {
-            confirmButtonText: '确认删除',
-            cancelButtonText: '取消',
-            type: 'warning',
-        }
-    ).then(() => {
-        drawRegionMap.abortOngoingDrawSketch();
-        if (drawRegionMap.kmap && drawRegionMap.kmap.polygonLayer && drawRegionMap.kmap.polygonLayer.source) {
-            drawRegionMap.kmap.polygonLayer.source.clear();
-        }
-        ElMessage.success("地块已删除");
-    }).catch(() => {
-        // 用户取消删除,不做任何操作
-    });
-};
-
-const saveAndBack = () => {
-    const polygonData = drawRegionMap.getAreaGeometry();
-    const subjectId = route.query.subjectId || localStorage.getItem("selectedFarmId") || "";
-    const geometryArr = Array.isArray(polygonData?.geometryArr)
-        ? polygonData.geometryArr
-            .map((g) => (g == null ? "" : String(g).trim()))
-            .filter((g) => g.length > 10)
-        : [];
-    const geom = geometryArr[0] || "";
-
-    const currentGroup = (regionTypeTabs.value || []).find(
-        (item) => String(item?.code) === String(activeRegionType.value)
-    );
-    const currentTab = (varietyTabs.value || [])[activeVariety.value] || {};
-
-    const regionList =
-        activeRegionType.value === "variety" && geom
-            ? [
-                {
-                    regionId: currentTab.regionId ?? currentTab.id ?? "",
-                    typeId: currentTab.typeId ?? "",
-                    regionName: currentTab.regionName ?? currentTab.problemZoneTypeName ?? "",
-                    geom,
-                },
-            ]
-            : [];
-
-    const problemZoneList =
-        activeRegionType.value !== "variety" && geom
-            ? [
-                {
-                    name: currentGroup?.name ?? "",
-                    code: currentGroup?.code ?? activeRegionType.value,
-                    children: [
-                        {
-                            farmSubjectId: subjectId,
-                            handleStatus: 1,
-                            createTime: new Date().toISOString(),
-                            problemZoneTypeId: currentTab.problemZoneTypeId ?? currentTab.typeId ?? "",
-                            problemZoneTypeName: currentTab.problemZoneTypeName ?? currentTab.regionName ?? "",
-                            parentName: currentGroup?.name ?? "",
-                            parentCode: currentGroup?.code ?? activeRegionType.value,
-                            geomItems: [{ geomWkt: geom, remark: "" }],
-                        },
-                    ],
-                },
-            ]
-            : [];
-
-    const params = {
-        subjectId,
-        regionList,
-        problemZoneList,
-    };
-
-    sessionStorage.setItem("drawRegionPolygonData", JSON.stringify(polygonData));
-    console.log(params);
-    sessionStorage.setItem("drawRegionSubmitParams", JSON.stringify(params));
-    if (selectedAbnormalType.value) {
-        sessionStorage.setItem("drawRegionAbnormalType", selectedAbnormalType.value);
-    } else {
-        sessionStorage.removeItem("drawRegionAbnormalType");
-    }
-    router.back();
-};
-
-const handleConfirmUpload = () => {
-    if (!selectedAbnormalType.value) {
-        ElMessage.warning("请选择异常类型");
-        return;
-    }
-    showAbnormalTypePopup.value = false;
-    saveAndBack();
-};
-
-const confirm = () => {
-    if (getCanonicalRegionTypeForStyles() === "ABNORMAL") {
-        showAbnormalTypePopup.value = true;
-        return;
-    }
-    saveAndBack();
-};
-</script>
-
-<style lang="scss" scoped>
-.edit-map {
-    width: 100%;
-    height: 100vh;
-    overflow: hidden;
-
-    .region-type-tabs {
-        display: flex;
-        align-items: center;
-        background: #f4f4f4;
-        margin: 10px 10px 0;
-        padding: 3px;
-        border-radius: 4px;
-        box-sizing: border-box;
-
-        .region-type-tab {
-            flex: 1;
-            text-align: center;
-            padding: 5px 0;
-            color: #767676;
-        }
-
-        .region-type-tab--active {
-            background: #ffffff;
-            border-radius: 4px;
-            color: #0D0D0D;
-        }
-    }
-
-    .variety-tabs {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-        padding: 10px 12px 0;
-        overflow-x: auto;
-        overflow-y: hidden;
-        flex-wrap: nowrap;
-        -webkit-overflow-scrolling: touch;
-
-        .variety-tab {
-            padding: 4px 12px;
-            border-radius: 2px;
-            color: #575757;
-            background: #F4F4F4;
-            border: 1px solid transparent;
-            white-space: nowrap;
-        }
-
-        .variety-tab--active {
-            background: rgba(33, 153, 248, 0.1);
-            color: #2199F8;
-            border: 1px solid #2199F8;
-        }
-    }
-
-    .edit-map-content {
-        width: 100%;
-        height: calc(100% - 96px);
-        margin-top: 10px;
-        position: relative;
-
-        .edit-map-tip {
-            position: absolute;
-            top: 23px;
-            left: calc(50% - 256px / 2);
-            z-index: 1;
-            font-size: 12px;
-            color: #fff;
-            padding: 9px 20px;
-            background: rgba(0, 0, 0, 0.5);
-            border-radius: 20px;
-        }
-
-        .map-container {
-            width: 100%;
-            height: 100%;
-        }
-
-        .edit-map-footer {
-            position: absolute;
-            bottom: 85px;
-            left: 12px;
-            width: calc(100% - 24px);
-            display: flex;
-            flex-direction: column;
-            align-items: flex-end;
-
-            .footer-back {
-                padding: 6px 7px 9px;
-                background: #fff;
-                border-radius: 8px;
-                margin-bottom: 30px;
-
-                .back-icon {
-                    width: 20px;
-                    height: 18px;
-                }
-            }
-
-            .edit-map-footer-btn {
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                width: 100%;
-                gap: 8px;
-
-                div {
-                    flex: 1;
-                    max-width: 100px;
-                    text-align: center;
-                    color: #666666;
-                    font-size: 14px;
-                    padding: 8px 0;
-                    border-radius: 25px;
-                    background: #fff;
-                }
-
-                .btn-delete {
-                    background: #ff4d4f;
-                    color: #fff;
-                }
-
-                .btn-confirm {
-                    background: #000;
-                    background-image: linear-gradient(180deg, #76c3ff 0%, #2199f8 100%);
-                    color: #fff;
-                }
-            }
-        }
-    }
-}
-
-.abnormal-popup {
-    width: 100%;
-
-    ::v-deep {
-        .van-popup__close-icon {
-            color: #000;
-        }
-    }
-
-    .abnormal-popup-content {
-        background: linear-gradient(180deg, #d9ecff 0%, #ffffff 60%);
-        border-radius: 18px;
-        padding: 24px 16px;
-        box-sizing: border-box;
-        position: relative;
-
-        .abnormal-popup-title {
-            font-size: 16px;
-            margin-bottom: 12px;
-        }
-
-        .abnormal-type-select {
-            width: 100%;
-        }
-
-        .abnormal-popup-confirm {
-            margin-top: 24px;
-            border-radius: 25px;
-            padding: 8px;
-            background: #2199F8;
-            color: #fff;
-            font-size: 16px;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-        }
-    }
-}
-</style>

+ 2 - 2
src/views/old_mini/interactionList/index.vue

@@ -778,7 +778,7 @@ const toggleExpand = (item) => {
 };
 
 const handleViewRegion = (item) => {
-    router.push(`/draw_region?viewOnly=1`);
+    router.push(`/draw_region?viewOnly=1&firstAct=ABNORMAL&secondAct=0&showData=true`);
 }
 
 const showClose = ref(false);
@@ -816,7 +816,7 @@ const handleDrawRegion = (item) => {
     }
 
     if (item.rangeWkt && item.rangeWkt.length > 10) {
-        router.push(`/draw_region?polygonData=${polygonData}&rangeWkt=${item.rangeWkt}&updatedTime=${item.updatedTime.slice(0, 10)}&reproductiveName=${item.reproductiveName}&mapCenter=${localStorage.getItem('selectedFarmPoint') || 'POINT (113.6142086995688 23.585836479509055)'}&firstAct=ABNORMAL&secondAct=0`);
+        router.push(`/draw_region?polygonData=${polygonData}&rangeWkt=${item.rangeWkt}&updatedTime=${item.updatedTime.slice(0, 10)}&mapCenter=${localStorage.getItem('selectedFarmPoint') || 'POINT (113.6142086995688 23.585836479509055)'}&firstAct=ABNORMAL&secondAct=0`);
     } else {
         if (polygonData) {
             router.push(`/draw_region?polygonData=${polygonData}&mapCenter=${localStorage.getItem('selectedFarmPoint') || 'POINT (113.6142086995688 23.585836479509055)'}&firstAct=ABNORMAL&secondAct=0`);

+ 107 - 16
src/views/old_mini/interactionList/map/drawRegionMap.js

@@ -66,6 +66,7 @@ class DrawRegionMap {
                     const isGrowthTreating = lockStyleType === "growthTreating";
                     const label = f.get("label") || (isControlled ? "病害已控制" : "病害治疗中");
                     const updatedTime = f.get("updatedTime") || "";
+                    const isNewAbnormal = String(label).startsWith("新增");
                     let areaText = "";
                     try {
                         let geom = f.getGeometry().clone();
@@ -86,9 +87,19 @@ class DrawRegionMap {
                         : isGrowthTreating
                             ? "rgba(255, 94, 0, 0.3)"
                             : "rgba(224, 49, 49, 0.3)";
-                    const badgeTextColor = isControlled ? "#ffffff" : isGrowthTreating ? "#F76F00" : "#E32A28";
-                    const badgeBgColor = isControlled ? "#A6A6A6" : "#ffffff";
-                    const badgeBorderColor = isControlled ? "" : isGrowthTreating ? "#F76F00" : "#E32A28";
+                    const badgeTextColor = isControlled
+                        ? "#ffffff"
+                        : isNewAbnormal
+                            ? "#ffffff"
+                            : isGrowthTreating
+                                ? "#F76F00"
+                                : "#E32A28";
+                    const badgeBgColor = isControlled
+                        ? "#A6A6A6"
+                        : isNewAbnormal
+                            ? (isGrowthTreating ? "#F76F00" : "#E32A28")
+                            : "#ffffff";
+                    const badgeBorderColor = isControlled || isNewAbnormal ? "" : isGrowthTreating ? "#F76F00" : "#E32A28";
                     const styles = [
                         new Style({
                             fill: new Fill({ color: fillColor }),
@@ -112,7 +123,13 @@ class DrawRegionMap {
                                 text: new Text({
                                     text: `发现时间:${updatedTime}`,
                                     font: "12px sans-serif",
-                                    fill: new Fill({ color: "#ffffff" }),
+                                    fill: new Fill({ color: isNewAbnormal ? "#333333" : "#ffffff" }),
+                                    ...(isNewAbnormal
+                                        ? {
+                                              backgroundFill: new Fill({ color: "#ffffff" }),
+                                              padding: [8, 12, 8, 12],
+                                          }
+                                        : {}),
                                     offsetY: -16,
                                 }),
                             })
@@ -137,22 +154,77 @@ class DrawRegionMap {
                 const reproductiveName = f.get("reproductiveName") || "";
                 const isResolved = status === "resolved";
                 const unresolvedBlueFill = f.get("unresolvedBlueFill") === true;
+                const regionStyleKind = String(f.get("regionStyleKind") || "");
+                const isAbnormalGrowth =
+                    regionStyleKind === "ABNORMAL" &&
+                    (reproductiveName.includes("长势") ||
+                        reproductiveName.includes("过慢") ||
+                        reproductiveName.includes("过快"));
+                let unresolvedFillColor = unresolvedBlueFill
+                    ? "rgba(33, 153, 248, 0.35)"
+                    : "rgba(0, 0, 0, 0.5)";
+                let unresolvedStrokeColor = "#2199F8";
+                let unresolvedBadgeColor = "#2199F8";
+
+                if (regionStyleKind === "variety") {
+                    unresolvedFillColor = "rgba(0, 57, 44, 0.5)";
+                    unresolvedStrokeColor = "#18AA8B";
+                    unresolvedBadgeColor = "#18AA8B";
+                } else if (regionStyleKind === "ABNORMAL") {
+                    unresolvedFillColor = isAbnormalGrowth
+                        ? "rgba(124, 46, 0, 0.5)"
+                        : "rgba(100, 0, 0, 0.5)";
+                    unresolvedStrokeColor = isAbnormalGrowth ? "#FF7300" : "#E03131";
+                    unresolvedBadgeColor = isAbnormalGrowth ? "#F76F00" : "#E03131";
+                } else if (regionStyleKind === "ENVIRONMENT") {
+                    unresolvedFillColor = "rgba(151, 96, 0, 0.5)";
+                    unresolvedStrokeColor = "#FDCF7F";
+                    unresolvedBadgeColor = "#FDCF7F";
+                }
                 // 已解决:深灰填充,浅白描边;未解决:默认深灰半透明 + 蓝描边;unresolvedBlueFill 时浅蓝填充(互动列表等)
                 const fillColor = isResolved
                     ? "rgba(0, 0, 0, 0.6)"
-                    : unresolvedBlueFill
-                      ? "rgba(33, 153, 248, 0.35)"
-                      : "rgba(0, 0, 0, 0.5)";
-                const strokeColor = isResolved ? "#7C7C7C" : "#2199F8";
+                    : unresolvedFillColor;
+                const strokeColor = isResolved ? "#7C7C7C" : unresolvedStrokeColor;
+                let abnormalBadgeText = "";
+                if (regionStyleKind === "ABNORMAL") {
+                    if (reproductiveName.includes("病害")) {
+                        abnormalBadgeText = "新增病害";
+                    } else if (reproductiveName.includes("虫害")) {
+                        abnormalBadgeText = "新增虫害";
+                    } else if (reproductiveName.includes("过慢")) {
+                        abnormalBadgeText = "新增长势过慢";
+                    } else if (reproductiveName.includes("过快")) {
+                        abnormalBadgeText = "新增长势过快";
+                    }
+                }
+                const statusText = status === "resolved" ? "已解决" : "未解决";
+                const mainLabelText =
+                    regionStyleKind === "ABNORMAL" && abnormalBadgeText
+                        ? abnormalBadgeText
+                        : reproductiveName
+                            ? `${reproductiveName} ${statusText}`
+                            : statusText;
+                let areaText = "";
+                try {
+                    let geom = f.getGeometry().clone();
+                    geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"));
+                    let area = getArea(geom);
+                    area = (area + area / 2) / 1000;
+                    areaText = `${Number(area).toFixed(2)}亩`;
+                } catch (_) {
+                    areaText = "";
+                }
 
                 const text = new Text({
-                    text: reproductiveName ? `${reproductiveName} ${status === "resolved" ? "已解决" : "未解决"}` : status === "resolved" ? "已解决" : "未解决",
-                    font: "12px sans-serif",
+                    text: mainLabelText,
+                    font: regionStyleKind === "ABNORMAL" ? "bold 13px sans-serif" : "12px sans-serif",
                     fill: new Fill({ color: "#ffffff" }),
                     backgroundFill: new Fill({ 
-                        color: isResolved ? "#949494" : "#2199F8" 
+                        color: isResolved ? "#949494" : unresolvedBadgeColor 
                     }),
                     padding: [1, 5, 1, 5],
+                    offsetY: -40,
                 });
                 const style =  new Style({
                         fill: new Fill({ color: fillColor }),
@@ -164,16 +236,34 @@ class DrawRegionMap {
                     text: new Text({
                         text: `发现时间:${f.get("updatedTime")}`,
                         font: "12px sans-serif",
-                        offsetY: -24,
+                        offsetY: -16,
                         fill: new Fill({ color: "#ffffff" }),
-                        backgroundFill: new Fill({ 
-                            color: isResolved ? "rgba(171, 171, 171, 0.4)" : "rgba(33, 153, 248, 0.6)" 
-                        }),
+                        ...(regionStyleKind === "ABNORMAL"
+                            ? {}
+                            : {
+                                  backgroundFill: new Fill({
+                                      color: isResolved ? "rgba(171, 171, 171, 0.4)" : `${unresolvedBadgeColor}99`,
+                                  }),
+                              }),
                         padding: [1, 5, 1, 5],
                     }),
                 });
 
-                return [style, text2];
+                const styles = [style, text2];
+                if (areaText) {
+                    styles.push(
+                        new Style({
+                            text: new Text({
+                                text: areaText,
+                                font: "16px sans-serif",
+                                fill: new Fill({ color: "#ffffff" }),
+                                offsetY: 14,
+                            }),
+                        })
+                    );
+                }
+
+                return styles;
             },
         });
     }
@@ -521,6 +611,7 @@ class DrawRegionMap {
                 feature.set("handleStatus", region.handleStatus);
                 feature.set("lockStyleType", region.lockStyleType || "");
                 feature.set("unresolvedBlueFill", region.unresolvedBlueFill === true);
+                feature.set("regionStyleKind", region.regionStyleKind || "");
                 this.staticRegionLayer.addFeature(feature);
             } catch (e) {
                 // 单个区域解析失败时忽略

+ 90 - 24
src/views/old_mini/monitor/subPages/agriculturalDetail.vue

@@ -2,29 +2,46 @@
     <div class="agricultural-detail">
         <custom-header name="农事详情"></custom-header>
         <div class="detail-content">
-             <div class="card-wrap">
+            <div class="card-wrap">
                 <div class="card-box photo-card">
                     <div class="card-title">
                         <span>农情照片</span>
                         <span class="date">{{ imgInfo.samplingTime }}</span>
                     </div>
                     <div class="ratio-tip">
-                        <div class="title">物候进程</div>
-                        <span>{{ imgInfo.regions }}出现新梢的果树占比为</span>
-                        <span class="ratio">{{ imgInfo.totalCount }}%</span>
+                        <div class="title">{{ route.query.title }}</div>
+                        <span>{{ imgInfo.regions }}{{ route.query.content }}</span>
+                        <!-- <span class="ratio">{{ imgInfo.totalCount }}%</span> -->
                     </div>
+                    <!-- <div class="location-tip">
+                        由于系统审核,某些照片拍摄位置与农场相差超 3 公里,请在农场现场使用水印相机重新拍摄上传,谢谢配合
+                    </div> -->
                     <div class="photo-grid">
-                        <div
-                            v-for="(photo, index) in imgInfo.imageList"
-                            :key="photo.id"
-                            class="photo-item"
-                            @click="handlePhotoClick(index)"
-                        >
+                        <div v-for="(photo, index) in imgInfo.imageList" :key="photo.id" class="photo-item"
+                            @click="handlePhotoClick(index)">
                             <img :src="photo.url" alt="农情照片" />
                         </div>
                     </div>
                 </div>
             </div>
+            <!-- <div class="card-wrap">
+                <div class="card-box risk-card">
+                    <div class="card-title">
+                        <span>农情详情</span>
+                        <span class="date">2025.05.06</span>
+                    </div>
+                    <img class="risk-image" src="" alt="">
+                    <div class="risk-content">
+                        <p class="risk-highlight">
+                            <span class="risk-title">高湿风险:</span>
+                            <span class="risk-text">预计未来7天平均相对湿度≥85%,伴随间歇性小雨或雾天。</span>
+                        </p>
+                        <p>潜在危害:田间湿度过高极易诱发霜疫霉病、炭疽病等真菌病害,病菌侵染幼果后导致腐烂、脱落。影响幼果正常发育和果皮着色,可能造成裂果、烂果。</p>
+                        <p>高湿环境利于蒂蛀虫等害虫繁殖,成虫活动增加,增加后期虫果率。</p>
+                        <div class="warning-tip">预警等级:黄色预警(中等风险),建议在未来48小时内启动预防措施。</div>
+                    </div>
+                </div>
+            </div> -->
         </div>
     </div>
 </template>
@@ -86,6 +103,19 @@ const getFarmImagePage = () => {
                 border-radius: 8px;
                 padding: 12px 16px;
                 box-sizing: border-box;
+
+                .card-title {
+                    font-size: 16px;
+                    display: flex;
+                    align-items: center;
+                    justify-content: space-between;
+                    margin-bottom: 12px;
+
+                    .date {
+                        font-size: 12px;
+                        color: rgba(0, 0, 0, 0.2);
+                    }
+                }
             }
 
             .sampling-card {
@@ -103,18 +133,6 @@ const getFarmImagePage = () => {
             }
 
             .photo-card {
-                .card-title {
-                    font-size: 16px;
-                    display: flex;
-                    align-items: center;
-                    justify-content: space-between;
-                    margin-bottom: 12px;
-                    .date {
-                        font-size: 12px;
-                        color: rgba(0, 0, 0, 0.2);
-                    }
-                }
-
                 .ratio-tip {
                     font-size: 14px;
                     padding: 5px 8px;
@@ -122,7 +140,8 @@ const getFarmImagePage = () => {
                     border-radius: 5px;
                     display: flex;
                     align-items: center;
-                    .title{
+
+                    .title {
                         font-size: 12px;
                         color: #fff;
                         margin-right: 12px;
@@ -130,12 +149,21 @@ const getFarmImagePage = () => {
                         padding: 2px 6px;
                         border-radius: 2px;
                     }
-                    .ratio{
+
+                    .ratio {
                         color: #2199f8;
                         margin-left: 3px;
                     }
                 }
 
+                .location-tip {
+                    margin-top: 12px;
+                    padding: 5px 10px;
+                    border: 1px solid #FF953D;
+                    border-radius: 5px;
+                    color: #FA7406;
+                }
+
                 .photo-grid {
                     margin-top: 12px;
                     display: grid;
@@ -161,6 +189,44 @@ const getFarmImagePage = () => {
                     }
                 }
             }
+
+            .risk-card {
+                .risk-image {
+                    width: 100%;
+                    height: 170px;
+                    border-radius: 5px;
+                    margin-bottom: 12px;
+                    background: #ff8a3d;
+                }
+
+                .risk-content {
+                    line-height: 22px;
+                    border-radius: 5px;
+                    padding: 5px 8px;
+                    background: rgba(190, 190, 190, 0.11);
+
+                    p {
+                        margin: 0 0 12px;
+                    }
+
+                    .risk-highlight {
+                        .risk-title {
+                            font-weight: 500;
+                            color: #FFAA14;
+                        }
+                    }
+                }
+
+                .warning-tip {
+                    border-radius: 5px;
+                    background: rgba(255, 177, 41, 0.1);
+                    color: #FFAA14;
+                    font-size: 14px;
+                    line-height: 22px;
+                    font-weight: 500;
+                    padding: 5px 8px;
+                }
+            }
         }
     }
 }