|
|
@@ -178,6 +178,13 @@ const highlightDeleteSelectedFeature = (feature) => {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
+const isFeatureLockedForDelete = (feature) => {
|
|
|
+ if (activeRegionType.value === "variety") return false;
|
|
|
+ const growth = getAbnormalGrowthOverlayMeta(feature);
|
|
|
+ const status = String(growth?.handleStatus || "");
|
|
|
+ return status === "2" || status === "3";
|
|
|
+};
|
|
|
+
|
|
|
const enableDeleteSelectionMode = () => {
|
|
|
const map = drawRegionMap.kmap?.map;
|
|
|
const polygonLayer = drawRegionMap.kmap?.polygonLayer?.layer;
|
|
|
@@ -195,6 +202,10 @@ const enableDeleteSelectionMode = () => {
|
|
|
}
|
|
|
);
|
|
|
if (hitFeature) {
|
|
|
+ if (isFeatureLockedForDelete(hitFeature)) {
|
|
|
+ ElMessage.warning("该地块不可删除");
|
|
|
+ return;
|
|
|
+ }
|
|
|
highlightDeleteSelectedFeature(hitFeature);
|
|
|
}
|
|
|
});
|
|
|
@@ -222,13 +233,24 @@ const handleRegionTypeClick = (item) => {
|
|
|
// 删除当前地块
|
|
|
const deletePolygon = () => {
|
|
|
const features = drawRegionMap.kmap?.polygonLayer?.source?.getFeatures?.() || [];
|
|
|
+ const deletableFeatures = features.filter((f) => !isFeatureLockedForDelete(f));
|
|
|
let matchedFeature = null;
|
|
|
if (features.length === 0) {
|
|
|
clearDeleteSelectionState();
|
|
|
ElMessage.warning("当前没有可删除的地块");
|
|
|
return;
|
|
|
}
|
|
|
+ if (features.length === 1 && deletableFeatures.length === 0) {
|
|
|
+ clearDeleteSelectionState();
|
|
|
+ ElMessage.warning("当前地块不可删除");
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (features.length > 1) {
|
|
|
+ if (deletableFeatures.length === 0) {
|
|
|
+ clearDeleteSelectionState();
|
|
|
+ ElMessage.warning("当前地块不可删除");
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (!selectedDeleteFeature.value) {
|
|
|
enableDeleteSelectionMode();
|
|
|
ElMessage.warning("当前有多个地块,请先选择地块");
|
|
|
@@ -245,6 +267,11 @@ const deletePolygon = () => {
|
|
|
ElMessage.warning("请选择地块后再删除");
|
|
|
return;
|
|
|
}
|
|
|
+ if (isFeatureLockedForDelete(matchedFeature)) {
|
|
|
+ clearDeleteSelectionState();
|
|
|
+ ElMessage.warning("该地块不可删除");
|
|
|
+ return;
|
|
|
+ }
|
|
|
} else {
|
|
|
clearDeleteSelectionState();
|
|
|
}
|
|
|
@@ -267,10 +294,8 @@ const deletePolygon = () => {
|
|
|
|
|
|
const polygonData = drawRegionMap.getAreaGeometry?.();
|
|
|
const geometryArr = polygonData?.geometryArr || [];
|
|
|
- const mergedGeom = mergePolygonWktsForApi(geometryArr);
|
|
|
-
|
|
|
try {
|
|
|
- const ok = await submitCurrentTabGeometryChange(mergedGeom);
|
|
|
+ const ok = await submitCurrentTabGeometryChange(geometryArr);
|
|
|
if (!ok) {
|
|
|
ElMessage.error("删除后保存失败");
|
|
|
return;
|
|
|
@@ -313,6 +338,26 @@ const getOuterRingCoordinates = (geometry) => {
|
|
|
|
|
|
const createPolygonStyleFunc = (fillColor, strokeColor) => {
|
|
|
return (feature) => {
|
|
|
+ const growth = getAbnormalGrowthOverlayMeta(feature);
|
|
|
+ const isTreatingDisease =
|
|
|
+ String(growth?.handleStatus || "") === "2" && !String(growth?.badgeText || "").includes("长势");
|
|
|
+ const isTreatingGrowth =
|
|
|
+ String(growth?.handleStatus || "") === "2" && String(growth?.badgeText || "").includes("长势");
|
|
|
+ const isControlledDisease = String(growth?.handleStatus || "") === "3";
|
|
|
+ const currentFillColor = isControlledDisease
|
|
|
+ ? SLEEP_ZONE_FILL_COLOR
|
|
|
+ : isTreatingGrowth
|
|
|
+ ? "rgba(255, 159, 102, 0.3)"
|
|
|
+ : isTreatingDisease
|
|
|
+ ? DISEASE_TREATING_FILL_COLOR
|
|
|
+ : fillColor;
|
|
|
+ const currentStrokeColor = isControlledDisease
|
|
|
+ ? SLEEP_ZONE_STROKE_COLOR
|
|
|
+ : isTreatingGrowth
|
|
|
+ ? "rgba(255, 94, 0, 0.3)"
|
|
|
+ : isTreatingDisease
|
|
|
+ ? DISEASE_TREATING_STROKE_COLOR
|
|
|
+ : strokeColor;
|
|
|
const styles = [];
|
|
|
const ring = getOuterRingCoordinates(feature.getGeometry());
|
|
|
const ringLen = ring.length;
|
|
|
@@ -325,7 +370,7 @@ const createPolygonStyleFunc = (fillColor, strokeColor) => {
|
|
|
image: new Circle({
|
|
|
radius: 4,
|
|
|
fill: new Fill({
|
|
|
- color: strokeColor,
|
|
|
+ color: currentStrokeColor,
|
|
|
}),
|
|
|
stroke: new Stroke({
|
|
|
color: "#fff",
|
|
|
@@ -350,10 +395,10 @@ const createPolygonStyleFunc = (fillColor, strokeColor) => {
|
|
|
}
|
|
|
const fillStyle = new Style({
|
|
|
fill: new Fill({
|
|
|
- color: fillColor,
|
|
|
+ color: currentFillColor,
|
|
|
}),
|
|
|
stroke: new Stroke({
|
|
|
- color: strokeColor,
|
|
|
+ color: currentStrokeColor,
|
|
|
width: 2,
|
|
|
}),
|
|
|
});
|
|
|
@@ -361,15 +406,17 @@ 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" }),
|
|
|
+ fill: new Fill({ color: growth.badgeTextColor || "#ffffff" }),
|
|
|
backgroundFill: new Fill({ color: growth.badgeBackground || ABNORMAL_BADGE_BG_GROWTH }),
|
|
|
+ backgroundStroke: growth.badgeBorderColor
|
|
|
+ ? new Stroke({ color: growth.badgeBorderColor, width: 1 })
|
|
|
+ : undefined,
|
|
|
padding: [4, 10, 4, 10],
|
|
|
offsetY: -40,
|
|
|
}),
|
|
|
@@ -558,6 +605,204 @@ const getGeomArrFromGeomItems = (tab) => {
|
|
|
.filter((x) => isValidGeom(x));
|
|
|
};
|
|
|
|
|
|
+const isDiseaseAbnormalTab = (tab) => {
|
|
|
+ const name = (tab?.problemZoneTypeName || tab?.regionName || "").toString();
|
|
|
+ return name.includes("病害") || name.includes("虫害");
|
|
|
+};
|
|
|
+
|
|
|
+const isGrowthAbnormalTab = (tab) => {
|
|
|
+ const name = (tab?.problemZoneTypeName || tab?.regionName || "").toString();
|
|
|
+ return name.includes("过慢") || name.includes("过快");
|
|
|
+};
|
|
|
+
|
|
|
+const hasLockedStatusItems = (tab) => {
|
|
|
+ const items = Array.isArray(tab?.geomItems) ? tab.geomItems : [];
|
|
|
+ return items.some((item) => {
|
|
|
+ const status = String(item?.handleStatus ?? "");
|
|
|
+ return status === "2" || status === "3";
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const isLockedAbnormalTab = (tab) =>
|
|
|
+ isDiseaseAbnormalTab(tab) || isGrowthAbnormalTab(tab) || hasLockedStatusItems(tab);
|
|
|
+
|
|
|
+const isLockedAbnormalItem = (tab, item) => {
|
|
|
+ const status = String(item?.handleStatus ?? "");
|
|
|
+ if (!isLockedAbnormalTab(tab)) return false;
|
|
|
+ return status === "2" || status === "3";
|
|
|
+};
|
|
|
+
|
|
|
+const formatDateToYmdDot = (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 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 getLockedDiseaseRegions = (tab) => {
|
|
|
+ if (!isLockedAbnormalTab(tab)) return [];
|
|
|
+ const items = Array.isArray(tab?.geomItems) ? tab.geomItems : [];
|
|
|
+ const out = [];
|
|
|
+ items.forEach((item) => {
|
|
|
+ if (!isLockedAbnormalItem(tab, item)) return;
|
|
|
+ const status = String(item?.handleStatus ?? "");
|
|
|
+ const polygons = flattenWktToPolygonWktArray(item?.geomWkt).filter(isValidGeom);
|
|
|
+ polygons.forEach((geometry) => {
|
|
|
+ const name = (tab?.problemZoneTypeName || tab?.regionName || "").toString();
|
|
|
+ const isGrowth = name.includes("过慢") || name.includes("过快");
|
|
|
+ const prefix = name.includes("虫害")
|
|
|
+ ? "虫害"
|
|
|
+ : name.includes("病害")
|
|
|
+ ? "病害"
|
|
|
+ : name.includes("过快")
|
|
|
+ ? "长势过快"
|
|
|
+ : "长势过慢";
|
|
|
+ out.push({
|
|
|
+ geometry,
|
|
|
+ displayMode: "lockedDisease",
|
|
|
+ handleStatus: status,
|
|
|
+ lockStyleType: isGrowth ? "growthTreating" : status === "3" ? "controlled" : "diseaseTreating",
|
|
|
+ label: status === "3" ? `${prefix}已控制` : `${prefix}治疗中`,
|
|
|
+ updatedTime: formatDateToYmdDot(item?.createTime),
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ return out;
|
|
|
+};
|
|
|
+
|
|
|
+const getEditableGeomArrForCurrentTab = (tab, rawGeomArr) => {
|
|
|
+ if (
|
|
|
+ !tab ||
|
|
|
+ !isLockedAbnormalTab(tab) ||
|
|
|
+ viewOnly.value ||
|
|
|
+ activeRegionType.value === "variety"
|
|
|
+ ) {
|
|
|
+ return flattenGeomWktListForMap(rawGeomArr);
|
|
|
+ }
|
|
|
+ const key = draftKeyFromParts(activeRegionType.value, tab);
|
|
|
+ const draft = key ? regionsDraftByKey.value[key] : null;
|
|
|
+ const draftItems = Array.isArray(draft?.geomItems) ? draft.geomItems : [];
|
|
|
+ const items = draftItems.length > 0 ? draftItems : Array.isArray(tab?.geomItems) ? tab.geomItems : [];
|
|
|
+ const editable = [];
|
|
|
+ items.forEach((item) => {
|
|
|
+ if (isLockedAbnormalItem(tab, item)) return;
|
|
|
+ editable.push(...flattenWktToPolygonWktArray(item?.geomWkt));
|
|
|
+ });
|
|
|
+ const editableGeomArr = flattenGeomWktListForMap(editable);
|
|
|
+ if (editableGeomArr.length > 0) return editableGeomArr;
|
|
|
+ // 当前小类存在 2/3 锁定地块时,编辑图层保持空,避免把锁定地块误加到可编辑层
|
|
|
+ const hasLockedItems = items.some((item) => isLockedAbnormalItem(tab, item));
|
|
|
+ if (hasLockedItems) return [];
|
|
|
+ // 仅当无锁定地块且没有 geomItems 时,兜底使用纯几何草稿(例如新勾画未落库)
|
|
|
+ const draftGeom = getDraftGeomForTab(activeRegionType.value, tab, activeVariety.value);
|
|
|
+ if (isValidGeom(draftGeom)) {
|
|
|
+ return flattenGeomWktListForMap([String(draftGeom).trim()]);
|
|
|
+ }
|
|
|
+ return editableGeomArr;
|
|
|
+};
|
|
|
+
|
|
|
+// 兜底防线:若锁定地块意外进入可编辑图层,按 handleStatus(2/3) 强制移除,避免只读层与可编辑层叠加
|
|
|
+const purgeLockedFeaturesFromEditableLayer = (tab) => {
|
|
|
+ if (!isLockedAbnormalTab(tab)) return;
|
|
|
+ const source = drawRegionMap.kmap?.polygonLayer?.source;
|
|
|
+ if (!source) return;
|
|
|
+ const features = source.getFeatures?.() || [];
|
|
|
+ features.forEach((feature) => {
|
|
|
+ const geomItem = getGeomItemByFeature(tab, feature);
|
|
|
+ const status = String(geomItem?.handleStatus ?? "");
|
|
|
+ if (status === "2" || status === "3") {
|
|
|
+ source.removeFeature?.(feature);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/** 非品种区提交:按当前地块顺序生成 geomItems(已有项更新,新项追加) */
|
|
|
+const buildGeomItemsFromGeometryArr = (tab, geometryArr, remarkOverride) => {
|
|
|
+ const geomArr = flattenGeomWktListForMap(geometryArr || []);
|
|
|
+ const existingItems = Array.isArray(tab?.geomItems) ? tab.geomItems : [];
|
|
|
+ const nowDate = new Date();
|
|
|
+ const y = nowDate.getFullYear();
|
|
|
+ const m = String(nowDate.getMonth() + 1).padStart(2, "0");
|
|
|
+ const d = String(nowDate.getDate()).padStart(2, "0");
|
|
|
+ const defaultCreateTime = `${y}-${m}-${d}`;
|
|
|
+ return geomArr.map((geomWkt, idx) => {
|
|
|
+ const base = existingItems[idx] && typeof existingItems[idx] === "object" ? existingItems[idx] : {};
|
|
|
+ const baseRemark = base?.remark != null ? String(base.remark) : "";
|
|
|
+ const finalRemark = remarkOverride != null ? String(remarkOverride) : baseRemark;
|
|
|
+ const createTime =
|
|
|
+ base?.createTime != null && String(base.createTime).trim() !== ""
|
|
|
+ ? String(base.createTime)
|
|
|
+ : defaultCreateTime;
|
|
|
+ const handleStatus =
|
|
|
+ base?.handleStatus != null && String(base.handleStatus).trim() !== ""
|
|
|
+ ? base.handleStatus
|
|
|
+ : 1;
|
|
|
+ return {
|
|
|
+ ...base,
|
|
|
+ geomWkt,
|
|
|
+ remark: finalRemark,
|
|
|
+ createTime,
|
|
|
+ handleStatus,
|
|
|
+ };
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/** 病害编辑态提交:2/3 保持只读原样,1 与新勾画项按当前可编辑地块更新/追加 */
|
|
|
+const buildDiseaseGeomItemsForSubmit = (tab, editableGeometryArr, remarkOverride) => {
|
|
|
+ const items = Array.isArray(tab?.geomItems) ? tab.geomItems : [];
|
|
|
+ const lockedItems = items
|
|
|
+ .filter((item) => {
|
|
|
+ const status = String(item?.handleStatus ?? "");
|
|
|
+ return status === "2" || status === "3";
|
|
|
+ })
|
|
|
+ .map((item) => ({
|
|
|
+ ...item,
|
|
|
+ geomWkt: isValidGeom(item?.geomWkt) ? String(item.geomWkt).trim() : "",
|
|
|
+ remark: item?.remark != null ? String(item.remark) : "",
|
|
|
+ }))
|
|
|
+ .filter((item) => isValidGeom(item.geomWkt));
|
|
|
+ const editableBaseItems = items.filter((item) => {
|
|
|
+ const status = String(item?.handleStatus ?? "");
|
|
|
+ return status === "1" || status === "";
|
|
|
+ });
|
|
|
+ const geomArr = flattenGeomWktListForMap(editableGeometryArr || []);
|
|
|
+ const editableItems = geomArr.map((geomWkt, idx) => {
|
|
|
+ const base =
|
|
|
+ editableBaseItems[idx] && typeof editableBaseItems[idx] === "object"
|
|
|
+ ? editableBaseItems[idx]
|
|
|
+ : {};
|
|
|
+ const baseRemark = base?.remark != null ? String(base.remark) : "";
|
|
|
+ const finalRemark = remarkOverride != null ? String(remarkOverride) : baseRemark;
|
|
|
+ const createTime =
|
|
|
+ base?.createTime != null && String(base.createTime).trim() !== ""
|
|
|
+ ? String(base.createTime)
|
|
|
+ : formatDateToYmdDot();
|
|
|
+ const handleStatus =
|
|
|
+ base?.handleStatus != null && String(base.handleStatus).trim() !== ""
|
|
|
+ ? String(base.handleStatus)
|
|
|
+ : 1;
|
|
|
+ return {
|
|
|
+ ...base,
|
|
|
+ geomWkt,
|
|
|
+ remark: finalRemark,
|
|
|
+ createTime: formatDateToYmdDash(createTime),
|
|
|
+ handleStatus,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ return [...lockedItems, ...editableItems];
|
|
|
+};
|
|
|
+
|
|
|
const draftKeyFromParts = (majorType, tabLike) => {
|
|
|
if (!majorType || !tabLike || typeof tabLike !== "object") return "";
|
|
|
const t = tabLike;
|
|
|
@@ -608,6 +853,25 @@ const getReadonlyVarietyRegions = (activeIndex) => {
|
|
|
return readonlyRegions;
|
|
|
};
|
|
|
|
|
|
+const getAllVarietyReadonlyRegions = () => {
|
|
|
+ const list = regionInfo.value?.regionList || [];
|
|
|
+ if (!Array.isArray(list) || list.length === 0) return [];
|
|
|
+ const regions = [];
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ const tab = list[i];
|
|
|
+ if (!tab) continue;
|
|
|
+ const draftGeom = getDraftGeomForTab("variety", tab, i);
|
|
|
+ const geometry = draftGeom || tab.geom;
|
|
|
+ if (!isValidGeom(geometry)) continue;
|
|
|
+ regions.push({
|
|
|
+ geometry,
|
|
|
+ label: tab.regionName || "",
|
|
|
+ displayMode: "readonlyVariety",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return regions;
|
|
|
+};
|
|
|
+
|
|
|
const renderReadonlyVarietyRegions = (activeIndex) => {
|
|
|
if (drawRegionMap && typeof drawRegionMap.setStatusRegions === "function") {
|
|
|
drawRegionMap.setStatusRegions(getReadonlyVarietyRegions(activeIndex));
|
|
|
@@ -620,24 +884,11 @@ const renderReadonlyVarietyRegions = (activeIndex) => {
|
|
|
*/
|
|
|
const renderAllVarietyRegionsReadonlyOnly = () => {
|
|
|
if (!drawRegionMap || typeof drawRegionMap.setStatusRegions !== "function") return;
|
|
|
- const list = regionInfo.value?.regionList || [];
|
|
|
- if (!Array.isArray(list) || list.length === 0) {
|
|
|
+ const regions = getAllVarietyReadonlyRegions();
|
|
|
+ if (regions.length === 0) {
|
|
|
drawRegionMap.setStatusRegions([]);
|
|
|
return;
|
|
|
}
|
|
|
- const regions = [];
|
|
|
- for (let i = 0; i < list.length; i++) {
|
|
|
- const tab = list[i];
|
|
|
- if (!tab) continue;
|
|
|
- const draftGeom = getDraftGeomForTab("variety", tab, i);
|
|
|
- const geometry = draftGeom || tab.geom;
|
|
|
- if (!isValidGeom(geometry)) continue;
|
|
|
- regions.push({
|
|
|
- geometry,
|
|
|
- label: tab.regionName || "",
|
|
|
- displayMode: "readonlyVariety",
|
|
|
- });
|
|
|
- }
|
|
|
drawRegionMap.setStatusRegions(regions);
|
|
|
};
|
|
|
|
|
|
@@ -693,7 +944,7 @@ const handleVarietyClick = (tab, index) => {
|
|
|
if (fromItems.length) return fromItems;
|
|
|
return isValidGeom(tab?.geom) ? [String(tab.geom).trim()] : [];
|
|
|
})();
|
|
|
- const geomArr = flattenGeomWktListForMap(rawGeomArr);
|
|
|
+ const geomArr = getEditableGeomArrForCurrentTab(tab, rawGeomArr);
|
|
|
// 保留一份“当前选中地块”的原始字符串形态给其它逻辑使用
|
|
|
regionGeom.value = geomArr.length > 1 ? JSON.stringify(geomArr) : geomArr[0] || "";
|
|
|
// 地图尚未初始化时,仅更新状态,不做图层绘制,避免首次进入重复叠加
|
|
|
@@ -705,7 +956,23 @@ const handleVarietyClick = (tab, index) => {
|
|
|
// 非品种区:底图显示品种区(只读绿色)+ 当前小类(按当前大类颜色渲染)
|
|
|
// 品种区:显示其他品种只读 + 当前品种可编辑
|
|
|
if (activeRegionType.value !== "variety") {
|
|
|
- renderAllVarietyRegionsReadonlyOnly();
|
|
|
+ if (isLockedAbnormalTab(tab)) {
|
|
|
+ drawRegionMap.setStatusRegions([
|
|
|
+ ...getAllVarietyReadonlyRegions(),
|
|
|
+ ...getLockedDiseaseRegions(tab),
|
|
|
+ ]);
|
|
|
+ // 查看态下锁定异常区只展示只读层,避免与 polygonLayer 回显叠层
|
|
|
+ if (viewOnly.value) {
|
|
|
+ drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
|
|
|
+ pendingGeomRenderTimer.value = setTimeout(() => {
|
|
|
+ drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
|
|
|
+ drawRegionMap.fitAllRegions?.();
|
|
|
+ }, 50);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ renderAllVarietyRegionsReadonlyOnly();
|
|
|
+ }
|
|
|
} else {
|
|
|
renderReadonlyVarietyRegions(index);
|
|
|
}
|
|
|
@@ -730,9 +997,12 @@ const handleVarietyClick = (tab, index) => {
|
|
|
false,
|
|
|
undefined,
|
|
|
undefined,
|
|
|
- getAbnormalGrowthOverlayMeta(),
|
|
|
+ getAbnormalGrowthOverlayMeta,
|
|
|
viewOnlySubCategoryLabelWithArea || undefined
|
|
|
);
|
|
|
+ if (!viewOnly.value && activeRegionType.value !== "variety") {
|
|
|
+ purgeLockedFeaturesFromEditableLayer(tab);
|
|
|
+ }
|
|
|
// 有当前小类地块:仅 fit 当前可编辑层,避免被其它只读参考地块拉远
|
|
|
drawRegionMap.fitView?.();
|
|
|
}, 50);
|
|
|
@@ -786,33 +1056,124 @@ const getCanonicalRegionTypeForStyles = () => {
|
|
|
|
|
|
const ABNORMAL_BADGE_BG_DISEASE_PEST = "#E32A28";
|
|
|
const ABNORMAL_BADGE_BG_GROWTH = "#F76F00";
|
|
|
+const DISEASE_TREATING_FILL_COLOR = "rgba(191, 91, 91, 0.36)";
|
|
|
+const DISEASE_TREATING_STROKE_COLOR = "rgba(224, 49, 49, 0.3)";
|
|
|
+const SLEEP_ZONE_STROKE_COLOR = "#A6A6A6";
|
|
|
+const SLEEP_ZONE_FILL_COLOR = [166, 166, 166, 0.25];
|
|
|
+
|
|
|
+const getGeomItemByFeature = (tab, feature) => {
|
|
|
+ if (!tab || !feature) return null;
|
|
|
+ const items = Array.isArray(tab.geomItems) ? tab.geomItems : [];
|
|
|
+ if (!items.length) return null;
|
|
|
+ let featureWkt = "";
|
|
|
+ try {
|
|
|
+ featureWkt = wktGeomFormat.writeGeometry(feature.getGeometry(), WGS84_WKT_OPTS);
|
|
|
+ } catch (_) {
|
|
|
+ featureWkt = "";
|
|
|
+ }
|
|
|
+ if (!isValidGeom(featureWkt)) return null;
|
|
|
+ const normalizeWktKey = (wkt) => String(wkt || "").replace(/\s+/g, "").trim();
|
|
|
+ const normalizedFeatureWkt = normalizeWktKey(featureWkt);
|
|
|
+ for (const item of items) {
|
|
|
+ const polygons = flattenWktToPolygonWktArray(item?.geomWkt);
|
|
|
+ if (polygons.some((w) => normalizeWktKey(w) === normalizedFeatureWkt)) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 兜底:仅在要素数量与 geomItems 数量一致时按索引映射,避免病害锁定态错位
|
|
|
+ const features = drawRegionMap.kmap?.polygonLayer?.source?.getFeatures?.() || [];
|
|
|
+ if (features.length === items.length) {
|
|
|
+ const featureIndex = features.findIndex((f) => f === feature);
|
|
|
+ if (featureIndex >= 0 && featureIndex < items.length) {
|
|
|
+ return items[featureIndex];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+};
|
|
|
|
|
|
/** 异常区小类(长势/病害/虫害等)闭合地块后在多边形内展示标签与发现日期(查看态 setAreaGeometry 同步使用) */
|
|
|
-const getAbnormalGrowthOverlayMeta = () => {
|
|
|
+const getAbnormalGrowthOverlayMeta = (feature) => {
|
|
|
if (getCanonicalRegionTypeForStyles() !== "ABNORMAL") return null;
|
|
|
const tab = varietyTabs.value?.[activeVariety.value];
|
|
|
if (!tab) return null;
|
|
|
+ const geomItem = getGeomItemByFeature(tab, feature);
|
|
|
const name = (tab.problemZoneTypeName || tab.regionName || "").toString();
|
|
|
+ const handleStatus = String(geomItem?.handleStatus || "");
|
|
|
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;
|
|
|
+ let badgeTextColor = "#ffffff";
|
|
|
+ let badgeBorderColor = "";
|
|
|
+ if (name.includes("病害") || name.includes("虫害")) {
|
|
|
+ // 病害/虫害:严格按当前地块对应 geomItem;未命中(新勾画)按“新增”展示,避免误用旧地块状态
|
|
|
+ const currentGeomItem = geomItem || null;
|
|
|
+ const currentHandleStatus = String(currentGeomItem?.handleStatus || "");
|
|
|
+ const currentCreateTime = currentGeomItem?.createTime;
|
|
|
+ const prefix = name.includes("虫害") ? "虫害" : "病害";
|
|
|
+ if (!currentGeomItem) {
|
|
|
+ badgeText = `新增${prefix}`;
|
|
|
+ badgeBackground = ABNORMAL_BADGE_BG_DISEASE_PEST;
|
|
|
+ } else if (currentHandleStatus === "2") {
|
|
|
+ badgeText = `${prefix}治疗中`;
|
|
|
+ badgeTextColor = ABNORMAL_BADGE_BG_DISEASE_PEST;
|
|
|
+ badgeBackground = "#fff";
|
|
|
+ badgeBorderColor = ABNORMAL_BADGE_BG_DISEASE_PEST;
|
|
|
+ } else if (currentHandleStatus == "3") {
|
|
|
+ badgeText = `${prefix}已控制`;
|
|
|
+ // 已控制态与休眠区视觉保持一致
|
|
|
+ badgeBackground = SLEEP_ZONE_STROKE_COLOR;
|
|
|
+ } else {
|
|
|
+ badgeText = `新增${prefix}`;
|
|
|
+ badgeBackground = ABNORMAL_BADGE_BG_DISEASE_PEST;
|
|
|
+ }
|
|
|
+ const rawCreateTime = currentCreateTime || tab?.createTime;
|
|
|
+ const sourceDate = rawCreateTime ? new Date(rawCreateTime) : 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 {
|
|
|
+ badgeText,
|
|
|
+ discoveryDate: `${y}.${m}.${d}`,
|
|
|
+ badgeBackground,
|
|
|
+ badgeTextColor,
|
|
|
+ badgeBorderColor,
|
|
|
+ handleStatus: currentHandleStatus,
|
|
|
+ };
|
|
|
} else if (name.includes("过慢")) {
|
|
|
- badgeText = "新增长势过慢";
|
|
|
+ if (handleStatus === "2") {
|
|
|
+ badgeText = "长势过慢治疗中";
|
|
|
+ badgeTextColor = ABNORMAL_BADGE_BG_GROWTH;
|
|
|
+ badgeBackground = "#fff";
|
|
|
+ badgeBorderColor = ABNORMAL_BADGE_BG_GROWTH;
|
|
|
+ } else {
|
|
|
+ badgeText = "新增长势过慢";
|
|
|
+ }
|
|
|
} else if (name.includes("过快")) {
|
|
|
- badgeText = "新增长势过快";
|
|
|
+ if (handleStatus === "2") {
|
|
|
+ badgeText = "长势过快治疗中";
|
|
|
+ badgeTextColor = ABNORMAL_BADGE_BG_GROWTH;
|
|
|
+ badgeBackground = "#fff";
|
|
|
+ badgeBorderColor = ABNORMAL_BADGE_BG_GROWTH;
|
|
|
+ } else {
|
|
|
+ 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 rawCreateTime = geomItem?.createTime || tab?.createTime;
|
|
|
+ const sourceDate = rawCreateTime ? new Date(rawCreateTime) : 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 {
|
|
|
+ badgeText,
|
|
|
+ discoveryDate: `${y}.${m}.${d}`,
|
|
|
+ badgeBackground,
|
|
|
+ badgeTextColor,
|
|
|
+ badgeBorderColor,
|
|
|
+ handleStatus,
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
const findProblemZoneGroupByDrawType = (majorType) => {
|
|
|
@@ -885,17 +1246,34 @@ const buildProblemZoneListForSubmit = () => {
|
|
|
if (!canonical) return null;
|
|
|
const k = draftKeyFromParts(canonical, child);
|
|
|
const d = k ? regionsDraftByKey.value[k] : null;
|
|
|
- const geomWkt = d?.geom != null ? String(d.geom).trim() : "";
|
|
|
- if (!isValidGeom(geomWkt)) return null;
|
|
|
- const remark =
|
|
|
- d && "remark" in d && d.remark != null ? String(d.remark) : "";
|
|
|
+ let geomItems = Array.isArray(d?.geomItems)
|
|
|
+ ? d.geomItems
|
|
|
+ : [];
|
|
|
+ geomItems = geomItems
|
|
|
+ .map((x) => {
|
|
|
+ const geomWkt = x?.geomWkt != null ? String(x.geomWkt).trim() : "";
|
|
|
+ if (!isValidGeom(geomWkt)) return null;
|
|
|
+ return {
|
|
|
+ ...x,
|
|
|
+ geomWkt,
|
|
|
+ remark: x?.remark != null ? String(x.remark) : "",
|
|
|
+ };
|
|
|
+ })
|
|
|
+ .filter(Boolean);
|
|
|
+ if (geomItems.length === 0) {
|
|
|
+ const geomWkt = d?.geom != null ? String(d.geom).trim() : "";
|
|
|
+ if (!isValidGeom(geomWkt)) return null;
|
|
|
+ const remark =
|
|
|
+ d && "remark" in d && d.remark != null ? String(d.remark) : "";
|
|
|
+ geomItems = [{ geomWkt, remark }];
|
|
|
+ }
|
|
|
return {
|
|
|
farmSubjectId: subjectId,
|
|
|
problemZoneTypeId: child.problemZoneTypeId ?? child.typeId ?? "",
|
|
|
problemZoneTypeName: child.problemZoneTypeName ?? child.regionName ?? "",
|
|
|
parentName: group.name ?? "",
|
|
|
parentCode: group.code ?? "",
|
|
|
- geomItems: [{ geomWkt, remark }],
|
|
|
+ geomItems,
|
|
|
};
|
|
|
})
|
|
|
.filter(Boolean);
|
|
|
@@ -1019,14 +1397,24 @@ const resetPolygon = () => {
|
|
|
if (fromItems.length) return fromItems;
|
|
|
return isValidGeom(currentTab?.geom) ? [String(currentTab.geom).trim()] : [];
|
|
|
})();
|
|
|
- const initialGeomArr = flattenGeomWktListForMap(initialGeomArrRaw);
|
|
|
+ const initialGeomArr = getEditableGeomArrForCurrentTab(currentTab, initialGeomArrRaw);
|
|
|
+ if (
|
|
|
+ !viewOnly.value &&
|
|
|
+ activeRegionType.value !== "variety" &&
|
|
|
+ isLockedAbnormalTab(currentTab)
|
|
|
+ ) {
|
|
|
+ drawRegionMap.setStatusRegions([
|
|
|
+ ...getAllVarietyReadonlyRegions(),
|
|
|
+ ...getLockedDiseaseRegions(currentTab),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
if (initialGeomArr.length > 0) {
|
|
|
drawRegionMap.setAreaGeometry(
|
|
|
initialGeomArr,
|
|
|
false,
|
|
|
undefined,
|
|
|
undefined,
|
|
|
- getAbnormalGrowthOverlayMeta()
|
|
|
+ getAbnormalGrowthOverlayMeta
|
|
|
);
|
|
|
regionGeom.value =
|
|
|
initialGeomArr.length > 1 ? JSON.stringify(initialGeomArr) : initialGeomArr[0];
|
|
|
@@ -1088,10 +1476,15 @@ const submitRegions = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-const submitCurrentTabGeometryChange = async (geomWkt) => {
|
|
|
+const submitCurrentTabGeometryChange = async (geomWktOrArr) => {
|
|
|
const tab = varietyTabs.value?.[activeVariety.value];
|
|
|
if (!tab) return false;
|
|
|
- const normalizedGeom = isValidGeom(geomWkt) ? String(geomWkt).trim() : "";
|
|
|
+ const geometryArr = Array.isArray(geomWktOrArr)
|
|
|
+ ? geomWktOrArr
|
|
|
+ : isValidGeom(geomWktOrArr)
|
|
|
+ ? [String(geomWktOrArr).trim()]
|
|
|
+ : [];
|
|
|
+ const normalizedGeom = mergePolygonWktsForApi(geometryArr);
|
|
|
const params = {
|
|
|
subjectId: route.query.subjectId,
|
|
|
regionList: [],
|
|
|
@@ -1110,7 +1503,9 @@ const submitCurrentTabGeometryChange = async (geomWkt) => {
|
|
|
} else {
|
|
|
const group = findProblemZoneGroupByDrawType(activeRegionType.value);
|
|
|
if (!group) return false;
|
|
|
- const remark = tab?.geomItems?.[0]?.remark || "";
|
|
|
+ const geomItems = isLockedAbnormalTab(tab)
|
|
|
+ ? buildDiseaseGeomItemsForSubmit(tab, geometryArr)
|
|
|
+ : buildGeomItemsFromGeometryArr(tab, geometryArr);
|
|
|
params.problemZoneList = [
|
|
|
{
|
|
|
name: group.name,
|
|
|
@@ -1122,7 +1517,7 @@ const submitCurrentTabGeometryChange = async (geomWkt) => {
|
|
|
problemZoneTypeName: tab.problemZoneTypeName ?? tab.regionName ?? "",
|
|
|
parentName: group.name ?? "",
|
|
|
parentCode: group.code ?? "",
|
|
|
- geomItems: normalizedGeom ? [{ geomWkt: normalizedGeom, remark: String(remark) }] : [],
|
|
|
+ geomItems,
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
@@ -1149,8 +1544,8 @@ const confirmArea = async (drawMeta) => {
|
|
|
ElMessage.warning("请先勾画地块后再确认");
|
|
|
return;
|
|
|
}
|
|
|
- const geom = mergePolygonWktsForApi(geometryArr);
|
|
|
- if (!isValidGeom(geom)) {
|
|
|
+ const normalizedGeomArr = flattenGeomWktListForMap(geometryArr);
|
|
|
+ if (!Array.isArray(normalizedGeomArr) || normalizedGeomArr.length === 0) {
|
|
|
ElMessage.warning("地块几何无效,请重新勾画");
|
|
|
return;
|
|
|
}
|
|
|
@@ -1159,15 +1554,23 @@ const confirmArea = async (drawMeta) => {
|
|
|
ElMessage.warning("无法识别勾画类别,请重新选择");
|
|
|
return;
|
|
|
}
|
|
|
- regionsDraftByKey.value[key] = {
|
|
|
- geomArr: geometryArr,
|
|
|
+ const geom = mergePolygonWktsForApi(normalizedGeomArr);
|
|
|
+ const currentTab = varietyTabs.value?.[activeVariety.value];
|
|
|
+ const nextDraft = {
|
|
|
+ geomArr: normalizedGeomArr,
|
|
|
geom,
|
|
|
remark: meta.remark != null ? String(meta.remark) : "",
|
|
|
};
|
|
|
+ if (meta.type !== "variety") {
|
|
|
+ nextDraft.geomItems = isLockedAbnormalTab(currentTab)
|
|
|
+ ? buildDiseaseGeomItemsForSubmit(currentTab, normalizedGeomArr, meta.remark)
|
|
|
+ : buildGeomItemsFromGeometryArr(currentTab, normalizedGeomArr, meta.remark);
|
|
|
+ }
|
|
|
+ regionsDraftByKey.value[key] = nextDraft;
|
|
|
const tabList = getTabsForMajorType(meta.type);
|
|
|
const idx = findTabIndexForCategory(tabList, meta.category);
|
|
|
if (activeRegionType.value === meta.type && idx >= 0) {
|
|
|
- regionsDraftByIndex.value[idx] = { geomArr: geometryArr, geom };
|
|
|
+ regionsDraftByIndex.value[idx] = { geomArr: normalizedGeomArr, geom };
|
|
|
}
|
|
|
|
|
|
const ok = await submitRegions();
|
|
|
@@ -1270,17 +1673,29 @@ const handleEditRegion = async () => {
|
|
|
applyRegionStyles();
|
|
|
// 清空当前可编辑图层,避免残留
|
|
|
drawRegionMap.kmap?.polygonLayer?.source?.clear?.();
|
|
|
- renderAllVarietyRegionsReadonlyOnly();
|
|
|
-
|
|
|
const tab = varietyTabs.value?.[activeVariety.value];
|
|
|
+ if (isLockedAbnormalTab(tab)) {
|
|
|
+ drawRegionMap.setStatusRegions([
|
|
|
+ ...getAllVarietyReadonlyRegions(),
|
|
|
+ ...getLockedDiseaseRegions(tab),
|
|
|
+ ]);
|
|
|
+ } else {
|
|
|
+ renderAllVarietyRegionsReadonlyOnly();
|
|
|
+ }
|
|
|
+
|
|
|
// 进入编辑时:缩放到当前要编辑的地块
|
|
|
- const currentGeomStr = tab
|
|
|
- ? (getDraftGeomForTab(activeRegionType.value, tab, activeVariety.value) || tab.geom)
|
|
|
- : "";
|
|
|
- const currentGeomArrRaw = isValidGeom(currentGeomStr)
|
|
|
- ? [String(currentGeomStr).trim()]
|
|
|
- : getGeomArrFromGeomItems(tab);
|
|
|
- const currentGeomArr = flattenGeomWktListForMap(currentGeomArrRaw);
|
|
|
+ // 异常锁定小类(2/3)进入编辑态时,禁止从 tab.geom / draftGeom 回填到可编辑层,避免与只读层重叠
|
|
|
+ const currentGeomArrRaw = isLockedAbnormalTab(tab)
|
|
|
+ ? getGeomArrFromGeomItems(tab)
|
|
|
+ : (() => {
|
|
|
+ const currentGeomStr = tab
|
|
|
+ ? (getDraftGeomForTab(activeRegionType.value, tab, activeVariety.value) || tab.geom)
|
|
|
+ : "";
|
|
|
+ return isValidGeom(currentGeomStr)
|
|
|
+ ? [String(currentGeomStr).trim()]
|
|
|
+ : getGeomArrFromGeomItems(tab);
|
|
|
+ })();
|
|
|
+ const currentGeomArr = getEditableGeomArrForCurrentTab(tab, currentGeomArrRaw);
|
|
|
regionGeom.value =
|
|
|
currentGeomArr.length > 1 ? JSON.stringify(currentGeomArr) : currentGeomArr[0] || "";
|
|
|
if (currentGeomArr.length > 0) {
|
|
|
@@ -1290,8 +1705,9 @@ const handleEditRegion = async () => {
|
|
|
true,
|
|
|
undefined,
|
|
|
undefined,
|
|
|
- getAbnormalGrowthOverlayMeta()
|
|
|
+ getAbnormalGrowthOverlayMeta
|
|
|
);
|
|
|
+ purgeLockedFeaturesFromEditableLayer(tab);
|
|
|
} else {
|
|
|
// 仍自适应到品种区底图
|
|
|
drawRegionMap.fitAllRegions?.();
|
|
|
@@ -1324,7 +1740,7 @@ const handleEditRegion = async () => {
|
|
|
true,
|
|
|
undefined,
|
|
|
undefined,
|
|
|
- getAbnormalGrowthOverlayMeta()
|
|
|
+ getAbnormalGrowthOverlayMeta
|
|
|
);
|
|
|
}
|
|
|
} else {
|
|
|
@@ -1337,7 +1753,7 @@ const handleEditRegion = async () => {
|
|
|
true,
|
|
|
undefined,
|
|
|
undefined,
|
|
|
- getAbnormalGrowthOverlayMeta()
|
|
|
+ getAbnormalGrowthOverlayMeta
|
|
|
);
|
|
|
}
|
|
|
}
|