|
|
@@ -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>
|