Переглянути джерело

Merge branch 'master' of http://www.sysuimars.cn:3000/feiniao/agriculture-one-map

lxf 6 днів тому
батько
коміт
f979b74039

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

@@ -39,5 +39,20 @@ export default {
         url: config.one_map_url + "agri_region_crop/query_area_yield",
         type: "get",
     },
+    //统计指定物种在不同物候期下的面积占比
+    fetchStatPhenologyRatio: {
+        url: config.one_map_url + "agri_land_crop/stat_phenology_ratio",
+        type: "get",
+    },
+    //统计指定物种在下级区划中的预估产量占比
+    fetchStatRegionYieldRatio: {
+        url: config.one_map_url + "agri_land_crop/stat_region_yield_ratio",
+        type: "get",
+    },
+    //获取农场列表
+    fetchFarmList: {
+        url: config.one_map_url + "agri_farm_crop/list",
+        type: "post",
+    },
 }
 

+ 46 - 8
src/components/chartBox.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="chart-box" :class="color">
-    <div class="chart-title" v-if="name">
+    <div class="chart-title" v-if="name" ref="titleRef">
       <div class="name">
         <img :src="`/src/assets/images/common/chart-${color || 'icon'}.png`" alt="" />
         <span>{{name}}</span>
@@ -11,7 +11,7 @@
       </div>
     </div>
     <slot v-else name="title-name"></slot>
-    <div class="chart-content">
+    <div class="chart-content" :style="{ height: contentHeight }">
         <div v-show="arrow" :class="['arrow',arrow]" @click="handleShrink">
             <el-icon class="icon" color="#141414"><DArrowLeft /></el-icon>
         </div>
@@ -21,7 +21,7 @@
 </template>
 
 <script setup>
