Browse Source

feat: 增加果园日志

刘秀芳 4 weeks ago
parent
commit
70e85fba39
61 changed files with 6824 additions and 212 deletions
  1. 1 0
      package.json
  2. 76 0
      src/api/modules/farm.js
  3. 21 0
      src/api/modules/order.js
  4. BIN
      src/assets/img/gallery/capital.png
  5. BIN
      src/assets/img/gallery/checked.png
  6. BIN
      src/assets/img/gallery/comment-1-active.png
  7. BIN
      src/assets/img/gallery/comment-1.png
  8. BIN
      src/assets/img/gallery/comment-2-active.png
  9. BIN
      src/assets/img/gallery/comment-2.png
  10. BIN
      src/assets/img/gallery/comment-3-active.png
  11. BIN
      src/assets/img/gallery/comment-3.png
  12. BIN
      src/assets/img/gallery/current.png
  13. BIN
      src/assets/img/gallery/done.png
  14. BIN
      src/assets/img/gallery/edit.png
  15. BIN
      src/assets/img/gallery/enter.png
  16. BIN
      src/assets/img/gallery/expert-icon.png
  17. BIN
      src/assets/img/gallery/farm-img.png
  18. BIN
      src/assets/img/gallery/img-icon.png
  19. BIN
      src/assets/img/gallery/item-name.png
  20. BIN
      src/assets/img/gallery/log-bg.png
  21. BIN
      src/assets/img/gallery/message-icon.png
  22. BIN
      src/assets/img/gallery/serve-img.png
  23. BIN
      src/assets/img/gallery/star.png
  24. BIN
      src/assets/img/gallery/step-0.png
  25. BIN
      src/assets/img/gallery/step-1.png
  26. BIN
      src/assets/img/gallery/step-2.png
  27. BIN
      src/assets/img/gallery/step-3.png
  28. BIN
      src/assets/img/gallery/step-4.png
  29. BIN
      src/assets/img/gallery/time.png
  30. BIN
      src/assets/img/gallery/todo.png
  31. BIN
      src/assets/img/gallery/warning.png
  32. 149 0
      src/components/common/stepBox.vue
  33. 10 2
      src/components/fnHeader.vue
  34. 9 1
      src/main.js
  35. 7 1
      src/router/mainRoutes.js
  36. 78 0
      src/views/home/album/album_compoents/albumCarousel.vue
  37. 107 0
      src/views/home/album/album_compoents/albumCarousel7d.vue
  38. 202 0
      src/views/home/album/album_compoents/albumCarouselItem.vue
  39. 287 0
      src/views/home/album/album_compoents/albumDrawBox.vue
  40. 75 0
      src/views/home/album/album_compoents/cacheImg.js
  41. 280 0
      src/views/home/album/album_compoents/detailDailog.vue
  42. 256 0
      src/views/home/album/album_compoents/utils.js
  43. 83 0
      src/views/home/album/components/chart.vue
  44. 270 0
      src/views/home/album/components/chartSeting.js
  45. 130 0
      src/views/home/album/components/indicatorChart.vue
  46. 1152 0
      src/views/home/album/index.vue
  47. 12 3
      src/views/home/index.vue
  48. 40 0
      src/views/workDetail/areaMap.js
  49. 630 0
      src/views/workDetail/components/boxClass.scss
  50. 190 0
      src/views/workDetail/components/chart.vue
  51. 227 0
      src/views/workDetail/components/chartSeting.js
  52. 173 0
      src/views/workDetail/components/commentBox.vue
  53. 174 0
      src/views/workDetail/components/enterBox.vue
  54. 103 0
      src/views/workDetail/components/executeBlueRegion.vue
  55. 180 0
      src/views/workDetail/components/executionProgress.vue
  56. 596 0
      src/views/workDetail/components/prescriptionBox.vue
  57. 263 0
      src/views/workDetail/components/recheckBox.vue
  58. 468 0
      src/views/workDetail/components/serviceBox.vue
  59. 104 0
      src/views/workDetail/components/serviceDialog.vue
  60. 94 0
      src/views/workDetail/index.vue
  61. 377 205
      yarn.lock

+ 1 - 0
package.json

@@ -50,6 +50,7 @@
     "vuedraggable": "^2.24.3",
     "vueshowpdf": "^1.1.2",
     "vuex": "^4.0.2",
+    "vue3-photo-preview": "^0.3.0",
     "xe-utils": "^3.5.4",
     "yarn": "^1.22.22",
     "zdog": "^1.1.3"

+ 76 - 0
src/api/modules/farm.js

@@ -0,0 +1,76 @@
+const config = require("../config")
+
+module.exports = {
+    list: {
+        url: config.base_dev_url + "farm/list?key="+config.mini_key,
+        type: "get",
+    },
+    weatherRiskVirtualFarmList: {
+        url: config.base_dev_url + "farm/weatherRiskVirtualFarmList?key="+config.mini_key,
+        type: "get",
+    },
+    getTimeLine: {
+        url: config.base_dev_url + "fairyland/getNodesByFarmId?key="+config.mini_key,
+        type: "get",
+    },
+    getReportData: {
+        url: config.base_dev_url + "fairyland/predict?key="+config.mini_key,
+        type: "post",
+    },
+    getCataclysmTimeLine: {
+        url: config.base_dev_url + "fairyland/getDisasterNodesByFarmId?key="+config.mini_key,
+        type: "get",
+    },
+    getCataclysmReportData: {
+        url: config.base_dev_url + "fairyland/disasterPredict?key="+config.mini_key,
+        type: "post",
+    },
+    getDisasterJudgment: {
+        url: config.base_dev_url + "fairyland/disasterJudgment?key="+config.mini_key,
+        type: "post",
+    },
+    fetchFarmDetail: {
+        url: config.base_dev_url + "farm/getById?key="+config.mini_key,
+        type: "get",
+    },
+    fetchFarmWorkList: {
+        url: config.base_dev_url + "farm/farm_work_list?key="+config.mini_key,
+        type: "get",
+    },
+    fetchFarmImgs: {
+        url: config.base_dev_url + "image/list?key="+config.mini_key,
+        type: "post",
+    },
+    getLiveStart: {
+        url: config.base_dev_url + "farm/liveStart?key="+config.mini_key,
+        type: "get",
+    },
+    getUserNewFarm: {
+        url: config.base_dev_url + "farm/getUserNewFarm?key="+config.mini_key,
+        type: "get",
+    },
+    fetchFarmLog: {
+        url: config.base_dev_url + "farm/vrOrchardContent?key="+config.mini_key,
+        type: "post",
+    },
+    fetchExpertFarmList: {
+        url: config.base_dev_url + "t_expert/expertFarmList?key="+config.mini_key,
+        type: "get",
+    },
+    pesticideFertilizers:{
+        url: config.base_dev_url + "z_farm_work_order/pesticideFertilizers?key="+config.mini_key,
+        type: "get",
+    },
+    fetchWorkList: {
+        url: config.base_dev_url + "z_farm_work_order/list?key="+config.mini_key,
+        type: "get",
+    },
+    blueRegionList: {
+        url: config.base_dev_url + "farm/blueRegionList?key="+config.mini_key,
+        type: "get",
+    },
+    confirm:{
+        url: config.base_dev_url + "z_farm_work_order/confirm?key="+config.mini_key,
+        type: "post",
+    },
+}

+ 21 - 0
src/api/modules/order.js

@@ -0,0 +1,21 @@
+const config = require("../config")
+
+module.exports = {
+    fetchWorkList: {
+        url: config.base_dev_url + "z_farm_work_order/list?key="+config.mini_key,
+        type: "get",
+    },
+    pesticideFertilizers:{
+        url: config.base_dev_url + "z_farm_work_order/pesticideFertilizers?key="+config.mini_key,
+        type: "get",
+    },
+    confirm:{
+        url: config.base_dev_url + "z_farm_work_order/confirm?key="+config.mini_key,
+        type: "post",
+    },
+    fetchFarmerJoin:{
+        url: config.base_dev_url + "z_farm_work_order/farmerJoin?key="+config.mini_key,
+        type: "get",
+    },
+
+}

BIN
src/assets/img/gallery/capital.png


BIN
src/assets/img/gallery/checked.png


BIN
src/assets/img/gallery/comment-1-active.png


BIN
src/assets/img/gallery/comment-1.png


BIN
src/assets/img/gallery/comment-2-active.png


BIN
src/assets/img/gallery/comment-2.png


BIN
src/assets/img/gallery/comment-3-active.png


BIN
src/assets/img/gallery/comment-3.png


BIN
src/assets/img/gallery/current.png


BIN
src/assets/img/gallery/done.png


BIN
src/assets/img/gallery/edit.png


BIN
src/assets/img/gallery/enter.png


BIN
src/assets/img/gallery/expert-icon.png


BIN
src/assets/img/gallery/farm-img.png


BIN
src/assets/img/gallery/img-icon.png


BIN
src/assets/img/gallery/item-name.png


BIN
src/assets/img/gallery/log-bg.png


BIN
src/assets/img/gallery/message-icon.png


BIN
src/assets/img/gallery/serve-img.png


BIN
src/assets/img/gallery/star.png


BIN
src/assets/img/gallery/step-0.png


BIN
src/assets/img/gallery/step-1.png


BIN
src/assets/img/gallery/step-2.png


BIN
src/assets/img/gallery/step-3.png


BIN
src/assets/img/gallery/step-4.png


BIN
src/assets/img/gallery/time.png


BIN
src/assets/img/gallery/todo.png


BIN
src/assets/img/gallery/warning.png


+ 149 - 0
src/components/common/stepBox.vue

@@ -0,0 +1,149 @@
+<template>
+    <div class="step-content" :class="isDone ? `step-4` : `step-${card.orderStatus ? card.orderStatus : card.orderStatus === 0 ? card.orderStatus : card.status}`">
+        <div class="step-text t-r">专家</div>
+        <div class="step-text b-l">农服</div>
+        <div class="step-text b-r">农资</div>
+        <!-- <div class="step-time time-t-l" v-if="card.updateDate0">
+                {{ formatDate(card.updateDate0) }}
+            </div>
+            <div class="step-time time-b-r" v-if="card.updateDate1">
+                {{ formatDate(card.updateDate1) }}
+            </div>
+            <div class="step-time time-b-l" v-if="card.updateDate4">
+                {{ formatDate(card.updateDate4) }}
+            </div> -->
+        <img class="step-img" :src="require(`@/assets/img/gallery/step-${isDone ? 4 : (card.orderStatus ? card.orderStatus : card.orderStatus === 0 ? card.orderStatus : card.status)}.png`)" />
+    </div>
+</template>
+
+<script setup>
+const props = defineProps({
+    card: {},
+    isDone: {
+        type: Boolean,
+        default: false
+    }
+})
+
+// 正则表达式 将日期 YYYY-MM-DD 格式的字符串转换为 MM-DD
+const formatDate = (dateStr) => {
+    return dateStr.replace(/^\d{4}-(\d{2}-\d{2})$/, "$1");
+};
+</script>
+
+<style lang="scss" scoped>
+.step-content {
+    position: relative;
+    width: 100px;
+    height: 58px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    &.step-0 {
+        .t-r {
+            color: #FFD489;
+        }
+    }
+    &.step-1 {
+        .t-r {
+            color: #FFD489;
+        }
+        .b-r {
+            color: #FFD489;
+        }
+    }
+    &.step-2 {
+        .t-r {
+            color: #FFD489;
+        }
+        .b-r {
+            color: #FFD489;
+        }
+        .b-l {
+            color: #FFD489;
+        }
+    }
+    &.step-3 {
+        .t-r {
+            color: #FFD489;
+        }
+        .b-r {
+            color: #FFD489;
+        }
+        .b-l {
+            color: #FFD489;
+        }
+        .t-l {
+            color: #FFD489;
+        }
+    }
+    &.step-4 {
+        .t-r {
+            color: #ffb132;
+        }
+        .b-r {
+            color: #ffb132;
+        }
+        .b-l {
+            color: #ffb132;
+        }
+        .t-l {
+            color: #ffb132;
+        }
+    }
+    .step-text {
+        position: absolute;
+        font-size: 12px;
+        color: #bbbbbb;
+    }
+    .t-l {
+        top: 0;
+        left: 0;
+    }
+    .t-r {
+        left: 50%;
+        transform: translateX(-50%);
+        top: -10px;
+    }
+    .b-l {
+        bottom: 8px;
+        left: 4px;
+    }
+    .b-r {
+        bottom: 8px;
+        right: 2px;
+    }
+
+    .step-time {
+        position: absolute;
+        font-size: 10px;
+        color: rgba(0, 0, 0, 0.2);
+        &.time-t-l {
+            left: 0;
+            right: 0;
+            top: -12px;
+            margin: 0 auto;
+            text-align: center;
+        }
+        &.time-b-l {
+            top: 0;
+            bottom: 0;
+            margin: 0 auto;
+            left: 0;
+            display: flex;
+            align-items: center;
+        }
+        &.time-b-r {
+            top: 0;
+            bottom: 0;
+            margin: 0 auto;
+            right: 0;
+            display: flex;
+            align-items: center;
+        }
+    }
+    .step-img {
+        width: 42px;
+    }
+}
+</style>

+ 10 - 2
src/components/fnHeader.vue

@@ -5,7 +5,7 @@
       <span>飞鸟智慧巡园平台</span>
       <img class="logo-icon" src="@/assets/images/common/logo-icon.png" alt="" />
     </div>
-    <div class="focus-farm">
+    <div class="focus-farm" v-show="!hideSwitch">
         <el-select
             v-model="farmVal"
             placeholder="我的关注农场"
@@ -26,7 +26,7 @@
     </div>
   </div>
   <!-- 四个方向的阴影 -->
-  <div class="page-shadow">
+  <div class="page-shadow" v-show="!hideShadow">
     <div class="page-bg bg-top"></div>
     <div class="page-bg bg-right"></div>
     <div class="page-bg bg-bottom"></div>
@@ -44,6 +44,14 @@ const props = defineProps({
     type:Boolean,
     defalut:false
   },
+  hideSwitch:{
+    type:Boolean,
+    defalut:false
+  },
+  hideShadow: {
+    type:Boolean,
+    defalut:false
+  }
 })
 
 const farmVal = ref(null)

+ 9 - 1
src/main.js

@@ -23,6 +23,8 @@ import router from "@/router";
 import 'video.js/dist/video-js.css'
 import '@/common/rem'
 import Print from 'vue3-print-nb'
+import vue3PhotoPreview from 'vue3-photo-preview'
+import 'vue3-photo-preview/dist/index.css'
 
 const app = createApp(App);
 app.use(mock)
@@ -33,7 +35,13 @@ app.use(mock)
     .use(axios, { router, store, opt: "VE_API" })
     .use(permission, { router, store })
     .use(directives, { router, store })
-    .use(Print);
+    .use(Print)
+    .use(vue3PhotoPreview, {
+        // 配置图片预览的参数
+        fullscreen: true, // 是否启用全屏
+        download: false, // 是否启用下载
+        rotate: false, // 是否启用旋转
+      });
 
 
 app.mount("#app");

+ 7 - 1
src/router/mainRoutes.js

@@ -63,5 +63,11 @@ export default [
         path: "/weatherDetails",
         name: "WeatherDetails",
         component: () => import("@/views/detailsPage/weather/index.vue"),
-    }
+    },
+    // 农事
+    {
+        path: "/work_detail",
+        name: "WorkDetail",
+        component: () => import("@/views/workDetail/index.vue"),
+    },
 ];

+ 78 - 0
src/views/home/album/album_compoents/albumCarousel.vue

@@ -0,0 +1,78 @@
+<template>
+    <album-carousel-item v-if="images" :temp="temp" :sampleId="sampleId" :farmId="farmId" :images="images" :lock="lock"></album-carousel-item>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onUnmounted, watch } from "vue";
+import "./cacheImg.js"
+import AlbumCarouselItem from "./albumCarouselItem";
+import {dateFormat} from "@/utils/date_util.js"
+import eventBus from "@/api/eventBus.js";
+
+const props =defineProps({
+  sampleId:{
+    type: [Number, String],
+    required: false
+  },
+  farmId:{
+    type: [Number, String],
+    required: true
+  },
+  blueGeoHash:{
+    type: String,
+    required: false
+  },
+  temp:{
+    type: String,
+    required: false
+  },
+  farmWork:{
+    type: Object,
+    required: false
+  },
+  lock:{
+    type: Boolean,
+    default: true
+  }
+})
+const images = ref(null);
+onMounted(() => {
+  getList()
+});
+
+watch(()=>props.farmId,(newValue,oldValue) =>{
+  if(newValue){
+    getList()
+  }
+})
+
+const getList = () =>{
+  if(!props.farmId) return
+  let params = {farmId: props.farmId}
+  if(props.blueGeoHash){
+    params.blueZone = props.blueGeoHash
+  }
+  if(props.sampleId){
+    params.sampleId = props.sampleId
+  }
+  if(props.farmWork?.executeDate){
+    let execcuteDate = new Date(props.farmWork.executeDate)
+    let beforeExecuteDate = new Date(props.farmWork.beforeExecuteDate)
+    const pastDate = new Date(beforeExecuteDate);
+    const futureDate = new Date(execcuteDate);
+    params.startDate = dateFormat(pastDate, "YY-mm-dd");
+    params.endDate = dateFormat(futureDate, "YY-mm-dd");
+  }
+  VE_API.image.list(params).then(res => {
+  if(res.code === 0){
+      images.value = res.data
+      eventBus.emit('image:resultNum',res.data.length)
+    }
+  })
+}
+
+</script>
+
+<style lang="scss" scoped>
+@import "src/styles/index";
+</style>

+ 107 - 0
src/views/home/album/album_compoents/albumCarousel7d.vue

@@ -0,0 +1,107 @@
+<template>
+  <template v-for="(images,index) in imagesList" :key="index">
+    <album-carousel-item :farmId="farmId" :images="images"></album-carousel-item>
+    <div style="height: 5px"></div>
+  </template>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onUnmounted } from "vue";
+import AlbumDrawBoxItem from "./albumCarouselItem";
+import "./cacheImg.js"
+import AlbumCarouselItem from "./albumCarouselItem";
+import {dateFormat} from "@/utils/date_util.js"
+import eventBus from "@/api/eventBus";
+
+const props =defineProps({
+  // sampleId:{
+  //   type: [Number, String],
+  //   required: false
+  // },
+  farmId:{
+    type: [Number, String],
+    required: true
+  },
+  farmWork:{
+    type: Object,
+    required: false
+  }
+})
+const imagesList = ref([]);
+let params = {farmId: props.farmId}
+
+onMounted(() => {
+  if(props.farmWork?.executeDate){
+    let execcuteDate = new Date(props.farmWork.executeDate)
+    let beforeExecuteDate = new Date(props.farmWork.beforeExecuteDate)
+    const pastDate = new Date(beforeExecuteDate);
+    const futureDate = new Date(execcuteDate);
+    params.startDate = dateFormat(pastDate, "YY-mm-dd");
+    params.endDate = dateFormat(futureDate, "YY-mm-dd");
+  }
+  // getImageList(params)
+});
+
+const updateData = () =>{
+  params.farmId = props.farmId
+  getImageList(params)
+}
+
+onUnmounted(()=>{
+  eventBus.off("chart:updateOption",updateData)
+})
+
+const getImageList = (params) =>{
+  VE_API.image.list(params).then(res => {
+    if(res.code === 0){
+      let result = splitByWeek(res.data, params.startDate, params.endDate);
+      if(result && result.length > 0){
+        result = result.reverse()
+      }
+      for(let i=0;result != null && i<result.length;i++){
+        if (result[i] && result[i].length > 0){
+          imagesList.value.push(result[i])
+        }
+      }
+    }
+  })
+}
+
+eventBus.on("chart:updateOption",updateData)
+
+function splitByWeek(items, startDate, endDate) {
+  // 将开始时间和结束时间转换为时间戳
+  const start = new Date(startDate).getTime();
+  const end = new Date(endDate).getTime();
+
+  // 创建一个用于存储按周分组的结果
+  const weeklyGroups = [];
+
+  // 遍历每一项
+  items.forEach(item => {
+    const uploadDate = new Date(item.uploadDate).getTime();
+
+    // 确保上传日期在开始日期和结束日期之间
+    if (uploadDate >= start && uploadDate <= end) {
+      // 计算上传日期属于第几周
+      const weekIndex = Math.floor((uploadDate - start) / (7 * 24 * 60 * 60 * 1000));
+
+      // 如果该周的数组不存在,则创建一个
+      if (!weeklyGroups[weekIndex]) {
+        weeklyGroups[weekIndex] = [];
+      }
+
+      // 将当前项添加到对应的周数组中
+      weeklyGroups[weekIndex].push(item);
+    }
+  });
+
+  return weeklyGroups;
+}
+
+
+</script>
+
+<style lang="scss" scoped>
+@import "src/styles/index";
+</style>

+ 202 - 0
src/views/home/album/album_compoents/albumCarouselItem.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="carousel-container">
+    <!-- 图片列表 -->
+    <div class="carousel-wrapper"  :style="carouselStyle">
+      <photo-provider v-if="images" :photo-closable="true" @visibleChange="handleVisibleChange">
+        <template  v-for="(photo, index) in images"
+                   :key="photo.id">
+          <album-draw-box :temp="temp"  :farmId="farmId" :photo="photo" :current="currentIndex" :index="index" :length="images.length"
+          ></album-draw-box>
+        </template>
+      </photo-provider>
+    </div>
+    <div class="blur-bg" v-if="lock && currentIndex !== 0">
+      <div class="blur-content">
+        <div class="blur-img">
+          <img src="@/assets/img/gallery/camera-icon.png" />
+        </div>
+      </div>
+    </div>
+
+    <!-- 左右箭头 -->
+    <div @click.stop="prev" v-if="currentIndex !== 0" class="arrow left-arrow">
+      <el-icon color="#F0D09C"><ArrowLeftBold /></el-icon>
+    </div>
+    <div
+      @click.stop="next"
+      v-if="images && currentIndex !== images.length - 1"
+      class="arrow right-arrow"
+    >
+      <el-icon color="#F0D09C"><ArrowRightBold /></el-icon>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { toRefs, ref, computed, onMounted, onUnmounted } from "vue";
+import AlbumDrawBox from "./albumDrawBox";
+import "./cacheImg.js"
+const props =defineProps({
+  images:{
+    type: Array,
+    required: true
+  },
+  farmId:{
+    type: [Number,String],
+    required: true
+  },
+  lock:{
+    type: Boolean,
+    default: true
+  }
+  ,temp:{
+    type: String,
+    required: false
+  },
+})
+const {images} = toRefs(props);
+let timer = null;
+const currentIndex = ref(0);
+
+onMounted(() => {
+  updateImagePosition();
+  clearAndRestartTimer();
+});
+onUnmounted(() => {
+  clearInterval(timer);
+});
+
+const updateImagePosition = () => {
+  carouselStyle.value.transform = `translateX(-${currentIndex.value * 100}%)`;
+};
+
+const clickPhotoShow = () => {
+  if (timer) {
+    clearInterval(timer)
+  };
+}
+
+// 图片显隐切换回调
+const handleVisibleChange = ({visible}) => {
+  if (visible.value) {
+    if (timer) {
+      clearInterval(timer)
+    }
+  } else {
+    clearAndRestartTimer();
+  }
+}
+
+// 计算轮播图样式
+const carouselStyle = computed(() => {
+  return {
+    transform: `translateX(-${currentIndex.value * 100}%)`,
+  };
+});
+
+// 下一张图片
+const next = () => {
+  // 图片总数
+  const totalImages = images.value.length;
+  currentIndex.value = (currentIndex.value + 1) % totalImages;
+  updateImagePosition();
+  clearAndRestartTimer();
+};
+
+// 上一张图片
+const prev = () => {
+  // 图片总数
+  const totalImages = images.value.length;
+  currentIndex.value = (currentIndex.value - 1 + totalImages) % totalImages;
+  updateImagePosition();
+  clearAndRestartTimer();
+};
+
+const clearAndRestartTimer = () => {
+  if (timer) {
+    clearInterval(timer);
+  }
+  // timer = setInterval(next, 5000);
+};
+
+
+</script>
+
+<style lang="scss" scoped>
+@import "src/styles/index";
+.carousel-container {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+  margin: 0 auto;
+  .carousel-wrapper {
+    display: flex;
+    transition: transform 0.5s ease;
+    width: 100%;
+  }
+  .blur-bg {
+    position: absolute;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    backdrop-filter: blur(1.4px);
+    .blur-content {
+      border-radius: 8px;
+      background: rgba(0, 0, 0, 0.5);
+      width: 100%;
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+      color: #fff;
+      .blur-img {
+        img {
+          width: 54px;
+          position: relative;
+          left: 4px;
+          top: 4px;
+        }
+      }
+      .blur-text {
+        padding: 8px 0;
+        text-align: center;
+        line-height: 1.5;
+      }
+      .blur-btn {
+        padding: 0 40px;
+        box-shadow: 0 -2px 2px #86C9FF;
+        height: 28px;
+        line-height: 28px;
+        border-radius: 50px;
+        background: rgba(33, 153, 248, 0.7);
+        // background: linear-gradient(#86C9FF, rgba(255, 255, 255, 0));
+      }
+    }
+  }
+
+  .arrow {
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    background: rgba(0, 0, 0, 0.5);
+    width: rpx(72);
+    height: rpx(72);
+    border-radius: 50%;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    pointer-events: all;
+  }
+
+  .left-arrow {
+    left: rpx(32);
+  }
+
+  .right-arrow {
+    right: rpx(32);
+  }
+}
+</style>

