浏览代码

feat:添加作物分布右侧组件和地图图例

wangsisi 1 周之前
父节点
当前提交
bbd49e7da7

二进制
src/assets/images/warningHome/lz-icon.png


+ 295 - 0
src/views/warningHome/components/crop.vue

@@ -0,0 +1,295 @@
+<template>
+    <div class="fly-photo">
+        <div class="select-group">
+            <el-date-picker
+                class="date-picker"
+                v-model="dateTime"
+                @change="changeDate"
+                size="large"
+                type="date"
+                popper-class="pic-time-picker"
+                format="YYYY年MM月DD日"
+                value-format="YYYY-MM-DD"
+                style="width: calc(100% - 145px)"
+            >
+                <template #default="cell">
+                    <div class="cell" :class="{ current: cell.isCurrent }">
+                        <span class="text">{{ cell.text }}</span>
+                        <span v-if="isHoliday(cell)" class="holiday" />
+                    </div>
+                </template>
+            </el-date-picker>
+            <el-select @change="changeArea" class="select" v-model="areaValue" size="large">
+                <el-option v-for="item in areaOptions" :key="item.value" :label="item.name" :value="item.id" />
+            </el-select>
+        </div>
+        <div class="photo-list" v-loading="loadingImgs" element-loading-background="rgba(0, 0, 0, 0.3)">
+            <div
+                class="photo-item"
+                v-for="(item, index) in imageList"
+                :class="{ 'm-l-10': index % 2 !== 0 }"
+                :key="index"
+            >
+                <album-carousel-item
+                    lbum-carousel-item
+                    :key="index"
+                    :name="item.code"
+                    :farmId="curFarmId"
+                    :images="[item]"
+                    :lock="false"
+                ></album-carousel-item>
+            </div>
+            <div class="no-photo" v-show="loadingImgs === false && !imageList.length">暂无数据</div>
+        </div>
+        <div class="pagination-wrap">
+            <el-pagination
+                background
+                :page-size="pageSize"
+                :pagerCount="5"
+                v-model:current-page="currentPage"
+                @current-change="geImgaeList"
+                layout="prev, pager, next"
+                :total="totalVal"
+            />
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import albumCarouselItem from "@/views/home/album_compoents/albumCarouselItem.vue";
+import config from "@/api/config";
+const resize = "?imageView/1/w/240/h/240";
+
+const curFarmId = ref(101532);
+
+function getCurrentMonth() {
+    const now = new Date();
+    const formattedDate = now.toISOString().slice(0, 10);
+    return formattedDate;
+}
+
+const dateTime = ref(getCurrentMonth());
+const changeDate = () => {
+    currentPage.value = 1;
+    geImgaeList();
+};
+
+const isHoliday = ({ dayjs }) => {
+    return allImgsDates.value.includes(dayjs.format("YYYY-MM-DD"));
+};
+
+const allImgsDates = ref([]);
+function getDates() {
+    VE_API.image.fetchGardenImgsDate({ farmId: curFarmId.value, regionId: areaValue.value }).then(({ data }) => {
+        allImgsDates.value = data;
+        dateTime.value = data[0];
+        geImgaeList();
+    });
+}
+
+const areaValue = ref(null); // 默认区域ID
+
+const areaOptions = ref([]);
+const currentPage = ref(1);
+const pageSize = ref(10); // 每页显示20条数据
+const totalVal = ref(0);
+
+const loadingImgs = ref(false);
+const imageList = ref([]);
+const geImgaeList = () => {
+    loadingImgs.value = true;
+    VE_API.image
+        .fetchGardenImgs(
+            {
+                date: dateTime.value,
+                organId: curFarmId.value,
+                areaId: areaValue.value,
+                limit: pageSize.value,
+                page: currentPage.value,
+            },
+            { Global: false }
+        )
+        .then((res) => {
+            imageList.value = res.data;
+            totalVal.value = res.count;
+            loadingImgs.value = false;
+        });
+};
+
+function changeArea() {
+    currentPage.value = 1; // 重置页码
+    getDates();
+}
+
+
+onMounted(() => {
+
+    getBlueRegionList();
+});
+
+function getBlueRegionList() {
+    VE_API.image.fetchRegionList({ farmId: curFarmId.value }).then(({ data }) => {
+        areaOptions.value = data;
+        areaValue.value = data[0].id;
+        getDates();
+    });
+}
+
+// 提取前6位并转换为日期格式
+function formatDatePart(originalStr) {
+    // 提取前6位数字
+    const datePart = originalStr.substring(0, 6);
+
+    // 解析为日期格式
+    const year = "20" + datePart.substring(0, 2); // 前两位是年份,加上20前缀
+    const month = datePart.substring(2, 4); // 中间两位是月份
+    const day = datePart.substring(4, 6); // 最后两位是日
+
+    // 组合成YYYY-MM-DD格式
+    const formattedDate = `${year}-${month}-${day}`;
+    // 返回新字符串(不改变原字符串)
+    return formattedDate;
+}
+</script>
+
+<style lang="scss" scoped>
+.fly-photo {
+    width: 100%;
+    height: 100%;
+    .select-group {
+        display: flex;
+        .select {
+            width: 140px;
+            margin-left: 5px;
+        }
+        ::v-deep {
+            .el-select__wrapper {
+                background: rgba(255, 255, 255, 0.07);
+                box-shadow: none;
+            }
+            .el-select__placeholder,
+            .el-select__caret,
+            .el-input__prefix {
+                color: #fff;
+                text-align: center;
+            }
+            .el-input__wrapper {
+                background: #323232;
+                box-shadow: none;
+            }
+            .el-input__inner {
+                color: #fff;
+            }
+        }
+    }
+    .photo-list {
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+        overflow: auto;
+        width: 100%;
+        height: calc(100% - 82px - 10px);
+        margin-top: 10px;
+        .photo-item {
+            width: calc(50% - 5px);
+            margin-bottom: 10px;
+            .img {
+                width: 100%;
+                height: 100%;
+                object-fit: cover;
+                border-radius: 4px;
+            }
+            ::v-deep {
+                .carousel-item .tag-box.leftTop {
+                    display: none;
+                }
+                .carousel-item img {
+                    border-radius: 4px;
+                }
+            }
+        }
+        .m-l-10 {
+            margin-left: 10px;
+        }
+        .no-photo {
+            color: #fff;
+            width: 100%;
+            text-align: center;
+            padding-top: 20px;
+        }
+    }
+    .pagination-wrap {
+        padding-top: 10px;
+        ::v-deep {
+            .el-pagination {
+                justify-content: center;
+            }
+            .el-pagination.is-background .el-pager li {
+                background: #424242;
+                color: rgba(255, 255, 255, 0.7);
+            }
+            .el-pagination.is-background .el-pager li.is-active {
+                background: #ffd489;
+                color: #000;
+            }
+            .el-pagination.is-background .btn-prev:disabled {
+                background: #424242;
+                color: rgba(255, 255, 255, 0.4);
+            }
+            .el-pagination button {
+                background: #424242;
+                color: rgba(255, 255, 255, 0.7);
+                &:hover {
+                    color: rgba(255, 255, 255, 1);
+                }
+            }
+        }
+    }
+}
+</style>
+
+<style lang="less">
+.pic-time-picker {
+    .cell {
+        height: 30px;
+        padding: 3px 0;
+        box-sizing: border-box;
+    }
+    .cell .text {
+        width: 24px;
+        height: 24px;
+        display: block;
+        margin: 0 auto;
+        line-height: 24px;
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+        border-radius: 50%;
+    }
+
+    .cell.current .text {
+        background: #409eff;
+        color: #fff;
+    }
+    .available.today {
+        .cell .text {
+            color: #409eff;
+        }
+        .cell.current .text {
+            color: #fff;
+        }
+    }
+
+    .cell .holiday {
+        position: absolute;
+        width: 6px;
+        height: 6px;
+        background: #2199f8;
+        border-radius: 50%;
+        bottom: 0px;
+        left: 50%;
+        transform: translateX(-50%);
+    }
+}
+</style>

