Jelajahi Sumber

feat:修改对接一张图页面

wangsisi 2 hari lalu
induk
melakukan
a2b9a0ff58

+ 1 - 1
src/api/config.js

@@ -3,7 +3,7 @@ let serverMock = "https://mock.apipark.cn/m1/4662471-4313509-default/"
 let pythonServe = VE_ENV.PYSERVER
 // let djiCloudBase = "http://127.0.0.1/";
 let djiCloudBase = "https://djiapi.sysuimars.com/";
-let oneMapServer = "https://one-map-api.feiniaotech.com/";
+let oneMapServer = "https://zytt-one-map-api.feiniaotech.com/";
 
 export const base_url = server + "site/";
 export const base_dev_url = server + "mini/";

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

@@ -64,5 +64,25 @@ export default {
         url: config.one_map_url + "agri_farm_device_record/list",
         type: "get",
     },
+    //按地块类型统计面积及面积占比
+    fetchStatLandTypeAreaRatio: {
+        url: config.one_map_url + "agri_land_crop/stat_land_type_area_ratio",
+        type: "get",
+    },
+    //根据作物类型ID统计物候期面积及面积占比
+    fetchStatPhenologyAreaRatioByCropType: {
+        url: config.one_map_url + "agri_land_crop/stat_phenology_area_ratio_by_crop_type",
+        type: "get",
+    },
+    //获取地图图例
+    fetchMapLegend: {
+        url: config.one_map_url + "agri_common/crop_types",
+        type: "get",
+    },
+    //获取地图图例
+    fetchLandTypes: {
+        url: config.one_map_url + "agri_common/land_types",
+        type: "get",
+    },
 }
 

+ 9 - 0
src/router/globalRoutes.js

