|
@@ -1,7 +1,11 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="alert-detail-page">
|
|
<div class="alert-detail-page">
|
|
|
<custom-header :name="pageTitle" />
|
|
<custom-header :name="pageTitle" />
|
|
|
- <div class="alert-detail-body" v-loading="loading">
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="alert-detail-body"
|
|
|
|
|
+ :class="{ 'alert-detail-body--with-bar': detailType === 'stress' }"
|
|
|
|
|
+ v-loading="loading"
|
|
|
|
|
+ >
|
|
|
<!-- 综合研判 -->
|
|
<!-- 综合研判 -->
|
|
|
<div class="section-card">
|
|
<div class="section-card">
|
|
|
<div class="section-card__title">综合研判</div>
|
|
<div class="section-card__title">综合研判</div>
|
|
@@ -9,7 +13,7 @@
|
|
|
|
|
|
|
|
<div class="crop-panel">
|
|
<div class="crop-panel">
|
|
|
<div class="crop-panel__head">
|
|
<div class="crop-panel__head">
|
|
|
- <img class="crop-panel__cover" :src="analysis.crop.cover" alt="" />
|
|
|
|
|
|
|
+ <!-- <img class="crop-panel__cover" :src="analysis.crop.cover" alt="" /> -->
|
|
|
<span class="crop-panel__name">{{ analysis.crop.name }}</span>
|
|
<span class="crop-panel__name">{{ analysis.crop.name }}</span>
|
|
|
<span class="crop-panel__tag">当前作物</span>
|
|
<span class="crop-panel__tag">当前作物</span>
|
|
|
</div>
|
|
</div>
|
|
@@ -29,10 +33,11 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <!-- 农事建议 -->
|
|
|
|
|
- <div class="section-card" v-if="adviceList.length">
|
|
|
|
|
- <div class="section-card__title">农事建议</div>
|
|
|
|
|
- <div class="advice-list">
|
|
|
|
|
|
|
+ <!-- 农事建议(风险) / 巡园重要性(胁迫) -->
|
|
|
|
|
+ <div class="section-card" v-if="showBottomSection">
|
|
|
|
|
+ <div class="section-card__title">{{ bottomSectionTitle }}</div>
|
|
|
|
|
+ <div v-if="importanceDesc" class="section-card__desc">{{ importanceDesc }}</div>
|
|
|
|
|
+ <div v-else class="advice-list">
|
|
|
<div
|
|
<div
|
|
|
v-for="item in adviceList"
|
|
v-for="item in adviceList"
|
|
|
:key="item.id"
|
|
:key="item.id"
|
|
@@ -44,18 +49,28 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 当下胁迫:底部巡园引导 -->
|
|
|
|
|
+ <div v-if="detailType === 'stress'" class="stress-patrol-bar">
|
|
|
|
|
+ <div class="stress-patrol-bar__icon">!</div>
|
|
|
|
|
+ <div class="stress-patrol-bar__text">当前气象胁迫风险较高,建议巡园诊断异常情况</div>
|
|
|
|
|
+ <div class="stress-patrol-bar__btn" @click="handleGoPatrol">巡园要点</div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import { computed, onActivated, onMounted, ref } from "vue";
|
|
import { computed, onActivated, onMounted, ref } from "vue";
|
|
|
-import { useRoute } from "vue-router";
|
|
|
|
|
|
|
+import { useRoute, useRouter } from "vue-router";
|
|
|
import customHeader from "@/components/customHeader.vue";
|
|
import customHeader from "@/components/customHeader.vue";
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
|
|
|
+const router = useRouter();
|
|
|
const ENTRY_FARM_DATA_KEY = "ENTRY_FARM_DATA";
|
|
const ENTRY_FARM_DATA_KEY = "ENTRY_FARM_DATA";
|
|
|
|
|
|
|
|
const pageTitle = computed(() => route.query.title || "预警详情");
|
|
const pageTitle = computed(() => route.query.title || "预警详情");
|
|
|
|
|
+/** risk:气象风险;stress:当下胁迫 */
|
|
|
|
|
+const detailType = computed(() => String(route.query.detailType || "risk"));
|
|
|
|
|
|
|
|
const phenologyIcon = require("@/assets/img/common/header-icon-3.png");
|
|
const phenologyIcon = require("@/assets/img/common/header-icon-3.png");
|
|
|
const weatherIcon = require("@/assets/img/common/header-icon-1.png");
|
|
const weatherIcon = require("@/assets/img/common/header-icon-1.png");
|
|
@@ -78,8 +93,17 @@ const analysis = ref({
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-/** 农事建议:farmwork_info */
|
|
|
|
|
|
|
+/** 农事建议:farmwork_info(risk) */
|
|
|
const adviceList = ref([]);
|
|
const adviceList = ref([]);
|
|
|
|
|
+/** 巡园重要性:importance_desc(stress) */
|
|
|
|
|
+const importanceDesc = ref("");
|
|
|
|
|
+
|
|
|
|
|
+const bottomSectionTitle = computed(() =>
|
|
|
|
|
+ detailType.value === "stress" ? "巡园重要性" : "农事建议"
|
|
|
|
|
+);
|
|
|
|
|
+const showBottomSection = computed(() =>
|
|
|
|
|
+ detailType.value === "stress" ? !!importanceDesc.value : adviceList.value.length > 0
|
|
|
|
|
+);
|
|
|
|
|
|
|
|
function readEntryFarmData() {
|
|
function readEntryFarmData() {
|
|
|
try {
|
|
try {
|
|
@@ -100,6 +124,14 @@ function resolveUserTel() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function buildMetrics({ phenophaseDesc, middleLabel, middleValue, soilDesc }) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ { label: "当前物候", value: phenophaseDesc || "", icon: phenologyIcon },
|
|
|
|
|
+ { label: middleLabel, value: middleValue || "", icon: weatherIcon },
|
|
|
|
|
+ { label: "土壤类型", value: soilDesc || "", icon: soilIcon },
|
|
|
|
|
+ ];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function applyRiskReport(data) {
|
|
function applyRiskReport(data) {
|
|
|
if (!data || typeof data !== "object") return;
|
|
if (!data || typeof data !== "object") return;
|
|
|
const entry = readEntryFarmData();
|
|
const entry = readEntryFarmData();
|
|
@@ -108,26 +140,16 @@ function applyRiskReport(data) {
|
|
|
crop: {
|
|
crop: {
|
|
|
name: data.crop_name || entry?.crop || "",
|
|
name: data.crop_name || entry?.crop || "",
|
|
|
cover: cropCover,
|
|
cover: cropCover,
|
|
|
- metrics: [
|
|
|
|
|
- {
|
|
|
|
|
- label: "当前物候",
|
|
|
|
|
- value: data.phenophase_desc || "",
|
|
|
|
|
- icon: phenologyIcon,
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- label: "气象预警",
|
|
|
|
|
- value: data.risk_desc || "",
|
|
|
|
|
- icon: weatherIcon,
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- label: "土壤类型",
|
|
|
|
|
- value: data.soil_desc || "",
|
|
|
|
|
- icon: soilIcon,
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ metrics: buildMetrics({
|
|
|
|
|
+ phenophaseDesc: data.phenophase_desc,
|
|
|
|
|
+ middleLabel: "气象预警",
|
|
|
|
|
+ middleValue: data.risk_desc,
|
|
|
|
|
+ soilDesc: data.soil_desc,
|
|
|
|
|
+ }),
|
|
|
},
|
|
},
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ importanceDesc.value = "";
|
|
|
const rawWorks = data.farmwork_info;
|
|
const rawWorks = data.farmwork_info;
|
|
|
let works = [];
|
|
let works = [];
|
|
|
if (Array.isArray(rawWorks)) {
|
|
if (Array.isArray(rawWorks)) {
|
|
@@ -147,18 +169,49 @@ function applyRiskReport(data) {
|
|
|
}));
|
|
}));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 预警详情:risk_report */
|
|
|
|
|
-async function fetchRiskReportDetail() {
|
|
|
|
|
|
|
+function applyStressReport(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: buildMetrics({
|
|
|
|
|
+ phenophaseDesc: data.phenophase_desc,
|
|
|
|
|
+ middleLabel: "当下胁迫",
|
|
|
|
|
+ middleValue: data.stress_desc,
|
|
|
|
|
+ soilDesc: data.soil_desc,
|
|
|
|
|
+ }),
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ adviceList.value = [];
|
|
|
|
|
+ importanceDesc.value = data.importance_desc || "";
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function requestReport(apiFn) {
|
|
|
// TODO: 接口有正式数据后改回 resolveUserTel()
|
|
// TODO: 接口有正式数据后改回 resolveUserTel()
|
|
|
- const tel = "13232323233";
|
|
|
|
|
- if (!tel) return;
|
|
|
|
|
|
|
+ const tel = resolveUserTel();
|
|
|
|
|
+ if (!tel) return null;
|
|
|
|
|
+ const raw = await apiFn({ tel });
|
|
|
|
|
+ const res = Array.isArray(raw) ? raw[0] : raw;
|
|
|
|
|
+ if (Number(res?.code) !== 200 || !res.data) return null;
|
|
|
|
|
+ return Array.isArray(res.data) ? res.data[0] : res.data;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 按 detailType 拉取详情:risk → risk_report;stress → stress_report */
|
|
|
|
|
+async function fetchAlertDetail() {
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
|
|
+ adviceList.value = [];
|
|
|
|
|
+ importanceDesc.value = "";
|
|
|
try {
|
|
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);
|
|
|
|
|
|
|
+ if (detailType.value === "stress") {
|
|
|
|
|
+ const data = await requestReport(VE_API.questionnaire.stressReport);
|
|
|
|
|
+ if (data) applyStressReport(data);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const data = await requestReport(VE_API.questionnaire.riskReport);
|
|
|
|
|
+ if (data) applyRiskReport(data);
|
|
|
} catch {
|
|
} catch {
|
|
|
// ignore
|
|
// ignore
|
|
|
} finally {
|
|
} finally {
|
|
@@ -166,8 +219,18 @@ async function fetchRiskReportDetail() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-onMounted(fetchRiskReportDetail);
|
|
|
|
|
-onActivated(fetchRiskReportDetail);
|
|
|
|
|
|
|
+onMounted(fetchAlertDetail);
|
|
|
|
|
+onActivated(fetchAlertDetail);
|
|
|
|
|
+
|
|
|
|
|
+function handleGoPatrol() {
|
|
|
|
|
+ router.push({
|
|
|
|
|
+ path: "/growth_track",
|
|
|
|
|
+ query: {
|
|
|
|
|
+ type: "abnormal",
|
|
|
|
|
+ pageName: "异常态势巡园",
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
@@ -181,6 +244,60 @@ onActivated(fetchRiskReportDetail);
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
|
height: calc(100vh - 44px);
|
|
height: calc(100vh - 44px);
|
|
|
|
|
+
|
|
|
|
|
+ &--with-bar {
|
|
|
|
|
+ padding-bottom: 88px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stress-patrol-bar {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ left: 10px;
|
|
|
|
|
+ right: 10px;
|
|
|
|
|
+ bottom: calc(32px + env(safe-area-inset-bottom));
|
|
|
|
|
+ z-index: 20;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding: 14px 10px;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ background: linear-gradient(180deg, #FFDFC5 -13%, #FFFFFF 38%);
|
|
|
|
|
+ box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ // background: linear-gradient(90deg, #fff7f0 0%, #ffffff 100%);
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+
|
|
|
|
|
+ &__icon {
|
|
|
|
|
+ flex: none;
|
|
|
|
|
+ width: 18px;
|
|
|
|
|
+ height: 18px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background: #FF953D;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__text {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ color: #FF953D;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__btn {
|
|
|
|
|
+ flex: none;
|
|
|
|
|
+ padding: 6px 12px;
|
|
|
|
|
+ border-radius: 20px;
|
|
|
|
|
+ background: #FF953D;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.section-card {
|
|
.section-card {
|