-import {ref} from 'vue'
+import {ref, onMounted, onBeforeUnmount, nextTick, watch} from 'vue'
 const props = defineProps({
     name:{
         type:String,
@@ -37,10 +37,50 @@ const props = defineProps({
     }
 })
 
-const isShrink = ref(true)
+const titleRef = ref(null)
+const contentHeight = ref('calc(100% - 38px)')
+let resizeObserver = null
+
+const updateContentHeight = () => {
+    nextTick(() => {
+        if (titleRef.value) {
+            const titleHeight = titleRef.value.offsetHeight
+            contentHeight.value = `calc(100% - ${titleHeight}px)`
+        } else {
+            // 如果没有标题,内容占满整个高度
+            contentHeight.value = '100%'
+        }
+    })
+}
+
 const handleShrink = () =>{
 
 }
+
+onMounted(() => {
+    updateContentHeight()
+    
+    // 使用 ResizeObserver 监听头部高度变化
+    if (titleRef.value && window.ResizeObserver) {
+        resizeObserver = new ResizeObserver(() => {
+            updateContentHeight()
+        })
+        resizeObserver.observe(titleRef.value)
+    }
+})
+
+onBeforeUnmount(() => {
+    // 清理 ResizeObserver
+    if (resizeObserver) {
+        resizeObserver.disconnect()
+        resizeObserver = null
+    }
+})
+
+// 监听 name 变化,重新计算高度
+watch(() => props.name, () => {
+    updateContentHeight()
+})
 </script>
 
 <style lang="scss" scoped>
@@ -54,7 +94,7 @@ const handleShrink = () =>{
 
   .chart-title {
     width: 100%;
-    height: 38px;
+    min-height: 38px;
     display: flex;
     align-items: center;
     justify-content: space-between;
@@ -77,7 +117,6 @@ const handleShrink = () =>{
   .chart-content{
     position: relative;
     width: 100%;
-    height: calc(100% - 38px);
     padding: 4px 8px;
     box-sizing: border-box;
     .arrow{
@@ -106,7 +145,7 @@ const handleShrink = () =>{
     border-radius: 8px;
     background: #191919;
     .chart-title{
-      height: 59px;
+      min-height: 59px;
       .name{
         color: #FFD489;
         span{
@@ -116,7 +155,6 @@ const handleShrink = () =>{
     }
     .chart-content{
       padding: 0;
-      height: calc(100% - 59px);
     }
   }
 }

+ 1 - 23
src/components/fnHeader.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="header">
-        <div class="title" @click="showDatePicker = !showDatePicker">
+        <div class="title">
           <img class="logo" src="@/assets/images/common/logo.png" alt="" />
           <span>{{headerName||"飞鸟智慧巡园平台"}}</span>
           <img class="logo-icon" src="@/assets/images/common/logo-icon.png" alt="" />
@@ -32,15 +32,6 @@
         <div class="page-bg bg-bottom"></div>
         <div class="page-bg bg-left"></div>
     </div>
-    <div v-if="showDatePicker" style="position: absolute;top:50%;left: 50%;width: 50%;height: 200px;">
-      <el-date-picker
-          @change="toggleFarmPicker"
-          v-model="date"
-          type="date"
-          value-format="YYYY-MM-DD"
-          placeholder="Pick a day"
-      />
-    </div>
 </template>
 
 <script setup>
@@ -49,8 +40,6 @@ import { useRouter } from "vue-router";
 import { convertPointToArray } from "@/utils/index";
 import eventBus from "@/api/eventBus";
 
-const date = ref(null);
-const showDatePicker = ref(false);
 const router = useRouter();
 const props = defineProps({
     showDate: {
@@ -83,10 +72,6 @@ const toggleFarm = (val) => {
     eventBus.emit('garden:organId',val)
     router.push({ name: "Home" });
 };
-const toggleFarmPicker = (val) => {
-  localStorage.setItem("date", val);
-  alert("已选择日期:" + localStorage.getItem("date"))
-}
 
 function getCurrentFormattedTime(type) {
     const now = new Date();
@@ -112,13 +97,6 @@ function getCurrentDayOfWeek() {
     return daysOfWeek[dayOfWeek];
 }
 
-function formatTimeToHHmmss(date) {
-    const hours = String(date.getHours()).padStart(2, "0");
-    const minutes = String(date.getMinutes()).padStart(2, "0");
-    const seconds = String(date.getSeconds()).padStart(2, "0");
-
-    return `${hours}.${minutes}.${seconds}`;
-}
 
 const options = ref([]);
 const userInfo = JSON.parse(sessionStorage.getItem("userinfo"));

+ 49 - 5
src/views/warningHome/components/chart_components/barChart.vue

@@ -27,16 +27,61 @@ let myChart = null;
 
 const initData = () => {
     const newOption = deepClone(barOption);
-    
+
     // 如果有传入数据,使用传入的数据
     if (props.chartData && props.chartData.categories && props.chartData.categories.length > 0) {
         newOption.xAxis.data = props.chartData.categories;
         newOption.series[0].data = props.chartData.values;
+
+        // 如果数据项超过 8 个,启用滚动功能
+        const dataLength = props.chartData.categories.length;
+        if (dataLength > 8) {
+            // 调整 grid 的 bottom,为 dataZoom 留出空间
+            newOption.grid.bottom = 20;
+            // 使用滚动时,不需要旋转标签
+            newOption.xAxis.axisLabel.rotate = 0;
+
+            // 添加 dataZoom 配置,实现 x 轴滚动
+            newOption.dataZoom = [
+                {
+                    type: 'slider',
+                    show: true,
+                    xAxisIndex: [0],
+                    start: 0,
+                    end: Math.min(100, (6 / dataLength) * 100), // 初始显示前 6 个或更少
+                    height: 0,
+                    bottom: 10,
+                    showDetail: false,
+                },
+                {
+                    type: 'inside',
+                    xAxisIndex: [0],
+                    start: 0,
+                    end: Math.min(100, (6 / dataLength) * 100),
+                },
+            ];
+        } else {
+            // 数据项较少时,恢复默认的 bottom 值
+            newOption.grid.bottom = 10;
+            // 数据项少时,可以旋转标签以避免重叠
+            newOption.xAxis.axisLabel.rotate = dataLength >= 5 ? 30 : 0;
+            // 移除 dataZoom
+            delete newOption.dataZoom;
+        }
+    } else {
+        // 数据为空时,清空图表
+        newOption.xAxis.data = [];
+        newOption.series[0].data = [];
+        // 恢复默认配置
+        newOption.grid.bottom = 10;
+        newOption.xAxis.axisLabel.rotate = 0;
+        // 移除 dataZoom
+        delete newOption.dataZoom;
     }
-    
+
     // 更新 yAxis 的 formatter
     newOption.yAxis.axisLabel.formatter = props.yAxisFormatter;
-    
+
     myChart.setOption(newOption);
 };
 
@@ -55,7 +100,6 @@ onMounted(() => {
     myChart = echarts.init(chartDom.value);
     initData();
 });
-
 </script>
 
 <style lang="scss" scoped>
@@ -63,4 +107,4 @@ onMounted(() => {
     width: 100%;
     height: 100%;
 }
-</style>
+</style>

+ 228 - 26
src/views/warningHome/components/chart_components/chartList.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="chart-list">
         <div class="chart-item">
-            <chart-box name="2025年广东省作物种植面积占比">
+            <chart-box :name="`${props.areaName}作物种植面积占比`">
                 <div class="box-content">
                     <div class="chart-dom">
                         <pie-chart :chartData="pieChartData" :totalArea="totalArea"></pie-chart>
@@ -23,7 +23,7 @@
             </chart-box>
         </div>
         <div class="chart-item">
-            <chart-box name="2025年广东省作物区域占比">
+            <chart-box :name="twoTitle">
                 <div class="box-content">
                     <div class="chart-dom">
                         <bar-chart :key="0" :chartData="regionChartData"></bar-chart>
@@ -38,17 +38,15 @@
                     <div class="chart-dom">
                         <line-chart :chartData="areaTrendChartData"></line-chart>
                     </div>
-                    <div class="box-bg">
-                        暂无数据
-                    </div>
+                    <div class="box-bg">暂无数据</div>
                 </div>
             </chart-box>
         </div>
         <div class="chart-item">
-            <chart-box name="2025年广东省作物预估产量对比">
+            <chart-box :name="threeTitle">
                 <div class="box-content">
                     <div class="chart-dom">
-                        <bar-chart :key="1" :chartData="yieldChartData" :yAxisFormatter="'{value}吨'"></bar-chart>
+                        <bar-chart :key="1" :chartData="yieldChartData" :yAxisFormatter="yAxisFormatter"></bar-chart>
                     </div>
                     <div class="box-bg">{{ yieldSummaryText }}</div>
                 </div>
@@ -61,7 +59,7 @@
 import chartBox from "@/components/chartBox.vue";
 import pieChart from "./pieChart.vue";
 import lineChart from "./lineChart.vue";
-import { computed, onMounted, ref } from "vue";
+import { computed, onMounted, ref, watch } from "vue";
 import { pieOption } from "./chartOption.js";
 import barChart from "./barChart.vue";
 
@@ -70,6 +68,14 @@ const props = defineProps({
         type: String,
         default: "作物分布",
     },
+    areaCode: {
+        type: String,
+        default: "156440000",
+    },
+    areaName: {
+        type: String,
+        default: "广东省",
+    },
 });
 
 // 图表数据
@@ -94,6 +100,11 @@ const yieldChartData = ref({
 // 预估产量摘要文字
 const yieldSummaryText = ref("暂无数据");
 
+// 根据 activeBaseTab 动态设置 y 轴单位
+const yAxisFormatter = computed(() => {
+    return props.activeBaseTab === "物候期分布" ? "{value}亩" : "{value}吨";
+});
+
 // 种植面积趋势图表数据
 const areaTrendChartData = ref({
     xAxisData: [], // 时间轴数据
@@ -116,16 +127,164 @@ const legendData = computed(() => {
     });
 });
 
-onMounted(() => {
-    fetchStatSpeciesAreaYield();
-    fetchStatRegionAreaRatio();
-    fetchAreaTrend();
-});
+const twoTitle = ref(`${props.areaName}作物区域占比`);
+const threeTitle = ref(`${props.areaName}作物预估产量对比`);
+
+// 监听 activeBaseTab 变化
+watch(
+    () => props.activeBaseTab,
+    () => {
+        initData();
+    }
+);
+
+// 监听 areaCode 变化,当切换到物候期分布时重新获取数据
+watch(
+    () => props.areaCode,
+    (newVal) => {
+        if (newVal) {
+            initData();
+        }
+    }
+);
+
+const initData = () => {
+    if (props.activeBaseTab === "物候期分布") {
+        twoTitle.value = `${props.areaName}小麦物候进程分布`;
+        threeTitle.value = `${props.areaName}小麦预告产量统计`;
+        fetchStatPhenologyRatio();
+        fetchStatRegionYieldRatio();
+    } else {
+        twoTitle.value = `${props.areaName}作物区域占比`;
+        threeTitle.value = `${props.areaName}作物预估产量对比`;
+        fetchStatSpeciesAreaYield();
+        fetchStatRegionAreaRatio();
+        fetchAreaTrend();
+    }
+};
+
+//统计指定物种在下级区划中的预估产量占比
+const fetchStatRegionYieldRatio = () => {
+    const params = {
+        speciesId: 1,
+        adminCode: props.areaCode,
+        adminLevel: "province",
+    };
+    VE_API.warning
+        .fetchStatRegionYieldRatio(params)
+        .then((res) => {
+            if (res.code === 0 && res.data && res.data.length > 0) {
+                // 转换接口数据为图表格式
+                const categories = res.data.map((item) => item.adminName);
+                // 如果是物候期分布,使用 expectYield(但单位显示为亩),否则使用 expectYield(单位显示为吨)
+                const values = res.data.map((item) => parseFloat(item.expectYield.toFixed(2)));
+
+                // 更新图表数据
+                yieldChartData.value = {
+                    categories,
+                    values,
+                };
+
+                // 找到最大值的区域
+                let maxValue = 0;
+                let maxRegion = "";
+                res.data.forEach((item) => {
+                    if (item.expectYield > maxValue) {
+                        maxValue = item.expectYield;
+                        maxRegion = item.adminName;
+                    }
+                });
+
+                // 更新摘要文字
+                if (maxRegion) {
+                    const maxValueFormatted = maxValue.toFixed(1);
+                    const unit = props.activeBaseTab === "物候期分布" ? "亩" : "吨";
+                    yieldSummaryText.value = `${maxRegion}的${
+                        props.activeBaseTab === "物候期分布" ? "种植面积" : "预估产量"
+                    }最大,为${maxValueFormatted}${unit}`;
+                } else {
+                    yieldSummaryText.value = "暂无数据";
+                }
+            } else {
+                yieldChartData.value = {
+                    categories: [],
+                    values: [],
+                };
+                yieldSummaryText.value = "暂无数据";
+            }
+        })
+        .catch((error) => {
+            console.error("获取区域产量占比数据失败:", error);
+            yieldChartData.value = {
+                categories: [],
+                values: [],
+            };
+            yieldSummaryText.value = "暂无数据";
+        });
+};
+
+//统计指定物种在不同物候期下的面积占比
+const fetchStatPhenologyRatio = () => {
+    const params = {
+        speciesId: 1,
+        adminCode: props.areaCode,
+        adminLevel: "province",
+    };
+
+    VE_API.warning
+        .fetchStatPhenologyRatio(params)
+        .then((res) => {
+            if (res.code === 0 && res.data && res.data.length > 0) {
+                console.log(res.data);
+                // 转换接口数据为图表格式
+                const categories = res.data.map((item) => item.phenologyName);
+                const values = res.data.map((item) => parseFloat((item.areaRatio * 100).toFixed(2))); // 转换为百分比,保留两位小数
+
+                // 更新图表数据
+                regionChartData.value = {
+                    categories,
+                    values,
+                };
+
+                // 找到最大占比的物候期
+                let maxRatio = 0;
+                let maxPhenology = "";
+                res.data.forEach((item) => {
+                    if (item.areaRatio > maxRatio) {
+                        maxRatio = item.areaRatio;
+                        maxPhenology = item.phenologyName;
+                    }
+                });
+
+                // 更新摘要文字
+                if (maxPhenology) {
+                    const maxPercent = (maxRatio * 100).toFixed(1);
+                    regionSummaryText.value = `${maxPhenology}的种植面积最大,占比${maxPercent}%`;
+                } else {
+                    regionSummaryText.value = "暂无数据";
+                }
+            } else {
+                regionChartData.value = {
+                    categories: [],
+                    values: [],
+                };
+                regionSummaryText.value = "暂无数据";
+            }
+        })
+        .catch((error) => {
+            console.error("获取物候期占比数据失败:", error);
+            regionChartData.value = {
+                categories: [],
+                values: [],
+            };
+            regionSummaryText.value = "暂无数据";
+        });
+};
 
 const fetchAreaTrend = () => {
     const params = {
         speciesIds: [1, 222, 60876],
-        adminCode: "156440000",
+        adminCode: props.areaCode,
         adminLevel: "province",
     };
     VE_API.warning.fetchAreaTrend(params).then((res) => {
@@ -141,17 +300,17 @@ const fetchAreaTrend = () => {
 
             // 转换为数组并排序
             const xAxisData = Array.from(timePointSet).sort((a, b) => {
-                const [yearA, quarterA] = a.split('Q').map(Number);
-                const [yearB, quarterB] = b.split('Q').map(Number);
+                const [yearA, quarterA] = a.split("Q").map(Number);
+                const [yearB, quarterB] = b.split("Q").map(Number);
                 if (yearA !== yearB) return yearA - yearB;
                 return quarterA - quarterB;
             });
 
             // 定义颜色映射(可以根据需要调整)
             const colorMap = {
-                '荔枝': '#2199F8',
-                '籼稻': '#178B00',
-                '小麦': '#FAA53D',
+                荔枝: "#2199F8",
+                籼稻: "#178B00",
+                小麦: "#FAA53D",
             };
 
             // 为每个作物创建系列数据
@@ -170,15 +329,15 @@ const fetchAreaTrend = () => {
 
                 return {
                     name: species.speciesName,
-                    type: 'line',
+                    type: "line",
                     smooth: true,
                     showSymbol: false,
                     data: data,
                     itemStyle: {
-                        color: colorMap[species.speciesName] || '#2199F8',
+                        color: colorMap[species.speciesName] || "#2199F8",
                     },
                     lineStyle: {
-                        color: colorMap[species.speciesName] || '#2199F8',
+                        color: colorMap[species.speciesName] || "#2199F8",
                     },
                 };
             });
@@ -188,15 +347,28 @@ const fetchAreaTrend = () => {
                 xAxisData,
                 series,
             };
+        } else {
+            console.log("空数据");
+            // 接口返回空数据时,清空图表数据
+            areaTrendChartData.value = {
+                xAxisData: [],
+                series: [],
+            };
         }
+    }).catch(() => {
+        // 错误时也清空数据
+        areaTrendChartData.value = {
+            xAxisData: [],
+            series: [],
+        };
     });
 };
 
 const fetchStatRegionAreaRatio = () => {
     const params = {
-        speciesId: 222,
-        adminCode: 156441400,
-        adminLevel: "city",
+        speciesId: "1",
+        adminCode: props.areaCode,
+        adminLevel: "",
     };
     VE_API.warning.fetchStatRegionAreaRatio(params).then((res) => {
         if (res.code === 0 && res.data && res.data.length > 0) {
@@ -228,15 +400,28 @@ const fetchStatRegionAreaRatio = () => {
                 regionSummaryText.value = "暂无数据";
             }
         } else {
+            // 接口返回空数据时,清空图表数据
+            regionChartData.value = {
+                categories: [],
+                values: [],
+            };
             regionSummaryText.value = "暂无数据";
         }
+    }).catch((error) => {
+        console.error("获取区域面积占比数据失败:", error);
+        // 错误时也清空数据
+        regionChartData.value = {
+            categories: [],
+            values: [],
+        };
+        regionSummaryText.value = "暂无数据";
     });
 };
 
 const fetchStatSpeciesAreaYield = () => {
     const params = {
         year: 2025,
-        adminCode: null,
+        adminCode: props.areaCode,
         adminLevel: null,
     };
     VE_API.warning.fetchStatSpeciesAreaYield(params).then((res) => {
@@ -284,8 +469,25 @@ const fetchStatSpeciesAreaYield = () => {
                 yieldSummaryText.value = "暂无数据";
             }
         } else {
+            // 接口返回空数据时,清空图表数据
+            pieChartData.value = [];
+            totalArea.value = 0;
+            yieldChartData.value = {
+                categories: [],
+                values: [],
+            };
             yieldSummaryText.value = "暂无数据";
         }
+    }).catch((error) => {
+        console.error("获取作物面积产量数据失败:", error);
+        // 错误时也清空数据
+        pieChartData.value = [];
+        totalArea.value = 0;
+        yieldChartData.value = {
+            categories: [],
+            values: [],
+        };
+        yieldSummaryText.value = "暂无数据";
     });
 };
 </script>

+ 6 - 0
src/views/warningHome/components/chart_components/chartOption.js

@@ -1,6 +1,7 @@
 import * as echarts from "echarts";
 
 const commonxAxis = {
+    axisTick: { show: false },
     axisLabel: { color: '#9F9F9F' },
     axisLine: { lineStyle: { color: 'rgba(185, 185, 185, 0.12)' } },
 }
@@ -79,6 +80,11 @@ export const barOption = {
     xAxis: {
         type: 'category',
         ...commonxAxis,
+        axisLabel: {
+            ...commonxAxis.axisLabel,
+            interval: 0,
+            rotate: 0, // 使用滚动后,不需要旋转标签
+        },
         data: ['从化区', '增城区', '花都区', '番禺区', '南沙区', '从化区', '荔湾区']
     },
     yAxis: {

+ 4 - 0
src/views/warningHome/components/chart_components/lineChart.vue

@@ -28,6 +28,10 @@ const initData = () => {
     if (props.chartData && props.chartData.xAxisData && props.chartData.xAxisData.length > 0) {
         newOption.xAxis.data = props.chartData.xAxisData;
         newOption.series = props.chartData.series;
+    } else {
+        // 数据为空时,清空图表
+        newOption.xAxis.data = [];
+        newOption.series = [];
     }
     
     myChart.setOption(newOption);

+ 11 - 0
src/views/warningHome/components/chart_components/pieChart.vue

@@ -39,6 +39,17 @@ const initData = () => {
                 return `${params.marker}${data.name}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${percent}%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${Math.round(data.value)}亩`;
             };
         }
+    } else {
+        // 数据为空时,清空图表
+        newOption.series[0].data = [];
+        newOption.title.subtext = '0';
+        // 恢复默认 tooltip
+        newOption.tooltip.formatter = function (params) {
+            const data = params.data;
+            const total = 3774;
+            const percent = (data.value / total * 100).toFixed(2);
+            return `${params.marker}${data.name}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${percent}%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${data.value}亩`;
+        };
     }
     
     myChart.setOption(newOption);

+ 108 - 77
src/views/warningHome/components/farmInfoGroup.vue

@@ -2,24 +2,23 @@
     <div class="farm-info-group">
         <!-- 顶部标题栏 -->
         <div class="info-header">
-            <div class="back-btn" @click="handleBack">返回</div>
+            <div class="back-btn" v-if="!farmList.length" @click="handleBack">返回</div>
             <div class="title">
                 <img class="title-icon" src="@/assets/images/common/chart-icon.png" alt="" />
-                <span class="title-text">荔博园</span>
+                <span class="title-text">{{ farmList.length ? "农场列表" : "荔博园" }}</span>
             </div>
         </div>
 
         <!-- 主要内容区域 -->
-        <div class="info-content">
-            <div class="farm-info">
+        <div class="info-content" :style="{ height: !farmList.length ? 'calc(100% - 168px)' : 'calc(100% - 50px)' }">
+            <div class="farm-info" v-for="item in farmList" :key="item" @click="handleFarmInfo(item)">
                 <div class="lz-icon">
                     <img src="@/assets/images/warningHome/lz-icon.png" alt="" />
                 </div>
                 <!-- 右侧信息区域 -->
                 <div class="info-right">
                     <div class="info-item farm-code">
-                        <span class="info-label">农场编号:</span>
-                        <span class="info-value">GZ86770</span>
+                        <span class="info-label">{{ !farmList.length ? '农场编号:GZ86770' : item.name }}</span>
                         <div class="tags">
                             <span class="tag">荔枝</span>
                             <span class="tag">井岗红糯</span>
@@ -39,61 +38,75 @@
                     </div>
                 </div>
             </div>
-            <div class="tabs">
-                <div
-                    v-for="tab in tabs"
-                    :key="tab.key"
-                    class="tab-item"
-                    :class="{ active: activeTab === tab.key }"
-                    @click="activeTab = tab.key"
-                >
-                    {{ tab.label }}
-                </div>
-            </div>
-            <div class="tab-content">
-                <div class="timeline" v-if="activeTab === 'crop'">
+            <template v-if="!farmList.length">
+                <div class="tabs">
                     <div
-                        class="timeline-item"
-                        v-for="item in timelineList"
-                        :key="item.id"
+                        v-for="tab in tabs"
+                        :key="tab.key"
+                        class="tab-item"
+                        :class="{ active: activeTab === tab.key }"
+                        @click="activeTab = tab.key"
                     >
-                        <div class="timeline-left">
-                            <div class="dot"></div>
-                            <div class="line"></div>
-                        </div>
-                        <div class="timeline-right">
-                            <div class="date">{{ item.date }}</div>
-                            <div class="text">{{ item.text }}</div>
-                        </div>
+                        {{ tab.label }}
                     </div>
                 </div>
-                <div v-else class="perception-content">
-                    <div class="perception-tabs">
-                        <div
-                            v-for="tab in perceptionTabs"
-                            :key="tab.key"
-                            class="perception-tab"
-                            :class="{ 'perception-tab--active': perceptionActive === tab.key }"
-                            @click="perceptionActive = tab.key"
-                        >
-                            <span class="label">{{ tab.label }}</span>
-                            <div class="underline"></div>
+                <div class="tab-content">
+                    <div class="timeline" v-if="activeTab === 'crop'">
+                        <div class="timeline-item" v-for="item in timelineList" :key="item.id">
+                            <div class="timeline-left">
+                                <div class="dot"></div>
+                                <div class="line"></div>
+                            </div>
+                            <div class="timeline-right">
+                                <div class="date">{{ item.date }}</div>
+                                <div class="text">{{ item.text }}</div>
+                            </div>
                         </div>
                     </div>
-                    <div class="perception-wrap">
-                        <equipment v-if="perceptionActive === 'device'"></equipment>
-                        <crop v-else></crop>
+                    <div v-else class="perception-content">
+                        <div class="perception-tabs">
+                            <div
+                                v-for="tab in perceptionTabs"
+                                :key="tab.key"
+                                class="perception-tab"
+                                :class="{ 'perception-tab--active': perceptionActive === tab.key }"
+                                @click="perceptionActive = tab.key"
+                            >
+                                <span class="label">{{ tab.label }}</span>
+                                <div class="underline"></div>
+                            </div>
+                        </div>
+                        <div class="perception-wrap">
+                            <equipment v-if="perceptionActive === 'device'"></equipment>
+                            <crop v-else></crop>
+                        </div>
                     </div>
                 </div>
-            </div>
+            </template>
         </div>
     </div>
 </template>
 
 <script setup>
-import { ref } from "vue";
+import { ref ,onMounted} from "vue";
 import equipment from "./equipment.vue";
 import crop from "./crop.vue";
+
+
+onMounted(() => {
+    fetchFarmList();
+});
+
+const fetchFarmList = () => {
+    VE_API.warning.fetchFarmList().then(res => {
+        if (res.code === 0 && res.data && res.data.length > 0) {
+            farmList.value = res.data;
+        } else {
+            farmList.value = [];
+        }
+    });
+};
+
 // 底部 tabs 配置
 const tabs = [
     { key: "crop", label: "作物档案" },
@@ -118,8 +131,15 @@ const timelineList = ref([
     { id: 5, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
 ]);
 
+const farmList = ref([]);
 const handleBack = () => {
-    // 返回逻辑
+    farmList.value = [
+        { id: 1, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
+        { id: 2, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
+        { id: 3, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
+        { id: 4, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
+        { id: 5, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
+    ];
 };
 
 const handleCropArchive = () => {
@@ -129,6 +149,10 @@ const handleCropArchive = () => {
 const handlePerceptionRecord = () => {
     // 感知记录逻辑
 };
+
+const handleFarmInfo = (item) => {
+    farmList.value = [item];
+};
 </script>
 <style lang="scss" scoped>
 .farm-info-group {
@@ -165,9 +189,15 @@ const handlePerceptionRecord = () => {
             }
         }
     }
+
     .info-content {
         padding: 5px 10px;
         height: calc(100% - 168px);
+        overflow-y: auto;
+
+        .farm-info + .farm-info {
+            margin-top: 12px;
+        }
         .farm-info {
             display: flex;
             align-items: flex-start;
@@ -187,8 +217,9 @@ const handlePerceptionRecord = () => {
                 .info-item {
                     display: flex;
                     align-items: center;
-                    gap: 8px;
                     line-height: 18px;
+                    gap: 6px;
+                    cursor: pointer;
                     &.farm-code {
                         font-weight: 700;
                         font-size: 16px;
@@ -202,9 +233,9 @@ const handlePerceptionRecord = () => {
 
                         .tag {
                             padding: 1px 6px;
-                            border: 1px solid #FFD489;
+                            border: 1px solid #ffd489;
                             border-radius: 2px;
-                            color: #FFD489;
+                            color: #ffd489;
                             font-size: 12px;
                             font-weight: 400;
                         }
@@ -216,7 +247,7 @@ const handlePerceptionRecord = () => {
             display: flex;
             gap: 10px;
             margin-top: 12px;
-            
+
             .tab-item {
                 padding: 6px 32px;
                 text-align: center;
@@ -225,86 +256,86 @@ const handlePerceptionRecord = () => {
                 color: #ffffff;
                 background: rgba(166, 166, 166, 0.08);
                 border: 1px solid transparent;
-                
+
                 &.active {
-                    color: #FFD489;
+                    color: #ffd489;
                     background: rgba(255, 212, 137, 0.2);
-                    border: 1px solid #FFD489;
+                    border: 1px solid #ffd489;
                 }
             }
         }
-        .tab-content{
+        .tab-content {
             margin: 12px 0;
             height: calc(100% - 24px);
             overflow-y: auto;
-            .timeline{
+            .timeline {
                 padding-right: 12px;
-                .timeline-item{
+                .timeline-item {
                     display: flex;
                     align-items: flex-start;
                     font-size: 14px;
                     color: #ffffff;
                     line-height: 22px;
-                    & + .timeline-item{
+                    & + .timeline-item {
                         margin-top: 24px;
                     }
-                    .timeline-left{
+                    .timeline-left {
                         width: 24px;
                         display: flex;
                         flex-direction: column;
                         align-items: center;
-                        .dot{
+                        .dot {
                             width: 6px;
                             height: 6px;
                             border-radius: 50%;
-                            background: #FFD489;
+                            background: #ffd489;
                             margin-top: 6px;
                         }
-                        .line{
+                        .line {
                             border-left: 1px dashed rgba(255, 212, 137, 0.6);
                             margin-top: 4px;
                             height: 40px;
                         }
                     }
-                    .timeline-right{
+                    .timeline-right {
                         padding-left: 8px;
-                        .date{
-                            color: #FFD489;
+                        .date {
+                            color: #ffd489;
                             font-weight: 500;
                             margin-bottom: 2px;
                         }
-                        .text{
-                            color: #D7D7D7;
+                        .text {
+                            color: #d7d7d7;
                         }
                     }
                 }
             }
-            .perception-content{
+            .perception-content {
                 height: 100%;
-                .perception-tabs{
+                .perception-tabs {
                     display: flex;
                     align-items: flex-end;
                     gap: 20px;
                     margin-bottom: 12px;
-                    .perception-tab{
-                        color: #FFFFFF;
+                    .perception-tab {
+                        color: #ffffff;
                         cursor: pointer;
-                        .underline{
+                        .underline {
                             margin-top: 6px;
                             width: 56px;
                             height: 2px;
                             background: transparent;
                             border-radius: 2px;
                         }
-                        &.perception-tab--active{
-                            color: #FFD489;
-                            .underline{
-                                background: #FFD489;
+                        &.perception-tab--active {
+                            color: #ffd489;
+                            .underline {
+                                background: #ffd489;
                             }
                         }
                     }
                 }
-                .perception-wrap{
+                .perception-wrap {
                     height: calc(100% - 40px);
                     overflow-y: auto;
                 }

+ 5 - 2
src/views/warningHome/components/timeLine.vue

@@ -58,7 +58,7 @@ const isCounting = ref(false);
 
 // 计算进度条百分比位置(根据数据数量动态计算)
 const numList = computed(() => {
-    return [4,16.5,29.5,43.5,56.5,69.5,82.8,95.5];
+    return [4,17,31,45,59,72,86,99];
     // const count = list.value.length;
     // if (count <= 1) return [100];
     // const step = 100 / (count - 1);
@@ -157,7 +157,7 @@ defineExpose({ restActive });
     .line {
         background: linear-gradient(30deg, #fff 0% , rgba(44, 44, 44,0.6) 100%);
         border-radius: 2px;
-        width: 90%;
+        width: 88%;
         height: 2px;
         display: flex;
         justify-content: space-between;
@@ -220,6 +220,9 @@ defineExpose({ restActive });
                 margin-left: -8px;
             }
         }
+        .mr{
+            margin-right: -35px;
+        }
     }
 }
 </style>

+ 83 - 30
src/views/warningHome/index.vue

@@ -7,6 +7,7 @@
                     <div class="top-l yes-events">
                         <div>
                             <el-cascader
+                                ref="cascaderRef"
                                 style="width: 184px"
                                 :show-all-levels="false"
                                 v-model="areaVal"
@@ -16,7 +17,7 @@
                             />
                         </div>
                     </div>
-                    <div class="top-r yes-events">
+                    <div class="top-r yes-events" v-show="activeBaseTab !== '作物分布'">
                         <div class="data-box" :class="{ active: activeBoxName === '面积' }">
                             <div class="data-value">
                                 <span>{{ regionCropData.plantArea }}</span
@@ -73,7 +74,7 @@
                 </el-tree>
             </div>
             <div v-if="panelType === 0" class="warning-r right chart-wrap yes-events">
-                <chart-list :activeBaseTab="activeBaseTab"></chart-list>
+                <chart-list :activeBaseTab="activeBaseTab" :areaCode="selectedAreaCode" :areaName="selectedAreaName"></chart-list>
                 <!-- <farmInfoGroup></farmInfoGroup> -->
             </div>
             <div v-if="panelType === 1" class="warning-r right yes-events">
@@ -259,22 +260,19 @@ onMounted(async () => {
         handleTimeChange(index)
     })
 
-    // 注意:getRegionCropAreaYield 会在省级列表加载完成后自动调用(在 lazyLoad 中)
-    // 如果 areaVal 已经有值,则立即调用
-    if (areaVal.value && areaVal.value.length > 0) {
-        getRegionCropAreaYield(areaVal.value[areaVal.value.length - 1]);
-    }
+    // 初始化区域选择器的默认值
+    initAreaDefaultValue();
 });
 
 const handleTimeChange = (index) => {
     console.log('index', index);
 }
 
-const getRegionCropAreaYield = async (adminCode) => {
-    const code = adminCode || (areaVal.value && areaVal.value.length > 0 ? areaVal.value[areaVal.value.length - 1] : '156440000');
-    
+const getRegionCropAreaYield = async () => {
+    if (activeBaseTab.value === '作物分布') return;
     const res = await VE_API.warning.fetchRegionCropAreaYield({
-        adminCode: code,
+        adminCode: selectedAreaCode.value,
+        speciesId: 1,
         year: '',
         quarter: '',
     });
@@ -287,6 +285,25 @@ const getRegionCropAreaYield = async (adminCode) => {
     }
 };
 
+// 初始化区域选择器的默认值
+const initAreaDefaultValue = async () => {
+    try {
+        const res = await VE_API.species.provinceList();
+        if (res.code === 0 && res.data && res.data.length > 0) {
+            // 设置第一个省为默认值
+            const firstProvinceCode = res.data[0].provCode || res.data[0].code;
+            const firstProvinceName = res.data[0].provName || res.data[0].name;
+            areaVal.value = [firstProvinceCode];
+            selectedAreaCode.value = firstProvinceCode;
+            selectedAreaName.value = firstProvinceName;
+            // 保存到映射中
+            areaCodeNameMap.value.set(firstProvinceCode, firstProvinceName);
+        }
+    } catch (error) {
+        console.error('初始化区域默认值失败:', error);
+    }
+};
+
 sessionStorage.removeItem("farmId");
 
 onUnmounted(() => {
@@ -420,6 +437,7 @@ const destroyPopup = () => {
 
 const handleTabClick = (item) => {
     activeBaseTab.value = item;
+    getRegionCropAreaYield();
 
     // 切换 Tab 时,先清空农场分布图层上的旧数据
     if (distributionLayer) {
@@ -476,7 +494,6 @@ const handleTabClick = (item) => {
                 }
                 return firstLevel;
             });
-            console.log('222222222222treeActionData.value', treeActionData.value);
             handlePhenologyTreeDefault();
             handlePhenologyLayer();
             break;
@@ -569,17 +586,25 @@ const props1 = {
             // 第一级:获取省级列表
             VE_API.species.provinceList().then((res) => {
                 if (res.code === 0 && res.data) {
-                    const nodes = res.data.map((item) => ({
-                        value: item.provCode || item.code, // 使用code,不使用id
-                        label: item.provName || item.name,
-                        leaf: false, // 省级不是叶子节点
-                    }));
+                    const nodes = res.data.map((item) => {
+                        const code = item.provCode || item.code;
+                        const name = item.provName || item.name;
+                        // 保存 code 到 name 的映射
+                        areaCodeNameMap.value.set(code, name);
+                        return {
+                            value: code, // 使用code,不使用id
+                            label: name,
+                            leaf: false, // 省级不是叶子节点
+                        };
+                    });
                     if (nodes.length > 0) {
                         // 设置第一个省的code
                         const firstProvinceCode = nodes[0].value;
                         areaVal.value = [firstProvinceCode];
+                        selectedAreaCode.value = firstProvinceCode;
+                        selectedAreaName.value = nodes[0].label;
                         // 使用第一个省的code初始化数据
-                        getRegionCropAreaYield(firstProvinceCode);
+                        getRegionCropAreaYield();
                     }
                     resolve(nodes);
                 } else {
@@ -593,11 +618,17 @@ const props1 = {
             const provCode = node.value;
             VE_API.species.cityList({ provCode }).then((res) => {
                 if (res.code === 0 && res.data) {
-                    const nodes = res.data.map((item) => ({
-                        value: item.cityCode || item.code,
-                        label: item.cityName || item.name,
-                        leaf: false, // 市级不是叶子节点
-                    }));
+                    const nodes = res.data.map((item) => {
+                        const code = item.cityCode || item.code;
+                        const name = item.cityName || item.name;
+                        // 保存 code 到 name 的映射
+                        areaCodeNameMap.value.set(code, name);
+                        return {
+                            value: code,
+                            label: name,
+                            leaf: false, // 市级不是叶子节点
+                        };
+                    });
                     resolve(nodes);
                 } else {
                     resolve([]);
@@ -610,11 +641,17 @@ const props1 = {
             const cityCode = node.value;
             VE_API.species.districtList({ cityCode }).then((res) => {
                 if (res.code === 0 && res.data) {
-                    const nodes = res.data.map((item) => ({
-                        value: item.districtCode || item.code,
-                        label: item.districtName || item.name,
-                        leaf: true, // 区级是叶子节点
-                    }));
+                    const nodes = res.data.map((item) => {
+                        const code = item.districtCode || item.code;
+                        const name = item.districtName || item.name;
+                        // 保存 code 到 name 的映射
+                        areaCodeNameMap.value.set(code, name);
+                        return {
+                            value: code,
+                            label: name,
+                            leaf: true, // 区级是叶子节点
+                        };
+                    });
                     resolve(nodes);
                 } else {
                     resolve([]);
@@ -628,13 +665,29 @@ const props1 = {
     },
 };
 
+const selectedAreaCode = ref(null);
+const selectedAreaName = ref(null);
+const cascaderRef = ref(null);
+
+// 保存 code 到 name 的映射关系
+const areaCodeNameMap = ref(new Map());
+
 const toggleArea = (v) => {
     activeBoxName.value = null;
     // 获取选中的最后一个值(即最终的code)
     const selectedCode = v && v.length > 0 ? v[v.length - 1] : null;
+    selectedAreaCode.value = selectedCode;
+    
+    // 从映射中获取对应的 name
+    if (selectedCode && areaCodeNameMap.value.has(selectedCode)) {
+        selectedAreaName.value = areaCodeNameMap.value.get(selectedCode);
+    } else {
+        selectedAreaName.value = null;
+    }
+    
     if (selectedCode) {
         // 调用接口更新区域作物数据
-        getRegionCropAreaYield(selectedCode);
+        getRegionCropAreaYield();
     }
 };
 
@@ -1064,7 +1117,7 @@ const getTreeChecks = async (nodeData, data) => {
             position: fixed;
             bottom: 20px;
             left: 20px;
-            width: 1080px;
+            width: 950px;
             height: 71px;
         }
     }