@@ -8,11 +8,20 @@
  */
 export default [
     {
+        path: "/",
+        redirect: "/auto_login",
+    },
+    {
         path: "/login",
         name: "Login",
         component: () => import(/* @vite-ignore */ "@/views/Login.vue"),
     },
     {
+        path: "/auto_login",
+        name: "AutoLogin",
+        component: () => import(/* @vite-ignore */ "@/views/AutoLogin.vue"),
+    },
+    {
         path: "/authentic_login",
         name: "AuthenticLogin",
         component: () => import(/* @vite-ignore */ "@/views/AuthenticLogin.vue"),

+ 99 - 0
src/views/AutoLogin.vue

@@ -0,0 +1,99 @@
+<template>
+    <div
+        class="auto-login-page"
+        v-loading="true"
+        element-loading-text="正在跳转中,请稍候..."
+        element-loading-background="rgba(255, 255, 255, 0.5)"
+    ></div>
+</template>
+
+<script setup>
+import { SET_TOKEN, SET_UNAME, SET_USER_INFO } from "@/store/modules/app/type";
+import { ref, reactive, toRefs, onMounted } from "vue";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+
+const store = useStore();
+const router = useRouter();
+
+// 与原 Login 页面保持一致的账号配置
+const guestAccount = { pwd: "游客", userName: "游客" };
+const regularAccount = { pwd: "qz123456789", userName: "13797066447" };
+
+const form = reactive({
+    pwd: "",
+    userName: "",
+});
+const { userName, pwd } = toRefs(form);
+const ref_form = ref(null);
+const success = ref(false);
+
+// 进入页面先清理登录状态
+sessionStorage.clear();
+store.commit(`app/${SET_TOKEN}`, "");
+router.options.isAddDynamicMenuRoutes = false;
+
+// 根据时间设置账号(与 Login.vue 完全一致)
+const setAccountByTime = () => {
+    const now = new Date();
+    const currentHour = now.getHours();
+
+    if (currentHour >= 18 && currentHour < 24) {
+        form.pwd = guestAccount.pwd;
+        form.userName = guestAccount.userName;
+    } else {
+        form.pwd = regularAccount.pwd;
+        form.userName = regularAccount.userName;
+    }
+};
+
+// 初始化账号
+setAccountByTime();
+
+// 直接调用登录接口,不做表单校验
+const doAutoLogin = async () => {
+    try {
+        const res = await VE_API.system.login(form);
+        if (res.code == 0) {
+            const { token, userName, djiCloudToken } = res.data;
+            store.commit(`app/${SET_TOKEN}`, token);
+            store.commit(`app/djiCloudToken`, djiCloudToken);
+            store.commit(`app/${SET_UNAME}`, userName);
+            res.data["token"] = undefined;
+            res.data["x-auth-token"] = undefined;
+            res.data["pwd"] = undefined;
+            store.commit(`app/${SET_USER_INFO}`, JSON.stringify(res.data));
+            success.value = true;
+            // 登录成功后直接跳转到预警首页(与 Login.vue 一致)
+            router.push({ name: "warningHome" });
+        }
+    } catch (e) {
+        // 自动登录失败时,跳到手动登录页
+        router.push({ name: "AuthenticLogin" });
+    }
+};
+
+onMounted(() => {
+    doAutoLogin();
+});
+</script>
+
+<style lang="scss" scoped>
+.auto-login-page {
+    height: 100vh;
+    width: 100%;
+    background: rgba(29, 29, 29, 0.24);
+    ::v-deep(.el-loading-text) {
+        font-size: 28px;
+    }
+    ::v-deep(.el-loading-spinner) {
+        .circular {
+            width: 60px;
+            height: 60px;
+            margin-top: -40px;
+        }
+    }
+}
+</style>
+
+

+ 116 - 251
src/views/warningHome/components/chart_components/chartList.vue

@@ -1,10 +1,13 @@
 <template>
     <div class="chart-list">
         <div class="chart-item">
-            <chart-box :name="`${props.areaName}作物种植面积占比`">
+            <chart-box :name="`四川省斑块占比统计`">
                 <div class="box-content">
                     <div class="chart-dom">
-                        <pie-chart :key="`pie-${activeBaseTab}`" :chartData="pieChartData" :totalArea="totalArea"></pie-chart>
+                        <pie-chart
+                            :chartData="pieChartData"
+                            :totalArea="totalArea"
+                        ></pie-chart>
                     </div>
                     <div class="box-bg">
                         <div class="legend-list">
@@ -23,7 +26,23 @@
             </chart-box>
         </div>
         <div class="chart-item">
-            <chart-box :name="`${props.areaName}小麦物候进程分布`">
+            <chart-box :name="`四川省物候进程分布`">
+                <template #title-right>
+                    <el-select
+                        v-model="selectedPieItem"
+                        placeholder="全部"
+                        size="small"
+                        style="width: 100px"
+                        @change="fetchStatPhenologyAreaRatioByCropType"
+                    >
+                        <el-option
+                            v-for="item in options"
+                            :key="item.speciesId"
+                            :label="item.speciesName"
+                            :value="item.speciesId"
+                        />
+                    </el-select>
+                </template>
                 <div class="box-content">
                     <div class="chart-dom">
                         <bar-chart :key="`region-${activeBaseTab}`" :chartData="regionChartData"></bar-chart>
@@ -33,10 +52,14 @@
             </chart-box>
         </div>
         <div class="chart-item">
-            <chart-box :name="threeTitle"> 
+            <chart-box name="四川省用地面积统计">
                 <div class="box-content">
                     <div class="chart-dom">
-                        <bar-chart :key="`yield-${activeBaseTab}`" :chartData="yieldChartData" :yAxisFormatter="yAxisFormatter"></bar-chart>
+                        <bar-chart
+                            :key="`yield-${activeBaseTab}`"
+                            :chartData="yieldChartData"
+                            :yAxisFormatter="yAxisFormatter"
+                        ></bar-chart>
                     </div>
                     <div class="box-bg">{{ yieldSummaryText }}</div>
                 </div>
@@ -48,8 +71,7 @@
 <script setup>
 import chartBox from "@/components/chartBox.vue";
 import pieChart from "./pieChart.vue";
-import lineChart from "./lineChart.vue";
-import { computed, ref, watch,onMounted} from "vue";
+import { computed, ref, watch, onMounted } from "vue";
 import { pieOption } from "./chartOption.js";
 import barChart from "./barChart.vue";
 
@@ -72,6 +94,9 @@ const props = defineProps({
 const pieChartData = ref([]);
 const totalArea = ref(0);
 
+// 物候进程分布标题右侧下拉框选中项(来自饼图数据)
+const selectedPieItem = ref(null);
+
 // 区域占比图表数据
 const regionChartData = ref({
     categories: [], // 区域名称数组
@@ -90,15 +115,9 @@ const yieldChartData = ref({
 // 预估产量摘要文字
 const yieldSummaryText = ref("暂无数据");
 
-// 根据 activeBaseTab 动态设置 y 轴单位
+// y 轴不显示单位
 const yAxisFormatter = computed(() => {
-    return props.activeBaseTab === "物候期分布" ? "{value}亩" : "{value}吨";
-});
-
-// 种植面积趋势图表数据
-const areaTrendChartData = ref({
-    xAxisData: [], // 时间轴数据
-    series: [], // 系列数据
+    return "{value}%";
 });
 
 // 计算图例数据
@@ -108,15 +127,27 @@ const legendData = computed(() => {
     }
     return pieChartData.value.map((item, index) => {
         const percent = ((item.value / totalArea.value) * 100).toFixed(1);
+        // 优先使用地图图例接口返回的颜色(与名称对应),没有匹配到再使用默认颜色
+        const legendItem = legendArr.value.find((l) => l.name === item.name);
+        const color = legendItem && legendItem.color ? legendItem.color : pieOption.color[index % pieOption.color.length];
         return {
             name: item.name,
             value: Math.round(item.value), // 显示整数
             percent: percent,
-            color: pieOption.color[index % pieOption.color.length],
+            color: color,
         };
     });
 });
 
+const legendArr = ref([]);
+const fetchMapLegend = () => {
+    VE_API.warning.fetchMapLegend().then((res) => {
+        if (res.code === 0 && res.data && res.data.length > 0) {
+            legendArr.value = res.data;
+        }
+    });
+};
+
 const threeTitle = ref(`${props.areaName}作物预估产量对比`);
 
 // 监听 activeBaseTab 变化
@@ -137,9 +168,6 @@ watch(
     }
 );
 
-const currentYear = ref(2025);
-const currentQuarter = ref(1);
-
 // 监听 areaName 变化,更新标题
 watch(
     () => props.areaName,
@@ -155,84 +183,83 @@ watch(
 onMounted(() => {
     // 初始化时调用一次
     initData();
-    // eventBus.off("weatherTime:changeTime");
-    // eventBus.on("weatherTime:changeTime", ({index, year, quarter}) => {
-    //     currentYear.value = year;
-    //     currentQuarter.value = quarter;
-    //     initData() 
-    // });
 });
 
 const initData = () => {
-    // 切换 tab 时,先清空所有图表数据,避免显示旧数据
-    fetchStatSpeciesAreaYield();
-    if (props.activeBaseTab === "物候期分布") {
-        // 清空其他 tab 的数据
-        pieChartData.value = [];
-        totalArea.value = 0;
-        areaTrendChartData.value = { xAxisData: [], series: [] };
-        
-        threeTitle.value = `${props.areaName}小麦预告产量统计`;
-        fetchStatPhenologyRatio();
-        fetchStatRegionYieldRatio();
-    } else if (props.activeBaseTab === "作物分布") {
-        // 清空物候期分布的数据
-        regionChartData.value = { categories: [], values: [] };
-        regionSummaryText.value = "暂无数据";
-        
-        threeTitle.value = `${props.areaName}作物预估产量对比`;
-        fetchAreaTrend();
-    } else {
-        // // 其他 tab(预警分布、农场分布、农服管理)时,清空所有图表数据
-        // pieChartData.value = [];
-        // totalArea.value = 0;
-        // regionChartData.value = { categories: [], values: [] };
-        // areaTrendChartData.value = { xAxisData: [], series: [] };
-        // yieldChartData.value = { categories: [], values: [] };
-        // regionSummaryText.value = "暂无数据";
-        // yieldSummaryText.value = "暂无数据";
-    }
+    fetchStatRegionAreaRatio();
+    fetchStatLandTypeAreaRatio();
+    fetchStatPhenologyAreaRatioByCropType();
+};
+
+const options = ref([]);
+//种植面积占比
+const fetchStatRegionAreaRatio = () => {
+    VE_API.warning
+        .fetchStatRegionAreaRatio()
+        .then((res) => {
+            if (res.code === 0 && res.data && res.data.length > 0) {
+                // 转换接口数据为饼图格式
+                // 饼图数据格式:{ value: 种植面积, name: 物种名称 }
+                const chartData = res.data.map((item) => ({
+                    value: item.plantArea, // 种植面积
+                    name: item.speciesName, // 物种名称
+                }));
+
+                // 计算总种植面积
+                const total = chartData.reduce((sum, item) => sum + item.value, 0);
+
+                // 更新饼图数据
+                pieChartData.value = chartData;
+                totalArea.value = total;
+                options.value = res.data
+                options.value.unshift({ speciesId: null, speciesName: "全部" });
+                fetchMapLegend();
+            } else {
+                // 接口返回空数据时,仅清空饼图数据
+                pieChartData.value = [];
+                totalArea.value = 0;
+            }
+        })
+        .catch((error) => {
+            console.error("获取区域产量占比数据失败:", error);
+            yieldChartData.value = {
+                categories: [],
+                values: [],
+            };
+            yieldSummaryText.value = "暂无数据";
+        });
 };
 
-//统计指定物种在下级区划中的预估产量占比
-const fetchStatRegionYieldRatio = () => {
-    const params = {
-        speciesId: 1,
-        adminCode: props.areaCode,
-        adminLevel: "province",
-    };
+//按地块类型统计面积及面积占比
+const fetchStatLandTypeAreaRatio = () => {
     VE_API.warning
-        .fetchStatRegionYieldRatio(params)
+        .fetchStatLandTypeAreaRatio()
         .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)));
+                const categories = res.data.map((item) => item.landTypeName);
+                const values = res.data.map((item) => parseFloat((item.areaRatio * 100).toFixed(2))); // 转换为百分比,保留两位小数
 
-                // 更新图表数据
+                // 更新图表数据到 yieldChartData
                 yieldChartData.value = {
                     categories,
                     values,
                 };
 
-                // 找到最大值的区域
-                let maxValue = 0;
-                let maxRegion = "";
+                // 找到最大占比的物候期
+                let maxRatio = 0;
+                let maxPhenology = "";
                 res.data.forEach((item) => {
-                    if (item.expectYield > maxValue) {
-                        maxValue = item.expectYield;
-                        maxRegion = item.adminName;
+                    if (item.areaRatio > maxRatio) {
+                        maxRatio = item.areaRatio;
+                        maxPhenology = item.landTypeName;
                     }
                 });
 
                 // 更新摘要文字
-                if (maxRegion) {
-                    const maxValueFormatted = maxValue.toFixed(1);
-                    const unit = props.activeBaseTab === "物候期分布" ? "亩" : "吨";
-                    yieldSummaryText.value = `${maxRegion}的${
-                        props.activeBaseTab === "物候期分布" ? "种植面积" : "预估产量"
-                    }最大,为${maxValueFormatted}${unit}`;
+                if (maxPhenology) {
+                    const maxPercent = (maxRatio * 100).toFixed(1);
+                    yieldSummaryText.value = `${maxPhenology}的种植面积最大,占比${maxPercent}%`;
                 } else {
                     yieldSummaryText.value = "暂无数据";
                 }
@@ -245,7 +272,7 @@ const fetchStatRegionYieldRatio = () => {
             }
         })
         .catch((error) => {
-            console.error("获取区域产量占比数据失败:", error);
+            console.error("获取地块类型面积占比数据失败:", error);
             yieldChartData.value = {
                 categories: [],
                 values: [],
@@ -254,24 +281,17 @@ const fetchStatRegionYieldRatio = () => {
         });
 };
 
-//统计指定物种在不同物候期下的面积占比
-const fetchStatPhenologyRatio = () => {
-    const params = {
-        speciesId: 1,
-        adminCode: props.areaCode,
-        adminLevel: "province",
-    };
-
+const fetchStatPhenologyAreaRatioByCropType = () => {
     VE_API.warning
-        .fetchStatPhenologyRatio(params)
+        .fetchStatPhenologyAreaRatioByCropType({ cropTypeId: selectedPieItem.value })
         .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))); // 转换为百分比,保留两位小数
+                // 转换接口数据为图表格式(物候进程分布)
+                // 横轴:物候期名称,纵轴:面积占比(百分比)
+                const categories = res.data.map((item) => item.speciesName);
+                const values = res.data.map((item) => parseFloat((item.areaRatio * 100).toFixed(2)));
 
-                // 更新图表数据
+                // 更新区域图表数据
                 regionChartData.value = {
                     categories,
                     values,
@@ -283,7 +303,7 @@ const fetchStatPhenologyRatio = () => {
                 res.data.forEach((item) => {
                     if (item.areaRatio > maxRatio) {
                         maxRatio = item.areaRatio;
-                        maxPhenology = item.phenologyName;
+                        maxPhenology = item.speciesName;
                     }
                 });
 
@@ -295,6 +315,7 @@ const fetchStatPhenologyRatio = () => {
                     regionSummaryText.value = "暂无数据";
                 }
             } else {
+                // 接口返回空数据时,清空区域图表数据
                 regionChartData.value = {
                     categories: [],
                     values: [],
@@ -302,8 +323,8 @@ const fetchStatPhenologyRatio = () => {
                 regionSummaryText.value = "暂无数据";
             }
         })
-        .catch((error) => {
-            console.error("获取物候期占比数据失败:", error);
+        .catch(() => {
+            // 错误时也清空数据
             regionChartData.value = {
                 categories: [],
                 values: [],
@@ -311,162 +332,6 @@ const fetchStatPhenologyRatio = () => {
             regionSummaryText.value = "暂无数据";
         });
 };
-
-const fetchAreaTrend = () => {
-    const params = {
-        speciesIds: [1, 222, 60876],
-        adminCode: props.areaCode,
-        adminLevel: "province",
-    };
-    VE_API.warning.fetchAreaTrend(params).then((res) => {
-        if (res.code === 0 && res.data && res.data.length > 0) {
-            // 收集所有唯一的时间点(year-quarter组合)
-            const timePointSet = new Set();
-            res.data.forEach((species) => {
-                species.timeSeries.forEach((item) => {
-                    const timeKey = `${item.year}Q${item.quarter}`;
-                    timePointSet.add(timeKey);
-                });
-            });
-
-            // 转换为数组并排序
-            const xAxisData = Array.from(timePointSet).sort((a, b) => {
-                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",
-            };
-
-            // 为每个作物创建系列数据
-            const series = res.data.map((species) => {
-                // 创建时间点到种植面积的映射
-                const dataMap = new Map();
-                species.timeSeries.forEach((item) => {
-                    const timeKey = `${item.year}Q${item.quarter}`;
-                    dataMap.set(timeKey, item.plantArea);
-                });
-
-                // 根据 xAxisData 顺序生成数据数组
-                const data = xAxisData.map((timeKey) => {
-                    return dataMap.has(timeKey) ? parseFloat(dataMap.get(timeKey).toFixed(2)) : null;
-                });
-
-                return {
-                    name: species.speciesName,
-                    type: "line",
-                    smooth: true,
-                    showSymbol: false,
-                    data: data,
-                    itemStyle: {
-                        color: colorMap[species.speciesName] || "#2199F8",
-                    },
-                    lineStyle: {
-                        color: colorMap[species.speciesName] || "#2199F8",
-                    },
-                };
-            });
-
-            // 更新图表数据
-            areaTrendChartData.value = {
-                xAxisData,
-                series,
-            };
-        } else {
-            console.log("空数据");
-            // 接口返回空数据时,清空图表数据
-            areaTrendChartData.value = {
-                xAxisData: [],
-                series: [],
-            };
-        }
-    }).catch(() => {
-        // 错误时也清空数据
-        areaTrendChartData.value = {
-            xAxisData: [],
-            series: [],
-        };
-    });
-};
-
-const fetchStatSpeciesAreaYield = () => {
-    const params = {
-        year: 2025,
-        adminCode: props.areaCode,
-        adminLevel: null,
-    };
-    VE_API.warning.fetchStatSpeciesAreaYield(params).then((res) => {
-        if (res.code === 0 && res.data && res.data.length > 0) {
-            // 转换接口数据为图表格式(用于饼图)
-            const chartData = res.data.map((item) => ({
-                value: item.plantArea, // 种植面积
-                name: item.speciesName, // 作物名称
-                expectYield: item.expectYield, // 预估产量
-                speciesId: item.speciesId, // 作物ID
-            }));
-
-            // 计算总种植面积
-            const total = chartData.reduce((sum, item) => sum + item.value, 0);
-
-            // 更新饼图数据
-            pieChartData.value = chartData;
-            totalArea.value = total;
-
-            // 处理预估产量数据(用于柱状图)
-            const categories = res.data.map((item) => item.speciesName);
-            const values = res.data.map((item) => parseFloat(item.expectYield.toFixed(2))); // 保留两位小数
-
-            // 更新预估产量图表数据
-            yieldChartData.value = {
-                categories,
-                values,
-            };
-
-            // 找到最大预估产量的作物
-            let maxYield = 0;
-            let maxSpecies = "";
-            res.data.forEach((item) => {
-                if (item.expectYield > maxYield) {
-                    maxYield = item.expectYield;
-                    maxSpecies = item.speciesName;
-                }
-            });
-
-            // 更新摘要文字
-            if (maxSpecies) {
-                const maxYieldFormatted = maxYield.toFixed(1);
-                yieldSummaryText.value = `${maxSpecies}的预估产量最高,为${maxYieldFormatted}吨`;
-            } else {
-                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>
 
 <style lang="scss" scoped>

+ 53 - 21
src/views/warningHome/components/mapLegend.vue

@@ -1,13 +1,18 @@
 <template>
-    <div class="map-legend" v-show="type !== '作物分布'">
-        <div
-            v-for="(item, index) in legendObj[type]"
-            :key="`${type}-${item.label}-${index}`"
-            class="legend-item"
-        >
-            <span class="legend-dot" v-if="item.color" :style="{ backgroundColor: item.color }"></span>
-            <img class="legend-icon" v-else :src="item.icon" alt="">
-            <span class="legend-label">{{ item.label }}</span>
+    <div class="map-legend-container">
+        <div class="map-legend">
+            <div v-for="(item, index) in legendArr" :key="`${type}-${item.name}-${index}`" class="legend-item">
+                <span class="legend-dot" v-if="item.color" :style="{ backgroundColor: item.color }"></span>
+                <img class="legend-icon" v-else :src="item.icon" alt="" />
+                <span class="legend-label">{{ item.name }}</span>
+            </div>
+        </div>
+        <div class="map-legend">
+            <div v-for="(item, index) in legendArr2" :key="`${type}-${item.name}-${index}`" class="legend-item">
+                <span class="legend-dot" v-if="item.color" :style="{ backgroundColor: item.color }"></span>
+                <img class="legend-icon" v-else :src="item.icon" alt="" />
+                <span class="legend-label">{{ item.name }}</span>
+            </div>
         </div>
     </div>
 </template>
@@ -16,6 +21,7 @@
 import legendIcon1 from "@/assets/images/common/legend-icon-1.png";
 import legendIcon2 from "@/assets/images/common/legend-icon-2.png";
 import legendIcon3 from "@/assets/images/common/legend-icon-3.png";
+import { ref, onMounted } from "vue";
 
 const props = defineProps({
     type: {
@@ -25,31 +31,56 @@ const props = defineProps({
 });
 
 const legendObj = {
-    "物候期分布": [
+    物候期分布: [
         { label: "抽穗期", color: "#E17C00" },
         { label: "拔节期", color: "#985300" },
         { label: "孕穗期", color: "#512D00" },
     ],
-    "预警分布": [
+    预警分布: [
         { label: "干旱等级Ⅰ级", color: "#BD231E" },
         { label: "干旱等级Ⅰ级", color: "#FF6600" },
         { label: "干旱等级Ⅰ级", color: "#FFCD00" },
     ],
-    "农场分布": [
+    农场分布: [
         { label: "冷链冷库", icon: legendIcon1 },
         { label: "加工厂", icon: legendIcon2 },
     ],
-    "农服管理": [
-        { label: "冷链仓库", icon: legendIcon3 },
-    ],
-}
+    农服管理: [{ label: "冷链仓库", icon: legendIcon3 }],
+};
+
+const legendArr = ref([]);
+const legendArr2 = ref([]);
+onMounted(() => {
+    fetchMapLegend();
+    fetchLandTypes();
+});
+
+const fetchMapLegend = () => {
+    VE_API.warning.fetchMapLegend().then((res) => {
+        if (res.code === 0 && res.data && res.data.length > 0) {
+            legendArr.value = res.data;
+        }
+    });
+};
+
+const fetchLandTypes = () => {
+    VE_API.warning.fetchLandTypes().then((res) => {
+        if (res.code === 0 && res.data && res.data.length > 0) {
+            legendArr2.value = res.data;
+        }
+    });
+};
 </script>
 
 <style lang="scss" scoped>
-.map-legend {
+.map-legend-container {
+    display: flex;
+    gap: 10px;
     position: fixed;
     bottom: 40px;
     right: 430px;
+}
+.map-legend {
     padding: 6px 20px;
     display: flex;
     align-items: center;
@@ -58,17 +89,18 @@ const legendObj = {
     border-radius: 20px;
     z-index: 9;
     color: #ffffff;
+    font-size: 16px;
     .legend-item {
         display: flex;
         align-items: center;
         gap: 8px;
         .legend-dot {
-            width: 8px;
-            height: 8px;
-            border-radius: 50%;
+            width: 15px;
+            height: 15px;
+            // border-radius: 50%;
             background: #f2a038; // 默认颜色,实际由内联样式覆盖
         }
-        .legend-icon{
+        .legend-icon {
             width: 20px;
             height: 20px;
         }

+ 65 - 101
src/views/warningHome/index.vue

@@ -3,8 +3,8 @@
         <fnHeader showDate :autoGo="true" hideSwitch></fnHeader>
         <div class="content">
             <div class="warning-l left">
-                <div class="warning-top">
-                    <div class="top-l yes-events">
+                <div class="warning-top yes-events">
+                    <!-- <div class="top-l yes-events">
                         <div>
                             <el-cascader
                                 style="width: 184px"
@@ -15,7 +15,9 @@
                                 popper-class="area-cascader"
                             />
                         </div>
-                    </div>
+                    </div> -->
+                    <div class="address-box">四川省-简阳市-平泉街道</div>
+                    <div class="btn-box" @click="handleClick">智慧果园</div>
                 </div>
             </div>
             <div class="warning-r right chart-wrap yes-events">
@@ -26,7 +28,7 @@
                 ></chart-list>
             </div>
             <!-- 地图图例 -->
-            <!-- <map-legend :type="activeBaseTab"></map-legend> -->
+            <map-legend></map-legend>
             <!-- 地图搜索 -->
             <div class="warning-search yes-events">
                 <el-select
@@ -68,17 +70,18 @@ import StaticMapLayers from "@/components/static_map_change/Layers.js";
 import StaticMapPointLayers from "@/components/static_map_change/pointLayer.js";
 import { onMounted, onUnmounted, ref, reactive, nextTick } from "vue";
 import fnHeader from "@/components/fnHeader.vue";
+import mapLegend from "./components/mapLegend.vue";
 import WarningMap from "./warningMap";
 import AlarmLayer from "./map/alarmLayer";
 import DistributionLayer from "./map/distributionLayer";
 import trackDialog from "./components/trackDialog.vue";
-import timeLine from "./components/timeLine.vue";
 import eventBus from "@/api/eventBus";
 import { useStore } from "vuex";
+import { useRouter } from "vue-router";
 import chartList from "./components/chart_components/chartList.vue";
 
 let store = useStore();
-
+const router = useRouter();
 let warningMap = new WarningMap();
 let alarmLayer = null;
 let staticMapLayers = null;
@@ -123,8 +126,8 @@ onMounted(async () => {
     staticMapPointLayers = new StaticMapPointLayers(warningMap.kmap);
     distributionLayer = new DistributionLayer(warningMap.kmap);
     await getSpeciesListData();
-    // 作物分布默认选中
-    await handleDistributionLayer();
+    // 页面一进来,直接请求作物分布接口并渲染地块(不展示点位)
+    await initDefaultDistribution();
 
     // 数据加载完成后,再次更新地图尺寸以确保正确渲染
     if (warningMap.kmap && warningMap.kmap.map) {
@@ -167,11 +170,6 @@ onMounted(async () => {
             }
         }
     });
-
-    // ai与地图交互
-    eventBus.off("chat:showMapLayer", handleMapLayer);
-    eventBus.on("chat:showMapLayer", handleMapLayer);
-
     // 初始化区域选择器的默认值
     initAreaDefaultValue();
 
@@ -189,6 +187,21 @@ onMounted(async () => {
     });
 });
 
+const handleClick = () => {
+    if (window.parent) {
+        window.parent.postMessage(
+            {
+                type: "land-click",
+                payload: {
+                    source: "warningHome",
+                    action: "openSmartGarden",
+                },
+            },
+            "*" // 如果有固定父应用域名,可以替换为 'http://localhost:8080' 等
+        );
+    }
+};
+
 // 时间轴
 eventBus.on("weatherTime:changeTime", ({ index, year, quarter }) => {
     handleTimeChange(index, year, quarter);
@@ -238,85 +251,6 @@ onUnmounted(() => {
     eventBus.off("weatherTime:changeTime");
 });
 
-// 作物分布默认选中并展开第一个节点,在地图上显示对应分布图层
-const handleDistributionLayer = async () => {
-    // 默认选中并展开第一个"果类"节点,在地图上显示对应分布图层
-    if (!treeActionData.value || treeActionData.value.length === 0) {
-        return;
-    }
-    const firstCategory = treeActionData.value[0];
-    if (firstCategory) {
-        getCommonMapData(firstCategory);
-    }
-};
-
-// 处理公共获取最后一级的节点数据
-const getCommonMapData = async (firstCategory) => {
-    if (!firstCategory || !distributionLayer) {
-        return;
-    }
-    // 递归查找最后一层的节点(没有子节点的叶子节点)
-    const getLastLevelNodes = (node) => {
-        const lastLevelNodes = [];
-        if ((!node.items || node.items.length === 0) && (!node.children || node.children.length === 0)) {
-            lastLevelNodes.push(node);
-        } else {
-            const children = node.items || node.children || [];
-            children.forEach((child) => {
-                lastLevelNodes.push(...getLastLevelNodes(child));
-            });
-        }
-        return lastLevelNodes;
-    };
-
-    const lastLevelNodes = getLastLevelNodes(firstCategory);
-    if (lastLevelNodes.length === 0) {
-        return;
-    }
-
-    if (activeBaseTab.value === "物候期分布") {
-        // 等待接口返回数据
-        const lastLevelIds = lastLevelNodes.map((n) => n.originalId);
-        const phenologyData = await getDistributionData(null, lastLevelIds);
-        if (distributionLayer) {
-            distributionLayer.initData(phenologyData, "phenologyName");
-        }
-        return;
-    }
-    if (activeBaseTab.value === "农场分布") {
-        const lastLevelIds = lastLevelNodes.map((n) => n.originalId);
-        await fetchFarmList(lastLevelIds);
-        return;
-    }
-    const lastLevelIds = lastLevelNodes.map((n) => n.id);
-    // 并发请求所有数据
-    const promises = lastLevelIds.map((id) => getDistributionData(id));
-    const results = await Promise.all(promises);
-    const finalMapData = results.flat();
-    if (distributionLayer) {
-        distributionLayer.initData(finalMapData);
-    }
-};
-
-// ai与地图交互
-const handleMapLayer = ({ mapName, isHome }) => {
-    staticMapPointLayers.hidePoint();
-    staticMapLayers.hideAll();
-
-    // 重置时间轴
-    // eventBus.emit("map_click_alarm")
-    if (mapName === "植保机") {
-        staticMapLayers.show("分散种植", true);
-        staticMapPointLayers.showPoint();
-    } else if (mapName) {
-        // staticMapLayers.show("作物种类")
-        if (isHome) {
-            staticMapLayers.show(mapName, true);
-        } else {
-            staticMapLayers.showSingle(mapName, true);
-        }
-    }
-};
 
 const getSpeciesListData = async () => {
     const res = await VE_API.species.speciesList();
@@ -325,15 +259,25 @@ const getSpeciesListData = async () => {
     originalTreeData.value = JSON.parse(JSON.stringify(res.data));
 };
 
-const getDistributionData = async (speciesId, phenologyIds) => {
-    const { data } = await VE_API.agri_land_crop.queryDistribution({
-        // year: currentYear.value,
-        year: 2025,
-        quarter: currentQuarter.value,
-        speciesId,
-        phenologyIds: phenologyIds || [],
-    });
-    return data;
+// 页面初始化时,直接请求一次作物分布接口并渲染地块
+const initDefaultDistribution = async () => {
+    if (!distributionLayer) return;
+    // 不传物种 / 物候参数,由后端按默认规则返回全部或指定范围内的地块分布
+    const data = await getDistributionData();
+    // 只渲染地块面,不展示点位(在 getDistributionData 中已去掉 centerPoint)
+    distributionLayer.initData(data);
+};
+
+const getDistributionData = async () => {
+    const { data } = await VE_API.agri_land_crop.queryDistribution();
+    // 把点位图层去掉:不返回 centerPoint,只保留地块相关信息
+    const list = Array.isArray(data)
+        ? data.map((item) => {
+              const { centerPoint, ...rest } = item || {};
+              return rest;
+          })
+        : [];
+    return list;
 };
 const props1 = {
     checkStrictly: true,
@@ -717,6 +661,26 @@ const getTreeChecks = async (nodeData, data) => {
             display: flex;
             width: max-content;
             align-items: center;
+            .address-box{
+                color: #f7be5a;
+                font-size: 20px;
+                font-family: "PangMenZhengDao";
+                margin-right: 15px;
+                border: 2px solid rgba(255, 212, 137, 0.3);
+                border-radius: 4px;
+                padding: 8px 14px 11px;
+                background: rgba(29, 29, 29, 0.54);
+            }
+            .btn-box {
+                cursor: pointer;
+                color: #f7be5a;
+                font-size: 20px;
+                font-family: "PangMenZhengDao";
+                border: 2px solid #f7be5a;
+                border-radius: 4px;
+                padding: 8px 14px 11px;
+                background: rgba(29, 29, 29, 0.54);
+            }
             .top-l {
                 display: flex;
                 flex-direction: column;

+ 9 - 37
src/views/warningHome/map/distributionLayer.js

@@ -23,10 +23,11 @@ class DistributionLayer {
         });
         this.kmap.addLayer(this.distributionLayer.layer)
 
-        // 创建聚合数据源
+        // 创建聚合数据源(必须提供一个非空的 VectorSource,避免内部调用 loadFeatures 时 source 为空报错)
         this.clusterSource = new Cluster({
             distance: 80, // 聚合距离(像素)
             minDistance: 50, // 最小聚合距离
+            source: new VectorSource({}), // 给一个空的数据源即可,目前我们不再使用点位图层
         });
 
         // 提取单个点样式函数
@@ -262,50 +263,21 @@ class DistributionLayer {
         this.clear();
         if(!data || data.length === 0) return;
 
-        // 创建临时 VectorSource 用于存储点数据
-        const pointSource = new VectorSource({});
-
         for (let item of data) {
             // 面数据(区域多边形)
             if (item.geom) {
                 item.color = item.speciesColor || "#2199F8";
                 this.distributionLayer.source.addFeature(newPolymerFeature(item));
             }
-            if (item.centerPoint) {
-                item.color = item.speciesColor || "#2199F8";
-                item.wkt = item.centerPoint;
-                item.label = item[field] || "";
-                pointSource.addFeature(newPoint(item));
-            }
-        }
-
-        // 将点数据源设置到聚合源
-        if (pointSource.getFeatures().length > 0) {
-            this.clusterSource.setSource(pointSource);
         }
-        // const extent = this.distributionLayer.source.getExtent();
-        // if (extent && !isNaN(extent[0])) {
-        //     this.kmap.map.getView().fit(extent, {
-        //         padding: [280, 400, 200, 150],
-        //         duration: 500,
-        //     });
-        // }
 
-        // 所有点位添加完成后,地图范围自适应到包含所有点
-        if (pointSource.getFeatures().length > 0) {
-            setTimeout(() => {
-                const source = this.clusterSource.getSource();
-                if (source) {
-                    const pointExtent = source.getExtent();
-                    if (pointExtent && !isNaN(pointExtent[0])) {
-                        this.kmap.map.getView().fit(pointExtent, {
-                            padding: [280, 400, 200, 150],
-                            duration: 500,
-                            maxZoom: 12,
-                        });
-                    }
-                }
-            }, 100);
+        // 地块面渲染完成后,按照面要素自适应地图范围(不再根据点位来缩放)
+        const extent = this.distributionLayer.source.getExtent();
+        if (extent && !isNaN(extent[0])) {
+            this.kmap.map.getView().fit(extent, {
+                padding: [50, 50, 50, 50],
+                duration: 500,
+            });
         }
     }