Parcourir la source

fix: 弹窗,页面,农情研判

lxf il y a 6 jours
Parent
commit
23846121bc

BIN
src/assets/img/agricultural/baidian.png


BIN
src/assets/img/agricultural/empty.png


BIN
src/assets/img/agricultural/question.png


+ 9 - 14
src/components/allGarden.vue

@@ -52,10 +52,11 @@
 
             <div class="farm-list">
                 <div
-                    v-for="item in filteredFarmList"
-                    :key="item.id"
+                    v-for="(item, index) in filteredFarmList"
+                    :key="index"
                     class="farm-card"
-                    :class="{ 'is-current': item.isCurrent }"
+                    @click.stop="handleEnterFarm(item, index)"
+                    :class="{ 'is-current': currentIndex === index }"
                 >
                     <div class="farm-card__header">
                         <div class="farm-card__main">
@@ -68,7 +69,7 @@
                             </div>
                         </div>
                         <div
-                            v-if="item.isCurrent"
+                            v-if="currentIndex === index"
                             class="action-btn action-btn-current"
                         >
                             {{ t("garden.currentFarm") }}
@@ -76,7 +77,7 @@
                         <div
                             v-else
                             class="action-btn action-btn-enter"
-                            @click="handleEnterFarm(item)"
+                            @click.stop="handleEnterFarm(item, index)"
                         >
                             {{ t("garden.enterFarm") }}
                         </div>
@@ -223,13 +224,6 @@ function saveSelectedFarm(farm) {
     localStorage.setItem("selectedFarmData", JSON.stringify(farm));
 }
 
-function markCurrentFarm(selectedId) {
-    farmList.value = farmList.value.map((farm) => ({
-        ...farm,
-        isCurrent: Number(farm.id) === Number(selectedId),
-    }));
-}
-
 function getDefaultMapPoint() {
     const withPoint = farmList.value.find((farm) => farm.wkt);
     return withPoint?.wkt || store.state.home.miniUserLocationPoint || "POINT(113.38 23.18)";
@@ -265,8 +259,9 @@ function getFarmList() {
         });
 }
 
