Browse Source

fix: 互动

lxf 2 weeks ago
parent
commit
8d90fa0689

+ 15 - 0
src/api/modules/questionnaire.js

@@ -46,9 +46,24 @@ module.exports = {
         url: config.base_new_url + "report/parse_interact",
         type: "get",
     },
+    // 异常内容查询
+    abnormalContent: {
+        url: config.base_new_url + "report/parse_anormal",
+        type: "get",
+    },
     // 物候调整确认
     adjustPhenology: {
         url: config.base_new_url + "report/adjust_phenophase",
         type: "get",
     },
+    // 是否需要弹窗诊断报告
+    getReportStatus: {
+        url: config.base_new_url + "report/get_popup_status",
+        type: "get",
+    },
+    // 修改诊断报告弹窗状态
+    setReportStatus: {
+        url: config.base_new_url + "report/set_popup_closed",
+        type: "post",
+    },
 }

+ 7 - 3
src/components/popup/diagnosisReportPopup.vue

@@ -18,7 +18,7 @@
 </template>
 
 <script setup>
-import { onMounted, ref, watch } from "vue";
+import { onActivated, onMounted, ref, watch } from "vue";
 import { useRouter } from "vue-router";
 import { Popup } from "vant";
 import { useI18n } from "@/i18n";
@@ -54,7 +54,7 @@ const markPopupShown = (tel) => {
     localStorage.setItem(getShownCacheKey(tel), "1");
 };
 
