浏览代码

feat:添加农事规划页面,修改农场报告页面和农场监测页面

wangsisi 2 周之前
父节点
当前提交
32b9029b65

+ 12 - 12
src/components/recordItem.vue

@@ -5,21 +5,9 @@
         </div>
         <div class="record-content">
             <div class="info-item">
-                触发条件:
-                <span class="info-val">{{ recordItemData?.farmWorkDetail?.condition }}</span>
-            </div>
-            <div class="info-item one-text">
-                农事编号:
-                <span class="info-val">{{ recordItemData?.farmWorkDetail?.code }}</span>
-            </div>
-            <div class="info-item">
                 推荐时间:
                 <span class="info-val">{{ recordItemData?.farmWorkDetail?.executeDate }}</span>
             </div>
-            <div class="info-item" v-if="recordItemData?.attention">
-                巡园提醒:
-                <span class="info-val">{{ recordItemData?.attention }}</span>
-            </div>
             <div class="info-item recipe-name" v-if="onlyRecipeName && recordItemData?.prescriptionList && recordItemData?.prescriptionList.length">
                 <span class="name-text">药物处方:</span>
                 <div class="rescription info-val">
@@ -77,6 +65,18 @@
                     </div>
                 </div>
             </div>
+            <div class="info-item">
+                触发条件:
+                <span class="info-val">{{ recordItemData?.farmWorkDetail?.condition }}</span>
+            </div>
+            <div class="info-item one-text">
+                农事编号:
+                <span class="info-val">{{ recordItemData?.farmWorkDetail?.code }}</span>
+            </div>
+            <div class="info-item" v-if="recordItemData?.attention">
+                巡园提醒:
+                <span class="info-val">{{ recordItemData?.attention }}</span>
+            </div>
         </div>
         <slot name="footer"></slot>
     </div>

+ 8 - 2
src/router/globalRoutes.js

@@ -158,9 +158,9 @@ export default [
         meta: { showTabbar: true, keepAlive: true },
         component: () => import("@/views/old_mini/plan/components/myPrescription.vue"),
     },
-    // 报告详情
+    // 农场报告
     {
-        path: "/report_detail",
+        path: "/farm-report",
         name: "ReportDetail",
         component: () => import("@/views/old_mini/report_detail/index.vue"),
     },
@@ -236,4 +236,10 @@ export default [
         },
         component: () => import("@/views/old_mini/mine/pages/register.vue"),
     },
+    // 农事规划
+    {
+        path: "/plan",
+        name: "Plan",
+        component: () => import("@/views/old_mini/monitor/subPages/plan.vue"),
+    },
 ];

+ 59 - 133
src/views/old_mini/agri_services/components/farmDynamics.vue

@@ -44,58 +44,23 @@
                             </div>
                         </template>
                         <template #footer>
-                            <div class="info-item" v-if="activePlanIndex === 1 || activePlanIndex === 2">
-                                服务报价
+                            <div class="info-item" v-if="activePlanIndex === 1">
+                                农资执行
                                 <span class="info-val">农资农资组织</span>
-                                <div class="info-price">
-                                    <span>¥ 5,642</span>
-                                    <el-icon><ArrowRight /></el-icon>
-                                </div>
-                            </div>
-                            <div class="apply-wrap" v-if="activePlanIndex === 0">
-                                <div class="apply-title">申请列表</div>
-                                <div class="apply-list">
-                                    <div class="apply-item" v-for="(item, itemIndex) in (section.isExpanded ? 4 : 2)" :key="itemIndex">
-                                        <div class="apply-info">
-                                            <el-avatar
-                                                :size="36"
-                                                src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
-                                            />
-                                            <div class="apply-text">
-                                                <div>农资农服农资{{ itemIndex + 1 }}</div>
-                                                <span>位于广东省广州市</span>
-                                            </div>
-                                        </div>
-                                        <div class="action-r apply-price">
-                                            <span>¥ 5,642</span>
-                                            <el-icon><ArrowRight /></el-icon>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="expand-more" @click="toggleExpand(index)">
-                                    <span>展开更多</span>
-                                    <el-icon :class="{ 'rotate': section.isExpanded }">
-                                        <ArrowDown />
-                                    </el-icon>
-                                </div>
                             </div>
-                            <div class="action-group" :class="{'flex-end': activePlanIndex === 0}">
-                                <div class="action-l" v-if="activePlanIndex !== 0">查看详情</div>
+                            <div class="action-group">
+                                <div class="action-l">查看详情</div>
                                 <div class="action-r" v-if="section.orderStatus === 0">
                                     <div class="action-item second-item">拍照识别</div>
                                     <div class="action-item primary-item">去确认</div>
                                 </div>