-function handleEnterFarm(item) {
-    markCurrentFarm(item.id);
+const currentIndex = ref(null);
+function handleEnterFarm(item, index) {
+    currentIndex.value = index;
     saveSelectedFarm(item);
     ElMessage.success(t("garden.enterFarmSuccess"));
     router.back();

+ 433 - 0
src/components/popup/restorePlantingPopup.vue

@@ -0,0 +1,433 @@
+<template>
+    <popup
+        v-model:show="showValue"
+        round
+        teleport="body"
+        class="restore-planting-popup"
+        :close-on-click-overlay="true"
+    >
+        <div class="popup-content">
+            <img
+                class="restore-planting-popup__decor"
+                src="@/assets/img/agricultural/question.png"
+                alt=""
+            />
+            <div class="restore-planting-popup__header">
+                <div class="restore-planting-popup__title">
+                    <div>{{ t("agriFile.restoreFillQuestions") }}</div>
+                    <div>{{ t("agriFile.restorePlantingService") }}</div>
+                </div>
+            </div>
+
+            <!-- 问题一:选择时间 -->
+            <div v-if="questionType === 'time'" class="restore-planting-popup__card">
+                <div class="restore-planting-popup__question">
+                    <template v-if="highlightWord && questionText.includes(highlightWord)">
+                        <span>{{ questionText.split(highlightWord)[0] }}</span>
+                        <span class="restore-planting-popup__keyword">{{ highlightWord }}</span>
+                        <span>{{ questionText.split(highlightWord).slice(1).join(highlightWord) }}</span>
+                    </template>
+                    <template v-else>{{ questionText }}</template>
+                </div>
+                <div class="restore-planting-popup__hint">{{ hintText }}</div>
+
+                <div class="restore-planting-popup__photo">
+                    <img :src="sampleImage" alt="" />
+                    <!-- <div class="restore-planting-popup__bbox">
+                        <span>{{ stageTag }}</span>
+                    </div>
+                    <div class="restore-planting-popup__detect">{{ detectText }}</div> -->
+                </div>
+
+                <el-date-picker
+                    v-model="selectedDate"
+                    class="restore-planting-popup__date-picker"
+                    type="date"
+                    format="YYYY.MM.DD"
+                    value-format="YYYY.MM.DD"
+                    :placeholder="t('agriFile.selectTime')"
+                    :editable="false"
+                    :clearable="false"
+                    style="width: 100%"
+                />
+            </div>
+
+            <!-- 问题二:物候期选择 -->
+            <div v-else class="restore-planting-popup__card">
+                <div class="restore-planting-popup__question">
+                    <template v-if="phenologyHighlight && phenologyQuestion.includes(phenologyHighlight)">
+                        <span>{{ phenologyQuestion.split(phenologyHighlight)[0] }}</span>
+                        <span class="restore-planting-popup__keyword">{{ phenologyHighlight }}</span>
+                        <span>{{ phenologyQuestion.split(phenologyHighlight).slice(1).join(phenologyHighlight) }}</span>
+                    </template>
+                    <template v-else>{{ phenologyQuestion }}</template>
+                </div>
+
+                <div class="restore-planting-popup__photo restore-planting-popup__photo--guide">
+                    <img :src="phenologyImage" alt="" />
+                    <div class="restore-planting-popup__guide-btn" @click="handleViewGuide">
+                        {{ t("agriFile.viewPatrolGuide") }}
+                    </div>
+                </div>
+
+                <div class="restore-planting-popup__options">
+                    <div
+                        v-for="item in phenologyOptions"
+                        :key="item.value"
+                        class="restore-planting-popup__option"
+                        :class="{ active: phenologyAnswer === item.value }"
+                        @click="phenologyAnswer = item.value"
+                    >
+                        <span>{{ item.label }}</span>
+                        <span v-if="phenologyAnswer === item.value" class="restore-planting-popup__option-check">
+                            <el-icon><Check /></el-icon>
+                        </span>
+                    </div>
+                </div>
+            </div>
+
+            <div class="restore-planting-popup__actions">
+                <div class="restore-planting-popup__btn restore-planting-popup__btn--ghost" @click="handleChangeQuestion">
+                    {{ t("agriFile.changeQuestion") }}
+                </div>
+                <div class="restore-planting-popup__btn restore-planting-popup__btn--primary" @click="handleConfirm">
+                    {{ questionType === 'time' ? t("agriFile.confirmNow") : t("agriFile.submitInfo") }}
+                </div>
+            </div>
+        </div>
+    </popup>
+</template>
+
+<script setup>
+import { computed, ref, watch } from "vue";
+import { Popup } from "vant";
+import { Check } from "@element-plus/icons-vue";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+    questionText: {
+        type: String,
+        default: "还记得农场的 “来梢” 的时间吗?",
+    },
+    highlightWord: {
+        type: String,
+        default: "来梢",
+    },
+    hintText: {
+        type: String,
+        default: "时间误差 5-7 天无需担心,系统将结合作物生长模拟模型自动校准",
+    },
+    sampleImage: {
+        type: String,
+        default: require("@/assets/img/agricultural/baidian.png"),
+    },
+    stageTag: {
+        type: String,
+        default: "白点",
+    },
+    detectText: {
+        type: String,
+        default: "生育期检测:当前检测到白点期",
+    },
+    phenologyQuestion: {
+        type: String,
+        default: "您的农场是否进入到 某某物候期 ?",
+    },
+    phenologyHighlight: {
+        type: String,
+        default: "某某物候期",
+    },
+    phenologyImage: {
+        type: String,
+        default: require("@/assets/img/common/sd-1.jpg"),
+    },
+});
+
+const emit = defineEmits(["update:show", "changeQuestion", "confirm", "viewGuide"]);
+
+const showValue = computed({
+    get: () => props.show,
+    set: (val) => emit("update:show", val),
+});
+
+const questionType = ref("time"); // time | phenology
+const selectedDate = ref("");
+const phenologyAnswer = ref("reached");
+
+const phenologyOptions = computed(() => [
+    { value: "not_reached", label: t("agriFile.notReached") },
+    { value: "reached", label: t("agriFile.reached") },
+    { value: "passed", label: t("agriFile.passed") },
+]);
+
+watch(
+    () => props.show,
+    (visible) => {
+        if (visible) {
+            questionType.value = "time";
+            selectedDate.value = "";
+            phenologyAnswer.value = "reached";
+        }
+    }
+);
+
+const handleChangeQuestion = () => {
+    questionType.value = questionType.value === "time" ? "phenology" : "time";
+    emit("changeQuestion", questionType.value);
+};
+
+const handleViewGuide = () => {
+    emit("viewGuide");
+};
+
+const handleConfirm = () => {
+    emit("confirm", {
+        type: questionType.value,
+        date: selectedDate.value,
+        phenologyAnswer: phenologyAnswer.value,
+    });
+    showValue.value = false;
+};
+</script>
+
+<style scoped lang="scss">
+.restore-planting-popup {
+    width: 86%;
+    padding: 18px 14px 16px;
+    box-sizing: border-box;
+    background: linear-gradient(180deg, #d7ecff 0%, #ffffff 28%);
+    overflow: visible;
+    
+
+    &__header {
+        // position: relative;
+        display: flex;
+        align-items: flex-start;
+        justify-content: space-between;
+        // padding-right: 72px;
+        // min-height: 64px;
+    }
+
+    &__title {
+        font-size: 22px;
+        line-height: 25px;
+        color: #111;
+        div {
+            font-family: "PangMenZhengDao";
+        }
+    }
+
+    .popup-content {
+        position: relative;
+    }
+
+    &__decor {
+        position: absolute;
+        top: -8px;
+        right: -4px;
+        width: 78px;
+        height: 78px;
+        object-fit: contain;
+        pointer-events: none;
+    }
+
+    &__card {
+        margin-top: 12px;
+        padding: 10px 12px 12px;
+        border-radius: 12px;
+        background: #eaf5ff;
+        box-sizing: border-box;
+        position: relative;
+        z-index: 2;
+    }
+
+    &__question {
+        font-size: 16px;
+        line-height: 24px;
+        color: #252525;
+        font-weight: 500;
+    }
+
+    &__keyword {
+        color: #2199f8;
+    }
+
+    &__hint {
+        margin-top: 4px;
+        font-size: 12px;
+        line-height: 16px;
+        color: #9E9E9E;
+    }
+
+    &__photo {
+        position: relative;
+        margin: 12px 0;
+        border-radius: 8px;
+        overflow: hidden;
+        background: #dfeaf5;
+
+        img {
+            display: block;
+            width: 100%;
+            height: 148px;
+            object-fit: cover;
+        }
+
+        &--guide img {
+            height: 132px;
+        }
+    }
+
+    &__bbox {
+        position: absolute;
+        top: 36%;
+        left: 50%;
+        width: 72px;
+        height: 72px;
+        transform: translate(-50%, -50%);
+        border: 2px solid #2199f8;
+        box-sizing: border-box;
+        pointer-events: none;
+
+        span {
+            position: absolute;
+            top: -18px;
+            left: 50%;
+            transform: translateX(-50%);
+            padding: 0 4px;
+            font-size: 12px;
+            line-height: 16px;
+            color: #2199f8;
+            white-space: nowrap;
+            background: rgba(255, 255, 255, 0.85);
+        }
+    }
+
+    &__detect {
+        position: absolute;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        padding: 8px 10px;
+        background: rgba(0, 0, 0, 0.55);
+        color: #fff;
+        font-size: 12px;
+        line-height: 18px;
+    }
+
+    &__guide-btn {
+        position: absolute;
+        top: 8px;
+        right: 8px;
+        height: 28px;
+        padding: 0 10px;
+        border-radius: 14px;
+        background: rgba(0, 0, 0, 0.55);
+        color: #fff;
+        font-size: 12px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+
+    &__options {
+        margin-top: 12px;
+        display: flex;
+        gap: 8px;
+    }
+
+    &__option {
+        position: relative;
+        flex: 1;
+        height: 36px;
+        border-radius: 8px;
+        background: #fff;
+        border: 1px solid transparent;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 13px;
+        color: #333;
+        box-sizing: border-box;
+
+        &.active {
+            border-color: #2199f8;
+            color: #2199f8;
+        }
+    }
+
+    &__option-check {
+        position: absolute;
+        top: 0;
+        right: 0;
+        width: 16px;
+        height: 16px;
+        border-radius: 0 6px 0 6px;
+        background: #2199f8;
+        color: #fff;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 10px;
+    }
+
+    &__date-picker {
+        width: 100%;
+
+        :deep(.el-input__wrapper) {
+            height: 40px;
+            border-radius: 8px;
+            box-shadow: none !important;
+            background: #fff;
+            padding: 0 12px;
+        }
+
+        :deep(.el-input__inner) {
+            font-size: 14px;
+            color: #333;
+        }
+
+        :deep(.el-input__prefix),
+        :deep(.el-input__suffix) {
+            color: rgba(0, 0, 0, 0.35);
+        }
+    }
+
+    &__actions {
+        margin-top: 12px;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        gap: 12px;
+    }
+
+    &__btn {
+        height: 40px;
+        line-height: 40px;
+        padding: 0 30px;
+        border-radius: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 16px;
+        font-weight: 500;
+        box-sizing: border-box;
+
+        &--ghost {
+            background: rgba(236, 236, 236, 0.5);
+            color: rgba(0, 0, 0, 0.6);
+        }
+
+        &--primary {
+            background: linear-gradient(180deg, #76C3FF 0%, #2199F8 100%);
+            color: #fff;
+        }
+    }
+}
+</style>

+ 24 - 0
src/i18n/messages.js

@@ -185,6 +185,16 @@ export default {
             agriAlbum: "农情相册",
             diagnosisReport: "诊断报告",
             reportUpdate: "更新",
+            restoreFillQuestions: "填写以下问题",
+            restorePlantingService: "立即恢复种植服务",
+            selectTime: "选择时间",
+            changeQuestion: "更换问题",
+            confirmNow: "立即确认",
+            submitInfo: "提交信息",
+            viewPatrolGuide: "查看巡园向导",
+            notReached: "暂未到达",
+            reached: "已到达",
+            passed: "已过",
             reportTitle: "标题",
             reportDesc: "主题主题主题主题主题主题主题主题主题主题主题主题主题主题主题主题",
             reportDate: "8月30日",
@@ -201,6 +211,7 @@ export default {
             customizeZoneHint: "为了更好记录长势,请自定义分区",
             zoneTwo: "分区二",
             photoCount: "{count}张",
+            emptyAlbumTip: "上传作物照片,记录作物长势",
             zoneLabel: "分区{n}",
             zoneOne: "分区一",
             legendZone: "管理分区",
@@ -291,6 +302,7 @@ export default {
             notes: "注意事项",
             prescription: "药物处方",
             executionMethod: "执行方式",
+            executeParameters: "执行参数",
         },
         workExecute: {
             pending: "待接受",
@@ -585,6 +597,16 @@ export default {
             agriAlbum: "Agri album",
             diagnosisReport: "Diagnosis report",
             reportUpdate: "Update",
+            restoreFillQuestions: "Answer the questions",
+            restorePlantingService: "Restore planting service",
+            selectTime: "Select time",
+            changeQuestion: "Change question",
+            confirmNow: "Confirm now",
+            submitInfo: "Submit",
+            viewPatrolGuide: "View patrol guide",
+            notReached: "Not reached",
+            reached: "Reached",
+            passed: "Passed",
             reportTitle: "Title",
             reportDesc: "Theme theme theme theme theme theme theme theme theme theme theme theme",
             reportDate: "Aug 30",
@@ -601,6 +623,7 @@ export default {
             customizeZoneHint: "Customize zones to better record growth",
             zoneTwo: "Zone 2",
             photoCount: "{count} photos",
+            emptyAlbumTip: "Upload crop photos to track growth",
             zoneLabel: "Zone {n}",
             zoneOne: "Zone 1",
             legendZone: "Management zone",
@@ -691,6 +714,7 @@ export default {
             notes: "Notes",
             prescription: "Rx",
             executionMethod: "Method",
+            executeParameters: "Parameters",
         },
         workExecute: {
             pending: "Pending",

+ 24 - 5
src/views/old_mini/agri_file/index.vue

@@ -72,7 +72,7 @@
                     </div>
                 </div>
                 <div class="album-grid">
-                    <div class="album-add">
+                    <div class="album-add" @click.stop="goAddZone">
                         <el-icon class="album-add__icon">
                             <Plus />
                         </el-icon>
@@ -113,6 +113,11 @@
         <album-upload-popup v-model:show="showUploadPopup" />
         <complete-farm-info-popup v-model:show="showCompleteFarmPopup" @confirm="goCompleteFarmInfo" />
         <diagnosis-report-popup v-model:show="showDiagnosisReportPopup" @confirm="goDiagnosisReport" />
+        <restore-planting-popup
+            v-model:show="showRestorePlantingPopup"
+            @change-question="handleChangeRestoreQuestion"
+            @confirm="handleRestorePlantingConfirm"
+        />
     </div>
 </template>
 
@@ -129,6 +134,7 @@ import farmHeader from "@/components/farmHeader.vue";
 import albumUploadPopup from "./components/albumUploadPopup.vue";
 import completeFarmInfoPopup from "@/components/popup/completeFarmInfoPopup.vue";
 import diagnosisReportPopup from "@/components/popup/diagnosisReportPopup.vue";
+import restorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
 import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
 
 const { t } = useI18n();
@@ -186,6 +192,8 @@ const reportList = ref([
 const showUploadPopup = ref(false);
 const showCompleteFarmPopup = ref(false);
 const showDiagnosisReportPopup = ref(false);
+// 后续由接口控制是否展示恢复种植服务弹窗
+const showRestorePlantingPopup = ref(false);
 const patrolList = ref([
     {
         id: 1,
@@ -322,11 +330,22 @@ const goDiagnosisReport = () => {
     activeNav.value = "report";
 };
 
+const handleChangeRestoreQuestion = () => {
+    // 后续对接接口更换问题
+};
+
+const handleRestorePlantingConfirm = () => {
+    // 后续对接接口提交时间
+};
+
 const goAlbumDetail = (item) => {
-    if (!item.count) return;
     router.push({
         path: "/region_albums",
-        query: { id: item?.id },
+        query: {
+            id: item?.id,
+            name: item?.name || "",
+            noImage: item?.count ? "false" : "true",
+        },
     });
 };
 
@@ -399,7 +418,7 @@ watch(activeNav, (key) => {
                     content: "";
                     position: absolute;
                     left: 50%;
-                    bottom: -6px;
+                    bottom: -5px;
                     transform: translateX(-50%);
                     border-left: 6px solid transparent;
                     border-right: 6px solid transparent;
@@ -517,7 +536,7 @@ watch(activeNav, (key) => {
                 width: 100%;
                 aspect-ratio: 1;
                 border-radius: 5px;
-                background: #F5F5F7;
+                background: #FFFFFF;
                 color: #969494;
                 font-size: 16px;
 

+ 0 - 36
src/views/old_mini/agri_file/pages/addZone.vue

@@ -4,12 +4,6 @@
         <div class="add-zone-content">
             <div class="map-container" ref="mapContainer"></div>
             <div class="search-bar">
-                <div class="search-bar__city">
-                    <span class="search-bar__city-name van-ellipsis">{{ cityName }}</span>
-                    <el-icon class="search-bar__city-icon">
-                        <ArrowDown />
-                    </el-icon>
-                </div>
                 <location-search class="search-bar__search" :user-location="userLocation"
                     @change="handleLocationChange" />
             </div>
@@ -31,7 +25,6 @@ import { nextTick, onActivated, onDeactivated, ref } from "vue";
 import { useRouter } from "vue-router";
 import { useStore } from "vuex";
 import { ElMessage } from "element-plus";
-import { ArrowDown } from "@element-plus/icons-vue";
 import customHeader from "@/components/customHeader.vue";
 import locationSearch from "@/components/pageComponents/locationSearch.vue";
 import RegionNamePopup from "@/views/old_mini/recordDetails/components/RegionNamePopup.vue";
@@ -53,7 +46,6 @@ const ZONE_STYLE = {
     stroke: "#2199F8",
 };
 
-const cityName = ref("");
 const userLocation = ref(
     store.state.home.miniUserLocation ||
     localStorage.getItem("MINI_USER_LOCATION") ||
@@ -122,11 +114,6 @@ function getDefaultMapCoordinate() {
     return util.wktCastGeom(getDefaultMapLocation()).getFirstCoordinate();
 }
 
-function loadCityName() {
-    const farmData = getFarmData();
-    cityName.value = farmData.city || farmData.farm_city || t("agriFile.defaultCity");
-}
-
 function initMapView() {
     if (!mapContainer.value) return;
     mapManage.destroyMap();
@@ -140,9 +127,6 @@ function initMapView() {
 const handleLocationChange = (payload) => {
     if (!payload?.coordinateArray) return;
     mapManage.setMapPosition(payload.coordinateArray);
-    if (payload.city) {
-        cityName.value = payload.city;
-    }
 };
 
 const handleLocate = () => {
@@ -177,7 +161,6 @@ const handleRegionNameConfirm = (regionName) => {
 };
 
 onActivated(() => {
-    loadCityName();
     nextTick(() => initMapView());
 });
 
@@ -221,28 +204,9 @@ onDeactivated(() => {
         background: rgba(0, 0, 0, 0.45);
         box-sizing: border-box;
 
-        &__city {
-            display: flex;
-            align-items: center;
-            flex-shrink: 0;
-            max-width: 92px;
-            color: #fff;
-            font-size: 14px;
-        }
-
-        &__city-name {
-            max-width: 72px;
-        }
-
-        &__city-icon {
-            margin-left: 2px;
-            font-size: 12px;
-        }
-
         &__search {
             flex: 1;
             min-width: 0;
-            margin-left: 8px;
 
             :deep(.el-select__wrapper) {
                 background: transparent;

+ 77 - 13
src/views/old_mini/agri_file/pages/regionAlbums.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="region-albums">
         <div class="region-albums__hero" :style="heroStyle">
-            <div class="region-albums__hero-mask"></div>
+            <div class="region-albums__hero-mask" :class="{ 'is-light': isEmpty }"></div>
             <div class="region-albums__nav" @click="goBack">
                 <el-icon class="region-albums__back-icon" color="#fff" :size="18">
                     <ArrowLeftBold />
@@ -9,14 +9,21 @@
             </div>
             <div class="region-albums__hero-info">
                 <span class="region-albums__title">{{ regionName }}</span>
-                <span class="region-albums__count">{{ totalCount }}张</span>
+                <span class="region-albums__count">{{ t("agriFile.photoCount", { count: totalCount }) }}</span>
             </div>
         </div>
 
         <div class="region-albums__panel" v-loading="loading">
-            <div v-if="!loading && !dateGroups.length" class="region-albums__empty">暂无相册照片</div>
+            <div v-if="!loading && isEmpty" class="region-albums__empty">
+                <img
+                    class="region-albums__empty-icon"
+                    src="@/assets/img/agricultural/empty.png"
+                    alt=""
+                />
+                <div class="region-albums__empty-text">{{ t("agriFile.emptyAlbumTip") }}</div>
+            </div>
 
-            <div class="region-albums__album-list">
+            <div v-else class="region-albums__album-list">
                 <div
                     v-for="group in dateGroups"
                     :key="group.date"
@@ -61,14 +68,16 @@
 </template>
 
 <script setup>
-import { computed, onMounted, ref } from "vue";
+import { computed, onActivated, onMounted, ref } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { ElMessage } from "element-plus";
 import { ArrowLeftBold, Plus } from "@element-plus/icons-vue";
 import { base_img_url2, resize_300 } from "@/api/config";
+import { useI18n } from "@/i18n";
 import ImagePreviewPopup from "@/components/popup/ImagePreviewPopup.vue";
 import albumUploadPopup from "../components/albumUploadPopup.vue";
 
+const { t } = useI18n();
 const route = useRoute();
 const router = useRouter();
 
@@ -85,16 +94,20 @@ const previewIndex = ref(0);
 const showUploadPopup = ref(false);
 
 const regionName = computed(
-    () => route.query.regionName || route.query.name || "分区一"
+    () => route.query.regionName || route.query.name || t("agriFile.zoneOne")
 );
 
 const organId = computed(() => route.query.organId || TEMP_ORGAN_ID);
 const areaId = computed(() => route.query.areaId || TEMP_AREA_ID);
 const queryDate = computed(() => route.query.date || TEMP_DATE);
+const forceEmpty = computed(
+    () => route.query.noImage === "true" || route.query.noImage === true
+);
 
 const totalCount = computed(() => imageList.value.length);
 
 const heroCover = computed(() => {
+    if (forceEmpty.value || !imageList.value.length) return "";
     const first = imageList.value[0];
     if (first?.baseMap) return first.baseMap;
     if (first?.url) return first.url;
@@ -104,8 +117,7 @@ const heroCover = computed(() => {
 const heroStyle = computed(() => {
     if (!heroCover.value) {
         return {
-            background:
-                "linear-gradient(180deg, rgba(34, 120, 68, 0.95) 0%, rgba(20, 80, 48, 0.9) 100%)",
+            background: "linear-gradient(180deg, #8ec8f5 0%, #5aadeb 55%, #3d9ae0 100%)",
         };
     }
     return {
@@ -153,6 +165,10 @@ const dateGroups = computed(() => {
     return [...map.values()].sort((a, b) => String(b.date).localeCompare(String(a.date)));
 });
 
+const isEmpty = computed(
+    () => forceEmpty.value || (!loading.value && !imageList.value.length)
+);
+
 const normalizeList = (raw) => {
     const list = Array.isArray(raw)
         ? raw
@@ -171,6 +187,11 @@ const normalizeList = (raw) => {
 };
 
 const fetchAlbumImages = async () => {
+    if (forceEmpty.value) {
+        imageList.value = [];
+        loading.value = false;
+        return;
+    }
     loading.value = true;
     try {
         // TODO: 临时接口,后续替换正式相册接口
@@ -262,8 +283,22 @@ const goBack = () => {
     router.back();
 };
 
-onMounted(() => {
+const syncAlbumByRoute = () => {
+    // 空相册:立即清空缓存照片,避免 keepAlive 残留上一相册数据
+    if (forceEmpty.value) {
+        imageList.value = [];
+        loading.value = false;
+        return;
+    }
     fetchAlbumImages();
+};
+
+onMounted(() => {
+    syncAlbumByRoute();
+});
+
+onActivated(() => {
+    syncAlbumByRoute();
 });
 </script>
 
@@ -290,6 +325,17 @@ onMounted(() => {
             rgba(0, 0, 0, 0.05) 45%,
             rgba(0, 0, 0, 0.35) 100%
         );
+
+        &.is-light {
+            // background: linear-gradient(
+            //     180deg,
+            //     rgba(255, 255, 255, 0.08) 0%,
+            //     rgba(0, 0, 0, 0.06) 100%
+            // );
+
+            background: linear-gradient(180deg, #d2ebff 0%, #c2dcef 70%, #8ca1b2 100%)
+
+        }
     }
 
     &__nav {
@@ -349,10 +395,28 @@ onMounted(() => {
     }
 
     &__empty {
-        padding: 60px 0;
-        text-align: center;
-        color: rgba(0, 0, 0, 0.35);
+        height: 100%;
+        min-height: 320px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        padding: 40px 20px 80px;
+        box-sizing: border-box;
+    }
+
+    &__empty-icon {
+        width: 58px;
+        height: 56px;
+        object-fit: contain;
+    }
+
+    &__empty-text {
+        margin-top: 16px;
         font-size: 14px;
+        line-height: 20px;
+        color: rgba(0, 0, 0, 0.71);
+        text-align: center;
     }
 
     &__album-list {
@@ -365,7 +429,7 @@ onMounted(() => {
     &__fab {
         position: fixed;
         right: 18px;
-        bottom: 28px;
+        bottom: 148px;
         z-index: 20;
         width: 56px;
         height: 56px;

+ 1 - 0
src/views/old_mini/entry_information/map/selectLocationMap.js

@@ -15,6 +15,7 @@ export let mapLocation = reactive({
 export const POINT_ICON = {
   resident: require("@/assets/img/map/current-point.png"),
   plant: require("@/assets/img/map/zhongzhi-point.png"),
+  current: require("@/assets/img/home/garden-point.png"),
 };
 
 /**

+ 13 - 4
src/views/old_mini/entry_information/selectLocation.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="select-location">
-        <custom-header name="选择种植点位"></custom-header>
+        <custom-header :name="fromGrowthReport ? '切换位置' : '选择种植点位'"></custom-header>
         <div class="select-location-content">
             <div class="search-bar">
                 <location-search
@@ -30,7 +30,8 @@ import locationSearch from "@/components/pageComponents/locationSearch.vue";
 import SelectLocationMap, { mapLocation, POINT_ICON } from "./map/selectLocationMap.js";
 import { convertPointToArray } from "@/utils/index";
 
-const SESSION_KEY = "ENTRY_RESIDENT_LOCATION";
+const SESSION_KEY_RESIDENT = "ENTRY_RESIDENT_LOCATION";
+const SESSION_KEY_GROWTH = "GROWTH_REPORT_LOCATION";
 const DEFAULT_POINT = "POINT(113.6142086995688 23.585836479509055)";
 
 const router = useRouter();
@@ -38,8 +39,16 @@ const route = useRoute();
 const store = useStore();
 const mapContainer = ref(null);
 const isPlantPoint = route.query.pointType === "plant";
+const fromGrowthReport = route.query.from === "growth_report";
+const sessionKey = fromGrowthReport ? SESSION_KEY_GROWTH : SESSION_KEY_RESIDENT;
+const iconSrc =
+    route.query.iconType && POINT_ICON[route.query.iconType]
+        ? POINT_ICON[route.query.iconType]
+        : isPlantPoint
+          ? POINT_ICON.plant
+          : POINT_ICON.resident;
 const selectLocationMap = new SelectLocationMap({
-    iconSrc: isPlantPoint ? POINT_ICON.plant : POINT_ICON.resident,
+    iconSrc,
 });
 
 const userLocation = ref(
@@ -92,7 +101,7 @@ const handleSubmit = () => {
     }
     const point = toPointWkt(coordinate);
     sessionStorage.setItem(
-        SESSION_KEY,
+        sessionKey,
         JSON.stringify({
             point,
             coordinate: [Number(coordinate[0]), Number(coordinate[1])],

+ 134 - 35
src/views/old_mini/growth_report/components/CropAlertPanel.vue

@@ -19,42 +19,56 @@
                             <span class="crop-name">{{ currentCropName }}</span>
                             <span class="crop-badge">当前作物</span>
                         </div>
-                        <el-tooltip
-                            v-model:visible="cropMenuVisible"
-                            placement="bottom"
-                            trigger="click"
-                            effect="light"
-                            :show-arrow="true"
-                            popper-class="crop-switch-tooltip"
-                            :teleported="true"
-                        >
-                            <template #content>
-                                <div class="crop-option-list">
-                                    <div
-                                        v-for="item in cropOptions"
-                                        :key="item"
-                                        class="crop-option-item"
-                                        :class="{ active: item === currentCropName }"
-                                        @click="handleSelectCrop(item)"
-                                    >
-                                        {{ item }}
+                        <div class="switch-wrap">
+                            <el-tooltip
+                                v-model:visible="cropMenuVisible"
+                                placement="bottom"
+                                trigger="click"
+                                effect="light"
+                                :show-arrow="true"
+                                popper-class="crop-switch-tooltip"
+                                :teleported="true"
+                            >
+                                <template #content>
+                                    <div class="crop-option-list">
+                                        <div
+                                            v-for="item in cropOptions"
+                                            :key="item"
+                                            class="crop-option-item"
+                                            :class="{ active: item === currentCropName }"
+                                            @click="handleSelectCrop(item)"
+                                        >
+                                            {{ item }}
+                                        </div>
                                     </div>
+                                </template>
+                                <div
+                                    class="switch-btn"
+                                    :class="{ 'is-open': cropMenuVisible }"
+                                    @click="dismissGuide"
+                                >
+                                    <span>{{ t("growthReport.switchCategory") }}</span>
+                                    <el-icon
+                                        class="switch-btn__icon"
+                                        :class="{ 'is-open-icon': cropMenuVisible }"
+                                    >
+                                        <CaretBottom />
+                                    </el-icon>
+                                </div>
+                            </el-tooltip>
+                            <div v-if="guideVisible" class="crop-guide-bubble">
+                                <span class="crop-guide-bubble__arrow"></span>
+                                <div class="crop-guide-bubble__content">
+                                    当下作物为<span class="crop-guide-bubble__crop">{{ currentCropName }}</span>,请录入拟种植的作物,之后变为对应作物的气象预警
                                 </div>
-                            </template>
-                            <div class="switch-btn" :class="{ 'is-open': cropMenuVisible }">
-                                <span>{{ t("growthReport.switchCategory") }}</span>
-                                <icon
-                                    :name="cropMenuVisible ? 'arrow-up' : 'arrow-down'"
-                                    class="switch-btn__icon"
-                                />
                             </div>
-                        </el-tooltip>
+                        </div>
                     </div>
 
                     <div class="alert-list" v-show="!isBackground">
                         <div
-                            v-for="item in alertCards"
-                            :key="item.id"
+                            v-for="(item, index) in alertCards"
+                            :key="index"
                             class="alert-card"
                         >
                             <div class="alert-card__header">
@@ -86,13 +100,17 @@
 </template>
 
 <script setup>
-import { FloatingPanel, Icon } from "vant";
-import { Link } from "@element-plus/icons-vue";
+import { FloatingPanel } from "vant";
+import { CaretBottom, Link } from "@element-plus/icons-vue";
 import { nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
 import { useI18n } from "@/i18n";
+import { useRouter } from "vue-router";
 
+const router = useRouter();
 const { t } = useI18n();
 
+const GUIDE_STORAGE_KEY = "GROWTH_REPORT_CROP_SWITCH_GUIDE";
+
 const props = defineProps({
     cropName: {
         type: String,
@@ -108,13 +126,14 @@ const props = defineProps({
     },
 });
 
-const emit = defineEmits(["switchCategory", "viewDetail", "patrolTip", "heightChange"]);
+const emit = defineEmits(["switchCategory", "viewDetail", "heightChange"]);
 
 const weatherIcon = require("@/assets/img/report/weather.png");
 const tabBarHeight = ref(Number(localStorage.getItem("tabBarHeight")) || 50);
 
 const currentCropName = ref(props.cropName);
 const cropMenuVisible = ref(false);
+const guideVisible = ref(false);
 
 watch(
     () => props.cropName,
@@ -123,7 +142,30 @@ watch(
     }
 );
 
+const dismissGuide = () => {
+    if (!guideVisible.value) return;
+    guideVisible.value = false;
+    localStorage.setItem(GUIDE_STORAGE_KEY, "1");
+};
+
+const tryShowGuide = () => {
+    if (localStorage.getItem(GUIDE_STORAGE_KEY)) return;
+    guideVisible.value = true;
+};
+
+const handleOutsideClick = (event) => {
+    if (!guideVisible.value) return;
+    const target = event.target;
+    if (target?.closest?.(".crop-guide-bubble") || target?.closest?.(".switch-btn")) return;
+    dismissGuide();
+};
+
+watch(cropMenuVisible, (visible) => {
+    if (visible) dismissGuide();
+});
+
 const handleSelectCrop = (name) => {
+    dismissGuide();
     if (!name || name === currentCropName.value) {
         cropMenuVisible.value = false;
         return;
@@ -181,7 +223,8 @@ const alertCards = [
         patrolTip: "",
     },
     {
-        id: "stress",
+        id: 2,
+        type: "abnormal",
         title: "某某胁迫",
         icon: weatherIcon,
         content: "详情详情详情具体预警具体预警情详情详详情详情详情详情详情详情",
@@ -194,7 +237,13 @@ const handleViewDetail = (item) => {
 };
 
 const handlePatrolTip = (item) => {
-    emit("patrolTip", item);
+    router.push({
+        path: "/growth_track",
+        query: {
+            id: item.id,
+            type: item.type || "growth",
+        },
+    });
 };
 
 const handleHeightChange = ({ height: nextHeight }) => {
@@ -210,6 +259,7 @@ onMounted(() => {
             setTimeout(() => {
                 syncMidAnchor();
                 emit("heightChange", height.value);
+                tryShowGuide();
             }, 80);
         });
         if (!panelShellRef.value || typeof ResizeObserver === "undefined") return;
@@ -219,9 +269,11 @@ onMounted(() => {
         });
         resizeObserver.observe(panelShellRef.value);
     });
+    document.addEventListener("click", handleOutsideClick, true);
 });
 
 onBeforeUnmount(() => {
+    document.removeEventListener("click", handleOutsideClick, true);
     resizeObserver?.disconnect?.();
     resizeObserver = null;
 });
@@ -307,6 +359,49 @@ onBeforeUnmount(() => {
         line-height: 20px;
     }
 
+    .switch-wrap {
+        position: relative;
+        flex-shrink: 0;
+        z-index: 5;
+    }
+
+    .crop-guide-bubble {
+        position: absolute;
+        top: calc(100% + 10px);
+        right: 20px;
+        width: 216px;
+        padding: 6px 10px;
+        border-radius: 6px;
+        background: rgba(0, 0, 0, 0.8);
+        box-sizing: border-box;
+        z-index: 20;
+        pointer-events: auto;
+
+        &__arrow {
+            position: absolute;
+            top: -5px;
+            right: 12px;
+            width: 0;
+            height: 0;
+            border-left: 6px solid transparent;
+            border-right: 6px solid transparent;
+            border-bottom: 6px solid rgba(0, 0, 0, 0.8);
+        }
+
+        &__content {
+            font-size: 14px;
+            line-height: 22px;
+            color: #fff;
+            word-break: break-all;
+            text-align: justify;
+        }
+
+        &__crop {
+            padding: 0 2px;
+            color: #2199f8;
+        }
+    }
+
     .switch-btn {
         display: flex;
         align-items: center;
@@ -326,8 +421,12 @@ onBeforeUnmount(() => {
         }
 
         &__icon {
-            font-size: 12px;
+            font-size: 14px;
             color: #2199f8;
+            transition: transform 0.3s ease;
+        }
+        .is-open-icon {
+            transform: rotate(180deg);
         }
     }
 

+ 16 - 1
src/views/old_mini/growth_report/growthReportMap.js

@@ -44,7 +44,7 @@ class IndexMap {
     this.kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
     this.kmap.addLayer(this.clickPointLayer.layer);
     this.setMapPoint(coordinate)
-    this.addMapSingerClick()
+    // this.addMapSingerClick()
   }
 
   setMapPosition(center) {
@@ -60,6 +60,21 @@ class IndexMap {
     this.clickPointLayer.source.clear()
     let point = new Feature(new Point(coordinate))
     this.clickPointLayer.addFeature(point)
+    mapLocation.data = coordinate
+  }
+
+  /** 当前地图点位(优先标记点,其次视图中心) */
+  getCurrentCoordinate() {
+    const features = this.clickPointLayer?.source?.getFeatures?.() || [];
+    if (features.length) {
+      return features[0].getGeometry()?.getCoordinates?.();
+    }
+    return this.kmap?.getView?.()?.getCenter?.() || mapLocation.data || null;
+  }
+
+  toPointWkt(coordinate) {
+    if (!coordinate?.length) return null;
+    return `POINT(${Number(coordinate[0])} ${Number(coordinate[1])})`;
   }
 
   // 地图点击事件

+ 71 - 14
src/views/old_mini/growth_report/index.vue

@@ -5,10 +5,10 @@
         </div>
 
         <!-- 左上角:位置切换 -->
-        <div class="location-bar" @click="handleSwitchLocation">
+        <div class="location-bar">
             <el-icon class="location-bar__icon"><LocationFilled /></el-icon>
             <span class="location-bar__name van-ellipsis">{{ locationName }}</span>
-            <span class="location-bar__action">切换位置</span>
+            <span class="location-bar__action" @click="handleSwitchLocation">切换位置</span>
         </div>
 
         <!-- 左侧:胁迫图层菜单 -->
@@ -17,8 +17,8 @@
                 v-for="item in stressMenuItems"
                 :key="item.key"
                 class="side-menu__item"
-                :class="{ active: activeStressKey === item.key }"
-                @click="activeStressKey = item.key"
+                :class="{ active: activeMenuKey === item.key }"
+                @click="handleSelectMenu(item.key)"
             >
                 <img class="side-menu__icon" :src="item.icon" alt="" />
                 <div class="side-menu__text">
@@ -34,8 +34,8 @@
                 v-for="item in layerMenuItems"
                 :key="item.key"
                 class="side-menu__item"
-                :class="{ active: activeLayerKey === item.key }"
-                @click="activeLayerKey = item.key"
+                :class="{ active: activeMenuKey === item.key }"
+                @click="handleSelectMenu(item.key)"
             >
                 <img class="side-menu__icon" :src="item.icon" alt="" />
                 <div class="side-menu__text">
@@ -79,7 +79,6 @@
 import { computed, nextTick, onActivated, onMounted, ref } from "vue";
 import { useRouter } from "vue-router";
 import { useStore } from "vuex";
-import { ElMessage } from "element-plus";
 import { LocationFilled } from "@element-plus/icons-vue";
 import { convertPointToArray } from "@/utils/index";
 import GrowthReportMap from "./growthReportMap.js";
@@ -90,6 +89,8 @@ const router = useRouter();
 
 const DEFAULT_MAP_POINT = "POINT(113.6142086995688 23.585836479509055)";
 const MAP_KEY = "CZLBZ-LJICQ-R4A5J-BN62X-YXCRJ-GNBUT";
+const LOCATION_SESSION_KEY = "GROWTH_REPORT_LOCATION";
+const MAP_POINT_STORAGE_KEY = "GROWTH_REPORT_MAP_POINT";
 
 const menuIcon = require("@/assets/img/common/farm-active.png");
 
@@ -105,9 +106,14 @@ const tabBarHeight = computed(() => store.state.home.tabBarHeight);
 const mapContainer = ref(null);
 const growthReportMap = new GrowthReportMap();
 const locationName = ref("");
+const currentMapPoint = ref(
+    localStorage.getItem(MAP_POINT_STORAGE_KEY) ||
+        localStorage.getItem("selectedFarmPoint") ||
+        store.state.home.miniUserLocationPoint ||
+        DEFAULT_MAP_POINT
+);
 const currentCropName = ref("水稻");
-const activeStressKey = ref("hot-drought-2");
-const activeLayerKey = ref("growth");
+const activeMenuKey = ref("hot-drought-2");
 const panelHeight = ref(280);
 const inviteBottom = computed(() => panelHeight.value);
 
@@ -123,8 +129,8 @@ const layerMenuItems = [
     { key: "phenology", line1: "物候", line2: "进程", icon: menuIcon },
 ];
 
-function getLocationName() {
-    const locationPoint = localStorage.getItem("selectedFarmPoint") || store.state.home.miniUserLocationPoint;
+function getLocationName(point = currentMapPoint.value) {
+    const locationPoint = point || localStorage.getItem("selectedFarmPoint") || store.state.home.miniUserLocationPoint;
     if (!locationPoint) return;
     const farmLocation = convertPointToArray(locationPoint);
     if (!farmLocation?.length) return;
@@ -139,20 +145,71 @@ function getLocationName() {
     });
 }
 
+function saveCurrentMapPoint(point) {
+    if (!point) return;
+    currentMapPoint.value = point;
+    localStorage.setItem(MAP_POINT_STORAGE_KEY, point);
+}
+
 const initMap = async () => {
     await nextTick();
     if (!mapContainer.value) return;
     if (growthReportMap.kmap) {
         growthReportMap.kmap.map?.updateSize?.();
+        const coordinate = convertPointToArray(currentMapPoint.value)?.map(Number);
+        if (coordinate?.length === 2) {
+            growthReportMap.setMapPosition(coordinate);
+        }
         return;
     }
-    const location = store.state.home.miniUserLocationPoint || DEFAULT_MAP_POINT;
+    const location = currentMapPoint.value || store.state.home.miniUserLocationPoint || DEFAULT_MAP_POINT;
+    currentMapPoint.value = location;
     growthReportMap.initMap(location, mapContainer.value);
 };
 
+function applySelectedLocation() {
+    const raw = sessionStorage.getItem(LOCATION_SESSION_KEY);
+    if (!raw) return false;
+    sessionStorage.removeItem(LOCATION_SESSION_KEY);
+    let saved = null;
+    try {
+        saved = JSON.parse(raw);
+    } catch {
+        return false;
+    }
+    if (!saved?.point && !saved?.coordinate) return false;
+
+    const point = saved.point || `POINT(${saved.coordinate[0]} ${saved.coordinate[1]})`;
+    const coordinate = (saved.coordinate || convertPointToArray(point)).map(Number);
+    saveCurrentMapPoint(point);
+
+    if (growthReportMap.kmap && coordinate?.length === 2) {
+        growthReportMap.setMapPosition(coordinate);
+    }
+    getLocationName(point);
+    return true;
+}
+
 const handleSwitchLocation = () => {
+    // 以已确认的 currentMapPoint 为准,避免地图临时中心覆盖已选位置
+    const mapCenter =
+        currentMapPoint.value ||
+        store.state.home.miniUserLocationPoint ||
+        DEFAULT_MAP_POINT;
+    router.push({
+        path: "/entry_select_location",
+        query: {
+            mapCenter,
+            iconType: "current",
+            from: "growth_report",
+        },
+    });
 };
 
+const handleSelectMenu = (key) => {
+    if (!key) return;
+    activeMenuKey.value = key;
+};
 
 const handleInvite = (item) => {
     let inviteName = "好友";
@@ -190,7 +247,6 @@ const handleViewDetail = (item) => {
 };
 
 const handlePatrolTip = () => {
-    ElMessage.info("巡园要点功能即将开放");
 };
 
 const handlePanelHeightChange = (height) => {
@@ -202,7 +258,8 @@ onMounted(() => {
     initMap();
 });
 onActivated(() => {
-    getLocationName();
+    const applied = applySelectedLocation();
+    if (!applied) getLocationName();
     initMap();
 });
 </script>

+ 26 - 44
src/views/old_mini/work_detail/index.vue

@@ -5,7 +5,7 @@
 
         <div class="work-detail-content">
             <!-- 顶部状态 -->
-            <div class="content-status" :style="getContentStatusStyle(farmData)">
+            <div class="content-status">
                 <div class="status-l">
                     <div class="status-title">
                         <span>{{ farmData.work_name }}</span>
@@ -46,7 +46,7 @@
                             </div>
                             <div class="map-container" ref="mapContainer"></div>
                         </div>
-                        <div class="area-list">
+                        <!-- <div class="area-list">
                             <div v-for="(zone, zoneIndex) in farmZones"
                                 :key="`${zone.zone_name || 'zone'}-${zoneIndex}`" class="area-item">
                                 <div class="area-l">
@@ -57,7 +57,7 @@
                                 </div>
                                 <div class="area-r" @click="handleExecute(farmData?.work_status, farmData?.farm_work_type)" :style="{background:farmData?.work_status === 6 ? '#37C11B' : '#2199F8'}">{{ farmData?.work_status === 6 ? '溯源认证' : '我已完成' }}</div>
                             </div>
-                        </div>
+                        </div> -->
                         <!-- <div class="ecological-plant-card">
                             <div class="ecological-plant-text">
                                 <div class="ecological-plant-line">
@@ -89,28 +89,19 @@
                     </div>
                     <div class="work-info">
                         <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.reason') }}</div>
-                            <div class="info-value">{{ farmData.work_reason_detail }}</div>
-                        </div>
-                        <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.purpose') }}
-                            </div>
-                            <div class="info-value">{{ farmData.work_purpose }}</div>
-                        </div>
-                        <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.notes') }}</div>
-                            <div class="info-value">{{ farmData.precautions }}</div>
-                        </div>
-                        <div class="info-item">
                             <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.prescription') }}
                             </div>
                             <div class="info-value">{{ farmData.drug_prescription }}</div>
                         </div>
                         <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.executionMethod')
+                            <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.executeParameters')
                                 }}</div>
                             <div class="info-value">{{ farmData.execution_method }}</div>
                         </div>
+                        <div class="info-item">
+                            <div class="info-title"><span class="title-block"></span>{{ $t('workDetail.notes') }}</div>
+                            <div class="info-value">{{ farmData.precautions }}</div>
+                        </div>
                     </div>
                 </div>
 
@@ -134,8 +125,8 @@
             </div>
         </div>
 
-        <div class="btn-fixed">
-            <div class="primary-btn">巡园要点</div>
+        <div class="btn-fixed" >
+            <div class="primary-btn" @click="goGrowthTrack">巡园要点</div>
         </div>
 
         <div class="footer-btn">
@@ -404,28 +395,6 @@ const getFarmWorkTypeColorStyle = (farmData) => {
     };
 };
 
-/** 顶部 content-status 背景色(::after 通过 CSS 变量读取) */
-const getContentStatusStyle = (farmData) => {
-    const type = Number(farmData?.farm_work_type);// 农事类型
-    const status = Number(farmData?.work_status);// 农事状态
-    let background = '#2199F8';
-
-    if(type === 3 || type === 4) {
-        background = '#FF943D';
-    } else if(type === 5) {
-        background = '#FF6A6A';
-    } else {
-        background = '#2199F8';
-    }
-
-    if(status === 3) {
-        background = '#C7C7C7';
-    }
-    return {
-        "--content-status-bg": background,
-    };
-};
-
 /**
  * 展示用「分段」列表:detail.pesticideList 或 prescriptionList 推导
  */
@@ -462,6 +431,16 @@ const handleViewArea = () => {
     });
 }
 
+const goGrowthTrack = () => {
+    router.push({
+        path: "/growth_track",
+        query: {
+            id: farmData.value?.id || route.query.id,
+            type: farmData.value?.type || "growth",
+        },
+    });
+};
+
 const handleBack = () => {
     router.back();
 };
@@ -557,11 +536,14 @@ const areaMap = new AreaMap();
 
 .content-status {
     position: relative;
-    padding: 12px 12px 0 12px;
+    padding: 12px;
     color: #fff;
     z-index: 1;
     height: 92px;
     box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
 
     &::after {
         content: "";
@@ -570,7 +552,7 @@ const areaMap = new AreaMap();
         left: 0;
         top: 0;
         height: 140px;
-        background: var(--content-status-bg, #2199F8);
+        background: #2199F8;
         width: 100%;
     }
 
@@ -592,7 +574,7 @@ const areaMap = new AreaMap();
         }
 
         .status-sub-wrap {
-            margin-top: 10px;
+            margin-top: 8px;
             display: flex;
             align-items: center;
             gap: 10px;

+ 1 - 1
src/views/old_mini/work_execute/index.vue

@@ -13,6 +13,7 @@
                     v-for="item in taskList"
                     :key="item.id"
                     class="task-card"
+                    @click.stop="handleViewDetail(item)"
                     :class="{ 'is-risk': item.isHighRisk }"
                 >
                     <div v-if="item.cornerTip" class="task-card__corner">{{ item.cornerTip }}</div>
@@ -127,7 +128,6 @@ const handleDateSelect = (date) => {
 };
 
 const handleForward = () => {
-    ElMessage.info(t("workExecute.forwardTip"));
 };
 
 const handleViewDetail = (item) => {