lxf 4 дней назад
Родитель
Сommit
b3d3539c8c

+ 1 - 0
src/components/popup/tipPopup.vue

@@ -37,6 +37,7 @@
             <img class="tip-icon success-icon" src="@/assets/img/home/create-farm-icon.png" alt="" />
             <div class="tip-text success-text">{{ text }} <span class="highlight-text">{{ highlightText }}</span> {{ text2 }}</div>
         </template>
+        <slot name="footer"></slot>
         <div class="tip-btn" v-if="!hideBtn" @click.stop="handleBtnClick">
             {{ buttonText || (type === "create" ? "去创建农场" : "我知道了") }}
         </div>

+ 30 - 1
src/i18n/messages.js

@@ -158,7 +158,9 @@ export default {
             interactQuestion: "互动问题",
             interactQuestionText: "具体问题具体问题具体问题具体问题具体?",
             ratioPlaceholder: "请输入比例",
+            pleaseSelectOption: "请先选择一个选项",
             submit: "提交",
+            submitSuccess: "提交成功",
             referenceImage: "参考图片",
             patrolGuide: "巡园向导",
             growthAbnormal: "长势异常",
@@ -184,6 +186,8 @@ export default {
             confirmLeave: "确认离开",
             agriAlbum: "农情相册",
             diagnosisReport: "诊断报告",
+            diagnosisReportGuidePrefix: "按照巡园要点 ",
+            diagnosisReportGuideSuffix: " 可获得种植诊断报告",
             reportUpdate: "更新",
             restoreFillQuestions: "填写以下问题",
             restorePlantingService: "立即恢复种植服务",
@@ -201,12 +205,23 @@ export default {
             addZone: "新增分区",
             drawFarmArea: "勾画区域",
             addAlbum: "新增相册",
-            selectZoneHint: "请勾选您的分区区域",
+            selectZoneHint: "请勾选新增分区的范围",
             drawFarmAreaHint: "请勾画农场区域",
             drawFarmAreaSuccess: "勾画农场区域成功",
             searchLocation: "搜索位置",
             defaultCity: "武汉市",
             cancelSelect: "取消勾选",
+            plantCrop: "种植作物",
+            cropCategory: "品类",
+            cropVariety: "品种",
+            plantStartPoint: "起种点问题",
+            selectTime: "选择时间",
+            regionName: "区域名称",
+            regionNamePlaceholder: "请输入区域名称",
+            pleaseSelectCategory: "请选择品类",
+            pleaseSelectVariety: "请选择品种",
+            pleaseSelectPlantDate: "请选择种植时间",
+            pleaseInputRegionName: "请输入区域名称",
             confirmArea: "确认区域",
             pleaseDrawZone: "请先勾画分区区域",
             createZoneSuccess: "分区创建成功",
@@ -587,6 +602,7 @@ export default {
             interactQuestion: "Interactive question",
             interactQuestionText: "Specific question specific question specific question?",
             ratioPlaceholder: "Enter ratio",
+            pleaseSelectOption: "Please select an option first",
             submit: "Submit",
             referenceImage: "Reference image",
             patrolGuide: "Patrol guide",
@@ -613,6 +629,8 @@ export default {
             confirmLeave: "Leave",
             agriAlbum: "Agri album",
             diagnosisReport: "Diagnosis report",
+            diagnosisReportGuidePrefix: "Follow patrol key points, ",
+            diagnosisReportGuideSuffix: " to get a planting diagnosis report",
             reportUpdate: "Update",
             restoreFillQuestions: "Answer the questions",
             restorePlantingService: "Restore planting service",
@@ -636,6 +654,17 @@ export default {
             searchLocation: "Search location",
             defaultCity: "Wuhan",
             cancelSelect: "Clear selection",
+            plantCrop: "Planted crop",
+            cropCategory: "Category",
+            cropVariety: "Variety",
+            plantStartPoint: "Planting start point",
+            selectTime: "Select time",
+            regionName: "Area name",
+            regionNamePlaceholder: "Please enter area name",
+            pleaseSelectCategory: "Please select a category",
+            pleaseSelectVariety: "Please select a variety",
+            pleaseSelectPlantDate: "Please select planting time",
+            pleaseInputRegionName: "Please enter area name",
             confirmArea: "Confirm area",
             pleaseDrawZone: "Please draw a zone first",
             createZoneSuccess: "Zone created successfully",

+ 339 - 0
src/views/old_mini/agri_file/components/addZoneInfoPopup.vue

@@ -0,0 +1,339 @@
+<template>
+    <popup
+        v-model:show="showValue"
+        round
+        closeable
+        class="add-zone-info-popup"
+        :close-on-click-overlay="false"
+        teleport="body"
+    >
+        <div class="add-zone-info-popup__content">
+            <div class="form-block">
+                <div class="form-block__title">{{ t("agriFile.plantCrop") }}</div>
+                <div class="form-block__row">
+                    <el-select
+                        v-model="form.categoryId"
+                        class="form-select"
+                        :placeholder="t('agriFile.cropCategory')"
+                        clearable
+                        filterable
+                        :loading="categoryLoading"
+                        @change="handleCategoryChange"
+                    >
+                        <el-option
+                            v-for="item in categoryOptions"
+                            :key="item.id"
+                            :label="item.name"
+                            :value="item.id"
+                        />
+                    </el-select>
+                    <el-select
+                        v-model="form.varietyId"
+                        class="form-select"
+                        :placeholder="t('agriFile.cropVariety')"
+                        clearable
+                        filterable
+                        :loading="varietyLoading"
+                        :disabled="!form.categoryId"
+                    >
+                        <el-option
+                            v-for="item in varietyOptions"
+                            :key="item.id"
+                            :label="item.name"
+                            :value="item.id"
+                        />
+                    </el-select>
+                </div>
+            </div>
+
+            <div class="form-block">
+                <div class="form-block__title">{{ startingPointQuestion }}</div>
+                <div class="form-date-wrap">
+                    <el-date-picker
+                        v-model="form.plantDate"
+                        class="form-date"
+                        type="date"
+                        :placeholder="t('agriFile.selectTime')"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
+                        :editable="false"
+                        :clearable="false"
+                        :prefix-icon="null"
+                        style="width: 100%"
+                    />
+                    <el-icon class="form-date-wrap__icon"><Clock /></el-icon>
+                </div>
+            </div>
+
+            <div class="form-block">
+                <div class="form-block__title">{{ t("agriFile.regionName") }}</div>
+                <el-input
+                    v-model="form.zoneName"
+                    :placeholder="t('agriFile.regionNamePlaceholder')"
+                />
+            </div>
+
+            <div class="add-zone-info-popup__confirm" @click="handleConfirm">
+                {{ t("agriFile.confirmArea") }}
+            </div>
+        </div>
+    </popup>
+</template>
+
+<script setup>
+import { computed, reactive, ref, watch } from "vue";
+import { Popup } from "vant";
+import { ElMessage } from "element-plus";
+import { Clock } from "@element-plus/icons-vue";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+
+const FIRST_CROPS = ["果树", "大田"];
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+});
+
+const emit = defineEmits(["update:show", "confirm"]);
+
+const form = reactive({
+    categoryId: "",
+    varietyId: "",
+    plantDate: "",
+    zoneName: "",
+});
+
+const categoryLoading = ref(false);
+const varietyLoading = ref(false);
+const categoryOptions = ref([]);
+const varietyOptions = ref([]);
+const startingPointQuestion = ref(t("agriFile.plantStartPoint"));
+const categoryLoaded = ref(false);
+
+const showValue = computed({
+    get: () => props.show,
+    set: (value) => emit("update:show", value),
+});
+
+const flattenThirdCrops = (list) => {
+    if (!Array.isArray(list)) return [];
+    const map = new Map();
+    list.forEach((group) => {
+        (group?.third_crops || []).forEach((crop) => {
+            if (crop?.id == null || !crop?.name) return;
+            map.set(String(crop.id), {
+                id: crop.id,
+                name: crop.name,
+            });
+        });
+    });
+    return Array.from(map.values());
+};
+
+const fetchCategoryOptions = async () => {
+    if (categoryLoaded.value || categoryLoading.value) return;
+    categoryLoading.value = true;
+    try {
+        const results = await Promise.all(
+            FIRST_CROPS.map((firstCrop) =>
+                VE_API.entry.getCropHierarchy({ first_crop: firstCrop })
+            )
+        );
+        const merged = [];
+        results.forEach((res) => {
+            if (res?.code === 200) {
+                merged.push(...flattenThirdCrops(res.data));
+            }
+        });
+        // 按 id 去重(果树/大田可能有交叉)
+        const uniqueMap = new Map();
+        merged.forEach((item) => uniqueMap.set(String(item.id), item));
+        categoryOptions.value = Array.from(uniqueMap.values());
+        categoryLoaded.value = true;
+    } catch {
+        categoryOptions.value = [];
+        ElMessage.error("获取种植品类失败,请稍后再试");
+    } finally {
+        categoryLoading.value = false;
+    }
+};
+
+const fetchVarietyOptions = async (cropId) => {
+    if (cropId == null || cropId === "") {
+        varietyOptions.value = [];
+        startingPointQuestion.value = t("agriFile.plantStartPoint");
+        return;
+    }
+    varietyLoading.value = true;
+    try {
+        const res = await VE_API.entry.getVarietiesByCrop({
+            crop_id: cropId,
+        });
+        if (res.code === 200 && res.data) {
+            varietyOptions.value = (res.data.varieties || []).map((item) => ({
+                id: item.id,
+                name: item.name,
+            }));
+            startingPointQuestion.value =
+                res.data.starting_point_question || t("agriFile.plantStartPoint");
+        } else {
+            varietyOptions.value = [];
+            startingPointQuestion.value = t("agriFile.plantStartPoint");
+            ElMessage.error(res.msg || "获取品种列表失败");
+        }
+    } catch {
+        varietyOptions.value = [];
+        startingPointQuestion.value = t("agriFile.plantStartPoint");
+        ElMessage.error("获取品种列表失败,请稍后再试");
+    } finally {
+        varietyLoading.value = false;
+    }
+};
+
+const resetForm = () => {
+    form.categoryId = "";
+    form.varietyId = "";
+    form.plantDate = "";
+    form.zoneName = "";
+    varietyOptions.value = [];
+    startingPointQuestion.value = t("agriFile.plantStartPoint");
+};
+
+watch(
+    () => props.show,
+    (val) => {
+        if (!val) return;
+        resetForm();
+        fetchCategoryOptions();
+    }
+);
+
+const handleCategoryChange = (cropId) => {
+    form.varietyId = "";
+    fetchVarietyOptions(cropId);
+};
+
+const handleConfirm = () => {
+    if (!form.categoryId) {
+        ElMessage.warning(t("agriFile.pleaseSelectCategory"));
+        return;
+    }
+    if (!form.varietyId) {
+        ElMessage.warning(t("agriFile.pleaseSelectVariety"));
+        return;
+    }
+    if (!form.plantDate) {
+        ElMessage.warning(t("agriFile.pleaseSelectPlantDate"));
+        return;
+    }
+    const zoneName = form.zoneName.trim();
+    if (!zoneName) {
+        ElMessage.warning(t("agriFile.pleaseInputRegionName"));
+        return;
+    }
+
+    const category = categoryOptions.value.find(
+        (item) => String(item.id) === String(form.categoryId)
+    );
+    const variety = varietyOptions.value.find(
+        (item) => String(item.id) === String(form.varietyId)
+    );
+
+    emit("confirm", {
+        zone_name: zoneName,
+        category_id: form.categoryId,
+        category_name: category?.name || "",
+        variety_id: form.varietyId,
+        variety_name: variety?.name || "",
+        plant_date: form.plantDate,
+    });
+    emit("update:show", false);
+};
+</script>
+
+<style scoped lang="scss">
+.add-zone-info-popup {
+    width: 330px;
+
+    :deep(.van-popup__close-icon) {
+        color: #333;
+        font-size: 18px;
+    }
+
+    &__content {
+        padding: 24px 16px 20px;
+        background: linear-gradient(360deg, #ffffff 74.2%, #d1ebff 100%);
+        border-radius: 16px;
+        box-sizing: border-box;
+    }
+
+    .form-block {
+        & + .form-block {
+            margin-top: 16px;
+        }
+
+        &__title {
+            margin-bottom: 10px;
+            font-size: 16px;
+            font-weight: 500;
+            color: #000;
+        }
+
+        &__row {
+            display: flex;
+            gap: 10px;
+        }
+    }
+
+    .form-select {
+        flex: 1;
+        min-width: 0;
+    }
+
+    :deep(.el-select__wrapper),
+    :deep(.el-input__wrapper) {
+        min-height: 40px;
+        border-radius: 4px;
+        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1) inset;
+        background: #fff;
+    }
+
+    :deep(.el-date-editor.el-input) {
+        width: 100%;
+    }
+
+    :deep(.el-date-editor .el-input__wrapper) {
+        padding-left: 12px;
+        padding-right: 36px;
+    }
+
+    .form-date-wrap {
+        position: relative;
+
+        &__icon {
+            position: absolute;
+            right: 12px;
+            top: 50%;
+            transform: translateY(-50%);
+            color: rgba(0, 0, 0, 0.35);
+            font-size: 16px;
+            pointer-events: none;
+            z-index: 1;
+        }
+    }
+
+    &__confirm {
+        margin-top: 24px;
+        padding: 10px;
+        border-radius: 25px;
+        background: #2199f8;
+        color: #fff;
+        font-size: 16px;
+        text-align: center;
+    }
+}
+</style>

+ 128 - 13
src/views/old_mini/agri_file/index.vue

@@ -2,10 +2,30 @@
     <div class="agri-file-page" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
         <farm-header />
         <div class="nav-cards">
-            <div v-for="item in navCards" :key="item.key" class="nav-card"
-                :class="{ 'is-active': activeNav === item.key }" @click="activeNav = item.key">
-                <img class="nav-card__icon" :src="item.icon" alt="" />
-                <span class="nav-card__label">{{ t(item.labelKey) }}</span>
+            <div
+                v-for="item in navCards"
+                :key="item.key"
+                class="nav-card-wrap"
+                :class="{ 'nav-card-wrap--report': item.key === 'report' }"
+            >
+                <div
+                    class="nav-card"
+                    :class="{ 'is-active': activeNav === item.key }"
+                    @click="handleNavClick(item)"
+                >
+                    <img class="nav-card__icon" :src="item.icon" alt="" />
+                    <span class="nav-card__label">{{ t(item.labelKey) }}</span>
+                </div>
+                <div v-if="item.key === 'report' && guideVisible" class="report-guide-bubble">
+                    <span class="report-guide-bubble__arrow"></span>
+                    <div class="report-guide-bubble__content">
+                        {{ t("agriFile.diagnosisReportGuidePrefix") }}
+                        <span class="report-guide-bubble__link" @click.stop="handleGuideUpload">
+                            {{ t("agriFile.uploadPhoto") }}
+                        </span>
+                        {{ t("agriFile.diagnosisReportGuideSuffix") }}
+                    </div>
+                </div>
             </div>
         </div>
         <div class="content-panel">
@@ -36,7 +56,7 @@
                                     <img :src="item.icon" alt="" />
                                     <span class="tracking-item__reason">{{ item.subject }}</span>
                                 </div>
-                                <div class="tracking-item__issue">{{ item.issue }}</div>
+                                <!-- <div class="tracking-item__issue">{{ item.issue }}</div> -->
                             </div>
                             <div class="tracking-item__btn" @click.stop="openUploadPopup">
                                 <el-icon>
@@ -57,11 +77,11 @@
                     <div ref="mapContainer" class="map-container"></div>
                     <div v-if="noFarmRange" class="map-mask" @click.stop="goAddZone('farm')">
                         <div class="mask-content">
-                            <div class="mask-content__title">勾画农场区域,关联作物照片</div>
+                            <div class="mask-content__title">勾画种植区域,关联作物照片</div>
                             <div class="mask-content__btn">去勾选</div>
                         </div>
                     </div>
-                    <div v-else class="add-zone-btn" @click.stop="goAddZone">
+                    <div v-else class="add-zone-btn" @click.stop="goAddZone('region')">
                         <el-icon>
                             <Plus />
                         </el-icon>
@@ -77,8 +97,8 @@
                         <div class="zone-marker__arrow"></div>
                     </div>
                 </div>
-                <div class="album-grid">
-                    <div class="album-add" @click.stop="goAddZone">
+                <div class="album-grid" v-if="!noFarmRange">
+                    <div class="album-add" @click.stop="goAddZone('region')">
                         <el-icon class="album-add__icon">
                             <Plus />
                         </el-icon>
@@ -148,6 +168,9 @@ const store = useStore();
 const router = useRouter();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
 
+const DIAGNOSIS_GUIDE_STORAGE_KEY = "AGRI_FILE_DIAGNOSIS_REPORT_GUIDE";
+const guideVisible = ref(false);
+
 const defaultCover = require("@/assets/img/home/banner.png");
 const DEFAULT_CENTER = [113.6142086995688, 23.585836479509055];
 
@@ -157,7 +180,7 @@ const fileMap = new FileMap();
 const markerEls = {};
 const mapOverlays = [];
 
-const noFarmRange = ref(true);
+const noFarmRange = ref(false);
 
 const navCards = [
     {
@@ -201,7 +224,7 @@ const showUploadPopup = ref(false);
 const showCompleteFarmPopup = ref(false);
 const showDiagnosisReportPopup = ref(false);
 // 后续由接口控制是否展示恢复种植服务弹窗
-const showRestorePlantingPopup = ref(true);
+const showRestorePlantingPopup = ref(false);
 const patrolList = ref([
     {
         id: 1,
@@ -306,7 +329,7 @@ const fillMockLabels = () => {
         level: t("agriFile.riskLevel2"),
         title: t(index === 0 ? "agriFile.patrolGrowthTitle" : "agriFile.patrolAbnormalTitle"),
         zone: albumName,
-        subject: t("agriFile.patrolSubject"),
+        subject: t("agriFile.patrolSubject") + '主题主题主题主题主题主题主题主题主题',
         issue: t("agriFile.patrolIssue"),
     }));
     cropArchiveList.value = cropArchiveList.value.map((item, index) => ({
@@ -342,6 +365,39 @@ const openUploadPopup = () => {
     showUploadPopup.value = true;
 };
 
+const dismissGuide = () => {
+    if (!guideVisible.value) return;
+    guideVisible.value = false;
+    localStorage.setItem(DIAGNOSIS_GUIDE_STORAGE_KEY, "1");
+};
+
+const tryShowGuide = () => {
+    if (localStorage.getItem(DIAGNOSIS_GUIDE_STORAGE_KEY)) return;
+    guideVisible.value = true;
+};
+
+const handleGuideUpload = () => {
+    dismissGuide();
+    openUploadPopup();
+};
+
+const handleNavClick = (item) => {
+    if (item.key === "report") dismissGuide();
+    activeNav.value = item.key;
+};
+
+const handleOutsideClick = (event) => {
+    if (!guideVisible.value) return;
+    const target = event.target;
+    if (
+        target?.closest?.(".report-guide-bubble") ||
+        target?.closest?.(".nav-card-wrap--report .nav-card")
+    ) {
+        return;
+    }
+    dismissGuide();
+};
+
 const goCompleteFarmInfo = () => {
     router.push("/entry_information");
 };
@@ -372,12 +428,18 @@ const goAlbumDetail = (item) => {
 onMounted(() => {
     fillMockLabels();
     initAlbumMap();
+    tryShowGuide();
+    document.addEventListener("click", handleOutsideClick, true);
 });
 onActivated(() => {
     fillMockLabels();
     initAlbumMap();
+    tryShowGuide();
+});
+onBeforeUnmount(() => {
+    clearMapOverlays();
+    document.removeEventListener("click", handleOutsideClick, true);
 });
-onBeforeUnmount(clearMapOverlays);
 
 watch(activeNav, (key) => {
     if (key === "album") {
@@ -404,6 +466,14 @@ watch(activeNav, (key) => {
         padding: 10px 10px;
         margin-bottom: 10px;
 
+        .nav-card-wrap {
+            position: relative;
+
+            &--report {
+                z-index: 5;
+            }
+        }
+
         .nav-card {
             position: relative;
             display: flex;
@@ -447,6 +517,45 @@ watch(activeNav, (key) => {
                 }
             }
         }
+
+        .report-guide-bubble {
+            position: absolute;
+            top: calc(100% + 10px);
+            right: 22px;
+            width: 216px;
+            padding: 6px 10px;
+            border-radius: 6px;
+            background: rgba(0, 0, 0, 0.8);
+            backdrop-filter: blur(4px);
+            box-sizing: border-box;
+            z-index: 20;
+            pointer-events: auto;
+
+            &__arrow {
+                position: absolute;
+                top: -5px;
+                right: 16px;
+                width: 0;
+                height: 0;
+                border-left: 6px solid transparent;
+                border-right: 6px solid transparent;
+                border-bottom: 6px solid rgba(0, 0, 0, 0.8);
+            }
+
+            &__content {
+                font-size: 14px;
+                line-height: 22px;
+                color: #fff;
+                word-break: break-all;
+                text-align: justify;
+            }
+
+            &__link {
+                padding: 0 2px;
+                color: #2199f8;
+                cursor: pointer;
+            }
+        }
     }
 
     .content-panel {
@@ -860,7 +969,13 @@ watch(activeNav, (key) => {
                 font-size: 12px;
             }
 
+            &__action-main {
+                flex: 1;
+                padding-right: 4px;
+            }
+
             &__btn {
+                flex: none;
                 display: flex;
                 align-items: center;
                 gap: 2px;

+ 62 - 16
src/views/old_mini/agri_file/pages/addZone.vue

@@ -7,7 +7,8 @@
                 <location-search class="search-bar__search" :user-location="userLocation"
                     @change="handleLocationChange" />
             </div>
-            <div class="map-tip">{{ isFarmDrawMode ? t("agriFile.drawFarmAreaHint") : t("agriFile.selectZoneHint") }}</div>
+            <div class="map-tip normal-text" v-show="isFarmDrawMode">请勾画 <span class="map-tip__highlight">桂味</span> 的种植区域</div>
+            <div class="map-tip" v-show="!isFarmDrawMode">{{ t("agriFile.selectZoneHint") }}</div>
             <div class="locate-btn" @click="handleLocate">
                 <img class="locate-btn__icon" src="@/assets/img/map/map-icon.png" alt="" />
             </div>
@@ -16,7 +17,8 @@
             <div class="bottom-btn secondary-btn" @click="handleClearDraw">{{ t("agriFile.cancelSelect") }}</div>
             <div class="bottom-btn primary-btn" @click="handleConfirmDraw">{{ t("agriFile.confirmArea") }}</div>
         </div>
-        <RegionNamePopup v-model:show="showRegionNamePopup" @confirm="handleRegionNameConfirm" />
+        <RegionNamePopup v-model:show="showRegionNamePopup" @confirm="handleFarmNameConfirm" />
+        <add-zone-info-popup v-model:show="showZoneInfoPopup" @confirm="handleZoneInfoConfirm" />
     </div>
 </template>
 
@@ -28,6 +30,7 @@ import { ElMessage } from "element-plus";
 import customHeader from "@/components/customHeader.vue";
 import locationSearch from "@/components/pageComponents/locationSearch.vue";
 import RegionNamePopup from "@/views/old_mini/recordDetails/components/RegionNamePopup.vue";
+import addZoneInfoPopup from "../components/addZoneInfoPopup.vue";
 import MapManage from "@/views/old_mini/recordDetails/map/mapManage.js";
 import { POINT_ICON } from "@/views/old_mini/entry_information/map/selectLocationMap.js";
 import * as util from "@/common/ol_common.js";
@@ -37,10 +40,16 @@ const { t } = useI18n();
 const route = useRoute();
 const router = useRouter();
 const isFarmDrawMode = computed(() => route.query.type === "farm");
+const targetUrl = computed(() => route.query.targetUrl);
+const isAbnormal = computed(() => {
+    const val = route.query.isAbnormal;
+    return val === true || val === "true";
+});
 const store = useStore();
 const mapContainer = ref(null);
 const mapManage = new MapManage();
 const showRegionNamePopup = ref(false);
+const showZoneInfoPopup = ref(false);
 
 const DEFAULT_MAP_LOCATION = "POINT(113.6142086995688 23.585836479509055)";
 const FARM_DRAW_LOCATION = "POINT(113.31812938867188 23.002427029296875)";
@@ -136,7 +145,7 @@ function initMapView() {
     });
     mapManage.enableRegionDrawing();
     if (isFarmDrawMode.value) {
-        mapManage.showCenterMarker(getFarmDrawCoordinate(), POINT_ICON.resident);
+        mapManage.showCenterMarker(getFarmDrawCoordinate(), POINT_ICON.plant);
     }
 }
 
@@ -149,7 +158,7 @@ const handleLocate = () => {
     if (isFarmDrawMode.value) {
         const coordinate = getFarmDrawCoordinate();
         mapManage.setMapPosition(coordinate);
-        mapManage.showCenterMarker(coordinate, POINT_ICON.resident);
+        mapManage.showCenterMarker(coordinate, POINT_ICON.plant);
         return;
     }
     mapManage.setMapPosition(getDefaultMapCoordinate());
@@ -166,25 +175,55 @@ const handleConfirmDraw = () => {
         return;
     }
     if (isFarmDrawMode.value) {
-        ElMessage.success(t("agriFile.drawFarmAreaSuccess"));
+        showRegionNamePopup.value = true;
+        return;
+    }
+    showZoneInfoPopup.value = true;
+};
+
+const leaveAfterConfirm = () => {
+    if (targetUrl.value) {
+        setTimeout(() => {
+            router.push(targetUrl.value);
+        }, 800);
+        return;
+    }
+    setTimeout(() => {
         router.back();
+    }, 800);
+};
+
+const handleFarmNameConfirm = (regionName) => {
+    const payload = mapManage.getAreaGeometry();
+    const geometryData = {
+        zone_name: regionName,
+        geometryArr: payload.geometryArr,
+        mianji: payload.mianji,
+    };
+    sessionStorage.setItem("FARM_RANGE_GEOMETRY", JSON.stringify(geometryData));
+    ElMessage.success(t("agriFile.drawFarmAreaSuccess"));
+    if (!isAbnormal.value) {
+        setTimeout(() => {
+            const query = { showUpload: "1" };
+            if (route.query.trackId) query.id = route.query.trackId;
+            if (route.query.trackType) query.type = route.query.trackType;
+            router.replace({ path: "/growth_track", query });
+        }, 800);
         return;
     }
-    showRegionNamePopup.value = true;
+    leaveAfterConfirm();
 };
 
-const handleRegionNameConfirm = (regionName) => {
+const handleZoneInfoConfirm = (formData) => {
     const payload = mapManage.getAreaGeometry();
-    sessionStorage.setItem(
-        "ADD_ZONE_GEOMETRY",
-        JSON.stringify({
-            zone_name: regionName,
-            geometryArr: payload.geometryArr,
-            mianji: payload.mianji,
-        })
-    );
+    const geometryData = {
+        ...formData,
+        geometryArr: payload.geometryArr,
+        mianji: payload.mianji,
+    };
+    sessionStorage.setItem("ADD_ZONE_GEOMETRY", JSON.stringify(geometryData));
     ElMessage.success(t("agriFile.createZoneSuccess"));
-    router.back();
+    leaveAfterConfirm();
 };
 
 onActivated(() => {
@@ -265,6 +304,13 @@ onDeactivated(() => {
         background: rgba(0, 0, 0, 0.5);
         color: #7CC5FF;
         font-size: 12px;
+        &.normal-text {
+            color: #fff;
+        }
+
+        &__highlight {
+            color: #2199F8;
+        }
     }
 
     .locate-btn {

+ 162 - 37
src/views/old_mini/agri_file/pages/growthTrack.vue

@@ -6,7 +6,10 @@
                 <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__title">速长窗口不追肥?后期发育慢人一步,直接掉队!</span>
+                        </div>
+                        <div class="growth-hero__tags">
+
                             <span class="growth-hero__tag growth-hero__tag--level">
                                 <el-icon>
                                     <BellFilled />
@@ -15,7 +18,6 @@
                             </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" />
@@ -37,13 +39,13 @@
                     </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 class="interact-card__options">
+                            <div v-for="item in interactOptions" :key="item.id" class="interact-option"
+                                :class="{ selected: selectedOptionId === item.id }" @click="selectedOptionId = item.id">
+                                <span class="interact-option__text">{{ item.name }}</span>
+                            </div>
                         </div>
+                        <div class="interact-card__submit" @click="handleSubmit">{{ t("agriFile.submit") }}</div>
                     </div>
                     <div class="interact-card__ref">
                         <span class="interact-card__ref-label">{{ t("agriFile.referenceImage") }}</span>
@@ -75,25 +77,32 @@
             </div>
         </div>
         <div class="guide-card__upload" @click="handleUploadClick">
-            <el-icon><Plus /></el-icon>
+            <el-icon>
+                <Plus />
+            </el-icon>
             <span>{{ t("agriFile.uploadPhoto") }}</span>
         </div>
-        <album-upload-popup v-model:show="showUploadPopup" />
-        <leave-confirm-popup
-            v-model:show="showLeavePopup"
-            @confirm="confirmLeave"
-        />
+        <album-upload-popup v-model:show="showUploadPopup" @confirm="handleUploadConfirm" />
+        <tip-popup v-model:show="showSuccessPopup" type="executeSuccess" text="您已上传成功" text2="请等待诊断报告生成" hideBtn>
+            <template #footer>
+                <div class="bottom-btn-container">
+                    <div class="bottom-btn primary-btn" @click="handleComplete">完成</div>
+                </div>
+            </template>
+        </tip-popup>
+        <leave-confirm-popup v-model:show="showLeavePopup" @confirm="confirmLeave" />
     </div>
 </template>
 
 <script setup>
-import { computed, ref } from "vue";
+import { computed, nextTick, onActivated, ref } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { Icon } from 'vant';
 import { ElMessage } from "element-plus";
 import customHeader from "@/components/customHeader.vue";
 import albumUploadPopup from "../components/albumUploadPopup.vue";
 import leaveConfirmPopup from "@/components/popup/leaveConfirmPopup.vue";
+import tipPopup from "@/components/popup/tipPopup.vue";
 import { useI18n } from "@/i18n";
 
 const { t } = useI18n();
@@ -105,8 +114,15 @@ const theme = computed(() => t("agriFile.interactTheme"));
 const level = computed(() => t("agriFile.riskLevel2"));
 const reason = computed(() => t("agriFile.interactReason"));
 const zoneName = computed(() => t("agriFile.zoneOne"));
-const ratio = ref("");
+/** 互动选项,后续由接口赋值 */
+const interactOptions = ref([
+    { id: 1, name: "选项一" },
+    { id: 2, name: "选项二" },
+    { id: 3, name: "选项三" },
+]);
+const selectedOptionId = ref(null);
 const showUploadPopup = ref(false);
+const showSuccessPopup = ref(false);
 const showLeavePopup = ref(false);
 const situationList = [
     { nameKey: "agriFile.growthAbnormal" },
@@ -114,25 +130,54 @@ const situationList = [
 ];
 
 const handleSubmit = () => {
-    if (ratio.value === "" || ratio.value == null) {
-        ElMessage.warning(t("agriFile.ratioPlaceholder"));
+    if (selectedOptionId.value == null) {
+        ElMessage.warning(t("agriFile.pleaseSelectOption"));
         return;
     }
-    ElMessage.success(t("agriFile.submit"));
+    ElMessage.success(t("agriFile.submitSuccess"));
 };
 
+const needDraw = ref(true);
 const handleUploadClick = () => {
+    if (needDraw.value) {
+        router.push({
+            path: "/add_zone",
+            query: {
+                type: "farm",
+                targetUrl: isAbnormal.value ? "/pest_interact" : "/growth_track",
+                ...(isAbnormal.value ? { isAbnormal: "true" } : {}),
+                ...(route.query.id ? { trackId: route.query.id } : {}),
+                ...(route.query.type ? { trackType: route.query.type } : {}),
+            },
+        });
+        return;
+    }
     if (isAbnormal.value) {
-        router.push("/pest_interact");
+        router.push({
+            path: "/pest_interact",
+        });
         return;
     }
     showUploadPopup.value = true;
 };
 
-const hasRatio = () => ratio.value !== "" && ratio.value != null;
+const handleUploadConfirm = () => {
+    showSuccessPopup.value = true;
+};
+
+const handleComplete = () => {
+    showSuccessPopup.value = false;
+    router.replace("/agri_file");
+};
+
+const handleContinueUpload = () => {
+    showSuccessPopup.value = false;
+};
+
+const hasAnswered = () => selectedOptionId.value != null;
 
 const handleBack = () => {
-    // if (!isAbnormal.value && !hasRatio()) {
+    // if (!isAbnormal.value && !hasAnswered()) {
     //     showLeavePopup.value = true;
     //     return;
     // }
@@ -142,6 +187,16 @@ const handleBack = () => {
 const confirmLeave = () => {
     router.back();
 };
+
+onActivated(() => {
+    if (route.query.showUpload !== "1") return;
+    needDraw.value = false;
+    nextTick(() => {
+        showUploadPopup.value = true;
+        const { showUpload, ...restQuery } = route.query;
+        router.replace({ path: route.path, query: restQuery });
+    });
+});
 </script>
 
 <style lang="scss" scoped>
@@ -176,7 +231,7 @@ const confirmLeave = () => {
             }
 
             &__title {
-                font-size: 28px;
+                font-size: 22px;
                 font-family: "PangMenZhengDao";
                 margin-top: -5px;
             }
@@ -200,14 +255,15 @@ const confirmLeave = () => {
             }
 
 
-            &__reason {
+            &__tags {
                 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%);
+                display: flex;
+                align-items: center;
+                gap: 8px;
             }
 
             &__share {
+                flex: none;
                 display: flex;
                 align-items: center;
                 gap: 4px;
@@ -286,23 +342,63 @@ const confirmLeave = () => {
             font-weight: 500;
         }
 
-        &__row {
+        &__options {
             display: flex;
-            align-items: center;
-            gap: 10px;
-            margin-top: 8px;
-        }
-
-        &__input {
-            flex: 1;
+            gap: 8px;
+            margin-top: 10px;
         }
 
         &__submit {
-            padding: 6px 21px;
-            border-radius: 4px;
+            width: 120px;
+            margin: 12px auto 0;
+            padding: 8px 0;
+            border-radius: 25px;
             background: #2199f8;
             color: #fff;
+            font-size: 14px;
+            text-align: center;
+        }
+
+        .interact-option {
+            position: relative;
+            flex: 1;
+            min-width: 0;
+            height: 36px;
+            line-height: 36px;
+            border-radius: 6px;
+            background: #fff;
+            border: 1px solid transparent;
             text-align: center;
+            box-sizing: border-box;
+            cursor: pointer;
+
+            &__text {
+                display: block;
+                padding: 0 6px;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                white-space: nowrap;
+                font-size: 14px;
+                color: #666;
+            }
+
+            &.selected {
+                border-color: #2199f8;
+
+                .interact-option__text {
+                    color: #2199f8;
+                }
+
+                &::after {
+                    content: "";
+                    position: absolute;
+                    top: -1px;
+                    right: -1px;
+                    width: 18px;
+                    height: 14px;
+                    background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
+                }
+            }
         }
 
         &__ref {
@@ -453,4 +549,33 @@ const confirmLeave = () => {
         }
     }
 }
+
+.bottom-btn-container {
+    display: flex;
+    justify-content: space-between;
+    gap: 12px;
+    width: 100%;
+
+    .bottom-btn {
+        padding: 0 25px;
+        border-radius: 4px;
+        font-size: 16px;
+        height: 40px;
+        line-height: 40px;
+        box-sizing: border-box;
+        font-weight: 500;
+        text-align: center;
+
+        &.secondary-btn {
+            color: #b1b1b1;
+            border: 1px solid #eaeaea;
+        }
+
+        &.primary-btn {
+            flex: 1;
+            background: #2199f8;
+            color: #fff;
+        }
+    }
+}
 </style>

+ 53 - 1
src/views/old_mini/agri_file/pages/pestInteract.vue

@@ -16,7 +16,21 @@
             <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" />
+        <album-upload-popup v-model:show="showUploadPopup" @confirm="handleUploadConfirm" />
+        <tip-popup
+            v-model:show="showSuccessPopup"
+            type="executeSuccess"
+            text="您已上传成功"
+            text2="请等待诊断报告生成"
+            hideBtn
+        >
+            <template #footer>
+                <div class="bottom-btn-container">
+                    <div class="bottom-btn secondary-btn" @click="handleComplete">完成</div>
+                    <div class="bottom-btn primary-btn" @click="handleContinueUpload">继续上传</div>
+                </div>
+            </template>
+        </tip-popup>
     </div>
 </template>
 
@@ -31,6 +45,7 @@ import albumUploadPopup from "../components/albumUploadPopup.vue";
 import FileMap from "../fileMap";
 import * as util from "@/common/ol_common.js";
 import { useI18n } from "@/i18n";
+import tipPopup from "@/components/popup/tipPopup.vue";
 
 const { t } = useI18n();
 const router = useRouter();
@@ -201,6 +216,17 @@ const handleConfirm = () => {
     showUploadPopup.value = true;
 };
 
+const showSuccessPopup = ref(false);
+const handleUploadConfirm = () => {
+    showSuccessPopup.value = true;
+};
+const handleComplete = () => {
+    showSuccessPopup.value = false;
+    router.replace("/agri_file");
+};
+const handleContinueUpload = () => {
+    showSuccessPopup.value = false;
+};
 onActivated(() => {
     initMap();
 });
@@ -317,4 +343,30 @@ onActivated(() => {
         background: #2199f8;
     }
 }
+
+.bottom-btn-container {
+    display: flex;
+    justify-content: space-between;
+    gap: 12px;
+    width: 100%;
+    .bottom-btn {
+        padding: 0 25px;
+        border-radius: 4px;
+        font-size: 16px;
+        height: 40px;
+        line-height: 40px;
+        box-sizing: border-box;
+        font-weight: 500;
+        text-align: center;
+        &.secondary-btn {
+            color: #B1B1B1;
+            border: 1px solid #EAEAEA;
+        }
+        &.primary-btn {
+            flex: 1;
+            background: #2199F8;
+            color: #fff;
+        }
+    }
+}
 </style>