-                                <div class="action-r" v-if="activePlanIndex === 1">
-                                    <div class="action-item warning-item">发起需求</div>
+                                <div class="action-r" v-if="activePlanIndex === 0">
                                     <div class="action-item primary-item">确认完成</div>
                                 </div>
-                                <div class="action-r" v-if="activePlanIndex === 2">
-                                    <div class="action-item warning-item">发起需求</div>
+                                <div class="action-r" v-if="activePlanIndex === 1">
+                                    <div class="action-item warning-item" @click="handleApply">发起需求</div>
                                     <div class="action-item primary-item">去复核</div>
                                 </div>
-                                <div class="action-r" v-if="activePlanIndex === 0">
-                                    <div class="action-item cancel-item">取消发起</div>
-                                </div>
                             </div>
                         </template>
                     </record-item>
@@ -103,23 +68,27 @@
             </div>
         </div>
     </div>
+    <!-- 需求发送成功弹窗 -->
+    <popup v-model:show="showApplyPopup" round class="apply-popup">
+        <img class="check-icon" src="@/assets/img/home/right.png" alt="">
+        <div class="apply-text">需求发送成功</div>
+        <div class="apply-btn" @click="showApplyPopup = false">我知道了</div>
+    </popup>
 </template>
 <script setup>
 import { ref } from "vue";
-import { ArrowRight, ArrowDown } from "@element-plus/icons-vue";
 import recordItem from "@/components/recordItem.vue";
+import { Popup } from "vant";
+
+const showApplyPopup = ref(false);
 
-const filterType = ref(["待支付", "待执行", "已完成","售后"]);
+const filterType = ref(["待执行", "已完成"]);
 const activePlanIndex = ref(0);
 
 const handlePlanClick = (index) => {
     activePlanIndex.value = index;
 };
 
-const toggleExpand = (sectionIndex) => {
-    contentData.value[sectionIndex].isExpanded = !contentData.value[sectionIndex].isExpanded;
-};
-
 const containerRef = ref(null);
 const handleClick = (e) => {
     e.preventDefault();
@@ -148,6 +117,10 @@ const menuData = [
     },
 ];
 