-/** 需先确认物候已到达,且 exists=1、本地未展示过时才弹窗 */
+/** 需先确认物候已到达,且 status=0、本地未展示过时才弹窗 */
 const checkShouldShow = async () => {
     const tel = readEntryFarmPhone();
     if (!tel || !hasPhenologyReached(tel) || hasShownPopup(tel)) {
@@ -63,7 +63,7 @@ const checkShouldShow = async () => {
     }
     try {
         const res = await VE_API.questionnaire.checkReportGenerated({ tel });
-        const shouldShow = res?.code === 200 && Number(res?.data?.exists) === 1;
+        const shouldShow = res?.code === 200 && Number(res?.data?.exists) === 2;
         visible.value = shouldShow;
         if (shouldShow) markPopupShown(tel);
     } catch {
@@ -87,6 +87,10 @@ onMounted(() => {
     checkShouldShow();
 });
 
+onActivated(() => {
+    console.log("rerereonActivated");
+});
+
 defineExpose({
     checkShouldShow,
 });

+ 8 - 2
src/components/popup/restorePlantingPopup.vue

@@ -45,6 +45,7 @@
                     :placeholder="t('agriFile.selectTime')"
                     :editable="false"
                     :clearable="false"
+                    :disabled-date="disabledFutureDate"
                     teleported
                     popper-class="restore-planting-date-popper"
                     style="width: 100%"
@@ -111,6 +112,7 @@ import { useI18n } from "@/i18n";
 import { base_img_url2 } from "@/api/config";
 
 const { t } = useI18n();
+const emit = defineEmits(["phenology-reached"]);
 
 // ---------------------------------------------------------------------------
 // 常量
@@ -165,6 +167,9 @@ const phenologyOptions = computed(() => [
 // ---------------------------------------------------------------------------
 // 工具函数
 // ---------------------------------------------------------------------------
+/** 禁用今天之后的日期 */
+const disabledFutureDate = (time) => time.getTime() > Date.now();
+
 const readEntryFarmData = () => {
     try {
         return JSON.parse(localStorage.getItem(ENTRY_FARM_DATA_KEY) || "null");
@@ -265,8 +270,8 @@ const checkShouldShow = async () => {
     userTel.value = tel;
 
     try {
-        const checkRes = await VE_API.questionnaire.checkReportGenerated({ tel });
-        if (checkRes?.code !== 200 || Number(checkRes?.data?.exists) === 2) {
+        const checkRes = await VE_API.questionnaire.getReportStatus({ tel });
+        if (checkRes?.code !== 200 || Number(checkRes?.data?.status) === 1) {
             visible.value = false;
             return;
         }
@@ -342,6 +347,7 @@ const confirmReachedPhenology = async () => {
         showMessage(res.msg || "提交成功", "success");
         visible.value = false;
         resetForm();
+        emit("phenology-reached");
         return true;
     } catch (err) {
         showMessage(err?.response?.data?.msg || err?.message || "提交失败,请稍后再试", "error");

+ 1 - 1
src/i18n/messages.js

@@ -182,7 +182,7 @@ export default {
             initialReport: "初次报告",
             forwardReport: "转发报告",
             confirmLeaveTitle: "确认要离开?",
-            confirmLeaveDesc1: "长势巡园比例还没回答,确定要走吗?",
+            confirmLeaveDesc1: "长势巡园互动问题,确定要走吗?",
             confirmLeaveDesc2: "少了这一步,估产数据就不准了哦~",
             continueRecord: "继续记录",
             confirmLeave: "确认离开",

+ 40 - 51
src/views/old_mini/agri_file/index.vue

@@ -2,21 +2,13 @@
     <div class="agri-file-page" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
         <farm-header />
         <div class="nav-cards">
-            <div
-                v-for="item in navCards"
-                :key="item.key"
-                class="nav-card-wrap"
-                :class="{ 'nav-card-wrap--report': item.key === 'report' }"
-            >
-                <div
-                    class="nav-card"
-                    :class="{ 'is-active': activeNav === item.key }"
-                    @click="handleNavClick(item)"
-                >
+            <div v-for="item in navCards" :key="item.key" class="nav-card-wrap"
+                :class="{ 'nav-card-wrap--report': item.key === 'report' }">
+                <div class="nav-card" :class="{ 'is-active': activeNav === item.key }" @click="handleNavClick(item)">
                     <img class="nav-card__icon" :src="item.icon" alt="" />
                     <span class="nav-card__label">{{ t(item.labelKey) }}</span>
                 </div>
-                <div v-if="item.key === 'report' && guideVisible" class="report-guide-bubble">
+                <!-- <div v-if="item.key === 'report' && guideVisible" class="report-guide-bubble">
                     <span class="report-guide-bubble__arrow"></span>
                     <div class="report-guide-bubble__content">
                         {{ t("agriFile.diagnosisReportGuidePrefix") }}
@@ -25,22 +17,20 @@
                         </span>
                         {{ t("agriFile.diagnosisReportGuideSuffix") }}
                     </div>
-                </div>
+                </div> -->
             </div>
         </div>
         <div class="content-panel">
             <div v-show="activeNav === 'patrol'">
-                <div
-                    v-for="(item, index) in patrolList"
-                    :key="index"
-                    class="tracking-item tracking-item--blue"
-                    @click="goGrowthTrack(item)"
-                >
+                <div v-for="(item, index) in patrolList" :key="index" class="tracking-item tracking-item--blue"
+                    @click="goGrowthTrack(item)">
                     <div class="tracking-item__header">
                         <div class="tracking-item__header-row">
                             <div class="tracking-item__header-left">
                                 <span class="tracking-item__level">
-                                    <el-icon><BellFilled /></el-icon>
+                                    <el-icon>
+                                        <BellFilled />
+                                    </el-icon>
                                     {{ t("agriFile.riskLevel2") }}
                                 </span>
                                 <span class="tracking-item__title van-ellipsis">
@@ -49,16 +39,10 @@
                                             ? t("agriFile.patrolAbnormalTitle")
                                             : t("agriFile.patrolGrowthTitle")
                                     }}
-                                    <span
-                                        v-if="item.crop_category_name"
-                                        class="tracking-item__zone"
-                                    >
+                                    <span v-if="item.crop_category_name" class="tracking-item__zone">
                                         ({{ item.crop_category_name }})
                                     </span>
-                                    <span
-                                        v-else-if="item.zone"
-                                        class="tracking-item__zone"
-                                    >
+                                    <span v-else-if="item.zone" class="tracking-item__zone">
                                         ({{ item.zone }})
                                     </span>
                                 </span>
@@ -114,10 +98,12 @@
                         </el-icon>
                         <span>{{ t("agriFile.addAlbum") }}</span>
                     </div> -->
-                    <div v-for="item in albumList" :key="item.id" class="album-card" :class="{ 'is-empty': !item.count }">
+                    <div v-for="item in albumList" :key="item.id" class="album-card"
+                        :class="{ 'is-empty': !item.count }">
                         <div class="album-card__cover" @click.stop="goAlbumDetail(item)">
                             <img v-if="item.count && item.cover" :src="item.cover" alt="" />
-                            <img v-else class="album-card__empty-icon" src="@/assets/img/agricultural/photo.png" alt="" />
+                            <img v-else class="album-card__empty-icon" src="@/assets/img/agricultural/photo.png"
+                                alt="" />
                             <span class="album-card__count">{{ t("agriFile.photoCount", { count: item.count }) }}</span>
                             <!-- <div class="album-card__add" @click.stop="openUploadPopup">
                                 <el-icon>
@@ -150,30 +136,20 @@
         <complete-farm-info-popup v-model:show="showCompleteFarmPopup" @confirm="goCompleteFarmInfo" />
 
         <!-- 诊断报告弹窗 -->
-        <diagnosis-report-popup />
+        <diagnosis-report-popup ref="diagnosisReportPopupRef" />
 
-        <!-- 恢复种植弹窗 -->
-        <restore-planting-popup />
+        <!-- 恢复种植弹窗:确认已到达后触发诊断报告弹窗检查 -->
+        <restore-planting-popup @phenology-reached="onPhenologyReached" />
 
         <!-- 代管弹窗:后续由接口控制是否展示 -->
-        <proxy-auth-popup
-            v-model:show="showProxyAuthPopup"
-            :service-name="proxyServiceName"
-            @reject="handleProxyReject"
-            @allow="handleProxyAllow"
-        />
+        <proxy-auth-popup v-model:show="showProxyAuthPopup" :service-name="proxyServiceName" @reject="handleProxyReject"
+            @allow="handleProxyAllow" />
 
         <!-- 申请更换物候期弹窗:后续由接口控制是否展示 -->
-        <phenology-update-popup
-            v-model:show="showPhenologyUpdatePopup"
-            :service-name="phenologyUpdateInfo.serviceName"
-            :current-stage="phenologyUpdateInfo.currentStage"
-            :target-stage="phenologyUpdateInfo.targetStage"
-            :current-image="phenologyUpdateInfo.currentImage"
-            :farm-location="phenologyUpdateInfo.farmLocation"
-            @reject="handlePhenologyUpdateReject"
-            @confirm="handlePhenologyUpdateConfirm"
-        />
+        <phenology-update-popup v-model:show="showPhenologyUpdatePopup" :service-name="phenologyUpdateInfo.serviceName"
+            :current-stage="phenologyUpdateInfo.currentStage" :target-stage="phenologyUpdateInfo.targetStage"
+            :current-image="phenologyUpdateInfo.currentImage" :farm-location="phenologyUpdateInfo.farmLocation"
+            @reject="handlePhenologyUpdateReject" @confirm="handlePhenologyUpdateConfirm" />
     </div>
 </template>
 
@@ -200,6 +176,12 @@ const { t } = useI18n();
 const store = useStore();
 const router = useRouter();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
+const diagnosisReportPopupRef = ref(null);
+
+/** 恢复种植确认「已到达」后,再检查是否弹出诊断报告 */
+const onPhenologyReached = () => {
+    diagnosisReportPopupRef.value?.checkShouldShow?.();
+};
 
 const DIAGNOSIS_GUIDE_STORAGE_KEY = "AGRI_FILE_DIAGNOSIS_REPORT_GUIDE";
 const ENTRY_FARM_DATA_KEY = "ENTRY_FARM_DATA";
@@ -498,7 +480,12 @@ const goAlbumMap = () => {
 const goGrowthTrack = (item) => {
     router.push({
         path: "/growth_track",
-        query: { type: item?.type || "growth" },
+        query: {
+            type: item?.type || "growth",
+            pageName: item.type === "abnormal"
+                ? t("agriFile.patrolAbnormalTitle")
+                : t("agriFile.patrolGrowthTitle"),
+        },
     });
 };
 
@@ -779,10 +766,12 @@ watch(activeNav, (key) => {
                 gap: 12px;
                 color: #fff;
                 pointer-events: none;
+
                 &__title {
                     font-size: 18px;
                     font-family: "PangMenZhengDao";
                 }
+
                 &__btn {
                     padding: 0 24px;
                     height: 32px;
@@ -1181,7 +1170,7 @@ watch(activeNav, (key) => {
                     height: 6px;
                     border-radius: 25px;
                     background: linear-gradient(90deg, #2199f8 0%, rgba(255, 255, 255, 0.37) 100%);
-                    opacity: 0.4; 
+                    opacity: 0.4;
                 }
             }
         }

+ 102 - 35
src/views/old_mini/agri_file/pages/growthTrack.vue

@@ -1,7 +1,7 @@
-<template>
+32<template>
     <div class="growth-track-page">
         <custom-header
-            :name="t('agriFile.workName')"
+            :name="pageTitle"
             :isGoBack="!isFromShare"
             :isClose="isFromShare"
             :showClose="false"
@@ -28,7 +28,7 @@
             <template v-if="!isAbnormal">
                 <div class="growth-content">
                     <div class="growth-content__title">{{ t("agriFile.agriAssessment") }}</div>
-                    <div class="growth-content__desc">{{ t("agriFile.agriAssessmentDesc") }}</div>
+                    <div class="growth-content__desc">{{ assessmentDesc }}</div>
                 </div>
                 <div class="interact-card">
                     <div class="interact-card__head">
@@ -38,23 +38,32 @@
                         </div>
                     </div>
                     <div class="interact-card__qa">
-                        <div class="interact-card__question">{{ t("agriFile.interactQuestionText") }}</div>
+                        <div class="interact-card__question">{{ interactQuestion }}</div>
                         <div class="interact-card__options">
-                            <div v-for="item in interactOptions" :key="item.id" class="interact-option"
-                                :class="{ selected: selectedOptionId === item.id }" @click="selectedOptionId = item.id">
+                            <div
+                                v-for="item in interactOptions"
+                                :key="item.id"
+                                class="interact-option"
+                                :class="{ selected: selectedOptionId === item.id }"
+                                @click="selectedOptionId = item.id"
+                            >
                                 <span class="interact-option__text">{{ item.name }}</span>
                             </div>
                         </div>
                         <div class="interact-card__submit" @click="handleSubmit">{{ t("agriFile.submit") }}</div>
                     </div>
-                    <div class="interact-card__ref">
+                    <!-- <div class="interact-card__ref">
                         <span class="interact-card__ref-label">{{ t("agriFile.referenceImage") }}</span>
                         <img class="interact-card__ref-img" src="@/assets/img/agricultural/patrol-guide.png" alt="" />
-                    </div>
+                    </div> -->
                 </div>
-                <div class="guide-card">
+                <div class="guide-card" v-if="interactImg">
                     <div class="guide-title">{{ t("agriFile.patrolGuide") }}</div>
-                    <img class="guide-card__img" src="@/assets/img/agricultural/patrol-guide.png" alt="" />
+                    <photo-provider :photo-closable="true">
+                        <photo-consumer :src="interactImg">
+                            <img class="guide-card__img" :src="interactImg" alt="" />
+                        </photo-consumer>
+                    </photo-provider>
                 </div>
             </template>
             <div v-else>
@@ -89,7 +98,7 @@
 </template>
 
 <script setup>
-import { computed, nextTick, onActivated, ref } from "vue";
+import { computed, nextTick, onActivated, onMounted, ref } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { ElMessage } from "element-plus";
 import customHeader from "@/components/customHeader.vue";
@@ -97,12 +106,16 @@ import albumUploadPopup from "../components/albumUploadPopup.vue";
 import leaveConfirmPopup from "@/components/popup/leaveConfirmPopup.vue";
 import tipPopup from "@/components/popup/tipPopup.vue";
 import { useI18n } from "@/i18n";
+import { base_img_url2 } from "@/api/config";
 
 const { t } = useI18n();
 const route = useRoute();
 const router = useRouter();
 
+const ENTRY_FARM_DATA_KEY = "ENTRY_FARM_DATA";
+
 const isAbnormal = computed(() => route.query.type === "abnormal");
+const pageTitle = computed(() => route.query.pageName || "巡园要点");
 
 function isTruthyFlag(value) {
     return value === 1 || value === "1" || value === true || value === "true";
@@ -129,16 +142,16 @@ const isFromShare = computed(() => {
     return false;
 });
 
-const theme = computed(() => t("agriFile.interactTheme"));
+const theme = ref("");
 const level = computed(() => t("agriFile.riskLevel2"));
-const reason = computed(() => t("agriFile.interactReason"));
-const zoneName = computed(() => t("agriFile.zoneOne"));
-/** 互动选项,后续由接口赋值 */
-const interactOptions = ref([
-    { id: 1, name: "选项一" },
-    { id: 2, name: "选项二" },
-    { id: 3, name: "选项三" },
-]);
+const reason = ref("");
+const zoneName = ref("");
+/** 农情研判描述:interaction_theme */
+const assessmentDesc = ref("");
+/** 互动问题:interaction_problem */
+const interactQuestion = ref("");
+/** 互动选项,由 parse_interact 赋值 */
+const interactOptions = ref([]);
 const selectedOptionId = ref(null);
 const showUploadPopup = ref(false);
 const showSuccessPopup = ref(false);
@@ -151,6 +164,54 @@ const situationList = [
 /** 作物档案本地记录(提交互动后写入,农事档案页读取) */
 const CROP_ARCHIVE_KEY = "CROP_ARCHIVE_RECORDS";
 
+function readEntryFarmData() {
+    try {
+        return JSON.parse(localStorage.getItem(ENTRY_FARM_DATA_KEY) || "null");
+    } catch {
+        return null;
+    }
+}
+
+const interactImg = ref("");
+function applyInteractData(data) {
+    if (!data) return;
+    theme.value = data.interaction_title || "";
+    reason.value = data.interaction_theme || "";
+    zoneName.value = data.crop_category_name || "";
+    assessmentDesc.value = data.inspection_guide || "";
+    interactQuestion.value = data.interaction_problem || "";
+    interactImg.value = base_img_url2 + data.interact_pic_url || "";
+    const options = Array.isArray(data.interaction_options) ? data.interaction_options : [];
+    interactOptions.value = options.map((name, index) => ({
+        id: index + 1,
+        name: String(name),
+    }));
+    selectedOptionId.value = null;
+}
+
+/** 长势跟踪:parse_interact */
+async function fetchGrowthInteract() {
+    if (isAbnormal.value) return;
+    const entry = readEntryFarmData();
+    const cropCategoryName = entry?.crop;
+    const phenophaseCode = entry?.cropCode;
+    const riskName = entry?.weatherRisk;
+    if (!cropCategoryName || !phenophaseCode || !riskName) return;
+
+    try {
+        const res = await VE_API.questionnaire.parseInteract({
+            crop_category_name: cropCategoryName,
+            phenophase_code: String(phenophaseCode),
+            risk_name: riskName,
+        });
+        if (res?.code === 200 && res.data) {
+            applyInteractData(res.data);
+        }
+    } catch {
+        // ignore
+    }
+}
+
 function formatToday() {
     const now = new Date();
     const y = now.getFullYear();
@@ -182,7 +243,7 @@ const handleSubmit = () => {
     saveCropArchiveAnswer({
         id: `answer-${Date.now()}`,
         date: formatToday(),
-        zone_name: t("agriFile.zoneOne"),
+        zone_name: zoneName.value || t("agriFile.zoneOne"),
         content: option?.name || "",
         optionId: option?.id ?? selectedOptionId.value,
     });
@@ -207,10 +268,10 @@ const handleContinueUpload = () => {
 const hasAnswered = () => selectedOptionId.value != null;
 
 const handleBack = () => {
-    if (!isAbnormal.value && !hasAnswered()) {
-        showLeavePopup.value = true;
-        return;
-    }
+    // if (!isAbnormal.value && !hasAnswered()) {
+    //     showLeavePopup.value = true;
+    //     return;
+    // }
     router.back();
 };
 
@@ -218,7 +279,12 @@ const confirmLeave = () => {
     router.back();
 };
 
+onMounted(() => {
+    fetchGrowthInteract();
+});
+
 onActivated(() => {
+    if (!isAbnormal.value) fetchGrowthInteract();
     if (route.query.showUpload !== "1") return;
     nextTick(() => {
         showUploadPopup.value = true;
@@ -355,7 +421,7 @@ onActivated(() => {
         }
 
         &__qa {
-            margin: 10px;
+            margin: 0 10px 10px;
             padding: 10px;
             border-radius: 8px;
             background: rgba(33, 153, 248, 0.1);
@@ -363,7 +429,7 @@ onActivated(() => {
         }
 
         &__question {
-            font-size: 14px;
+            font-size: 18px;
             font-weight: 500;
         }
 
@@ -374,9 +440,10 @@ onActivated(() => {
         }
 
         &__submit {
-            width: 120px;
-            margin: 12px auto 0;
-            padding: 8px 0;
+            width: 112px;
+            margin: 10px auto 0;
+            height: 32px;
+            line-height: 32px;
             border-radius: 25px;
             background: #2199f8;
             color: #fff;
@@ -388,9 +455,9 @@ onActivated(() => {
             position: relative;
             flex: 1;
             min-width: 0;
-            height: 36px;
-            line-height: 36px;
-            border-radius: 6px;
+            height: 32px;
+            line-height: 32px;
+            border-radius: 4px;
             background: #fff;
             border: 1px solid transparent;
             text-align: center;
@@ -403,8 +470,8 @@ onActivated(() => {
                 overflow: hidden;
                 text-overflow: ellipsis;
                 white-space: nowrap;
-                font-size: 14px;
-                color: #666;
+                font-size: 15px;
+                color: #7F7F7F;
             }
 
             &.selected {

+ 10 - 1
src/views/old_mini/entry_information/components/baInformation.vue

@@ -443,7 +443,12 @@ const handleNext = async () => {
         localStorage.setItem(HAS_FARM_KEY, "1");
         VE_API.questionnaire.generateReport({ region, crop, tel:form.phone }).catch(() => {});
         clearFormDraft();
-        ElMessage.success("上传成功,报告正在生成中...");
+        ElMessage({
+            type: "success",
+            message: "上传成功,报告正在生成中...",
+            customClass: "growth-report-message",
+            zIndex: 40000,
+        });
         router.replace("/growth_report");
     } catch (e) {
         console.error("addPlotInfo failed", e);
@@ -726,4 +731,8 @@ onBeforeUnmount(() => {
 .crop-select-popper {
     min-width: 120px !important;
 }
+
+.el-message.growth-report-message {
+    z-index: 40000 !important;
+}
 </style>

+ 67 - 82
src/views/old_mini/growth_report/index.vue

@@ -10,23 +10,13 @@
                 <div class="interact-card__head">
                     <div class="interact-card__tab">
                         <img class="interact-card__star" src="@/assets/img/agricultural/start.png" alt="" />
-                        <span>长势互动</span>
+                        <span>异常互动</span>
                     </div>
                     <el-icon class="interact-card__close" @click="showInteractCard = false"><Close /></el-icon>
                 </div>
                 <div class="interact-card__qa">
                     <div class="interact-card__question">{{ interactQuestionText }}</div>
-                    <div class="interact-card__options">
-                        <div
-                            v-for="item in interactOptions"
-                            :key="item.id"
-                            class="interact-option"
-                            :class="{ selected: selectedOptionId === item.id }"
-                            @click="selectedOptionId = item.id"
-                        >
-                            <span class="interact-option__text">{{ item.name }}</span>
-                        </div>
-                    </div>
+                    <div @click="handleGoPatrol" class="interact-card__btn">去巡园</div>
                 </div>
             </div>
         </div>
@@ -62,10 +52,10 @@
 
     
     <!-- 诊断报告弹窗 -->
-    <diagnosis-report-popup />
+    <diagnosis-report-popup ref="diagnosisReportPopupRef" />
 
-    <!-- 恢复种植弹窗 -->
-    <restore-planting-popup />
+    <!-- 恢复种植弹窗:确认已到达后触发诊断报告弹窗检查 -->
+    <restore-planting-popup @phenology-reached="onPhenologyReached" />
 
     <!-- 录入信息邀请弹窗:分享链接带 showInviteEntry 时展示 -->
     <invite-entry-popup ref="invitePopupRef" />
@@ -83,9 +73,17 @@ import CropAlertPanel from "./components/CropAlertPanel.vue";
 import inviteEntryPopup from "@/views/old_mini/entry_information/components/inviteEntryPopup.vue";
 import DiagnosisReportPopup from "@/components/popup/diagnosisReportPopup.vue";
 import wx from "weixin-js-sdk";
+import { useI18n } from "@/i18n";
 
+const { t } = useI18n();
 const router = useRouter();
 const route = useRoute();
+const diagnosisReportPopupRef = ref(null);
+
+/** 恢复种植确认「已到达」后,再检查是否弹出诊断报告 */
+const onPhenologyReached = () => {
+    diagnosisReportPopupRef.value?.checkShouldShow?.();
+};
 
 const DEFAULT_MAP_POINT = "POINT(113.6142086995688 23.585836479509055)";
 const MAP_KEY = "CZLBZ-LJICQ-R4A5J-BN62X-YXCRJ-GNBUT";
@@ -132,26 +130,48 @@ const panelHeight = ref(280);
 const inviteBottom = computed(() => panelHeight.value);
 const invitePopupRef = ref(null);
 
-const showInteractCard = ref(true);
-const interactQuestionText = ref("具体问题具体问题具体问题具体问题具体?");
-const interactOptions = ref([
-    { id: 1, name: "选项一" },
-    { id: 2, name: "选项二" },
-    { id: 3, name: "选项三" },
-]);
-const selectedOptionId = ref(null);
+const showInteractCard = ref(false);
+const interactQuestionText = ref("");
+
+/** 异常互动:parse_anormal */
+async function fetchAbnormalContent() {
+    const entry = readEntryFarmData();
+    const cropCategoryName = entry?.crop;
+    const phenophaseCode = entry?.cropCode;
+    const riskName = entry?.weatherStress;
+    if (!cropCategoryName || !phenophaseCode || !riskName) {
+        showInteractCard.value = false;
+        return;
+    }
 
-const stressMenuItems = [
-    { key: "stress", line1: "胁迫", line2: "胁迫", icon: menuIcon },
-    { key: "hot-drought-1", line1: "高温", line2: "干旱", icon: menuIcon },
-    { key: "hot-drought-2", line1: "高温", line2: "干旱", icon: menuIcon },
-    { key: "hot-drought-3", line1: "高温", line2: "干旱", icon: menuIcon },
-];
+    try {
+        const res = await VE_API.questionnaire.abnormalContent({
+            crop_category_name: cropCategoryName,
+            phenophase_code: String(phenophaseCode),
+            risk_name: riskName,
+        });
+        if (res?.code !== 200 || !res.data) {
+            showInteractCard.value = false;
+            return;
+        }
+        const title = res.data.interaction_title || "";
+        const theme = res.data.interaction_theme || "";
+        interactQuestionText.value = [title, theme].filter(Boolean).join(":");
+        showInteractCard.value = !!interactQuestionText.value;
+    } catch {
+        showInteractCard.value = false;
+    }
+}
 
-const layerMenuItems = [
-    { key: "growth", line1: "作物", line2: "长势", icon: menuIcon },
-    { key: "phenology", line1: "物候", line2: "进程", icon: menuIcon },
-];
+function handleGoPatrol() {
+    router.push({
+        path: "/growth_track",
+        query: {
+            type: "abnormal",
+            pageName: t("agriFile.patrolAbnormalTitle"),
+        },
+    });
+}
 
 function getLocationName(point = currentMapPoint.value) {
     const locationPoint = point || localStorage.getItem("selectedFarmPoint") || store.state.home.miniUserLocationPoint;
@@ -369,6 +389,7 @@ onMounted(() => {
     getLocationName();
     initMap();
     tryOpenInviteEntryPopup();
+    fetchAbnormalContent();
 });
 onActivated(() => {
     syncEntryFarmData();
@@ -376,6 +397,7 @@ onActivated(() => {
     if (!applied) getLocationName();
     initMap();
     tryOpenInviteEntryPopup();
+    fetchAbnormalContent();
 });
 </script>
 
@@ -439,63 +461,26 @@ onActivated(() => {
 
         .interact-card__qa {
             margin: 0 10px 10px;
-            padding: 10px;
             border-radius: 8px;
-            background: rgba(33, 153, 248, 0.1);
         }
-
         .interact-card__question {
-            font-size: 14px;
+            font-size: 16px;
+            color: #000;
             font-weight: 500;
-            color: #333;
-        }
 
-        .interact-card__options {
-            display: flex;
-            gap: 8px;
-            margin-top: 10px;
         }
-
-        .interact-option {
-            position: relative;
-            flex: 1;
-            min-width: 0;
-            height: 36px;
-            line-height: 36px;
-            border-radius: 6px;
-            background: #fff;
-            border: 1px solid transparent;
+        .interact-card__btn {
+            margin: 10px auto 0;
+            height: 32px;
+            line-height: 32px;
+            background: #2199F8;
+            color: #fff;
+            width: 100px;
             text-align: center;
-            box-sizing: border-box;
+            border-radius: 4px;
+            font-size: 15px;
+            font-weight: 500;
             cursor: pointer;
-
-            &__text {
-                display: block;
-                padding: 0 6px;
-                overflow: hidden;
-                text-overflow: ellipsis;
-                white-space: nowrap;
-                font-size: 14px;
-                color: #666;
-            }
-
-            &.selected {
-                border-color: #2199f8;
-
-                .interact-option__text {
-                    color: #2199f8;
-                }
-
-                &::after {
-                    content: "";
-                    position: absolute;
-                    top: -1px;
-                    right: -1px;
-                    width: 18px;
-                    height: 14px;
-                    background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
-                }
-            }
         }
     }