| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- <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 { data } = await VE_API.basic_farm.fetchRegionInfo({ subjectId: localStorage.getItem('selectedFarmId') });
- if (data && data.length > 0) {
- regionInfo.value = data[0] || [];
- regionTypeTabs.value = regionInfo.value.problemZoneList || [];
- regionTypeTabs.value.unshift({ name: "品种区", code: "variety" });
- // if (data[0]?.regionList?.length) {
- // // if (!hasAppliedInitialVariety.value && route.query?.varietyId) {
- // // activeVariety.value = resolveInitialVarietyIndex(data[0]?.regionList);
- // // hasAppliedInitialVariety.value = true;
- // // }
- // // point.value = data[0].point;
- // varietyTabs.value = regionInfo.value.regionList || [];
- // }
- if (route.query.firstAct) {
- activeRegionType.value = route.query.firstAct;
- const index = regionTypeTabs.value.findIndex(item => item.code === route.query.firstAct);
- if (index !== -1) {
- varietyTabs.value = data[0].problemZoneList[index].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();
- sessionStorage.setItem("drawRegionPolygonData", JSON.stringify(polygonData));
- 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>
|