+const handleApply = () => {
+    showApplyPopup.value = true;
+};
+
 const contentData = ref([
     {
         targetId: "part1",
@@ -1177,9 +1150,11 @@ const contentData = ref([
             height: 28px;
             line-height: 28px;
             border-radius: 20px;
+            border: 1px solid transparent;
             &.active {
-                background: rgba(33, 153, 248, 0.2);
+                background: rgba(33, 153, 248, 0.1);
                 color: #2199f8;
+                border: 1px solid #2199f8;
             }
         }
         .filter-item + .filter-item {
@@ -1324,89 +1299,16 @@ const contentData = ref([
                     }
                 }
             }
-            .info-item {
-                color: #bbbbbb;
-                margin-bottom: 4px;
-                display: flex;
-                align-items: center;
-                .info-val {
-                    font-size: 12px;
-                    color: #666666;
-                }
-                .info-price {
-                    margin-left: 8px;
-                    display: flex;
-                    align-items: center;
-                    font-weight: 500;
-                    font-size: 14px;
-                    color: #1D2129;
-                }
-            }
-
-            .apply-wrap {
-                .apply-title {
-                    color: #2199f8;
-                    font-weight: 500;
-                    margin: 8px 0;
-                }
-                .apply-list {
-                    .apply-item {
-                        display: flex;
-                        align-items: center;
-                        justify-content: space-between;
-                        border-radius: 8px;
-                        border: 1px solid rgba(0, 0, 0, 0.1);
-                        padding: 10px;
-                        .apply-info{
-                            display: flex;
-                            align-items: center;
-                            font-weight: 500;
-                        }
-                        .apply-text {
-                            font-size: 14px;
-                            color: #1D2129;
-                            margin-left: 8px;
-                            span{
-                                font-weight: 400;
-                                font-size: 12px;
-                                color: rgba(29, 33, 41, 0.2);
-                            }
-                        }
-                    }
-                    .apply-item + .apply-item {
-                        margin-top: 8px;
-                    }
-                }
-                .expand-more {
-                    display: flex;
-                    align-items: center;
-                    justify-content: center;
-                    margin-top: 8px;
-                    cursor: pointer;
-                    color: rgba(0, 0, 0, 0.4);
-                    font-size: 12px;
-                    span {
-                        margin-right: 4px;
-                    }
-                    .el-icon {
-                        font-size: 12px;
-                        transition: transform 0.3s ease;
-                        &.rotate {
-                            transform: rotate(180deg);
-                        }
-                    }
-                    &:hover {
-                        color: rgba(0, 0, 0, 0.6);
-                    }
-                }
-            }
             .action-r{
                 color: #1D2129;
             }
-            .apply-price{
-                font-weight: 500;
+            .info-item{
+                font-size: 12px;
+                color: #BBBBBB;
+                .info-val{
+                    color: #666666;
+                }
             }
-
             .action-group {
                 display: flex;
                 align-items: center;
@@ -1414,15 +1316,10 @@ const contentData = ref([
                 padding-top: 8px;
                 margin-top: 8px;
                 border-top: 1px solid #f5f5f5;
-                &.flex-end{
-                    justify-content: flex-end;
-                }
                 .action-l {
                     font-size: 12px;
-                    color: rgba(0, 0, 0, 0.4);
                 }
             }
-            .apply-wrap,
             .action-group {
                 .action-r {
                     display: flex;
@@ -1465,4 +1362,33 @@ const contentData = ref([
         }
     }
 }
+.apply-popup {
+    width: 75%;
+    padding: 28px 28px 20px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    .check-icon{
+        width: 68px;
+        height: 68px;
+        margin-bottom: 12px;
+    }
+    .apply-text{
+        font-size: 24px;
+        font-weight: 500;
+        margin-bottom: 32px;
+        text-align: center;
+    }
+    .apply-btn{
+        width: 100%;
+        box-sizing: border-box;
+        padding: 8px;
+        border-radius: 25px;
+        font-size: 16px;
+        background: #2199F8;
+        color: #fff;
+        text-align: center;
+    }
+}
 </style>

+ 15 - 3
src/views/old_mini/create_farm/editMap.vue

@@ -32,7 +32,7 @@ import EditMap from "./map/editMap.js";
 import { useRouter, useRoute } from "vue-router";
 import { convertPointToArray } from "@/utils/index";
 import { useStore } from "vuex";
-import { ElMessage } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
 
 const router = useRouter();
 const route = useRoute();
@@ -111,8 +111,20 @@ function backgToCreate() {
     }
 }
 const deletePolygon = () => {
-    editMap.deleteCurrentPolygon();
-    ElMessage.success("地块已删除");
+    ElMessageBox.confirm(
+        '确认要删除当前地块吗?删除后可以重新勾画。',
+        '删除确认',
+        {
+            confirmButtonText: '确认删除',
+            cancelButtonText: '取消',
+            type: 'warning',
+        }
+    ).then(() => {
+        editMap.deleteCurrentPolygon();
+        ElMessage.success("地块已删除");
+    }).catch(() => {
+        // 用户取消删除,不做任何操作
+    });
 };
 
 const confirm = () => {

+ 1 - 1
src/views/old_mini/create_farm/map/editMap.js

@@ -114,7 +114,7 @@ class EditMap {
   setMapPosition(center) {
     this.kmap.getView().animate({
       center,
-      zoom: 16,
+      zoom: 17,
       duration: 500,
     });
     this.setMapPoint(center)

+ 1 - 1
src/views/old_mini/home/components/problemReminder.vue

@@ -45,7 +45,7 @@ import detailDialog from "@/components/detailDialog.vue"
 import { useRouter } from "vue-router";
 const router = useRouter();
 
-const show = ref(false);
+const show = ref(true);
 const noShow = ref(false);
 const noClick = () => {
     show.value = false;

+ 3 - 0
src/views/old_mini/home/index.vue

@@ -46,6 +46,8 @@
         </template>
         <div class="create-farm-btn" @click="handleBtn">{{ farmPopupType === 'create' ? '去创建农场' : '我知道了' }}</div>
     </popup>
+    <!-- 问题提醒 -->
+    <problem-reminder></problem-reminder>
 </template>
 
 <script setup>
@@ -56,6 +58,7 @@ import weatherInfo from "@/components/weatherInfo.vue";
 import AgriculturalDynamics from "./components/AgriculturalDynamics.vue";
 import { useRouter, useRoute } from "vue-router";
 import wx from "weixin-js-sdk";
+import problemReminder from "./components/problemReminder.vue";
 
 const store = useStore();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);

+ 8 - 8
src/views/old_mini/mine/index.vue

@@ -121,14 +121,14 @@ const cellItems = ref([
         title: "我的农场",
         path: "/my_farm",
     },
-    {
-        title: "认证农资/专家",
-        path: "/authentication",
-    },
-    {
-        title: "联系客服",
-        path: "/customer-service",
-    },
+    // {
+    //     title: "认证农资/专家",
+    //     path: "/authentication",
+    // },
+    // {
+    //     title: "联系客服",
+    //     path: "/customer-service",
+    // },
     {
         title: "退出登录",
         path: "/logout",

+ 24 - 19
src/views/old_mini/monitor/index.vue

@@ -11,12 +11,12 @@
                     <img class="button-icon" src="@/assets/img/tab_bar/home-active.png" alt="" />
                     <span>基本信息</span>
                 </div>
-                <div class="button-item" @click="toFarmPhoto">
+                <div class="button-item" @click="handlePage('/farm_photo')">
                     <img class="button-icon" src="@/assets/img/home/photo-icon.png" alt="" />
                     <span>农场相册</span>
                 </div>
             </div>
-            <badge dot :offset="[-4, 5]">
+            <badge dot :offset="[-4, 5]" @click="handlePage('/message')">
                 <div class="add-farm-button">
                     <img class="icon" src="@/assets/img/monitor/notice.png" alt="" />
                     <span>农场消息</span>
@@ -31,8 +31,8 @@
                 class="function-card"
                 @click="handleCardClick(card)"
             >
-                <img :src="require(`@/assets/img/monitor/grid-${index + 1}.png`)" :alt="card.title" />
                 <div class="card-title">{{ card.title }}</div>
+                <img :src="require(`@/assets/img/monitor/grid-${index + 1}.png`)" :alt="card.title" />
                 <div class="card-status">
                     {{ card.status }}
                 </div>
@@ -81,6 +81,8 @@
             </div>
         </div>
     </div>
+    <!-- 农场信息 -->
+    <farm-info-popup ref="farmInfoRef"></farm-info-popup>
 </template>
 
 <script setup>
@@ -89,17 +91,24 @@ import { useStore } from "vuex";
 import { Badge } from "vant";
 import weatherInfo from "@/components/weatherInfo.vue";
 import { useRouter } from "vue-router";
+import farmInfoPopup from "../home/components/farmInfoPopup.vue";
 
 const store = useStore();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
 const router = useRouter();
 
+
+const farmInfoRef = ref(null);
+function toFarmInfo() {
+    farmInfoRef.value.handleShow();
+}
+
 // 功能卡片数据
 const functionCards = ref([
     {
         title: "农事规划",
         status: "2 待执行",
-        route: "/agricultural-planning",
+        route: "/plan",
     },
     {
         title: "农场报告",
@@ -144,8 +153,7 @@ const broadcastList = ref([
 
 // 卡片点击事件
 const handleCardClick = (card) => {
-    console.log("卡片点击:", card);
-    // router.push(card.route);
+    router.push(card.route);
 };
 
 // 播报相关事件
@@ -175,15 +183,11 @@ function toSubPage() {
     router.push("/create_farm?isFromHome=true");
 }
 
-function toFarmPhoto() {
+function handlePage(url) {
     router.push({
-        path: "/farm_photo",
+        path: url,
     });
 }
-
-function toFarmInfo() {
-    // farmInfoRef.value.handleShow();
-}
 </script>
 
 <style scoped lang="scss">
@@ -244,6 +248,7 @@ function toFarmInfo() {
             align-items: center;
             justify-content: center;
             gap: 4px;
+            color: rgba(0, 0, 0, 0.8);
             background: rgba(33, 153, 248, 0.1);
             border: 1px solid #fff;
             .icon {
@@ -267,7 +272,7 @@ function toFarmInfo() {
         .function-card {
             background: #fff;
             border-radius: 12px;
-            padding: 25px 0;
+            padding: 20px 0;
             box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
             position: relative;
             display: flex;
@@ -275,21 +280,21 @@ function toFarmInfo() {
             justify-content: center;
             gap: 10px;
             img {
-                width: 30px;
-                height: 30px;
+                width: 40px;
+                height: 40px;
             }
             .card-title {
-                font-size: 14px;
+                font-size: 16px;
                 font-weight: 500;
-                color: #1d2129;
+                color: #1D2129;
             }
             .card-status {
                 position: absolute;
-                top: 0;
+                top: -5px;
                 right: 0;
                 padding: 1px 4px;
                 border-radius: 6px 8px 8px 2px;
-                font-size: 10px;
+                font-size: 11px;
                 background: #2199f8;
                 color: #fff;
             }

+ 453 - 0
src/views/old_mini/monitor/subPages/plan.vue

@@ -0,0 +1,453 @@
+<template>
+    <div class="plan-page">
+        <custom-header name="农事规划"></custom-header>
+        <div class="plan-content">
+            <div class="status-filter">
+                <div class="status-label">农事状态</div>
+                <div class="status-items">
+                    <div v-for="status in statusList" :key="status.value" class="status-item" :class="status.color">
+                        <div class="status-dot"></div>
+                        <span class="status-text">{{ status.label }}</span>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 三行循环时间线 -->
+            <div class="cycle-timeline-container">
+                <div class="cycle-timeline">
+                    <div
+                        v-for="(row, rowIndex) in timelineRows"
+                        :key="rowIndex"
+                        class="cycle-row"
+                        :class="{ 'odd-index': rowIndex % 2 === 1 }"
+                    >
+                        <div
+                            v-for="(item, itemIndex) in row.items"
+                            :key="itemIndex"
+                            class="cycle-item"
+                            :class="[item.type + '-item', item.status]"
+                        >
+                            <!-- 任务框 -->
+                            <div v-if="item.type === 'task'" class="cycle-task-box">
+                                <div class="cycle-task-text">梢期</div>
+                                <div class="cycle-task-text">杀虫</div>
+                                <div v-if="item.icon" class="status-icon" :class="item.icon.type">
+                                    <el-icon v-if="item.icon.type === 'complete'" size="16" color="#1CA900"><SuccessFilled /></el-icon>
+                                    <el-icon v-if="item.icon.type === 'warning'" size="18" color="#FF953D"><WarnTriangleFilled /></el-icon>
+                                </div>
+                            </div>
+                            <!-- 任务连接器 -->
+                            <div v-if="item.type === 'task'" class="cycle-task-connector"></div>
+
+                            <!-- 节气节点 -->
+                            <template v-if="item.type === 'term'">
+                                <div class="cycle-term-dot"></div>
+                                <div class="cycle-term-label">立春</div>
+                            </template>
+                        </div>
+
+                        <!-- 行连接器 -->
+                        <div
+                            v-if="rowIndex < timelineRows.length - 1"
+                            class="cycle-connector"
+                            :class="rowIndex % 2 === 1 ? 'middle-connector' : 'top-connector'"
+                        ></div>
+                    </div>
+                </div>
+            </div>
+
+            <div class="control-section">
+                <div class="toggle-group">
+                    <el-switch v-model="isDefaultEnabled" />
+                    <span class="toggle-label">默认发起农情需求</span>
+                </div>
+                <div class="add-button" @click="addNewTask">新增农事</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { reactive, ref } from "vue";
+import customHeader from "@/components/customHeader.vue";
+
+// 状态列表数据
+const statusList = reactive([
+    { value: "pending", label: "待触发", color: "gray" },
+    { value: "executing", label: "待执行", color: "blue" },
+    { value: "completed", label: "已完成", color: "green" },
+    { value: "expired", label: "已过期", color: "orange" },
+]);
+
+// 切换开关状态
+const isDefaultEnabled = ref(true);
+
+// 时间线行数据
+const timelineRows = reactive([
+    {
+        items: [
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+        ],
+    },
+    {
+        items: [
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "active" },
+            { type: "term" },
+            { type: "task", status: "default" },
+        ],
+    },
+    {
+        items: [
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+        ],
+    },
+    {
+        items: [
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "active" },
+            { type: "term" },
+            { type: "task", status: "default" },
+        ],
+    },
+    {
+        items: [
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+        ],
+    },
+    {
+        items: [
+            { type: "term" },
+            { type: "task", status: "default" },
+            { type: "term" },
+            { type: "task", status: "active" },
+            { type: "term" },
+            { type: "task", status: "default" },
+        ],
+    },
+    {
+        items: [
+            { type: "task", status: "default", icon: { type: "normal", text: "✓" } },
+            { type: "term" },
+            { type: "task", status: "normal", icon: { type: "normal", text: "!" } },
+            { type: "term", status: "active" },
+            { type: "task", status: "complete", icon: { type: "complete", text: "✓" } },
+            { type: "term", status: "active" },
+        ],
+    },
+    {
+        items: [
+        { type: "term", status: "active" },
+            { type: "task", status: "complete", icon: { type: "warning", text: "✓" } },
+            { type: "term", status: "active" },
+            { type: "task", status: "warning", icon: { type: "complete", text: "!" } },
+            { type: "term", status: "active" },
+            { type: "task", status: "complete", icon: { type: "complete", text: "✓" } },
+        ],
+    },
+]);
+
+// 切换默认发起农情需求
+const toggleDefault = () => {
+    isDefaultEnabled.value = !isDefaultEnabled.value;
+};
+
+// 新增农事
+const addNewTask = () => {
+    console.log("新增农事");
+    // 这里可以添加新增农事的逻辑
+};
+</script>
+
+<style scoped lang="scss">
+.plan-page {
+    width: 100%;
+    height: 100vh;
+    background: #f5f7fb;
+    .plan-content {
+        padding: 13px 12px;
+        .status-filter {
+            background: #fff;
+            border-radius: 25px;
+            padding: 6px 17px;
+            margin-bottom: 16px;
+            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+            display: flex;
+            align-items: center;
+            gap: 16px;
+            font-size: 12px;
+
+            .status-label {
+                font-size: 12px;
+                white-space: nowrap;
+            }
+
+            .status-items {
+                display: flex;
+                align-items: center;
+                gap: 20px;
+
+                .status-item {
+                    display: flex;
+                    align-items: center;
+                    gap: 6px;
+                    &.gray {
+                        color: #c4c6c9;
+                        .status-dot {
+                            background-color: #c4c6c9;
+                        }
+                    }
+
+                    &.blue {
+                        color: #2199f8;
+                        .status-dot {
+                            background-color: #2199f8;
+                        }
+                    }
+
+                    &.green {
+                        color: #1ca900;
+                        .status-dot {
+                            background-color: #1ca900;
+                        }
+                    }
+
+                    &.orange {
+                        color: #ff953d;
+                        .status-dot {
+                            background-color: #ff953d;
+                        }
+                    }
+                    .status-dot {
+                        width: 6px;
+                        height: 6px;
+                        border-radius: 50%;
+                    }
+                }
+            }
+        }
+
+        // 循环时间线样式
+        .cycle-timeline-container {
+            padding: 15px;
+            .cycle-timeline {
+                position: relative;
+
+                .cycle-row {
+                    position: relative;
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+                    margin-bottom: 36px;
+                    padding-right: 10px;
+                    &.odd-index {
+                        padding: 0;
+                        padding-left: 10px;
+                    }
+
+                    &:last-child {
+                        margin-bottom: 0;
+                    }
+
+                    // 水平时间线
+                    &::before {
+                        content: "";
+                        position: absolute;
+                        top: 0;
+                        left: 20px;
+                        right: 10px;
+                        height: 1px;
+                        background: #e3e3e3;
+                        transform: translateY(-50%);
+                        z-index: 1;
+                    }
+
+                    .cycle-item {
+                        position: relative;
+                        z-index: 2;
+                        top: 8px;
+
+                        &.task-item {
+                            .cycle-task-box {
+                                background: #fff;
+                                border: 1px solid #dde1e7;
+                                border-radius: 4px;
+                                padding: 4px 8px;
+                                text-align: center;
+                                position: relative;
+
+                                .cycle-task-text {
+                                    font-size: 12px;
+                                    color: #dde1e7;
+                                    line-height: 1.2;
+
+                                    &:first-child {
+                                        margin-bottom: 2px;
+                                    }
+                                }
+                                .status-icon {
+                                    position: absolute;
+                                    bottom: -10px;
+                                    right: -10px;
+                                }
+                            }
+
+                            .cycle-task-connector {
+                                position: absolute;
+                                top: -4px;
+                                left: 50%;
+                                transform: translateX(-50%);
+                                width: 0;
+                                height: 0;
+                                border-left: 4px solid transparent;
+                                border-right: 4px solid transparent;
+                                border-bottom: 4px solid #dde1e7;
+                            }
+
+                            &.warning{
+                                .cycle-task-box{
+                                    border-color: #FF953D;
+                                }
+                                .cycle-task-text {
+                                    color: #FF953D;
+                                }
+                                .cycle-task-connector{
+                                    border-bottom-color: #FF953D;
+                                }
+                            }
+                            &.complete{
+                                .cycle-task-box{
+                                    border-color: #1CA900;
+                                }
+                                .cycle-task-text {
+                                    color: #1CA900;
+                                }
+                                .cycle-task-connector{
+                                    border-bottom-color: #1CA900;
+                                }
+                            }
+                            &.normal{
+                                .cycle-task-box{
+                                    border-color: #2199F8;
+                                }
+                                .cycle-task-text {
+                                    color: #2199F8;
+                                }
+                                .cycle-task-connector{
+                                    border-bottom-color: #2199F8;
+                                }
+                            }
+                        }
+
+                        &.term-item {
+                            display: flex;
+                            flex-direction: column;
+                            align-items: center;
+                            top: -11px;
+
+                            .cycle-term-dot {
+                                width: 6px;
+                                height: 6px;
+                                background: #c7c7c7;
+                                border-radius: 50%;
+                                margin-bottom: 4px;
+                            }
+
+                            .cycle-term-label {
+                                font-size: 11px;
+                                color: #c7c7c7;
+                            }
+
+                            &.active{
+                                .cycle-term-dot {
+                                    background: #858383;
+                                }
+                                .cycle-term-label {
+                                    color: #858383;
+                                }
+                            }
+                        }
+                    }
+
+                    .cycle-connector {
+                        position: absolute;
+                        right: 0;
+                        top: 38.5px;
+                        transform: translateY(-50%);
+                        width: 30px;
+                        height: 76px;
+                        border: 1px solid #e3e3e3;
+                        border-left: none;
+                        background: transparent;
+
+                        &.top-connector {
+                            border-top-right-radius: 5px;
+                            border-bottom-right-radius: 5px;
+                        }
+
+                        &.middle-connector {
+                            border-top-left-radius: 5px;
+                            border-bottom-left-radius: 5px;
+                            left: 0;
+                            border-right: none;
+                            border-left: 1px solid #e3e3e3;
+                        }
+                    }
+                }
+            }
+        }
+
+        // 控制区域样式
+        .control-section {
+            position: fixed;
+            width: 100%;
+            left: 0;
+            box-sizing: border-box;
+            bottom: 0px;
+            background: #fff;
+            padding: 16px 12px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            border-top: 1px solid #f0f0f0;
+
+            .toggle-group {
+                display: flex;
+                align-items: center;
+                gap: 8px;
+
+                .toggle-label {
+                    font-size: 14px;
+                    color: #141414;
+                }
+            }
+
+            .add-button {
+                background: linear-gradient(120deg, #76c3ff 0%, #2199f8 100%);
+                color: white;
+                border-radius: 25px;
+                padding: 10px 20px;
+            }
+        }
+    }
+}
+</style>

+ 62 - 126
src/views/old_mini/report_detail/index.vue

@@ -1,26 +1,14 @@
 <template>
     <div class="report-detail-page">
-        <custom-header name="报告详情"></custom-header>
+        <custom-header name="农场报告"></custom-header>
         <div class="report-content">
             <div class="report-header">
-                <div class="header-t">
-                    <div class="report-info">
-                        <img src="" alt="" />
-                        <div class="report-text">
-                            <div class="report-title">未命名农场1</div>
-                            <span class="report-address">广东省广州市从化区从化区</span>
-                        </div>
-                    </div>
-                    <el-select class="header-select" v-model="value">
-                        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
-                    </el-select>
-                </div>
-                <div class="header-b">
-                    <div class="b-item" v-for="item in 3" :key="item">
-                        <span class="item-title">荔枝桂味</span>
-                        <div>农场品种</div>
-                    </div>
-                </div>
+                <el-select class="header-item" v-model="value" placeholder="Select" style="width: 240px">
+                    <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+                </el-select>
+                <el-select class="header-item" v-model="value1" placeholder="Select" style="width: 240px">
+                    <el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value" />
+                </el-select>
             </div>
             <tabs v-model:active="active" class="tabs" scrollspy sticky offset-top="40" background="#f5f7fb">
                 <tab title="果园总览" class="tab-item">
@@ -101,17 +89,8 @@
         </div>
         <!-- 底部 -->
         <div class="fixed-bottom">
-            <div class="bottom-l">
-                <div class="l-btn">
-                    <el-icon color="#666666" class="btn-icon" size="16"><Download /></el-icon>
-                    导出报告
-                </div>
-                <div class="l-btn" @click="sharePopup">
-                    <Icon color="#666666" name="share-o" size="16" class="btn-icon" />
-                    分享报告
-                </div>
-            </div>
-            <div class="bottom-r">推荐农事</div>
+            <div class="btn bottom-l " @click="sharePopup">保存报告</div>
+            <div class="btn bottom-r">转发报告</div>
         </div>
     </div>
     <!-- 报告弹窗 -->
@@ -120,34 +99,33 @@
 
 <script setup>
 import customHeader from "@/components/customHeader.vue";
-import { Icon, Tab, Tabs } from "vant";
+import { Tab, Tabs } from "vant";
 import { ref } from "vue";
 import reportPopup from "@/components/reportPopup.vue";
 import { useRouter } from "vue-router";
 
 const router = useRouter();
-const value = ref("");
 
+const value = ref("Option1");
 const options = [
     {
         value: "Option1",
-        label: "Option1",
+        label: "时间筛选",
     },
     {
         value: "Option2",
         label: "Option2",
     },
+];
+const value1 = ref("Option1");
+const options1 = [
     {
-        value: "Option3",
-        label: "Option3",
-    },
-    {
-        value: "Option4",
-        label: "Option4",
+        value: "Option1",
+        label: "区域筛选",
     },
     {
-        value: "Option5",
-        label: "Option5",
+        value: "Option2",
+        label: "Option2",
     },
 ];
 
@@ -171,71 +149,47 @@ const handleSeeMore = () => {
     .report-content {
         height: calc(100% - 40px);
         overflow: auto;
-        padding: 10px 12px;
+        padding: 12px;
         box-sizing: border-box;
         .report-header {
-            background-color: #fff;
-            border-radius: 14px;
-            padding: 12px;
-            .header-t {
-                display: flex;
-                align-items: center;
-                justify-content: space-between;
-                .report-info {
-                    display: flex;
-                    align-items: center;
-                    gap: 8px;
-                    img {
-                        width: 40px;
-                        height: 40px;
-                        border-radius: 8px;
-                        background-color: #2199f8;
+            display: flex;
+            align-items: center;
+            gap: 10px;
+            .header-item {
+                flex: 1;
+                ::v-deep{
+                    .el-select__wrapper{
+                        padding: 5px 0;
+                        justify-content: center;
+                        background: #fff;
                     }
-                    .report-text {
-                        .report-title {
-                            font-weight: 500;
-                            color: #171717;
-                        }
-                        .report-address {
-                            font-size: 12px;
-                            color: rgba(23, 23, 23, 0.5);
-                        }
+                    .el-select__selection {
+                        flex: none;
+                        width: fit-content;
                     }
-                }
-                .header-select {
-                    width: 100px;
-                }
-            }
-            .header-b {
-                display: flex;
-                align-items: center;
-                gap: 5px;
-                margin-top: 10px;
-                .b-item {
-                    flex: 1;
-                    border-radius: 5px;
-                    border: 1px solid rgba(2, 2, 2, 0.3);
-                    font-size: 10px;
-                    padding: 10px 0;
-                    box-sizing: border-box;
-                    text-align: center;
-                    .item-title {
-                        font-size: 16px;
-                        color: #000;
-                        font-family: "SmileySans";
+                    .el-select__placeholder {
+                        position: static;
+                        transform: none;
+                        width: fit-content;
+                        color: #4E5969;
+                    }
+                    .el-select__caret {
+                        color: #4E5969;
                     }
                 }
             }
         }
         .tabs {
-            margin-top: 10px;
+            margin-top: 16px;
             ::v-deep {
                 .van-tabs__nav {
                     height: 70%;
+                    .van-tab{
+                        border-bottom: 2px solid #E5E6EB;
+                    }
                     .van-tab--active{
                         color: #2199F8;
-                        background: rgba(33, 153, 248, 0.2);
-                        border-radius: 25px;
+                        border-bottom: 2px solid #2199F8;
                     }
                 }
                 .van-tabs__line{
@@ -324,49 +278,31 @@ const handleSeeMore = () => {
     }
     .fixed-bottom {
         position: absolute;
-        bottom: 12px;
-        left: 12px;
-        width: calc(100% - 24px);
+        bottom: 0;
+        left: 0;
+        width: 100%;
         display: flex;
         align-items: center;
         justify-content: space-between;
-        padding: 14px 12px;
-        background: linear-gradient(180deg, #f0f8ff 6px, #ffffff 20px);
-        border-radius: 14px;
+        padding: 15px 12px;
+        background: #fff;
         box-sizing: border-box;
-        box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.1);
-        .bottom-l {
+        .btn{
+            width: 142px;
+            border-radius: 30px;
+            padding: 10px;
+            box-sizing: border-box;
             display: flex;
             align-items: center;
-            .l-btn {
-                border: 1px solid rgba(153, 153, 153, 0.5);
-                border-radius: 30px;
-                padding: 0 8px 0 12px;
-                height: 32px;
-                line-height: 32px;
-                box-sizing: border-box;
-                display: flex;
-                align-items: center;
-                justify-content: center;
-                color: #666666;
-                .btn-icon {
-                    padding-right: 3px;
-                }
-                .calculator-icon {
-                    width: 12px;
-                }
-            }
-            .l-btn + .l-btn {
-                margin-left: 10px;
-            }
+            justify-content: center;
+        }
+        .bottom-l {
+            border: 1px solid rgba(133, 133, 133, 0.2);
+            color: #666666;
         }
         .bottom-r {
-            height: 32px;
-            line-height: 32px;
-            background: #2199f8;
-            border-radius: 20px;
+            background: linear-gradient(160deg, #76C3FF, #2199F8);
             color: #fff;
-            padding: 0 12px;
         }
     }
 }