Quellcode durchsuchen

fix: 对接新的初始化弹窗

lxf vor 2 Wochen
Ursprung
Commit
3174151259

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

@@ -41,4 +41,14 @@ module.exports = {
         url: config.base_new_url + "report/warning_agronomy",
         type: "get",
     },
+    // 物候期问题查询
+    phenologyProblem: {
+        url: config.base_new_url + "report/crop_question",
+        type: "get",
+    },
+    // 暂未到达物候
+    adjustPhenology: {
+        url: config.base_new_url + "report/adjust_phenophase",
+        type: "get",
+    },
 }

+ 128 - 177
src/components/popup/restorePlantingPopup.vue

@@ -30,7 +30,7 @@
                     </template>
                     <template v-else>{{ questionText }}</template>
                 </div>
-                <div class="restore-planting-popup__hint">{{ hintText }}</div>
+                <div v-if="hintText" class="restore-planting-popup__hint">{{ hintText }}</div>
 
                 <div class="restore-planting-popup__photo">
                     <img :src="sampleImage" alt="" />
@@ -113,17 +113,9 @@ import { base_img_url2 } from "@/api/config";
 const { t } = useI18n();
 
 // ---------------------------------------------------------------------------
-// Props
-// ---------------------------------------------------------------------------
-const props = defineProps({
-    /** 分区 id,初始互动接口必填 */
-    zoneId: { type: [Number, String], default: 42 },
-});
-
-// ---------------------------------------------------------------------------
 // 常量
 // ---------------------------------------------------------------------------
-const PHENOLOGY_PREFIX = "您的农场是否进入到 ";
+const ENTRY_FARM_DATA_KEY = "ENTRY_FARM_DATA";
 const DEFAULT_SAMPLE_IMAGE = require("@/assets/img/agricultural/baidian.png");
 const DEFAULT_PHENOLOGY_IMAGE = require("@/assets/img/common/sd-1.jpg");
 /** 巡园向导预览图 */
@@ -131,6 +123,12 @@ const PATROL_GUIDE_IMAGE_PATH =
     "e84a8f1b-9842-434c-951f-068ff6963f31/6c8b7135-95b7-4c04-82ef-a2daeb674d0c/DJI_202608190758_001_6c8b7135-95b7-4c04-82ef-a2daeb674d0c/DJI_20260819080818_0085_V_code-ws0fsmg8zg8u.jpeg";
 const patrolGuideImage = base_img_url2 + PATROL_GUIDE_IMAGE_PATH;
 
+/** adjustPhenology:暂未到达 / 已过 */
+const ADJUST_ACTION = {
+    not_reached: 1,
+    passed: 2,
+};
+
 // ---------------------------------------------------------------------------
 // 页面状态
 // ---------------------------------------------------------------------------
@@ -141,16 +139,9 @@ const selectedDate = ref("");
 const phenologyAnswer = ref("");
 const submitting = ref(false);
 
-const hasDateInteract = ref(false);
-const hasOptionsInteract = ref(false);
-/** date_interact 原始数据,提交时可能用到 */
-const dateInteractData = ref(null);
-/** options_interact.interact 映射表,key 为 start_with 同类序号 */
-const interactMap = ref({});
-/** 当前展示的物候期序号(对应 interact 的 key) */
-const currentInteractKey = ref("");
-/** 用户确认「已到达」后写入,提交接口使用 */
-const selectedPeriodCode = ref("");
+/** 当前物候期编码,调整后用接口返回值替换 */
+const currentPhenophaseCode = ref("");
+const cropType = ref("");
 
 const questionText = ref("");
 const highlightWord = ref("");
@@ -169,25 +160,17 @@ const phenologyOptions = computed(() => [
     { value: "passed", label: t("agriFile.passed") },
 ]);
 
-const interactKeys = computed(() =>
-    Object.keys(interactMap.value).sort((a, b) => Number(a) - Number(b))
-);
-
 // ---------------------------------------------------------------------------
 // 工具函数
 // ---------------------------------------------------------------------------
