ソースを参照

fix: 农事执行

lxf 2 日 前
コミット
0d62522205

+ 0 - 244
src/views/old_mini/farm_manage/map/index.js

@@ -1,244 +0,0 @@
-import * as KMap from "@/utils/ol-map/KMap";
-import * as util from "@/common/ol_common.js";
-import config from "@/api/config.js";
-import { Point } from 'ol/geom';
-import Feature from "ol/Feature";
-import Style from "ol/style/Style";
-import Photo from "ol-ext/style/Photo";
-import { Fill, Text, Icon, Stroke } from "ol/style.js";
-import { newPoint } from "@/utils/map";
-
-/**
- * @description 地图层对象
- */
-class IndexMap {
-  constructor() {
-    let that = this;
-    let vectorStyle = new KMap.VectorStyle();
-    this.vectorStyle = vectorStyle;
-    // 位置图标
-    this.clickPointLayer = new KMap.VectorLayer("clickPointLayer", 9999, {
-      style: (f) => {
-        return new Style({
-          image: new Icon({
-            src: require("@/assets/img/home/garden-point.png"),
-            scale: 0.5,
-            anchor: [0.5, 1],
-          }),
-        });
-      },
-    });
-
-    this.gardenPointLayer = new KMap.VectorLayer("gardenPointLayer", 99, {
-      minZoom: 6,
-      maxZoom: 22,
-      style: (feature) => {
-        let style1 = new Style({
-          image: new Photo({
-            src: "https://birdseye-img.sysuimars.com/ai_result/2023/11/20/tree_4414/img_27572.jpg" + '?imageView2/1/w/300/interlace/1',
-            radius: 19,
-            shadow: 0,
-            crop: true,
-            onload: function () {
-              that.gardenPointLayer.layer.changed();
-            },
-            displacement: [-1, -1],
-            stroke: new Stroke({
-              width: 2,
-              color: "#fdfcfc00",
-            }),
-          }),
-        });
-        let style2 = new Style({
-          image: new Photo({
-            src: require("@/assets/img/map/garden-border.png"),
-            radius: 24,
-            shadow: 0,
-            crop: false,
-            onload: function () {
-              that.gardenPointLayer.layer.changed();
-            },
-            displacement: [0, -6],
-            stroke: new Stroke({
-              width: 0,
-              color: "#fdfcfc00",
-            }),
-          }),
-        });
-        let style3 = new Style({
-          text: new Text({
-            text: feature.get('farmName') || feature.get('mapInfo'),
-            offsetX: 0,
-            offsetY: -30,
-            font: "bold 12px sans-serif",
-            fill: new Fill({ color: "#fff" }), // 字体颜色
-          }),
-        });
-
-        const canvas = document.createElement('canvas');
-        const ctx = canvas.getContext('2d');
-
-        // 矩形的参数
-        const x = 150; // 矩形中心点的x坐标
-        const y = 100; // 矩形中心点的y坐标
-        const width = 98; // 矩形的宽度
-        const height = 20; // 矩形的高度
-        const cornerRadius = 8; // 圆角半径
-
-        // 创建渐变
-        const gradient = ctx.createLinearGradient(x - width / 2, y, x + width / 2, y);
-        gradient.addColorStop(0, '#2199F8');   // 渐变起始颜色
-        gradient.addColorStop(1, '#2199F8');  // 渐变结束颜色
-
-        // 绘制圆角矩形
-        ctx.beginPath();
-        ctx.moveTo(x - width / 2 + cornerRadius, y - height / 2); // 左上角
-        ctx.lineTo(x + width / 2 - cornerRadius, y - height / 2); // 上边
-        ctx.arc(x + width / 2 - cornerRadius, y - height / 2 + cornerRadius, cornerRadius, -Math.PI / 2, 0); // 右上角
-        ctx.lineTo(x + width / 2, y + height / 2 - cornerRadius); // 右边
-        ctx.arc(x + width / 2 - cornerRadius, y + height / 2 - cornerRadius, cornerRadius, 0, Math.PI / 2); // 右下角
-        ctx.lineTo(x - width / 2 + cornerRadius, y + height / 2); // 下边
-        ctx.arc(x - width / 2 + cornerRadius, y + height / 2 - cornerRadius, cornerRadius, Math.PI / 2, Math.PI); // 左下角
-        ctx.lineTo(x - width / 2, y - height / 2 + cornerRadius); // 左边
-        ctx.arc(x - width / 2 + cornerRadius, y - height / 2 + cornerRadius, cornerRadius, Math.PI, -Math.PI / 2); // 左上角
-        ctx.closePath();
-
-        // 填充颜色
-        ctx.fillStyle = gradient;
-        ctx.fill();
-        const newStyle = new Style({
-          image: new Icon({
-            src: canvas.toDataURL(),
-            displacement: [0, 56],
-          }),
-        });
-
-
-        return [style1, style2, newStyle, style3];
-      },
-    });
-
-    this.gardenHallLayer = new KMap.VectorLayer("gardenHallLayer", 99, {
-      minZoom: 6,
-      maxZoom: 22,
-      style: (feature) => {
-        let style2 = new Style({
-          image: new Icon({
-            src: require("@/assets/img/map/hall.png"),
-            scale: 0.4,
-            anchor: [0.5, 1],
-            displacement: [0, -36],
-          }),
-        });
-        let style3 = new Style({
-          text: new Text({
-            text: feature.get('totalArea') ? feature.get('totalArea').toFixed(2) + '亩' : '--',
-            offsetX: 0,
-            offsetY: -30,
-            font: "bold 12px sans-serif",
-            fill: new Fill({ color: "#fff" }), // 字体颜色
-          }),
-        });
-        let style4 = new Style({
-          text: new Text({
-            text: feature.get('districtName'),
-            offsetX: 0,
-            offsetY: -6,
-            font: "bold 14px sans-serif",
-            fill: new Fill({ color: "#2199F8" }), // 字体颜色
-          }),
-        });
-
-        const canvas = document.createElement('canvas');
-        const ctx = canvas.getContext('2d');
-
-        // 矩形的参数
-        const x = 150; // 矩形中心点的x坐标
-        const y = 100; // 矩形中心点的y坐标
-        const width = 58; // 矩形的宽度
-        const height = 20; // 矩形的高度
-        const cornerRadius = 4; // 圆角半径
-
-        // 创建渐变
-        const gradient = ctx.createLinearGradient(x - width / 2, y, x + width / 2, y);
-        gradient.addColorStop(0, '#2199F8');   // 渐变起始颜色
-        gradient.addColorStop(1, '#2199F8');  // 渐变结束颜色
-
-        // 绘制圆角矩形
-        ctx.beginPath();
-        ctx.moveTo(x - width / 2 + cornerRadius, y - height / 2); // 左上角
-        ctx.lineTo(x + width / 2 - cornerRadius, y - height / 2); // 上边
-        ctx.arc(x + width / 2 - cornerRadius, y - height / 2 + cornerRadius, cornerRadius, -Math.PI / 2, 0); // 右上角
-        ctx.lineTo(x + width / 2, y + height / 2 - cornerRadius); // 右边
-        ctx.arc(x + width / 2 - cornerRadius, y + height / 2 - cornerRadius, cornerRadius, 0, Math.PI / 2); // 右下角
-        ctx.lineTo(x - width / 2 + cornerRadius, y + height / 2); // 下边
-        ctx.arc(x - width / 2 + cornerRadius, y + height / 2 - cornerRadius, cornerRadius, Math.PI / 2, Math.PI); // 左下角
-        ctx.lineTo(x - width / 2, y - height / 2 + cornerRadius); // 左边
-        ctx.arc(x - width / 2 + cornerRadius, y - height / 2 + cornerRadius, cornerRadius, Math.PI, -Math.PI / 2); // 左上角
-        ctx.closePath();
-
-        // 填充颜色
-        ctx.fillStyle = gradient;
-        ctx.fill();
-        const newStyle = new Style({
-          image: new Icon({
-            src: canvas.toDataURL(),
-            displacement: [0, 56],
-          }),
-        });
-
-
-        return [style2, newStyle, style3, style4];
-      },
-    });
-  }
-
-  initMap(location, target, isCapital) {
-    let level = 16;
-    let coordinate = util.wktCastGeom(location).getFirstCoordinate();
-    this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 6, 22, isCapital ? "img" : "vec");
-    let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
-    this.kmap.addXYZLayer(xyz2, { minZoom: 6, maxZoom: 22 }, 2);
-    this.kmap.addLayer(this.clickPointLayer.layer);
-    this.kmap.addLayer(this.gardenPointLayer.layer);
-    this.kmap.addLayer(this.gardenHallLayer.layer);
-    if (isCapital) {
-      // this.initData()
-      const point = ["113.61652616170711,23.58399613872042", "113.61767554789421, 23.590079887444034", "113.62757101477101, 23.590796948574365", "113.62240816252164, 23.59499176519138"]
-    } else {
-      let point = new Feature(new Point(coordinate))
-      this.clickPointLayer.addFeature(point)
-    }
-  }
-
-  initData(taskList, label,pointType='point') {
-    this.gardenPointLayer.source.clear();
-    if (taskList.length > 0) {  // 如果任务列表不为空,则添加任务点
-      for (let item of taskList) {
-        item.mapInfo = label ? item[label] : (item.executeDeadlineDate || item.executeDate)?.replace(/^\d{4}-(\d{2})-(\d{2})$/, '$1.$2') + '  ' + item.farmWorkName
-        this.gardenPointLayer.source.addFeature(newPoint(item, pointType, "myGarden"))
-      }
-      this.kmap.getView().fit(this.gardenPointLayer.source.getExtent(), { padding: [42, 20, 30, 20] });
-      const finalZoom = this.kmap.getView().getZoom();
-      if (finalZoom > 18) {
-        this.kmap.getView().setZoom(18);
-      }
-    }
-  }
-
-  initDataHall(taskList) {
-    this.gardenHallLayer.source.clear();
-    if (taskList.length > 0) {  // 如果任务列表不为空,则添加任务点
-      for (let item of taskList) {
-        this.gardenHallLayer.source.addFeature(newPoint(item, "centerPoint", "hallGarden"))
-      }
-      this.kmap.getView().fit(this.gardenHallLayer.source.getExtent(), { padding: [60, 40, 30, 40] });
-      const finalZoom = this.kmap.getView().getZoom();
-      if (finalZoom > 18) {
-        this.kmap.getView().setZoom(18);
-      }
-    }
-  }
-}
-
-export default IndexMap;