+ 219 - 0
src/views/warningHome/components/equipment.vue

@@ -0,0 +1,219 @@
+<template>
+    <div class="chart-list">
+        <!-- <div class="chart-item weather-item">
+            <chart-box name="干热风险">
+                <div class="base-wrap">
+                    <div class="base-item" v-for="(item, index) in baseData.labels" :key="index">
+                        <div class="label">{{ item }}风险</div>
+                        <div class="value">{{ baseData.valueMaxList[index].toFixed(0) }}<span>%</span></div>
+                    </div>
+                </div>
+                <weatherChart class="line-chart"></weatherChart>
+            </chart-box>
+        </div> -->
+        <div class="chart-item">
+            <chart-box name="温度">
+                <one-line-chart
+                    class="line-chart"
+                    key="tr"
+                    name="温度"
+                    :yData="[22, 20, 22, 30, 32, 22, 25]"
+                    :minData="[]"
+                    :chartDate="chartDate1"
+                ></one-line-chart>
+                <div class="box-bg tips">
+                    <div class="text">
+                        这里是预警提示的内容,有点长,先用这些文字占位,后续开发会把内容不上的。这里是预警提示的内容,有点长,先用这些文字占位,后续开发会把内容不上的。
+                    </div>
+                </div>
+            </chart-box>
+        </div>
+        <div class="chart-item">
+            <chart-box name="湿度">
+                <one-line-chart
+                    class="line-chart"
+                    key="tr"
+                    name="湿度"
+                    :yData="[22, 20, 22, 30, 32, 22, 25]"
+                    :minData="[]"
+                    :chartDate="chartDate1"
+                ></one-line-chart>
+                <div class="box-bg tips">
+                    <div class="text">
+                        这里是预警提示的内容,有点长,先用这些文字占位,后续开发会把内容不上的。这里是预警提示的内容,有点长,先用这些文字占位,后续开发会把内容不上的。
+                    </div>
+                </div>
+            </chart-box>
+        </div>
+        <div class="chart-item">
+            <chart-box name="光照">
+                <one-line-chart
+                    class="line-chart"
+                    name="光照"
+                    :key="1"
+                    :yData="[22, 20, 22, 30, 32, 22, 25]"
+                    :minData="[]"
+                    :chartDate="chartDate"
+                ></one-line-chart>
+                <div class="box-bg tips">
+                    <div class="text">
+                        这里是预警提示的内容,有点长,先用这些文字占位,后续开发会把内容不上的。这里是预警提示的内容,有点长,先用这些文字占位,后续开发会把内容不上的。
+                    </div>
+                </div>
+            </chart-box>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import chartBox from "@/components/chartBox.vue";
+import oneLineChart from "@/components/charts/oneLineChart.vue";
+import { onMounted, ref, onUnmounted } from "vue";
+import eventBus from "@/api/eventBus";
+
+const riskKeys = ref([]);
+
+const yData = ref([]);
+const yData1 = ref([]);
+
+const chartDate = ref([]);
+const chartDate1 = ref([]);
+
+const text = ref(null);
+const text1 = ref(null);
+
+const baseData = ref({});
+
+const organId = ref(sessionStorage.getItem("farmId"));
+
+onMounted(() => {
+    // getList();
+    //   getBaseData()
+    //   eventBus.on("area:id", changeAreaId);
+});
+onUnmounted(() => {
+    eventBus.off("area:id", changeAreaId);
+});
+
+function changeAreaId({ farmId }) {
+    organId.value = farmId;
+    getList();
+    getBaseData();
+}
+
+function getList() {
+    riskKeys.value = [];
+    yData.value.value = [];
+    yData1.value.value = [];
+    chartDate.value.value = [];
+    chartDate1.value.value = [];
+    // VE_API.farm.getFarmReport({ farmId: organId.value, type: "气象风险" }).then((res) => {
+    //     // if (res.data["物候进程"]) {}
+    //     // 使用 Object.keys() 提取对象中的键
+    //     riskKeys.value = Object.keys(res.data);
+    //     yData.value = res.data[riskKeys.value[0]].list.map((item) => parseFloat(item.value.toFixed(1)))
+    //     yData1.value = res.data[riskKeys.value[1]].list.map((item) => parseFloat(item.value.toFixed(1)))
+
+    //     chartDate.value = formattedDates(res.data[riskKeys.value[0]].list.map((item) => item.name))
+    //     chartDate1.value = formattedDates(res.data[riskKeys.value[1]].list.map((item) => item.name))
+
+    //     text.value = res.data[riskKeys.value[0]].text
+    //     text1.value = res.data[riskKeys.value[1]].text
+    // })
+}
+
+function formattedDates(dates) {
+    return dates.map((date) => {
+        const [year, month, day] = date.split("-");
+        return `${month}-${day}`; // 使用模板字符串格式化为 MM/DD
+    });
+}
+
+const getBaseData = () => {
+    const point = sessionStorage.getItem("point");
+    // 获取气象图表数据
+    VE_API.mini_farm.weather_warning_land_check({ farmId: organId.value, point }).then((res) => {
+        baseData.value = res.data || {};
+    });
+};
+</script>
+
+<style lang="scss" scoped>
+.chart-list {
+    width: 100%;
+    height: 100%;
+    .chart-item {
+        width: 100%;
+        margin-bottom: 5px;
+        height: 280px;
+        .import {
+            font-size: 12px;
+            background: rgba(255, 255, 255, 0.2);
+            border-radius: 4px;
+            padding: 5px 13px;
+            cursor: pointer;
+        }
+        .line-chart {
+            width: 100%;
+            height: calc(100% - 70px);
+        }
+
+        &.weather-item {
+            height: 266px;
+        }
+
+        .base-wrap {
+            width: 100%;
+            height: 56px;
+            margin-top: 4px;
+            display: flex;
+            justify-content: space-evenly;
+            .base-item {
+                width: 110px;
+                height: 100%;
+                font-size: 12px;
+                text-align: center;
+                box-sizing: border-box;
+                color: #f3c11d;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                margin: 0 12px;
+                background: url("@/assets/images/home/scale-bg.png") no-repeat center center / 100% 100%;
+                .label {
+                    width: 85px;
+                    height: 16px;
+                    line-height: 16px;
+                    color: #fff;
+                    background: url("@/assets/images/home/label-bg.png") no-repeat center center / 100% 100%;
+                }
+                .value {
+                    font-size: 18px;
+                    font-family: "PangMenZhengDao";
+                    span {
+                        font-size: 12px;
+                    }
+                }
+            }
+        }
+
+        .box-bg {
+            margin-top: 8px;
+            border-radius: 2px 2px 0 0;
+            font-size: 12px;
+            padding: 3px 6px;
+            box-sizing: border-box;
+            font-family: Arial, Helvetica, sans-serif;
+            overflow-y: auto;
+            background: linear-gradient(180deg, rgb(85, 85, 85, 0.4) 0%, rgb(35, 35, 35, 1) 100%);
+            .text {
+                position: relative;
+                span {
+                    color: rgba(255, 255, 255, 0.4);
+                    line-height: 1.7;
+                }
+            }
+        }
+    }
+}
+</style>

