Explorar el Código

Merge branch 'agriculture' of http://www.sysuimars.cn:3000/feiniao/feiniao-farm-h5 into agriculture

lxf hace 3 semanas
padre
commit
f9ba2a9451

+ 65 - 4
src/views/old_mini/agri_file/pages/diagnosisReport.vue

@@ -1,5 +1,9 @@
 <template>
 <template>
-    <div class="diagnosis-report-page" v-loading="loading">
+    <div
+        class="diagnosis-report-page"
+        v-loading="pageLoading"
+        :element-loading-text="loadingText"
+    >
         <div class="diagnosis-header">
         <div class="diagnosis-header">
             <div class="diagnosis-header__title-row">
             <div class="diagnosis-header__title-row">
                 <span class="diagnosis-header__title">种植报告</span>
                 <span class="diagnosis-header__title">种植报告</span>
@@ -91,7 +95,7 @@
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { computed, onActivated, ref } from "vue";
+import { computed, onActivated, onDeactivated, onBeforeUnmount, ref } from "vue";
 import { useRouter } from "vue-router";
 import { useRouter } from "vue-router";
 import { useStore } from "vuex";
 import { useStore } from "vuex";
 import { ElMessage } from "element-plus";
 import { ElMessage } from "element-plus";
@@ -117,6 +121,9 @@ const store = useStore();
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // 常量
 // 常量
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
+/** 报告未生成时轮询间隔(ms) */
+const REPORT_POLL_INTERVAL = 3000;
+
 /** module1_basic 字段标签 → 基本信息展示项(labels 含接口原文别名) */
 /** module1_basic 字段标签 → 基本信息展示项(labels 含接口原文别名) */
 const BASE_FIELD_META = [
 const BASE_FIELD_META = [
     { key: "crop", labels: ["作物品种", "种植作物"], label: "种植作物:", actionInline: { key: "changeVariety", text: "更改品种" } },
     { key: "crop", labels: ["作物品种", "种植作物"], label: "种植作物:", actionInline: { key: "changeVariety", text: "更改品种" } },
@@ -146,7 +153,8 @@ const reportTabs = [
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // 页面状态
 // 页面状态
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-const loading = ref(false);
+const loading = ref(false); // 首次拉取中
+const reportGenerating = ref(false); // 报告尚未生成,持续 loading + 轮询
 const varietyList = ref([]);
 const varietyList = ref([]);
 const activeVarietyId = ref(null);
 const activeVarietyId = ref(null);
 const headerDesc = ref("");
 const headerDesc = ref("");
@@ -159,10 +167,16 @@ const adjustPhenologyPopupRef = ref(null);
 const restingDatePopupRef = ref(null);
 const restingDatePopupRef = ref(null);
 const editPlantPointPopupRef = ref(null);
 const editPlantPointPopupRef = ref(null);
 const showCompleteFarmPopup = ref(false); // 无农场时展示「完善农场信息」
 const showCompleteFarmPopup = ref(false); // 无农场时展示「完善农场信息」
+let pollTimer = null;
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // 计算属性
 // 计算属性
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
+const pageLoading = computed(() => loading.value || reportGenerating.value);
+const loadingText = computed(() =>
+    reportGenerating.value ? "报告正在生成中..." : "加载中..."
+);
+
 const activeReportTabMeta = computed(
 const activeReportTabMeta = computed(
     () => reportTabs.find((tab) => tab.key === activeReportTab.value) || reportTabs[0]
     () => reportTabs.find((tab) => tab.key === activeReportTab.value) || reportTabs[0]
 );
 );
@@ -356,10 +370,46 @@ async function checkCompleteFarmPopup() {
     }
     }
 }
 }
 
 