+ 287 - 0
src/views/home/album/album_compoents/albumDrawBox.vue

@@ -0,0 +1,287 @@
+<template>
+  <photo-consumer
+      class="carousel-item"
+      :src="watermark || base_img_url2 + (photo.resFilename ? photo.resFilename : photo.filename) + resize"
+  >
+    <img v-if="Math.abs(current - index) < 3" crossorigin="anonymous" @load="drawWatermark($event)" loading="lazy" :src="watermark || (base_img_url2 + (photo.resFilename ? photo.resFilename : photo.filename) + resize)" style="width: 100%;" />
+    <canvas  ref="canvasRef" style="position: absolute;"></canvas>
+    <div class="tag-text" v-if="showTagBox" >
+      <span v-html="photo.growText"></span>
+      <button class="close-button" @click="hideTagBox">✖</button>
+    </div>
+    <div class="tag-box right" :class="{'leftTop': 'leftTop'}">{{ index+1 }}/{{ length }}</div>
+<!--    <div class="center-mark">mark</div>-->
+  </photo-consumer>
+
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, defineProps } from "vue";
+import { base_img_url2 } from "@/api/config";
+import {imageCache,loadImage} from "./cacheImg.js"
+import {dateFormat} from "@/utils/date_util.js"
+import {drawTextInRect, drawBorderImageInRect, drawImageInRect, drawRectInRect, drawHorizontalTextList} from "./utils"
+const resize = "?x-oss-process=image/resize,p_30/format,webp/quality,q_40";
+
+const canvasRef = ref(null);
+const watermark = ref(null)
+const baseMapBig = ref(false)
+
+const props = defineProps({
+  farmId:{
+    type: [Number,String],
+    required: true
+  },
+  photo:{
+    type: Object,
+    required: true
+  },
+  index:{
+    type: Number,
+    required: true
+  },
+  length:{
+    type: Number,
+    required: true
+  },
+  current:{
+    type: Number,
+    required: true
+  }
+  ,temp:{
+    type: String,
+    required: false
+  },
+})
+let img = null;
+let ctx = null;
+let data = {year:props.photo.uploadDate.substring(0,4),
+  monthDay:dateFormat(new Date(props.photo.uploadDate),'mm/dd'),
+  address:props.photo.district.replaceAll("\"","") + props.photo.gardenName,
+  tempImg:imageCache.get("temp"),temp:"10°C-20°C",wendu:"适宜",
+  feiniao:imageCache.get("feiniao"),
+  baseMap:imageCache.get("base_map_"+props.photo.treeId),
+  fusheImg:imageCache.get("fushe"),fushe:"光照优",
+  shiduImg:imageCache.get("shidu"),shidu:"湿度适宜",
+  text:"病害风险,及时喷药",
+  shotCode:props.photo.shotCode,
+  treeCode:props.photo.treeCode,
+  pingzhong:props.photo.pingzhong,
+  uploadDate:props.photo.uploadDate,
+}
+
+async function drawWatermark(event) {
+  img = event.target
+  await loadImage(props.photo.baseMap,"base_map_"+props.photo.treeId)
+  data.baseMap = imageCache.get("base_map_"+props.photo.treeId)
+  if(!watermark.value){
+    let param = {farmId:props.farmId, date: props.photo.uploadDate}
+    let weather = null
+    VE_API.image.findSuitabilityByPoint(param).then((res)=>{
+      if(res.code === 0){
+        weather = res.data
+        drawWatermark2(img,weather)
+      }else{
+        drawWatermark2(img,null)
+      }
+    })
+  }
+}
+
+function drawWatermark2(img,weather) {
+  const canvas = canvasRef.value;
+  let scale = 3
+  canvas.width = img.width * scale;
+  canvas.height = img.height * scale;
+  ctx = canvas.getContext('2d');
+  ctx.scale(scale, scale)
+  ctx.drawImage(img, 0, 0, img.width, img.height);
+  drawBottom(img.width, img.height, weather)
+  watermark.value = canvas.toDataURL();
+}
+
+
+// console.log(data)
+const drawBottom = (imgWidth, imgHeight, weather) => {
+  if (weather) {
+    data["temp"] = weather.tempMin + "°C" + "-" + weather.tempMax + "°C" + " " + weather.tempSuitability
+    data["fushe"] = "光照"+weather.vindexSuitability
+    data["shidu"] = "湿度"+weather.humiditySuitability
+  }
+  // 设置文本样式
+  ctx.font = "8px Arial";
+  ctx.textAlign = "left"; // 设置为左对齐
+  let imgRect = { x: 0, y: 0, width: imgWidth, height: imgHeight}
+  // 绘制头部黑色半透明遮罩
+  let topRect = drawRectInRect(ctx,imgRect, 0, 0, 80, 15,"rgba(0, 0, 0, 0.6)")
+  // 绘制黑色半透明遮罩
+  let bottomRect = drawRectInRect(ctx,imgRect, 0, 5/6 * 100, 100, 1/6 * 100,"rgba(0, 0, 0, 0.6)")
+
+  drawHorizontalTextList(ctx, topRect, '#ffffff',[data.treeCode, "蓬径:5m", "高度:3m", "高产树",data.pingzhong],
+      40/1.5, 0,
+      65/1.5 , 0,"|",0,
+      30/1.5,"#ffffff",1,2)
+  if(props.temp){
+    drawHorizontalTextList(ctx, topRect, '#ffffff',["高温控梢", props.temp, "新梢萌动5级"],
+        40/1.5, -18,
+        80 , 0,"|",0,
+        30/1.5,"#ffffff",1,2)
+  }
+
+  ctx.fillStyle = "white"; // 文本颜色为白色
+  // 绘制温度
+  let startXPercent = 1;
+  drawImageInRect(ctx, bottomRect, data.tempImg, startXPercent, 5+10, 5, 30)
+  drawTextInRect(ctx, bottomRect,`${data.temp}`,startXPercent + 4, 28+10, 20)
+  // 绘制湿度
+  drawImageInRect(ctx, bottomRect, data.shiduImg, startXPercent+26, 7 + 10, 4, 30)
+  drawTextInRect(ctx, bottomRect,`${data.shidu}`,startXPercent + 31, 28 + 10, 20)
+  // 绘制辐射
+  drawImageInRect(ctx, bottomRect, data.fusheImg, startXPercent+26 + 18, 7 + 10, 5, 30)
+  drawTextInRect(ctx, bottomRect,`${data.fushe}`,startXPercent+31 + 18, 28 + 10, 20)
+  //绘制位置信息
+  ctx.fillStyle = "#FFFFFF90";
+  drawTextInRect(ctx, bottomRect,`${formatDate(new Date(data.uploadDate))}_${data.treeCode}_S3_SCS3-3_D0P0G1`,startXPercent +1.7, 75, 16)
+
+  if(data.baseMap){
+    drawBorderImageInRect(ctx, imgRect, data.baseMap, 2/3*100, 2/3*100,
+        1/3*100, 1/3*100, 5, 5)
+  }
+}
+
+
+const showTagBox = ref(true); // 控制 tag-box 的显示状态
+const hideTagBox = (event) => {
+  event.stopPropagation();
+  showTagBox.value = false; // 隐藏 tag-box
+};
+
+const formatDate = (date) => {
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
+  const day = String(date.getDate()).padStart(2, '0');
+  return `${(year+"").substring(2)}${month}${day}`;
+};
+
+
+
+
+</script>
+
+<style lang="scss" scoped>
+.canvas-container {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+}
+.carousel-item {
+  min-width: 100%;
+  flex-shrink: 0;
+  width: 100%;
+  pointer-events: auto;
+  position: relative;
+  .tag-box {
+    position: absolute;
+    bottom: 30%;
+    left: 50%;
+    transform: translate(-50%, 50%); // 确保在高二分之一的位置水平居中
+    height: 18px;
+    padding: 0 6px;
+    background: rgba(108, 108, 108, 0.67);
+    border-radius: 10px;
+    display: flex;
+    align-items: center;
+    color: #FFFFFF;
+    font-size: 12px;
+
+    &.right {
+      left: auto;
+      right: 10px;
+    }
+    &.leftTop {
+      height: 25px;
+      line-height: 26px;
+      padding: 0 8px;
+      border-radius: 16px;
+      background: rgba(0, 0, 0, 0.6);
+      bottom: auto;
+      top: 6px;
+    }
+  }
+  .tag-text {
+    position: absolute;
+    bottom: 31%;
+    left: 50%;
+    width: 80%;
+    transform: translate(-50%, 50%); // 确保在高二分之一的位置水平居中
+    height: 24px;
+    padding: 10px 0px 10px 0px;
+    background: rgba(0, 0, 0, 0.67);
+    border-radius: 6px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: center;
+    color: #FFFFFF;
+    font-size: 12px;
+  }
+
+  .center-mark {
+    position: absolute;
+    bottom: 10px;
+    left: 50%;
+    transform: translateX(-50%);
+    color: #36402c;
+    font-size: rpx(24);
+    font-weight: bold;
+    padding: rpx(14) rpx(30);
+    background: linear-gradient(
+            90deg,
+            rgba(255, 255, 255, 0) 0%,
+            rgba(255, 255, 255, 0.6) 24%,
+            rgba(255, 255, 255, 0.6) 76%,
+            rgba(255, 255, 255, 0) 100%
+    );
+  }
+}
+.carousel-item img {
+  width: 100%;
+  display: block;
+}
+canvas {
+  position: absolute;
+}
+.close-button {
+  background: transparent;
+  border: none;
+  color: #FFFFFF;
+  cursor: pointer;
+  font-size: 10px; // 可以根据需求调整大小
+  position: absolute;
+  top: -1px;
+  right: -9px; // 调整为合适的间距
+  transform: translateY(-50%);
+}
+
+.floating-img {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  width: auto !important;
+  height: 25% !important;
+}
+.floating-img-big {
+  position: fixed !important;
+  z-index: 99999 !important;
+  top: 50% !important;
+  left: 50% !important;
+  width: auto !important;
+  height: 100% !important;
+  transform: translate(-50%, -50%) !important;
+}
+
+
+
+
+</style>

+ 75 - 0
src/views/home/album/album_compoents/cacheImg.js

@@ -0,0 +1,75 @@
+// 创建一个全局的图片缓存
+const imageCache = new Map();
+
+function loadImage(url, key) {
+    return new Promise((resolve, reject) => {
+        // 检查缓存中是否存在该key的图片
+        if (imageCache.has(key)) {
+            resolve(imageCache.get(key));
+            return;
+        }
+
+        // 如果缓存中没有,则创建一个新的图片对象
+        const img = new Image();
+        img.src = url;
+        img.crossOrigin = 'anonymous';
+
+        img.onload = () => {
+            // 图片加载完成后,将其存入缓存
+            imageCache.set(key, img);
+            resolve(img);
+        };
+
+        img.onerror = (error) => {
+            reject(error);
+        };
+    });
+}
+
+// 使用示例
+loadImage(require('@/assets/watermark/feiniao.png'), 'feiniao')
+    .then((img) => {
+        // 在这里使用加载完成的图片
+        // console.log('图片加载成功', img);
+    })
+    .catch((error) => {
+        console.error('图片加载失败', error);
+    });
+loadImage(require('@/assets/watermark/fushe.png'), 'fushe')
+    .then((img) => {
+        // 在这里使用加载完成的图片
+        // console.log('图片加载成功', img);
+    })
+    .catch((error) => {
+        console.error('图片加载失败', error);
+    });
+loadImage(require('@/assets/watermark/shidu.png'), 'shidu')
+    .then((img) => {
+        // 在这里使用加载完成的图片
+        // console.log('图片加载成功', img);
+    })
+    .catch((error) => {
+        console.error('图片加载失败', error);
+    });
+loadImage(require('@/assets/watermark/temp.png'), 'temp')
+    .then((img) => {
+        // 在这里使用加载完成的图片
+        // console.log('图片加载成功', img);
+    })
+    .catch((error) => {
+        console.error('图片加载失败', error);
+    });
+
+loadImage("https://birdseye-img.sysuimars.com/birdseye-look-mini/base_map/90378.jpg", 'base_map')
+    .then((img) => {
+        // 在这里使用加载完成的图片
+        // console.log('图片加载成功', img);
+    })
+    .catch((error) => {
+        console.error('图片加载失败', error);
+    });
+
+
+export { imageCache ,loadImage};
+
+

+ 280 - 0
src/views/home/album/album_compoents/detailDailog.vue

