Ver código fonte

fix: 对接接口

lxf 1 semana atrás
pai
commit
61e4b4a715

+ 20 - 0
src/api/modules/questionnaire.js

@@ -36,4 +36,24 @@ module.exports = {
         url: config.base_new_url + "pheno/fw_task_details",
         type: "get",
     },
+    // 查询当前农户/农资下的果园信息
+    getFarms: {
+        url: config.base_new_url + "questionnaire/find_farm_information",
+        type: "get",
+    },
+    // 查询是否填写农场信息
+    checkFarmInit: {
+        url: config.base_new_url + "questionnaire/farm_init/get",
+        type: "get",
+    },
+    // 填写农场信息弹窗更新状态
+    updateFarmInitStatus: {
+        url: config.base_new_url + "questionnaire/farm_init/set",
+        type: "post",
+    },
+    // 查询分区照片数量
+    getZones: {
+        url: config.base_new_url + "questionnaire/zone_images",
+        type: "get",
+    }
 }

+ 20 - 20
src/components/allGarden.vue

@@ -182,17 +182,6 @@ function resolveFarmPointWkt(farm) {
     return farm.geom_wkt || "";
 }
 
-function resolveCategoryLabel(farm) {
-    return (
-        farm.category_name ||
-        farm.farm_category_name ||
-        farm.crop_name ||
-        farm.species_name ||
-        farm.farm_category ||
-        ""
-    );
-}
-
 function normalizeFarmList(data) {
     const selectedId = localStorage.getItem("selectedFarmId");
     return (data || []).map((farm) => {
@@ -200,13 +189,13 @@ function normalizeFarmList(data) {
         return {
             ...farm,
             id,
-            name: farm.farm_name,
+            name: farm.farm_name || "",
             wkt: resolveFarmPointWkt(farm),
-            farmName: farm.farm_name,
-            rawAddress: farm.farm_address || farm.city || "",
-            categoryLabel: resolveCategoryLabel(farm),
-            phenologyText: farm.current_phenology || farm.phenology_name || farm.phenology || "",
-            taskName: farm.pending_work_name || farm.farm_work_name || farm.work_name || "",
+            farmName: farm.farm_name || "",
+            rawAddress: farm.farm_address || "",
+            categoryLabel: farm.variety_name || "",
+            phenologyText: farm.period_name || "",
+            taskName: farm.fw_name || "",
             taskStatus: farm.work_status_name || farm.work_status || "",
             isCurrent: selectedId != null && Number(id) === Number(selectedId),
         };
@@ -242,10 +231,21 @@ function ensureMap() {
     });
 }
 
+function getMiniUserId() {
+    return store.state.home.miniUserId || localStorage.getItem("MINI_USER_ID");
+}
+
 function getFarmList() {
-    VE_API.farm.getFarmList()
-        .then(({ data }) => {
-            farmList.value = normalizeFarmList(data);
+    const id = getMiniUserId();
+    if (!id) {
+        farmList.value = [];
+        return;
+    }
+    VE_API.questionnaire
+        .getFarms({ id })
+        .then((res) => {
+            const list = Array.isArray(res?.data) ? res.data : [];
+            farmList.value = normalizeFarmList(list);
             nextTick(() => {
                 ensureMap();
             });

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

@@ -3,6 +3,7 @@
         v-model:show="showValue"
         round
         teleport="body"
+        :z-index="20000"
         class="complete-farm-popup"
         :close-on-click-overlay="true"
     >

+ 100 - 11
src/views/old_mini/agri_file/index.vue

@@ -185,6 +185,7 @@ import proxyAuthPopup from "@/components/popup/proxyAuthPopup.vue";
 import phenologyUpdatePopup from "@/components/popup/phenologyUpdatePopup.vue";
 import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
 import wx from "weixin-js-sdk";
+import { base_img_url2 } from "@/api/config";
 
 const { t } = useI18n();
 const store = useStore();
@@ -223,12 +224,7 @@ const navCards = [
     },
 ];
 
-const zoneList = ref([
-    { id: 1, name: "", count: 2, cover: defaultCover, longitude: 113.612409, latitude: 23.587036 },
-    { id: 2, name: "", count: 2, cover: defaultCover, longitude: 113.615809, latitude: 23.586636 },
-    { id: 3, name: "", count: 2, cover: defaultCover, longitude: 113.613609, latitude: 23.584436 },
-    { id: 4, name: "", count: 2, cover: defaultCover, longitude: 113.615609, latitude: 23.585036 },
-]);
+const zoneList = ref([]);
 
 const albumList = ref([
     { id: 1, name: "", count: 0, cover: "" },
@@ -245,6 +241,27 @@ const reportList = ref([
 
 const showUploadPopup = ref(false);
 const showCompleteFarmPopup = ref(false);
+
+function getMiniUserId() {
+    return store.state.home.miniUserId || localStorage.getItem("MINI_USER_ID");
+}
+
+/** 无果园信息时展示「完善农场信息」弹窗 */
+async function checkCompleteFarmPopup() {
+    const id = getMiniUserId();
+    if (!id) {
+        showCompleteFarmPopup.value = false;
+        return;
+    }
+    try {
+        const res = await VE_API.questionnaire.getFarms({ id });
+        const list = Array.isArray(res?.data) ? res.data : [];
+        showCompleteFarmPopup.value = list.length === 0;
+    } catch (e) {
+        console.warn("[agri_file] getFarms failed", e);
+        showCompleteFarmPopup.value = false;
+    }
+}
 // 后续由接口控制是否展示代管授权弹窗
 const showProxyAuthPopup = ref(false);
 const proxyServiceName = ref("农服名称");
@@ -349,6 +366,71 @@ const setMarkerEl = (id, el) => {
     else delete markerEls[id];
 };
 
+function getSelectedFarmId() {
+    const stored = localStorage.getItem("selectedFarmId");
+    if (stored != null && stored !== "") {
+        return Number(stored);
+    }
+    try {
+        const farm = JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
+        const id = farm.farm_id ?? farm.id;
+        return id != null && id !== "" ? Number(id) : null;
+    } catch {
+        return null;
+    }
+}
+
+function parsePointCoordinates(pointWkt) {
+    if (!pointWkt || !/^POINT\s*\(/i.test(String(pointWkt).trim())) {
+        return null;
+    }
+    try {
+        const coord = util.wktCastGeom(pointWkt).getFirstCoordinate();
+        if (!coord || coord.length < 2) return null;
+        return { longitude: coord[0], latitude: coord[1] };
+    } catch {
+        return null;
+    }
+}
+
+function resolveZoneCoverUrl(path) {
+    if (!path) return defaultCover;
+    const text = String(path).trim();
+    if (!text) return defaultCover;
+    if (/^https?:\/\//i.test(text)) return text;
+    return base_img_url2 + text.replace(/^\//, "");
+}
+
+function mapZoneItem(zone) {
+    const coords = parsePointCoordinates(zone?.point);
+    return {
+        id: zone.zone_id,
+        zoneId: zone.zone_id,
+        name: zone.zone_name || "",
+        count: zone.image_count ?? 0,
+        cover: resolveZoneCoverUrl(zone.cover_url),
+        longitude: coords?.longitude,
+        latitude: coords?.latitude,
+        point: zone.point,
+    };
+}
+
+async function fetchZoneList() {
+    const farmId = getSelectedFarmId();
+    if (!farmId) {
+        zoneList.value = [];
+        return;
+    }
+    try {
+        const res = await VE_API.questionnaire.getZones({ farm_id: farmId });
+        const zones = res?.data?.zones;
+        zoneList.value = Array.isArray(zones) ? zones.map(mapZoneItem) : [];
+    } catch (e) {
+        console.warn("[agri_file] getZones failed", e);
+        zoneList.value = [];
+    }
+}
+
 const getFarmCenter = () => {
     try {
         const farm = JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
@@ -359,6 +441,12 @@ const getFarmCenter = () => {
     } catch {
         // ignore
     }
+    const firstZone = zoneList.value.find(
+        (z) => z.longitude != null && z.latitude != null
+    );
+    if (firstZone) {
+        return [firstZone.longitude, firstZone.latitude];
+    }
     return DEFAULT_CENTER;
 };
 
@@ -391,22 +479,21 @@ const bindZoneOverlays = () => {
 const initAlbumMap = async () => {
     await nextTick();
     if (!mapContainer.value) return;
-    const center = getFarmCenter();
-    fileMap.initMap(`POINT(${center[0]} ${center[1]})`, mapContainer.value);
-    await nextTick();
     if (noFarmRange.value) {
         zoneList.value = [];
         albumList.value = [];
         return;
     }
+    await fetchZoneList();
+    const center = getFarmCenter();
+    fileMap.initMap(`POINT(${center[0]} ${center[1]})`, mapContainer.value);
+    await nextTick();
     bindZoneOverlays();
     fileMap.kmap?.map?.updateSize?.();
 };
 
 const fillMockLabels = () => {
-    const zoneName = t("agriFile.zoneLabel", { n: 1 });
     const albumName = t("agriFile.zoneOne");
-    zoneList.value = zoneList.value.map((item) => ({ ...item, name: zoneName }));
     albumList.value = albumList.value.map((item) => ({ ...item, name: albumName }));
     reportList.value = reportList.value.map((item) => ({
         ...item,
@@ -556,6 +643,7 @@ const goAlbumDetail = (item) => {
 
 onMounted(() => {
     fillMockLabels();
+    checkCompleteFarmPopup();
     fetchPatrolInteractTask();
     initAlbumMap();
     tryShowGuide();
@@ -563,6 +651,7 @@ onMounted(() => {
 });
 onActivated(() => {
     fillMockLabels();
+    checkCompleteFarmPopup();
     fetchPatrolInteractTask();
     initAlbumMap();
     tryShowGuide();

+ 104 - 9
src/views/old_mini/growth_report/index.vue

@@ -21,8 +21,11 @@
                             v-for="item in interactOptions"
                             :key="item.id"
                             class="interact-option"
-                            :class="{ selected: selectedOptionId === item.id }"
-                            @click="selectedOptionId = item.id"
+                            :class="{
+                                selected: selectedOptionId === item.id,
+                                disabled: interactSubmitting,
+                            }"
+                            @click="handleInteractOptionClick(item)"
                         >
                             <span class="interact-option__text">{{ item.name }}</span>
                         </div>
@@ -109,6 +112,7 @@
 import { computed, nextTick, onActivated, onMounted, ref } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { useStore } from "vuex";
+import { ElMessage } from "element-plus";
 import { Close, LocationFilled } from "@element-plus/icons-vue";
 import { convertPointToArray } from "@/utils/index";
 import GrowthReportMap from "./growthReportMap.js";
@@ -150,14 +154,98 @@ const panelHeight = ref(280);
 const inviteBottom = computed(() => panelHeight.value);
 const invitePopupRef = ref(null);
 
-const showInteractCard = ref(true);
-const interactQuestionText = ref("具体问题具体问题具体问题具体问题具体?");
-const interactOptions = ref([
-    { id: 1, name: "选项一" },
-    { id: 2, name: "选项二" },
-    { id: 3, name: "选项三" },
-]);
+/** 与 agri_file 一致:外部感知任务 zone_id(后续可改为当前地块) */
+const INTERACT_ZONE_ID = 42;
+
+const showInteractCard = ref(false);
+const interactQuestionText = ref("");
+const interactOptions = ref([]);
 const selectedOptionId = ref(null);
+const currentInteractTask = ref(null);
+const interactSubmitting = ref(false);
+
+function formatToday() {
+    const d = new Date();
+    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}`;
+}
+
+function getRequestErrorMessage(err) {
+    const data = err?.response?.data;
+    if (typeof data === "string" && data) return data;
+    if (data?.msg) return data.msg;
+    if (data?.message) return data.message;
+    if (err?.message) return err.message;
+    return "提交失败,请稍后再试";
+}
+
+function buildInteractSubmitPayload(answerText) {
+    const task = currentInteractTask.value || {};
+    const content = task.interact_content || {};
+    return {
+        zone_id: Number(task.zone_id ?? INTERACT_ZONE_ID),
+        date: formatToday(),
+        interact_code: task.interact_code || content.interact_code || "",
+        interact_answer: answerText,
+    };
+}
+
+function mapInteractOptionsFromContent(content) {
+    const options = content?.interact_options;
+    if (!Array.isArray(options)) return [];
+    return options.map((name, index) => ({
+        id: index + 1,
+        name: String(name),
+    }));
+}
+
+function applyFirstInteractTask(task) {
+    currentInteractTask.value = task || null;
+    const content = task?.interact_content || {};
+    const question = content.interact_problem || "";
+    const options = mapInteractOptionsFromContent(content);
+    interactQuestionText.value = question;
+    interactOptions.value = options;
+    selectedOptionId.value = null;
+    showInteractCard.value = Boolean(question || options.length);
+}
+
+async function handleInteractOptionClick(item) {
+    if (interactSubmitting.value || !item?.name || !currentInteractTask.value) return;
+    selectedOptionId.value = item.id;
+    interactSubmitting.value = true;
+    try {
+        const res = await VE_API.questionnaire.saveInteractResult(
+            buildInteractSubmitPayload(item.name)
+        );
+        if (res?.code === 200) {
+            ElMessage.success(res.msg || "提交成功");
+            showInteractCard.value = false;
+            await fetchInteractTask();
+            return;
+        }
+        ElMessage.error(res?.msg || "提交失败,请稍后再试");
+    } catch (err) {
+        ElMessage.error(getRequestErrorMessage(err));
+    } finally {
+        interactSubmitting.value = false;
+    }
+}
+
+async function fetchInteractTask() {
+    try {
+        const res = await VE_API.questionnaire.getExternalInteractTasks({
+            zone_id: INTERACT_ZONE_ID,
+        });
+        const tasks = res?.data?.interact_tasks;
+        const first = Array.isArray(tasks) ? tasks[0] : null;
+        applyFirstInteractTask(first);
+    } catch (e) {
+        console.warn("[growth_report] getExternalInteractTasks failed", e);
+    }
+}
 
 const stressMenuItems = [
     { key: "stress", line1: "胁迫", line2: "胁迫", icon: menuIcon },
@@ -389,12 +477,14 @@ const handlePanelHeightChange = (height) => {
 onMounted(() => {
     getLocationName();
     initMap();
+    fetchInteractTask();
     tryOpenInviteEntryPopup();
 });
 onActivated(() => {
     const applied = applySelectedLocation();
     if (!applied) getLocationName();
     initMap();
+    fetchInteractTask();
     tryOpenInviteEntryPopup();
 });
 </script>
@@ -516,6 +606,11 @@ onActivated(() => {
                     background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
                 }
             }
+
+            &.disabled {
+                opacity: 0.65;
+                pointer-events: none;
+            }
         }
     }