+function stopReportPolling() {
+    if (pollTimer) {
+        clearInterval(pollTimer);
+        pollTimer = null;
+    }
+}
+
+/** 报告未就绪时保持 loading,并定时重试 check */
+function startReportPolling(farmId, zoneId) {
+    stopReportPolling();
+    pollTimer = setInterval(() => {
+        pollDiagnosisReport(farmId, zoneId);
+    }, REPORT_POLL_INTERVAL);
+}
+
+async function pollDiagnosisReport(farmId, zoneId) {
+    try {
+        const raw = await VE_API.questionnaire.checkReportGenerated({
+            farm_id: farmId,
+            zone_id: zoneId,
+        });
+        const res = Array.isArray(raw) ? raw[0] : raw;
+        const report = res?.data?.report;
+        if (res?.code === 200 && report) {
+            stopReportPolling();
+            reportGenerating.value = false;
+            applyReportData(report, getSelectedFarm());
+        }
+    } catch (error) {
+        console.warn("[diagnosisReport] poll failed", error);
+    }
+}
+
 async function fetchDiagnosisReport() {
 async function fetchDiagnosisReport() {
     const farm = getSelectedFarm();
     const farm = getSelectedFarm();
     const farmId = farm.farm_id ?? farm.id;
     const farmId = farm.farm_id ?? farm.id;
     const zoneId = farm.zone_id ?? farm.zoneId;
     const zoneId = farm.zone_id ?? farm.zoneId;
+    stopReportPolling();
+    reportGenerating.value = false;
+
     if (farmId == null || farmId === "" || zoneId == null || zoneId === "") {
     if (farmId == null || farmId === "" || zoneId == null || zoneId === "") {
         resetReportView(farm);
         resetReportView(farm);
         // ElMessage.warning("请先选择农场与分区");
         // ElMessage.warning("请先选择农场与分区");
@@ -378,11 +428,14 @@ async function fetchDiagnosisReport() {
             applyReportData(report, farm);
             applyReportData(report, farm);
             return;
             return;
         }
         }
+        // 报告尚未生成:保持页面 loading,并轮询直到有数据
         resetReportView(farm);
         resetReportView(farm);
-        ElMessage.warning(res?.msg || res?.message || "报告正在生成中...");
+        reportGenerating.value = true;
+        startReportPolling(farmId, zoneId);
     } catch (error) {
     } catch (error) {
         console.error("fetchDiagnosisReport failed", error);
         console.error("fetchDiagnosisReport failed", error);
         resetReportView(farm);
         resetReportView(farm);
+        reportGenerating.value = false;
         ElMessage.error("报告加载失败");
         ElMessage.error("报告加载失败");
     } finally {
     } finally {
         loading.value = false;
         loading.value = false;
@@ -433,6 +486,14 @@ onActivated(() => {
     checkCompleteFarmPopup();
     checkCompleteFarmPopup();
     fetchDiagnosisReport();
     fetchDiagnosisReport();
 });
 });
+
+onDeactivated(() => {
+    stopReportPolling();
+});
+
+onBeforeUnmount(() => {
+    stopReportPolling();
+});
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>

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

@@ -45,7 +45,10 @@
                             v-for="item in group.items"
                             v-for="item in group.items"
                             :key="item.id"
                             :key="item.id"
                             class="tag-item"
                             class="tag-item"
-                            :class="{ selected: item.selected }"
+                            :class="{
+                                selected: item.selected,
+                                disabled: !isCropAllowed(item),
+                            }"
                             @click="handleSelect(item)"
                             @click="handleSelect(item)"
                         >
                         >
                             <span class="text">{{ item.name }}</span>
                             <span class="text">{{ item.name }}</span>
@@ -570,6 +573,11 @@ onMounted(() => {
                     background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
                     background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
                 }
                 }
             }
             }
+
+            &.disabled {
+                color: rgba(0, 0, 0, 0.25);
+                cursor: not-allowed;
+            }
         }
         }
 
 
         &.add-tag-group {
         &.add-tag-group {
@@ -583,6 +591,10 @@ onMounted(() => {
                     background: #e8f5ff;
                     background: #e8f5ff;
                     color: #2199f8;
                     color: #2199f8;
                 }
                 }
+
+                &.disabled {
+                    color: rgba(0, 0, 0, 0.25);
+                }
             }
             }
         }
         }
     }
     }

+ 103 - 41
src/views/old_mini/work_execute/index.vue

@@ -93,6 +93,7 @@ import surveyAskPopup from "@/components/popup/surveyAskPopup.vue";
 import diagnosisReportPopup from "@/components/popup/diagnosisReportPopup.vue";
 import diagnosisReportPopup from "@/components/popup/diagnosisReportPopup.vue";
 import restorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
 import restorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
 import { useI18n } from "@/i18n";
 import { useI18n } from "@/i18n";
+import { saveSelectedFarm } from "@/utils/selectedFarm";
 import wx from "weixin-js-sdk";
 import wx from "weixin-js-sdk";
 
 
 
 