+ 315 - 0
src/views/warningHome/components/farmInfoGroup.vue

@@ -0,0 +1,315 @@
+<template>
+    <div class="farm-info-group">
+        <!-- 顶部标题栏 -->
+        <div class="info-header">
+            <div class="back-btn" @click="handleBack">返回</div>
+            <div class="title">
+                <img class="title-icon" src="@/assets/images/common/chart-icon.png" alt="" />
+                <span class="title-text">荔博园</span>
+            </div>
+        </div>
+
+        <!-- 主要内容区域 -->
+        <div class="info-content">
+            <div class="farm-info">
+                <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>
+                        <div class="tags">
+                            <span class="tag">荔枝</span>
+                            <span class="tag">井岗红糯</span>
+                        </div>
+                    </div>
+                    <div class="info-item">
+                        <span class="info-label">农场面积:</span>
+                        <span class="info-value">58亩</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="info-label">农场位置:</span>
+                        <span class="info-value">广东省广州市***</span>
+                    </div>
+                    <div class="info-item">
+                        <span class="info-label">权属人:</span>
+                        <span class="info-value">张扬</span>
+                    </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'">
+                    <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 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>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import equipment from "./equipment.vue";
+import crop from "./crop.vue";
+// 底部 tabs 配置
+const tabs = [
+    { key: "crop", label: "作物档案" },
+    { key: "perception", label: "感知记录" },
+];
+
+// 感知类型 tabs
+const perceptionTabs = [
+    { key: "device", label: "设备感知" },
+    { key: "crop", label: "作物感知" },
+];
+
+const activeTab = ref("crop"); // 默认选中感知记录
+const perceptionActive = ref("device"); // 默认选中设备感知
+
+// 时间轴示例数据(后续可替换为接口数据)
+const timelineList = ref([
+    { id: 1, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
+    { id: 2, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
+    { id: 3, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
+    { id: 4, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
+    { id: 5, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
+]);
+
+const handleBack = () => {
+    // 返回逻辑
+};
+
+const handleCropArchive = () => {
+    // 作物档案逻辑
+};
+
+const handlePerceptionRecord = () => {
+    // 感知记录逻辑
+};
+</script>
+<style lang="scss" scoped>
+.farm-info-group {
+    position: absolute;
+    top: 30px;
+    right: 0;
+    width: 376px;
+    height: calc(100% - 20px);
+    background: #232323;
+    border-radius: 4px;
+    box-sizing: border-box;
+    border: 1px solid rgba(255, 255, 255, 0.16);
+    .info-header {
+        display: flex;
+        align-items: center;
+        padding: 6px 14px;
+        border-bottom: 1px solid rgba(255, 255, 255, 0.16);
+
+        .back-btn {
+            cursor: pointer;
+            color: rgba(255, 255, 255, 0.55);
+        }
+
+        .title {
+            flex: 1;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            gap: 8px;
+            font-size: 18px;
+            .title-icon {
+                width: 19px;
+                height: 13px;
+            }
+        }
+    }
+    .info-content {
+        padding: 5px 10px;
+        height: calc(100% - 168px);
+        .farm-info {
+            display: flex;
+            align-items: flex-start;
+            gap: 12px;
+            .lz-icon {
+                border-radius: 6px;
+                background: rgba(70, 70, 70, 0.26);
+                img {
+                    width: 65px;
+                    height: 65px;
+                    padding: 7px;
+                }
+            }
+            .info-right {
+                font-size: 12px;
+                color: #9f9f9f;
+                .info-item {
+                    display: flex;
+                    align-items: center;
+                    gap: 8px;
+                    line-height: 18px;
+                    &.farm-code {
+                        font-weight: 700;
+                        font-size: 16px;
+                        margin-bottom: 4px;
+                        color: #ffffff;
+                    }
+                    .tags {
+                        display: flex;
+                        gap: 4px;
+                        margin-left: auto;
+
+                        .tag {
+                            padding: 1px 6px;
+                            border: 1px solid #FFD489;
+                            border-radius: 2px;
+                            color: #FFD489;
+                            font-size: 12px;
+                            font-weight: 400;
+                        }
+                    }
+                }
+            }
+        }
+        .tabs {
+            display: flex;
+            gap: 10px;
+            margin-top: 12px;
+            
+            .tab-item {
+                padding: 6px 32px;
+                text-align: center;
+                border-radius: 4px;
+                cursor: pointer;
+                color: #ffffff;
+                background: rgba(166, 166, 166, 0.08);
+                border: 1px solid transparent;
+                
+                &.active {
+                    color: #FFD489;
+                    background: rgba(255, 212, 137, 0.2);
+                    border: 1px solid #FFD489;
+                }
+            }
+        }
+        .tab-content{
+            margin: 12px 0;
+            height: calc(100% - 24px);
+            overflow-y: auto;
+            .timeline{
+                padding-right: 12px;
+                .timeline-item{
+                    display: flex;
+                    align-items: flex-start;
+                    font-size: 14px;
+                    color: #ffffff;
+                    line-height: 22px;
+                    & + .timeline-item{
+                        margin-top: 24px;
+                    }
+                    .timeline-left{
+                        width: 24px;
+                        display: flex;
+                        flex-direction: column;
+                        align-items: center;
+                        .dot{
+                            width: 6px;
+                            height: 6px;
+                            border-radius: 50%;
+                            background: #FFD489;
+                            margin-top: 6px;
+                        }
+                        .line{
+                            border-left: 1px dashed rgba(255, 212, 137, 0.6);
+                            margin-top: 4px;
+                            height: 40px;
+                        }
+                    }
+                    .timeline-right{
+                        padding-left: 8px;
+                        .date{
+                            color: #FFD489;
+                            font-weight: 500;
+                            margin-bottom: 2px;
+                        }
+                        .text{
+                            color: #D7D7D7;
+                        }
+                    }
+                }
+            }
+            .perception-content{
+                height: 100%;
+                .perception-tabs{
+                    display: flex;
+                    align-items: flex-end;
+                    gap: 20px;
+                    margin-bottom: 12px;
+                    .perception-tab{
+                        color: #FFFFFF;
+                        cursor: pointer;
+                        .underline{
+                            margin-top: 6px;
+                            width: 56px;
+                            height: 2px;
+                            background: transparent;
+                            border-radius: 2px;
+                        }
+                        &.perception-tab--active{
+                            color: #FFD489;
+                            .underline{
+                                background: #FFD489;
+                            }
+                        }
+                    }
+                }
+                .perception-wrap{
+                    height: calc(100% - 40px);
+                    overflow-y: auto;
+                }
+            }
+        }
+    }
+}
+</style>

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

@@ -0,0 +1,53 @@
+<template>
+    <div class="map-legend">
+        <div
+            v-for="item in legendList"
+            :key="item.label"
+            class="legend-item"
+        >
+            <span class="legend-dot" :style="{ backgroundColor: item.color }"></span>
+            <span class="legend-label">{{ item.label }}</span>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+
+const legendImg = ref('');
+
+const legendList = ref([
+    { label: '抽穗期', color: '#E17C00' },
+    { label: '拔节期', color: '#985300' },
+    { label: '孕穗期', color: '#512D00' }
+]);
+</script>
+
+<style lang="scss" scoped>
+.map-legend {
+    position: fixed;
+    bottom: 18px;
+    right: 460px;
+    padding: 6px 20px;
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    background: rgba(0, 0, 0, 0.6);
+    border-radius: 20px;
+    z-index: 9;
+    color: #ffffff;
+    .legend-item {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        .legend-dot {
+            width: 8px;
+            height: 8px;
+            border-radius: 50%;
+            background: #f2a038; // 默认颜色,实际由内联样式覆盖
+        }
+    }
+}
+
+
+</style>

+ 99 - 108
src/views/warningHome/index.vue

@@ -1,13 +1,9 @@
 <template>
     <div class="base-container no-events">
-        <fnHeader showDate :autoGo="true"></fnHeader>
+        <fnHeader showDate :autoGo="true" hideSwitch></fnHeader>
         <div class="content">
             <div class="warning-l left">
                 <div class="warning-top">
-                    <div class="back-icon yes-events" v-if="!hideChatMapLayer" @click="toggleChatMapLayer">
-                        <img src="@/assets/images/common/back-icon.png" />
-                        返回
-                    </div>
                     <div class="top-l yes-events">
                         <div>
                             <el-cascader
@@ -20,11 +16,13 @@
                                 popper-class="area-cascader"
                             />
                         </div>
-                        <!-- <div class="type-box"><img src="@/assets/images/warningHome/lz.png" /></div> -->
                     </div>
                     <div class="top-r yes-events">
                         <div class="data-box" @click="toggleBox('面积')" :class="{ active: activeBoxName === '面积' }">
-                            <div class="data-value"><span>{{areaVal.includes('3186') ? 31.2 : 419.89}}</span>万亩</div>
+                            <div class="data-value">
+                                <span>{{ areaVal.includes("3186") ? 31.2 : 419.89 }}</span
+                                >万亩
+                            </div>
                             <div class="data-name">种植面积</div>
                         </div>
                         <div
@@ -37,44 +35,39 @@
                             <div class="data-name">疑似失管面积</div>
                         </div>
                         <div class="data-box" @click="toggleBox('产量')" :class="{ active: activeBoxName === '产量' }">
-                            <div class="data-value"><span>{{areaVal.includes('3186') ? 10.4 : 192.12}}</span>万吨</div>
+                            <div class="data-value">
+                                <span>{{ areaVal.includes("3186") ? 10.4 : 192.12 }}</span
+                                >万吨
+                            </div>
                             <div class="data-name">预估产量</div>
                         </div>
                     </div>
                 </div>
-                <div class="warning-alarm yes-events" v-show="hideChatMapLayer">
+                <div class="warning-alarm yes-events" v-show="activeBaseTab === '预警分布'">
                     <alarm-list></alarm-list>
                 </div>
                 <div class="time-wrap yes-events">
                     <time-line></time-line>
                 </div>
             </div>
+            <div class="base-tabs yes-events">
+                <div
+                    v-for="item in baseTabs"
+                    :key="item"
+                    class="tab-item"
+                    :class="{ active: item === activeBaseTab }"
+                    @click="activeBaseTab = item"
+                >
+                    {{ item }}
+                </div>
+            </div>
             <div class="warning-r right yes-events">
-                <!-- <album></album> -->
-                <!-- <chat></chat> -->
-
-                <!-- 地图图例 -->
-<!--                <div class="map-legend" v-if="legendImg">-->
-<!--                    <img :src="legendImg" />-->
-<!--                </div>-->
-<!--                <div class="map-legend chat-legend" v-show="!legendImg">-->
-<!--                    <StaticMapLegend></StaticMapLegend>-->
-<!--                </div>-->
+                <farmInfoGroup></farmInfoGroup>
             </div>
+            <!-- 地图图例 -->
+            <map-legend></map-legend>
             <div class="warning-search yes-events">
                 <img src="@/assets/images/warningHome/search-img.png" />
-                <!-- <div class="focus-farm">
-                    <el-select
-                        v-model="farmVal"
-                        placeholder="我的关注农场"
-                        style="width: 189px"
-                        popper-class="focus-farm-select"
-                        @change="toggleFarm"
-                    >
-                        <el-option label="荔博园" :value="1" />
-                        <el-option label="井冈红糯基地" :value="2" />
-                    </el-select>
-                </div> -->
             </div>
         </div>
     </div>
@@ -96,9 +89,9 @@
 
 <script setup>
 import "./map/mockFarmLayer";
-import StaticMapLayers from "@/components/static_map_change/Layers.js"
-import StaticMapLegend from "@/components/static_map_change/legend.vue"
-import StaticMapPointLayers from "@/components/static_map_change/pointLayer.js"
+import StaticMapLayers from "@/components/static_map_change/Layers.js";
+import StaticMapLegend from "@/components/static_map_change/legend.vue";
+import StaticMapPointLayers from "@/components/static_map_change/pointLayer.js";
 import { onMounted, onUnmounted, ref } from "vue";
 import fnHeader from "@/components/fnHeader.vue";
 import WarningMap from "./warningMap";
@@ -113,6 +106,8 @@ import { useRouter } from "vue-router";
 import eventBus from "@/api/eventBus";
 import { areaListOptions } from "./area";
 import { useStore } from "vuex";
+import farmInfoGroup from "./components/farmInfoGroup.vue";
+import mapLegend from "./components/mapLegend.vue";
 
 let store = useStore();
 
@@ -126,8 +121,12 @@ const router = useRouter();
 const areaVal = ref(["3"]);
 const mapRef = ref();
 
+// 顶部基础 tabs
+const baseTabs = ["作物分布", "物候期分布", "预警分布", "农场分布", "农服管理"];
+const activeBaseTab = ref("作物分布");
+
 const legendImg = ref("");
-const warningLayers = ref({})
+const warningLayers = ref({});
 onMounted(() => {
     warningMap.initMap(store.getters.userinfo.location, mapRef.value);
     alarmLayer = new AlarmLayer(warningMap.kmap);
@@ -138,73 +137,73 @@ onMounted(() => {
     //   staticMapLayers.show("testpng")
     // },2000)
 
-    eventBus.emit("warningMap:init", warningMap.kmap)
+    eventBus.emit("warningMap:init", warningMap.kmap);
 
     // 图例数据
     eventBus.on("alarmList:warningLayers", (data) => {
-        warningLayers.value = data
+        warningLayers.value = data;
     });
 
-    eventBus.on("alarmList:changeMapLayer", ({name, legendUrl}) => {
-      if(legendUrl){
-        legendImg.value = legendUrl;
-      }else{
-        legendImg.value = warningLayers.value[`${name}图例`];
-        let text = ''
-        if(name==='日间温度'){
-            text = '从化地块日温'
-        }else if(name==='夜间温度'){
-            text = '从化地块夜温'
-        }else if(name==='土壤水分'){
-            text = '从化地块水分'
-        }
-        if(text!==''){
-            staticMapLayers.showSingle(text,true)
-        }else{
-            staticMapLayers.hideAll()
+    eventBus.on("alarmList:changeMapLayer", ({ name, legendUrl }) => {
+        if (legendUrl) {
+            legendImg.value = legendUrl;
+        } else {
+            legendImg.value = warningLayers.value[`${name}图例`];
+            let text = "";
+            if (name === "日间温度") {
+                text = "从化地块日温";
+            } else if (name === "夜间温度") {
+                text = "从化地块夜温";
+            } else if (name === "土壤水分") {
+                text = "从化地块水分";
+            }
+            if (text !== "") {
+                staticMapLayers.showSingle(text, true);
+            } else {
+                staticMapLayers.hideAll();
+            }
         }
-      }
     });
 
     // ai与地图交互
-    eventBus.off("chat:showMapLayer", handleMapLayer)
-    eventBus.on("chat:showMapLayer", handleMapLayer)
+    eventBus.off("chat:showMapLayer", handleMapLayer);
+    eventBus.on("chat:showMapLayer", handleMapLayer);
 });
-sessionStorage.removeItem("farmId")
+sessionStorage.removeItem("farmId");
 
-onUnmounted(()=>{
-    eventBus.off("alarmList:changeMapLayer")
-})
+onUnmounted(() => {
+    eventBus.off("alarmList:changeMapLayer");
+});
 
 // ai与地图交互
-const hideChatMapLayer = ref(true)
-const handleMapLayer = ({mapName, isHome}) => {
+const hideChatMapLayer = ref(true);
+const handleMapLayer = ({ mapName, isHome }) => {
     if (!isHome) {
-        hideChatMapLayer.value = false
+        hideChatMapLayer.value = false;
     }
-    staticMapPointLayers.hidePoint()
-    staticMapLayers.hideAll()
+    staticMapPointLayers.hidePoint();
+    staticMapLayers.hideAll();
 
     // 重置时间轴
     // eventBus.emit("map_click_alarm")
     if (mapName === "植保机") {
-        staticMapLayers.show("分散种植", true)
-        staticMapPointLayers.showPoint()
+        staticMapLayers.show("分散种植", true);
+        staticMapPointLayers.showPoint();
     } else if (mapName) {
         // staticMapLayers.show("作物种类")
         if (isHome) {
-            staticMapLayers.show(mapName, true)
+            staticMapLayers.show(mapName, true);
         } else {
-            staticMapLayers.showSingle(mapName, true)
+            staticMapLayers.showSingle(mapName, true);
         }
     }
-}
+};
 
 const toggleChatMapLayer = () => {
-    hideChatMapLayer.value = true
+    hideChatMapLayer.value = true;
     eventBus.emit("chat:hideMapLayer");
-    staticMapLayers.hideAll()
-}
+    staticMapLayers.hideAll();
+};
 
 const destroyPopup = () => {
     eventBus.emit("map:destroyPopup");
@@ -285,52 +284,44 @@ const toggleBox = (name) => {
                 bottom: -12px;
             }
         }
+        .base-tabs {
+            position: fixed;
+            top: 32px;
+            left: 390px;
+            display: flex;
+            align-items: center;
+            .tab-item {
+                padding: 7px 12px 9px;
+                margin-right: 28px;
+                text-align: center;
+                font-family: "PangMenZhengDao";
+                font-size: 16px;
+                color: #fff;
+                background: rgba(28, 36, 41, 0.8);
+                border-radius: 4px;
+                cursor: pointer;
+                border: 1px solid transparent;
+                &.active {
+                    color: #ffdf9a;
+                    background: rgba(19, 22, 16, 0.8);
+                    border: 1px solid #ffd489;
+                }
+            }
+        }
         .warning-search {
             position: absolute;
-            right: 400px;
-            top: -34px;
+            right: 208px;
+            top: -44px;
             display: flex;
             align-items: center;
             .focus-farm {
                 padding-left: 15px;
             }
-            ::v-deep {
-                .el-select__wrapper {
-                    background: rgba(255, 212, 137, 0.2);
-                    box-shadow: 0 0 0 1px rgba(255, 212, 137, 0.3) inset;
-                    height: 50px;
-                    line-height: 50px;
-                    .el-select__caret {
-                        color: #ffd489;
-                    }
-                }
-                .el-select__placeholder {
-                    color: #f7be5a;
-                    font-size: 20px;
-                    font-family: "PangMenZhengDao";
-                    text-align: center;
-                }
-            }
         }
         .warning-top {
             display: flex;
             width: max-content;
             align-items: center;
-            .back-icon {
-                cursor: pointer;
-                margin-right: 20px;
-                height: 50px;
-                display: flex;
-                align-items: center;
-                padding: 0 20px;
-                border: 1px solid rgba(255, 255, 255, 0.6);
-                background: rgba(0, 0, 0, 0.2);
-                border-radius: 4px;
-                img {
-                    width: 17px;
-                    margin-right: 10px;
-                }
-            }
             .top-l {
                 display: flex;
                 flex-direction: column;