lxf 1 день назад
Родитель
Сommit
61ba9c205f

+ 1 - 1
src/App.vue

@@ -36,7 +36,7 @@
                     />
                 </template>
             </tabbar-item>
-            <tabbar-item replace to="/monitor">
+            <tabbar-item replace to="/agri_file">
                 <span>农情档案</span>
                 <template #icon="props">
                     <img

+ 13 - 4
src/components/weatherInfo.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="weather-info is-garden"
-        :class="{ expanded: isExpanded, 'no-farm': !hasFarm, 'farm-list': activeGarden === 'list' }">
+        :class="{ expanded: isExpanded, 'no-farm': !hasFarm, 'no-weather': !hasWeather, 'farm-list': activeGarden === 'list' }">
         <div class="header flex-center">
             <div class="header-left">
                 <div class="address-select flex-center" v-if="hasFarm">
@@ -37,7 +37,7 @@
                 <div class="address-select flex-center farm-name" v-else>
                     示范农场
                 </div>
-                <div class="farm-l" v-show="activeGarden === 'current'">
+                <div class="farm-l" v-show="activeGarden === 'current' && hasWeather">
                     <div class="temperature flex-center">
 
                         <div class="weather-icon" v-if="currentWeather.iconDay">
@@ -67,12 +67,14 @@
                         </div> -->
                     </div>
                 </div>
+
+                <slot v-if="!hasWeather" name="types-content"></slot>
             </div>
             <!-- <div class="weather-icon" v-else>
                 <img :src="`https://birdseye-img.sysuimars.com/weather/${currentWeather.iconDay}.svg`" alt="" />
             </div> -->
         </div>
-        <div class="weather-chart-container" v-show="activeGarden === 'current'">
+        <div class="weather-chart-container" v-show="activeGarden === 'current' && hasWeather">
             <div class="weather-chart-title">
                 <span>未来七天天气</span>
                 <div class="weather-chart-title-more" @click="toggleExpand">收起</div>