@@ -0,0 +1,280 @@
+<template>
+    <el-dialog v-model="winDialogVisible" lock-scroll modal-class="album-detail-modal" :showClose="false" width="90%" align-center>
+        <div>
+            <div class="congratulation-wrap">
+                <div class="congratulation-box">
+                    <div class="win-des">
+                        <!-- <img src="@/assets/img/weather_index/box-top.png" class="win-icon" /> -->
+                    </div>
+                    <div class="album-detail-box">
+                        <div class="detail-title">{{ dialogData.farmWorkName }}</div>
+                        <div class="detail-desc-box">
+                            <div class="desc-item">
+                                <span class="item-name">触发条件</span>
+                                {{ dialogData.condition }}
+                            </div>
+                            <div class="desc-item">
+                                <span class="item-name">农事编号</span>
+                                {{ dialogData.code }}
+                            </div>
+                            <div class="desc-item">
+                                <div v-if="dialogData.status === 2">
+                                    <span class="item-name">推荐时间</span>
+                                    {{ dialogData.solarName }}
+                                </div>
+                                <div v-if="dialogData.status === 1">
+                                    <span class="item-name">推荐时间</span>
+                                    {{ dialogData.executeDate }}
+                                </div>
+                                
+                                <div v-if="dialogData.status === 0">
+                                    <span class="item-name">{{ dialogData.reCheck ? "复核时间" : "执行时间" }}</span>
+                                    {{ dialogData.executeDate }}
+                                </div>
+                                <!-- <span class="item-name">{{ dialogData.status === 0 ? (dialogData.reCheck ? "复核农事" : "完成农事") : "推荐时间" }}</span>
+                                {{ dialogData.code }} -->
+                            </div>
+                            <div class="desc-item">
+                                <span class="item-name">农事宗旨</span>
+                                {{ dialogData.purpose }}
+                            </div>
+                            <div class="desc-item">
+                                <div class="item-name">药物处方</div>
+                                <div class="item-table">
+                                    <el-table :data="dialogData.pesticideFertilizerList" style="width: 100%" :header-cell-style="{background: '#F5F5F5'}">
+                                        <el-table-column prop="pesticideFertilizerCode" label="功效" width="62">
+                                            <template #default="scope">
+                                                {{scope.row.typeName}}
+                                            </template>
+                                        </el-table-column>
+                                        <el-table-column prop="name" label="名称" />
+                                        <!-- <el-table-column prop="ratio" label="配比" width="50">
+                                            <template #default="scope">
+                                                {{scope.row.ratio ? scope.row.ratio : "--"}}
+                                            </template>
+                                        </el-table-column> -->
+                                        <el-table-column prop="ratio" label="方式" width="62">
+                                            <template #default="scope">
+                                                {{scope.row.ratio ? "人工" : "人工"}}
+                                            </template>
+                                        </el-table-column>
+                                    </el-table>
+                                </div>
+                            </div>
+
+                            <div class="card-link">
+                                <img src="@/assets/img/weather_index/expert-icon.png" />
+                                <div class="expert-name">
+                                    {{ dialogData.expertName }}
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            <div class="close-btn" @click.stop="winDialogVisible=false">
+                    <!-- <el-icon size="32" color="#fff"><CircleCloseFilled /></el-icon> -->
+                    <el-button type="primary" class="one-btn" @click.stop="toShare"> 立即分享 </el-button>
+            </div>
+        </div>
+        <template #footer>
+            <div class="dialog-footer">
+                <div class="close-btn">
+                    <!-- <el-icon size="32" color="#fff"><CircleCloseFilled /></el-icon> -->
+                    <!-- <el-button type="primary" class="one-btn" @click="toShare"> 立即分享 </el-button> -->
+                </div>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+<script setup>
+import { ref } from "vue";
+import eventBus from "@/api/eventBus";
+import wx from "weixin-js-sdk";
+import { useRoute, useRouter } from "vue-router";
+
+const winDialogVisible = ref(false);
+
+const route = useRoute();
+const sampleId = route.query.sampleId
+const farmId = route.query.farmId;
+
+const dialogData = ref({});
+const currentCard = ref({});
+const showDialog = (pageParams) => {
+    dialogData.value = pageParams.card;
+    currentCard.value = {
+        activeIndex: pageParams.activeIndex,
+        farmWorkName: dialogData.value.farmWorkName,
+        farmId: farmId,
+        sampleId: sampleId,
+    };
+    winDialogVisible.value = true;
+};
+eventBus.on("detailDialog:showDialog", (data) => {
+    showDialog(data)
+});
+
+const toShare = () => {
+    wx.miniProgram.navigateTo({
+        url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify(currentCard.value)}&type=album`,
+    });
+}
+</script>
+
+<style lang="less" scoped>
+.congratulation-wrap {
+    border-radius: 12px;
+    background: #f4f5f4;
+}
+.close-btn {
+    text-align: center;
+    margin-top: 20px;
+}
+.congratulation-box {
+    border-radius: 12px;
+    background: url("@/assets/img/weather_index/box-top.png") no-repeat top center /contain;
+    .el-message-box__message {
+        padding: 12px 0 24px 0;
+    }
+    .win-title {
+        color: #1d1e1f;
+        font-family: "PangMenZhengDao", sans-serif; /* 使用自定义字体 */
+        text-align: center;
+        font-size: 24px;
+        line-height: 32px;
+    }
+    .win-detail {
+        text-align: center;
+        color: #252525;
+        padding-top: 6px;
+        font-size: 16px;
+        span {
+            font-size: 22px;
+            color: #2199f8;
+            padding: 0 6px;
+            font-weight: bold;
+        }
+    }
+    .win-des {
+        height: 205px;
+        text-align: center;
+    }
+    .win-icon {
+        width: 100%;
+        border-radius: 12px 12px 0 0;
+    }
+}
+.album-detail-box {
+    padding: 0 24px 16px 12px;
+    color: #000;
+    position: relative;
+    // top: -58px;
+    .detail-title {
+        font-size: 24px;
+        font-weight: bold;
+        padding-bottom: 8px;
+        letter-spacing: 1.6px;
+    }
+    .detail-desc-box {
+        position: relative;
+        .desc-item {
+            font-size: 14px;
+            .item-name {
+                color: #999999;
+                margin-right: 12px;
+            }
+            .item-table {
+                margin-top: 8px;
+                border: 1px solid rgba(255, 255, 255, 0.6);
+                border-radius: 4px;
+                ::v-deep {
+                    .el-table th.el-table__cell {
+                        padding: 6px 0;
+                    }
+                    .el-table--default .el-table__cell {
+                        padding: 6px 0;
+                    }
+                    .el-table--default .cell {
+                        padding: 0px 6px;
+                    }
+                }
+            }
+        }
+        .desc-item + .desc-item {
+            padding-top: 8px;
+        }
+        .card-link {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            color: #2199f8;
+            font-size: 12px;
+            position: absolute;
+            right: 6px;
+            top: 0px;
+            .expert-name {
+                background: #d3e8ff;
+                border-radius: 4px;
+                padding: 1px 12px;
+                margin-top: 4px;
+                display: flex;
+                align-items: center;
+            }
+            img {
+                width: 62px;
+            }
+            .icon {
+                padding-right: 2px;
+            }
+        }
+    }
+}
+.dialog-footer {
+    position: relative;
+    .close-btn {
+        position: absolute;
+        bottom: -58px;
+        left: 0;
+        right: 0;
+        margin: 0 auto;
+        text-align: center;
+    }
+}
+</style>
+
+<style lang="less">
+.album-detail-modal {
+    .el-overlay-dialog {
+        .el-dialog {
+            padding: 0;
+            border-radius: 12px;
+            background: none;
+            box-shadow: none;
+            margin-bottom: 70px;
+            margin-top: 10px;
+            overflow: auto;
+            scrollbar-width: none;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            pointer-events: none;
+            .el-dialog__header {
+                padding: 0;
+            }
+            .el-dialog__body {
+                pointer-events: all;
+                width: 90%;
+            }
+        }
+
+        .one-btn {
+            width: 210px;
+            height: 40px;
+            line-height: 40px;
+        }
+    }
+}
+</style>

+ 256 - 0
src/views/home/album/album_compoents/utils.js

@@ -0,0 +1,256 @@
+/**
+ * 在矩形内绘制矩形
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {number} startXPercent - 内部矩形起始点横坐标,范围从 0 到 100
+ * @param {number} startYPercent - 内部矩形起始点纵坐标,范围从 0 到 100
+ * @param {number} widthPercent - 内部矩形宽度,范围从 0 到 100
+ * @param {number} heightPercent - 内部矩形高度,范围从 0 到 100
+ * @param {string} color - 填充颜色,可选参数,默认为黑色
+ */
+const drawRectInRect = (ctx, rect, startXPercent, startYPercent, widthPercent, heightPercent, color = 'black') => {
+    // 计算内部矩形的起始坐标
+    const startX = rect.x + (startXPercent / 100) * rect.width;
+    const startY = rect.y + (startYPercent / 100) * rect.height;
+    // 计算内部矩形的宽度和高度
+    const width = (widthPercent / 100) * rect.width;
+    const height = (heightPercent / 100) * rect.height;
+    ctx.fillStyle = color; // 设置填充颜色
+    ctx.fillRect(startX, startY, width, height); // 绘制矩形
+    return { x:startX, y:startY, width, height };
+};
+
+
+
+/**
+ * 在矩形内绘制文本
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {string} text - 要绘制的文本
+ * @param {number} startXPercent - 文字起始横坐标,范围从 0 到 100
+ * @param {number} startYPercent - 文字起始纵坐标,范围从 0 到 100
+ * @param {number} fontSizePercent - 文字大小,范围从 0 到 100,基于矩形高度
+ */
+const drawTextInRect = (ctx, rect, text, startXPercent, startYPercent, fontSizePercent) => {
+    // 计算文字起始坐标
+    const startX = rect.x + (startXPercent / 100) * rect.width;
+    const startY = rect.y + (startYPercent / 100) * rect.height;
+
+    // 计算文字大小
+    const fontSize = (fontSizePercent / 100) * rect.height;
+    ctx.font = `${fontSize}px sans-serif`; // 设置字体样式
+    ctx.fillText(text, startX, startY);
+};
+
+/**
+ * 在矩形内绘制线
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {number} startXPercent - 起始点横坐标,范围从 0 到 100
+ * @param {number} startYPercent - 起始点纵坐标,范围从 0 到 100
+ * @param {number} endXPercent - 结束点横坐标,范围从 0 到 100
+ * @param {number} endYPercent - 结束点纵坐标,范围从 0 到 100
+ * @param {string} color - 线条颜色,可选参数,默认为黑色
+ * @param {number} lineWidth - 线条宽度,可选参数,默认为1像素
+ */
+const drawLineInRect = (ctx, rect, startXPercent, startYPercent, endXPercent, endYPercent, color = 'black', lineWidth = 1) => {
+    // 计算起始点坐标
+    const startX = rect.x + (startXPercent / 100) * rect.width;
+    const startY = rect.y + (startYPercent / 100) * rect.height;
+
+    // 计算结束点坐标
+    const endX = rect.x + (endXPercent / 100) * rect.width;
+    const endY = rect.y + (endYPercent / 100) * rect.height;
+
+    // 设置线条样式
+    ctx.strokeStyle = color; // 设置线条颜色
+    ctx.lineWidth = lineWidth; // 设置线条宽度
+
+    // 绘制线
+    ctx.beginPath();
+    ctx.moveTo(startX, startY);
+    ctx.lineTo(endX, endY);
+    ctx.stroke();
+};
+
+/**
+ * 在矩形内绘制点
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {number} xPercent - 点的横坐标,范围从 0 到 100
+ * @param {number} yPercent - 点的纵坐标,范围从 0 到 100
+ * @param {string} color - 点的颜色,可选参数,默认为黑色
+ * @param {number} radius - 点的半径,可选参数,默认为1像素
+ */
+const drawPointInRect = (ctx, rect, xPercent, yPercent, color = 'black', radius = 1) => {
+    // 计算点的坐标
+    const x = rect.x + (xPercent / 100) * rect.width;
+    const y = rect.y + (yPercent / 100) * rect.height;
+
+    // 设置点的样式
+    ctx.fillStyle = color; // 设置点的颜色
+
+    // 开始绘制路径
+    ctx.beginPath();
+    ctx.arc(x, y, radius, 0, Math.PI * 2); // 绘制一个圆
+    ctx.fill(); // 填充圆
+};
+
+/**
+ * 在矩形内绘制图片
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {HTMLImageElement} img - 要绘制的图片对象
+ * @param {number} imgXPercent - 图片起始横坐标,范围从 0 到 100
+ * @param {number} imgYPercent - 图片起始纵坐标,范围从 0 到 100
+ * @param {number} imgWidthPercent - 图片宽度百分比,范围从 0 到 100
+ * @param {number} imgHeightPercent - 图片高度百分比,范围从 0 到 100
+ */
+const drawImageInRect = (ctx, rect, img, imgXPercent, imgYPercent, imgWidthPercent, imgHeightPercent) => {
+    // 计算图片的起始坐标
+    const imgX = rect.x + (imgXPercent / 100) * rect.width;
+    const imgY = rect.y + (imgYPercent / 100) * rect.height;
+    // 计算图片的宽度和高度
+    const imgWidth = (imgWidthPercent / 100) * rect.width;
+    const imgHeight = (imgHeightPercent / 100) * rect.height;
+    // 开始绘制图片
+    ctx.drawImage(img, imgX, imgY, imgWidth, imgHeight);
+};
+
+/**
+ * 在矩形内绘制图片,带圆角和白色边框
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {HTMLImageElement} img - 要绘制的图片对象
+ * @param {number} imgXPercent - 图片起始横坐标,范围从 0 到 100
+ * @param {number} imgYPercent - 图片起始纵坐标,范围从 0 到 100
+ * @param {number} imgWidthPercent - 图片宽度百分比,范围从 0 到 100
+ * @param {number} imgHeightPercent - 图片高度百分比,范围从 0 到 100
+ * @param {number} borderRadius - 圆角半径,以像素为单位
+ * @param {number} borderWidth - 边框宽度,以像素为单位
+ */
+const drawBorderImageInRect = (ctx, rect, img, imgXPercent, imgYPercent, imgWidthPercent, imgHeightPercent, borderRadius, borderWidth) => {
+    // 计算图片的起始坐标
+    const imgX = rect.x + (imgXPercent / 100) * rect.width + borderWidth;
+    const imgY = rect.y + (imgYPercent / 100) * rect.height + borderWidth;
+    // 计算图片的宽度和高度
+    const imgWidth = (imgWidthPercent / 100) * rect.width - 2 * borderWidth;
+    const imgHeight = (imgHeightPercent / 100) * rect.height - 2 * borderWidth;
+
+    // 保存画布状态
+    ctx.save();
+
+    // 开始绘制圆角矩形的边框
+    ctx.beginPath();
+    ctx.moveTo(imgX + borderRadius, imgY);
+    ctx.lineTo(imgX + imgWidth - borderRadius, imgY);
+    ctx.arcTo(imgX + imgWidth, imgY, imgX + imgWidth, imgY + borderRadius, borderRadius);
+    ctx.lineTo(imgX + imgWidth, imgY + imgHeight - borderRadius);
+    ctx.arcTo(imgX + imgWidth, imgY + imgHeight, imgX + imgWidth - borderRadius, imgY + imgHeight, borderRadius);
+    ctx.lineTo(imgX + borderRadius, imgY + imgHeight);
+    ctx.arcTo(imgX, imgY + imgHeight, imgX, imgY + imgHeight - borderRadius, borderRadius);
+    ctx.lineTo(imgX, imgY + borderRadius);
+    ctx.arcTo(imgX, imgY, imgX + borderRadius, imgY, borderRadius);
+    ctx.closePath();
+
+    // 设置边框颜色和宽度
+    ctx.strokeStyle = 'white';
+    ctx.lineWidth = borderWidth;
+    ctx.stroke();
+
+    // 开始绘制圆角矩形的填充(可选,如果需要背景)
+    // ctx.fillStyle = 'transparent'; // 或者设置其他背景颜色
+    // ctx.fill();
+
+    // 开始裁剪
+    ctx.clip();
+
+    // 绘制图片
+    ctx.drawImage(img, imgX, imgY, imgWidth, imgHeight);
+
+    // 恢复画布状态
+    ctx.restore();
+};
+
+/**
+ * 在矩形内绘制横向文本列表
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {Array<string>} texts - 要绘制的文本数组
+ * @param {number} fontSizePercent - 文字大小,范围从 0 到 100,基于矩形高度
+ * @param {number} startXPercent - 文字起始横坐标,范围从 0 到 100,基于矩形宽度
+ * @param {number} startYPercent - 文字起始纵坐标,范围从 0 到 100,基于矩形高度
+ * @param {number} columnSpacingPercent - 列间距,范围从 0 到 100,基于矩形宽度
+ * @param {string} columnSeparator - 列间隔字符,默认为空字符串
+ * @param {number} columnSeparatorWidthPercent - 列间隔字符的宽度百分比,范围从 0 到 100,基于矩形宽度
+ * @param {number} separatorFontSizePercent - 分隔符字号大小,范围从 0 到 100,基于矩形高度
+ * @param {string} separatorColor - 分隔符颜色,默认为黑色
+ * @param {number} separatorMarginLeftPercent - 分隔符左边的边距,范围从 0 到 100,基于矩形宽度
+ * @param {number} separatorMarginRightPercent - 分隔符右边的边距,范围从 0 到 100,基于矩形宽度
+ */
+const drawHorizontalTextList = (ctx, rect,color='white', texts, fontSizePercent, startXPercent = 0, startYPercent = 50, columnSpacingPercent = 0, columnSeparator = '', columnSeparatorWidthPercent = 0, separatorFontSizePercent = fontSizePercent, separatorColor = 'black', separatorMarginLeftPercent = 0, separatorMarginRightPercent = 0) => {
+    // 计算文字大小
+    const fontSize = (fontSizePercent / 100) * rect.height;
+    ctx.font = `${fontSize}px sans-serif`; // 设置字体样式
+
+    // 计算文字的总宽度
+    let totalTextWidth = 0;
+    texts.forEach(text => {
+        totalTextWidth += ctx.measureText(text).width;
+    });
+
+    // 计算分隔符的字体大小
+    const separatorFontSize = (separatorFontSizePercent / 100) * rect.height;
+
+    // 计算分隔符的总宽度
+    ctx.font = `${separatorFontSize}px sans-serif`; // 设置分隔符字体样式
+    const separatorWidth = (columnSeparatorWidthPercent / 100) * rect.width;
+    const totalSeparatorWidth = (texts.length - 1) * separatorWidth;
+
+    // 计算分隔符的左右边距
+    const separatorMarginLeft = (separatorMarginLeftPercent / 100) * rect.width;
+    const separatorMarginRight = (separatorMarginRightPercent / 100) * rect.width;
+
+    // 计算可用的总间距
+    const totalSpacing = (columnSpacingPercent / 100) * rect.width;
+
+    // 计算所有文字和间隔字符以及间距的总宽度
+    const totalWidth = totalTextWidth + totalSeparatorWidth + totalSpacing + (texts.length - 1) * (separatorMarginLeft + separatorMarginRight);
+
+    // 计算实际的起始坐标,使得文本和间隔字符居中于矩形
+    const startX = rect.x + (startXPercent / 100) * rect.width + (rect.width - totalWidth) / 2;
+    const startY = rect.y + (startYPercent / 100) * rect.height - (fontSize / 2); // 绘制文字,纵坐标居中
+
+    // 绘制文本和间隔字符
+    let currentX = startX;
+    texts.forEach((text, index) => {
+        ctx.font = `${fontSize}px sans-serif`; // 设置文字字体样式
+        ctx.fillStyle = color; // 设置文字颜色
+        ctx.fillText(text, currentX, startY + (fontSize / 2)); // 绘制文字,纵坐标居中
+        currentX += ctx.measureText(text).width;
+
+        // 如果不是最后一列,则绘制间隔字符
+        if (index < texts.length - 1) {
+            // 添加左边距
+            currentX += separatorMarginLeft;
+
+            // 设置分隔符颜色和字体样式
+            ctx.fillStyle = separatorColor;
+            ctx.font = `${separatorFontSize}px sans-serif`;
+
+            // 绘制分隔符
+            ctx.fillText(columnSeparator, currentX, startY + (separatorFontSize / 2));
+
+            // 添加右边距
+            currentX += separatorWidth + separatorMarginRight + columnSpacingPercent / 100 * rect.width;
+        }
+    });
+};
+
+
+
+
+
+
+export { drawTextInRect, drawLineInRect, drawPointInRect ,drawImageInRect,drawBorderImageInRect, drawRectInRect, drawHorizontalTextList};
+

File diff suppressed because it is too large
+ 83 - 0
src/views/home/album/components/chart.vue


+ 270 - 0
src/views/home/album/components/chartSeting.js

@@ -0,0 +1,270 @@
+import * as echarts from "echarts";
+import { color } from "highcharts";
+
+export const galleryLine = {
+    tooltip: {
+        trigger: "axis",
+    },
+    grid: {
+        top: 28,
+        left: 10,
+        right: 26,
+        bottom: 0,
+        containLabel: true,
+    },
+    xAxis: {
+        name: "时间",
+        nameLocation: "end",
+        nameGap: 4,
+        nameTextStyle: {
+            fontSize: 10,
+            color: "#666666"
+        },
+        type: 'category',
+        // 分割线
+        axisTick: {
+            show: false,
+        },
+        splitLine: {
+            show: false,
+        },
+        axisLabel: {
+            // interval: 0,
+            rotate: 15,
+            margin: 14,
+            textStyle: {
+                color: "#999999",
+                fontSize: 10
+            },
+        },
+        data: ['9/1', '9/7', '9/13', '9/19', '9/25', '10/1', '10/7']
+    },
+    yAxis: {
+        name: "xx指标",
+        nameLocation: "end",
+        type: 'value',
+        nameTextStyle: {
+            fontSize: 10,
+            color: "#666666"
+        },
+        axisTick: {
+            show: false,
+        },
+        axisLine: {
+            show: true,
+            lineStyle: {
+                color: "#BBBBBB",
+            },
+        },
+        axisLabel: {
+            textStyle: {
+                color: "#666666",
+                fontSize: 10
+            },
+        },
+        // 分割线
+        splitLine: {
+            show: false,
+            lineStyle: {
+                color: "rgba(255, 255, 255, 0.1)",
+            },
+        },
+    },
+    series: [
+        {
+            data: [40, 58, 62, 64, 68, 74, 80],
+            type: 'line',
+            symbolSize: 6,
+            itemStyle: {
+                color: '#FFD489' // 设置数据点颜色为红色
+            },
+
+            // itemStyle: {
+            //     color: '#CECECE' // 设置数据点颜色
+            // },
+            // lineStyle: {
+            //     type: 'dashed', // 设置线条为虚线
+            //     width: 2,       // 设置线条宽度
+            //     color: '#CECECE' // 设置线条颜色
+            // }
+        }
+    ],
+}
+
+export const galleryIndicatorLine = {
+    tooltip: {
+        // trigger: "axis",
+    },
+    legend: {
+        icon: 'roundRect',
+        top: 0,
+        right: 0,
+        orient: 'horizontal',
+        itemWidth: 6,
+        itemHeight: 2,
+        itemGap: 3,
+        textStyle: {
+          align: 'left',
+          fontSize: 10,
+        },
+        data: [
+          { name: '褪绿率', textStyle: { color: '#66B9FB' }, itemStyle: {color: "#66B9FB"} },
+          { name: '花芽率', textStyle: { color: '#1B98FC' }, itemStyle: {color: "#1B98FC"} },
+          { name: '花蕾率', textStyle: { color: '#036CC0' }, itemStyle: {color: "#036CC0"} },
+          { name: '生长异常', textStyle: { color: '#F7BE5A' }, itemStyle: {color: "#F7BE5A"} },
+          { name: '短花穗率', textStyle: { color: '#F7BE5A' }, itemStyle: {color: "#F7BE5A"} },
+          { name: '病虫异常', textStyle: { color: '#F99851' }, itemStyle: {color: "#F99851"} }
+        ]
+      },
+    grid: {
+        top: 30,
+        left: 8,
+        right: 18,
+        bottom: 4,
+        containLabel: true,
+    },
+    xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        // 分割线
+        axisTick: {
+            show: false,
+        },
+        splitLine: {
+            show: false,
+        },
+        axisLabel: {
+            // interval: 0,
+            interval: "auto", // 自动间隔显示
+            showMinLabel: true, // 显示第一个
+            showMaxLabel: true, // 确保最后一个显示
+            rotate: 40,
+            margin: 14,
+            textStyle: {
+                color: "#666666",
+                fontSize: 10
+            },
+            formatter: function (value) {
+                // 获取后 5 位字符(假设日期格式始终为 'YYYY-MM-DD')
+                let shortenedValue = value.slice(-5);
+
+                // 自定义处理省略号(由于 ECharts 不直接支持,需手动处理)
+                // 此处简单判断,若原长度超过 10(即 'YYYY-MM-DD' 格式),则添加省略号
+                if (value.length > 10) {
+                    shortenedValue = '...' + shortenedValue;
+                }
+
+                return shortenedValue;
+            }
+        },
+        axisLine: {
+            lineStyle: {
+                color: "#ddd",
+            }
+        },
+        data: ['9/1', '9/7', '9/13', '9/19', '9/25', '10/1', '10/7']
+    },
+    yAxis: [{
+        type: 'value',
+        offset: 6,
+        axisTick: {
+            show: false,
+        },
+        axisLine: {
+            show: false,
+        },
+        axisLabel: {
+            align: "center",
+            textStyle: {
+                color: "#666666",
+                fontSize: 12
+            },
+        },
+        // 分割线
+        splitLine: {
+            lineStyle: {
+                type: [4, 3],
+                dashOffset: 5,
+                color: "#DDDDDD",
+            },
+        },
+        // min: 0, // 最小值固定为0
+        // max: 100, // 最大值固定为100
+        // splitNumber: 5, // 设置5个分割段
+    }],
+    series: [
+        {
+            name: "病虫异常",
+            data: [40, 28, 22, 64, 28, 24, 40],
+            type: 'line',
+            smooth: true,
+            symbol: "none",
+            itemStyle: { color: "#FF7219" },
+            lineStyle: {
+                color: "#FF7219", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+        {
+            name: "生长异常",
+            data: [80, 88, 92, 84, 88, 74, 80],
+            type: 'line',
+            symbol: "none",
+            smooth: true,
+            itemStyle: { color: "#FFB82E" },
+            lineStyle: {
+                color: "#FFB82E", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+        {
+            name: "褪绿率",
+            data: [
+                ["9/1", 10],
+                ["9/7", 30],
+                ["9/13", 80],
+            ],
+            type: 'line',
+            smooth: true,
+            symbol: "none",
+            itemStyle: { color: "#66B9FB" },
+            lineStyle: {
+                color: "#66B9FB", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+        {
+            name: "花芽率",
+            data: [
+                ["9/7", 8],
+                ["9/19", 50],
+                ["9/25", 80],
+            ],
+            type: 'line',
+            smooth: true,
+            symbol: "none",
+            itemStyle: { color: "#1B98FC" },
+            lineStyle: {
+                color: "#1B98FC", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+        {
+            name: "花蕾率",
+            data: [
+                ["9/19", 12],
+                ["9/25", 30],
+                ["10/1", 60],
+                ["10/7", 70],
+            ],
+            type: 'line',
+            smooth: true,
+            symbol: "none",
+            itemStyle: { color: "#036CC0" },
+            lineStyle: {
+                color: "#036CC0", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+    ],
+}

+ 130 - 0
src/views/home/album/components/indicatorChart.vue

@@ -0,0 +1,130 @@
+<template>
+    <div class="card-chart" ref="chartRef"></div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import * as echarts from "echarts";
+import { galleryIndicatorLine } from "./chartSeting";
+import { deepClone } from "@/common/commonFun";
+import eventBus from "@/api/eventBus";
+
+const props = defineProps({
+    indexName: {
+        type: String,
+        default: "",
+    },
+    type: {
+        type: String,
+        default: "",
+    },
+    chartData: {
+        type: Object,
+        default: () => {},
+    },
+    isDark: {
+        type: Boolean,
+        default: false
+    },
+});
+
+let myChart = ref(null);
+const chartRef = ref();
+const options = ref({});
+
+
+onMounted(() => {
+    myChart.value = echarts.init(chartRef.value);
+    options.value = deepClone(galleryIndicatorLine);
+    updateValue(false)
+});
+
+eventBus.on("chart:updateOption",(data)=>{
+    updateValue(true, data)
+})
+
+const updateValue = (isUpdate, data) =>{
+    let chartList = {}
+    if (isUpdate) {
+        // const filterData = filterDataAfterJanuary(data)
+        chartList = data
+    } else {
+        chartList = props.chartData
+    }
+    myChart.value.clear()
+    if(chartList.dates?.length){
+        options.value.xAxis.data = chartList.dates
+        options.value.series[0].name = chartList.diseaseRiskName
+        options.value.legend.data[0] = chartList.diseaseRiskName
+        options.value.series[0].data = chartList.diseaseRisk
+        options.value.series[1].name = chartList.growthRiskName
+        options.value.legend.data[1] = chartList.growthRiskName
+        options.value.series[1].data = chartList.growthRisk
+        options.value.series[2].name = chartList.phenoParam1Name
+        options.value.legend.data[2] = chartList.phenoParam1Name
+        options.value.series[2].data = chartList.phenoParam1
+        options.value.series[3].name = chartList.phenoParam2Name
+        options.value.legend.data[3] = chartList.phenoParam2Name
+        options.value.series[3].data = chartList.phenoParam2
+        options.value.series[4].name = chartList?.phenoParam3Name
+        options.value.legend.data[4] = chartList.phenoParam3Name
+        options.value.series[4].data = chartList?.phenoParam3
+        if (props.isDark) {
+            options.value.yAxis[0].splitLine.lineStyle.color = "#333333"
+            options.value.yAxis[0].axisLabel.textStyle.color = "#999999"
+            options.value.xAxis.axisLine.lineStyle.color = "#333333"
+            options.value.xAxis.axisLabel.textStyle.color = "#999999"
+            options.value.legend.textStyle.color = "rgba(255, 255, 255, 0.6)"
+        }
+        myChart.value.setOption(options.value);
+    }
+}
+
+// 过滤函数,根据日期截取数据
+const filterDataAfterJanuary = (rawData) => {
+      const startDate = new Date('2025-01-01');
+      const filtered = {
+        dates: [],
+        growthRisk: [],
+        diseaseRiskName: "",
+        growthRiskName: "",
+        phenoParam1Name: "",
+        phenoParam2Name: "",
+        phenoParam3Name: "",
+        phenoParam4Name: "",
+        phenoParam1: [],
+        phenoParam2: [],
+        phenoParam3: [],
+        phenoParam4: [],
+      };
+ 
+      rawData?.dates.forEach((dateStr, index) => {
+        const date = new Date(dateStr);
+        if (date >= startDate) {
+          filtered.dates.push(dateStr);
+          filtered.diseaseRiskName = rawData.diseaseRiskName
+          filtered.growthRiskName = rawData.growthRiskName
+          filtered.phenoParam1Name = rawData.phenoParam1Name
+          filtered.phenoParam2Name = rawData.phenoParam2Name
+          filtered.phenoParam3Name = rawData.phenoParam3Name
+          filtered.phenoParam4Name = rawData.phenoParam4Name
+          filtered.growthRisk.push(rawData.growthRisk[index]);
+          filtered.phenoParam1.push(rawData.phenoParam1[index]);
+          filtered.phenoParam2.push(rawData.phenoParam2[index]);
+          filtered.phenoParam3.push(rawData.phenoParam3[index]);
+          filtered.phenoParam4.push(rawData.phenoParam4[index]);
+        }
+      });
+ 
+      return filtered;
+    };
+</script>
+
+<style lang="scss" scoped>
+.card-chart {
+    padding: 8px 4px 0 4px;
+    width: 100%;
+    height: 160px;
+    box-sizing: border-box;
+}
+</style>

+ 1152 - 0
src/views/home/album/index.vue

@@ -0,0 +1,1152 @@
+<template>
+    <div class="album-page">
+        <div class="album-wrap" ref="scrollContainer">
+            <!-- <div class="barrage-title">
+                农情动态
+            </div> -->
+            <div class="album-content">
+                <div class="album-bottom">
+                    <el-affix :offset="108">
+                        <div class="album-l">
+                            <el-menu default-active="0" class="el-menu-vertical-demo">
+                                <el-menu-item
+                                    v-for="(item, index) in farmWorkList"
+                                    :key="index"
+                                    :index="index.toString()"
+                                    :id="'menu' + index"
+                                    :class="{ active: activeSection === index }"
+                                    @click="scrollToSection(index)"
+                                    v-show="item.menu"
+                                >
+                                    <img
+                                        class="menu-icon"
+                                        :src="
+                                            require('@/assets/img/gallery/icon-' +
+                                                item.icon +
+                                                (item.statuss === 0 ? '-no' : '') +
+                                                '.png')
+                                        "
+                                    />
+                                    <div
+                                        :class="[
+                                            'menu-name',
+                                            {
+                                                noTime: item.status === 0,
+                                                onTime: item.status === 2,
+                                                now: item.status === 1,
+                                            },
+                                        ]"
+                                    >
+                                        {{ item.farmWorkName }}
+                                    </div>
+                                </el-menu-item>
+                            </el-menu>
+                        </div>
+                    </el-affix>
+                    <!-- <div
+                        class="export-btn"
+                        v-show="farmId == 766 || farmId == 1"
+                        @click="handleExport"
+                        :style="{ bottom: `${tabBarHeight}px` }"
+                    >
+                        <div class="btn-item"><img src="@/assets/img/old_mini/export.png" />导出</div>
+                    </div> -->
+                    <div class="album-r" @scroll="debouncedHandleScroll2">
+                        <div
+                            class="done-card section"
+                            v-for="(card, cardI) in farmWorkList"
+                            :key="cardI"
+                            :class="{ flashing: card.status === 1 && isFlashing }"
+                        >
+                            <div
+                                class="card-dom"
+                                :id="'section' + cardI"
+                            ></div>
+                            <div v-if="card.status === 1 && indicatorChartData.content" class="log-wrap">
+                                <div class="log-box">
+                                    <div class="log-title PangMenZhengDao-FONT">
+                                        <span>果园日志</span>
+                                    </div>
+                                    <div class="log-content">
+                                        <div class="log-desc" v-html="indicatorChartData?.content"></div>
+                                        <indicatorChart
+                                            :key="cardI+'log'"
+                                            :isDark="true"
+                                            :chartData="indicatorChartData"
+                                        ></indicatorChart>
+                                    </div>
+                                    <div v-show="farmId == 766" class="time-img">
+                                        <photo-provider :photo-closable="true">
+                                            <photo-consumer
+                                                class="time-item"
+                                                :src="require('@/assets/img/gallery/time.png')"
+                                            >
+                                            <img src="@/assets/img/gallery/time.png" style="width: 100%" />
+                                            </photo-consumer>
+                                        </photo-provider>
+                                        <!-- <img src="@/assets/img/gallery/time.png" /> -->
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="card-wrap" :class="{ noCarousel: card.status !== 0, 'done': card.status === 0 }">
+                                <div
+                                    class="card-content"
+                                    :class="{ push: card.status === 1, hasBg: card.status === 2 && !card.hasBg }"
+                                >
+                                    <div
+                                        class="card-top"
+                                        @click.stop="showDetail(card)"
+                                    >
+                                        <!-- :class="{ noChart: !card.indexChart.length }" -->
+                                        <div class="card-title serve-title">
+                                            <div class="title-name">{{ card.farmWorkName }}</div>
+                                            <div v-if="card.type === 1" class="status-tag standard">标准农事</div>
+                                            <div v-if="card.type === 2" class="status-tag advice">推荐农事</div>
+                                            <div v-if="card.type === 0" class="status-tag warning">预警农事</div>
+                                            <!-- 右上角角标 -->
+                                            <div v-if="card.status === 2 && card.execute === 0" class="add-btn">
+                                                未触发
+                                            </div>
+                                            <div
+                                                v-if="card.execute === 2"
+                                                class="add-btn unactive"
+                                            >
+                                                未激活
+                                            </div>
+                                            <div v-if="card.execute === 3" class="add-btn">待执行</div>
+                                            <div v-if="card.status === 0 && card.execute === 4" class="add-btn recheck">
+                                                已完成
+                                            </div>
+                                            <div v-if="card.status === 0 && card.reCheck === 1" class="add-btn recheck">
+                                                已复核
+                                            </div>
+                                            <div v-if="card.execute === 1" class="add-btn unactive">已失效</div>
+                                        </div>
+                                        <!-- <div v-else class="card-title">
+                                            <div class="title-name">{{ card.farmWorkName }}</div>
+                                            <div class="status-tag blue">{{ card.reCheck ? "已复核" : "已完成" }}</div>
+                                            <div class="status-tag blue good-wrap">
+                                                优<img class="good" src="@/assets/img/weather_index/good.png" />
+                                            </div>
+                                        </div> -->
+                                        <div v-if="card.status === 0">
+                                            <!-- 已完成 -->
+                                            <div class="card-desc">
+                                                <span class="desc-title">服务主体:</span>
+                                                {{ card.serviceMain || "大荔农业" }}
+                                            </div>
+                                            <div class="card-desc">
+                                                <span class="desc-title">执行主体:</span>
+                                                {{ card.executeMain || "大荔农业" }}
+                                            </div>
+                                            <div class="card-desc">
+                                                <span class="desc-title">指导专家:</span>
+                                                <span class="expert-link" @click="toExpert(card.expertName)">
+                                                    {{ card.expertName }}
+                                                    <el-icon color="#F0AC37" class="icon" size="12"><Link /></el-icon>
+                                                </span>
+                                            </div>
+                                        </div>
+                                        <div v-else>
+                                            <div class="card-desc condition-wrap">
+                                                <span class="desc-title">触发条件:</span>
+                                                {{ card.condition }}
+                                            </div>
+                                            <div class="card-desc">
+                                                <span class="desc-title">农事编号:</span>
+                                                {{ card.code }}
+                                            </div>
+                                            <div v-if="card.status === 2" class="card-desc">
+                                                <span class="desc-title">推荐时间:</span>
+                                                {{ card.solarName }}
+                                            </div>
+                                            <div v-if="card.status === 1" class="card-desc">
+                                                <span class="desc-title">推荐时间:</span>
+                                                {{ card.executeDate }}
+                                            </div>
+                                            <div v-if="card.status === 0" class="card-desc">
+                                                <span class="desc-title">{{
+                                                    card.reCheck ? "复核时间:" : "执行时间:"
+                                                }}</span>
+                                                {{ card.executeDate }}
+                                            </div>
+                                        </div>
+                                        <div class="card-desc last-desc">
+                                            <span class="desc-title">药物处方:</span>
+                                            <div class="rescription-wrap">
+                                                <div class="rescription">
+                                                    <span
+                                                        v-for="(
+                                                            fertilizer, fertilizerI
+                                                        ) in card.pesticideFertilizerList"
+                                                        :key="fertilizerI"
+                                                    >
+                                                        {{ fertilizer.ratio ? fertilizer.ratio + "倍" : "" }}
+                                                        {{ fertilizer.name }}
+                                                        <span
+                                                            v-if="
+                                                                fertilizerI !== card.pesticideFertilizerList.length - 1
+                                                            "
+                                                        >
+                                                            +
+                                                        </span>
+                                                    </span>
+                                                </div>
+                                                <div class="detail-text">查看详情></div>
+                                            </div>
+                                        </div>
+                                        <div class="card-link" v-if="card.status === 2" @click.stop="handlePage(card)">
+                                            <img src="@/assets/img/gallery/expert-icon.png" />
+                                            <div class="expert-name">
+                                                {{ card.expertName
+                                                }}<el-icon size="12" class="icon"><ArrowRightBold /></el-icon>
+                                            </div>
+                                        </div>
+                                        <div class="step-box" v-else>
+                                            <step-box :card="card" :isDone="card.status === 0 && (card.reCheck === 1 || card.execute === 4)" v-if="card.orderStatus || card.status === 0 || card.orderStatus === 0"></step-box>
+                                        </div>
+                                    </div>
+                                    <chart
+                                        v-if="card.status !== 2 && card.indexChart.length && card.status !== 0"
+                                        :key="cardI"
+                                        :indexName="card.indexName"
+                                        :type="card.status !== 1 ? 'normal' : 'feature'"
+                                        :chartData="card.indexChart"
+                                    ></chart>
+
+                                    <div v-if="card.status === 0 && card.userEvaluation">
+                                        <div class="evaluate">
+                                            <div class="evaluate-title">用户评价</div>
+                                            <div class="comment">
+                                                <div class="user-info">
+                                                    <el-avatar
+                                                        class="avatar"
+                                                        :size="40"
+                                                        :src="card.userEvaluation.icon"
+                                                    />
+                                                    <div class="user-name">
+                                                        <div>{{ card.userEvaluation.commentUserName || "麦田守望者" }}</div>
+                                                        <span>{{ card.userEvaluation.commentDate }}</span>
+                                                    </div>
+                                                </div>
+                                                <div class="rate">
+                                                    <div class="rate-item">
+                                                        <span class="name">专家</span>
+                                                        <el-rate v-model="card.userEvaluation.expertServiceRating" size="small" />
+                                                    </div>
+                                                    <div class="rate-item">
+                                                        <span class="name">农资</span>
+                                                        <el-rate v-model="card.userEvaluation.agricultureServiceRating" size="small" />
+                                                    </div>
+                                                    <div class="rate-item">
+                                                        <span class="name">农服</span>
+                                                        <el-rate v-model="card.userEvaluation.farmServiceRating" size="small" />
+                                                    </div>
+                                                </div>
+                                                <div class="text van-multi-ellipsis--l2">
+                                                    {{ card.userEvaluation.detailedComment }}
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div v-if="card.status !== 2" :class="['over-img']">
+                                <!-- 图片列表 -->
+                                <album-carousel7d
+                                    :key="farmName"
+                                    :farmId="farmId"
+                                    :farmWork="card"
+                                ></album-carousel7d>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref, computed, onUnmounted, nextTick, onActivated, onDeactivated } from "vue";
+import { useStore } from "vuex";
+import { useRoute, useRouter } from "vue-router";
+import stepBox from "@/components/common/stepBox.vue";
+import eventBus from "@/api/eventBus";
+import chart from "./components/chart.vue";
+import indicatorChart from "./components/indicatorChart.vue";
+const store = useStore();
+import AlbumCarousel7d from "./album_compoents/albumCarousel7d";
+const miniUserId = 81881;
+
+const route = useRoute();
+// 监听页面滚动,更新当前激活的锚点
+const handleScroll = () => {
+    const scrollPosition = window.scrollY; // 加上头部偏移量
+    farmWorkList.value.forEach((_, index) => {
+        const section = document.getElementById(`section${index}`);
+        if (section && section.offsetTop <= scrollPosition) {
+            activeSection.value = index;
+        }
+    });
+};
+
+// 防抖函数
+function debounce(func, wait) {
+    let timeout;
+    return function (...args) {
+        const later = () => {
+            clearTimeout(timeout);
+            func.apply(this, args);
+        };
+        clearTimeout(timeout);
+        timeout = setTimeout(later, wait);
+    };
+}
+
+
+const handleRightScroll = () => {
+    activeSection.value = null;
+};
+const debouncedHandleScroll = debounce(handleScroll, 100);
+const debouncedHandleScroll2 = debounce(handleRightScroll, 500);
+// onMounted(() => {
+//     window.addEventListener("scroll", debouncedHandleScroll);
+//     // myPushChart = echarts.init(chartPushRef.value);
+//     // myPushChart.setOption(galleryLine);
+
+//     getFarmDetail(farmId);
+//     getFarmWorkList(farmId);
+// });
+
+const handlePage = (item) =>{
+    router.push('/expert_page?name='+item.expertName)
+}
+
+const toExpert = (expertName) =>{
+    router.push('/expert_page?name='+expertName)
+}
+
+onMounted(() => {
+    sampleId.value = 766;
+    farmId.value = 88388;
+
+    // window.addEventListener("scroll", debouncedHandleScroll);
+
+    getFarmDetail(farmId.value);
+    getFarmWorkList(farmId.value);
+    getFarmLog()
+    window.addEventListener("scroll", debouncedHandleScroll);
+});
+
+const sampleId = ref(null);
+const farmId = ref(null);
+
+
+// 移除滚动事件监听
+onUnmounted(() => {
+    window.removeEventListener("scroll", debouncedHandleScroll);
+});
+
+// 果园日志
+const indicatorChartData = ref({})
+const getFarmLog = () => {
+    VE_API.farm.fetchFarmLog({id: "96966", farmId: 766}).then(({data,code}) => {
+      if(code === 0){
+        indicatorChartData.value = data
+        eventBus.emit('chart:updateOption', indicatorChartData.value)
+      } else {
+        indicatorChartData.value = {}
+      }
+    })
+}
+
+// 农场详情
+const farmDetail = ref({});
+const getFarmDetail = (id) => {
+    VE_API.farm.fetchFarmDetail({ id }).then(({ data }) => {
+        farmDetail.value = data;
+    });
+};
+
+// 农事列表
+const farmWorkList = ref([]);
+const getFarmWorkList = (farmId) => {
+    VE_API.farm.fetchFarmWorkList({ farmId }).then(({ data }) => {
+        getSecondLastIndex(data);
+        // farmWorkList.value = data;
+        nextTick(() => {
+            initScrollToSection(secondLastIndex.value);
+        });
+        setTimeout(() => {
+            startFlashing();
+        }, 100);
+    });
+};
+
+const router = useRouter();
+
+const activeSection = ref(0);
+
+const scrollContainer = ref(null);
+// 点击左侧菜单项,滚动到对应内容
+const scrollToSection = (index) => {
+    //   const section = farmWorkList.value[index];
+    const sectionElement = document.getElementById(`section${index}`);
+    if (sectionElement) {
+        sectionElement.scrollIntoView({ behavior: "smooth" });
+        activeSection.value = index;
+        setTimeout(() => {
+            // 获取滚动容器的当前滚动位置
+            let scrollTop = scrollContainer.value.scrollTop;
+            scrollTop += 44; // 加多 44px,头部的高度
+            // 滚动至目标位置
+            scrollContainer.value.scrollTop = scrollTop;
+        }, 300);
+    }
+};
+
+// 点击左侧菜单项,滚动到对应内容
+const initScrollToSection = (index) => {
+    const menuElement = document.getElementById(`menu${index}`);
+    if (menuElement) {
+        menuElement.scrollIntoView({ behavior: "smooth" });
+        activeSection.value = index;
+        setTimeout(() => {
+            scrollToSection(index);
+        }, 0);
+    }
+};
+const secondLastIndex = ref(-1); // 初始化索引值
+const getSecondLastIndex = (data) => {
+    // 过滤出 status 为 2 的项
+    const filteredItems = data.filter((item) => item.status === 1);
+    // 获取倒数第二项
+    if (filteredItems.length >= 1) {
+        const secondLastItem = filteredItems[filteredItems.length - 1];
+        // 获取该项在原数组中的索引
+        secondLastIndex.value = data.indexOf(secondLastItem);
+
+        const lastTwoItems = filteredItems.slice(-1);
+        farmWorkList.value = data.map((item) => {
+            // 如果是最后两项之一,则添加 feature 属性
+            if (lastTwoItems.includes(item)) {
+                return { ...item, hasBg: true };
+            }
+            return item;
+        });
+    } else {
+        secondLastIndex.value = -1; // 如果没有足够的项,设置为 -1
+    }
+};
+
+const isFlashing = ref(false);
+const startFlashing = () => {
+    isFlashing.value = true;
+
+    // 使用 setTimeout 在一段时间后停止闪动
+    setTimeout(() => {
+        isFlashing.value = false;
+    }, 3000); // 3 秒后停止闪动
+};
+
+// 显示详情
+const showDetail = (card) => {
+    const pageParams = {
+        activeIndex: activeSection.value,
+        card,
+    };
+    // eventBus.emit("detailDialog:showDialog", pageParams);
+    if (card.orderStatus || card.orderStatus === 0) {
+        router.push({ path: "/work_detail", query: { data: JSON.stringify(card), recordId: null } });
+    }
+    if (card.status === 0 && (card.reCheck === 1 || card.execute === 4)) {
+        // 农事成效
+        router.push({ path: "/ns_recrod_effect", query: { data: JSON.stringify(card) } });
+    }
+};
+
+const farmName = ref("")
+//用户果园数据
+const userFarmData = ref([]);
+const initUserGardenData = () => {
+    VE_API.garden.userGarden({ userId: miniUserId, show3dFarm: false }).then(({ data }) => {
+        userFarmData.value = data;
+        const arr = data.filter(item =>item.organId===farmId.value)
+        farmName.value = arr[0].organId
+    });
+};
+
+function addNsRecord(){
+    router.push({
+      path: "/edit_ns_record",
+      query: { recordId: null },
+    });
+}
+</script>
+
+<style lang="scss" scoped>
+.album-page {
+    position: relative;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    background: #011217;
+
+    .album-wrap {
+        width: 100%;
+        height: 100%;
+        
+        .slot-right {
+        width: 70%;
+        cursor: pointer;
+        display: flex;
+        flex-direction: row;
+        justify-content: right;
+        align-items: center;
+        padding-right: 12px;
+        .btn {
+            cursor: pointer;
+            background: url("@/assets/img/tabs_btn3.png") no-repeat center center /
+            100% 100%;
+            margin-right: 5px;
+            color: rgba(180,255,251,.8);
+            font-weight: bold;
+            font-size: 15px;
+            width: 92px;
+            height: 32px;
+            text-align: center;
+            line-height: 32px;
+        }
+        }
+        .barrage-title {
+            height: 44px;
+            line-height: 44px;
+            text-align: center;
+            font-size: 17px;
+            font-weight: bold;
+            border: 1px solid #f5f5f5;
+            width: 100%;
+            background: #fff;
+            position: fixed;
+            top: 0;
+            left: 0;
+            z-index: 9;
+            .title-icon {
+                cursor: pointer;
+                position: absolute;
+                left: 16px;
+                top: 13px;
+            }
+        }
+        .album-content {
+            height: 100%;
+            // overflow: auto;
+            // margin-top: 45px;
+            .album-fixed {
+                position: fixed;
+                top: 0;
+                z-index: 10;
+                background: #fff;
+                width: 100%;
+            }
+            .album-top {
+                display: flex;
+                padding: 12px 10px 16px 10px;
+                width: 100%;
+                overflow: hidden;
+                box-sizing: border-box;
+                .album-img {
+                    img {
+                        width: 78px;
+                        height: 78px;
+                        object-fit: cover;
+                        border-radius: 6px;
+                    }
+                }
+                .garden-content {
+                    position: relative;
+                    padding-left: 12px;
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: center;
+                    .report_btn{
+                      position: absolute;
+                      bottom: 0px;
+                      right: 0px;
+                      z-index: 1000;
+                      width: 40px;
+                      height: 40px;
+                      display: flex;
+                      flex-direction: column;
+                      align-items: center;
+                      justify-content: center;
+                      font-size: 8px;
+                      color:#ffffff;
+                      background: rgba(30, 34, 83, 0.44);
+                      border-radius: 5px;
+                    }
+                    .garden-name {
+                        font-weight: bold;
+                        font-size: 16px;
+                        color: #000000;
+                        display: flex;
+                        align-items: center;
+                        flex-wrap: wrap;
+                        .tag-wrap {
+                            display: flex;
+                            flex-wrap: wrap;
+                            flex: 1;
+                        }
+                        .type-tag {
+                            margin-top: 2px;
+                            font-weight: normal;
+                            margin-left: 4px;
+                            padding: 0 6px;
+                            height: 21px;
+                            line-height: 21px;
+                            background: rgba(255, 149, 61, 0.32);
+                            border-radius: 2px;
+                            // border: 0.5px solid #FF953D;
+                            font-size: 12px;
+                            color: #fc8a2c;
+                            &.item-type {
+                                background: #f0e0fe;
+                                color: #ac4dff;
+                            }
+                        }
+                    }
+                    .garden-text {
+                        display: flex;
+                        align-items: flex-end;
+                        justify-content: space-between;
+                        .edit-btn {
+                            border: 1px solid #F0AC37;
+                            border-radius: 5px;
+                            color: #F0AC37;
+                            text-align: center;
+                            padding: 4px 2px;
+                            font-size: 9px;
+                            font-weight: bold;
+                            box-sizing: border-box;
+                            background: rgba(33, 153, 248, 0.22);
+                        }
+                    }
+                    .garden-desc {
+                        padding-top: 4px;
+                        font-size: 13px;
+                        color: #666666;
+                    }
+                }
+            }
+            .album-camera {
+                width: 100%;
+                padding: 0 10px 10px;
+                box-sizing: border-box;
+                position: relative;
+                .tips-text {
+                    position: absolute;
+                    left: calc(50% + 20px);
+                    top: 8px;
+                    height: 17px;
+                    padding: 1px 5px 2px 8px;
+                    color: #fff;
+                    font-size: 12px;
+                    border-radius: 24px 20px 20px 0;
+                    line-height: 18px;
+                    background: linear-gradient(45deg, #00d4ff, #008eff);
+                }
+                img {
+                    width: 100%;
+                }
+            }
+            .album-bottom {
+                display: flex;
+                // height: calc(100% - 106px);
+                position: relative;
+                background: rgba(73, 73, 73, 0.3);
+                height: 100%;
+                overflow: hidden;
+
+                .export-btn {
+                    position: fixed;
+                    z-index: 999;
+                    left: 0;
+                    bottom: 0px;
+                    height: 60px;
+                    padding-top: 10px;
+                    background: #f2f2f4;
+                    .btn-item {
+                        font-size: 14px;
+                        color: #fff;
+                        display: flex;
+                        align-items: center;
+                        justify-content: center;
+                        background: linear-gradient(45deg, #00d4ff, #008eff);
+                        width: 90px;
+                        height: 36px;
+                    }
+                    img {
+                        width: 24px;
+                    }
+                }
+                .album-l {
+                    width: 90px;
+                    height: 100%;
+                    overflow: auto;
+                    background: rgba(73, 73, 73, 0.31);
+                    border-radius: 0 4px 4px 0;
+                    .menu-icon {
+                        width: 16px;
+                        margin-right: 4px;
+                    }
+                    .menu-name {
+                        max-width: 80px;
+                        white-space: nowrap;
+                        overflow: hidden;
+                        text-overflow: ellipsis;
+                    }
+                    ::v-deep {
+                        .el-menu {
+                            background: transparent;
+                            border-right: 0;
+                            .el-menu-item {
+                                height: 32px;
+                                line-height: 32px;
+                                justify-content: center;
+                                color: #CECECE;
+                                margin: 10px 0;
+                                padding: 4px;
+                                &:hover {
+                                    background: none;
+                                }
+                                &.is-active {
+                                    background: transparent;
+                                    color: #F0AC37;
+                                    .menu-name {
+                                        // color: #666666;
+                                    }
+                                }
+                                &.active {
+                                    background: #232323;
+                                    color: #F0AC37;
+                                    .menu-name {
+                                        color: #F0AC37;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    .noTime {
+                        // color: #cccccc;
+                    }
+                    .now {
+                        color: rgba(33, 153, 248, 1);
+                    }
+                    .onTime {
+                        color: rgba(255, 212, 137, 0.6);
+                    }
+                }
+                ::v-deep {
+                    .el-affix {
+                        background: transparent;
+                        border-radius: 0 4px 4px 0;
+                        width: 90px !important;
+                        & > div {
+                            height: 100%;
+                        }
+                    }
+                }
+                .album-r {
+                    padding: 2px 10px 10px 10px;
+                    flex: 1;
+                    background: #232323;
+                    height: 100%;
+                    overflow: auto;
+                    .common-btn {
+                        background: #F0AC37;
+                        border-radius: 4px;
+                        color: #fff;
+                        padding: 8px 10px;
+                        text-align: center;
+                    }
+                    .card-wrap {
+                        border: 1px solid #dddddd;
+                        border-radius: 8px;
+                        padding: 8px 8px;
+                        position: relative;
+                        z-index: 1;
+                        // margin-top: -20px;
+                        &.noCarousel {
+                            padding: 0;
+                            // margin-top: 12px;
+                            // padding-top: 10px;
+                            border: none;
+                            .card-top .card-link {
+                                top: 24px;
+                            }
+                        }
+                        &.done {
+                            padding: 0;
+                            border: none;
+                        }
+                    }
+                    .card-content {
+                        // background: #001917;
+                        border-radius: 8px;
+                        padding: 0 8px 8px 8px;
+                        border: 1px solid rgba(255, 218, 102, 0.3);
+                        background: rgba(105, 73, 13, 0.1);
+                        &.push {
+                            padding: 0 8px 12px 8px;
+                            // margin-bottom: 8px;
+                            // border: 1px solid rgba(102, 102, 102, 0.38);
+                        }
+                        &.hasBg {
+                            border: 1px solid #494949;
+                            padding: 0 8px 12px 8px;
+                            background: transparent;
+                            // border: 1px solid #045151;
+                            .card-top {
+                                border-bottom: none;
+                                padding-bottom: 0;
+                            }
+                        }
+                        .card-top {
+                            padding-bottom: 12px;
+                            // border-bottom: 1px solid rgba(255, 238, 208, 0.2);
+                            position: relative;
+                            &.noChart {
+                                border-bottom: 0;
+                                padding-bottom: 0;
+                            }
+                            .card-title {
+                                display: flex;
+                                align-items: center;
+                                padding-bottom: 8px;
+                                .serve-btn {
+                                    padding: 3px 10px;
+                                    background: #F0AC37;
+                                    border-radius: 22px;
+                                    color: #fff;
+                                    text-align: center;
+                                }
+                                .title-name {
+                                    color: #FFFFFF;
+                                    font-size: 20px;
+                                    padding-right: 8px;
+                                    font-weight: 600;
+                                }
+                                img {
+                                    width: 24px;
+                                }
+
+                                .status-tag {
+                                    color: #f3a302;
+                                    font-size: 12px;
+                                    padding: 2px 6px;
+                                    border-radius: 2px;
+                                    background: rgba(243, 163, 2, 0.2);
+                                    border: 1px solid transparent;
+                                    &.blue {
+                                        color: #F0AC37;
+                                        background: rgba(33, 153, 248, 0.2);
+                                    }
+                                    &.good-wrap {
+                                        margin-left: 4px;
+                                        display: flex;
+                                        align-items: center;
+                                        .good {
+                                            margin-left: 3px;
+                                            width: 13px;
+                                        }
+                                    }
+                                    &.standard {
+                                        color: #F0AC37;
+                                        background: #232323;
+                                        border-color: #F0AC37;
+                                    }
+                                    &.advice {
+                                        color: rgba(252, 167, 3, 0.9);
+                                        background: rgba(255, 220, 104, 0.38);
+                                    }
+                                    &.warning {
+                                        color: #ff4221;
+                                        background: rgba(255, 175, 160, 0.24);
+                                    }
+                                }
+                                &.serve-title {
+                                    padding: 12px 0 8px 0;
+                                    display: flex;
+                                    align-items: center;
+                                    position: relative;
+                                    .add-btn {
+                                        position: absolute;
+                                        right: -8px;
+                                        top: 0;
+                                        background: #F7BE5A;
+                                        color: #fff;
+                                        font-size: 12px;
+                                        border-radius: 0 8px 0 8px;
+                                        padding: 2px 6px;
+                                        &.unactive {
+                                            color: #9F9F9F;
+                                            background: #494949;
+                                        }
+                                        &.recheck {
+                                            background: #f3c11d;
+                                        }
+                                    }
+                                    .tag {
+                                        display: flex;
+                                        align-items: center;
+                                        color: #ff7000;
+                                        font-size: 10px;
+                                        .desc {
+                                            position: relative;
+                                            left: -4px;
+                                        }
+                                        .photo-grid {
+                                            display: flex;
+                                            .photo {
+                                                position: relative;
+                                                img {
+                                                    width: 18px; /* 示例宽度,你可以根据需要调整 */
+                                                    height: 18px;
+                                                    object-fit: cover;
+                                                    border-radius: 50%;
+                                                    display: block; /* 去除图片底部的空白间隙 */
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            .card-desc {
+                                color: #9F9F9F;
+                                font-size: 12px;
+                                line-height: 18.2px;
+                                .desc-title {
+                                    color: #494949;
+                                }
+                                .rescription-wrap {
+                                    display: flex;
+                                    align-items: center;
+                                    .detail-text {
+                                        color: #FFD489;
+                                        padding-left: 6px;
+                                    }
+                                }
+                                .rescription {
+                                    max-width: 100px;
+                                    white-space: nowrap;
+                                    overflow: hidden;
+                                    text-overflow: ellipsis;
+                                }
+                                .expert-link {
+                                    display: inline-flex;
+                                    align-items: center;
+                                    color: #F0AC37;
+                                    .icon {
+                                        padding-left: 4px;
+                                    }
+                                }
+                            }
+                            .step-box {
+                                position: absolute;
+                                right: 0px;
+                                top: 32px;
+                            }
+                            .card-link {
+                                display: flex;
+                                flex-direction: column;
+                                align-items: center;
+                                justify-content: center;
+                                color: #A46700;
+                                font-size: 12px;
+                                position: absolute;
+                                right: 6px;
+                                top: 20px;
+                                .expert-name {
+                                    background: #FFD489;
+                                    border-radius: 4px;
+                                    padding: 2px 6px 3px 10px;
+                                    margin-top: 4px;
+                                    display: flex;
+                                    align-items: center;
+                                }
+                                img {
+                                    width: 64px;
+                                }
+                                .icon {
+                                    padding-right: 2px;
+                                }
+                            }
+                        }
+                        .card-chart {
+                            padding: 8px 4px 0 4px;
+                            height: 130px;
+                            border-top: 1px solid rgba(255, 238, 208, 0.2);
+                        }
+                    }
+
+                    .done-card {
+                        margin-bottom: 10px;
+                    }
+                    .card-dom {
+                        position: relative;
+                        // top: -216px;
+                        top: -6px;
+                    }
+
+                    .over-img {
+                        transition: all 0.2s ease-in-out;
+                        position: relative;
+                        z-index: 2;
+                        ::v-deep {
+                            img {
+                                border-radius: 8px;
+                            }
+                        }
+                    }
+                    .log-wrap {
+                        padding: 4px 0 10px 0;
+                        .log-box {
+                            background: #232323;
+                            border-radius: 8px;
+                            color: #FFD489;
+                            border: 1px solid rgba(255, 212, 137, 0.3);
+                            .log-title {
+                                font-family: "PangMenZhengDao";
+                                position: relative;
+                                top: -4px;
+                                background: url("@/assets/img/gallery/log-bg.png") no-repeat top center / cover;
+                                width: 97px;
+                                height: 42px;
+                                text-align: center;
+                                font-size: 18px;
+                                color: #fff;
+                                span {
+                                    position: absolute;
+                                    top: 7px;
+                                    display: flex;
+                                    align-items: center;
+                                    justify-content: center;
+                                    width: 100%;
+                                }
+                            }
+                            .log-content {
+                                padding-bottom: 10px;
+                                .log-desc {
+                                    padding: 0 10px;
+                                    font-size: 14px;
+                                    line-height: 22px;
+                                }
+                            }
+                            .time-img {
+                                width: 100%;
+                                height: 100%;
+                                img {
+                                    width: 100%;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            .flashing {
+                position: relative;
+                &::after {
+                    content: "";
+                    position: absolute;
+                    top: 0;
+                    left: -2px;
+                    width: 100%;
+                    height: calc(100% - 2px);
+                    border: 2px solid #F0AC37;
+                    animation: flash 0.4s infinite alternate;
+                    border-radius: 8px;
+                    pointer-events: none;
+                }
+            }
+
+            @keyframes flash {
+                0% {
+                    box-shadow: 0 0 5px #F0AC37;
+                    border-color: #F0AC37;
+                }
+                100% {
+                    box-shadow: 0 0 20px #F0AC37;
+                    border: 2px solid #F0AC37;
+                }
+            }
+        }
+
+        .evaluate {
+            border-top: 1px solid rgba(255, 238, 208, 0.2);
+            color: #fff;
+            .evaluate-title {
+                padding-top: 8px;
+                font-size: 14px;
+                color: #fff;
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 8px;
+                .more {
+                    font-size: 14px;
+                    color: #fff;
+                    font-weight: 400;
+                    display: flex;
+                    align-items: center;
+                }
+            }
+            .rate {
+                display: flex;
+                justify-content: space-between;
+                ::v-deep {
+                    .el-rate {
+                        --el-rate-icon-margin: 0;
+                    }
+                    .el-rate--small .el-rate__icon {
+                        font-size: 11px;
+                    }
+                }
+                .rate-item {
+                    display: flex;
+                    align-items: center;
+                    // background: #f5f5f5;
+                    border-radius: 4px;
+                    padding: 4px 0px;
+                    font-size: 10px;
+                    .name {
+                        // margin-right: 2px;
+                        color: #fff;
+                    }
+                    .num {
+                        color: #f3c11d;
+                        margin-left: 2px;
+                    }
+                }
+            }
+            .comment {
+                margin-top: 12px;
+                .user-info {
+                    display: flex;
+                    align-items: center;
+                    margin-bottom: 2px;
+                    .user-name {
+                        font-weight: 500;
+                        margin-left: 8px;
+                        span {
+                            font-weight: 400;
+                            font-size: 12px;
+                            color: #fff;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+</style>

+ 12 - 3
src/views/home/index.vue

@@ -42,12 +42,13 @@
       </div>
       <div class="right yes-events">
         <div class="list">
-          <chart-box name="农事列表" arrow="arrow-left">
+          <chart-box name="农事列表" arrow="arrow-left" :class="{'list-wrap': rightIndex===0}">
             <template v-if="rightIndex===0">
-              <img class="tabs" src="@/assets/images/home/ns-tabs.png" alt="">
+              <album></album>
+              <!-- <img class="tabs" src="@/assets/images/home/ns-tabs.png" alt="">
               <div class="img-box">
                 <img @click="handleAct(item)" v-for="item in 2" :key="item" :src="require(`@/assets/images/home/0${act<=2&&act==item?item+'-act':item}.png`)" alt="">
-              </div>
+              </div> -->
             </template>
             <template v-if="rightIndex===1">
               <div class="img-box1">
@@ -111,6 +112,7 @@ import {useStore} from "vuex";
 import RegionLayer from "./map/regionLayer";
 import eventBus from "@/api/eventBus";
 import AlbumCarousel from "./album_compoents/albumCarousel.vue";
+import album from "./album/index.vue";
 import PdfDialog from "../../components/PdfDialog"
 let store = useStore()
 const components = {
@@ -463,6 +465,13 @@ const toFilePage = () => {
         .mt{
           margin-top: -12px;
         }
+        .list-wrap {
+          ::v-deep {
+            .chart-content {
+              padding: 16px 0 0 0;
+            }
+          }
+        }
       }
     }
     .overflow{

+ 40 - 0
src/views/workDetail/areaMap.js

@@ -0,0 +1,40 @@
+import Layer from "ol/layer/Vector";
+import config from "@/api/config.js";
+import * as KMap from "@/utils/ol-map/KMap";
+import Stroke from "ol/style/Stroke";
+import * as util from "@/common/ol_common.js";
+import Style from "ol/style/Style";
+import Icon from "ol/style/Icon";
+import {Point } from 'ol/geom';
+import Feature from "ol/Feature";
+import WKT from "ol/format/WKT";
+
+/**
+ * @description 智能巡园地图层对象
+ */
+class AreaMap {
+  constructor() {
+    let that = this;
+    let vectorStyle = new KMap.VectorStyle();
+    this.vectorStyle = vectorStyle;
+  }
+
+  initMap(location, target) {
+    let level = 16;
+    let coordinate = util.wktCastGeom(location).getFirstCoordinate();
+    this.kmap = new KMap.Map( target, level, coordinate[0], coordinate[1], null, 1, 22 ,"vec", true, true);
+  }
+
+  // 重新渲染地图
+  updateMap() {
+    setTimeout(() => {
+      this.kmap.map.updateSize()
+    }, 1000);
+  }
+
+  fit(geomOrExtent, padding) {
+    this.kmap.fit(geomOrExtent, padding);
+  }
+}
+
+export default AreaMap;

+ 630 - 0
src/views/workDetail/components/boxClass.scss

@@ -0,0 +1,630 @@
+.work-item {
+    margin-bottom: 12px;
+    &.last-enter {
+        .work-line .line-box::after {
+            height: calc(100% - 26px);
+        }
+    }
+    position: relative;
+    &.has-bottom-btn {
+        margin-bottom: 52px;
+
+        .enter-btn {
+            width: calc(100% - 22px);
+            position: absolute;
+            left: 22px;
+            bottom: -52px;
+            height: 40px;
+            line-height: 40px;
+            .btn {
+                width: 100%;
+                height: 40px;
+                line-height: 40px;
+            }
+        }
+    }
+    &.dark {
+        .work-info {
+            background: #fff;
+            border-color: rgba(218, 218, 218, 0.57);
+            .card-item-title {
+                color: #C3C3C3;
+                .dotted {
+                    background: #C3C3C3;
+                }
+            }
+            .service-name {
+                color: #888888;
+            }
+            .item-table ::v-deep {
+                border-color: rgba(218, 218, 218, 0.57);
+                border-bottom: 0;
+                .el-table .el-table__header th.el-table__cell {
+                    background: #F9F9F9 !important;
+                }
+                .el-table tr {
+                    background: #fff;
+                }
+                .el-table thead {
+                    color: #CCCCCC;
+                }
+                .el-table td.el-table__cell {
+                    border-color: #DADADA;
+                }
+                &.has-sub-title .el-table__header .el-table__cell:first-child {
+                    border-color: rgba(218, 218, 218, 0.57);
+                }
+                &.has-sub-title .el-table,
+                &.has-sub-title .el-table__header,
+                &.has-sub-title .el-table td.el-table__cell,
+                &.has-sub-title .el-table__row .el-table__cell:first-child {
+                    border-color: rgba(218, 218, 218, 0.57);
+                }
+                &.has-sub-title .el-table__row .el-table__cell:first-child {
+                    border-color: rgba(218, 218, 218, 0.57);
+                    background: #FAFAFA;
+                }
+                &.has-sub-title {
+                    // border: none;
+                    border: 1px solid rgba(218, 218, 218, 0.57);
+                    .el-table, .el-table__header {
+                        border-bottom: 0;
+                    }
+                }
+                &.has-sub-title .remark-text {
+                    border-color: rgba(218, 218, 218, 0.57);
+                }
+                &.no-wrap-table {
+                    border: none;
+                }
+            }
+            .prescription-box .prescription-item .sub-name {
+                color: rgba(102, 102, 102);
+            }
+            .step-3-info .step-info-item .info-serve-r {
+                color: rgba(102, 102, 102, 0.4);
+                background: rgba(102, 102, 102, 0.05);
+            }
+            .prescription-box {
+                .desc {
+                    color: rgba(0, 0, 0, 0.3);
+                    background: #FAFAFA;
+                    span {
+                        color: #BBBBBB;
+                    }
+                }
+            }
+        }
+        .prescription-result {
+            // border: 0.5px solid rgba(218, 218, 218, 0.57);
+            border: none;
+            .result-index {
+                background: #DDDDDD;
+            }
+            .result-title .title-item .name {
+                color: #CCCCCC;
+            }
+            .result-title .title-item .value {
+                color: #AAAAAA;
+            }
+        }
+        .work-line {
+            .line-box::after {
+                background: #C3C3C3;
+            }
+            .line-dotted {
+                background: #DDDDDD;
+                .dotted-inset {
+                    background: #C3C3C3;
+                }
+            }
+        }
+    }
+    .work-line {
+        position: absolute;
+        left: 0;
+        top: 20px;
+        height: 100%;
+        .line-box {
+            position: relative;
+            height: 100%;
+            &::after {
+                content: "";
+                position: absolute;
+                top: 4.5px;
+                left: 5px;
+                width: 1px;
+                height: calc(100% + 20px);
+                background: #2199f8;
+            }
+        }
+        .line-dotted {
+            width: 10px;
+            height: 10px;
+            background: rgba(33, 153, 248, 0.2);
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            .dotted-inset {
+                width: 7px;
+                height: 7px;
+                background: #2199f8;
+                border-radius: 50%;
+            }
+        }
+    }
+
+    .card-item-title {
+        display: inline-flex;
+        align-items: center;
+        color: rgba(0, 0, 0, 0.8);
+        font-size: 16px;
+        .dotted {
+            display: block;
+            width: 3px;
+            height: 10px;
+            border-radius: 20px;
+            background: #2199f8;
+            margin-right: 10px;
+        }
+        .real-text {
+            padding-left: 4px;
+            font-size: 12px;
+            color: #2199F8;
+        }
+    }
+    .work-info {
+        margin-left: 22px;
+        background: #232323;
+        border: 1px solid rgba(33, 153, 248, 0.57);
+        border-radius: 5px;
+        padding: 12px 0 12px 10px;
+        position: relative;
+        .step-box {
+            position: absolute;
+            right: 10px;
+            top: 30px;
+        }
+        .todo-img {
+            position: absolute;
+            top: -1px;
+            right: -1px;
+            width: 46px;
+        }
+        .done-img {
+            position: absolute;
+            top: -1px;
+            right: -1px;
+            width: 30px;
+        }
+        .work-info-conetnt {
+            padding-top: 6px;
+        }
+        .info-item {
+            display: flex;
+            align-items: center;
+            .info-name {
+                color: rgba(0, 0, 0, 0.2);
+                flex: none;
+            }
+            .info-value {
+                color: rgba(0, 0, 0, 0.4);
+                padding-left: 12px;
+                display: flex;
+                align-items: center;
+                &.primary-color {
+                    color: #2199f8;
+                }
+                .select-value {
+                    padding-left: 4px;
+                }
+                ::v-deep {
+                    .el-select__wrapper {
+                        box-shadow: 0 0 0 1px #2199F8 inset;
+                        background: none;
+                        height: 24px;
+                        min-height: 24px;
+                        padding: 4px 8px;
+                        .el-select__placeholder {
+                            color: #2199F8;
+                        }
+                        .el-select__caret {
+                            color: #2199F8;
+                        }
+                    }
+                    .date-info {
+                        height: 24px;
+                        .el-input__wrapper {
+                            box-shadow: 0 0 0 1px #2199F8 inset;
+                            background: none;
+                            .el-input__prefix {
+                                color: #2199F8;
+                            }
+                            .el-input__inner {
+                                color: #2199F8;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        .info-item + .info-item {
+            // margin-top: 2px;
+            &.time-picker {
+                margin-top: 6px;
+            }
+        }
+
+        .prescription-box {
+            position: relative;
+            padding-right: 10px;
+            .desc {
+                margin-top: 12px;
+                border-radius: 4px;
+                color: #999999;
+                padding: 10px;
+                background: #fff;
+                span {
+                    color: #2199f8;
+                }
+            }
+            .service-wrap {
+                padding: 12px 0;
+                .service-item {
+                    margin-top: 8px;
+                    display: flex;
+                    align-items: center;
+                    border: 1px solid #DDDDDD;
+                    padding: 0 12px;
+                    height: 48px;
+                    color: #000000;
+                    font-size: 14px;
+                    border-radius: 8px;
+                    .service-img {
+                        width: 32px;
+                        margin-right: 8px;
+                        border-radius: 50%;
+                    }
+                    .service-c {
+                        flex: 1;
+                        text-align: center;
+                        color: #666666;
+                    }
+                }
+                .service-bottom {
+                    padding-top: 12px;
+                    .service-btn {
+                        padding-top: 12px;
+                        width: 100%;
+                        .btn {
+                            width: 100%;
+                        }
+                    }
+                }
+            }
+            .bottom-map {
+                width: 100%;
+                height: 150px;
+                border-radius: 8px;
+            }
+            .prescription-item {
+                padding-top: 12px;
+                .prescription-chart {
+                    margin-top: 6px;
+                    border: 1px solid rgba(196, 196, 196, 0.8);
+                    border-radius: 5px;
+                    padding: 4px 0 8px 0;
+                    position: relative;
+                    z-index: 1;
+                    ::v-deep {
+                        .card-chart {
+                            width: calc(100vw - 60px - 40px);
+                            height: 160px;
+                        }
+                    }
+                }
+                .prescription-map {
+                    border: none;
+                }
+                .sub-name {
+                    color: #2199f8;
+                    font-size: 14px;
+                }
+                .edit-select {
+                    ::v-deep {
+                        .el-select__wrapper {
+                            box-shadow: 0 0 0 1px #2199F8 inset;
+                            background: #f1f9ff;
+                        }
+                    }
+                }
+                .recipe-item {
+                    .recipe-form {
+                        padding-top: 8px;
+                    }
+                    .form-box {
+                        border: 1px solid #2199F8;
+                        border-radius: 4px;
+                        padding: 20px 10px;
+                        width: 100%;
+                        position: relative;
+                        .form-index {
+                            position: absolute;
+                            left: 0;
+                            top: 0;
+                            padding: 0 6px;
+                            background: #2199F8;
+                            border-radius: 4px 0 4px 0;
+                            height: 18px;
+                            line-height: 18px;
+                            font-size: 12px;
+                            color: #fff;
+                        }
+                        .box-item {
+                            display: flex;
+                            align-items: center;
+                            justify-content: space-between;
+                            color: rgba(0, 0, 0, 0.4);
+                            .r-text {
+                                width: 140px;
+                                text-align: center;
+                            }
+                        }
+                        .input-box {
+                            padding: 8px 0 12px 0;
+
+                            ::v-deep {
+                                .el-input__wrapper {
+                                    box-shadow: 0 0 0 1px #2199F8 inset;
+                                }
+                                .el-input__inner {
+                                    color: #2199F8;
+                                }
+                            }
+                        }
+                        .text-center {
+                            ::v-deep {
+                                .el-input__inner {
+                                    text-align: center;
+                                }
+                            }
+                        }
+                        .action-btn {
+                            .btn {
+                                flex: 1;
+                                width: calc(50% - 6px);
+                            }
+                            .delete-btn {
+                                color: #FF943D;
+                                background: rgba(255, 148, 61, 0.1);
+                                border: 1px solid #FF943D;
+                            }
+                        }
+                        .btn-group {
+                            padding-top: 12px;
+                        }
+                        .sub-item {
+                            padding-left: 10px;
+                            .has-sub {
+                                display: flex;
+                                flex-direction: column;
+                                align-items: center;
+                                .main-name {
+                                    line-height: 20px;
+                                }
+                                .sub-name {
+                                    font-size: 10px;
+                                    color: rgba(129, 129, 129, 0.5);
+                                    line-height: 14px;
+                                }
+                            }
+                            .colunm-sub {
+                                display: flex;
+                                align-items: center;
+                                .sub-name {
+                                    font-size: 10px;
+                                    color: rgba(129, 129, 129, 0.5);
+                                }
+                            }
+                            .r-text {
+                                width: 132px;
+                                text-align: center;
+                                font-size: 14px;
+                                color: #474747;
+                            }
+                            .price {
+                                ::v-deep {
+                                    .el-input__wrapper {
+                                        box-shadow: 0 0 0 1px #2199F8 inset;
+                                    }
+                                    .el-input__inner {
+                                        color: #2199F8;
+                                    }
+                                }
+                            }
+                        }
+                        .form-title {
+                            color: #008EFF;
+                            font-size: 14px;
+                            padding-top: 6px;
+                        }
+                        .box-item + .box-item {
+                            margin-top: 8px;
+                        }
+                        ::v-deep {
+                            .el-select__wrapper {
+                                box-shadow: 0 0 0 1px #2199F8 inset;
+                                .el-select__placeholder {
+                                    color: #2199F8;
+                                    text-align: center;
+                                }
+                            }
+                        }
+                    }
+                    .form-box + .form-box {
+                        margin-top: 8px;
+                    }
+
+                }
+                .one-btn {
+                    width: 100%;
+                }
+            }
+        }
+
+        .step-3-info {
+            padding-top: 12px;
+            .step-info-item {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                .info-serve-l {
+                    flex: 1;
+                    display: flex;
+                    align-items: center;
+                    color: rgba(0, 0, 0, 0.4);
+                    .type-name {
+                        flex: none;
+                    }
+                    .img-list {
+                        display: flex;
+                        .img-item {
+                            position: relative;
+                            .serve-img {
+                                width: 32px;
+                                height: 32px;
+                                object-fit: cover;
+                                border-radius: 50%;
+                            }
+                            .checked-icon {
+                                position: absolute;
+                                bottom: 0;
+                                right: 0;
+                                width: 14px;
+                            }
+                        }
+                        .img-item + .img-item {
+                            margin-left: 10px;
+                        }
+                    }
+                }
+                .info-serve-r {
+                    flex: none;
+                    color: #2199f8;
+                    font-size: 12px;
+                    border-radius: 20px;
+                    padding: 4px 12px;
+                    background: rgba(33, 153, 248, 0.12);
+                }
+            }
+            .step-info-item + .step-info-item {
+                padding-top: 12px;
+            }
+        }
+
+        .item-table {
+            margin-top: 8px;
+            border: 1px solid rgba(33, 153, 248, 0.5);
+            // border-top: 0;
+            // border-bottom: 0;
+            border-radius: 4px;
+            color: #333333;
+
+            &.has-wrap-table {
+                ::v-deep {
+                    .el-table__row {
+                        .el-table__cell:first-child {
+                            background: #E0F2FF;
+                            border-right: 1px solid rgba(33, 153, 248, 0.5);
+                        }
+
+                    }
+                    .el-table__header {
+                        .el-table__cell:first-child{
+                                border-right: 1px solid rgba(33, 153, 248, 0.5);
+                            }
+                    }
+                }
+            }
+
+            &.has-sub-title {
+                border: none;
+                ::v-deep {
+                    .el-table__cell {
+                        text-align: center;
+                    }
+                    .el-table__row {
+                        // .el-table__cell:first-child {
+                        //     background: #E0F2FF;
+                        //     border-right: 1px solid rgba(33, 153, 248, 0.5);
+                        // }
+
+                    }
+                    .el-table__header {
+                        border-bottom: 1px solid rgba(33, 153, 248, 0.5);
+                        // .el-table__cell:first-child{
+                        //         border-right: 1px solid rgba(33, 153, 248, 0.5);
+                        //     }
+                    }
+                    .el-table {
+                        border: 1px solid rgba(33, 153, 248, 0.5);
+                        border-radius: 4px 4px 0 0 ;
+                        // border-bottom: 0;
+                    }
+                }
+                .remark-text {
+                    border: 1px solid rgba(33, 153, 248, 0.5);
+                    color: rgba(0, 0, 0, 0.3);
+                    height: 36px;
+                    line-height: 36px;
+                    padding: 0 6px;
+                    border-radius: 0 0 4px 4px;
+                    border-top: 0;
+                }
+            }
+
+            &.no-wrap-table {
+                border: none;
+            }
+            .table-l-title {
+                height: 100%;
+            }
+            ::v-deep {
+                .el-table {
+                    color: rgba(0, 0, 0, 0.4);
+                }
+                .el-table th.el-table__cell {
+                    padding: 4px 3px;
+                }
+                .el-table thead {
+                    color: rgba(33, 153, 248, 0.6);
+                    font-weight: normal !important;
+                }
+                .el-table .el-table__header th.el-table__cell {
+                    background: #e0f2ff !important;
+                }
+                .el-table tr {
+                    background: #f1f9ff;
+                }
+                .el-table--default .el-table__cell {
+                    padding: 4px 0;
+                }
+                .el-table--default .cell {
+                    padding: 0px 2px;
+                    font-size: 11px;
+                    font-weight: normal;
+                }
+                .el-table__header, .el-table__body {
+                    width: auto !important;
+                }
+                .el-table__header .cell {
+                    line-height: 13px;
+                }
+                .el-table td.el-table__cell {
+                    border-color: rgba(33, 153, 248, 0.5);
+                }
+            }
+        }
+    }
+}

+ 190 - 0
src/views/workDetail/components/chart.vue

@@ -0,0 +1,190 @@
+<template>
+    <div class="card-chart" ref="chartRef"></div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import * as echarts from "echarts";
+import { galleryIndicatorLine } from "./chartSeting";
+import { deepClone } from "@/common/commonFun";
+
+const props = defineProps({
+    type: {
+        type: String,
+        default: "",
+    },
+    chartJson: {
+        type: String,
+        required: true,
+    },
+});
+
+let myChart = null;
+const chartRef = ref();
+const chartData = ref(JSON.parse(props.chartJson))
+
+let chartDataArr = ref([
+    { date: "9/1", value: 40 },
+    { date: "9/7", value: 60 },
+    { date: "9/13", value: 63 },
+    { date: "9/19", value: 68 },
+    { date: "9/25", value: 80, feature: true },
+    { date: "10/1", value: 85, feature: true },
+    { date: "10/7", value: 88, feature: true },
+]);
+
+const getFeatureSerie = (forecastData) => {
+  return {
+    name: "",
+    type: "line",
+    data: forecastData, // 取后三天的数据
+    itemStyle: {
+      color: "#CECECE", // 设置数据点颜色
+    },
+    lineStyle: {
+      type: "dashed", // 设置线条为虚线
+      width: 2, // 设置线条宽度
+      color: "#CECECE", // 设置线条颜色
+    },
+  }
+}
+
+onMounted(() => {
+    myChart = echarts.init(chartRef.value);
+    const options = deepClone(galleryIndicatorLine);
+    if (props.type === "feature") {
+        processData();
+        const series = []
+        chartData.value.datas.forEach(({name,values}) => {
+          // 数据处理
+          let currentData = [];
+          let forecastData = [];
+          for(let i = 0; i < chartData.value.dates.length; i++){
+            let item = chartData.value.dates[i];
+            if (item.feature) {
+              forecastData.push([item.date, Number(values[i])]);
+            } else {
+              currentData.push([item.date, Number(values[i])]);
+            }
+          }
+          // 追加实际数据的最后一组数据
+          forecastData.unshift(currentData[currentData.length - 1]);
+          options.series.forEach((item) => {
+            if(item.name === name){
+              item.data = currentData;
+              series.push(item)
+              series.push(getFeatureSerie(forecastData))
+            }
+          })
+        })
+        options.series = series;
+    } else {
+        // 完成农事
+        let seriesData = chartData.value.map((m) => {
+            return Number(m.value);
+        });
+
+        options.series = [
+            {
+                data: seriesData,
+                type: "line",
+                symbolSize: 6,
+                itemStyle: {
+                    color: "#2199F8", // 设置数据点颜色为红色
+                },
+            },
+        ];
+    }
+
+    let labels = chartData.value.dates.map((m) => {
+        return m.date;
+    });
+
+    options.xAxis.data = labels;
+    // if (props.type === "normal") {
+    //     options.series = [
+    //         {
+    //             data: [40, 58, 62, 64, 68, 74, 80],
+    //             type: "line",
+    //             symbolSize: 6,
+    //             itemStyle: {
+    //                 color: "#2199F8", // 设置数据点颜色为红色
+    //             },
+    //         },
+    //     ];
+    // } else {
+    //     // options.series = [
+    //     //     {
+    //     //         data: [40, 58, 62, 64, 68, 74, 80],
+    //     //         type: "line",
+    //     //         symbolSize: 6,
+
+    //     //         itemStyle: {
+    //     //             color: "#CECECE", // 设置数据点颜色
+    //     //         },
+    //     //         lineStyle: {
+    //     //             type: "dashed", // 设置线条为虚线
+    //     //             width: 2, // 设置线条宽度
+    //     //             color: "#CECECE", // 设置线条颜色
+    //     //         },
+    //     //     },
+    //     // ];
+    //     options.series = [
+    //         {
+    //             name: "实线数据",
+    //             type: "line",
+    //             data: currentData, // 取前四天(包括当天)的数据
+    //             lineStyle: {
+    //                 type: "solid", // 这里可以省略,因为我们在下面通过itemStyle为每个点设置样式时会更具体
+    //             },
+    //             itemStyle: {
+    //                 color: "#2199F8", // 设置数据点颜色为红色
+    //             },
+    //         },
+    //         {
+    //             name: "虚线数据",
+    //             type: "line",
+    //             data: forecastData, // 取后三天的数据
+    //             itemStyle: {
+    //                 color: "#CECECE", // 设置数据点颜色
+    //             },
+    //             lineStyle: {
+    //                 type: "dashed", // 设置线条为虚线
+    //                 width: 2, // 设置线条宽度
+    //                 color: "#CECECE", // 设置线条颜色
+    //             },
+    //         },
+    //     ];
+    // }
+    myChart.setOption(options);
+});
+
+const processData = () => {
+    const today = new Date();
+    const datePattern = /(\d+)\/(\d+)/; // 日期格式匹配,假设为MM/DD
+    let dates = chartData.value.dates;
+    chartData.value.dates = []
+    dates.forEach((date) => {
+        let item = {date: date, feature: false};
+        const match = item.date.match(datePattern);
+        if (match) {
+            const month = parseInt(match[1], 10);
+            const day = parseInt(match[2], 10);
+            const itemDate = new Date(today.getFullYear(), month - 1, day); // 月份从0开始,所以需要减1
+            if (itemDate > today) {
+                item.feature = true;
+            }
+            chartData.value.dates.push(item);
+        }
+    });
+};
+</script>
+
+<style lang="scss" scoped>
+.card-chart {
+    padding: 8px 4px 0 4px;
+    width: 100%;
+    height: 130px;
+    box-sizing: border-box;
+}
+</style>

+ 227 - 0
src/views/workDetail/components/chartSeting.js

@@ -0,0 +1,227 @@
+import * as echarts from "echarts";
+import { color } from "highcharts";
+
+export const galleryLine = {
+    tooltip: {
+        trigger: "axis",
+    },
+    grid: {
+        top: 28,
+        left: 10,
+        right: 26,
+        bottom: 0,
+        containLabel: true,
+    },
+    xAxis: {
+        name: "时间",
+        nameLocation: "end",
+        nameGap: 4,
+        nameTextStyle: {
+            fontSize: 10,
+            color: "#666666"
+        },
+        type: 'category',
+        // 分割线
+        axisTick: {
+            show: false,
+        },
+        splitLine: {
+            show: false,
+        },
+        axisLabel: {
+            // interval: 0,
+            rotate: 15,
+            margin: 14,
+            textStyle: {
+                color: "#666666",
+                fontSize: 10
+            },
+        },
+        data: ['9/1', '9/7', '9/13', '9/19', '9/25', '10/1', '10/7']
+    },
+    yAxis: {
+        name: "xx指标",
+        nameLocation: "end",
+        type: 'value',
+        nameTextStyle: {
+            fontSize: 10,
+            color: "#666666"
+        },
+        axisTick: {
+            show: false,
+        },
+        axisLine: {
+            show: true,
+            lineStyle: {
+                color: "#BBBBBB",
+            },
+        },
+        axisLabel: {
+            textStyle: {
+                color: "#666666",
+                fontSize: 10
+            },
+        },
+        // 分割线
+        splitLine: {
+            show: false,
+            lineStyle: {
+                color: "rgba(255, 255, 255, 0.1)",
+            },
+        },
+    },
+    series: [
+        {
+            data: [40, 58, 62, 64, 68, 74, 80],
+            type: 'line',
+            symbolSize: 6,
+            itemStyle: {
+                color: '#2199F8' // 设置数据点颜色为红色
+            },
+
+            // itemStyle: {
+            //     color: '#CECECE' // 设置数据点颜色
+            // },
+            // lineStyle: {
+            //     type: 'dashed', // 设置线条为虚线
+            //     width: 2,       // 设置线条宽度
+            //     color: '#CECECE' // 设置线条颜色
+            // }
+        }
+    ],
+}
+
+export const galleryIndicatorLine = {
+    tooltip: {
+        // trigger: "axis",
+    },
+    legend: {
+        icon: 'roundRect',
+        top: 0,
+        right: 0,
+        orient: 'horizontal',
+        itemWidth: 6,
+        itemHeight: 2,
+        itemGap: 3,
+        textStyle: {
+          align: 'left',
+          fontSize: 10,
+        },
+        data: [
+          { name: '花带叶率', textStyle: { color: '#66B9FB' }, itemStyle: {color: "#66B9FB"} },
+          { name: '病虫害率', textStyle: { color: '#fbb366' }, itemStyle: {color: "#fbb366"} },
+          { name: '来花率', textStyle: { color: '#ec66fb' }, itemStyle: {color: "#ec66fb"} },
+        ]
+      },
+    grid: {
+        top: 30,
+        left: 8,
+        right: 18,
+        bottom: 4,
+        containLabel: true,
+    },
+    xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        // 分割线
+        axisTick: {
+            show: false,
+        },
+        splitLine: {
+            show: false,
+        },
+        axisLabel: {
+            interval: 0,
+            rotate: 40,
+            margin: 14,
+            textStyle: {
+                color: "#666666",
+                fontSize: 10
+            },
+            formatter: function (value) {
+                // 获取后 5 位字符(假设日期格式始终为 'YYYY-MM-DD')
+                let shortenedValue = value.slice(-5);
+
+                // 自定义处理省略号(由于 ECharts 不直接支持,需手动处理)
+                // 此处简单判断,若原长度超过 10(即 'YYYY-MM-DD' 格式),则添加省略号
+                if (value.length > 10) {
+                    shortenedValue = '...' + shortenedValue;
+                }
+
+                return shortenedValue;
+            }
+        },
+        axisLine: {
+            lineStyle: {
+                color: "#ddd",
+            }
+        },
+        data: ['9/1', '9/7', '9/13', '9/19', '9/25', '10/1', '10/7']
+    },
+    yAxis: [{
+        type: 'value',
+        offset: 6,
+        axisTick: {
+            show: false,
+        },
+        axisLine: {
+            show: false,
+        },
+        axisLabel: {
+            align: "center",
+            textStyle: {
+                color: "#666666",
+                fontSize: 12
+            },
+        },
+        // 分割线
+        splitLine: {
+            lineStyle: {
+                type: [4, 3],
+                dashOffset: 5,
+                color: "#DDDDDD",
+            },
+        },
+        // min: 0, // 最小值固定为0
+        // max: 100, // 最大值固定为100
+        // splitNumber: 5, // 设置5个分割段
+    }],
+    series: [
+        {
+            name: "花带叶率",
+            data: [40, 28, 22, 64, 28, 24, 40],
+            type: 'line',
+            smooth: true,
+            symbol: "none",
+            itemStyle: { color: "#FF7219" },
+            lineStyle: {
+                color: "#FF7219", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+        {
+            name: "病虫害率",
+            data: [80, 88, 92, 84, 88, 74, 80],
+            type: 'line',
+            symbol: "none",
+            smooth: true,
+            itemStyle: { color: "#FFB82E" },
+            lineStyle: {
+                color: "#FFB82E", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+        {
+            name: "来花率",
+            data: [80, 88, 92, 84, 88, 74, 80],
+            type: 'line',
+            symbol: "none",
+            smooth: true,
+            itemStyle: { color: "#ec66fb" },
+            lineStyle: {
+                color: "#ec66fb", // 折线颜色为红色
+                width: 2, // 线条宽度
+            },
+        },
+    ],
+}

+ 173 - 0
src/views/workDetail/components/commentBox.vue

@@ -0,0 +1,173 @@
+<template>
+    <div class="work-item" v-if="commentData?.expertServiceRating">
+        <div class="work-line">
+            <div class="line-box">
+                <div class="line-dotted">
+                    <div class="dotted-inset"></div>
+                </div>
+            </div>
+        </div>
+        <div class="work-info">
+            <img v-if="progress === 5" class="done-img" src="@/assets/img/gallery/done.png" />
+            <div class="card-item-title"><span class="dotted"></span>用户评价</div>
+            <div class="evaluate" v-if="commentData?.expertServiceRating">
+                <div class="comment">
+                    <div class="user-info">
+                        <el-avatar
+                            class="avatar"
+                            :size="40"
+                            :src="commentData.icon"
+                        />
+                        <div class="user-name">
+                            <div>{{ commentData.commentUserName }}</div>
+                            <span>{{ commentData.commentDate }}</span>
+                        </div>
+                    </div>
+                    <div class="rate">
+                        <div class="rate-item">
+                            <span class="name">专家</span>
+                            <el-rate disabled v-model="rateValue" size="small" />
+                        </div>
+                        <div class="line"></div>
+                        <div class="rate-item">
+                            <span class="name">农资</span>
+                            <el-rate disabled v-model="rateValue2" size="small" />
+                        </div>
+                        <div class="line"></div>
+                        <div class="rate-item">
+                            <span class="name">农服</span>
+                            <el-rate disabled v-model="rateValue3" size="small" />
+                        </div>
+                    </div>
+                    <div class="text van-multi-ellipsis--l2">
+                        {{ commentData.detailedComment }}
+                    </div>
+                </div>
+            </div>
+            <div class="comment-btn">
+                <el-button class="outline-btn btn">查看更多</el-button>
+                <el-button class="btn" type="primary" @click="toComment">去评价</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from 'vue';
+import { useRouter } from "vue-router";
+const props = defineProps({
+    progress: {
+        type: Number,
+        required: false,
+    },
+    orderId: {
+        required: true
+    },
+    commentData: {},
+    farmWorkName: {}
+})
+
+const rateValue = ref(5)
+const rateValue2 = ref(5)
+const rateValue3 = ref(5)
+
+onMounted(() => {
+    rateValue.value = props.commentData?.expertServiceRating
+    rateValue2.value = props.commentData?.agricultureServiceRating
+    rateValue3.value = props.commentData?.farmServiceRating
+})
+
+const router = useRouter();
+const toComment = () => {
+    router.push({ path: "/work_comment", query: {orderId: props.orderId, farmName: props.farmWorkName} });
+}
+</script>
+
+<style lang="scss" scoped>
+@import "./boxClass.scss";
+.evaluate {
+    // padding-top: 16px;
+    background: #fff;
+    border-radius: 5px;
+    padding: 4px 8px 10px 8px;
+    margin-right: 8px;
+    .evaluate-title {
+        font-size: 16px;
+        font-weight: 500;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin-bottom: 8px;
+        .more {
+            font-size: 14px;
+            color: #999999;
+            font-weight: 400;
+            display: flex;
+            align-items: center;
+        }
+    }
+    .rate {
+        display: flex;
+        justify-content: space-between;
+        ::v-deep {
+            .el-rate {
+                --el-rate-icon-margin: 0;
+            }
+            .el-rate--small .el-rate__icon {
+                font-size: 12px;
+            }
+        }
+        .rate-item {
+            display: flex;
+            align-items: center;
+            // background: #f5f5f5;
+            border-radius: 4px;
+            padding: 4px 0px;
+            font-size: 11px;
+            .name {
+                margin-right: 2px;
+                color: #666666;
+                position: relative;
+                top: 2px;
+            }
+            .num {
+                color: #f3c11d;
+                margin-left: 2px;
+            }
+        }
+        .line {
+            width: 1px;
+            height: 12px;
+            background: #CDD7E1;
+            position: relative;
+            top: 10px;
+        }
+    }
+    .comment {
+        margin-top: 12px;
+        .user-info {
+            display: flex;
+            align-items: center;
+            margin-bottom: 2px;
+            .user-name {
+                font-weight: 500;
+                margin-left: 8px;
+                span {
+                    font-weight: 400;
+                    font-size: 12px;
+                    color: #999999;
+                }
+            }
+        }
+    }
+}
+
+.comment-btn {
+    display: flex;
+    padding-top: 12px;
+    margin-right: 10px;
+    .btn {
+        flex: 1;
+    }
+}
+</style>

+ 174 - 0
src/views/workDetail/components/enterBox.vue

@@ -0,0 +1,174 @@
+<template>
+    <div class="work-item " :class="{ dark: progress === 0 || progress === 1, 'has-bottom-btn': progress === 2 || progress === 3 || progress === 4, 'last-enter':  progress !== 5}">
+        <div class="work-line">
+            <div class="line-box">
+                <div class="line-dotted">
+                    <div class="dotted-inset"></div>
+                </div>
+            </div>
+        </div>
+        <div class="work-info">
+            <img v-if="progress === 2" class="todo-img" src="@/assets/img/gallery/enter.png" />
+            <img v-if="progress === 3 || progress === 4" class="done-img" src="@/assets/img/gallery/done.png" />
+            <div class="card-item-title"><span class="dotted"></span>参与主体</div>
+            <div class="prescription-box">
+                <div class="prescription-item">
+                    <div class="enter-info" v-if="progress === 4">
+                        <div class="enter-l">亩数价格:<span>1500元/亩</span></div>
+                        <div class="enter-r">服务亩数:<span>1000亩</span></div>
+                    </div>
+                    <div class="enter-top" v-if="progress === 2 || progress === 3">
+                        <div class="enter-info">
+                            <div class="enter-l">当前价格:<span>1亩/1500元</span></div>
+                            <div class="enter-r">亩数:<span>500亩/1000亩</span></div>
+                        </div>
+                        <div class="price-text">
+                            还差<span>500亩</span>可享<span>九折</span>价格
+                        </div>
+                    </div>
+                    <div class="step-3-info">
+                        <div class="step-info-item">
+                            <div class="info-serve-l">
+                                <span class="type-name">农户:</span>
+                                <div class="img-list" v-if="progress === 4">
+                                    <template v-for="item in users" :key="item.id">
+                                      <div class="img-item" v-if="item.userType == 1 && item.joinStatus === 1">
+                                        <img class="serve-img" :src="item.icon" />
+                                        <!-- <img class="checked-icon" v-if="item.joinStatus === 1" src="@/assets/img/gallery/checked.png" /> -->
+                                      </div>
+                                    </template>
+                                </div>
+                                <div class="img-list" v-else>
+                                    <template v-for="item in users" :key="item.id">
+                                      <div class="img-item" v-if="item.userType == 1">
+                                        <img class="serve-img" :src="item.icon" />
+                                        <img class="checked-icon" v-if="item.joinStatus === 1" src="@/assets/img/gallery/checked.png" />
+                                      </div>
+                                    </template>
+                                </div>
+                            </div>
+                            <div class="info-serve-r" @click="showDialog(users)">查看详情</div>
+                        </div>
+                        <div class="step-info-item">
+                            <div class="info-serve-l">
+                                <span class="type-name">农服:</span>
+                                <div class="img-list" v-if="progress === 4">
+                                  <template v-for="item in users" :key="item.id">
+                                    <div class="img-item" v-if="item.userType == 2 && item.joinStatus === 1">
+                                        <img class="serve-img" :src="item.icon" />
+                                    </div>
+                                  </template>
+                                </div>
+                                <div class="img-list" v-else>
+                                  <template v-for="item in users" :key="item.id">
+                                    <div class="img-item" v-if="item.userType == 2">
+                                        <img class="serve-img" :src="item.icon" />
+                                        <img class="checked-icon" v-if="item.joinStatus === 1" src="@/assets/img/gallery/checked.png" />
+                                    </div>
+                                  </template>
+                                </div>
+                            </div>
+                            <div class="info-serve-r" @click="showDialog(users)">查看详情</div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div class="enter-btn" v-if="progress === 2 && (ROLETYPE === '2')">
+            <el-button class="btn" type="primary" @click="lockWork(3)">开始执行</el-button>
+        </div>
+        <div class="enter-btn" v-if="progress === 3 && (ROLETYPE === '2')">
+            <el-button class="btn" type="primary" @click="lockWork(4)">执行完成</el-button>
+        </div>
+        <div class="enter-btn" v-if="progress === 4 && (ROLETYPE === '2')">
+            <el-button class="btn" type="primary" @click="lockWork(0)">邀请评价</el-button>
+        </div>
+        <!-- 农户、农服 -->
+        <div class="enter-btn" v-if="progress === 2 && (ROLETYPE === '0' || ROLETYPE === '1')">
+            <el-button class="btn" type="primary" @click="joinWork">确认加入</el-button>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import eventBus from "@/api/eventBus";
+
+import { useStore } from "vuex";
+const store = useStore();
+const ROLETYPE = store.state.app.curRole
+
+const props = defineProps({
+    progress: {
+        type: Number,
+        required: false,
+    },
+    users: {
+        type: Array,
+        required: false,
+    },
+    orderId: {
+        type: [Number, String],
+        required: false,
+    },
+});
+
+// 农户农服加入
+const joinWork = () => {
+    console.log('props.orderId', props.orderId);
+    VE_API.order.fetchFarmerJoin({orderId: props.orderId}).then(({ code }) => {
+        if (code == 0) {
+            console.log('加入成功');
+            eventBus.emit('discover:submitForm')
+            window.location.reload()
+        }
+    });
+}
+
+// 农资锁单
+const lockWork = (orderStatus) => {
+    const data = {
+        orderId: props.orderId,
+        orderStatus,
+    };
+    VE_API.order.confirm(data).then(({ code }) => {
+        if (code == 0) {
+            console.log('成功');
+            eventBus.emit('discover:submitForm')
+            window.location.reload()
+        }
+    });
+}
+
+const showDialog = (data) => {
+    eventBus.emit("enterBox:showDialog", data)
+}
+</script>
+
+<style lang="scss" scoped>
+@import "./boxClass.scss";
+.enter-info {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #BBBBBB;
+    font-size: 14px;
+    span {
+        color: #008EFF;
+    }
+}
+.enter-top {
+    .price-text {
+        margin-top: 5px;
+        font-size: 14px;
+        padding: 2px 10px;
+        border-radius: 30px 0 0 30px;
+        color: rgba(255, 255, 255, 0.6);
+        background: linear-gradient( to right, #2199F8, #FFFFFF);
+        span {
+            padding: 0 4px;
+            color: #fff;
+        }
+    }
+}
+</style>

+ 103 - 0
src/views/workDetail/components/executeBlueRegion.vue

@@ -0,0 +1,103 @@
+<template>
+    <div class="bottom-map" ref="areaRef"></div>
+</template>
+
+<script setup>
+import Layer from "ol/layer/Vector";
+import config from "@/api/config.js";
+import { newAreaFeature } from "@/common/util";
+import * as KMap from "@/utils/ol-map/KMap";
+import Stroke from "ol/style/Stroke";
+import * as util from "@/common/ol_common.js";
+import Style from "ol/style/Style";
+import Icon from "ol/style/Icon";
+import { Point } from "ol/geom";
+import Feature from "ol/Feature";
+import WKT from "ol/format/WKT";
+import { onMounted, ref } from "vue";
+
+const props = defineProps({
+    executeBlueZones: {
+        type: String,
+        required: true,
+    },
+    executeBlueZones: {}
+});
+
+const areaRef = ref(null);
+
+const initMap = () => {
+    let executeBlueZones = props.executeBlueZones
+    let vectorStyle = new KMap.VectorStyle();
+    // 位置图标
+    let blueRegionLayer = new KMap.VectorLayer("blueRegionLayer", 99999, {
+        minZoom: 1,
+        maxZoom: 22,
+    });
+    let level = 16;
+    let coordinate = util.wktCastGeom("POINT(113.6142086995688 23.585836479509055)").getFirstCoordinate();
+    let kmap = new KMap.Map(areaRef.value, level, coordinate[0], coordinate[1], null, 1, 22, "img", false, false);
+    kmap.addLayer(blueRegionLayer.layer);
+    let xyz = config.base_img_url + "map/lby/{z}/{x}/{y}.png";
+    kmap.addXYZLayer(xyz, { minZoom: 8, maxZoom: 22 }, 2);
+    // let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
+    // kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
+
+    
+    VE_API.farm.blueRegionList({ farmId: 766 }).then(({ data, code }) => {
+        console.log("dat22222a", executeBlueZones);
+        for (let item of data) {
+          executeBlueZones.map((zone) => {
+                if (zone.id === item.blueZoneCode) {
+                    item.wkt = item.geom;
+                    item.id = item.blueZoneCode;
+                    let feature = newAreaFeature(item);
+                    blueRegionLayer.addFeature(feature);
+                }
+            });
+        }
+        kmap.fit(blueRegionLayer.source.getExtent(), [0, 0, 0, 0]);
+        kmap.addLayer(blueRegionLayer.layer);
+    });
+
+    // VE_API.temp.tempBlueRegionList().then(({ data, code }) => {
+    //     console.log("ddd", data);
+    //     for (let item of data) {
+    //         let feature = newAreaFeature(item);
+    //         let blueRegionLevel = executeBlueRegion.find((executeBlueRegionItem) => {
+    //             return executeBlueRegionItem.id === parseInt(item.id);
+    //         });
+    //         let fillColor = "";
+    //         let strokeColor = "#2199F8";
+    //         if (blueRegionLevel && blueRegionLevel.level == 1) {
+    //             fillColor = strokeColor + "40";
+    //             strokeColor += "40";
+    //         } else if (blueRegionLevel && blueRegionLevel.level == 2) {
+    //             fillColor = strokeColor + "90";
+    //             strokeColor += "90";
+    //         } else {
+    //             fillColor = "rgba(111,110,110,0.50)";
+    //             strokeColor = "rgba(111,110,110,0.50)";
+    //         }
+    //         feature.setStyle(vectorStyle.getPolygonStyle(fillColor, strokeColor, 1));
+    //         blueRegionLayer.addFeature(feature);
+    //     }
+    //     kmap.fit(blueRegionLayer.source.getExtent(), [0, 0, 0, 0]);
+    // });
+};
+
+const getBlueRegion = (executeBlueZones) => {
+};
+
+onMounted(() => {
+    initMap();
+});
+</script>
+
+<style scoped>
+.bottom-map {
+    width: 100%;
+    height: 150px;
+    border-radius: 8px;
+}
+</style>

+ 180 - 0
src/views/workDetail/components/executionProgress.vue

@@ -0,0 +1,180 @@
+<template>
+    <div class="work-item">
+        <div class="work-line">
+            <div class="line-box">
+                <div class="line-dotted">
+                    <div class="dotted-inset"></div>
+                </div>
+            </div>
+        </div>
+        <div class="work-info">
+            <img class="todo-img" src="@/assets/img/gallery/todo.png" />
+            <img v-if="progress === 5" class="done-img" src="@/assets/img/gallery/done.png" />
+            <div class="card-item-title"><span class="dotted"></span>执行进度</div>
+            <div class="execution-content">
+                <div class="execution-text">正在实时记录当前执行轨迹</div>
+                <div class="execution-map">
+                    <div class="bottom-map" ref="executionRef"></div>
+                </div>
+
+                <div class="execution-text">执行农事区域</div>
+
+                <div class="prescription-chart prescription-map">
+                    <execute-blue-region :executeBlueRegion="executionData.executeBlueRegion"></execute-blue-region>
+                </div>
+            </div>
+            <div class="comment-btn">
+                <el-button class="outline-btn btn">结束任务</el-button>
+                <el-button class="outline-btn btn">暂停任务</el-button>
+                <el-button class="btn" type="primary">分享协作</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import ExecuteBlueRegion from "./executeBlueRegion";
+import * as util from "@/common/ol_common.js";
+import config from "@/api/config.js";
+import LineString from "ol/geom/LineString.js";
+import * as KMap from "@/utils/ol-map/KMap";
+import { Vector as VectorSource } from "ol/source.js";
+import { Vector as LayerVector } from "ol/layer";
+import Icon from "ol/style/Icon";
+import { newPoint } from "@/views/util";
+import Style from "ol/style/Style";
+import Stroke from "ol/style/Stroke";
+import Feature from "ol/Feature";
+const props = defineProps({
+    progress: {
+        type: Number,
+        required: false,
+    },
+    executionData: {},
+});
+
+const executionRef = ref(null);
+
+onMounted(() => {
+    initMap();
+});
+
+const initMap = () => {
+    let vectorStyle = new KMap.VectorStyle();
+    let level = 16;
+    let coordinate = util.wktCastGeom("POINT(113.6142086995688 23.585836479509055)").getFirstCoordinate();
+    let kmap = new KMap.Map(executionRef.value, level, coordinate[0], coordinate[1], null, 1, 22, "img");
+
+    let xyz = config.base_img_url + "map/lby/{z}/{x}/{y}.png";
+    kmap.addXYZLayer(xyz, { minZoom: 8, maxZoom: 22 }, 2);
+    // let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
+    // kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
+    setLine(kmap);
+    setMapPoint(kmap);
+};
+
+// 航线
+const setLine = (kmap) => {
+    let flyLineLayer = null;
+    flyLineLayer && flyLineLayer.getSource().clear();
+    // 定义线的坐标
+    let lineCoordinates = [
+        [113.61497958300038, 23.58644139996892], // 起点
+        [113.61508237571593, 23.586383341120325],
+        [113.61486060995014, 23.586364305432255],
+        [113.61483681534014, 23.58650707309272],
+    ];
+
+    // 创建线的几何对象
+    let lineGeometry = new LineString(lineCoordinates);
+
+    // 创建特征对象
+    let lineFeature = new Feature({
+        geometry: lineGeometry,
+    });
+
+    // 创建矢量图层并添加到地图
+    flyLineLayer = new LayerVector({
+        source: new VectorSource({
+            features: [lineFeature], // 添加线特征
+        }),
+        style: new Style({
+            stroke: new Stroke({
+                color: "#fff",
+                width: 3,
+                lineDash: [10, 8], // 10像素的实线,5像素的空白
+            }), // 线条颜色
+        }),
+        zIndex: 1000,
+    });
+    console.log('extent', flyLineLayer.getSource().getExtent());
+    kmap.fit(flyLineLayer.getSource().getExtent(),[20,20,20,20])
+    kmap.addLayer(flyLineLayer);
+};
+
+const setMapPoint = (kmap) => {
+    // 位置图标
+    let pointLayer = new KMap.VectorLayer("pointLayer", 99999, {
+        style: () => {
+            return new Style({
+                image: new Icon({
+                    src: require("@/assets/img/gallery/star.png"),
+                    scale: 0.5,
+                }),
+            });
+        },
+        minZoom: 1,
+        maxZoom: 22,
+    });
+    let currentPointLayer = new KMap.VectorLayer("pointLayer", 99999, {
+        style: () => {
+            return new Style({
+                image: new Icon({
+                    src: require("@/assets/img/gallery/current.png"),
+                    scale: 0.5,
+                }),
+            });
+        },
+        minZoom: 1,
+        maxZoom: 22,
+    });
+    // 图标点位
+    let point = { wkt: "POINT(113.61483681534014 23.58650707309272)" };
+    let mapPoint = newPoint(point, "wkt");
+    pointLayer.addFeature(mapPoint);
+    kmap.addLayer(pointLayer.layer);
+    // 当前图标
+    let currentPoint = { wkt: "POINT(113.61497958300038 23.58644139996892)" };
+    let mapCurrentPoint = newPoint(currentPoint, "wkt");
+    currentPointLayer.addFeature(mapCurrentPoint);
+    kmap.addLayer(currentPointLayer.layer);
+};
+</script>
+
+<style lang="scss" scoped>
+@import "./boxClass.scss";
+
+.comment-btn {
+    display: flex;
+    padding-top: 12px;
+    margin-right: 10px;
+    .btn {
+        flex: 1;
+    }
+}
+
+.execution-text {
+    font-size: 14px;
+    padding: 8px 0 10px 0;
+    color: #2199f8;
+}
+.execution-map {
+    padding-bottom: 4px;
+    .bottom-map {
+        width: 100%;
+        height: 150px;
+        border-radius: 8px;
+    }
+}
+</style>

+ 596 - 0
src/views/workDetail/components/prescriptionBox.vue

@@ -0,0 +1,596 @@
+<template>
+    <!-- 专家处方 -->
+    <div class="work-item" :class="{ dark: prescriptioData?.progress === 0 && ROLETYPE != '3' }">
+        <div class="work-line">
+            <div class="line-box">
+                <div class="line-dotted">
+                    <div class="dotted-inset"></div>
+                </div>
+            </div>
+        </div>
+        <div class="work-info">
+            <img v-if="prescriptioData?.progress === 0 && ROLETYPE == '3'" class="todo-img" src="@/assets/img/gallery/todo.png" />
+            <img v-if="prescriptioData?.progress !== 0" class="done-img" src="@/assets/img/gallery/done.png" />
+            <div class="card-item-title"><span class="dotted"></span>专家处方</div>
+            <div class="prescription-box">
+                <div class="step-box">
+                    <step-box :card="infoItem" :isDone="infoItem.status === 0 && (infoItem.reCheck === 1 || infoItem.execute === 4)" v-if="infoItem.orderStatus || infoItem.status === 0 || infoItem.orderStatus === 0"></step-box>
+                </div>
+                <!-- <div class="blur-bg">
+                        <div class="lock-btn">解锁智能处方</div>
+                    </div> -->
+                <div class="work-info-conetnt">
+                    <div class="info-item">
+                        <div class="info-name">农事编号</div>
+                        <div class="info-value">{{ infoItem.orderId }}</div>
+                    </div>
+                    <div class="info-item">
+                        <div class="info-name">服务亩数</div>
+                        <div class="info-value">{{ infoItem.area }}亩</div>
+                    </div>
+                    <div class="info-item">
+                        <div class="info-name">服务区域</div>
+                        <div class="info-value">{{ infoItem.serviceRegion }}</div>
+                    </div>
+                    <div v-if="prescriptioData?.progress === 0 && ROLETYPE == '3'">
+                        <div class="info-item">
+                            <div class="info-name">触发条件</div>
+                            <div class="info-value">
+                                {{ infoItem.farmWorkLibName }}
+                                <div class="select-value">
+                                    <el-select v-model="infoItem.usageMode" placeholder="请选择" style="width: 80px">
+                                        <el-option v-for="num in 9" :key="num" :value="20 + (num - 1) * 5 + '%'">
+                                            {{ 20 + (num - 1) * 5 }}%
+                                        </el-option>
+                                    </el-select>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="info-item time-picker">
+                            <div class="info-name">执行时间</div>
+                            <div class="info-value">
+                                <el-date-picker
+                                    style="width: 150px"
+                                    value-format="YYYY-MM-DD"
+                                    v-model="infoItem.executeDate"
+                                    type="date"
+                                    :clearable="false"
+                                    class="date-info"
+                                    placeholder="选择日期"
+                                />
+                            </div>
+                        </div>
+                    </div>
+                    <div v-else>
+                        <div class="info-item">
+                            <div class="info-name">触发条件</div>
+                            <div class="info-value">{{ infoItem.farmWorkLibName }}</div>
+                        </div>
+                        <div class="info-item">
+                            <div class="info-name">执行时间</div>
+                            <div class="info-value">{{ infoItem.executeDate }}</div>
+                        </div>
+                    </div>
+                    <!-- <div class="info-item">
+                        <li class="info-name">执行时间</li>
+                        <div class="info-value primary-color">2025.01.12</div>
+                    </div> -->
+                </div>
+                <div class="desc">
+                    {{ prescriptioData?.expertPrescription }}
+                </div>
+                <div class="prescription-item" v-if="prescriptioData?.progress === 0 && ROLETYPE == '3'">
+                    <div class="recipe-box">
+                        <div class="recipe-title">
+                            <div class="recipe-name">编辑处方</div>
+                            <div class="recipe-btn" @click="addDomain">
+                                <el-icon><Plus /></el-icon>新增药物
+                            </div>
+                        </div>
+                        <div class="recipe-item">
+                            <div class="recipe-form">
+                                <el-form
+                                    ref="formRef"
+                                    style="max-width: 600px"
+                                    :model="dynamicValidateForm"
+                                    class="demo-dynamic"
+                                >
+                                    <el-form-item
+                                        v-for="(domain, index) in dynamicValidateForm.domains"
+                                        :key="domain.key"
+                                        :prop="'domains.' + index + '.value'"
+                                    >
+                                        <div class="form-box">
+                                            <div class="form-index">{{ formatIndex(index) }}</div>
+                                            <div class="box-item" v-if="domain.typeName">
+                                                <div class="form-l">使用功效</div>
+                                                <div class="form-r r-text">
+                                                    {{ domain.typeName }}
+                                                    <!-- <el-select
+                                                        v-model="domain.typeName"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                        >
+                                                            <el-option :label="domain.typeName" :value="domain.typeName" />
+                                                    </el-select> -->
+                                                </div>
+                                            </div>
+                                            <div class="box-item">
+                                                <div class="form-l">肥药名称</div>
+                                                <div class="form-r">
+                                                    <el-select
+                                                        @change="handlePesticideFertilizerChange(index)"
+                                                        v-model="domain.pesticideFertilizerId"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                    >
+                                                        <el-option
+                                                            v-for="item in pesticideFertilizersOptions"
+                                                            :key="item.id"
+                                                            :label="item.defaultName"
+                                                            :value="item.id"
+                                                        />
+                                                    </el-select>
+                                                </div>
+                                            </div>
+                                            <div class="form-title">人工方式</div>
+                                            <div class="box-item sub-item">
+                                                <div class="form-l has-sub">
+                                                    <div class="main-name">肥药配比</div>
+                                                    <div class="sub-name">(药剂:兑水量)</div>
+                                                </div>
+                                                <div class="form-r input-box text-center">
+                                                    <el-input
+                                                        v-model="domain.ratio"
+                                                        style="width: 140px"
+                                                        placeholder="请输入"
+                                                    />
+                                                    <!-- <el-select
+                                                        v-model="domain.value3"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                        >
+                                                            <el-option label="1ml:5000ml" value="1" />
+                                                            <el-option label="1ml:10000ml" value="3" />
+                                                    </el-select> -->
+                                                </div>
+                                            </div>
+                                            <div class="box-item sub-item">
+                                                <div class="form-l">施用方式</div>
+                                                <div class="form-r">
+                                                    <el-select
+                                                        v-model="domain.usageMode"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                    >
+                                                        <el-option
+                                                            v-for="(usage, uId) in domain.usageModeList"
+                                                            :key="uId"
+                                                            :label="usage"
+                                                            :value="usage"
+                                                        />
+                                                    </el-select>
+                                                </div>
+                                            </div>
+                                            <div class="box-item sub-item">
+                                                <div class="form-l has-sub">
+                                                    <div class="main-name">单亩用量</div>
+                                                    <div class="sub-name">(亩数:药剂)</div>
+                                                </div>
+                                                <div class="form-r input-box text-center">
+                                                    <el-input
+                                                        v-model="domain.muUsage"
+                                                        style="width: 140px"
+                                                        placeholder="请输入"
+                                                    />
+                                                    <!-- <el-select
+                                                        v-model="domain.value5"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                        >
+                                                            <el-option label="1:5000ml" value="1" />
+                                                            <el-option label="1:10000ml" value="3" />
+                                                    </el-select> -->
+                                                </div>
+                                            </div>
+                                            <div class="form-title">无人机方式</div>
+                                            <div class="box-item sub-item">
+                                                <div class="form-l has-sub">
+                                                    <div class="main-name">肥药配比</div>
+                                                    <div class="sub-name">(药剂:兑水量)</div>
+                                                </div>
+                                                <div class="form-r input-box text-center">
+                                                    <el-input
+                                                        v-model="domain.muUsage2"
+                                                        style="width: 140px"
+                                                        placeholder="请输入"
+                                                    />
+                                                    <!-- <el-select
+                                                        v-model="domain.value6"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                        >
+                                                            <el-option label="1ml:5000ml" value="1" />
+                                                            <el-option label="1ml:10000ml" value="3" />
+                                                    </el-select> -->
+                                                </div>
+                                            </div>
+                                            <!-- <div class="box-item sub-item">
+                                                <div class="form-l">施用方式
+                                                </div>
+                                                <div class="form-r">
+                                                    <el-select
+                                                        v-model="domain.value7"
+                                                        placeholder="请选择"
+                                                        style="width: 140px"
+                                                        >
+                                                            <el-option label="根部肥" value="1" />
+                                                            <el-option label="叶面肥" value="3" />
+                                                    </el-select>
+                                                </div>
+                                            </div> -->
+                                            <div class="box-item sub-item">
+                                                <div class="form-l has-sub">
+                                                    <div class="main-name">单亩用量</div>
+                                                    <div class="sub-name">(亩数:药剂)</div>
+                                                </div>
+                                                <div class="form-r input-box text-center">
+                                                    <el-input
+                                                        v-model="domain.ratio2"
+                                                        style="width: 140px"
+                                                        placeholder="请输入"
+                                                    />
+                                                </div>
+                                            </div>
+                                            <div class="input-box">
+                                                <el-input
+                                                    v-model="domain.remark"
+                                                    style="width: 100%"
+                                                    placeholder="备注:用药注意事项"
+                                                />
+                                            </div>
+                                            <div class="action-btn">
+                                                <el-button class="btn delete-btn" @click.prevent="removeDomain(domain)">
+                                                    删除
+                                                </el-button>
+                                                <el-button
+                                                    type="primary"
+                                                    class="btn"
+                                                    @click.prevent="resetItemForm(index)"
+                                                >
+                                                    重置
+                                                </el-button>
+                                            </div>
+                                        </div>
+                                    </el-form-item>
+                                </el-form>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="prescription-item" v-else>
+                    <div class="sub-title">
+                        <div class="sub-name">药物处方</div>
+                    </div>
+                    <div v-if="workType">
+                        <div
+                            class="item-table has-sub-title has-wrap-table"
+                            v-for="(item, index) in prescriptioData.pesticideFertilizers"
+                            :key="index"
+                        >
+                            <div class="prescription-result" :class="{ 'has-wrap': workType }">
+                                <div class="result-index">0{{ index + 1 }}</div>
+                                <div class="result-title">
+                                    <div class="title-item">
+                                        <div class="name">功效:</div>
+                                        <div class="value">{{ item.typeName }}</div>
+                                    </div>
+                                    <div class="title-item">
+                                        <div class="name">名称:</div>
+                                        <div class="value">{{ item.defaultName }}</div>
+                                    </div>
+                                </div>
+                                <div>
+                                    <el-table
+                                        :data="handleTableData(item)"
+                                        style="width: 100%"
+                                        :header-cell-style="{ background: '#F5F5F5' }"
+                                    >
+                                        <el-table-column label="" width="50">
+                                            <template #default="scope">
+                                                <div class="table-l-title">
+                                                    {{ scope.row.executeStyle == 1 ? "人工" : "无人机" }}
+                                                </div>
+                                            </template>
+                                        </el-table-column>
+                                        <el-table-column label="配比" width="56">
+                                            <template #default="scope">
+                                                {{ scope.row.ratio ? scope.row.ratio + scope.row.unit : "---" }}
+                                            </template>
+                                        </el-table-column>
+                                        <el-table-column prop="usageMode" label="施用方式" width="56" />
+                                        <el-table-column prop="muUsage" label="单亩用量">
+                                            <template #default="scope">
+                                                {{ scope.row.muUsage ? scope.row.muUsage + scope.row.unit : "---" }}
+                                            </template>
+                                        </el-table-column>
+                                    </el-table>
+                                </div>
+                                <div class="remark-text">备注:{{ item.remark }}</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div v-else class="item-table has-sub-title no-wrap-table">
+                        <div class="prescription-result">
+                            <el-table
+                                :data="prescriptioData.pesticideFertilizers"
+                                style="width: 100%"
+                                :header-cell-style="{ background: '#F5F5F5' }"
+                            >
+                                <el-table-column prop="typeName" label="功效" width="42" />
+                                <el-table-column prop="defaultName" label="名称"/>
+                                <el-table-column label="配比" width="52">
+                                    <template #default="scope">
+                                        {{ scope.row.ratio ? scope.row.ratio + scope.row.unit : "---" }}
+                                    </template>
+                                </el-table-column>
+                                <el-table-column prop="usageMode" label="施用方式" width="42" />
+                                <el-table-column prop="muUsage" label="单亩用量" width="42">
+                                    <template #default="scope">
+                                        {{ scope.row.muUsage ? scope.row.muUsage + scope.row.unit : "---" }}
+                                    </template>
+                                </el-table-column>
+                                <el-table-column label="执行方式" width="40">
+                                    <template #default="scope">
+                                        <div class="table-l-title">
+                                            {{ scope.row.executeStyle == 1 ? "人工" : "人工" }}
+                                        </div>
+                                    </template>
+                                </el-table-column>
+                            </el-table>
+                        </div>
+                    </div>
+                </div>
+                <div class="prescription-item">
+                    <div class="sub-title">
+                        <div class="sub-line"></div>
+                        <div class="sub-name">执行农事区域</div>
+                        <div class="sub-line"></div>
+                    </div>
+                    <div class="prescription-chart prescription-map" v-if="infoItem?.executeBlueZones">
+                        <execute-blue-region :executeBlueRegion="prescriptioData?.executeBlueRegion" :executeBlueZones="infoItem.executeBlueZones"></execute-blue-region>
+                    </div>
+                </div>
+                <div class="prescription-item" v-if="prescriptioData?.progress === 0">
+                    <div class="sub-title">
+                        <div class="sub-line"></div>
+                        <div class="sub-name">指标动态</div>
+                        <div class="sub-line"></div>
+                    </div>
+                    <div class="prescription-chart">
+                        <chart :key="1" type="feature" :chartJson="prescriptioData.indexJson"></chart>
+                    </div>
+                </div>
+                <div class="prescription-item" v-if="prescriptioData?.progress === 0 && ROLETYPE == '3'">
+                    <el-button class="one-btn" type="primary" @click="submitForm(formRef)">确认下发</el-button>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import eventBus from "@/api/eventBus";
+import chart from "./chart.vue";
+import { onActivated, onMounted, reactive, ref } from "vue";
+import { useStore } from "vuex";
+import ExecuteBlueRegion from "./executeBlueRegion";
+import stepBox from "@/components/common/stepBox.vue";
+
+const props = defineProps({
+    prescriptioData: {
+        type: Object,
+    },
+});
+
+const infoItem = ref({});
+
+const store = useStore();
+const ROLETYPE = store.state.app.curRole;
+
+// 农事类型--区分表格显示字段等
+const workType = ref(false);
+
+let pesticideFertilizersOptions = ref([
+    {
+        id: "null",
+        name: "芸苔素内酯 15000倍",
+        typeName: "30",
+        defaultRatio: null,
+        defaultDroneRatio: null,
+        unit: 0,
+        defaultName: "调节",
+    },
+]);
+VE_API.order.pesticideFertilizers().then(({ data }) => {
+    pesticideFertilizersOptions.value = data;
+});
+
+onMounted(() => {
+    console.log(" props.prescriptioData", props.prescriptioData);
+    dynamicValidateForm.domains = props.prescriptioData?.pesticideFertilizers.map((item) => ({ ...item }));
+    // submit()
+    infoItem.value = props.prescriptioData;
+});
+
+// 表单
+const formRef = ref();
+const dynamicValidateForm = reactive({
+    domains: [
+        {
+            key: 1,
+            typeName: "",
+            muUsage: "",
+            muUsage2: "",
+            ratio: "",
+            ratio2: "",
+            remark: "",
+        },
+    ],
+});
+
+const addDomain = () => {
+    dynamicValidateForm.domains.unshift({
+        key: Date.now(),
+        muUsage: "",
+        muUsage2: "",
+        ratio: "",
+        ratio2: "",
+        remark: "",
+    });
+};
+
+const removeDomain = (item) => {
+    const index = dynamicValidateForm.domains.indexOf(item);
+    if (index !== -1) {
+        dynamicValidateForm.domains.splice(index, 1);
+    }
+};
+
+const resetItemForm = (index) => {
+    dynamicValidateForm.domains[index] = { typeName: "", muUsage: "", muUsage2: "", ratio: "", ratio2: "", remark: "" };
+};
+
+/**
+ * 选择药肥的时候修改订单中药肥pesticideFertilizerId 以外其他数据
+ * @param index
+ */
+const handlePesticideFertilizerChange = (index) => {
+    let obj = pesticideFertilizersOptions.value.filter(
+        (item) => dynamicValidateForm.domains[index].pesticideFertilizerId === item.id
+    )[0];
+    dynamicValidateForm.domains[index] = {
+        ...dynamicValidateForm.domains[index],
+        typeName: obj.typeName,
+        unit: obj.unit,
+        defaultRatio: obj.defaultRatio,
+        usageModeList: obj.usageModeList,
+        ratio: obj.defaultRatio,
+        defaultName: obj.defaultName,
+        pesticideFertilizerName: obj.name,
+    };
+    console.log(dynamicValidateForm.domains);
+};
+
+const submitForm = (formEl) => {
+    if (!formEl) return;
+    formEl.validate((valid) => {
+        if (valid) {
+            submit();
+        } else {
+            console.log("error submit!");
+        }
+    });
+};
+
+const submit = () => {
+    const data = {
+        orderId: props.prescriptioData.orderId,
+        orderStatus: 1,
+        pesticideFertilizers: dynamicValidateForm.domains,
+    };
+    VE_API.order.confirm(data).then(({ code }) => {
+        if (code == 0) {
+            console.log("专家下发处方成功");
+            eventBus.emit("discover:submitForm");
+            window.location.reload();
+        }
+    });
+};
+
+const formatIndex = (index) => {
+    return String(index + 1).padStart(2, "0");
+};
+
+const handleTableData = (item) => {
+    return [
+        {
+            typeName: item.typeName,
+            defaultName: item.defaultName,
+            executeStyle: 1,
+            ratio: item.ratio || item.defaultRatio,
+            usageMode: item.usageMode,
+            muUsage: item.muUsage,
+            unit: item.unit,
+        },
+        {
+            typeName: item.typeName,
+            defaultName: item.defaultName,
+            executeStyle: 2,
+            ratio: item.ratio2,
+            usageMode: "---",
+            muUsage: item.muUsage2,
+            unit: item.unit,
+        },
+    ];
+};
+</script>
+
+<style lang="scss" scoped>
+@import "./boxClass.scss";
+.recipe-box {
+    .recipe-title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .recipe-name {
+            color: #000000;
+            font-size: 14px;
+        }
+        .recipe-btn {
+            font-size: 12px;
+            color: #2199f8;
+        }
+    }
+}
+.prescription-result {
+    position: relative;
+    // border: 0.5px solid rgba(33, 153, 248, 0.5);
+    // border-radius: 4px;
+    // padding: 8px 10px 10px 10px;
+    &.has-wrap {
+        border: 0.5px solid rgba(33, 153, 248, 0.5);
+        border-radius: 4px;
+        padding: 8px 10px 10px 10px;
+    }
+    .result-index {
+        position: absolute;
+        left: -1px;
+        top: -1px;
+        padding: 0 8px;
+        background: #2199f8;
+        color: #fff;
+        font-size: 12px;
+        border-radius: 4px 0 4px 0;
+    }
+    .result-title {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        padding-bottom: 8px;
+        .title-item {
+            display: flex;
+            align-items: center;
+            .name {
+                color: rgba(33, 153, 248, 0.85);
+            }
+            .value {
+                color: rgba(0, 0, 0, 0.4);
+            }
+        }
+        .title-item + .title-item {
+            padding-left: 16px;
+        }
+    }
+}
+</style>

+ 263 - 0
src/views/workDetail/components/recheckBox.vue

@@ -0,0 +1,263 @@
+<template>
+    <div class="work-item last-enter">
+        <div class="work-line">
+            <div class="line-box">
+                <div class="line-dotted">
+                    <div class="dotted-inset"></div>
+                </div>
+            </div>
+        </div>
+        <div class="work-info">
+            <img class="done-img" src="@/assets/img/gallery/done.png" />
+            <div class="card-item-title"><span class="dotted"></span>复核对比</div>
+            <div class="recheck-content">
+                <div class="sub-title">报价处方</div>
+                <div class="over-img">
+                    <!-- 图片列表 -->
+                    <div class="carousel-wrapper" :style="carouselStyle">
+                        <photo-provider :photo-closable="true">
+                            <photo-consumer
+                                class="carousel-item"
+                                v-for="(photo, index) in treeImgList"
+                                :key="index"
+                                :src="base_img_url2 + photo.filename + resize"
+                            >
+                                <div class="img-two">
+                                    <img :id="'img1' + index" :src="base_img_url2 + photo.filename + resize" />
+                                    <img :id="'img2' + index" :src="base_img_url2 + photo.filename + resize" />
+                                    <div class="tag tag-l">执行前</div>
+                                    <div class="tag tag-r">执行后</div>
+                                </div>
+                            </photo-consumer>
+                        </photo-provider>
+                    </div>
+
+                    <!-- 左右箭头 -->
+                    <div @click="prev" class="arrow left-arrow">
+                        <el-icon color="#F0D09C" size="20"><ArrowLeftBold /></el-icon>
+                    </div>
+                    <div @click="next" class="arrow right-arrow">
+                        <el-icon color="#F0D09C" size="20"><ArrowRightBold /></el-icon>
+                    </div>
+                </div>
+
+                <div class="compare-b">
+                    <div class="sub-title">指标动态</div>
+                    <div class="compare-chart">
+                        <!-- <chart
+                                        :key="'compare' + workItem.id"
+                                        :indexName="workItem.indexName"
+                                        :type="workItem.status !== 1 ? 'normal' : 'feature'"
+                                        :chartData="workItem.indexChart"
+                                    ></chart> -->
+
+                        <chart :key="1" type="feature" :chartJson="indexJson"></chart>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { base_img_url2, resize } from "@/api/config";
+import chart from "./chart.vue";
+const props = defineProps({
+    progress: {
+        type: Number,
+        required: false,
+    },
+    indexJson: {},
+});
+
+const treeImgList = ref([
+    {
+        filename:
+            "b68f2665-fd4c-4f54-a875-56ba882c85a1/cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_202501201000_001_cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_20250120100535_0012_Z_code-ws0y1mmqk4sp.jpeg",
+    },
+    {
+        filename:
+            "b68f2665-fd4c-4f54-a875-56ba882c85a1/cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_202501201000_001_cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_20250120100514_0011_Z_code-ws0y1mmqz3k2.jpeg",
+    },
+    {
+        filename:
+            "b68f2665-fd4c-4f54-a875-56ba882c85a1/cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_202501201000_001_cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_20250120100348_0006_Z_code-ws0y1mmnxuqb.jpeg",
+    },
+    {
+        filename:
+            "b68f2665-fd4c-4f54-a875-56ba882c85a1/cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_202501201000_001_cf9512f7-c55f-4cdc-ac68-3e58814fc378/DJI_20250120100217_0001_Z_code-ws0y1mkyefed.jpeg",
+    },
+]);
+
+const updateImagePosition = () => {
+    carouselStyle.value.transform = `translateX(-${currentIndex.value * 100}%)`;
+};
+const currentIndex = ref(0);
+// 计算轮播图样式
+const carouselStyle = computed(() => {
+    return {
+        transform: `translateX(-${currentIndex.value * 100}%)`,
+    };
+});
+// 下一张图片
+const next = () => {
+    // 图片总数
+    const totalImages = treeImgList.value.length;
+    currentIndex.value = (currentIndex.value + 1) % totalImages;
+    updateImagePosition();
+};
+
+// 上一张图片
+const prev = () => {
+    // 图片总数
+    const totalImages = treeImgList.value.length;
+    currentIndex.value = (currentIndex.value - 1 + totalImages) % totalImages;
+    updateImagePosition();
+};
+</script>
+
+<style lang="scss" scoped>
+@import "./boxClass.scss";
+
+.recheck-content {
+    padding-right: 10px;
+    .sub-title {
+        color: #2199f8;
+    }
+    .over-img {
+        margin-top: 8px;
+        width: 100%;
+        overflow: hidden;
+        transition: transform 0.5s ease;
+        display: flex;
+        position: relative;
+        .carousel-container {
+            flex: none;
+            // transform: translateX(-100%);
+            transition: transform 0.5s ease;
+            &.hideMap {
+                flex: none;
+                transform: translateX(0);
+                // transform: translateX(-100%);
+            }
+            position: relative;
+            width: 100%;
+            overflow: hidden;
+            margin: 0 auto;
+        }
+
+        .carousel-wrapper {
+            display: flex;
+            transition: transform 0.5s ease;
+            width: 100%;
+            height: 100%;
+        }
+
+        .carousel-item {
+            min-width: 100%;
+            flex-shrink: 0;
+            width: 100%;
+            pointer-events: auto;
+            position: relative;
+            .img-two {
+                display: flex;
+                position: relative;
+                .tag {
+                    position: absolute;
+                    bottom: 0;
+                    background: rgba(0, 0, 0, 0.6);
+                    color: #fff;
+                    font-size: 10px;
+                    padding: 2px 10px;
+                }
+                .tag-l {
+                    left: 0;
+                    border-radius: 0 5px 0 5px;
+                }
+                .tag-r {
+                    right: 0;
+                    border-radius: 5px 0 0 5px;
+                }
+            }
+        }
+
+        .carousel-item img {
+            width: 50%;
+            display: block;
+        }
+
+        .overlay {
+            position: absolute;
+            top: 0;
+            right: 0;
+            background: rgba(255, 255, 255, 0.3);
+            width: 100%;
+            height: 100%;
+            transition: width 0.5s ease-in-out;
+        }
+
+        .percentage {
+            position: absolute;
+            bottom: 0;
+            width: 100%;
+        }
+
+        .arrow {
+            position: absolute;
+            top: 50%;
+            transform: translateY(-50%);
+            background: rgba(0, 0, 0, 0.5);
+            width: 36px;
+            height: 36px;
+            border-radius: 50%;
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            cursor: pointer;
+        }
+
+        .left-arrow {
+            left: 16px;
+        }
+
+        .right-arrow {
+            right: 16px;
+        }
+        ::v-deep {
+            .el-carousel__arrow {
+                background: rgba(0, 0, 0, 0.5);
+            }
+            .PhotoConsumer {
+                width: 100%;
+                height: 100%;
+                img {
+                    width: calc(50% - 4px);
+                    height: 100%;
+                    // height: 300px;
+                    // max-height: 500px;
+                    object-fit: scale-down;
+                    border-radius: 5px;
+                }
+                img + img {
+                    padding-left: 8px;
+                }
+            }
+        }
+    }
+    .compare-b {
+        padding: 12px 0 8px 0;
+        .compare-chart {
+            margin-top: 8px;
+            background: #eaf4ff;
+            border-radius: 5px;
+            padding: 4px 0 8px 0;
+            ::v-deep {
+                .card-chart {
+                    height: 160px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 468 - 0
src/views/workDetail/components/serviceBox.vue

@@ -0,0 +1,468 @@
+<template>
+    <div class="work-item" :class="{ dark: progress === 0 }">
+        <div class="work-line">
+            <div class="line-box">
+                <div class="line-dotted">
+                    <div class="dotted-inset"></div>
+                </div>
+            </div>
+        </div>
+        <div class="work-info">
+            <img v-if="progress === 1" class="todo-img" src="@/assets/img/gallery/todo.png" />
+            <img v-if="progress === 2 || progress === 3 || progress === 4" class="done-img" src="@/assets/img/gallery/done.png" />
+            <div class="card-item-title">
+                <span class="dotted"></span>农资报价
+                <span class="real-text" v-if="progress === 2 || progress === 3 || progress === 4">(大禹科技公司)</span>
+            </div>
+            <div class="prescription-box">
+                <div class="prescription-item" v-if="progress === 1 && ROLETYPE == '2'">
+                    <div class="service-time">
+                        执行时间
+                        <div class="time-picker">
+                            <el-date-picker
+                                value-format="YYYY-MM-DD"
+                                v-model="pickerTime"
+                                type="date"
+                                placeholder="选择日期"
+                            />
+                        </div>
+                    </div>
+                    <div class="sub-title">
+                        <div class="service-name">报价处方</div>
+                    </div>
+                    <div class="recipe-item">
+                        <div class="recipe-form">
+                            <el-form ref="formRef" style="max-width: 600px" class="demo-dynamic">
+                                <el-form-item
+                                    v-for="(domain, index) in dynamicValidateForm"
+                                    :key="domain.id"
+                                    :prop="'domains.' + index + '.value'"
+                                >
+                                    <div class="form-box">
+                                        <div class="form-index">{{ formatIndex(index) }}</div>
+                                        <div class="box-item">
+                                            <div class="form-l">肥药名称</div>
+                                            <div class="form-r r-text">{{ domain.defaultName }}</div>
+                                        </div>
+                                        <div class="box-item">
+                                            <div class="form-l">使用功效</div>
+                                            <div class="form-r r-text">{{ domain.typeName }}</div>
+                                        </div>
+                                        <div class="box-item">
+                                            <div class="form-l">执行方式</div>
+                                            <div class="form-r">
+                                                <el-select
+                                                    v-model="domain.executeStyle"
+                                                    placeholder="请选择"
+                                                    style="width: 140px"
+                                                >
+                                                    <el-option label="人工" :value="1" />
+                                                    <el-option label="无人机" :value="2" />
+                                                </el-select>
+                                            </div>
+                                        </div>
+                                        <div class="box-item sub-item">
+                                            <div class="form-l colunm-sub">
+                                                <div class="main-name">肥药配比</div>
+                                                <div class="sub-name">(药剂:兑水量)</div>
+                                            </div>
+                                            <div class="form-r r-text">
+                                                <!-- {{
+                                                    domain.ratio
+                                                        ? domain.ratio + "倍"
+                                                        : domain.defaultRatio
+                                                        ? domain.defaultRatio + "倍"
+                                                        : "---"
+                                                }} -->
+                                                {{ domain.executeStyle === 2 ? ((domain.ratio2 ? domain.ratio2 + "倍" : (domain.defaultRatio ? domain.defaultRatio + "倍" : '---'))) :  ((domain.ratio ? domain.ratio + "倍" : (domain.defaultRatio ? domain.defaultRatio + "倍" : '---'))) }}
+                                            </div>
+                                        </div>
+                                        <div class="box-item sub-item">
+                                            <div class="form-l">单亩用量</div>
+                                            <div class="form-r r-text">{{ domain.executeStyle === 2 ? domain.muUsage2 : domain.muUsage }}{{ domain.unit }}</div>
+                                        </div>
+                                        <div class="box-item sub-item">
+                                            <div class="form-l colunm-sub">品牌</div>
+                                            <div class="form-r price">
+                                                <el-input v-model="domain.brand" style="width: 140px" placeholder="" />
+                                            </div>
+                                        </div>
+                                        <div class="box-item sub-item">
+                                            <div class="form-l">亩单价</div>
+                                            <div class="form-r price">
+                                                <el-input v-model="domain.muPrice" style="width: 140px" placeholder="">
+                                                    <template #append>元</template>
+                                                </el-input>
+                                            </div>
+                                        </div>
+                                        <div class="action-btn btn-group">
+                                            <el-button class="btn delete-btn" @click.prevent="removeDomain(domain)">
+                                                删除
+                                            </el-button>
+                                            <el-button type="primary" class="btn" @click.prevent="resetItemForm(index)">
+                                                重置
+                                            </el-button>
+                                        </div>
+                                    </div>
+                                </el-form-item>
+                            </el-form>
+                        </div>
+                    </div>
+
+                    <div class="service-wrap">
+                        <div class="sub-title">
+                            <div class="sub-name">选择农户</div>
+                        </div>
+                        <div class="bottom-map" ref="areaRef" :id="'areaMap' + boxId"></div>
+                        <template v-for="(item, index) in displayedItems" :key="index">
+                            <div class="service-item">
+                                <div class="service-l">
+                                    <img class="service-img" :src="item.icon" />
+                                    {{ item.userName || "农户" }}
+                                </div>
+                                <div class="service-c">{{ item.area || 50 }}亩</div>
+                                <div class="service-r">
+                                    <el-checkbox
+                                        @change="handleUsersCheckChange()"
+                                        :true-value="1"
+                                        :false-value="0"
+                                        v-model="item.selected"
+                                        size="large"
+                                    />
+                                </div>
+                            </div>
+                        </template>
+                        <div v-if="hasMoreItems">
+                            <div @click="toggleMore" class="icon-text">
+                                {{ isMoreVisible ? '收起' : '展开' }}
+                                <el-icon :class="isMoreVisible ? 'down-icon' : 'up-icon'"><DArrowRight /></el-icon>
+                            </div>
+                        </div>
+
+                        <div class="service-bottom">
+                            <div class="sub-title">
+                                <div class="sub-name">选择农服</div>
+                            </div>
+                            <template v-for="(item, i) in displayedItems2" :key="i">
+                                <div class="service-item">
+                                    <div class="service-l">
+                                        <img class="service-img" :src="item.icon" />
+                                        {{ item.userName || "农服" }}
+                                    </div>
+                                    <div class="service-c">{{ item.serviceType == 1 ? "技术类" : "劳力类" }}</div>
+                                    <div class="service-r">
+                                        <el-checkbox
+                                            :true-value="1"
+                                            :false-value="0"
+                                            v-model="item.selected"
+                                            size="large"
+                                        />
+                                    </div>
+                                </div>
+                            </template>
+                            <div v-if="hasMoreItems2">
+                                <div @click="toggleMore2" class="icon-text">
+                                    {{ isMoreVisible2 ? '收起' : '展开' }}
+                                    <el-icon :class="isMoreVisible2 ? 'down-icon' : 'up-icon'"><DArrowRight /></el-icon>
+                                </div>
+                            </div>
+
+                            <div class="service-btn">
+                                <el-button class="btn" @click.stop="submit" type="primary">确认订单</el-button>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div v-else>
+                      <div class="sub-title">
+                          <div class="service-name">报价处方</div>
+                      </div>
+                    <div class="item-table">
+                        <el-table
+                            :data="pesticideFertilizers"
+                            style="width: 100%"
+                            :header-cell-style="{ background: '#F5F5F5' }"
+                        >
+                            <el-table-column align="center" prop="defaultName" label="名称" />
+                            <el-table-column align="center" prop="brand" label="品牌" width="52">
+                                <template #default="scope">
+                                  <div>{{ scope.row.brand ? scope.row.brand : '---' }}</div>
+                                </template>
+                            </el-table-column>
+                            <el-table-column align="center" label="执行方式" width="40">
+                                <template #default="scope">
+                                    <div class="table-l-title">
+                                        {{ scope.row.executeStyle == 1 ? "人工" : "人工" }}
+                                    </div>
+                                </template>
+                            </el-table-column>
+                            <el-table-column align="center" prop="ratio" label="配比" width="52">
+                                <template #default="scope">
+                                    <div v-if="scope.row.executeStyle === 1">
+                                        <!-- <div>{{`1${scope.row.unit}:${scope.row.ratio || scope.row.defaultRatio}${scope.row.unit}`}}</div> -->
+                                        <!-- <div>人工</div> -->
+                                        <div>{{`1:${scope.row.ratio || scope.row.defaultRatio}${scope.row.unit}`}}</div>
+                                    </div>
+                                    <div v-else-if="scope.row.executeStyle === 2">
+                                        <!-- <div>{{`1${scope.row.unit}:${scope.row.ratio2 || scope.row.defaultRatio}${scope.row.unit}`}}</div> -->
+                                        <!-- <div>无人机</div> -->
+                                        <div>{{`1:${scope.row.ratio2 || scope.row.defaultRatio}${scope.row.unit}`}}</div>
+                                    </div>
+                                    <div v-else>
+                                        <div>{{`1${scope.row.unit}:${scope.row.defaultRatio}${scope.row.unit}`}}</div>
+                                    </div>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="muUsage" label="单亩用量" width="42">
+                                <template #default="scope">
+                                    {{ scope.row.muUsage ? scope.row.muUsage + scope.row.unit : "---" }}
+                                </template>
+                            </el-table-column>
+                            <el-table-column align="center" prop="muPrice" label="亩单价" width="48">
+                              <template #default="scope">
+                                  <div>{{ scope.row.muPrice ? scope.row.muPrice + '元' : '---' }} </div>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <service-dialog></service-dialog>
+    </div>
+</template>
+
+<script setup>
+import eventBus from "@/api/eventBus";
+import { onMounted, ref, computed, onActivated } from "vue";
+import AreMap from "../areaMap";
+import serviceDialog from "./serviceDialog.vue";
+
+import { useStore } from "vuex";
+import { deepClone } from "@/common/commonFun";
+const store = useStore();
+
+const ROLETYPE = store.state.app.curRole
+
+const checkService = ref(true);
+const props = defineProps({
+    progress: {
+        type: Number,
+        required: false,
+    },
+    pesticideFertilizers: {
+        type: Array,
+        required: true,
+    },
+    users: {
+        type: Array,
+        required: true,
+    },
+    executeDate: {
+        type: String,
+        required: false,
+    },
+    boxId: {
+        type: String,
+        required: false,
+    },
+});
+const removeDomain = (item) => {
+    const index = dynamicValidateForm.value.indexOf(item);
+    if (index !== -1) {
+        dynamicValidateForm.value.splice(index, 1);
+    }
+};
+
+const resetItemForm = (index) => {
+    dynamicValidateForm.value[index] = props.pesticideFertilizers[index];
+};
+
+let pickerTime = ref(props.executeDate);
+
+let pesticideFertilizersOptions = ref([
+    {
+        id: 'null',
+        name: "芸苔素内酯 15000倍",
+        typeName: "30",
+        defaultRatio: null,
+        unit: 0,
+        defaultName: "调节",
+    },
+]);
+// VE_API.order.pesticideFertilizers().then(({ data }) => {
+//     pesticideFertilizersOptions.value = data;
+// });
+
+// 表单
+const formRef = ref();
+const dataModel = () => {
+    return {
+        id: null,
+        orderId: null, //订单id
+        pesticideFertilizerId: null, //农药id
+        pesticideFertilizerName: null, //农药名称
+        typeName: null, //功效类型名称
+        brand: null, //品牌
+        price: null, //单价
+        unit: null, //单位
+        executeStyle: null, //执行方式
+        muUsage: null, //亩用量
+        muPrice: null, //单亩价格
+        defaultRatio: null, //默认配比
+        ratio: null, //配比
+        defaultName: null, //默认名称
+    };
+};
+const dynamicValidateForm = ref([]);
+
+const formatIndex = (index) => {
+    return String(index + 1).padStart(2, "0");
+};
+const settingData = () => {
+    for (let i = 0; i < props.pesticideFertilizers.length; i++) {
+        dynamicValidateForm.value.push(deepClone(props.pesticideFertilizers[i]));
+    }
+};
+
+/**
+ * 选择药肥的时候修改订单中药肥pesticideFertilizerId 以外其他数据
+ * @param index
+ */
+const handlePesticideFertilizerChange = (index) => {
+    let obj = pesticideFertilizersOptions.value.filter(
+        (item) => dynamicValidateForm.value[index].pesticideFertilizerId === item.id
+    )[0];
+    dynamicValidateForm.value[index] = {
+        ...dynamicValidateForm.value[index],
+        typeName: obj.typeName,
+        unit: obj.unit,
+        defaultRatio: obj.defaultRatio,
+        ratio: obj.defaultRatio,
+        defaultName: obj.defaultName,
+        pesticideFertilizerName: obj.name,
+    };
+    console.log(dynamicValidateForm.value);
+};
+
+const handleUsersCheckChange = (index) => {
+    console.log(props.users);
+};
+
+const submit = () => {
+    const data = {
+        orderId: dynamicValidateForm.value[0].orderId,
+        orderStatus: 2,
+        pesticideFertilizers: dynamicValidateForm.value,
+        users: props.users,
+        executeDate: pickerTime.value,
+    };
+    VE_API.order.confirm(data).then(({ code }) => {
+        if (code == 0) {
+            console.log('订单确认成功');
+            eventBus.emit('discover:submitForm')
+        }
+    });
+};
+
+const areaRef = ref();
+let farmArr = ref([])
+let serviceArr = ref([])
+onMounted(() => {
+    if (props.progress === 1 && ROLETYPE == '2') {
+        const dom = document.getElementById(`areaMap${props.boxId}`)
+        let areMap = new AreMap();
+        areMap.initMap("POINT(113.1093017627431 22.574540836684672)", dom);
+        areMap.setPoints(props.users.filter((item) => item.userType === 1).map((item) => item.point));
+        settingData();
+    }
+    farmArr.value = props.users.filter(item => item.userType === 1)
+    serviceArr.value = props.users.filter(item => item.userType === 2)
+
+});
+
+// 控制是否展开更多列表项的布尔值
+const isMoreVisible = ref(false);
+const isMoreVisible2 = ref(false);
+ 
+// 计算属性,返回应该显示的列表项
+const displayedItems = computed(() => {
+  return isMoreVisible.value ? farmArr.value : props.users.slice(0, 3);
+});
+// 计算属性,返回应该显示的列表项
+const displayedItems2 = computed(() => {
+  return isMoreVisible2.value ? serviceArr.value : serviceArr.value.slice(0, 3);
+});
+ 
+// 计算属性,判断是否有更多项可以展开
+const hasMoreItems = computed(() => {
+  return farmArr.value.length > 3;
+});
+ 
+// 计算属性,判断是否有更多项可以展开
+const hasMoreItems2 = computed(() => {
+  return serviceArr.value.length > 3;
+});
+ 
+// 方法,用于切换展开/收起状态
+const toggleMore = () => {
+  isMoreVisible.value = !isMoreVisible.value;
+};
+ 
+// 方法,用于切换展开/收起状态
+const toggleMore2 = () => {
+  isMoreVisible2.value = !isMoreVisible2.value;
+};
+</script>
+
+<style lang="scss" scoped>
+@import "./boxClass.scss";
+.service-time {
+    display: flex;
+    align-items: center;
+    color: rgba(0, 0, 0, 0.2);
+    .time-picker {
+        padding-left: 10px;
+        ::v-deep {
+            .el-input__wrapper {
+                background: #f0f8fd;
+                box-shadow: 0 0 0 1px #2199f8 inset;
+                .el-input__prefix {
+                    color: #2199f8;
+                }
+                .el-input__inner {
+                    color: #2199f8;
+                }
+            }
+        }
+    }
+}
+.service-name {
+    padding-top: 12px;
+    color: #2199F8;
+    font-size: 14px;
+}
+
+
+/* 定义过渡效果 */
+.list-enter-active, .list-leave-active {
+    transition: all 0.5s;
+}
+.list-enter, .list-leave-to /* .list-leave-active in <2.1.8 */ {
+    opacity: 0;
+    transform: translateY(30px);
+}
+.icon-text {
+    padding-top: 8px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .down-icon {
+        transform: rotate(270deg);
+    }
+    .up-icon {
+        transform: rotate(90deg);
+    }
+}
+</style>

+ 104 - 0
src/views/workDetail/components/serviceDialog.vue

@@ -0,0 +1,104 @@
+<template>
+    <div>
+        <el-dialog
+            v-model="dialogVisible"
+            width="90%"
+        >
+            <div>
+                <div class="dialog-map" ref="areaRef"></div>
+                <div class="dialog-content service-wrap">
+                    <div class="service-item" v-for="(item, i) in boxList" :key="i">
+                        <div class="service-l">
+                            <img class="service-img" :src="item.icon" />
+                            {{ item.userName || "农服" }}
+                        </div>
+                        <div class="service-c">{{ item.userType === 1 ? (item.area || 50) + '亩' : (item.serviceType == 1 ? "技术类" : "劳力类") }}</div>
+                        <div class="service-r">
+                            <!-- <el-checkbox
+                                :true-value="1"
+                                :false-value="0"
+                                v-model="item.selected"
+                                size="large"
+                            /> -->
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <template #footer>
+            <div class="dialog-footer">
+                <el-button type="primary" @click="dialogVisible = false">
+                一键提醒
+                </el-button>
+            </div>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup>
+import eventBus from '@/api/eventBus';
+import { onMounted, ref } from 'vue';
+import AreMap from "../areaMap";
+
+const areaRef = ref();
+const dialogVisible = ref(false)
+onMounted(() => {
+    
+})
+
+const boxList = ref([])
+let areMap = null
+const handleEvent = (data) => {
+    boxList.value = data
+    dialogVisible.value = true
+    setTimeout(() => {
+        if (!areMap) {
+            areMap = new AreMap();
+            areMap.initMap("POINT(113.1093017627431 22.574540836684672)", areaRef.value);
+            areMap.setPoints(data.filter((item) => item.userType === 1).map((item) => item.point));
+        }
+}, 100)
+}
+eventBus.on("enterBox:showDialog", handleEvent)
+</script>
+
+<style lang="scss" scoped>
+.dialog-map {
+    width: 100%;
+    height: 150px;
+}
+.service-wrap {
+                padding: 12px 0;
+                .service-item {
+                    margin-top: 8px;
+                    display: flex;
+                    align-items: center;
+                    border: 1px solid #DDDDDD;
+                    padding: 0 12px;
+                    height: 48px;
+                    color: #000000;
+                    font-size: 14px;
+                    border-radius: 8px;
+                    .service-img {
+                        width: 32px;
+                        margin-right: 8px;
+                        border-radius: 50%;
+                    }
+                    .service-c {
+                        flex: 1;
+                        text-align: center;
+                        color: #666666;
+                    }
+                }
+                .service-bottom {
+                    padding-top: 12px;
+                    .service-btn {
+                        padding-top: 12px;
+                        width: 100%;
+                        .btn {
+                            width: 100%;
+                        }
+                    }
+                }
+            }
+</style>

+ 94 - 0
src/views/workDetail/index.vue

@@ -0,0 +1,94 @@
+<template>
+    <div class="base-container">
+        <fnHeader :hideSwitch="true" :hideShadow="true"></fnHeader>
+        <div class="content">
+            <div class="left">
+                <div @click="goBack">返回</div>
+                <component v-if="workList && workList.length" :is="components[currentComponent]" :prescriptioData="workList[0]" />
+            </div>
+            <div class="right">
+                <div class="excute-title">执行农事区域</div>
+                <div ref="mapRef" class="bottom-map"></div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import fnHeader from "@/components/fnHeader.vue";
+import AreaMap from "./areaMap";
+import prescriptionBox from './components/prescriptionBox'
+import { useRouter, useRoute } from "vue-router";
+import { useStore } from "vuex";
+let store = useStore();
+const components = {
+    prescriptionBox,
+};
+let areaMap = new AreaMap();
+
+const router = useRouter();
+const route = useRoute();
+const mapRef = ref();
+onMounted(() => {
+    getList()
+    areaMap.initMap("POINT(113.61448114737868 23.585550924763083)", mapRef.value);
+    
+});
+
+const workList = ref([])
+const getList = () => {
+  VE_API.order.fetchWorkList().then(({data}) => {
+    // data[0].orderStatus = data[0].orderStatus + 1
+    // && (ROLETYPE.value == '0' || ROLETYPE.value == '3')
+    if (route.query.data && JSON.parse(route.query.data)?.farmWorkId) {
+      data = data.find(item => item.farmWorkLibId === JSON.parse(route.query.data)?.farmWorkId)
+      workList.value = [data]
+    } else {
+      workList.value = data
+    }
+    console.log('dtafds,', data)
+  })
+}
+
+
+const currentComponent = ref("prescriptionBox");
+
+const goBack = () => {
+    router.go(-1)
+}
+</script>
+
+<style lang="scss" scoped>
+.base-container {
+    width: 100%;
+    height: 100vh;
+    color: #fff;
+    position: relative;
+    box-sizing: border-box;
+    z-index: 1;
+    background: #000;
+
+    .content {
+        width: 100%;
+        height: calc(100% - 74px - 48px);
+        display: flex;
+        justify-content: space-between;
+        box-sizing: border-box;
+        .left {
+            width: 512px;
+            height: 100%;
+            padding-top: 10px;
+            box-sizing: border-box;
+        }
+        .right {
+            width: calc(100% - 512px);
+            height: 100%;
+            .bottom-map {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+}
+</style>

File diff suppressed because it is too large
+ 377 - 205
yarn.lock


Some files were not shown because too many files changed in this diff