Ver código fonte

feat:对接报告接口

wangsisi 2 semanas atrás
pai
commit
ccd63e6861

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

@@ -76,4 +76,14 @@ module.exports = {
         url: config.base_new_url + "report/get_phenophase",
         type: "get",
     },
+    // 气象风险报告
+    riskReport: {
+        url: config.base_new_url + "report/risk_report",
+        type: "get",
+    },
+    // 胁迫报告
+    stressReport: {
+        url: config.base_new_url + "report/stress_report",
+        type: "get",
+    },
 }

+ 109 - 187
src/views/old_mini/growth_report/alert_detail.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="alert-detail-page">
         <custom-header :name="pageTitle" />
-        <div class="alert-detail-body">
+        <div class="alert-detail-body" v-loading="loading">
             <!-- 综合研判 -->
             <div class="section-card">
                 <div class="section-card__title">综合研判</div>
@@ -29,42 +29,8 @@
                 </div>
             </div>
 
-            <!-- 巡园结果与分析 -->
-            <div class="section-card">
-                <div class="section-card__title">巡园结果与分析</div>
-                <div class="patrol-list">
-                    <div
-                        v-for="item in patrolResults"
-                        :key="item.id"
-                        class="patrol-item"
-                    >
-                        <img class="patrol-item__cover" :src="item.cover" alt="" />
-                        <div class="patrol-item__body">
-                            <div class="patrol-item__title">{{ item.title }}</div>
-                            <div class="patrol-item__desc">{{ item.desc }}</div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-
-            <!-- 诊断研判分析 -->
-            <div class="section-card">
-                <div class="section-card__title">诊断研判分析</div>
-                <div class="diag-list">
-                    <div
-                        v-for="item in diagnosisList"
-                        :key="item.id"
-                        class="diag-item"
-                    >
-                        <span class="diag-item__tag">{{ item.tag }}</span>
-                        <div class="diag-item__title">{{ item.title }}</div>
-                        <div class="diag-item__desc">{{ item.desc }}</div>
-                    </div>
-                </div>
-            </div>
-
             <!-- 农事建议 -->
-            <div class="section-card">
+            <div class="section-card" v-if="adviceList.length">
                 <div class="section-card__title">农事建议</div>
                 <div class="advice-list">
                     <div
@@ -82,92 +48,126 @@
 </template>
 
 <script setup>
-import { computed, ref } from "vue";
+import { computed, onActivated, onMounted, ref } from "vue";
 import { useRoute } from "vue-router";
 import customHeader from "@/components/customHeader.vue";
 
 const route = useRoute();
+const ENTRY_FARM_DATA_KEY = "ENTRY_FARM_DATA";
 
-const pageTitle = computed(() => route.query.title || "具体预警/具体胁迫");
+const pageTitle = computed(() => route.query.title || "预警详情");
 
 const phenologyIcon = require("@/assets/img/common/header-icon-3.png");
 const weatherIcon = require("@/assets/img/common/header-icon-1.png");
 const soilIcon = require("@/assets/img/common/header-icon-2.png");
 const cropCover = require("@/assets/img/home/plant.png");
-const patrolCover = require("@/assets/img/common/tp-3.png");
+
+const loading = ref(false);
 
 /** 综合研判 */
 const analysis = ref({
-    summary:
-        "综合巡园照片、当前物候、近期气象和土壤条件判断,本次异常以持续高温干旱造成的水分失衡为主,已表现为嫩梢萎蔫、卷曲和局部灼伤,并伴有浅层根区干燥",
+    summary: "",
     crop: {
-        name: "荔枝",
+        name: "",
         cover: cropCover,
         metrics: [
-            {
-                label: "当前物候",
-                value: "秋梢新叶速长—红黄叶旺长期",
-                icon: phenologyIcon,
-            },
-            {
-                label: "气象预警",
-                value: "连续3天最高温36-38°C,近7天基本无有效降雨",
-                icon: weatherIcon,
-            },
-            {
-                label: "土壤类型",
-                value: "酸性沙土,树盘及浅层根区明显偏干",
-                icon: soilIcon,
-            },
+            { label: "当前物候", value: "", icon: phenologyIcon },
+            { label: "气象预警", value: "", icon: weatherIcon },
+            { label: "土壤类型", value: "", icon: soilIcon },
         ],
     },
 });
 