+ 0 - 86
src/views/old_mini/work_execute/components/ExecutorHeader.vue

@@ -1,86 +0,0 @@
-<template>
-    <div class="executor-header">
-        <el-avatar :size="size" :src="avatar" class="executor-avatar">
-            {{ name?.charAt(0) }}
-        </el-avatar>
-        <div class="executor-profile">
-            <div class="executor-name-row">
-                <span class="executor-name">{{ name }}</span>
-                <span v-if="subjectName" class="executor-tag">{{ subjectName }}</span>
-            </div>
-            <div v-if="phone" class="executor-phone">电话:{{ phone }}</div>
-        </div>
-        <slot name="info"></slot>
-    </div>
-</template>
-
-<script setup>
-defineProps({
-    name: {
-        type: String,
-        default: "",
-    },
-    phone: {
-        type: String,
-        default: "",
-    },
-    avatar: {
-        type: String,
-        default: "",
-    },
-    subjectName: {
-        type: String,
-        default: "执行者",
-    },
-    size: {
-        type: Number,
-        default: 36,
-    },
-});
-</script>
-
-<style lang="scss" scoped>
-.executor-header {
-    display: flex;
-    align-items: center;
-    gap: 10px;
-}
-
-.executor-avatar {
-    flex-shrink: 0;
-    background: rgba(33, 153, 248, 0.15);
-    color: #2199f8;
-    font-size: 16px;
-}
-
-.executor-profile {
-    flex: 1;
-    min-width: 0;
-}
-
-.executor-name-row {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-    margin-bottom: 4px;
-}
-
-.executor-name {
-    font-size: 14px;
-    color: #1d2129;
-}
-
-.executor-tag {
-    flex-shrink: 0;
-    padding: 1px 6px;
-    font-size: 10px;
-    color: #2199f8;
-    background: rgba(33, 153, 248, 0.1);
-    border-radius: 2px;
-}
-
-.executor-phone {
-    font-size: 10px;
-    color: #86909c;
-}
-</style>

+ 434 - 0
src/views/old_mini/work_execute/components/calendar.vue

