Ver código fonte

feat:对接新增喝农场档案接口

wangsisi 1 semana atrás
pai
commit
6e5c592dd2

+ 8 - 3
src/api/modules/entry.js

@@ -11,9 +11,9 @@ module.exports = {
         url: config.base_new_url + "questionnaire/farmer_crop_pheno",
         type: "get",
     },
-    // 录入用户权属信息
-    addPlotInfo: {
-        url: config.base_new_url + "questionnaire/add_plot_info",
+    // 农户新增
+    addFarmInfo: {
+        url: config.base_new_url + "questionnaire/add_farm_info",
         type: "post",
     },
     // 品类列表
@@ -31,4 +31,9 @@ module.exports = {
         url: config.base_new_url + "questionnaire/farm_tasks",
         type: "get",
     },
+    // 作物档案-物候记录
+    getPhenoRecord: {
+        url: config.base_new_url + "questionnaire/pheno_record",
+        type: "get",
+    },
 }

+ 5 - 0
src/api/modules/report.js

@@ -16,6 +16,11 @@ module.exports = {
         url: config.base_dev_url + "container/reproductiveReport/listLatestByFarmRegion",
         type: "get",
     },
+    // 生成长势报告
+    generateReport: {
+        url: config.base_new_url + "report/generate",
+        type: "get",
+    },
     // 添加执行图片并完成任务
     addExecuteImgAndComplete: {
         url: config.base_dev_url + "v2/z_farm_work_record/addExecuteImgAndComplete",

+ 60 - 30
src/components/pageComponents/PhenologyTrackTimelineItem.vue

@@ -1,21 +1,27 @@
 <template>
-    <div v-if="yearText" class="track-year">{{ yearText }}</div>
-    <template v-if="groupedList.length">
-        <div class="phenology-track-item" v-for="group in groupedList" :key="group.date">
-            <div class="track-axis">
-                <span class="track-date-side">{{ group.date }}</span>
-                <div class="track-line-wrap">
-                    <span class="track-dot"></span>
-                    <div class="track-line"></div>
+    <template v-if="yearGroupedList.length">
+        <template v-for="yearGroup in yearGroupedList" :key="yearGroup.year || 'unknown'">
+            <div v-if="yearGroup.yearText" class="track-year">{{ yearGroup.yearText }}</div>
+            <div
+                class="phenology-track-item"
+                v-for="group in yearGroup.dateGroups"
+                :key="`${yearGroup.year}-${group.date}`"
+            >
+                <div class="track-axis">
+                    <span class="track-date-side">{{ group.date }}</span>
+                    <div class="track-line-wrap">
+                        <span class="track-dot"></span>
+                        <div class="track-line"></div>
+                    </div>
                 </div>
-            </div>
-            <div class="track-cards">
-                <div class="track-card" v-for="item in group.items" :key="item.id">
-                    <span v-if="item.zone_name" class="track-badge">{{ item.zone_name }}</span>
-                    <div v-if="item.content" class="track-content" v-html="highlightContent(item.content)"></div>
+                <div class="track-cards">
+                    <div class="track-card" v-for="item in group.items" :key="item.id">
+                        <span v-if="item.zone_name" class="track-badge">{{ item.zone_name }}</span>
+                        <div v-if="item.content" class="track-content" v-html="highlightContent(item.content)"></div>
+                    </div>
                 </div>
             </div>
-        </div>
+        </template>
     </template>
     <div v-else-if="dataLoaded" class="track-empty">{{ t("agriFile.noData") }}</div>
 </template>
@@ -42,12 +48,11 @@ const dataLoaded = ref(false);
 
 const sourceList = computed(() => (Array.isArray(props.list) ? props.list : imageList.value));
 
-const yearText = computed(() => {
-    if (!Array.isArray(props.list)) return "";
-    const item = props.list.find((entry) => entry?.date || entry?.latest_time);
-    const match = String(item?.date || item?.latest_time || "").match(/(\d{4})/);
-    return match ? t("agriFile.yearLabel", { year: match[1] }) : "";
-});
+function extractYear(value) {
+    if (!value) return "";
+    const match = String(value).match(/(\d{4})/);
+    return match ? match[1] : "";
+}
 
 function formatDate(value) {
     if (!value) return "";
@@ -61,27 +66,48 @@ function formatDate(value) {
 }
 
 function normalizeItem(item, index) {
+    const rawDate = item.date || item.enter_date || item.latest_time || "";
     return {
         id: item.id ?? index,
-        date: formatDate(item.date || item.latest_time),
+        year: extractYear(rawDate),
+        date: formatDate(rawDate),
         zone_name: item.zone_name || item.zoneName || "",
-        content: item.content || item.desc || item.text || "",
+        content: item.content || item.sentence || item.desc || item.text || "",
     };
 }
 
-const groupedList = computed(() => {
-    const groups = [];
-    const indexMap = new Map();
+/** 先按年份分段,再按月日分组,避免跨年同月日互相覆盖 */
+const yearGroupedList = computed(() => {
+    const yearGroups = [];
+    const yearMap = new Map();
+
     sourceList.value.forEach((item, index) => {
         const normalized = normalizeItem(item, index);
         if (!normalized.date) return;
-        if (!indexMap.has(normalized.date)) {
-            indexMap.set(normalized.date, groups.length);
-            groups.push({ date: normalized.date, items: [] });
+
+        const year = normalized.year || "";
+        if (!yearMap.has(year)) {
+            const group = {
+                year,
+                yearText: year ? t("agriFile.yearLabel", { year }) : "",
+                dateGroups: [],
+                dateMap: new Map(),
+            };
+            yearMap.set(year, group);
+            yearGroups.push(group);
         }
-        groups[indexMap.get(normalized.date)].items.push(normalized);
+
+        const yearGroup = yearMap.get(year);
+        const dateKey = `${year}-${normalized.date}`;
+        if (!yearGroup.dateMap.has(dateKey)) {
+            const dateGroup = { date: normalized.date, items: [] };
+            yearGroup.dateMap.set(dateKey, dateGroup);
+            yearGroup.dateGroups.push(dateGroup);
+        }
+        yearGroup.dateMap.get(dateKey).items.push(normalized);
     });
-    return groups;
+
+    return yearGroups.map(({ year, yearText, dateGroups }) => ({ year, yearText, dateGroups }));
 });
 
 function highlightContent(text) {
@@ -137,6 +163,10 @@ defineExpose({
     color: rgba(0, 0, 0, 0.4);
 }
 
+.phenology-track-item + .track-year {
+    margin-top: 16px;
+}
+
 .track-empty {
     padding: 24px 0;
     text-align: center;

+ 43 - 10
src/views/old_mini/agri_file/index.vue

@@ -338,11 +338,48 @@ const fetchPatrolInteractTask = async () => {
     }
 };
 
-const cropArchiveList = ref([
-    { id: 1, date: "2025-04-18", zone_name: "", content: "" },
-    { id: 2, date: "2025-04-18", zone_name: "", content: "" },
-    { id: 3, date: "2025-04-10", zone_name: "", content: "" },
-]);
+const cropArchiveList = ref([]);
+
+const mapPhenoRecordToArchive = (item, index, zoneName) => ({
+    id: [item.zone_id, item.enter_date, item.period_code, index].filter((v) => v != null && v !== "").join("_"),
+    date: item.enter_date || "",
+    zone_name: item.zone_name || zoneName || "",
+    content: item.sentence || (item.period_name ? `到达 ${item.period_name}` : ""),
+    period_code: item.period_code,
+    period_name: item.period_name,
+    shoot_type: item.shoot_type,
+    zone_id: item.zone_id,
+    crop_code: item.crop_code,
+});
+
+const fetchCropArchive = async () => {
+    const farm = JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
+    const params = {
+        farm_id: farm.farm_id,
+        zone_id: 46,
+        crop_code: 'LCH',
+    }
+    if (!params) {
+        cropArchiveList.value = [];
+        return;
+    }
+    const { zone_name: zoneName, ...query } = params;
+    try {
+        const res = await VE_API.entry.getPhenoRecord(query);
+        if (res?.code !== 200) {
+            cropArchiveList.value = [];
+            return;
+        }
+        const list = Array.isArray(res.data) ? res.data : [];
+        const fallbackZone = zoneName || t("agriFile.zoneOne");
+        cropArchiveList.value = list
+            .map((item, index) => mapPhenoRecordToArchive(item, index, fallbackZone))
+            .sort((a, b) => String(b.date).localeCompare(String(a.date)));
+    } catch (e) {
+        console.warn("[agri_file] getPhenoRecord failed", e);
+        cropArchiveList.value = [];
+    }
+};
 
 const setMarkerEl = (id, el) => {
     if (el) markerEls[id] = el;
@@ -426,11 +463,6 @@ const fillMockLabels = () => {
             issue: t("agriFile.patrolIssue"),
         };
     });
-    cropArchiveList.value = cropArchiveList.value.map((item, index) => ({
-        ...item,
-        zone_name: albumName,
-        content: t(index % 2 === 0 ? "agriFile.cropArchiveMetric" : "agriFile.cropArchiveAbnormal"),
-    }));
 };
 
 const goAddZone = (type) => {
@@ -564,6 +596,7 @@ onMounted(() => {
 onActivated(() => {
     fillMockLabels();
     fetchPatrolInteractTask();
+    fetchCropArchive();
     initAlbumMap();
     tryShowGuide();
 });

+ 94 - 27
src/views/old_mini/entry_information/components/manualFarmingService.vue

@@ -302,10 +302,6 @@ function getVarietyDraftList() {
     return [];
 }
 
-function getPhenophaseLabel(item) {
-    return item.phenophase || item.phenologyName || String(item.phenologyId || "");
-}
-
 function getUserId() {
     const id = localStorage.getItem("MINI_USER_ID");
     return id != null && id !== "" ? Number(id) : 0;
@@ -318,41 +314,104 @@ function getAdminId() {
     return getUserId();
 }
 
-/** 第一步常驻点位选点时写入的区县 adcode */
-function getCountyCode() {
-    const location = readJson(LOCATION_KEY);
-    return location?.adcode ? String(location.adcode) : "";
-}
-
-function buildPlotPayload() {
+function buildFarmPayload() {
     const baForm = readJson("ENTRY_BA_FORM") || {};
     const varietyList = getVarietyDraftList().filter((item) => item?.id != null && item.id !== "");
     const equipmentCache = readJson(EQUIPMENT_KEY);
     const equipmentList = Array.isArray(equipmentCache)
         ? equipmentCache
         : [...(equipmentCache?.field || []), ...(equipmentCache?.fruit || [])];
-
-    return {
+    const categories = readJson(CATEGORY_SESSION_KEY) || [];
+    const location = readJson(LOCATION_KEY);
+    const cropDetailMap = readJson("ENTRY_CROP_DETAIL") || {};
+    const taskCache = readJson(TASK_KEY);
+    const taskList = Array.isArray(taskCache)
+        ? taskCache
+        : [...(taskCache?.field || []), ...(taskCache?.fruit || [])];
+    const farmTaskId = Number(taskList[0]?.id);
+
+    const primaryCropId = varietyList[0]?.categoryId ?? categories[0]?.id;
+    const primaryDetail = cropDetailMap[String(primaryCropId)] || {};
+
+    const cropBigType =
+        primaryDetail.crop_group ||
+        varietyList.find((item) => item.firstCrop)?.firstCrop ||
+        categories.find((item) => item.firstCrop)?.firstCrop ||
+        (isFruitView.value ? "果树" : "大田");
+
+    const farmLocation =
+        location?.point ||
+        varietyList.find((item) => item.location)?.location ||
+        "";
+
+    const payload = {
         user_name: baForm.name,
         tel: baForm.phone,
         admin_id: getAdminId(),
         user_id: getUserId(),
-        county_code: getCountyCode(),
         machine_id: equipmentList
             .map((item) => Number(item.id))
             .filter((id) => Number.isFinite(id)),
-        crops: varietyList.map((item) => ({
-            crop_type: item.categoryName,
-            crop_id: Number(item.categoryId),
-            variety_list: [String(item.id)],
-            phenophase: getPhenophaseLabel(item),
-            start_time: item.startTime,
-            plant_area: Number(item.area),
-            point: item.location,
-        })),
+        farm_location: farmLocation,
+        farm_address: location?.address || "",
+        crop_big_type: cropBigType,
+        crops: varietyList.map((item) => {
+            const detail = cropDetailMap[String(item.categoryId)] || {};
+            return {
+                crop_type: detail.crop_name || item.categoryName,
+                crop_code: detail.crop_code || "",
+                crop_id: Number(item.categoryId),
+                variety_list: String(item.varietyCode || item.id),
+                phenophase_code: String(item.phenophaseCode || item.phenologyId || ""),
+            };
+        }),
+    };
+
+    if (Number.isFinite(farmTaskId)) {
+        payload.farm_task_id = farmTaskId;
+    }
+
+    return payload;
+}
+
+/** 点击「完成」时用于初始化报告的参数(提交成功时缓存,清空 session 前) */
+const pendingReportParams = ref(null);
+
+/** 从当前 session 组装 report/generate 参数 */
+function buildReportParams() {
+    const baForm = readJson("ENTRY_BA_FORM") || {};
+    const varietyList = getVarietyDraftList().filter((item) => item?.id != null && item.id !== "");
+    const cropDetailMap = readJson("ENTRY_CROP_DETAIL") || {};
+    const meta = readJson("ENTRY_CROP_META") || {};
+    const first = varietyList[0];
+    const detail = first ? cropDetailMap[String(first.categoryId)] || {} : {};
+
+    return {
+        region: meta.city || meta.county || "",
+        crop: detail.crop_name || first?.categoryName || "",
+        variety: first?.name || "",
+        tel: baForm.phone || "",
     };
 }
 
+/** 跳转长势报告前生成报告 */
+async function generateGrowthReport(params) {
+    if (!params?.region || !params?.crop || !params?.variety || !params?.tel) {
+        console.warn("generateReport skipped: missing params", params);
+        return;
+    }
+
+    try {
+        const raw = await VE_API.report.generateReport(params);
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (res?.code !== 200) {
+            console.warn("generateReport failed", res?.msg || res?.message || res);
+        }
+    } catch (error) {
+        console.error("generateReport error", error);
+    }
+}
+
 function clearEntrySession() {
     sessionStorage.removeItem(SELECTED_LIST_KEY);
     sessionStorage.removeItem(CATEGORY_SESSION_KEY);
@@ -365,6 +424,8 @@ function clearEntrySession() {
     sessionStorage.removeItem(STEP_KEY);
     sessionStorage.removeItem("ENTRY_INVITE_TYPE");
     sessionStorage.removeItem("ENTRY_ADMIN_ID");
+    sessionStorage.removeItem("ENTRY_CROP_DETAIL");
+    sessionStorage.removeItem("ENTRY_CROP_META");
 }
 
 const handleSubmit = async () => {
@@ -376,13 +437,16 @@ const handleSubmit = async () => {
     }
     submitting.value = true;
     try {
-        const params = buildPlotPayload();
-        const res = await VE_API.entry.addPlotInfo(params);
-        if (res.code === 200) {
+        const params = buildFarmPayload();
+        const raw = await VE_API.entry.addFarmInfo(params);
+        // 部分接口返回 [body, httpStatus]
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (res?.code === 200) {
+            pendingReportParams.value = buildReportParams();
             clearEntrySession();
             showSuccessPopup.value = true;
         } else {
-            ElMessage.error(res.msg || "提交失败,请稍后再试");
+            ElMessage.error(res?.msg || res?.message || "提交失败,请稍后再试");
         }
     } catch (error) {
         console.error("entry submit failed", error);
@@ -393,6 +457,9 @@ const handleSubmit = async () => {
 };
 
 const handleComplete = () => {
+    // 只触发初始化,不等待结果;新增已成功即可跳转
+    generateGrowthReport(pendingReportParams.value);
+    pendingReportParams.value = null;
     showSuccessPopup.value = false;
     emit("confirm");
 };

+ 17 - 1
src/views/old_mini/entry_information/components/selectCategory.vue

@@ -121,6 +121,7 @@ const selectedItems = computed(() => {
                     list.push({
                         id: item.id,
                         name: item.name,
+                        code: item.code || "",
                         groupName: group.name,
                         majorKey: tab.key,
                         majorLabel: tab.label,
@@ -154,6 +155,7 @@ const mapCropGroups = (list) => {
             items: (group.items || []).map((crop) => ({
                 id: crop.crop_id,
                 name: crop.crop_name,
+                code: crop.crop_code || "",
                 isAdvantage: !!crop.is_advantage,
                 advantageRank: crop.advantage_rank,
                 selected: false,
@@ -311,7 +313,21 @@ const fetchCrops = async () => {
             county_code: countyCode,
         });
         if (res.code === 200) {
-            const tabs = buildTabsFromData(res.data);
+            const data = res.data || {};
+            try {
+                sessionStorage.setItem(
+                    "ENTRY_CROP_META",
+                    JSON.stringify({
+                        city: data.city || "",
+                        county: data.county || "",
+                        province: data.province || "",
+                        county_code: data.county_code || countyCode,
+                    })
+                );
+            } catch {
+                // ignore
+            }
+            const tabs = buildTabsFromData(data);
             majorTabs.value = tabs;
             if (!tabs.some((tab) => tab.key === activeTabKey.value)) {
                 activeTabKey.value = tabs[0]?.key || "";

+ 123 - 30
src/views/old_mini/entry_information/components/selectEquipment.vue

@@ -117,6 +117,10 @@ const SESSION = {
     MANUAL_VIEW: "ENTRY_MANUAL_VIEW",
     INVITE_TYPE: "ENTRY_INVITE_TYPE",
     ADMIN_ID: "ENTRY_ADMIN_ID",
+    /** farmer_crop_detail 返回的品类详情,按 crop_id 缓存 */
+    CROP_DETAIL: "ENTRY_CROP_DETAIL",
+    /** crops 接口返回的城市等元信息 */
+    CROP_META: "ENTRY_CROP_META",
 };
 
 /** 用户手动添加的农机归入此分组,始终置顶 */
@@ -268,6 +272,25 @@ function updateCropGroupLabel(category, apiCropGroup) {
         (category?.firstCrop ? `${category.firstCrop}类` : "");
 }
 
+/** 缓存 farmer_crop_detail 品类信息,供提交时取 crop_code / crop_group */
+function saveCropDetail(data) {
+    if (data?.crop_id == null || data?.crop_id === "") return;
+    const cache = readJson(SESSION.CROP_DETAIL) || {};
+    cache[String(data.crop_id)] = {
+        crop_id: data.crop_id,
+        crop_code: data.crop_code || "",
+        crop_group: data.crop_group || "",
+        crop_name: data.crop_name || "",
+        crop_type: data.crop_type || "",
+    };
+    writeJson(SESSION.CROP_DETAIL, cache);
+}
+
+function getCropDetailMap() {
+    const cache = readJson(SESSION.CROP_DETAIL);
+    return cache && typeof cache === "object" ? cache : {};
+}
+
 // ---------------------------------------------------------------------------
 // 选中态:恢复 / 持久化
 // ---------------------------------------------------------------------------
@@ -381,6 +404,7 @@ async function fetchEquipmentList() {
         const res = await VE_API.entry.getMachines({ crop_id: cropId });
         if (res.code === 200) {
             const data = res.data || {};
+            saveCropDetail(data);
             updateCropGroupLabel(category, data.crop_group);
             equipmentGroups.value = mapMachinesToGroups(data.machines);
         } else {
@@ -458,10 +482,6 @@ function getVarietyDraftList() {
     return [];
 }
 
-function getPhenophaseLabel(item) {
-    return item.phenophase || item.phenologyName || String(item.phenologyId || "");
-}
-
 function getUserId() {
     const id = localStorage.getItem("MINI_USER_ID");
     return id != null && id !== "" ? Number(id) : 0;
@@ -474,40 +494,107 @@ function getAdminId() {
     return getUserId();
 }
 
-/** 第一步常驻点位选点时写入的区县 adcode */
-function getCountyCode() {
-    const location = readJson(SESSION.LOCATION);
-    return location?.adcode ? String(location.adcode) : "";
-}
-
-/** 汇总各步骤 session 数据,供 addPlotInfo 提交 */
-function buildPlotPayload(includeEquipment = true) {
+/** 汇总各步骤 session 数据,供 addFarmInfo 提交 */
+function buildFarmPayload(includeEquipment = true) {
     const baForm = readJson(SESSION.BA_FORM) || {};
     const varietyList = getVarietyDraftList().filter((item) => item?.id != null && item.id !== "");
     const equipmentCache = readJson(SESSION.EQUIPMENT);
     const equipmentList = includeEquipment ? flattenEquipment(equipmentCache) : [];
-
-    return {
+    const categories = readJson(SESSION.CATEGORY) || [];
+    const location = readJson(SESSION.LOCATION);
+    const cropDetailMap = getCropDetailMap();
+    const taskCache = readJson(SESSION.TASK);
+    const taskList = Array.isArray(taskCache)
+        ? taskCache
+        : [...(taskCache?.field || []), ...(taskCache?.fruit || [])];
+    const farmTaskId = Number(taskList[0]?.id);
+
+    const primaryCropId =
+        varietyList[0]?.categoryId ??
+        getCategoryForMachines()?.id ??
+        categories[0]?.id;
+    const primaryDetail = cropDetailMap[String(primaryCropId)] || {};
+
+    const cropBigType =
+        primaryDetail.crop_group ||
+        varietyList.find((item) => item.firstCrop)?.firstCrop ||
+        categories.find((item) => item.firstCrop)?.firstCrop ||
+        "";
+
+    const farmLocation =
+        location?.point ||
+        varietyList.find((item) => item.location)?.location ||
+        "";
+
+    const payload = {
         user_name: baForm.name,
         tel: baForm.phone,
-        admin_id: getAdminId(),
+        admin_id: 94494,
+        // admin_id: getAdminId(),
         user_id: getUserId(),
-        county_code: getCountyCode(),
         machine_id: equipmentList
             .map((item) => Number(item.id))
             .filter((id) => Number.isFinite(id)),
-        crops: varietyList.map((item) => ({
-            crop_type: item.categoryName,
-            crop_id: Number(item.categoryId),
-            variety_list: [String(item.id)],
-            phenophase: getPhenophaseLabel(item),
-            start_time: item?.startTime,
-            plant_area: Number(item.area),
-            point: item.location,
-        })),
+        farm_location: farmLocation,
+        farm_address: location?.address || "",
+        crop_big_type: cropBigType,
+        crops: varietyList.map((item) => {
+            const detail = cropDetailMap[String(item.categoryId)] || {};
+            return {
+                crop_type: detail.crop_name || item.categoryName,
+                crop_code: detail.crop_code || "",
+                crop_id: Number(item.categoryId),
+                variety_list: String(item.varietyCode || item.id),
+                phenophase_code: String(item.phenophaseCode || item.phenologyId || ""),
+            };
+        }),
+    };
+
+    if (Number.isFinite(farmTaskId)) {
+        payload.farm_task_id = farmTaskId;
+    }
+
+    return payload;
+}
+
+/** 点击「完成」时用于初始化报告的参数(提交成功时缓存,清空 session 前) */
+const pendingReportParams = ref(null);
+
+/** 从当前 session 组装 report/generate 参数 */
+function buildReportParams() {
+    const baForm = readJson(SESSION.BA_FORM) || {};
+    const varietyList = getVarietyDraftList().filter((item) => item?.id != null && item.id !== "");
+    const cropDetailMap = getCropDetailMap();
+    const meta = readJson(SESSION.CROP_META) || {};
+    const first = varietyList[0];
+    const detail = first ? cropDetailMap[String(first.categoryId)] || {} : {};
+
+    return {
+        region: meta.city || meta.county || "",
+        crop: detail.crop_name || first?.categoryName || "",
+        variety: first?.name || "",
+        tel: baForm.phone || "",
     };
 }
 
+/** 跳转长势报告前生成报告 */
+async function generateGrowthReport(params) {
+    if (!params?.region || !params?.crop || !params?.variety || !params?.tel) {
+        console.warn("generateReport skipped: missing params", params);
+        return;
+    }
+
+    try {
+        const raw = await VE_API.report.generateReport(params);
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (res?.code !== 200) {
+            console.warn("generateReport failed", res?.msg || res?.message || res);
+        }
+    } catch (error) {
+        console.error("generateReport error", error);
+    }
+}
+
 function clearEntrySession() {
     Object.values(SESSION).forEach((key) => sessionStorage.removeItem(key));
 }
@@ -528,13 +615,16 @@ async function submitEntry(includeEquipment = true) {
 
     submitting.value = true;
     try {
-        const params = buildPlotPayload(includeEquipment);
-        const res = await VE_API.entry.addPlotInfo(params);
-        if (res.code === 200) {
+        const params = buildFarmPayload(includeEquipment);
+        const raw = await VE_API.entry.addFarmInfo(params);
+        // 部分接口返回 [body, httpStatus]
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        if (res?.code === 200) {
+            pendingReportParams.value = buildReportParams();
             clearEntrySession();
             showSuccessPopup.value = true;
         } else {
-            ElMessage.error(res.msg || "提交失败,请稍后再试");
+            ElMessage.error(res?.msg || res?.message || "提交失败,请稍后再试");
         }
     } catch (error) {
         console.error("entry submit failed", error);
@@ -544,7 +634,10 @@ async function submitEntry(includeEquipment = true) {
     }
 }
 
-function handleComplete() {
+async function handleComplete() {
+    // 只触发初始化,不等待结果;新增已成功即可跳转
+    generateGrowthReport(pendingReportParams.value);
+    pendingReportParams.value = null;
     showSuccessPopup.value = false;
     emit("confirm");
 }

+ 10 - 0
src/views/old_mini/entry_information/components/selectVariety.vue

@@ -370,11 +370,15 @@ const initCategoryTabsFromSession = () => {
             return {
                 ...cached,
                 name: item.name || cached.name,
+                code: item.code || cached.code || "",
+                firstCrop: item.firstCrop || cached.firstCrop || "",
             };
         }
         return {
             id,
             name: item.name || "",
+            code: item.code || "",
+            firstCrop: item.firstCrop || "",
             varieties: [],
             phenology: [],
             startingPointQuestion: "起种点问题",
@@ -500,6 +504,8 @@ const createEmptyForm = (category) => ({
     name: "",
     categoryId: category?.id ?? null,
     categoryName: category?.name ?? "",
+    cropCode: category?.code || "",
+    firstCrop: category?.firstCrop || "",
     phenologyId: "",
     startTime: "",
     area: "",
@@ -537,6 +543,8 @@ const ensureDefaultForm = () => {
     }
     item.categoryId = category.id;
     item.categoryName = category.name;
+    item.cropCode = category.code || item.cropCode || "";
+    item.firstCrop = category.firstCrop || item.firstCrop || "";
 };
 
 const getCurrentForm = () => {
@@ -585,6 +593,8 @@ const applyVarietyToForm = (variety) => {
     item.name = variety.name;
     item.categoryId = currentCategory.value.id;
     item.categoryName = currentCategory.value.name;
+    item.cropCode = currentCategory.value.code || item.cropCode || "";
+    item.firstCrop = currentCategory.value.firstCrop || item.firstCrop || "";
     item.phenologyId = "";
     saveSelectedListDraft();
 };

+ 25 - 6
src/views/old_mini/entry_information/selectLocation.vue

@@ -56,11 +56,14 @@ const userLocation = ref(
 );
 /** 搜索选中时带出的区县编码 */
 const selectedAdcode = ref("");
+/** 搜索选中时带出的地址文案 */
+const selectedAddress = ref("");
 
 const selectLocationMap = new SelectLocationMap({
     iconSrc,
     onMapClick: () => {
         selectedAdcode.value = "";
+        selectedAddress.value = "";
     },
 });
 
@@ -79,17 +82,26 @@ function toPointWkt(coordinate) {
     return `POINT(${lng} ${lat})`;
 }
 
-async function fetchAdcodeByCoordinate(coordinate) {
-    if (!coordinate?.length) return "";
+async function fetchLocationInfoByCoordinate(coordinate) {
+    if (!coordinate?.length) return { adcode: "", address: "" };
     try {
         const { result } = await VE_API.old_mini_map.location({
             key: MAP_KEY,
             location: `${coordinate[1]},${coordinate[0]}`,
         });
         const adcode = result?.ad_info?.adcode;
-        return adcode != null && adcode !== "" ? String(adcode) : "";
+        const address =
+            result?.formatted_addresses?.recommend ||
+            result?.address ||
+            (result?.address_component
+                ? `${result.address_component.city || ""}${result.address_component.district || ""}`
+                : "");
+        return {
+            adcode: adcode != null && adcode !== "" ? String(adcode) : "",
+            address: address || "",
+        };
     } catch {
-        return "";
+        return { adcode: "", address: "" };
     }
 }
 
@@ -105,14 +117,17 @@ onUnmounted(() => {
 const handleLocationChange = (payload) => {
     if (!payload?.coordinateArray) {
         selectedAdcode.value = "";
+        selectedAddress.value = "";
         return;
     }
     selectedAdcode.value = payload.adcode ? String(payload.adcode) : "";
+    selectedAddress.value = payload.address || payload.pointAddress || "";
     selectLocationMap.setMapPosition(payload.coordinateArray);
 };
 
 const handleLocate = () => {
     selectedAdcode.value = "";
+    selectedAddress.value = "";
     const point =
         localStorage.getItem("MINI_USER_LOCATION_POINT") ||
         store.state.home.miniUserLocationPoint ||
@@ -131,8 +146,11 @@ const handleSubmit = async () => {
     }
     const point = toPointWkt(coordinate);
     let adcode = selectedAdcode.value;
-    if (!adcode) {
-        adcode = await fetchAdcodeByCoordinate(coordinate);
+    let address = selectedAddress.value;
+    if (!adcode || !address) {
+        const info = await fetchLocationInfoByCoordinate(coordinate);
+        if (!adcode) adcode = info.adcode;
+        if (!address) address = info.address;
     }
     sessionStorage.setItem(
         sessionKey,
@@ -140,6 +158,7 @@ const handleSubmit = async () => {
             point,
             coordinate: [Number(coordinate[0]), Number(coordinate[1])],
             adcode: adcode || "",
+            address: address || "",
         })
     );
     router.back();