-/** 巡园结果与分析 */
-const patrolResults = ref([
-    {
-        id: 1,
-        title: "嫩梢萎蔫与灼伤",
-        cover: patrolCover,
-        desc: "最嫩叶出现轻度皱缩、展开不完全,个别嫩梢顶部出现干尖。成熟叶未见明显连续叶缘焦枯,老叶也未见明显脉间黄化。",
-    },
-    {
-        id: 2,
-        title: "嫩梢萎蔫与灼伤",
-        cover: patrolCover,
-        desc: "最嫩叶出现轻度皱缩、展开不完全,个别嫩梢顶部出现干尖。成熟叶未见明显连续叶缘焦枯,老叶也未见明显脉间黄化。",
-    },
-]);
-
-/** 诊断研判分析 */
-const diagnosisList = ref([
-    {
-        id: 1,
-        tag: "表观影响",
-        title: "浅层吸收根已经受到干旱影响,但尚无腐烂迹象",
-        desc: "本物候期尚无花、果器官,研判重点放在叶和梢。高温、强光叠加缺水后,嫩叶失水速度超过根系补水速度,首先表现",
-    },
-    {
-        id: 2,
-        tag: "表观影响",
-        title: "浅层吸收根已经受到干旱影响,但尚无腐烂迹象",
-        desc: "本物候期尚无花、果器官,研判重点放在叶和梢。高温、强光叠加缺水后,嫩叶失水速度超过根系补水速度,首先表现",
-    },
-]);
-
-/** 农事建议 */
-const adviceList = ref([
-    {
-        id: 1,
-        title: "优先恢复根区水分",
-        desc: "本物候期尚无花、果器官,研判重点放在叶和梢。高温、强光叠加缺水后,嫩叶失水速度超过根系补水速度,首先表现",
-    },
-    {
-        id: 2,
-        title: "暂不建议立即大剂量追肥",
-        desc: "本物候期尚无花、果器官,研判重点放在叶和梢。高温、强光叠加缺水后,嫩叶失水速度超过根系补水速度,首先表现",
-    },
-]);
+/** 农事建议:farmwork_info */
+const adviceList = ref([]);
+
+function readEntryFarmData() {
+    try {
+        return JSON.parse(localStorage.getItem(ENTRY_FARM_DATA_KEY) || "null");
+    } catch {
+        return null;
+    }
+}
+
+function resolveUserTel() {
+    const entry = readEntryFarmData();
+    if (entry?.phone) return String(entry.phone).trim();
+    try {
+        const userInfo = JSON.parse(localStorage.getItem("localUserInfo") || "{}");
+        return String(userInfo.tel || userInfo.phone || userInfo.mobile || "").trim();
+    } catch {
+        return "";
+    }
+}
+
+function applyRiskReport(data) {
+    if (!data || typeof data !== "object") return;
+    const entry = readEntryFarmData();
+    analysis.value = {
+        summary: data.interact_explain || "",
+        crop: {
+            name: data.crop_name || entry?.crop || "",
+            cover: cropCover,
+            metrics: [
+                {
+                    label: "当前物候",
+                    value: data.phenophase_desc || "",
+                    icon: phenologyIcon,
+                },
+                {
+                    label: "气象预警",
+                    value: data.risk_desc || "",
+                    icon: weatherIcon,
+                },
+                {
+                    label: "土壤类型",
+                    value: data.soil_desc || "",
+                    icon: soilIcon,
+                },
+            ],
+        },
+    };
+
+    const rawWorks = data.farmwork_info;
+    let works = [];
+    if (Array.isArray(rawWorks)) {
+        works = rawWorks;
+    } else if (rawWorks && typeof rawWorks === "object") {
+        // 接口可能返回单条对象,而非数组
+        if (rawWorks["农事名称"] || rawWorks["内容信息"] || rawWorks.farmwork_name) {
+            works = [rawWorks];
+        } else {
+            works = Object.values(rawWorks).filter((item) => item && typeof item === "object");
+        }
+    }
+    adviceList.value = works.map((item, index) => ({
+        id: item?.id ?? index,
+        title: item?.["农事名称"] || item?.farmwork_name || item?.name || "",
+        desc: item?.["内容信息"] || item?.content || item?.desc || "",
+    }));
+}
+
+/** 预警详情:risk_report */
+async function fetchRiskReportDetail() {
+    // TODO: 接口有正式数据后改回 resolveUserTel()
+    const tel = "13232323233";
+    if (!tel) return;
+    loading.value = true;
+    try {
+        const raw = await VE_API.questionnaire.riskReport({ tel });
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (Number(res?.code) !== 200 || !res.data) return;
+        const data = Array.isArray(res.data) ? res.data[0] : res.data;
+        applyRiskReport(data);
+    } catch {
+        // ignore
+    } finally {
+        loading.value = false;
+    }
+}
+
+onMounted(fetchRiskReportDetail);
+onActivated(fetchRiskReportDetail);
 </script>
 
 <style lang="scss" scoped>