@@ -182,12 +183,25 @@ const pendingCount = computed(() => dayTaskList.value.filter((item) => item.stat
 const completedCount = computed(() => dayTaskList.value.filter((item) => item.status === "completed").length);
 const completedCount = computed(() => dayTaskList.value.filter((item) => item.status === "completed").length);
 const displayTaskList = computed(() => dayTaskList.value.filter((item) => item.status === activeTab.value));
 const displayTaskList = computed(() => dayTaskList.value.filter((item) => item.status === activeTab.value));
 
 
-/** zone_id / farm_id / crop_type 取当前选中农场,trigger_date 取日历选中日 */
-const resolveQueryParams = () => {
+/** 日历窗口:今天起连续 7 天 */
+function getCalendarDateRange() {
+    const start = new Date();
+    return Array.from({ length: 7 }, (_, i) => {
+        const d = new Date(start);
+        d.setDate(start.getDate() + i);
+        const y = d.getFullYear();
+        const m = String(d.getMonth() + 1).padStart(2, "0");
+        const day = String(d.getDate()).padStart(2, "0");
+        return `${y}-${m}-${day}`;
+    });
+}
+
+/** zone_id / farm_id / crop_type 取当前选中农场;trigger_date 为窗口内某一天 */
+const resolveQueryParams = (triggerDate = getTodayDate()) => {
     const params = {
     const params = {
         zone_id: getSelectedZoneId(),
         zone_id: getSelectedZoneId(),
         farm_id: getSelectedFarmId(),
         farm_id: getSelectedFarmId(),
-        trigger_date: filterDate.value || getTodayDate(),
+        trigger_date: triggerDate,
     };
     };
     const cropType = getSelectedCropType();
     const cropType = getSelectedCropType();
     if (cropType) params.crop_type = cropType;
     if (cropType) params.crop_type = cropType;
@@ -311,36 +325,77 @@ async function checkFarmCalendarPopup() {
     }
     }
 }
 }
 
 
+/** 无选中农场时写入列表首项,保证 zone_fw_task_list 有 farm_id */
+async function ensureSelectedFarm() {
+    if (getSelectedFarmId() != null) return true;
+    const id = getMiniUserId();
+    if (!id) return false;
+    try {
+        const res = await VE_API.questionnaire.getFarms({ id });
+        const list = Array.isArray(res?.data) ? res.data : [];
+        return list[0] ? saveSelectedFarm(list[0]) : false;
+    } catch (e) {
+        console.warn("[work_execute] ensureSelectedFarm failed", e);
+        return false;
+    }
+}
+
 const refreshMapMarkers = () => {
 const refreshMapMarkers = () => {
     if (!indexMap.kmap) return;
     if (!indexMap.kmap) return;
     indexMap.initData(getMapMarkerList(), "farmName", "wkt");
     indexMap.initData(getMapMarkerList(), "farmName", "wkt");
 };
 };
 
 
-/** 地图展示全部日期的待执行农事 */
+/** 与日历一致:展示 7 天窗口内全部待执行农事(列表仍按选中日过滤) */
 const getMapMarkerList = () =>
 const getMapMarkerList = () =>
     taskList.value.filter((item) => item.status === "pending" && item.wkt);
     taskList.value.filter((item) => item.status === "pending" && item.wkt);
 
 
+/**
+ * 按日历 7 天逐日请求后合并(接口按 trigger_date 返回当日任务)。
+ * 切换日期只本地过滤列表,地图仍用完整 7 天点位。
+ */
 const fetchZoneFwTaskList = async () => {
 const fetchZoneFwTaskList = async () => {
     try {
     try {
-        const params = resolveQueryParams();
-        const res = await VE_API.questionnaire.getZoneFwTaskList(params);
-        const data = res?.data;
-        if (!data) {
-            taskList.value = [];
-            mapCenterPoint.value = null;
+        const dates = getCalendarDateRange();
+        const settled = await Promise.all(
+            dates.map((triggerDate) =>
+                VE_API.questionnaire
+                    .getZoneFwTaskList(resolveQueryParams(triggerDate))
+                    .then((res) => ({ triggerDate, data: res?.data || null }))
+                    .catch((e) => {
+                        console.warn("[work_execute] zone_fw_task_list failed", triggerDate, e);
+                        return { triggerDate, data: null };
+                    })
+            )
+        );
+        const merged = [];
+        const seen = new Set();
+        let lat = null;
+        let lng = null;
+        for (const { triggerDate, data } of settled) {
+            if (!data) continue;
+            const mapped = mapZoneTaskList(data, triggerDate);
+            if (lat == null && mapped.lat != null) lat = mapped.lat;
+            if (lng == null && mapped.lng != null) lng = mapped.lng;
+            for (const item of mapped.list) {
+                if (seen.has(item.id)) continue;
+                seen.add(item.id);
+                merged.push(item);
+            }
+        }
+        mapCenterPoint.value =
+            lat != null && lng != null ? `POINT(${lng} ${lat})` : null;
+        taskList.value = merged;
+        if (!merged.length) {
             calendarRef.value?.selectDateByValue?.(filterDate.value);
             calendarRef.value?.selectDateByValue?.(filterDate.value);
             calendarRef.value?.setSolarTerm?.([]);
             calendarRef.value?.setSolarTerm?.([]);
-            refreshMapMarkers();
-            return;
+        } else {
+            syncCalendar();
         }
         }
-        const { list, lat, lng } = mapZoneTaskList(data, params.trigger_date);
-        mapCenterPoint.value = `POINT(${lng} ${lat})`;
-        taskList.value = list;
-        syncCalendar();
         nextTick(() => refreshMapMarkers());
         nextTick(() => refreshMapMarkers());
     } catch (e) {
     } catch (e) {
         console.error("获取农事任务失败", e);
         console.error("获取农事任务失败", e);
         taskList.value = [];
         taskList.value = [];
+        refreshMapMarkers();
     }
     }
 };
 };
 
 
@@ -350,11 +405,11 @@ const handleTabChange = (key) => {
     activeTab.value = key;
     activeTab.value = key;
 };
 };
 
 
