Просмотр исходного кода

feat:添加管理分区页面和修改互动页面样式

wangsisi 1 неделя назад
Родитель
Сommit
e4c06034da

BIN
src/assets/img/agricultural/gaojin.png


BIN
src/assets/img/agricultural/interact-ref.png


+ 12 - 0
src/i18n/messages.js

@@ -162,7 +162,13 @@ export default {
             referenceImage: "参考图片",
             referenceImage: "参考图片",
             patrolGuide: "巡园向导",
             patrolGuide: "巡园向导",
             growthAbnormal: "长势异常",
             growthAbnormal: "长势异常",
+            rootAbnormal: "根系异常类",
             uploadPhoto: "上传照片",
             uploadPhoto: "上传照片",
+            pestInteract: "病虫害互动",
+            selectAbnormalPlot: "请选择发生异常的地块",
+            confirmPlot: "确认地块",
+            pleaseSelectPlot: "请先选择地块",
+            cancel: "取消",
             agriAlbum: "农情相册",
             agriAlbum: "农情相册",
             diagnosisReport: "诊断报告",
             diagnosisReport: "诊断报告",
             reportUpdate: "更新",
             reportUpdate: "更新",
@@ -542,7 +548,13 @@ export default {
             referenceImage: "Reference image",
             referenceImage: "Reference image",
             patrolGuide: "Patrol guide",
             patrolGuide: "Patrol guide",
             growthAbnormal: "Growth abnormality",
             growthAbnormal: "Growth abnormality",
+            rootAbnormal: "Root abnormality",
             uploadPhoto: "Upload photo",
             uploadPhoto: "Upload photo",
+            pestInteract: "Pest interaction",
+            selectAbnormalPlot: "Please select the plot where the anomaly occurred",
+            confirmPlot: "Confirm plot",
+            pleaseSelectPlot: "Please select a plot first",
+            cancel: "Cancel",
             agriAlbum: "Agri album",
             agriAlbum: "Agri album",
             diagnosisReport: "Diagnosis report",
             diagnosisReport: "Diagnosis report",
             reportUpdate: "Update",
             reportUpdate: "Update",

+ 7 - 0
src/router/globalRoutes.js

@@ -205,4 +205,11 @@ export default [
         meta: { keepAlive: true },
         meta: { keepAlive: true },
         component: () => import("@/views/old_mini/agri_file/pages/growthTrack.vue"),
         component: () => import("@/views/old_mini/agri_file/pages/growthTrack.vue"),
     },
     },
+    // 病虫害互动选地块
+    {
+        path: "/pest_interact",
+        name: "PestInteract",
+        meta: { keepAlive: true },
+        component: () => import("@/views/old_mini/agri_file/pages/pestInteract.vue"),
+    },
 ];
 ];

+ 35 - 0
src/views/old_mini/agri_file/components/albumUploadPopup.vue

@@ -23,6 +23,20 @@
                 </div>
                 </div>
             </uploader>
             </uploader>
 
 
+            <div class="album-upload-popup__date">
+                <div class="album-upload-popup__date-label">上传日期</div>
+                <el-date-picker
+                    v-model="uploadDate"
+                    type="date"
+                    format="YYYY.MM.DD"
+                    value-format="YYYY.MM.DD"
+                    placeholder="请选择上传日期"
+                    :editable="false"
+                    style="width: 100%"
+                    size="large"
+                />
+            </div>
+
             <el-input
             <el-input
                 v-model="description"
                 v-model="description"
                 class="album-upload-popup__desc"
                 class="album-upload-popup__desc"
@@ -79,8 +93,16 @@ const showValue = ref(false);
 const fileList = ref([]);
 const fileList = ref([]);
 const uploadedKeys = ref([]);
 const uploadedKeys = ref([]);
 const description = ref("");
 const description = ref("");
+const uploadDate = ref("");
 const confirming = ref(false);
 const confirming = ref(false);
 
 