@@ -205,6 +205,7 @@ const adviceList = ref([
         word-break: break-all;
         text-align: justify;
         font-weight: 350;
+        white-space: pre-line;
     }
 }
 
@@ -296,54 +297,6 @@ const adviceList = ref([
     }
 }
 
-.patrol-list {
-    margin-top: 12px;
-    display: flex;
-    flex-direction: column;
-    gap: 14px;
-}
-
-.patrol-item {
-    display: flex;
-    align-items: flex-start;
-    gap: 10px;
-    background: #F7F8FA;
-    padding: 10px;
-    border-radius: 4px;
-    box-sizing: border-box;
-
-    &__cover {
-        width: 100px;
-        height: 86px;
-        border-radius: 5px;
-        object-fit: cover;
-        flex-shrink: 0;
-        background: #f5f5f5;
-    }
-
-    &__body {
-        flex: 1;
-        min-width: 0;
-    }
-
-    &__title {
-        font-size: 14px;
-        font-weight: 500;
-        line-height: 22px;
-        color: #1D2129;
-    }
-
-    &__desc {
-        margin-top: 4px;
-        font-size: 12px;
-        line-height: 20px;
-        color: #4E5969;
-        word-break: break-all;
-        text-align: justify;
-    }
-}
-
-.diag-list,
 .advice-list {
     margin-top: 10px;
     display: flex;
@@ -351,57 +304,26 @@ const adviceList = ref([
     gap: 10px;
 }
 
-.diag-item,
 .advice-item {
     padding: 10px;
-    border-radius: 4px;
+    border-radius: 8px;
     background: #F7F8FA;
     box-sizing: border-box;
-}
 
-.diag-item {
-    &__tag {
-        display: inline-block;
-        padding: 0 8px;
-        height: 22px;
-        line-height: 22px;
-        border-radius: 2px;
-        background: #2199f8;
-        color: #fff;
-        font-size: 12px;
-    }
-
-    &__title {
-        margin-top: 4px;
-        font-size: 14px;
-        font-weight: 500;
-        line-height: 22px;
-        color: #1D2129;
-    }
-
-    &__desc {
-        margin-top: 4px;
-        font-size: 12px;
-        line-height: 20px;
-        color: #4E5969;
-        word-break: break-all;
-    }
-}
-
-.advice-item {
     &__title {
         font-size: 14px;
         font-weight: 500;
         line-height: 22px;
-        color: #1D2129;
+        color: #000;
     }
 
     &__desc {
-        margin-top: 4px;
-        font-size: 12px;
+        margin-top: 6px;
+        font-size: 13px;
         line-height: 20px;
-        color: #4E5969;
+        color: rgba(6, 6, 6, 0.55);
         word-break: break-all;
+        white-space: pre-line;
     }
 }
 </style>

+ 12 - 2
src/views/old_mini/growth_report/components/CropAlertPanel.vue

@@ -132,6 +132,16 @@ const props = defineProps({
         type: String,
         default: "某某胁迫",
     },
+    /** 未来预警正文:risk_report.explain_simple */
+    weatherRiskExplain: {
+        type: String,
+        default: "",
+    },
+    /** 当下胁迫正文:stress_report.explain_simple */
+    weatherStressExplain: {
+        type: String,
+        default: "",
+    },
 });
 
 const emit = defineEmits(["switchCategory", "viewDetail", "heightChange"]);
@@ -262,7 +272,7 @@ const alertCards = computed(() => [
         id: "warning",
         title: `未来预警: ${props.weatherRisk || "具体预警"}`,
         icon: weatherIcon,
-        content: "详情体预警体预警体预警体预警体预警体预详情详情详情详情详情详情详情详情详情详情",
+        content: props.weatherRiskExplain || "",
         patrolTip: "",
     },
     {
@@ -270,7 +280,7 @@ const alertCards = computed(() => [
         type: "abnormal",
         title: `当下胁迫: ${props.weatherStress || "某某胁迫"}`,
         icon: weatherIcon,
-        content: "详情详情详情具体预警具体预警情详情详详情详情详情详情详情详情",
+        content: props.weatherStressExplain || "",
         patrolTip: "异常态势巡园要点",
     },
 ]);

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

@@ -43,6 +43,8 @@
             :crop-name="currentCropName"
             :weather-risk="weatherRisk"
             :weather-stress="weatherStress"
+            :weather-risk-explain="weatherRiskExplain"
+            :weather-stress-explain="weatherStressExplain"
             @switch-category="handleSwitchCategory"
             @view-detail="handleViewDetail"
             @patrol-tip="handlePatrolTip"
@@ -125,6 +127,10 @@ const currentMapPoint = ref(
 const currentCropName = ref(entryFarmData?.crop || "水稻");
 const weatherRisk = ref(entryFarmData?.weatherRisk || "具体预警");
 const weatherStress = ref(entryFarmData?.weatherStress || "某某胁迫");
+/** 未来预警正文:risk_report.explain_simple */
+const weatherRiskExplain = ref("");
+/** 当下胁迫正文:stress_report.explain_simple */
+const weatherStressExplain = ref("");
 const activeMenuKey = ref("hot-drought-2");
 const panelHeight = ref(280);
 const inviteBottom = computed(() => panelHeight.value);
@@ -133,6 +139,65 @@ const invitePopupRef = ref(null);
 const showInteractCard = ref(false);
 const interactQuestionText = ref("");
 
+function resolveUserTel() {
+    const entry = readEntryFarmData();
+    if (entry?.phone) return String(entry.phone).trim();
+    try {
+        const userInfo = JSON.parse(localStorage.getItem("localUserInfo") || "{}");
+        return String(userInfo.tel || userInfo.phone || userInfo.mobile || "").trim();
+    } catch {
+        return "";
+    }
+}
+
+/** 未来预警卡片:risk_report → explain_simple */
+async function fetchRiskReport() {
+    // TODO: 接口有正式数据后改回 resolveUserTel()
+    const tel = "13232323233";
+    if (!tel) {
+        weatherRiskExplain.value = "";
+        return;
+    }
+    try {
+        const raw = await VE_API.questionnaire.riskReport({ tel });
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (Number(res?.code) !== 200 || !res.data) {
+            weatherRiskExplain.value = "";
+            return;
+        }
+        const data = res.data;
+        const item = Array.isArray(data) ? data[0] : data;
+        weatherRiskExplain.value =
+            item?.explain_simple || item?.interact_explain || "";
+    } catch {
+        weatherRiskExplain.value = "";
+    }
+}
+
+/** 当下胁迫卡片:stress_report → explain_simple */
+async function fetchStressReport() {
+    // TODO: 接口有正式数据后改回 resolveUserTel()
+    const tel = "13232323233";
+    if (!tel) {
+        weatherStressExplain.value = "";
+        return;
+    }
+    try {
+        const raw = await VE_API.questionnaire.stressReport({ tel });
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (Number(res?.code) !== 200 || !res.data) {
+            weatherStressExplain.value = "";
+            return;
+        }
+        const data = res.data;
+        const item = Array.isArray(data) ? data[0] : data;
+        weatherStressExplain.value =
+            item?.explain_simple || item?.interact_explain || "";
+    } catch {
+        weatherStressExplain.value = "";
+    }
+}
+
 /** 异常互动:parse_anormal */
 async function fetchAbnormalContent() {
     const entry = readEntryFarmData();
@@ -390,6 +455,8 @@ onMounted(() => {
     initMap();
     tryOpenInviteEntryPopup();
     fetchAbnormalContent();
+    fetchRiskReport();
+    fetchStressReport();
 });
 onActivated(() => {
     syncEntryFarmData();
@@ -398,6 +465,8 @@ onActivated(() => {
     initMap();
     tryOpenInviteEntryPopup();
     fetchAbnormalContent();
+    fetchRiskReport();
+    fetchStressReport();
 });
 </script>