@@ -116,6 +118,10 @@ const props = defineProps({
         type: [Number, String],
         default: null
     },
+    hasWeather: {
+        type: Boolean,
+        default: true
+    },
     from: {
         type: String,
         default: null
@@ -321,12 +327,15 @@ defineExpose({
     overflow: hidden;
 
     &.is-garden {
-        height: 130px;
         border-radius: 8px;
         box-shadow: 0px -2px 4px 0px #0000000D;
         height: 130px;
     }
 
+    &.no-weather {
+        height: auto;
+    }
+
     &.no-farm {
         height: 80px;
 

+ 8 - 0
src/router/globalRoutes.js

@@ -515,4 +515,12 @@ export default [
         meta: { keepAlive: true },
         component: () => import("@/views/old_mini/create_farm/selectCrop.vue"),
     },
+
+    // 农事档案
+    {
+        path: "/agri_file",
+        name: "AgriFile",
+        meta: { showTabbar: true, keepAlive: true },
+        component: () => import("@/views/old_mini/agri_file/index.vue"),
+    },
 ];

+ 74 - 0
src/views/old_mini/agri_file/components/fileFloat.vue

@@ -0,0 +1,74 @@
+<template>
+    <floating-panel class="file-float-panel" :class="{'custom-panel': height === anchors[0]}" v-model:height="height" :anchors="anchors">
+        <div class="file-float-content">
+            <div class="float-tabs">
+                <div class="tab-item" @click="changeTab(0)" :class="{ 'tab-item-active': activeTab === 0 }">物候记录</div>
+                <div class="tab-item" @click="changeTab(1)" :class="{ 'tab-item-active': activeTab === 1 }">异常记录</div>
+                <div class="tab-item" @click="changeTab(2)" :class="{ 'tab-item-active': activeTab === 2 }">农事记录</div>
+            </div>
+        </div>
+    </floating-panel>
+</template>
+
+<script setup>
+
+import { FloatingPanel } from 'vant';
+import { ref } from 'vue';
+const anchors = [
+    130,
+    Math.round(0.4 * window.innerHeight),
+    Math.round(0.8 * window.innerHeight),
+];
+const height = ref(anchors[0]);
+
+const activeTab = ref(0);
+const changeTab = (index) => {
+    activeTab.value = index;
+};
+
+</script>
+
+<style lang="scss" scoped>
+.file-float-panel {
+    left: 12px;
+    width: calc(100% - 24px);
+    &.custom-panel {
+        background: transparent;
+        ::v-deep {
+            .van-floating-panel__header {
+                background: #fff;
+                border-radius: 10px 10px 0 0;
+            }
+            .van-floating-panel__content {
+                background: transparent;
+                margin-top: -1px;
+            }
+        }
+    }
+}
+.file-float-content {
+    padding: 0 10px 10px;
+    background: #fff;
+    border-radius: 0 0 10px 10px;
+    .float-tabs {
+        border-radius: 4px;
+        padding: 3px;
+        background: #E9E9E9;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .tab-item {
+            flex: 1;
+            height: 26px;
+            line-height: 26px;
+            text-align: center;
+            color: #767676;
+            border-radius: 4px;
+            &.tab-item-active {
+                background: #fff;
+                color: #0D0D0D;
+            }
+        }
+    }
+}
+</style>

+ 36 - 0
src/views/old_mini/agri_file/fileMap.js

@@ -0,0 +1,36 @@
+import * as KMap from "@/utils/ol-map/KMap";
+import * as util from "@/common/ol_common.js";
+import config from "@/api/config.js";
+import { Point } from 'ol/geom';
+
+/**
+ *
+ */
+class FileMap {
+    constructor() {
+        let that = this;
+        // let vectorStyle = new KMap.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, 8, 22);
+        let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
+        this.kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
+    }
+
+    /**
+   * 调整地图视图以适应地块范围
+   */
+    fitView() {
+        let extent = this.gardenPolygonLayer.source.getExtent()
+        if (extent) {
+            // 地图自适应到区域可视范围
+            this.kmap.getView().fit(extent, { duration: 50, padding: [80, 80, 80, 80] });
+        }
+    }
+}
+
+export default FileMap;

+ 161 - 0
src/views/old_mini/agri_file/index.vue

@@ -0,0 +1,161 @@
+<template>
+    <div class="agri-file" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
+        <!-- 天气遮罩 -->
+        <div class="weather-mask" v-show="isExpanded" @click="handleMaskClick"></div>
+        <!-- 天气 -->
+        <weather-info ref="weatherInfoRef" :hasWeather="false" from="monitor" class="weather-info"
+            @weatherExpanded="weatherExpanded" @changeGarden="changeGarden" @changeGardenTab="changeGardenTab"
+            :isGarden="true" :gardenId="defaultGardenId">
+            <template #types-content>
+                <div class="type-tabs">
+                    <div class="type-item" @click="changeType('荔枝')" :class="{ 'type-item-active': activeType === '荔枝' }">荔枝</div>
+                    <div class="type-item" @click="changeType('水稻')" :class="{ 'type-item-active': activeType === '水稻' }">水稻</div>
+                    <div class="type-item" @click="changeType('柑橘')" :class="{ 'type-item-active': activeType === '柑橘' }">柑橘</div>
+                    <div class="type-item" @click="changeType('小麦')" :class="{ 'type-item-active': activeType === '小麦' }">小麦</div>
+                </div>
+            </template>
+        </weather-info>
+        <!-- 农场列表 -->
+        <div v-show="activeGardenTab === 'list'">
+            <garden-list ref="gardenListRef" :garden-id="selectedGardenId" @loaded="handleGardenLoaded"
+                @selectGarden="handleGardenSelected" />
+        </div>
+
+        <div class="file-content" v-show="activeGardenTab === 'current'">
+            <div class="map-container" ref="mapContainer"></div>
+            <file-float></file-float>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { computed, onActivated, ref } from "vue";
+import { useRoute } from "vue-router";
+import { useStore } from "vuex";
+import weatherInfo from "@/components/weatherInfo.vue";
+import gardenList from "@/components/gardenList.vue";
+import fileFloat from "./components/fileFloat.vue";
+import FileMap from "./fileMap.js";
+
+const store = useStore();
+const route = useRoute();
+const tabBarHeight = computed(() => store.state.home.tabBarHeight);
+
+const isExpanded = ref(false);
+const weatherInfoRef = ref(null);
+const defaultGardenId = ref(null);
+const selectedGardenId = ref(null);
+const gardenListRef = ref(null);
+const activeGardenTab = ref("current");
+const activeType = ref("荔枝");
+
+const fileMap = new FileMap();
+
+const weatherExpanded = (isExpandedValue) => {
+    isExpanded.value = isExpandedValue;
+};
+
+const handleMaskClick = () => {
+    if (weatherInfoRef.value?.toggleExpand) {
+        weatherInfoRef.value.toggleExpand();
+    }
+};
+
+const changeGardenTab = (tab) => {
+    activeGardenTab.value = tab;
+};
+const changeType = (type) => {
+    activeType.value = type;
+};
+
+const handleGardenLoaded = ({ hasFarm }) => {
+    weatherInfoRef.value?.setGardenLoaded?.(hasFarm);
+};
+
+const handleGardenSelected = (garden) => {
+    selectedGardenId.value = garden?.id ?? null;
+    weatherInfoRef.value?.setSelectedGarden?.(garden);
+};
+
+const changeGarden = ({ id }) => {
+    if (!id) return;
+    store.commit("home/SET_GARDEN_ID", id);
+};
+
+const mapContainer = ref(null);
+onActivated(() => {
+    if (route.query?.farmId) {
+        defaultGardenId.value = route.query.farmId;
+    }
+    const savedFarmId = localStorage.getItem("selectedFarmId");
+    selectedGardenId.value = savedFarmId ? Number(savedFarmId) : null;
+    gardenListRef.value?.refreshFarmList?.();
+
+    fileMap.initMap("POINT(114.31 30.57)", mapContainer.value);
+});
+</script>
+
+<style lang="scss" scoped>
+.agri-file {
+    width: 100%;
+    height: 100%;
+    background: #F5F7FB;
+    box-sizing: border-box;
+
+    .weather-mask {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background-color: rgba(0, 0, 0, 0.52);
+        z-index: 11;
+    }
+
+    .weather-info {
+        width: calc(100% - 20px);
+        position: absolute;
+        z-index: 12;
+        left: 10px;
+        top: 12px;
+    }
+
+    .file-content {
+        // padding-top: 104px;
+        height: 100%;
+        box-sizing: border-box;
+
+        .map-container {
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    .type-tabs {
+        width: 100%;
+        background: #FFF;
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: 8px;
+        padding: 8px;
+
+        .type-item {
+            height: 28px;
+            line-height: 28px;
+            text-align: center;
+            padding: 0 6px;
+            min-width: 78px;
+            color: #9A9A9A;
+            background: #EFEFEF;
+            box-sizing: border-box;
+            border-radius: 2px;
+
+            &.type-item-active {
+                background: #2199F8;
+                color: #fff;
+            }
+        }
+    }
+}
+</style>

+ 0 - 2
src/views/old_mini/work_detail/components/mapInfo.vue

@@ -9,7 +9,6 @@
 
 <script setup>
 import { ref, nextTick, onMounted, watch } from "vue";
-import IndexMap from "@/views/old_mini/home/map/index.js";
 import AreaMap from "./areaMap.js";
 
 const props = defineProps({
@@ -24,7 +23,6 @@ const props = defineProps({
 });
 
 const mapContainer = ref(null);
-const indexMap = new IndexMap();
 const areaMap = new AreaMap();
 
 // 加载农场地图信息

+ 211 - 14
src/views/old_mini/work_detail/index.vue

@@ -19,7 +19,7 @@
                 </div>
             </div>
 
-            <div class="work-wrap" :class="{ 'has-bottom': info?.appType === 2 || (info?.appType === 1 && detail?.flowStatus === 3) }">
+            <div class="work-wrap has-bottom warning-info-show" :class="{ 'warning-info-show': detail?.flowStatus === 1 }">
                 <!-- 农事组信息 -->
                 <div class="box-wrap group-info group-box" v-if="(detail?.executionLimitDays || detail?.executionLimitDays === 0) && detail?.flowStatus !== 5">
                     <div class="group-name">
@@ -30,7 +30,7 @@
                 </div>
 
                 <!-- 每一段农事 -->
-                <div v-for="(prescription, index) in stageList" :key="index" class="box-wrap stage-card">
+                <!-- <div v-for="(prescription, index) in stageList" :key="index" class="box-wrap stage-card">
                     <div class="work-info">
                         <div class="stage-header">
                             <div class="stage-title">{{ detail.farmWorkName }}</div>
@@ -68,7 +68,6 @@
                             </div>
                         </div>
 
-                        <!-- 执行方式 -->
                         <div class="stage-tabs" v-if="hasAnyAvailableExecutionMethod(prescription)">
                             <div v-for="tab in getAvailableExecutionTabs(prescription)" :key="tab.value" class="tab-pill"
                                 :class="{ active: getStageExecutionMethod(index) === tab.value }"
@@ -77,7 +76,6 @@
                             </div>
                         </div>
 
-                        <!-- 药物处方表 -->
                         <div class="prescription-wrap"
                             v-if="prescription.pesticideList && prescription.pesticideList.length && hasAnyAvailableExecutionMethod(prescription)">
                             <div class="prescription-table">
@@ -112,7 +110,6 @@
                     </div>
 
 
-                    <!-- 农事凭证 -->
                     <div class="work-info photo-box" v-if="prescription.cropAlbum && prescription.cropAlbum.length">
                         <div class="photo-title">农事凭证</div>
                         <div class="tips-text" v-if="detail?.imageAuditRejectReason">{{ detail?.imageAuditRejectReason }}</div>
@@ -132,6 +129,57 @@
                             </photo-provider>
                         </div>
                     </div>
+                </div> -->
+
+                
+                <div class="box-wrap stage-card">
+                    <div class="work-info">
+                        <div class="map-box">
+                            <div class="map-title">执行区域</div>
+                            <div class="map-container" ref="mapContainer"></div>
+                        </div>
+                        <div class="area-list">
+                            <div class="area-item" v-for="item in 2" :key="item">
+                                <div class="area-l">
+                                    分区名称1:具体时间
+                                    <span class="area-tag">未激活</span>
+                                </div>
+                                <div class="area-r">我已完成</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="box-wrap stage-card">
+                    <div class="work-info">
+                        <div class="info-item">
+                            <div class="info-title"><span class="title-block"></span>农事目的</div>
+                            <div class="info-value">农事目的农事目的农事目的农事目的农事目的农事目的农事目的</div>
+                        </div>
+                        <div class="info-item">
+                            <div class="info-title"><span class="title-block"></span>注意事项</div>
+                            <div class="info-value">注意事项注意事项注意事项注意事项注意事项注意事项注意事项</div>
+                        </div>
+                        <div class="info-item">
+                            <div class="info-title"><span class="title-block"></span>药物处方</div>
+                            <div class="info-value"><span class="blod-text">机械:</span>药肥处方药肥处方药肥处方药肥处方药肥处方药肥</div>
+                        </div>
+                        <div class="info-item">
+                            <div class="info-title"><span class="title-block"></span>执行方式</div>
+                            <div class="info-value"><span class="blod-text">机械:</span>执行方式执行方式执行方式执行方式执行方式执行方式</div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="warning-info">
+                    <div class="warning-l">
+                        <div class="warning-title">
+                            <el-icon size="16" color="#FF953D"><WarningFilled /></el-icon>
+                            存在继发危害需巡园
+                        </div>
+                        <div class="warning-text">文案文案文案文案文案</div>
+                    </div>
+                    <div class="warning-r">异常记录</div>
                 </div>
 
                 <!-- 底部按钮 -->
@@ -141,9 +189,9 @@
                     </div>
                 </div>
 
-                <div class="fixed-btn-wrap execute-action" :class="{ 'no-share': miniJson?.hideDraw }" v-if="info?.appType === 1 && detail?.flowStatus === 3">
-                    <div class="action-item second" v-if="!miniJson?.hideDraw" @click="handleConvert">转发农事</div>
-                    <div class="action-item primary" @click="handleExecute">溯源认证</div>
+                <div class="fixed-btn-wrap execute-action">
+                    <div class="action-item second" v-if="!miniJson?.hideDraw" @click="handleConvert">转发给执行人员</div>
+                    <!-- <div class="action-item primary" @click="handleExecute">溯源认证</div> -->
                 </div>
             </div>
         </div>
@@ -170,6 +218,7 @@ import UploadTips from "@/components/popup/uploadTips.vue";
 import { Popup } from "vant";
 import MapInfo from "./components/mapInfo.vue";
 import { useRoute } from "vue-router";
+import AreaMap from "./components/areaMap.js";
 
 const route = useRoute();
 const showUploadTipsPopup = ref(false);
@@ -259,6 +308,9 @@ const getDetail = () => {
                     data?.expertNameFromFarmBasicInfo ?? "",
                 rangeWkt: data?.rangeWkt ?? null,
             };
+
+            // 地图
+            areaMap.initMap("POINT(113.1093017627431 22.57454083668)", mapContainer.value);
         });
 };
 
@@ -509,6 +561,11 @@ const changeExecutionMethod = (stageIndex, value) => {
         [stageIndex]: value
     };
 };
+
+// 地图
+
+const mapContainer = ref(null);
+const areaMap = new AreaMap();
 </script>
 
 <style scoped lang="scss">
@@ -604,7 +661,10 @@ const changeExecutionMethod = (stageIndex, value) => {
     padding: 0 12px 12px;
     z-index: 2;
     &.has-bottom {
-        margin-bottom: 76px;
+        margin-bottom: 88px;
+    }
+    &.warning-info-show {
+        margin-bottom: 168px;
     }
 }
 
@@ -617,7 +677,7 @@ const changeExecutionMethod = (stageIndex, value) => {
     .work-info {
         background: #ffffff;
         border-radius: 8px;
-        padding: 14px 10px 10px 10px;
+        padding: 10px 10px;
         box-shadow: 0 2px 8px rgba(15, 35, 52, 0.06);
     }
 
@@ -689,6 +749,103 @@ const changeExecutionMethod = (stageIndex, value) => {
     margin-top: 10px;
 }
 
+.info-item + .info-item {
+    margin-top: 10px;
+}
+
+.info-item {
+    .info-title {
+        display: flex;
+        align-items: center;
+        gap: 4px;
+        color: #2199F8;
+        font-weight: 500;
+        .title-block {
+            width: 6px;
+            height: 6px;
+            background: #2199F8;
+            border-radius: 1px;
+            transform: rotate(45deg);
+        }
+    }
+
+    .info-value {
+        padding-top: 4px;
+        color: #ACACAC;
+        line-height: 21px;
+    }
+
+    .blod-text {
+        font-weight: 500;
+    }
+}
+.map-box {
+    position: relative;
+    .map-title {
+        z-index: 12;
+        position: absolute;
+        border-radius: 5px 0 0 5px;
+        top: 0;
+        left: 0;
+        height: 25px;
+        padding: 0 18px;
+        line-height: 25px;
+        font-size: 12px;
+        font-weight: 500;
+        color: #fff;
+        background: rgba(0, 0, 0, 0.45);
+        backdrop-filter: blur(4px);
+    }
+    
+    .map-container {
+        height: 140px;
+        width: 100%;
+        clip-path: inset(0px round 5px);
+    }
+}
+
+
+.area-list {
+    margin-top: 10px;
+    .area-item + .area-item {
+        margin-top: 6px;
+    }
+    .area-item {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 10px;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        border-radius: 6px;
+        .area-l {
+            color: #000000;
+            font-weight: 500;
+            .area-tag {
+                background: rgba(98, 98, 98, 0.1);
+                color: #626262;
+                height: 22px;
+                line-height: 22px;
+                padding: 0 5px;
+                border-radius: 2px;
+                font-size: 12px;
+                margin-left: 5px;
+                display: inline-block;
+            }
+        }
+        .area-r {
+            background: #2199F8;
+            color: #fff;
+            font-size: 12px;
+            font-weight: 500;
+            cursor: pointer;
+            height: 28px;
+            line-height: 28px;
+            padding: 0 10px;
+            border-radius: 20px;
+        }
+    }
+}
+
 .stage-card {
 
     .stage-header {
@@ -912,8 +1069,8 @@ const changeExecutionMethod = (stageIndex, value) => {
 .execute-action {
     display: flex;
     align-items: center;
-    // justify-content: center;
-    justify-content: space-between;
+    justify-content: center;
+    // justify-content: space-between;
     gap: 16px;
 
     .action-item {
@@ -940,10 +1097,50 @@ const changeExecutionMethod = (stageIndex, value) => {
     }
 }
 
-.fixed-btn-wrap {
+.warning-info {
+    position: fixed;
+    bottom: 100px;
+    left: 10px;
+    width: calc(100% - 20px);
+    background: linear-gradient(180deg, #FFDFC5 -13%, #FFFFFF 39%);
+    border-radius: 8px;
+    padding: 14px 10px;
+    box-sizing: border-box;
     display: flex;
-    // justify-content: center;
+    align-items: center;
     justify-content: space-between;
+    box-shadow: 0px 4px 4px 0px #0000001A;
+    .warning-l {
+        .warning-title {
+            display: inline-flex;
+            align-items: center;
+            gap: 6px;
+            font-size: 16px;
+            color: #FF953D;
+        }
+        .warning-text {
+            color: rgba(0, 0, 0, 0.4);
+            font-size: 12px;
+            // margin-top: 4px;
+        }
+    }
+
+    .warning-r {
+        background: #FF953D;
+        color: #fff;
+        font-size: 12px;
+        cursor: pointer;
+        padding: 0 7px;
+        height: 32px;
+        line-height: 32px;
+        border-radius: 20px;
+    }
+}
+
+.fixed-btn-wrap {
+    display: flex;
+    justify-content: center;
+    // justify-content: space-between;
 
     &.center-btn {
         justify-content: center;