lxf 2 недель назад
Родитель
Сommit
60840c74f7
2 измененных файлов с 140 добавлено и 68 удалено
  1. 5 0
      src/api/modules/questionnaire.js
  2. 135 68
      src/views/old_mini/growth_report/index.vue

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

@@ -153,4 +153,9 @@ module.exports = {
         url: config.base_new_url + "questionnaire/find_farm_information_options",
         type: "get",
     },
+    // 外层长势问题互动接口
+    getOuterRecords: {
+        url: config.base_new_url + "pheno/pheno_template_records",
+        type: "get",
+    },
 }

+ 135 - 68
src/views/old_mini/growth_report/index.vue

@@ -3,14 +3,12 @@
         <div class="report-content">
             <div class="map-container" ref="mapContainer"></div>
             <!-- Overlay 宿主:无范围时挂到地图方框下方 -->
-            <div
-                ref="noRangeTipRef"
-                class="no-range-tip"
-                :style="{ display: noFarmRange ? 'inline-flex' : 'none' }"
-                @click.stop="goAddZone('farm')"
-            >
+            <div ref="noRangeTipRef" class="no-range-tip" :style="{ display: noFarmRange ? 'inline-flex' : 'none' }"
+                @click.stop="goAddZone('farm')">
                 <span>勾画区域,获地块级长势报告</span>
-                <el-icon class="no-range-tip__arrow"><ArrowRight /></el-icon>
+                <el-icon class="no-range-tip__arrow">
+                    <ArrowRight />
+                </el-icon>
             </div>
         </div>
 
@@ -22,7 +20,9 @@
                         <img class="interact-card__star" src="@/assets/img/agricultural/start.png" alt="" />
                         <span>长势互动</span>
                     </div>
-                    <el-icon class="interact-card__close" @click="showInteractCard = false"><Close /></el-icon>
+                    <el-icon class="interact-card__close" @click="showInteractCard = false">
+                        <Close />
+                    </el-icon>
                 </div>
                 <div class="interact-card__qa">
                     <div class="interact-card__question">{{ interactQuestionText }}</div>
@@ -46,24 +46,17 @@
 
         <!-- 左上角:位置切换 -->
         <div v-if="!showInteractCard" class="location-bar">
-            <el-icon class="location-bar__icon"><LocationFilled /></el-icon>
+            <el-icon class="location-bar__icon">
+                <LocationFilled />
+            </el-icon>
             <span class="location-bar__name van-ellipsis">{{ locationName }}</span>
-            <span
-                v-if="hasSelectedFarm()"
-                class="location-bar__action"
-                @click="handleSwitchFarm"
-            >切换农场</span>
+            <span v-if="hasSelectedFarm()" class="location-bar__action" @click="handleSwitchFarm">切换农场</span>
         </div>
 
         <!-- 左侧:气象底图图层菜单(有农场 getHomeMapCalc / 无农场 getHomeMapCalcByCounty) -->
         <div v-if="stressMenuItems.length" class="side-menu side-menu--left">
-            <div
-                v-for="item in stressMenuItems"
-                :key="item.key"
-                class="side-menu__item"
-                :class="{ active: activeMenuKey === item.key }"
-                @click="handleSelectMenu(item.key)"
-            >
+            <div v-for="item in stressMenuItems" :key="item.key" class="side-menu__item"
+                :class="{ active: activeMenuKey === item.key }" @click="handleSelectMenu(item.key)">
                 <img class="side-menu__icon" :src="base_img_url3 + item.icon_path" alt="" />
                 <div class="side-menu__text">
                     <span>{{ item.line1 }}</span>
@@ -72,38 +65,31 @@
             </div>
         </div>
 
-        <div
-            v-if="activeLegendUrl"
-            class="map-legend map-legend--image"
-            :style="{ bottom: `${inviteBottom}px` }"
-        >
+        <div v-if="activeLegendUrl" class="map-legend map-legend--image" :style="{ bottom: `${inviteBottom}px` }">
             <img class="map-legend__img" :src="activeLegendUrl" alt="" />
         </div>
 
         <div class="invite-group" :style="{ bottom: `${inviteBottom}px` }">
             <div class="invite-btn" @click="handleInvite('service')">
                 <img class="invite-icon" src="@/assets/img/home/user-icon.png" alt="">
-                邀请农服</div>
+                邀请农服
+            </div>
             <div class="invite-btn" @click="handleInvite('farmer')">
                 <img class="invite-icon" src="@/assets/img/home/user-icon.png" alt="">
-                邀请农户</div>
+                邀请农户
+            </div>
         </div>
 
-        <crop-alert-panel
-            :crop-name="currentCropName"
-            :farm-id="selectedFarmId"
-            :county-code="countyCode"
-            @zone-change="handleZoneChange"
-            @height-change="handlePanelHeightChange"
-        />
+        <crop-alert-panel :crop-name="currentCropName" :farm-id="selectedFarmId" :county-code="countyCode"
+            @zone-change="handleZoneChange" @height-change="handlePanelHeightChange" />
     </div>
 
     <!-- 录入信息邀请弹窗:分享链接带 showInviteEntry 时展示 -->
     <invite-entry-popup ref="invitePopupRef" />
 
-    
-        <!-- 恢复种植弹窗 -->
-        <restore-planting-popup />
+
+    <!-- 恢复种植弹窗 -->
+    <restore-planting-popup />
 </template>
 
 <script setup>