+function formatDate(date = new Date()) {
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, "0");
+    const day = String(date.getDate()).padStart(2, "0");
+    return `${year}.${month}.${day}`;
+}
+
 watch(
 watch(
     () => props.show,
     () => props.show,
     (val) => {
     (val) => {
@@ -100,6 +122,7 @@ function resetForm() {
     fileList.value = [];
     fileList.value = [];
     uploadedKeys.value = [];
     uploadedKeys.value = [];
     description.value = "";
     description.value = "";
+    uploadDate.value = formatDate();
     confirming.value = false;
     confirming.value = false;
 }
 }
 
 
@@ -162,6 +185,7 @@ const handleConfirm = () => {
     emit("confirm", {
     emit("confirm", {
         images: [...uploadedKeys.value],
         images: [...uploadedKeys.value],
         description: description.value.trim(),
         description: description.value.trim(),
+        date: uploadDate.value,
     });
     });
     // 正式上传接口未定时,先关闭弹窗由父组件处理
     // 正式上传接口未定时,先关闭弹窗由父组件处理
     confirming.value = false;
     confirming.value = false;
@@ -245,6 +269,17 @@ const handleClosed = () => {
         font-weight: 300;
         font-weight: 300;
     }
     }
 
 
+    &__date {
+        margin-bottom: 12px;
+
+        &-label {
+            margin-bottom: 8px;
+            font-size: 14px;
+            line-height: 20px;
+            color: #333;
+        }
+    }
+
     &__desc {
     &__desc {
         border: 1px solid rgba(220, 220, 220, 1);
         border: 1px solid rgba(220, 220, 220, 1);
         margin-bottom: 16px;
         margin-bottom: 16px;

+ 242 - 334
src/views/old_mini/agri_file/pages/growthTrack.vue

@@ -2,94 +2,81 @@
     <div class="growth-track-page">
     <div class="growth-track-page">
         <custom-header :name="t('agriFile.workName')" />
         <custom-header :name="t('agriFile.workName')" />
         <div class="growth-track-body">
         <div class="growth-track-body">
-        <div class="growth-hero">
-            <div class="growth-hero__top">
-                <div class="growth-hero__main">
-                    <div class="growth-hero__title-row">
-                        <span class="growth-hero__title">{{ theme }}</span>
-                        <span class="growth-hero__tag growth-hero__tag--level">
-                            <el-icon class="growth-hero__bell">
-                                <Bell />
-                            </el-icon>
-                            {{ level }}
-                        </span>
-                        <span class="growth-hero__tag growth-hero__tag--zone">{{ zoneName }}</span>
+            <div class="growth-hero">
+                <div class="growth-hero__top">
+                    <div class="growth-hero__main">
+                        <div class="growth-hero__title-row">
+                            <span class="growth-hero__title">{{ theme }}</span>
+                            <span class="growth-hero__tag growth-hero__tag--level">
+                                <el-icon>
+                                    <BellFilled />
+                                </el-icon>
+                                {{ level }}
+                            </span>
+                            <span class="growth-hero__tag growth-hero__tag--zone">{{ zoneName }}</span>
+                        </div>
+                        <div class="growth-hero__reason">{{ reason }}</div>
+                    </div>
+                    <div class="growth-hero__share">
+                        <Icon name="share" />
+                        <span>{{ t("agriFile.forward") }}</span>
                     </div>
                     </div>
-                    <div class="growth-hero__reason">{{ reason }}</div>
-                </div>
-                <div class="growth-hero__share">
-                    <el-icon>
-                        <Right />
-                    </el-icon>
-                    <span>{{ t("agriFile.forward") }}</span>
                 </div>
                 </div>
             </div>
             </div>
-        </div>
-        <template v-if="!isAbnormal">
-        <div class="growth-content">
-            <div class="growth-content__title">{{ t("agriFile.agriAssessment") }}</div>
-            <div class="growth-content__desc">{{ t("agriFile.agriAssessmentDesc") }}</div>
-        </div>
-        <div class="interact-card">
-            <div class="interact-card__head">
-                <div class="interact-card__tab">
-                    <img class="interact-card__star" src="@/assets/img/agricultural/start.png" alt="" />
-                    <span>{{ t("agriFile.interactQuestion") }}</span>
+            <template v-if="!isAbnormal">
+                <div class="growth-content">
+                    <div class="growth-content__title">{{ t("agriFile.agriAssessment") }}</div>
+                    <div class="growth-content__desc">{{ t("agriFile.agriAssessmentDesc") }}</div>
                 </div>
                 </div>
-            </div>
-            <div class="interact-card__qa">
-                <div class="interact-card__question">{{ t("agriFile.interactQuestionText") }}</div>
-                <div class="interact-card__row">
-                    <div class="interact-card__input">
-                        <input
-                            v-model="ratio"
-                            type="number"
-                            inputmode="decimal"
-                            :placeholder="t('agriFile.ratioPlaceholder')"
-                        />
-                        <span>%</span>
+                <div class="interact-card">
+                    <div class="interact-card__head">
+                        <div class="interact-card__tab">
+                            <img class="interact-card__star" src="@/assets/img/agricultural/start.png" alt="" />
+                            <span>{{ t("agriFile.interactQuestion") }}</span>
+                        </div>
+                    </div>
+                    <div class="interact-card__qa">
+                        <div class="interact-card__question">{{ t("agriFile.interactQuestionText") }}</div>
+                        <div class="interact-card__row">
+                            <el-input v-model="ratio" class="interact-card__input" type="number" inputmode="decimal"
+                                :placeholder="t('agriFile.ratioPlaceholder')">
+                                <template #suffix>%</template>
+                            </el-input>
+                            <div class="interact-card__submit" @click="handleSubmit">{{ t("agriFile.submit") }}</div>
+                        </div>
+                    </div>
+                    <div class="interact-card__ref">
+                        <span class="interact-card__ref-label">{{ t("agriFile.referenceImage") }}</span>
+                        <img class="interact-card__ref-img" src="@/assets/img/agricultural/patrol-guide.png" alt="" />
                     </div>
                     </div>
-                    <div class="interact-card__submit" @click="handleSubmit">{{ t("agriFile.submit") }}</div>
                 </div>
                 </div>
-            </div>
-            <div class="interact-card__ref">
-                <span class="interact-card__ref-label">{{ t("agriFile.referenceImage") }}</span>
-                <img class="interact-card__ref-img" src="@/assets/img/agricultural/interact-ref.png" alt="" />
-            </div>
-        </div>
-        <div class="guide-card">
-            <div class="guide-card__title">{{ t("agriFile.patrolGuide") }}</div>
-            <div class="guide-card__upload" @click="showUploadPopup = true">
-                <el-icon class="guide-card__upload-icon"><Plus /></el-icon>
-                <span>{{ t("agriFile.uploadPhoto") }}</span>
-            </div>
-            <img class="guide-card__img" src="@/assets/img/agricultural/patrol-guide.png" alt="" />
-        </div>
-        </template>
-        <div v-else class="situation-card">
-            <div class="situation-card__head">
-                <span class="situation-card__icon">
-                    <svg viewBox="0 0 32 32" aria-hidden="true">
-                        <circle cx="16" cy="16" r="16" fill="#2199F8" />
-                        <path
-                            fill="#fff"
-                            d="M16 6.5c-1.1 0-2 .8-2 1.9v1.1c-2.6.7-4.5 3-4.5 5.7V18h13v-2.8c0-2.7-1.9-5-4.5-5.7V8.4c0-1.1-.9-1.9-2-1.9zm-7 13.2h14c.4 1.4.7 2.3.7 2.3H8.3s.3-.9.7-2.3zM11 23.5h10c0 .8-.9 1.5-2.1 1.5h-5.8c-1.2 0-2.1-.7-2.1-1.5z"
-                        />
-                    </svg>
-                </span>
-                <span class="situation-card__name">{{ t("agriFile.growthAbnormal") }}</span>
-            </div>
-            <div class="situation-card__assess">
-                <div class="situation-card__assess-title">{{ t("agriFile.agriAssessment") }}</div>
-                <div class="situation-card__assess-desc">{{ t("agriFile.agriAssessmentDesc") }}</div>
-            </div>
-            <div class="situation-card__guide">
-                <div class="situation-card__guide-title">{{ t("agriFile.patrolGuide") }}</div>
-                <div class="situation-card__guide-box">
-                    <img src="@/assets/img/agricultural/patrol-guide.png" alt="" />
+                <div class="guide-card">
+                    <div class="guide-title">{{ t("agriFile.patrolGuide") }}</div>
+                    <img class="guide-card__img" src="@/assets/img/agricultural/patrol-guide.png" alt="" />
+                </div>
+            </template>
+            <div v-else>
+                <div class="situation-card__head" v-for="item in situationList" :key="item.nameKey">
+                    <div class="situation-card__name">
+                        <img class="situation-card__icon" src="@/assets/img/agricultural/gaojin.png" alt="" />
+                        <span>{{ t(item.nameKey) }}</span>
+                    </div>
+                    <div class="situation-card__assess">
+                        <div class="situation-card__assess-title">{{ t("agriFile.agriAssessment") }}</div>
+                        <div class="situation-card__assess-desc">{{ t("agriFile.agriAssessmentDesc") }}</div>
+                    </div>
+                    <div class="situation-card__guide">
+                        <div class="guide-title">{{ t("agriFile.patrolGuide") }}</div>
+                        <div class="situation-card__guide-box">
+                            <img src="@/assets/img/agricultural/patrol-guide.png" alt="" />
+                        </div>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
+        <div class="guide-card__upload" @click="handleUploadClick">
+            <el-icon><Plus /></el-icon>
+            <span>{{ t("agriFile.uploadPhoto") }}</span>
         </div>
         </div>
         <album-upload-popup v-model:show="showUploadPopup" />
         <album-upload-popup v-model:show="showUploadPopup" />
     </div>
     </div>
@@ -97,8 +84,8 @@
 
 
 <script setup>
 <script setup>
 import { computed, ref } from "vue";
 import { computed, ref } from "vue";
-import { useRoute } from "vue-router";
-import { Bell, Right, Plus } from "@element-plus/icons-vue";
+import { useRoute, useRouter } from "vue-router";
+import { Icon } from 'vant';
 import { ElMessage } from "element-plus";
 import { ElMessage } from "element-plus";
 import customHeader from "@/components/customHeader.vue";
 import customHeader from "@/components/customHeader.vue";
 import albumUploadPopup from "../components/albumUploadPopup.vue";
 import albumUploadPopup from "../components/albumUploadPopup.vue";
@@ -106,6 +93,7 @@ import { useI18n } from "@/i18n";
 
 
 const { t } = useI18n();
 const { t } = useI18n();
 const route = useRoute();
 const route = useRoute();
+const router = useRouter();
 
 
 const isAbnormal = computed(() => route.query.type === "abnormal");
 const isAbnormal = computed(() => route.query.type === "abnormal");
 const theme = computed(() => t("agriFile.interactTheme"));
 const theme = computed(() => t("agriFile.interactTheme"));
@@ -114,6 +102,10 @@ const reason = computed(() => t("agriFile.interactReason"));
 const zoneName = computed(() => t("agriFile.zoneOne"));
 const zoneName = computed(() => t("agriFile.zoneOne"));
 const ratio = ref("");
 const ratio = ref("");
 const showUploadPopup = ref(false);
 const showUploadPopup = ref(false);
+const situationList = [
+    { nameKey: "agriFile.growthAbnormal" },
+    { nameKey: "agriFile.rootAbnormal" },
+];
 
 
 const handleSubmit = () => {
 const handleSubmit = () => {
     if (ratio.value === "" || ratio.value == null) {
     if (ratio.value === "" || ratio.value == null) {
@@ -122,6 +114,14 @@ const handleSubmit = () => {
     }
     }
     ElMessage.success(t("agriFile.submit"));
     ElMessage.success(t("agriFile.submit"));
 };
 };
+
+const handleUploadClick = () => {
+    if (isAbnormal.value) {
+        router.push("/pest_interact");
+        return;
+    }
+    showUploadPopup.value = true;
+};
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -135,106 +135,93 @@ const handleSubmit = () => {
 
 
     .growth-track-body {
     .growth-track-body {
         flex: 1;
         flex: 1;
-        min-height: 0;
         overflow-y: auto;
         overflow-y: auto;
         -webkit-overflow-scrolling: touch;
         -webkit-overflow-scrolling: touch;
-        padding-bottom: 16px;
-    }
 
 
-    .growth-hero {
-        padding: 18px 0 28px 10px;
-        background: #2199f8;
-        color: #fff;
+        .growth-hero {
+            padding: 18px 10px 38px;
+            background: #2199f8;
+            color: #fff;
 
 
-        &__top {
-            display: flex;
-            align-items: flex-start;
-            justify-content: space-between;
-            gap: 10px;
-        }
+            &__top {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+            }
 
 
-        &__title-row {
-            display: flex;
-            align-items: center;
-            gap: 10px;
-        }
+            &__title-row {
+                display: flex;
+                align-items: center;
+                gap: 10px;
+            }
 
 
-        &__title {
-            font-size: 28px;
-            font-family: "PangMenZhengDao";
-            margin-top: -5px;
-        }
+            &__title {
+                font-size: 28px;
+                font-family: "PangMenZhengDao";
+                margin-top: -5px;
+            }
 
 
-        &__tag {
-            display: inline-flex;
-            align-items: center;
-            gap: 8px;
-            height: 20px;
-            padding: 0 8px;
-            border-radius: 20px;
-            background: #fff;
-            font-size: 12px;
-            line-height: 20px;
-        }
+            &__tag {
+                display: inline-flex;
+                align-items: center;
+                gap: 4px;
+                padding: 1px 4px;
+                border-radius: 2px;
+                background: #fff;
+                font-size: 12px;
+            }
 
 
-        &__tag--level {
-            color: #ff6a6a;
-        }
+            &__tag--level {
+                color: #FF6A6A;
+            }
 
 
-        &__tag--zone {
-            color: #2199f8;
-        }
+            &__tag--zone {
+                color: #2199f8;
+            }
 
 
-        &__bell {
-            font-size: 12px;
-        }
 
 
-        &__reason {
-            margin-top: 10px;
-            padding: 4px 10px;
-            border-radius: 4px;
-            background: linear-gradient(90deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
-        }
+            &__reason {
+                margin-top: 10px;
+                padding: 4px 10px;
+                border-radius: 4px;
+                background: linear-gradient(90deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
+            }
 
 
-        &__share {
-            display: flex;
-            align-items: center;
-            flex-shrink: 0;
-            gap: 2px;
-            margin-top: 4px;
-            padding: 4px 10px;
-            border-radius: 16px;
-            background: rgba(13, 92, 173, 0.35);
-            color: #fff;
-            font-size: 12px;
+            &__share {
+                display: flex;
+                align-items: center;
+                gap: 4px;
+                padding: 4px 14px;
+                border-radius: 25px;
+                background: rgba(19, 125, 209, 0.5);
+                color: #fff;
+            }
         }
         }
     }
     }
 
 
     .growth-content {
     .growth-content {
         position: relative;
         position: relative;
         z-index: 1;
         z-index: 1;
-        margin: -16px 10px 12px;
-        padding: 14px 12px 16px;
-        border-radius: 12px;
+        margin: -24px 10px 12px;
+        padding: 10px;
+        border-radius: 8px;
         background: #fff;
         background: #fff;
 
 
         &__title {
         &__title {
-            color: #1f1f1f;
+            color: #4F4F4F;
             font-size: 16px;
             font-size: 16px;
-            font-weight: 600;
-            line-height: 22px;
+            font-family: "PangMenZhengDao";
         }
         }
 
 
         &__desc {
         &__desc {
-            margin-top: 8px;
-            color: rgba(0, 0, 0, 0.45);
-            font-size: 13px;
-            line-height: 20px;
+            margin-top: 4px;
+            color: #9C9C9C;
+            font-size: 14px;
         }
         }
     }
     }
 
 
     .interact-card {
     .interact-card {
-        margin: 0 10px 16px;
+        margin: 0 10px 10px;
         overflow: hidden;
         overflow: hidden;
         border: 1px solid rgba(33, 153, 248, 0.5);
         border: 1px solid rgba(33, 153, 248, 0.5);
         border-radius: 12px;
         border-radius: 12px;
@@ -242,120 +229,77 @@ const handleSubmit = () => {
 
 
         &__head {
         &__head {
             position: relative;
             position: relative;
-            min-height: 36px;
             background: linear-gradient(180deg, #e5f4ff 0%, #ffffff 72%);
             background: linear-gradient(180deg, #e5f4ff 0%, #ffffff 72%);
         }
         }
 
 
         &__tab {
         &__tab {
             display: inline-flex;
             display: inline-flex;
             align-items: center;
             align-items: center;
-            isolation: isolate;
-            gap: 6px;
-            height: 36px;
-            padding: 0 28px 4px 12px;
+            gap: 5px;
+            height: 32px;
+            padding: 0 32px 10px 8px;
             color: #fff;
             color: #fff;
-            font-size: 15px;
-            font-weight: 600;
-            line-height: 1;
+            font-size: 16px;
+            font-family: "PangMenZhengDao";
             background: url("@/assets/img/agricultural/title-bg.png") no-repeat center / 100% 100%;
             background: url("@/assets/img/agricultural/title-bg.png") no-repeat center / 100% 100%;
+
+            span {
+                margin-top: -3px;
+            }
         }
         }
 
 
         &__star {
         &__star {
             width: 16px;
             width: 16px;
             height: 14px;
             height: 14px;
-            object-fit: contain;
-            mix-blend-mode: screen;
         }
         }
 
 
         &__qa {
         &__qa {
-            margin: 10px 10px 0;
-            padding: 12px 10px;
+            margin: 10px;
+            padding: 10px;
             border-radius: 8px;
             border-radius: 8px;
-            background: #e8f5ff;
+            background: rgba(33, 153, 248, 0.1);
+
         }
         }
 
 
         &__question {
         &__question {
-            color: #1f1f1f;
             font-size: 14px;
             font-size: 14px;
-            font-weight: 600;
-            line-height: 20px;
+            font-weight: 500;
         }
         }
 
 
         &__row {
         &__row {
             display: flex;
             display: flex;
             align-items: center;
             align-items: center;
-            gap: 8px;
-            margin-top: 10px;
+            gap: 10px;
+            margin-top: 8px;
         }
         }
 
 
         &__input {
         &__input {
-            display: flex;
-            align-items: center;
             flex: 1;
             flex: 1;
-            min-width: 0;
-            height: 36px;
-            padding: 0 10px;
-            border: 1px solid #dcdfe6;
-            border-radius: 6px;
-            background: #fff;
-
-            input {
-                flex: 1;
-                min-width: 0;
-                border: none;
-                outline: none;
-                color: #333;
-                font-size: 13px;
-                background: transparent;
-
-                &::placeholder {
-                    color: #c0c4cc;
-                }
-
-                &::-webkit-outer-spin-button,
-                &::-webkit-inner-spin-button {
-                    appearance: none;
-                    margin: 0;
-                }
-            }
-
-            span {
-                flex-shrink: 0;
-                color: #999;
-                font-size: 13px;
-            }
         }
         }
 
 
         &__submit {
         &__submit {
-            flex-shrink: 0;
-            height: 36px;
-            padding: 0 16px;
-            border-radius: 6px;
+            padding: 6px 21px;
+            border-radius: 4px;
             background: #2199f8;
             background: #2199f8;
             color: #fff;
             color: #fff;
-            font-size: 14px;
-            line-height: 36px;
             text-align: center;
             text-align: center;
         }
         }
 
 
         &__ref {
         &__ref {
             position: relative;
             position: relative;
-            margin: 12px 10px 10px;
-            overflow: hidden;
+            margin: 10px;
             border-radius: 8px;
             border-radius: 8px;
         }
         }
 
 
         &__ref-label {
         &__ref-label {
             position: absolute;
             position: absolute;
-            top: 8px;
-            left: 8px;
             z-index: 1;
             z-index: 1;
-            padding: 2px 8px;
-            border-radius: 10px;
-            background: rgba(60, 60, 60, 0.72);
+            padding: 3px 11px;
+            border-radius: 8px 0 8px 0;
+            background: rgba(0, 0, 0, 0.43);
+            backdrop-filter: blur(5px);
             color: #fff;
             color: #fff;
-            font-size: 11px;
-            line-height: 16px;
+            font-size: 12px;
         }
         }
 
 
         &__ref-img {
         &__ref-img {
@@ -364,63 +308,56 @@ const handleSubmit = () => {
         }
         }
     }
     }
 
 
-    .guide-card {
-        position: relative;
-        margin: 0 10px 16px;
-        padding: 14px 12px 12px;
-        overflow: visible;
-        border-radius: 12px;
-        background: #fff;
+    .guide-card__upload {
+        position: fixed;
+        right: 7px;
+        bottom: 94px;
+        z-index: 10;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        width: 58px;
+        height: 58px;
+        border-radius: 50%;
+        background: #2199f8;
+        color: #fff;
+        box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.1);
+        font-size: 15px;
+    }
 
 
-        &__title {
-            position: relative;
-            z-index: 1;
-            display: inline-block;
-            color: #1f1f1f;
-            font-size: 16px;
-            font-weight: 600;
-            line-height: 22px;
-
-            &::after {
-                content: "";
-                position: absolute;
-                left: 0;
-                bottom: 1px;
-                z-index: -1;
-                width: 64px;
-                height: 6px;
-                border-radius: 2px;
-                background: linear-gradient(90deg, #7cc5ff 0%, rgba(124, 197, 255, 0) 100%);
-            }
-        }
+    .guide-card__upload span {
+        margin-top: 3px;
+        font-size: 10px;
+    }
 
 
-        &__upload {
-            position: absolute;
-            top: 8px;
-            right: 12px;
-            z-index: 2;
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            justify-content: center;
-            width: 72px;
-            height: 72px;
-            border-radius: 50%;
-            background: #2199f8;
-            color: #fff;
-            box-shadow: 0 4px 12px rgba(33, 153, 248, 0.35);
-        }
+    .guide-title {
+        position: relative;
+        z-index: 1;
+        display: inline-block;
+        font-size: 16px;
+        font-weight: 500;
 
 
-        &__upload-icon {
-            font-size: 22px;
-            font-weight: 600;
+        &::after {
+            content: "";
+            position: absolute;
+            left: 0;
+            bottom: 2px;
+            z-index: -1;
+            width: 90px;
+            height: 6px;
+            border-radius: 25px;
+            background: linear-gradient(90deg, #2199f8 0%, rgba(255, 255, 255, 0.37) 100%);
+            opacity: 0.4;
         }
         }
+    }
 
 
-        &__upload span {
-            margin-top: 2px;
-            font-size: 11px;
-            line-height: 14px;
-        }
+    .guide-card {
+        position: relative;
+        margin: 10px;
+        padding: 10px;
+        border-radius: 8px;
+        background: #fff;
 
 
         &__img {
         &__img {
             display: block;
             display: block;
@@ -430,93 +367,64 @@ const handleSubmit = () => {
     }
     }
 
 
     .situation-card {
     .situation-card {
-        position: relative;
-        z-index: 1;
-        margin: -16px 0 0;
-        padding: 16px 12px 20px;
-        min-height: calc(100% + 16px);
-        border-radius: 16px 16px 0 0;
-        background: #fff;
-
         &__head {
         &__head {
-            display: flex;
-            align-items: center;
-            gap: 8px;
-            padding-bottom: 12px;
-            border-bottom: 1px solid #eee;
-        }
-
-        &__icon {
-            display: flex;
-            width: 22px;
-            height: 22px;
+            position: relative;
+            z-index: 1;
+            margin: 0 10px 12px;
+            padding: 12px;
+            border-radius: 10px;
+            background: #fff;
 
 
-            svg {
-                display: block;
-                width: 100%;
-                height: 100%;
+            &:first-child {
+                margin-top: -24px;
             }
             }
         }
         }
 
 
         &__name {
         &__name {
+            display: flex;
+            align-items: stretch;
+            gap: 10px;
+            padding-bottom: 10px;
+            border-bottom: 1px solid rgba(0, 0, 0, 0.08);
             color: #2199f8;
             color: #2199f8;
             font-size: 16px;
             font-size: 16px;
-            font-weight: 600;
-            line-height: 22px;
+            font-weight: 500;
+        }
+
+        &__icon {
+            width: 18px;
+            height: 18px;
         }
         }
 
 
         &__assess {
         &__assess {
-            margin-top: 12px;
-            padding: 12px;
+            margin-top: 10px;
+            padding: 10px;
             border-radius: 8px;
             border-radius: 8px;
-            background: #f7f7f7;
+            background: #F9F9F9;
         }
         }
 
 
         &__assess-title {
         &__assess-title {
-            color: #1f1f1f;
-            font-size: 15px;
-            font-weight: 600;
-            line-height: 22px;
+            color: #4F4F4F;
+            font-size: 16px;
+            margin-top: -3px;
+            font-family: "PangMenZhengDao";
         }
         }
 
 
         &__assess-desc {
         &__assess-desc {
-            margin-top: 8px;
-            color: rgba(0, 0, 0, 0.45);
-            font-size: 13px;
-            line-height: 20px;
+            margin-top: 4px;
+            color: #9C9C9C;
         }
         }
 
 
         &__guide {
         &__guide {
-            margin-top: 16px;
-        }
-
-        &__guide-title {
-            position: relative;
-            z-index: 1;
-            display: inline-block;
-            color: #1f1f1f;
-            font-size: 16px;
-            font-weight: 600;
-            line-height: 22px;
-
-            &::after {
-                content: "";
-                position: absolute;
-                left: 0;
-                bottom: 1px;
-                z-index: -1;
-                width: 64px;
-                height: 6px;
-                border-radius: 2px;
-                background: linear-gradient(90deg, #7cc5ff 0%, rgba(124, 197, 255, 0) 100%);
-            }
+            margin-top: 10px;
+            padding: 10px;
+            border: 1px solid rgba(33, 153, 248, 0.2);
+            border-radius: 8px;
+            background: linear-gradient(360deg, #FFFFFF 90.33%, #ABD8FC 116.61%, #2199F8 148.55%);
         }
         }
 
 
         &__guide-box {
         &__guide-box {
-            margin-top: 10px;
-            overflow: hidden;
-            border: 1px solid #ddecff;
-            border-radius: 8px;
+            margin-top: 12px;
 
 
             img {
             img {
                 display: block;
                 display: block;

+ 350 - 0
src/views/old_mini/agri_file/pages/pestInteract.vue

@@ -0,0 +1,350 @@
+<template>
+    <div class="pest-interact-page">
+        <custom-header :name="t('agriFile.pestInteract')" />
+        <div class="pest-interact-content">
+            <div class="map-container" ref="mapContainer"></div>
+            <div class="search-bar">
+                <div class="search-bar__city">
+                    <span class="search-bar__city-name van-ellipsis">{{ cityName }}</span>
+                    <el-icon class="search-bar__city-icon">
+                        <ArrowDown />
+                    </el-icon>
+                </div>
+                <location-search class="search-bar__search" :user-location="userLocation"
+                    @change="handleLocationChange" />
+            </div>
+            <div class="map-tip">{{ t("agriFile.selectAbnormalPlot") }}</div>
+            <div class="locate-btn" @click="handleLocate">
+                <img class="locate-btn__icon" src="@/assets/img/map/map-icon.png" alt="" />
+            </div>
+        </div>
+        <div class="custom-bottom-fixed-btns">
+            <div class="bottom-btn secondary-btn" @click="handleCancel">{{ t("agriFile.cancel") }}</div>
+            <div class="bottom-btn primary-btn" @click="handleConfirm">{{ t("agriFile.confirmPlot") }}</div>
+        </div>
+        <album-upload-popup v-model:show="showUploadPopup" />
+    </div>
+</template>
+
+<script setup>
+import { nextTick, onActivated, ref } from "vue";
+import { useRouter } from "vue-router";
+import { useStore } from "vuex";
+import { ElMessage } from "element-plus";
+import { ArrowDown } from "@element-plus/icons-vue";
+import customHeader from "@/components/customHeader.vue";
+import locationSearch from "@/components/pageComponents/locationSearch.vue";
+import albumUploadPopup from "../components/albumUploadPopup.vue";
+import FileMap from "../fileMap";
+import * as util from "@/common/ol_common.js";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+const router = useRouter();
+const store = useStore();
+const mapContainer = ref(null);
+const fileMap = new FileMap();
+const selectedZoneId = ref(null);
+const showUploadPopup = ref(false);
+let clickBound = false;
+
+const DEFAULT_MAP_LOCATION = "POINT(113.6142086995688 23.585836479509055)";
+const cityName = ref("");
+const userLocation = ref(
+    store.state.home.miniUserLocation ||
+    localStorage.getItem("MINI_USER_LOCATION") ||
+    "113.61702297075017,23.584863449735067"
+);
+
+const MOCK_ZONES = [
+    { id: 1, nameKey: "agriFile.legendZone", dlng: -0.00115, dlat: 0.00055, delta: 0.00155 },
+];
+
+function squarePolygonWkt(lng, lat, delta = 0.0014) {
+    const ring = [
+        [lng - delta, lat + delta * 0.7],
+        [lng + delta * 0.35, lat + delta],
+        [lng + delta, lat - delta * 0.2],
+        [lng + delta * 0.15, lat - delta],
+        [lng - delta * 0.85, lat - delta * 0.45],
+        [lng - delta, lat + delta * 0.7],
+    ];
+    return `POLYGON((${ring.map((point) => point.join(" ")).join(", ")}))`;
+}
+
+function getFarmData() {
+    try {
+        return JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
+    } catch {
+        return {};
+    }
+}
+
+function isPointWkt(value) {
+    return typeof value === "string" && /^POINT\s*\(/i.test(value.trim());
+}
+
+function wkbHexToPointWkt(hex) {
+    const text = String(hex || "").trim();
+    if (!/^[0-9a-fA-F]+$/.test(text) || text.length < 42) return null;
+    const bytes = new Uint8Array(text.length / 2);
+    for (let i = 0; i < bytes.length; i++) {
+        bytes[i] = parseInt(text.slice(i * 2, i * 2 + 2), 16);
+    }
+    const view = new DataView(bytes.buffer);
+    const little = view.getUint8(0) === 1;
+    let type = view.getUint32(1, little);
+    const hasSrid = (type & 0x20000000) !== 0;
+    type &= 0xff;
+    if (type !== 1) return null;
+    let offset = 5;
+    if (hasSrid) offset += 4;
+    if (offset + 16 > bytes.length) return null;
+    const lng = view.getFloat64(offset, little);
+    const lat = view.getFloat64(offset + 8, little);
+    if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null;
+    return `POINT(${lng} ${lat})`;
+}
+
+function toPointWkt(value) {
+    if (!value || typeof value !== "string") return null;
+    if (isPointWkt(value)) return value.trim();
+    return wkbHexToPointWkt(value);
+}
+
+function getFarmMapLocation() {
+    const farmData = getFarmData();
+    const candidates = [farmData.wkt, farmData.geom_wkt, farmData.farm_location];
+    for (const item of candidates) {
+        const pointWkt = toPointWkt(item);
+        if (pointWkt) return pointWkt;
+    }
+    return DEFAULT_MAP_LOCATION;
+}
+
+function getDefaultMapLocation() {
+    return (
+        toPointWkt(localStorage.getItem("MINI_USER_LOCATION_POINT")) ||
+        toPointWkt(store.state.home.miniUserLocationPoint) ||
+        getFarmMapLocation()
+    );
+}
+
+function getDefaultMapCoordinate() {
+    return util.wktCastGeom(getDefaultMapLocation()).getFirstCoordinate();
+}
+
+function loadPlotLayers() {
+    const farmData = getFarmData();
+    cityName.value = farmData.city || farmData.farm_city || t("agriFile.defaultCity");
+    const [lng, lat] = getDefaultMapCoordinate();
+    const labels = MOCK_ZONES.map((item) => ({
+        name: t(item.nameKey),
+        longitude: lng + item.dlng,
+        latitude: lat + item.dlat,
+    }));
+    const zoneRecords = MOCK_ZONES.map((item) => ({
+        id: item.id,
+        zone_name: "",
+        polygon: squarePolygonWkt(lng + item.dlng, lat + item.dlat, item.delta),
+    }));
+    fileMap.setRecordPolygons(zoneRecords, "album");
+    fileMap.setAlbumMarkers({ labels, photos: [] });
+    fileMap.recordPolygonLayer.source.getFeatures().forEach((feature, index) => {
+        feature.set("zone_id", MOCK_ZONES[index]?.id);
+    });
+    selectedZoneId.value = MOCK_ZONES[0]?.id ?? null;
+}
+
+function onMapClick(evt) {
+    const feature = fileMap.kmap?.map?.forEachFeatureAtPixel(evt.pixel, (hit) => {
+        if (hit.get("zone_id")) return hit;
+        return undefined;
+    });
+    if (!feature) return;
+    selectedZoneId.value = feature.get("zone_id");
+}
+
+function bindMapClick() {
+    if (clickBound || !fileMap.kmap?.map) return;
+    clickBound = true;
+    fileMap.kmap.map.on("singleclick", onMapClick);
+}
+
+const initMap = async () => {
+    await nextTick();
+    if (!mapContainer.value) return;
+    fileMap.initMap(getDefaultMapLocation(), mapContainer.value);
+    loadPlotLayers();
+    fileMap.kmap?.map?.updateSize?.();
+    bindMapClick();
+};
+
+const handleLocationChange = (payload) => {
+    if (!payload?.coordinateArray || !fileMap.kmap) return;
+    fileMap.kmap.getView().animate({
+        center: payload.coordinateArray,
+        zoom: 16,
+        duration: 0,
+    });
+    if (payload.city) cityName.value = payload.city;
+};
+
+const handleLocate = () => {
+    if (!fileMap.kmap) return;
+    fileMap.kmap.getView().animate({
+        center: getDefaultMapCoordinate(),
+        zoom: 16,
+        duration: 0,
+    });
+};
+
+const handleCancel = () => {
+    router.back();
+};
+
+const handleConfirm = () => {
+    if (!selectedZoneId.value) {
+        ElMessage.warning(t("agriFile.pleaseSelectPlot"));
+        return;
+    }
+    sessionStorage.setItem("SELECTED_ABNORMAL_PLOT", String(selectedZoneId.value));
+    showUploadPopup.value = true;
+};
+
+onActivated(() => {
+    initMap();
+});
+</script>
+
+<style lang="scss" scoped>
+.pest-interact-page {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: 100vh;
+    overflow: hidden;
+    background: #fff;
+}
+
+.pest-interact-content {
+    position: relative;
+    flex: 1;
+    min-height: 0;
+    overflow: hidden;
+
+    .map-container {
+        width: 100%;
+        height: 100%;
+    }
+
+    .search-bar {
+        position: absolute;
+        top: 12px;
+        left: 12px;
+        right: 12px;
+        z-index: 2;
+        display: flex;
+        align-items: center;
+        height: 40px;
+        padding: 0 4px 0 12px;
+        border-radius: 20px;
+        background: rgba(0, 0, 0, 0.45);
+        box-sizing: border-box;
+
+        &__city {
+            display: flex;
+            align-items: center;
+            flex-shrink: 0;
+            max-width: 92px;
+            color: #fff;
+            font-size: 14px;
+        }
+
+        &__city-name {
+            max-width: 72px;
+        }
+
+        &__city-icon {
+            margin-left: 2px;
+            font-size: 12px;
+        }
+
+        &__search {
+            flex: 1;
+            min-width: 0;
+            margin-left: 8px;
+
+            :deep(.el-select__wrapper) {
+                background: transparent;
+                box-shadow: none;
+                border: none;
+                min-height: 40px;
+                padding-left: 0;
+            }
+
+            :deep(.el-select__placeholder),
+            :deep(.el-select__input) {
+                color: rgba(255, 255, 255, 0.7);
+            }
+
+            :deep(.el-icon) {
+                color: rgba(255, 255, 255, 0.85);
+            }
+        }
+    }
+
+    .map-tip {
+        position: absolute;
+        top: 64px;
+        left: 50%;
+        z-index: 2;
+        transform: translateX(-50%);
+        padding: 8px 30px;
+        border-radius: 25px;
+        background: rgba(0, 0, 0, 0.5);
+        color: #7CC5FF;
+        font-size: 12px;
+        white-space: nowrap;
+    }
+
+    .locate-btn {
+        position: absolute;
+        right: 12px;
+        bottom: 24px;
+        z-index: 2;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 36px;
+        height: 36px;
+        border-radius: 8px;
+        background: #fff;
+        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+
+        &__icon {
+            width: 16px;
+            height: 18px;
+        }
+    }
+}
+
+.custom-bottom-fixed-btns {
+    position: relative;
+    justify-content: space-between;
+
+    .bottom-btn {
+        padding: 10px 20px;
+        border-radius: 25px;
+    }
+
+    .secondary-btn {
+        color: #666;
+        border: 1px solid rgba(153, 153, 153, 0.5);
+    }
+
+    .primary-btn {
+        background: #2199f8;
+    }
+}
+</style>