-/** 当天日期 YYYY-MM-DD */
-const formatToday = () => {
-    const d = new Date();
-    const y = d.getFullYear();
-    const m = String(d.getMonth() + 1).padStart(2, "0");
-    const day = String(d.getDate()).padStart(2, "0");
-    return `${y}-${m}-${day}`;
+const readEntryFarmData = () => {
+    try {
+        return JSON.parse(localStorage.getItem(ENTRY_FARM_DATA_KEY) || "null");
+    } catch {
+        return null;
+    }
 };
 
-/** 日期选择器 YYYY.MM.DD → 接口 YYYY-MM-DD */
-const toApiDate = (value) => (value ? String(value).replace(/\./g, "-") : "");
-
 /** 拼接 CDN 图片地址 */
 const resolveImageUrl = (path) => {
     if (!path) return "";
@@ -195,13 +178,20 @@ const resolveImageUrl = (path) => {
     return base_img_url2 + String(path).replace(/^\//, "");
 };
 
-/** 从 interact_theme 引号中提取高亮词,如「来梢」 */
+/** 从引号中提取高亮词,如「坐粒」 */
 const extractHighlightWord = (text) => {
     if (!text) return "";
     const match = String(text).match(/[“"「]([^”"」]+)[”"」]/);
     return match ? match[1] : "";
 };
 
+/** 从物候问题中提取高亮词,如「灌浆期」 */
+const extractPhenologyHighlight = (text) => {
+    if (!text) return "";
+    const match = String(text).match(/进入到\s*(.+?)\s*[??]?$/);
+    return match ? match[1].trim() : "";
+};
+
 /** Message 需高于弹窗 z-index: 20000 */
 const showMessage = (message, type = "warning") => {
     ElMessage({
@@ -215,84 +205,105 @@ const showMessage = (message, type = "warning") => {
 const showWarning = (message) => showMessage(message, "warning");
 
 const resetForm = () => {
-    questionType.value = hasDateInteract.value ? "time" : "phenology";
+    questionType.value = "time";
     selectedDate.value = "";
     phenologyAnswer.value = "";
-    selectedPeriodCode.value = "";
 };
 
 // ---------------------------------------------------------------------------
-// 业务逻辑:填充问题 / 物候期切换
+// 业务逻辑:加载 / 调整物候问题
 // ---------------------------------------------------------------------------
-const applyDateInteract = (data) => {
-    dateInteractData.value = data;
-    questionText.value = data.interact_theme || "";
-    highlightWord.value = extractHighlightWord(data.interact_theme);
-    hintText.value = data.interact_reason || "";
-    sampleImage.value = resolveImageUrl(data.guide_url) || DEFAULT_SAMPLE_IMAGE;
-};
+/** 用 phenologyProblem 返回填充问题一、问题二 */
+const applyQuestionData = (data) => {
+    if (!data) return;
 
-/** 按 interact key 刷新问题二文案与配图 */
-const applyPhenologyByKey = (key) => {
-    const item = interactMap.value[key];
-    if (!item) return;
-    currentInteractKey.value = String(key);
-    const name = item.period_name || "";
-    phenologyHighlight.value = name;
-    phenologyQuestion.value = `${PHENOLOGY_PREFIX}${name} ?`;
-    phenologyImage.value = resolveImageUrl(item.url) || DEFAULT_PHENOLOGY_IMAGE;
-    selectedPeriodCode.value = "";
-};
+    const timeQuestion = data.phenophase_question_time || "";
+    const phenoQuestion = data.phenophase_question || "";
+    const pic = resolveImageUrl(data.phenophase_pic_url);
 
-/** 暂未到达 → 前一个;已过 → 后一个 */
-const movePhenology = (direction) => {
-    const keys = interactKeys.value;
-    const idx = keys.indexOf(String(currentInteractKey.value));
-    if (idx < 0) return false;
-    const nextIdx = direction === "prev" ? idx - 1 : idx + 1;
-    if (nextIdx < 0 || nextIdx >= keys.length) {
-        showWarning(direction === "prev" ? "已是最早物候期" : "已是最晚物候期");
-        return false;
-    }
-    applyPhenologyByKey(keys[nextIdx]);
+    questionText.value = timeQuestion;
+    highlightWord.value = extractHighlightWord(timeQuestion);
+    hintText.value = "";
+    sampleImage.value = pic || DEFAULT_SAMPLE_IMAGE;
+
+    phenologyQuestion.value = phenoQuestion;
+    phenologyHighlight.value = extractPhenologyHighlight(phenoQuestion);
+    phenologyImage.value = pic || DEFAULT_PHENOLOGY_IMAGE;
+
+    // 有时间题先展示问题一,否则直接问题二
+    questionType.value = timeQuestion ? "time" : "phenology";
     phenologyAnswer.value = "";
-    return true;
 };
 
-const applyQuestionData = (data) => {
-    hasDateInteract.value = !!data?.date_interact;
-    hasOptionsInteract.value = !!data?.options_interact?.interact;
-
-    if (hasDateInteract.value) {
-        applyDateInteract(data.date_interact);
+/** 拉取物候期问题 */
+const fetchPhenologyProblem = async (phenophaseCode) => {
+    const res = await VE_API.questionnaire.phenologyProblem({
+        crop_type: cropType.value,
+        phenophase_code: phenophaseCode,
+    });
+    if (res?.code !== 200 || !res.data) {
+        throw new Error(res?.msg || "获取问题失败");
     }
+    return res.data;
+};
 
-    if (hasOptionsInteract.value) {
-        interactMap.value = data.options_interact.interact || {};
-        const startKey = String(data.options_interact.start_with ?? interactKeys.value[0] ?? "");
-        applyPhenologyByKey(startKey);
+/** 请求接口判断是否展示,并填充问题内容 */
+const checkShouldShow = async () => {
+    const farm = readEntryFarmData();
+    const crop = farm?.crop || "";
+    const code = farm?.cropCode || "";
+    if (!crop || !code) {
+        visible.value = false;
+        return;
     }
 
-    // 有时间题优先展示;仅物候题则直接进入问题二
-    questionType.value = hasDateInteract.value ? "time" : "phenology";
-    visible.value = hasDateInteract.value || hasOptionsInteract.value;
+    cropType.value = crop;
+    currentPhenophaseCode.value = String(code);
+
+    try {
+        const data = await fetchPhenologyProblem(currentPhenophaseCode.value);
+        applyQuestionData(data);
+        visible.value = !!(data.phenophase_question_time || data.phenophase_question);
+    } catch {
+        visible.value = false;
+    }
 };
 
-/** 请求接口判断是否展示,并填充问题内容 */
-const checkShouldShow = async () => {
+/**
+ * 暂未到达 / 已过:调 adjustPhenology,再用新 code 刷新问题二
+ * @param {0|1} action 0=已过,1=暂未到达
+ */
+const adjustAndRefreshQuestion = async (action) => {
+    if (submitting.value) return;
+    submitting.value = true;
     try {
-        const res = await VE_API.questionnaire.getInitialInteraction({
-            zone_id: props.zoneId,
-            date: formatToday(),
-            // date: '2025-07-10',
+        const adjustRes = await VE_API.questionnaire.adjustPhenology({
+            phenophase_code: currentPhenophaseCode.value,
+            action,
         });
-        if (res?.code === 200 && res.data) {
-            applyQuestionData(res.data);
-        } else {
-            visible.value = false;
+        if (adjustRes?.code !== 200) {
+            showMessage(adjustRes?.msg || "调整物候期失败", "error");
+            return;
         }
-    } catch (e) {
-        visible.value = false;
+        const nextCode = adjustRes?.data?.adjusted_phenophase_code;
+        if (nextCode == null || nextCode === "") {
+            showMessage("暂无更多物候期可调整", "warning");
+            return;
+        }
+        currentPhenophaseCode.value = String(nextCode);
+        const data = await fetchPhenologyProblem(currentPhenophaseCode.value);
+        // 调整后只替换问题二(及配图),停留在问题二
+        const phenoQuestion = data.phenophase_question || "";
+        const pic = resolveImageUrl(data.phenophase_pic_url);
+        phenologyQuestion.value = phenoQuestion;
+        phenologyHighlight.value = extractPhenologyHighlight(phenoQuestion);
+        phenologyImage.value = pic || DEFAULT_PHENOLOGY_IMAGE;
+        if (pic) sampleImage.value = pic;
+        phenologyAnswer.value = "";
+    } catch (err) {
+        showMessage(err?.response?.data?.msg || err?.message || "调整物候期失败", "error");
+    } finally {
+        submitting.value = false;
     }
 };
 
@@ -304,116 +315,57 @@ onMounted(() => {
 });
 
 /**
- * 暂未到达 / 已过:立刻切到相邻物候期;
- * 已到达:选中并记下当前 period_code,供提交使用
+ * 暂未到达 / 已过:调接口刷新问题;
+ * 已到达:仅提示
  */
-const handlePhenologySelect = (value) => {
+const handlePhenologySelect = async (value) => {
     if (value === "not_reached") {
-        movePhenology("prev");
+        phenologyAnswer.value = value;
+        await adjustAndRefreshQuestion(ADJUST_ACTION.not_reached);
         return;
     }
     if (value === "passed") {
-        movePhenology("next");
+        phenologyAnswer.value = value;
+        await adjustAndRefreshQuestion(ADJUST_ACTION.passed);
         return;
     }
     phenologyAnswer.value = "reached";
-    const current = interactMap.value[currentInteractKey.value];
-    selectedPeriodCode.value = current?.period_code || "";
-};
-
-/** 组装保存感知结果载荷 */
-/**
- * 组装保存感知结果载荷
- * @param {'time'|'phenology'} type 问题一不含 period_code;问题二不含 interact_code
- */
-const buildSubmitPayload = (type = "time") => {
-    const payload = {
-        zone_id: Number(props.zoneId) || props.zoneId,
-        date: toApiDate(selectedDate.value) || formatToday(),
-    };
-    if (type === "time") {
-        payload.interact_code = dateInteractData.value?.interact_code || "";
-    } else {
-        payload.period_code = selectedPeriodCode.value || "";
-    }
-    return payload;
-};
-
-/** 从 axios 错误中取出可读信息 */
-const getRequestErrorMessage = (err) => {
-    const data = err?.response?.data;
-    if (typeof data === "string" && data) return data;
-    if (data?.msg) return data.msg;
-    if (data?.message) return data.message;
-    if (err?.message) return err.message;
-    return "提交失败,请稍后再试";
-};
-
-/**
- * 调用保存感知结果接口
- * @param {'time'|'phenology'} type
- * @param {{ closeOnSuccess?: boolean }} options closeOnSuccess=false 时成功后不关弹窗(用于继续答问题二)
- */
-const submitInteractResult = async (type = "time", { closeOnSuccess = true } = {}) => {
-    if (submitting.value) return false;
-    submitting.value = true;
-    try {
-        const res = await VE_API.questionnaire.saveInteractResult(buildSubmitPayload(type));
-        if (res?.code === 200) {
-            if (closeOnSuccess) {
-                showMessage(res.msg || "提交成功", "success");
-                visible.value = false;
-                resetForm();
-            }
-            return true;
-        }
-        showMessage(res?.msg || "提交失败,请稍后再试", "error");
-        return false;
-    } catch (err) {
-        showMessage(getRequestErrorMessage(err), "error");
-        return false;
-    } finally {
-        submitting.value = false;
-    }
+    showMessage("提交成功", "success");
+    visible.value = false;
+    resetForm();
 };
 
 const handleConfirm = async () => {
     if (submitting.value) return;
 
-    // 问题一:立即确认 → 提交(不含 period_code)
+    // 问题一:确认后直接进入问题二,不调接口
     if (questionType.value === "time") {
         if (!selectedDate.value) {
             showWarning(t("agriFile.selectTime"));
             return;
         }
-        const ok = await submitInteractResult("time", {
-            // 还有问题二则提交成功后继续展示,不关弹窗
-            closeOnSuccess: !hasOptionsInteract.value,
-        });
-        if (ok && hasOptionsInteract.value) {
-            questionType.value = "phenology";
+        if (!phenologyQuestion.value) {
+            showMessage("暂无物候期问题", "warning");
+            visible.value = false;
+            resetForm();
+            return;
         }
+        questionType.value = "phenology";
         return;
     }
 
-    // 问题二:需已选择「已到达」并写入 period_code;提交不含 interact_code
-    if (phenologyAnswer.value !== "reached" || !selectedPeriodCode.value) {
-        const current = interactMap.value[currentInteractKey.value];
-        if (phenologyAnswer.value === "reached" && current?.period_code) {
-            selectedPeriodCode.value = current.period_code;
-        } else {
-            showWarning("请确认当前物候期是否已到达");
-            return;
-        }
+    // 问题二:需选择「已到达」
+    if (phenologyAnswer.value !== "reached") {
+        showWarning("请确认当前物候期是否已到达");
+        return;
     }
-
-    await submitInteractResult("phenology");
+    showMessage("已确认当前物候期", "success");
+    visible.value = false;
+    resetForm();
 };
 
 defineExpose({
     checkShouldShow,
-    buildSubmitPayload,
-    selectedPeriodCode,
 });
 </script>
 
@@ -599,7 +551,6 @@ defineExpose({
         box-sizing: border-box;
 
         &--primary {
-            // flex: 1;
             background: linear-gradient(180deg, #76c3ff 0%, #2199f8 100%);
             color: #fff;
 

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

@@ -139,7 +139,7 @@
         <album-upload-popup v-model:show="showUploadPopup" />
         <complete-farm-info-popup v-model:show="showCompleteFarmPopup" @confirm="goCompleteFarmInfo" />
 
-        <!-- 诊断报告弹窗:组件内请求接口判断是否展示并跳转 -->
+        <!-- 诊断报告弹窗 -->
         <diagnosis-report-popup />
 
         <!-- 恢复种植弹窗 -->

+ 5 - 0
src/views/old_mini/growth_report/index.vue

@@ -60,6 +60,10 @@
         />
     </div>
 
+    
+    <!-- 诊断报告弹窗 -->
+    <diagnosis-report-popup />
+
     <!-- 恢复种植弹窗 -->
     <restore-planting-popup />
 
@@ -77,6 +81,7 @@ import GrowthReportMap from "./growthReportMap.js";
 import RestorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
 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";
 
 const router = useRouter();