@@ -304,8 +290,13 @@ const showInteractCard = ref(false);
 const interactQuestionText = ref("");
 const interactOptions = ref([]);
 const selectedOptionId = ref(null);
-const currentInteractTask = ref(null);
 const interactSubmitting = ref(false);
+/** getOuterRecords 返回的 records */
+const outerRecords = ref([]);
+/** 当前展示的 records 下标 */
+const currentRecordIndex = ref(-1);
+/** 防止切换分区时旧请求回写 */
+let interactFetchToken = 0;
 
 function formatToday() {
     const d = new Date();
@@ -324,39 +315,62 @@ function getRequestErrorMessage(err) {
     return "提交失败,请稍后再试";
 }
 
-function buildInteractSubmitPayload(answerText) {
-    const task = currentInteractTask.value || {};
-    const content = task.interact_content || {};
-    return {
-        zone_id: Number(task.zone_id ?? panelZoneId.value ?? getSelectedZoneId()),
-        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) => ({
+/** interact_options 可能是 JSON 字符串或数组 */
+function parseInteractOptions(raw) {
+    let list = raw;
+    if (typeof raw === "string") {
+        try {
+            list = JSON.parse(raw);
+        } catch {
+            return [];
+        }
+    }
+    if (!Array.isArray(list)) return [];
+    return list.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);
+function clearInteractCard() {
+    outerRecords.value = [];
+    currentRecordIndex.value = -1;
+    interactQuestionText.value = "";
+    interactOptions.value = [];
+    selectedOptionId.value = null;
+    showInteractCard.value = false;
+}
+
+function applyOuterRecordAt(index) {
+    const records = outerRecords.value;
+    if (!records.length || index < 0 || index >= records.length) {
+        clearInteractCard();
+        return;
+    }
+    currentRecordIndex.value = index;
+    const record = records[index] || {};
+    const question = record.interact_problem || "";
+    const options = parseInteractOptions(record.interact_options);
     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;
+function buildInteractSubmitPayload(optionText) {
+    const record = outerRecords.value[currentRecordIndex.value] || {};
+    const zoneId = panelZoneId.value ?? getSelectedZoneId();
+    return {
+        zone_id: Number(zoneId),
+        date: formatToday(),
+        // interact_code: record.interact_code || "",
+        period_code: record.period_code || "",
+        // interact_option: optionText,
+    };
+}
+
+async function submitInteractOption(item) {
+    if (interactSubmitting.value) return;
     selectedOptionId.value = item.id;
     interactSubmitting.value = true;
     try {
@@ -377,18 +391,69 @@ async function handleInteractOptionClick(item) {
     }
 }
 
+/** 首项 → 上一条;末项 → 下一条;中间项提交 */
+async function handleInteractOptionClick(item) {
+    const options = interactOptions.value;
+    if (interactSubmitting.value || !item?.name || !options.length || currentRecordIndex.value < 0) {
+        return;
+    }
+    const isFirst = item.id === options[0]?.id;
+    const isLast = item.id === options[options.length - 1]?.id;
+    // 仅一项时按提交处理
+    if (options.length === 1 || (!isFirst && !isLast)) {
+        await submitInteractOption(item);
+        return;
+    }
+    selectedOptionId.value = item.id;
+    if (isFirst) {
+        if (currentRecordIndex.value > 0) {
+            applyOuterRecordAt(currentRecordIndex.value - 1);
+            return;
+        }
+        ElMessage.info("已是第一条");
+        return;
+    }
+    if (isLast) {
+        if (currentRecordIndex.value < outerRecords.value.length - 1) {
+            applyOuterRecordAt(currentRecordIndex.value + 1);
+            return;
+        }
+        ElMessage.info("已是最后一条");
+    }
+}
+
 async function fetchInteractTask() {
     const zoneId = panelZoneId.value ?? getSelectedZoneId();
-    if (zoneId === "" || zoneId == null) return;
+    if (zoneId === "" || zoneId == null) {
+        clearInteractCard();
+        return;
+    }
+    const token = ++interactFetchToken;
     try {
-        const res = await VE_API.questionnaire.getExternalInteractTasks({
+        const res = await VE_API.questionnaire.getOuterRecords({
             zone_id: zoneId,
         });
-        const tasks = res?.data?.interact_tasks;
-        const first = Array.isArray(tasks) ? tasks[0] : null;
-        applyFirstInteractTask(first);
+        if (token !== interactFetchToken) return;
+        if (res?.code !== 200) {
+            clearInteractCard();
+            return;
+        }
+        const data = res?.data || {};
+        const records = Array.isArray(data.records) ? data.records : [];
+        outerRecords.value = records;
+        if (!records.length) {
+            clearInteractCard();
+            return;
+        }
+        // 默认展示与外层 interact_code 匹配的那一项
+        const matchedIndex = records.findIndex(
+            (item) => item?.interact_code && item.interact_code === data.interact_code
+        );
+        applyOuterRecordAt(matchedIndex >= 0 ? matchedIndex : 0);
     } catch (e) {
-        console.warn("[growth_report] getExternalInteractTasks failed", e);
+        if (token !== interactFetchToken) return;
+        console.warn("[growth_report] getOuterRecords failed", e);
+        clearInteractCard();
     }
 }
 
@@ -1041,11 +1106,13 @@ onActivated(() => {
             color: #2199F8;
             font-weight: 500;
             font-size: 12px;
+
             .invite-icon {
                 width: 16px;
             }
         }
-        .invite-btn + .invite-btn {
+
+        .invite-btn+.invite-btn {
             margin-top: 10px;
         }
     }