+/** 切日期:只过滤下方列表;地图与日历角标共用完整 7 天数据 */
 const handleDateSelect = (date) => {
 const handleDateSelect = (date) => {
     const next = date ?? getTodayDate();
     const next = date ?? getTodayDate();
     if (filterDate.value === next) return;
     if (filterDate.value === next) return;
     filterDate.value = next;
     filterDate.value = next;
-    fetchZoneFwTaskList();
 };
 };
 
 
 const handleForward = (item) => {
 const handleForward = (item) => {
@@ -459,36 +514,43 @@ const initMap = () => {
     refreshMapMarkers();
     refreshMapMarkers();
 };
 };
 
 
+/** keep-alive 下切农场 / 再次进入只走 onActivated,需在此拉列表 */
+const refreshPage = async () => {
+    await ensureSelectedFarm();
+    // 无农场时不打任务列表(弹窗由 checkFarmCalendarPopup 负责)
+    if (getSelectedFarmId() == null) {
+        taskList.value = [];
+        if (!indexMap.kmap) initMap();
+        return;
+    }
+    await fetchZoneFwTaskList();
+    if (!indexMap.kmap) {
+        initMap();
+        return;
+    }
+    if (!mapContainer.value || !indexMap.kmap.map) return;
+    const checkAndUpdateSize = () => {
+        const container = mapContainer.value;
+        if (!container) return;
+        const rect = container.getBoundingClientRect();
+        if (rect.width > 0 && rect.height > 0) {
+            indexMap.kmap.map.updateSize();
+            refreshMapMarkers();
+        } else {
+            setTimeout(checkAndUpdateSize, 100);
+        }
+    };
+    setTimeout(checkAndUpdateSize, 200);
+};
+
 onMounted(() => {
 onMounted(() => {
     checkFarmCalendarPopup();
     checkFarmCalendarPopup();
-    nextTick(async () => {
-        await fetchZoneFwTaskList();
-        initMap();
-    });
+    nextTick(refreshPage);
 });
 });
 
 
 onActivated(() => {
 onActivated(() => {
     checkFarmCalendarPopup();
     checkFarmCalendarPopup();
-    nextTick(() => {
-        if (!indexMap.kmap) {
-            initMap();
-            return;
-        }
-        if (mapContainer.value && indexMap.kmap.map) {
-            const checkAndUpdateSize = () => {
-                const container = mapContainer.value;
-                if (!container) return;
-                const rect = container.getBoundingClientRect();
-                if (rect.width > 0 && rect.height > 0) {
-                    indexMap.kmap.map.updateSize();
-                    refreshMapMarkers();
-                } else {
-                    setTimeout(checkAndUpdateSize, 100);
-                }
-            };
-            setTimeout(checkAndUpdateSize, 200);
-        }
-    });
+    nextTick(refreshPage);
 });
 });
 onDeactivated(() => {
 onDeactivated(() => {
     showFarmCalendarPopup.value = false;
     showFarmCalendarPopup.value = false;