@@ -0,0 +1,434 @@
+<template>
+    <div class="calendar">
+        <div class="header-wrap">
+            <!-- <div class="header-l">
+                <div class="top-l">
+                    <el-icon class="icon icon-l" color="#999999" size="11" @click="prevPeriod"><ArrowLeftBold /></el-icon>
+                </div>
+                <div class="top-c">
+                    <span class="header-text">
+                        {{ dateRange.start }} <span class="center-line">-</span> {{ dateRange.end }}
+                    </span>
+                </div>
+                <div class="top-r">
+                    <el-icon class="icon icon-r" color="#999999" size="11" @click="nextPeriod"><ArrowRightBold /></el-icon>
+                </div>
+            </div> -->
+            <!-- <div class="header-r">
+                <span class="line"></span>
+                高温预警
+            </div> -->
+        </div>
+        <div class="days">
+            <div
+                class="days-item"
+                v-for="(day, index) in calendarDays"
+                :key="index"
+                :class="[{ activeDay: activeDay === day.date, today: day.isToday && !day.solarTerm }]"
+                @click="selectDate(day.date, day)"
+            >
+                <div class="day-box">
+                    <span class="days-week">{{ day.isToday ? "今天" : `周${day.dayOfWeek}` }}</span>
+                    <span class="days-one">{{ day.day }}</span>
+                </div>
+                <div v-if="day.solarTerm" class="solar-term">{{ day.solarTerm }}</div>
+                <div v-if="day.typeName" class="type-num">{{ day.typeName.lengthVal }}</div>
+                <!-- <div v-if="day.isHeatWarning" class="heat-warning"></div>
+                <div v-if="day.typeName" class="type-name">
+                    <div class="type-text">{{ day.typeName.farmWorkName }}</div>
+                </div> -->
+            </div>
+
+            <div class="days-fixed">
+                <div>
+                    <el-icon><Calendar /></el-icon>
+                </div>
+                <div>日历</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, computed, onDeactivated, onMounted } from "vue";
+import solarLunar from "solarlunar";
+// const props = defineProps({
+//     calendarWorkList: {
+//         type: Array,
+//         required: true,
+//     },
+// });
+
+const today = new Date();
+// const startDate = ref(getAlignedStartDate(today));
+const startDate = ref(new Date(today));
+// 定义星期几的名称
+const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
+const weekdaysShort = ["一", "二", "三", "四", "五", "六", "日"];
+// 存储任务列表数据
+const taskListData = ref([]);
+
+const days = computed(() => {
+    return Array.from({ length: 7 }, (_, i) => {
+        const date = new Date(startDate.value);
+        date.setDate(startDate.value.getDate() + i);
+        return date;
+    });
+});
+
+const calendarDays = computed(() => {
+    const daysList = [];
+    for (let i = 0; i < days.value.length; i++) {
+        const date = days.value[i];
+        const dayOfWeek = date.getDay(); // 0是周日,1是周一,...,6是周六
+        // 调整显示:周一显示为"一",周二显示为"二",...,周日显示为"日"
+        const displayDayOfWeek = dayOfWeek === 0 ? "日" : weekdaysShort[dayOfWeek - 1];
+
+        // 获取该日期的节气
+        const solarTerm = getSolarTerm(date);
+        // 获取该日期的农事数据
+        const typeName = getTaskByDate(formatDate(date));
+
+        daysList.push({
+            day: date.getDate(),
+            date: formatDate(date),
+            isToday: formatDate(date) === formatDate(today),
+            dayOfWeek: displayDayOfWeek,
+            solarTerm: solarTerm, // 使用真实计算的节气数据
+            isHeatWarning: i === 5,
+            typeName: typeName, // 使用传入的任务数据
+        });
+    }
+    return daysList;
+});
+
+// 根据日期获取农事数据
+function getTaskByDate(dateStr) {
+    if (!taskListData.value || taskListData.value.length === 0) {
+        return null;
+    }
+    // 查找匹配日期的农事
+    const matchedTasks = taskListData.value.filter(task => {
+        if (!task.expectedExecuteDate && !task.executeDeadlineDate) return false;
+        // 格式化日期字符串进行比较(确保格式一致)
+        const taskDate = formatDate(new Date(task.expectedExecuteDate || task.executeDeadlineDate));
+        return taskDate === dateStr;
+    });
+
+    if (matchedTasks.length === 0) {
+        return null;
+    }
+
+    // const farmWorkNames = matchedTasks.map(task => task.farmWorkName || '').filter(Boolean);
+    return {
+        lengthVal: matchedTasks.length,
+        // farmWorkName: farmWorkNames.length > 1 ? farmWorkNames.join('、') : farmWorkNames[0] || ''
+    };
+}
+
+function setSolarTerm(taskList) {
+    // 存储任务列表数据
+    taskListData.value = taskList || [];
+    // 为每个任务计算节气
+    for (let task of taskListData.value) {
+        if (task.expectedExecuteDate || task.executeDeadlineDate) {
+            task.solarTerm = getSolarTerm(new Date(task.expectedExecuteDate || task.executeDeadlineDate));
+        }
+    }
+}
+
+const expiredCounts = ref(0);
+const completedCounts = ref(0);
+function setCounts(index, counts) {
+    if (index === 2) {
+        completedCounts.value = counts;
+    } else if (index === 3) {
+        expiredCounts.value = counts;
+    }
+}
+
+// 清除选中状态
+const clearSelection = () => {
+    activeDay.value = null;
+    selectedDate.value = null;
+};
+
+defineExpose({
+    setSolarTerm,
+    setCounts,
+    clearSelection
+});
+
+const dateRange = computed(() => {
+    let start = calendarDays.value[0].date;
+    start = start.replace(/-/g, ".");
+    let end = calendarDays.value[6].date;
+    end = end.replace(/^\d{4}-(\d{2})-(\d{2})$/, "$1.$2");
+    return { start, end };
+});
+
+function getAlignedStartDate(referenceDate) {
+    const start = new Date(referenceDate);
+    const dayOfWeek = start.getDay();
+    start.setDate(start.getDate() - dayOfWeek + (dayOfWeek === 0 ? -13 : 1)); // 对齐至周一,确保21天周期合理
+    return start;
+}
+
+function formatDate(date) {
+    // String(currentMonth.value).padStart(2, "0")
+    return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(
+        2,
+        "0"
+    )}`;
+}
+
+// 获取指定日期的节气
+function getSolarTerm(date) {
+    try {
+        const year = date.getFullYear();
+        const month = date.getMonth() + 1; // getMonth() 返回 0-11,需要加1
+        const day = date.getDate();
+        const lunar = solarLunar.solar2lunar(year, month, day);
+        // lunar.term 返回节气名称,如果没有节气则返回空字符串
+        return lunar.term || null;
+    } catch (error) {
+        console.error("获取节气失败:", error);
+        return null;
+    }
+}
+
+function prevPeriod() {
+    startDate.value.setDate(startDate.value.getDate() - 7);
+    startDate.value = new Date(startDate.value);
+}
+
+function nextPeriod() {
+    startDate.value.setDate(startDate.value.getDate() + 7);
+    startDate.value = new Date(startDate.value);
+}
+
+const activeDay = ref(null);
+const emit = defineEmits(['dateSelect']);
+
+const selectDate = (date, day) => {
+    // 如果点击的是已选中的日期,取消选中
+    if (activeDay.value === date) {
+        activeDay.value = null;
+        selectedDate.value = null;
+        emit('dateSelect', null); // 传递 null 表示取消筛选
+    } else {
+        activeDay.value = date;
+        selectedDate.value = `${date} (${day.dayOfWeek})`;
+        emit('dateSelect', date); // 传递选中的日期
+    }
+};
+
+// 初始化时不选择任何日期(默认不选中)
+// onMounted(() => {
+//     selectDate(formatDate(today), {
+//         day: today.getDate(),
+//         dayOfWeek: weekdaysShort[today.getDay() === 0 ? 6 : today.getDay() - 1],
+//     });
+// });
+
+function closeDialog() {
+    activeDay.value = null;
+}
+const selectedDate = ref(null);
+</script>
+
+<style lang="scss" scoped>
+.calendar {
+    width: 100%;
+    text-align: center;
+    box-sizing: border-box;
+    .header-wrap {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 10px;
+        .header-l {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            width: 100%;
+            .top-c {
+                flex: 1;
+                text-align: center;
+            }
+            .header-text {
+                color: #000;
+                font-size: 16px;
+                font-weight: bold;
+                .center-line {
+                    position: relative;
+                    top: -3px;
+                }
+            }
+            .icon {
+                width: 20px;
+                height: 20px;
+                background: #F2F3F5;
+                border-radius: 50%;
+                text-align: center;
+                line-height: 20px;
+                &.icon-l {
+                    margin-right: 2px;
+                }
+                &.icon-r {
+                    margin-left: 2px;
+                }
+            }
+            .top-tag {
+                font-size: 12px;
+                padding: 0 8px;
+                height: 20px;
+                line-height: 20px;
+                border-radius: 20px;
+                &.red {
+                    color: #FF0000;
+                    background: rgba(255, 0, 0, 0.1);
+                }
+                &.orange {
+                    color: #FF790B;
+                    background: rgba(255, 149, 61, 0.2);
+                }
+            }
+        }
+        .header-r {
+            background: rgba(252, 138, 44, 0.12);
+            padding: 6px 10px;
+            border-radius: 28px;
+            color: #fc8a2c;
+            display: inline-flex;
+            align-items: center;
+            font-size: 10px;
+            .line {
+                width: 12px;
+                height: 1px;
+                margin-right: 5px;
+                background: #fc8a2c;
+            }
+        }
+    }
+}
+
+.weekdays {
+    display: grid;
+    grid-template-columns: repeat(7, 1fr);
+    font-size: 12px;
+}
+.days {
+    display: grid;
+    grid-template-columns: repeat(7, 1fr);
+    // gap: 5px;
+    font-size: 12px;
+    position: relative;
+    .days-fixed {
+        position: absolute;
+        top: 0;
+        right: 0;
+        display: flex;
+        align-items: center;
+        flex-direction: column;
+        justify-content: center;
+        box-shadow: -4px 0px 4px 0px #00000000;
+        width: 48px;
+        height: 50px;
+        background: #fff;
+        color: #969696;
+    }
+    .days-item + .days-item {
+        margin-left: 6px;
+    }
+    .days-item {
+        cursor: pointer;
+        position: relative;
+        &.today {
+            // min-height: 70px;
+            .day-box {
+                color: #2199f8;
+            }
+        }
+
+        &.activeDay {
+            .day-box {
+                color: #fff;
+                background: linear-gradient(136deg, #9fd5ff, #2199f8);
+            }
+        }
+        .day-box {
+            background: #F7F7F7;
+            color: #000;
+            border-radius: 8px;
+            padding: 7px 0;
+            position: relative;
+            .days-week {
+                font-size: 12px;
+            }
+        }
+        .solar-term {
+            padding-top: 3px;
+            color: #8D8D8D;
+            font-size: 12px;
+        }
+        .type-num {
+            position: absolute;
+            top: -5px;
+            right: -5px;
+            color: #fff;
+            font-size: 10px;
+            background: #2199F8;
+            width: 14px;
+            height: 14px;
+            line-height: 16px;
+            border-radius: 50%;
+        }
+        .days-one {
+            text-align: center;
+            display: block;
+            margin: 0 auto;
+            font-size: 14px;
+            line-height: 16px;
+            font-weight: bold;
+            padding-top: 2px;
+            // width: 32px;
+            // height: 32px;
+            // line-height: 32px;
+            // border-radius: 50%;
+        }
+        .type-name {
+            font-size: 10px;
+            position: relative;
+            top: -4px;
+            border-radius: 12px;
+            position: relative;
+            background: #fff;
+            padding-top: 2px;
+            .type-text {
+                border-radius: 12px;
+                padding: 2px;
+            }
+        }
+    }
+}
+.today {
+    position: relative;
+    // &::after {
+    //     content: "";
+    //     position: absolute;
+    //     left: 0;
+    //     right: 0;
+    //     bottom: 0;
+    //     margin: 0 auto;
+    //     width: 10px;
+    //     height: 10px;
+    //     background: url("@/assets/img/home/today.png") no-repeat center center / 100% 100%;
+    // }
+    &.no-type {
+        &::after {
+            bottom: 14px;
+        }
+    }
+}
+</style>

+ 162 - 0
src/views/old_mini/work_execute/index.js

@@ -0,0 +1,162 @@
+import * as KMap from "@/utils/ol-map/KMap";
+import * as util from "@/common/ol_common.js";
+import config from "@/api/config.js";
+import Style from "ol/style/Style";
+import Photo from "ol-ext/style/Photo";
+import { Icon, Stroke } from "ol/style.js";
+import { newPoint } from "@/utils/map";
+
+const LABEL_FONT = "bold 12px sans-serif";
+const LABEL_PADDING = [4, 10, 4, 10];
+const LABEL_RADIUS = 8;
+const LABEL_DPR = window.devicePixelRatio || 2;
+const PHOTO_RESIZE = "?imageView2/1/w/300/interlace/1";
+const DEFAULT_PHOTO =
+  "https://birdseye-img-ali-cdn.sysuimars.com/sikong/fh_sync/71892baf-42b7-4d35-a763-2a420014d901/82d4e04b-df4e-4eef-b220-bd388f5f8299/media/71de4f44-235a-4274-ad5d-e5a0ce6f2abe/DJI_20260530085247_0222_V_code-ws0fsme6cx56.jpeg" + PHOTO_RESIZE;
+
+function getFeaturePhotoSrc(feature) {
+  // const image = feature.get("beforeImage") || feature.get("afterImage") || feature.get("coverImage");
+  // if (image?.cloud_filename) {
+  //   return config.base_img_url3 + image.cloud_filename + PHOTO_RESIZE;
+  // }
+  // const src = feature.get("mapPhotoUrl") || feature.get("coverImg") || feature.get("imgUrl");
+  // if (src) {
+  //   return src.startsWith("http") ? src : config.base_img_url3 + src + PHOTO_RESIZE;
+  // }
+  return DEFAULT_PHOTO;
+}
+
+function createPillLabel(text) {
+  const [paddingTop, paddingRight, paddingBottom, paddingLeft] = LABEL_PADDING;
+  const canvas = document.createElement("canvas");
+  const ctx = canvas.getContext("2d");
+  const labelText = text || "";
+  ctx.font = LABEL_FONT;
+  const metrics = ctx.measureText(labelText);
+  const textWidth = metrics.width;
+  const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent || 12;
+  const width = Math.ceil(textWidth + paddingLeft + paddingRight);
+  const height = Math.ceil(textHeight + paddingTop + paddingBottom);
+
+  canvas.width = Math.ceil(width * LABEL_DPR);
+  canvas.height = Math.ceil(height * LABEL_DPR);
+  ctx.scale(LABEL_DPR, LABEL_DPR);
+  ctx.imageSmoothingEnabled = true;
+  ctx.imageSmoothingQuality = "high";
+
+  ctx.font = LABEL_FONT;
+  ctx.beginPath();
+  ctx.roundRect(0, 0, width, height, Math.min(LABEL_RADIUS, height / 2));
+  ctx.fillStyle = "#2199F8";
+  ctx.fill();
+
+  ctx.fillStyle = "#fff";
+  ctx.textAlign = "center";
+  ctx.textBaseline = "middle";
+  ctx.fillText(labelText, Math.round(width / 2), Math.round(height / 2));
+
+  return canvas.toDataURL();
+}
+
+/**
+ * @description 地图层对象
+ */
+class IndexMap {
+  constructor() {
+    let that = this;
+    let vectorStyle = new KMap.VectorStyle();
+    this.vectorStyle = vectorStyle;
+    this.photoStyleCache = {};
+    this.labelStyleCache = {};
+
+    this.borderStyle = new Style({
+      image: new Photo({
+        src: require("@/assets/img/map/garden-border.png"),
+        radius: 24,
+        shadow: 0,
+        crop: false,
+        onload: function () {
+          that.gardenPointLayer.layer.changed();
+        },
+        displacement: [0, -6],
+        stroke: new Stroke({
+          width: 0,
+          color: "#fdfcfc00",
+        }),
+      }),
+    });
+
+    this.gardenPointLayer = new KMap.VectorLayer("gardenPointLayer", 99, {
+      minZoom: 6,
+      maxZoom: 22,
+      style: (feature) => {
+        const photoSrc = getFeaturePhotoSrc(feature);
+        if (!this.photoStyleCache[photoSrc]) {
+          this.photoStyleCache[photoSrc] = new Style({
+            image: new Photo({
+              src: photoSrc,
+              crossOrigin: "anonymous",
+              radius: 19,
+              shadow: 0,
+              crop: true,
+              onload: function () {
+                that.gardenPointLayer.layer.changed();
+              },
+              displacement: [-1, -1],
+              stroke: new Stroke({
+                width: 2,
+                color: "#fdfcfc00",
+              }),
+            }),
+          });
+        }
+
+        const labelText = feature.get("farmName") || feature.get("mapInfo") || "";
+        if (!this.labelStyleCache[labelText]) {
+          this.labelStyleCache[labelText] = new Style({
+            image: new Icon({
+              src: createPillLabel(labelText),
+              scale: 1 / LABEL_DPR,
+              anchor: [0.5, 1],
+              displacement: [0, 32],
+            }),
+          });
+        }
+
+        return [this.photoStyleCache[photoSrc], this.borderStyle, this.labelStyleCache[labelText]];
+      },
+    });
+  }
+
+  initMap(location, target, isCapital) {
+    let level = 16;
+    let coordinate = util.wktCastGeom(location).getFirstCoordinate();
+    this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 6, 22, isCapital ? "img" : "vec");
+    let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
+    this.kmap.addXYZLayer(xyz2, { minZoom: 6, maxZoom: 22 }, 2);
+    this.kmap.addLayer(this.gardenPointLayer.layer);
+  }
+
+  initData(taskList, label, pointType = "point") {
+    this.gardenPointLayer.source.clear();
+    this.photoStyleCache = {};
+    this.labelStyleCache = {};
+    if (taskList.length > 0) {
+      for (let item of taskList) {
+        item.mapInfo = label
+          ? item[label]
+          : (item.executeDeadlineDate || item.executeDate)?.replace(/^\d{4}-(\d{2})-(\d{2})$/, "$1.$2") +
+            "  " +
+            item.farmWorkName;
+        this.gardenPointLayer.source.addFeature(newPoint(item, pointType));
+      }
+      this.kmap.getView().fit(this.gardenPointLayer.source.getExtent(), { padding: [42, 20, 30, 20] });
+      const finalZoom = this.kmap.getView().getZoom();
+      if (finalZoom > 18) {
+        this.kmap.getView().setZoom(18);
+      }
+    }
+  }
+}
+
+export default IndexMap;

+ 261 - 154
src/views/old_mini/work_execute/index.vue

@@ -3,120 +3,126 @@
         <div class="task-top">
             <div class="map-container" ref="mapContainer"></div>
         </div>
+        <div class="calendar-container">
+            <customCalendar ref="calendarRef" @dateSelect="handleDateSelect"></customCalendar>
+        </div>
         <div class="task-list">
             <div class="list-filter">
                 <div class="filter-item" :class="{ active: activeIndex === 0 }" @click="handleActiveFilter(0)">
-                    待确认({{ taskCounts[0] || 0 }})
+                    待接受({{ taskCounts[0] || 0 }})
                 </div>
                 <div class="filter-item" :class="{ active: activeIndex === 2 }" @click="handleActiveFilter(2)">
-                    已确认({{ taskCounts[2] || 0 }})
+                    已接受({{ taskCounts[2] || 0 }})
                 </div>
                 <div class="filter-item" :class="{ active: activeIndex === 3 }" @click="handleActiveFilter(3)">
-                    待提醒({{ taskCounts[3] || 0 }})
+                    执行中({{ taskCounts[3] || 0 }})
+                </div>
+                <div class="filter-item" :class="{ active: activeIndex === 4 }" @click="handleActiveFilter(3)">
+                    已完成({{ taskCounts[4] || 0 }})
                 </div>
             </div>
-            <div class="select-group">
-                <el-select
-                    class="select-item"
-                    v-model="selectParma.farmWorkTypeId"
-                    placeholder="用户类型"
-                    @change="getSimpleList"
-                >
-                    <el-option v-for="item in farmWorkTypeList" :key="item.id" :label="item.name" :value="item.id" />
-                </el-select>
-                <el-select
-                    class="select-item"
-                    v-model="selectParma.districtCode"
-                    placeholder="切换作物"
-                    @change="getSimpleList"
-                >
-                    <el-option v-for="item in districtList" :key="item.code" :label="item.name" :value="item.code" />
-                </el-select>
-            </div>
-            <!-- 任务列表 -->
-            <div class="work-task-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.3)">
-                <div class="task-item" v-for="(item, index) in taskList" :key="index" :class="isTimeoutItem(index) ? 'timeout-item' : ''" @click="handleItem(item, index)">
-                    <div class="item-title">
-                        <img class="task-icon" src="@/assets/img/home/task.png" alt="">
-                        <span class="title-text">{{ item.operation?.name }}</span>
-                        <span class="task-status" :class="`task-status--${item.operation?.type}`">{{ item.operation?.typeName }}</span>
+            <div class="list-box">
+                <div class="list-header">
+                    <div class="select-group">
+                        <el-select class="select-item" v-model="selectParma.farmWorkTypeId" placeholder="农事类型"
+                            @change="getSimpleList">
+                            <el-option v-for="item in farmWorkTypeList" :key="item.id" :label="item.name"
+                                :value="item.id" />
+                        </el-select>
+                        <el-select class="select-item" v-model="selectParma.districtCode" placeholder="农场筛选"
+                            @change="getSimpleList">
+                            <el-option v-for="item in districtList" :key="item.code" :label="item.name"
+                                :value="item.code" />
+                        </el-select>
                     </div>
-                    <span class="task-tag timeout" v-if="isTimeoutItem(index)">
-                        <el-icon><WarningFilled /></el-icon>
-                        {{ getTimeoutText() }}
-                    </span>
-                    <span class="task-tag" v-else>
-                        {{ activeStatus }}
-                    </span>
-                    <div class="item-content">
-                        <div class="item-info">
-                            <div class="info-item">
-                                <div class="info-name">负责人:</div><span class="val-text">
-                                    某某某、某某某</span>
-                            </div>
-                            <div class="info-item">
-                                <div class="info-name">农情研判:</div><span class="val-text">
-                                    {{ item.operation?.work_reason }}</span>
-                            </div>
-                            <div class="info-item" v-if="mode !== 'review'">
-                                <div class="info-name">药物处方:</div><span class="val-text">
-                                    {{ item.operation?.drug }}</span>
-                            </div>
+                    <div class="action-btn">批量接受</div>
+                </div>
+                <!-- 任务列表 -->
+                <div class="work-task-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.3)">
+                    <div class="task-item" v-for="(item, index) in taskList" :key="index"
+                        :class="isTimeoutItem(index) ? 'timeout-item' : ''" @click="handleItem(item, index)">
+                        <div class="item-title">
+                            <img class="task-icon" src="@/assets/img/home/task.png" alt="">
+                            <span class="title-text">{{ item.operation?.name }}</span>
+                            <span class="task-status" :class="`task-status--${item.operation?.type}`">{{
+                                item.operation?.typeName }}</span>
                         </div>
-                        <div class="excutor-info">
-                            <div class="excutor-info-top">
-                                <executor-header :name="getExecutorName(index)" :phone="getExecutorPhone(index)"
-                                    :avatar="getExecutorAvatar(index)" />
-                            </div>
-                            <div class="executor-stats">
-                                <div class="stat-cell">
-                                    <div class="stat-value">
-                                        {{ item.time ? formatGMTToYMD(item.time) : "--" }}
-                                    </div>
-                                    <div class="stat-label">执行时间</div>
+                        <span class="task-tag timeout" v-if="isTimeoutItem(index)">
+                            <el-icon>
+                                <WarningFilled />
+                            </el-icon>
+                            {{ getTimeoutText() }}
+                        </span>
+                        <span class="task-tag" v-else>
+                            {{ activeStatus }}
+                        </span>
+                        <div class="item-content">
+                            <div class="item-info">
+                                <div class="info-item">
+                                    <div class="info-name">负责人:</div><span class="val-text">
+                                        某某某、某某某</span>
                                 </div>
-                                <div class="cell-line"></div>
-                                <div class="stat-cell">
-                                    <div class="stat-value">{{ item.geohash_sample || "--" }}</div>
-                                    <div class="stat-label">执行区域</div>
+                                <div class="info-item" v-if="mode !== 'review'">
+                                    <div class="info-name">农事详情:</div><span class="val-text">
+                                        {{ item.operation?.drug }}</span>
                                 </div>
-                                <div class="cell-line"></div>
-                                <div class="stat-cell">
-                                    <div class="stat-value">{{ item.operation?.machine_code || "--" }}</div>
-                                    <div class="stat-label">执行农机</div>
+                                <div class="info-item">
+                                    <div class="info-name">农情研判:</div><span class="val-text">
+                                        {{ item.operation?.work_reason }}</span>
+                                </div>
+                            </div>
+                            <div class="excutor-info">
+                                <div class="executor-stats">
+                                    <div class="stat-cell">
+                                        <div class="stat-value">
+                                            {{ item.time ? formatGMTToYMD(item.time) : "--" }}
+                                        </div>
+                                        <div class="stat-label">执行时间</div>
+                                    </div>
+                                    <div class="cell-line"></div>
+                                    <div class="stat-cell">
+                                        <div class="stat-value">{{ item.geohash_sample || "--" }}</div>
+                                        <div class="stat-label">执行区域</div>
+                                    </div>
+                                    <div class="cell-line"></div>
+                                    <div class="stat-cell">
+                                        <div class="stat-value">{{ item.operation?.machine_code || "--" }}</div>
+                                        <div class="stat-label">执行农机</div>
+                                    </div>
                                 </div>
                             </div>
                         </div>
-                    </div>
 
-                    <div class="unqualified-reason" v-if="activeStatus === '未达标'">
-                        未达标原因:{{ item.operation?.reason || "未在合适时间执行,药液未充分喷撒吸收" }}
-                    </div>
-
-                    <div
-                        class="compare-imgs"
-                        v-if="activeStatus === '未达标' && (item.beforeImage || item.afterImage)"
-                        @click.stop
-                    >
-                        <div class="img-tag">前</div>
-                        <div class="img-tag right-tag">后</div>
-                        <div class="img-item" v-if="item.beforeImage">
-                            <img :src="getWorkImageUrl(item.beforeImage)" alt="" />
+                        <div class="unqualified-reason" v-if="activeStatus === '未达标'">
+                            未达标原因:{{ item.operation?.reason || "未在合适时间执行,药液未充分喷撒吸收" }}
                         </div>
-                        <div class="img-item" v-if="item.afterImage">
-                            <img :src="getWorkImageUrl(item.afterImage)" alt="" />
+
+                        <div class="compare-imgs" v-if="activeStatus === '未达标' && (item.beforeImage || item.afterImage)"
+                            @click.stop>
+                            <div class="img-tag">前</div>
+                            <div class="img-tag right-tag">后</div>
+                            <div class="img-item" v-if="item.beforeImage">
+                                <img :src="getWorkImageUrl(item.beforeImage)" alt="" />
+                            </div>
+                            <div class="img-item" v-if="item.afterImage">
+                                <img :src="getWorkImageUrl(item.afterImage)" alt="" />
+                            </div>
                         </div>
-                    </div>
 
-                    <div class="btn-group" v-if="getItemStatusButtons(index).length">
-                        <div v-for="btn in getItemStatusButtons(index)" :key="btn.label" class="edit-btn" :class="btn.type"
-                            @click.stop="handleStatusBtn(btn, item, index)">
-                            {{ btn.label }}
+                        <div class="task-footer">
+                            <div class="farm-info">来自某某农场农场</div>
+                            <div class="btn-group" v-if="getItemStatusButtons(index).length">
+                                <div v-for="btn in getItemStatusButtons(index)" :key="btn.label" class="edit-btn"
+                                    :class="btn.type" @click.stop="handleStatusBtn(btn, item, index)">
+                                    {{ btn.label }}
+                                </div>
+                            </div>
                         </div>
+
                     </div>
-                </div>
 
-                <div class="empty-tip" v-if="!loading && taskList.length === 0">暂无数据</div>
+                    <div class="empty-tip" v-if="!loading && taskList.length === 0">暂无数据</div>
+                </div>
             </div>
         </div>
     </div>
@@ -131,12 +137,12 @@
 import { computed, nextTick, onActivated, onMounted, ref, watch } from "vue";
 import { useRoute } from "vue-router";
 import { useStore } from "vuex";
-import IndexMap from "../farm_manage/map/index";
+import IndexMap from "./index";
 import { useRouter } from "vue-router";
 import { WarningFilled } from "@element-plus/icons-vue";
 import { ElMessage } from "element-plus";
-import ExecutorHeader from "./components/ExecutorHeader.vue";
 import config from "@/api/config.js";
+import customCalendar from "./components/calendar.vue";
 
 const store = useStore();
 const router = useRouter();
@@ -154,6 +160,16 @@ const taskList = ref([
     {
         time: "Fri, 22 May 2026 00:00:00 GMT",
         geohash_sample: "ws0gs49ns213",
+        farmName: "冬季清园",
+        typeName: "荔枝",
+        userType: 1,
+        address: "广东省广州市从化区太平镇凤凰路88号",
+        farmPoint: "POINT(113.61652616170711 23.58399613872042)",
+        sourceData: {
+            currentPhenologyName: "花芽分化",
+            currentPhenologyStartDate: "2026-01-15",
+            daysUntilNext: 3,
+        },
         operation: {
             name: "冬季清园",
             type: "1",
@@ -167,6 +183,16 @@ const taskList = ref([
     {
         time: "Mon, 26 May 2026 00:00:00 GMT",
         geohash_sample: "ws0gefxm68u5",
+        farmName: "施肥促花",
+        typeName: "柑橘",
+        userType: 2,
+        address: "广东省肇庆市德庆县官圩镇果园示范区",
+        farmPoint: "POINT(113.62757101477101 23.590796948574365)",
+        sourceData: {
+            currentPhenologyName: "开花期",
+            currentPhenologyStartDate: "2026-02-20",
+            daysUntilNext: 12,
+        },
         operation: {
             name: "施肥促花",
             type: "2",
@@ -180,6 +206,17 @@ const taskList = ref([
     {
         time: "Wed, 28 May 2026 00:00:00 GMT",
         geohash_sample: "ws0gefzjqqqw",
+        farmId: 1003,
+        farmName: "病虫害防治",
+        typeName: "水稻",
+        userType: 1,
+        address: "广东省清远市清城区龙塘镇农业科技园",
+        farmPoint: "POINT(113.62240816252164 23.59499176519138)",
+        sourceData: {
+            currentPhenologyName: "分蘖期",
+            currentPhenologyStartDate: "2026-03-01",
+            daysUntilNext: 8,
+        },
         operation: {
             name: "病虫害防治",
             type: "1",
@@ -208,18 +245,6 @@ const STATUS_BTN_MAP = {
     未达标: [{ label: "重新下发", type: "primary", action: "redispatch" }],
 };
 
-const nameArr = ["张扬", "王明", "詹金华"];
-const phoneArr = ["13939189356", "13800138000", "13712345678"];
-const avatarArr = [
-    "https://birdseye-img.sysuimars.com/shuichan/image.png",
-    "",
-    "",
-];
-
-const getExecutorName = (index) => nameArr[index % nameArr.length];
-const getExecutorPhone = (index) => phoneArr[index % phoneArr.length];
-const getExecutorAvatar = (index) => avatarArr[index % avatarArr.length] || "";
-
 const isTimeoutItem = (index) => index === 0;
 
 const getTimeoutRemindBtn = () => {
@@ -278,9 +303,6 @@ const handleItem = (item, index) => {
         query: {
             miniJson: JSON.stringify({
                 id: item.operation?.work_id,
-                name: getExecutorName(index),
-                phone: getExecutorPhone(index),
-                avatar: getExecutorAvatar(index),
                 status: activeStatus.value,
                 time: item.time,
                 geohash_sample: item.geohash_sample,
@@ -297,6 +319,36 @@ const handleStatusBtn = (btn, item, index) => {
         ElMessage.success("提醒成功!");
     }
 };
+
+const calendarRef = ref(null);
+const filterDate = ref(null);
+const fullTaskList = ref([]);
+
+const getCalendarTaskList = (list) => {
+    return list.map((item) => ({
+        expectedExecuteDate: item.time,
+        executeDeadlineDate: item.time,
+    }));
+};
+
+const syncCalendarData = () => {
+    nextTick(() => {
+        calendarRef.value?.setSolarTerm(getCalendarTaskList(fullTaskList.value));
+    });
+};
+
+const handleDateSelect = (date) => {
+    filterDate.value = date;
+    // if (!date) {
+    //     taskList.value = [...fullTaskList.value];
+    // } else {
+    //     taskList.value = fullTaskList.value.filter((item) => {
+    //         if (!item.time) return false;
+    //         return formatGMTToYMD(item.time) === date;
+    //     });
+    // }
+    syncCalendarData();
+};
 // 各状态任务数量
 const taskCounts = ref([0, 0, 0]);
 // 当前选中的筛选索引
@@ -318,16 +370,6 @@ const getFutureFarmWorkWarning = async (item) => {
 const cityCode = ref("");
 //根据城市的坐标返回区县列表
 const districtList = ref([]);
-function getDistrictListByCity() {
-    VE_API.z_farm_work_record.getDistrictListByCity({ point: mapPoint.value }).then(({ data }) => {
-        districtList.value = data || [];
-        // cityCode.value = data[0].code.slice(0, -2);
-        cityCode.value = "";
-        districtList.value.unshift({ code: cityCode.value, name: "全部" });
-        selectParma.value.districtCode = cityCode.value;
-        resetAndLoad();
-    });
-}
 
 //农事类型列表
 const farmWorkTypeList = ref([]);
@@ -342,11 +384,16 @@ const mapPoint = ref(null);
 
 onMounted(() => {
     mapPoint.value = store.state.home.miniUserLocationPoint;
+    fullTaskList.value = [...taskList.value];
     // getDistrictListByCity();
     resetAndLoad();
     getFarmWorkTypeList();
+    syncCalendarData();
     nextTick(() => {
         indexMap.initMap(mapPoint.value, mapContainer.value, true);
+        
+        // 更新地图数据
+        indexMap.initData(taskList.value, '', 'farmPoint');
     });
 });
 
@@ -448,17 +495,17 @@ async function getSimpleList(isLoadMore = false) {
             // 追加数据
             // const newTaskList = [...taskList.value, ...newItems];
             // taskList.value = newTaskList.filter(item => item.timelineList.length > 0);
-            
+
             // 更新分页
             page.value += 1;
-            
+
             // 判断是否还有更多数据
             if (data.length < limit.value) {
                 finished.value = true;
             }
 
             // 更新地图数据
-            indexMap.initData(taskList.value,'','farmPoint');
+            indexMap.initData(taskList.value, '', 'farmPoint');
         } else {
             finished.value = true;
             if (taskList.value.length === 0) {
@@ -504,6 +551,10 @@ function handleActiveFilter(i) {
     selectParma.value.farmWorkTypeId = null;
     const statusMap = { 0: "待接受", 2: "已接受", 3: "执行中" };
     activeStatus.value = statusMap[i] || "待接受";
+    filterDate.value = null;
+    taskList.value = [...fullTaskList.value];
+    calendarRef.value?.clearSelection();
+    syncCalendarData();
 }
 
 function handleRemindCustomer(item) {
@@ -514,21 +565,48 @@ function handleRemindCustomer(item) {
 <style lang="scss" scoped>
 .task-page {
     width: 100%;
-    height: calc(100vh - 50px - 50px);
+    height: calc(100vh - 50px);
     overflow: auto;
     box-sizing: border-box;
-    background: #f5f7fb;
+    background: #FFFFFF;
+
     .map-container {
         width: 100%;
         height: 162px;
         clip-path: inset(0px round 8px);
     }
 
+    .list-box {
+        background: #F1F3F4;
+        padding: 12px 12px 8px 12px;
+
+        .list-header {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+
+            .action-btn {
+                box-sizing: border-box;
+                height: 32px;
+                line-height: 30px;
+                color: #2199F8;
+                font-size: 14px;
+                border-radius: 5px;
+                padding: 0 10px;
+                background: #FFFFFF;
+                border: 1px solid #2199F8;
+                color: #2199F8;
+            }
+        }
+    }
+
     .select-group {
         display: flex;
         padding: 0px 12px 0 12px;
+
         .select-item {
             width: 100%;
+
             ::v-deep {
                 .el-select__wrapper {
                     text-align: center;
@@ -537,16 +615,19 @@ function handleRemindCustomer(item) {
                     justify-content: center;
                     background: none;
                 }
+
                 .el-select__selection {
                     flex: none;
                     width: fit-content;
                 }
+
                 .el-select__placeholder {
                     position: static;
                     transform: none;
                     width: fit-content;
                     color: rgba(0, 0, 0, 0.2);
                 }
+
                 .el-select__caret {
                     color: rgba(0, 0, 0, 0.2);
                 }
@@ -554,6 +635,10 @@ function handleRemindCustomer(item) {
         }
     }
 
+    .calendar-container {
+        padding: 4px 12px 8px 12px;
+    }
+
     .task-top {
         padding: 10px 12px 0 12px;
     }
@@ -577,28 +662,42 @@ function handleRemindCustomer(item) {
         color: #6f7274;
         padding: 20px 0;
     }
+
     .task-list {
         position: relative;
         background: #fff;
-        padding: 12px 12px 8px 12px;
     }
+
     .list-filter {
         display: flex;
         align-items: center;
         justify-content: space-around;
+        margin-top: 10px;
+
         .filter-item {
             padding: 0 12px;
-            height: 28px;
-            color: rgba(0, 0, 0, 0.5);
+            height: 32px;
+            color: #1D2129;
             font-size: 14px;
-            line-height: 28px;
             border-radius: 20px;
+            position: relative;
+
             &.active {
                 color: #2199f8;
-                background: rgba(33, 153, 248, 0.2);
+
+                &::after {
+                    content: "";
+                    position: absolute;
+                    bottom: 0;
+                    left: 0;
+                    width: 100%;
+                    height: 2px;
+                    background: #2199f8;
+                }
             }
         }
     }
+
     .work-task-list {
         margin-top: 10px;
 
@@ -634,7 +733,7 @@ function handleRemindCustomer(item) {
                     margin-left: 8px;
                     border-radius: 2px;
                     height: 20px;
-                    line-height: 18px;
+                    line-height: 20px;
                     padding: 1px 6px;
                     box-sizing: border-box;
                     font-size: 12px;
@@ -645,10 +744,12 @@ function handleRemindCustomer(item) {
                         background: rgba(33, 153, 248, 0.1);
                         color: #2199f8;
                     }
+
                     &.task-status--2 {
                         background: rgba(255, 179, 47, 0.1);
                         color: #FFB32F;
                     }
+
                     &.task-status--3 {
                         background: rgba(58, 173, 148, 0.1);
                         color: #3AAD94;
@@ -660,17 +761,17 @@ function handleRemindCustomer(item) {
                 position: absolute;
                 right: 0;
                 top: 0;
-                background: rgba(33, 153, 248, 0.1);
-                color: #2199f8;
+                background: rgba(255, 149, 61, 0.1);
+                color: #F46E00;
                 font-size: 12px;
-                padding: 0 6px;
+                padding: 0 10px;
                 height: 25px;
                 line-height: 25px;
                 border-radius: 0 8px 0 8px;
 
                 &.timeout {
-                    background: #ff4747;
-                    color: #fff;
+                    background: rgba(255, 106, 106, 0.1);
+                    color: #FF6A6A;
                     display: flex;
                     align-items: center;
                     gap: 2px;
@@ -707,22 +808,23 @@ function handleRemindCustomer(item) {
                 .stat-value {
                     font-size: 14px;
                     font-weight: 500;
-                    color: #1d2129;
+                    color: #0B0B0B;
                     margin-bottom: 4px;
                     word-break: break-all;
                 }
 
                 .stat-label {
-                    font-size: 10px;
+                    font-size: 12px;
                     line-height: 20px;
-                    color: #86909c;
+                    color: rgba(107, 107, 107, 0.5);
+                    text-align: center;
                 }
             }
 
             .item-info {
-                color: #86909c;
-                font-size: 12px;
-                line-height: 18px;
+                color: rgba(111, 114, 116, 0.6);
+                font-size: 14px;
+                line-height: 21px;
 
                 .info-item {
                     display: flex;
@@ -733,11 +835,22 @@ function handleRemindCustomer(item) {
                 }
 
                 .val-text {
-                    color: #1d2129;
+                    // color: #1d2129;
                 }
 
-                .info-item + .info-item {
-                    padding-top: 12px;
+                .info-item+.info-item {
+                    padding-top: 6px;
+                }
+            }
+
+            .task-footer {
+                padding-top: 4px;
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                .farm-info {
+                    font-size: 14px;
+                    color: rgba(32, 32, 32, 0.4);
                 }
             }
 
@@ -745,30 +858,24 @@ function handleRemindCustomer(item) {
                 display: flex;
                 align-items: center;
                 justify-content: end;
-                width: 100%;
                 gap: 10px;
             }
 
             .edit-btn {
-                flex: 1;
-                width: 100%;
                 height: 30px;
-                border-radius: 4px;
+                padding: 0 16px;
+                border-radius: 24px;
                 text-align: center;
                 line-height: 30px;
                 font-size: 14px;
-                border: 0.5px solid transparent;
 
                 &.normal {
-                    flex: none;
-                    width: 131px;
-                    background: #fff;
-                    border: 0.5px solid rgba(0, 0, 0, 0.2);
-                    color: #1d2129;
+                    background: #F6F6F6;
+                    color: #000000;
                 }
 
                 &.primary {
-                    background: #2199f8;
+                    background: #FF953D;
                     color: #fff;
                 }
             }