Browse Source

fix: 对接品种

lxf 1 hour ago
parent
commit
bf4c9ec2ff

+ 10 - 0
src/api/modules/entry.js

@@ -6,4 +6,14 @@ module.exports = {
         url: config.base_new_url + "get_crop_hierarchy",
         type: "get",
     },
+    // 获取小品种对应列表
+    getVarietiesByCrop: {
+        url: config.base_new_url + "get_varieties_by_crop",
+        type: "get",
+    },
+    // 录入用户权属信息
+    addPlotInfo: {
+        url: config.base_new_url + "add_plot_info",
+        type: "post",
+    },
 }

+ 302 - 88
src/views/old_mini/entry_information/components/selectVariety.vue

@@ -7,17 +7,23 @@
             </div>
 
             <!-- 品类 Tab + 品种选择,照常显示 -->
-            <div class="category-tabs">
-                <div v-for="(tab, index) in categoryTabs" :key="tab.id" class="category-tab" :class="{
-                    active: activeCategoryId === tab.id,
-                    first: index === 0,
-                    last: index === categoryTabs.length - 1,
-                }">
+            <div class="category-tabs" v-if="categoryTabs.length">
+                <div
+                    v-for="(tab, index) in categoryTabs"
+                    :key="tab.id"
+                    class="category-tab"
+                    :class="{
+                        active: activeCategoryId === tab.id,
+                        first: index === 0,
+                        last: index === categoryTabs.length - 1,
+                    }"
+                    @click="activeCategoryId = tab.id"
+                >
                     <span>{{ tab.name }}</span>
                 </div>
             </div>
 
-            <div class="variety-card">
+            <div class="variety-card" v-loading="varietyLoading">
                 <el-input v-model="searchKeyword" class="search-bar" placeholder="请输入品种" @keyup.enter="handleSearch">
                     <template #prefix>
                         <el-icon class="search-icon">
@@ -36,6 +42,8 @@
                     </div>
                 </div>
 
+                <div v-if="!varietyLoading && !filteredVarieties.length" class="empty-tip">暂无品种</div>
+
                 <div v-if="filteredVarieties.length > DEFAULT_VISIBLE_COUNT" class="expand-trigger"
                     @click="gridExpanded = !gridExpanded">
                     {{ gridExpanded ? "点击收起" : "点击展开更多" }}
@@ -64,7 +72,7 @@
                             popper-class="variety-select-popper"
                         >
                             <el-option
-                                v-for="opt in phenologyOptions"
+                                v-for="opt in currentPhenologyOptions"
                                 :key="opt.id"
                                 :label="opt.name"
                                 :value="opt.id"
@@ -73,7 +81,7 @@
                     </div>
 
                     <div class="form-row">
-                        <span class="form-label muted">起种点问题</span>
+                        <span class="form-label muted">{{ currentStartingPointQuestion }}</span>
                         <el-date-picker
                             v-model="defaultForm.startTime"
                             class="form-picker"
@@ -127,7 +135,7 @@
                             popper-class="variety-select-popper"
                         >
                             <el-option
-                                v-for="opt in phenologyOptions"
+                                v-for="opt in getPhenologyOptions(item.categoryId)"
                                 :key="opt.id"
                                 :label="opt.name"
                                 :value="opt.id"
@@ -136,7 +144,7 @@
                     </div>
 
                     <div class="form-row">
-                        <span class="form-label">什么时候来一梢?</span>
+                        <span class="form-label">{{ getStartingPointQuestion(item.categoryId) }}</span>
                         <el-date-picker
                             v-model="item.startTime"
                             class="form-picker"
@@ -168,7 +176,9 @@
 
         <div class="custom-bottom-fixed-btns">
             <div class="bottom-btn secondary-btn" @click="handlePrev">上一步</div>
-            <div class="bottom-btn primary-btn" @click="handleConfirm">确定信息</div>
+            <div class="bottom-btn primary-btn" :class="{ disabled: submitting }" @click="handleConfirm">
+                {{ submitting ? "提交中..." : "确定信息" }}
+            </div>
         </div>
     </div>
 </template>
@@ -190,6 +200,7 @@ const DEFAULT_POINT = "POINT(113.6142086995688 23.585836479509055)";
 const LOCATION_KEY = "ENTRY_RESIDENT_LOCATION";
 const EDIT_UID_KEY = "ENTRY_VARIETY_EDIT_UID";
 const SELECTED_LIST_KEY = "ENTRY_SELECTED_VARIETY_LIST";
+const CATEGORY_SESSION_KEY = "ENTRY_SELECTED_CATEGORY";
 const MAP_PADDING = [6, 6, 6, 6];
 let selectedUid = 0;
 
@@ -330,19 +341,27 @@ const goSelectItemLocation = (item) => {
     });
 };
 
-onMounted(() => {
+onMounted(async () => {
+    initCategoryTabsFromSession();
     restoreSelectedListDraft();
     syncItemLocationFromSession();
     nextTick(() => initDefaultMap());
+    await Promise.all(
+        categoryTabs.value.map((tab) => fetchVarietiesByCrop(tab.id))
+    );
 });
 
-onActivated(() => {
+onActivated(async () => {
+    initCategoryTabsFromSession();
     restoreSelectedListDraft();
     syncItemLocationFromSession();
     nextTick(() => {
         initDefaultMap();
         itemPreviewMaps.forEach((map) => map.kmap?.map?.updateSize?.());
     });
+    await Promise.all(
+        categoryTabs.value.map((tab) => fetchVarietiesByCrop(tab.id))
+    );
 });
 
 onBeforeUnmount(() => {
@@ -352,48 +371,69 @@ onBeforeUnmount(() => {
     itemPreviewMaps.clear();
 });
 
-const categoryTabs = ref([
-    {
-        id: 1,
-        name: "品类一",
-        varieties: [
-            { id: "1-1", name: "妃子笑" },
-            { id: "1-2", name: "桂味" },
-            { id: "1-3", name: "桂早荔" },
-            { id: "1-4", name: "黑叶" },
-            { id: "1-5", name: "灵山香荔" },
-            { id: "1-6", name: "钦州红荔" },
-            { id: "1-7", name: "贵妃红" },
-            { id: "1-8", name: "翡脆" },
-            { id: "1-9", name: "糯米糍" },
-            { id: "1-10", name: "三月红" },
-            { id: "1-11", name: "白糖罂" },
-            { id: "1-12", name: "怀枝" },
-        ],
-    },
-    {
-        id: 2,
-        name: "品类二",
-        varieties: [
-            { id: "2-1", name: "品种A" },
-            { id: "2-2", name: "品种B" },
-            { id: "2-3", name: "品种C" },
-            { id: "2-4", name: "品种D" },
-        ],
-    },
-    {
-        id: 3,
-        name: "品类三",
-        varieties: [
-            { id: "3-1", name: "品种E" },
-            { id: "3-2", name: "品种F" },
-            { id: "3-3", name: "品种G" },
-            { id: "3-4", name: "品种H" },
-        ],
-    },
-]);
+const getSelectedCategoriesFromSession = () => {
+    try {
+        const raw = sessionStorage.getItem(CATEGORY_SESSION_KEY);
+        if (!raw) return [];
+        const list = JSON.parse(raw);
+        return Array.isArray(list) ? list : [];
+    } catch {
+        return [];
+    }
+};
+
+const mapPhenologyList = (list) => {
+    if (!Array.isArray(list)) return [];
+    return list
+        .map((item, index) => {
+            if (typeof item === "string") {
+                return { id: item, name: item };
+            }
+            const name = item?.name || item?.phenology || String(item?.id ?? index);
+            const id = item?.id ?? name;
+            return { id, name };
+        })
+        .filter((item) => item.name);
+};
+
+const initCategoryTabsFromSession = () => {
+    const selected = getSelectedCategoriesFromSession();
+    const prevLoaded = new Map(
+        categoryTabs.value.map((tab) => [String(tab.id), tab])
+    );
+    categoryTabs.value = selected.map((item) => {
+        const id = item.id;
+        const cached = prevLoaded.get(String(id));
+        if (cached?.loaded) {
+            return {
+                ...cached,
+                name: item.name || cached.name,
+            };
+        }
+        return {
+            id,
+            name: item.name || "",
+            varieties: [],
+            phenology: [],
+            startingPointQuestion: "起种点问题",
+            loaded: false,
+        };
+    });
+    if (!categoryTabs.value.length) {
+        activeCategoryId.value = null;
+        return;
+    }
+    const stillExists = categoryTabs.value.some(
+        (tab) => String(tab.id) === String(activeCategoryId.value)
+    );
+    if (!stillExists) {
+        activeCategoryId.value = categoryTabs.value[0].id;
+    }
+};
 
-const activeCategoryId = ref(1);
+const categoryTabs = ref([]);
+const activeCategoryId = ref(null);
+const varietyLoading = ref(false);
 const searchKeyword = ref("");
 const appliedKeyword = ref("");
 const gridExpanded = ref(false);
@@ -406,6 +446,48 @@ const defaultForm = reactive({
     area: "",
 });
 
+const fetchVarietiesByCrop = async (cropId, force = false) => {
+    if (cropId == null || cropId === "") return;
+    const tab = categoryTabs.value.find((item) => String(item.id) === String(cropId));
+    if (!tab) return;
+    if (tab.loaded && !force) return;
+
+    const showLoading = String(cropId) === String(activeCategoryId.value);
+    if (showLoading) varietyLoading.value = true;
+    try {
+        const res = await VE_API.entry.getVarietiesByCrop({
+            crop_id: cropId,
+        });
+        if (res.code === 200 && res.data) {
+            const data = res.data;
+            tab.varieties = (data.varieties || []).map((item) => ({
+                id: item.id,
+                name: item.name,
+            }));
+            tab.phenology = mapPhenologyList(data.phenology);
+            tab.startingPointQuestion =
+                data.starting_point_question || "起种点问题";
+            tab.loaded = true;
+        } else {
+            tab.varieties = [];
+            tab.phenology = [];
+            tab.startingPointQuestion = "起种点问题";
+            if (showLoading) {
+                ElMessage.error(res.msg || "获取品种列表失败");
+            }
+        }
+    } catch {
+        tab.varieties = [];
+        tab.phenology = [];
+        tab.startingPointQuestion = "起种点问题";
+        if (showLoading) {
+            ElMessage.error("获取品种列表失败,请稍后再试");
+        }
+    } finally {
+        if (showLoading) varietyLoading.value = false;
+    }
+};
+
 watch(
     selectedList,
     () => {
@@ -415,7 +497,25 @@ watch(
 );
 
 const currentCategory = computed(
-    () => categoryTabs.value.find((tab) => tab.id === activeCategoryId.value) || categoryTabs.value[0]
+    () =>
+        categoryTabs.value.find((tab) => String(tab.id) === String(activeCategoryId.value)) ||
+        categoryTabs.value[0]
+);
+
+const getPhenologyOptions = (categoryId) => {
+    const tab = categoryTabs.value.find((item) => String(item.id) === String(categoryId));
+    return tab?.phenology || [];
+};
+
+const getStartingPointQuestion = (categoryId) => {
+    const tab = categoryTabs.value.find((item) => String(item.id) === String(categoryId));
+    return tab?.startingPointQuestion || "起种点问题";
+};
+
+const currentPhenologyOptions = computed(() => getPhenologyOptions(activeCategoryId.value));
+
+const currentStartingPointQuestion = computed(() =>
+    getStartingPointQuestion(activeCategoryId.value)
 );
 
 const allVarieties = computed(() => {
@@ -444,27 +544,24 @@ const visibleVarieties = computed(() => {
     return filteredVarieties.value.slice(0, DEFAULT_VISIBLE_COUNT);
 });
 
-watch(activeCategoryId, () => {
+watch(activeCategoryId, (id) => {
     searchKeyword.value = "";
     appliedKeyword.value = "";
     gridExpanded.value = false;
+    resetDefaultForm();
+    if (id != null) {
+        fetchVarietiesByCrop(id);
+    }
 });
 
-const isSelected = (id) => selectedList.value.some((item) => item.id === id);
+const isSelected = (id) =>
+    selectedList.value.some((item) => String(item.id) === String(id));
 
 const handleSearch = () => {
     appliedKeyword.value = searchKeyword.value;
     gridExpanded.value = false;
 };
 
-const phenologyOptions = [
-    { id: "p1", name: "梢期" },
-    { id: "p2", name: "花期" },
-    { id: "p3", name: "坐果期" },
-    { id: "p4", name: "转色期" },
-    { id: "p5", name: "成熟期" },
-];
-
 const createSelectedItem = (variety, category) => ({
     uid: `sel-${++selectedUid}`,
     id: variety.id,
@@ -492,7 +589,7 @@ const tipSelectVariety = () => {
 /** 顶部「选择品种」:选中后新增下方卡片,顶部表单重置为空 */
 const handleDefaultVarietyChange = (varietyId) => {
     if (!varietyId) return;
-    const found = allVarieties.value.find((item) => item.id === varietyId);
+    const found = allVarieties.value.find((item) => String(item.id) === String(varietyId));
     if (!found) {
         resetDefaultForm();
         return;
@@ -512,18 +609,19 @@ const handleDefaultVarietyChange = (varietyId) => {
 };
 
 const handleSelectedVarietyChange = (index, varietyId) => {
-    const found = allVarieties.value.find((item) => item.id === varietyId);
+    const found = allVarieties.value.find((item) => String(item.id) === String(varietyId));
     const item = selectedList.value[index];
     if (!item || !found) return;
     item.id = found.id;
     item.name = found.name;
     item.categoryId = found.categoryId;
     item.categoryName = found.categoryName;
+    item.phenologyId = "";
     saveSelectedListDraft();
 };
 
 const toggleVariety = (variety) => {
-    const index = selectedList.value.findIndex((item) => item.id === variety.id);
+    const index = selectedList.value.findIndex((item) => String(item.id) === String(variety.id));
     if (index > -1) {
         selectedList.value.splice(index, 1);
         saveSelectedListDraft();
@@ -542,7 +640,9 @@ const removeVariety = (uid) => {
 };
 
 const handlePrev = () => {
-    const idx = categoryTabs.value.findIndex((tab) => tab.id === activeCategoryId.value);
+    const idx = categoryTabs.value.findIndex(
+        (tab) => String(tab.id) === String(activeCategoryId.value)
+    );
     if (idx > 0) {
         activeCategoryId.value = categoryTabs.value[idx - 1].id;
         saveSelectedListDraft();
@@ -551,27 +651,122 @@ const handlePrev = () => {
     emit("prev");
 };
 
-const handleConfirm = () => {
-    const idx = categoryTabs.value.findIndex((tab) => tab.id === activeCategoryId.value);
+const getBaForm = () => {
+    try {
+        const raw = sessionStorage.getItem("ENTRY_BA_FORM");
+        return raw ? JSON.parse(raw) : null;
+    } catch {
+        return null;
+    }
+};
+
+const getPhenophaseLabel = (item) => {
+    const options = getPhenologyOptions(item.categoryId);
+    const found = options.find((opt) => String(opt.id) === String(item.phenologyId));
+    return found?.name || String(item.phenologyId || "");
+};
+
+const validateSubmitData = () => {
+    if (!categoryTabs.value.length) {
+        ElMessage.warning("请先选择种植品类");
+        return false;
+    }
+    if (!selectedList.value.length) {
+        ElMessage.warning("请至少选择一个品种");
+        return false;
+    }
+    for (const tab of categoryTabs.value) {
+        const hasVariety = selectedList.value.some(
+            (item) => String(item.categoryId) === String(tab.id)
+        );
+        if (!hasVariety) {
+            ElMessage.warning(`请为「${tab.name}」选择品种并填写信息`);
+            activeCategoryId.value = tab.id;
+            return false;
+        }
+    }
+    for (const item of selectedList.value) {
+        if (!item.phenologyId) {
+            ElMessage.warning(`请选择「${item.name}」的当下物候期`);
+            return false;
+        }
+        if (!item.startTime) {
+            ElMessage.warning(`请填写「${item.name}」的起种点时间`);
+            return false;
+        }
+        if (item.area === "" || item.area == null || Number(item.area) <= 0) {
+            ElMessage.warning(`请填写「${item.name}」的种植亩数`);
+            return false;
+        }
+        if (!item.location) {
+            ElMessage.warning(`请选择「${item.name}」的种植点位`);
+            return false;
+        }
+    }
+    const baForm = getBaForm();
+    if (!baForm?.name || !baForm?.phone) {
+        ElMessage.warning("请先完善个人信息");
+        return false;
+    }
+    return true;
+};
+
+const buildPlotPayload = () => {
+    const baForm = getBaForm() || {};
+    return {
+        user_name: baForm.name,
+        tel: baForm.phone,
+        crops: selectedList.value.map((item) => ({
+            crop_type: item.categoryName,
+            crop_id: Number(item.categoryId),
+            variety_list: [Number(item.id)],
+            phenophase: getPhenophaseLabel(item),
+            start_time: item.startTime,
+            plant_area: Number(item.area),
+            point: item.location,
+        })),
+    };
+};
+
+const clearEntrySession = () => {
+    sessionStorage.removeItem(SELECTED_LIST_KEY);
+    sessionStorage.removeItem(CATEGORY_SESSION_KEY);
+    sessionStorage.removeItem("ENTRY_BA_FORM");
+    sessionStorage.removeItem(LOCATION_KEY);
+    sessionStorage.removeItem(EDIT_UID_KEY);
+};
+
+const submitting = ref(false);
+
+const handleConfirm = async () => {
+    const idx = categoryTabs.value.findIndex(
+        (tab) => String(tab.id) === String(activeCategoryId.value)
+    );
     if (idx > -1 && idx < categoryTabs.value.length - 1) {
         activeCategoryId.value = categoryTabs.value[idx + 1].id;
         saveSelectedListDraft();
         return;
     }
-    sessionStorage.removeItem(SELECTED_LIST_KEY);
-    emit("confirm", {
-        defaultForm: { ...defaultForm },
-        varieties: selectedList.value.map(({ id, name, categoryId, categoryName, phenologyId, startTime, area, location }) => ({
-            id,
-            name,
-            categoryId,
-            categoryName,
-            phenologyId,
-            startTime,
-            area,
-            location,
-        })),
-    });
+    if (!validateSubmitData()) return;
+    if (submitting.value) return;
+
+    submitting.value = true;
+    try {
+        const params = buildPlotPayload();
+        console.log('params', params);
+        // const res = await VE_API.entry.addPlotInfo(params);
+        // if (res.code === 200) {
+        //     ElMessage.success(res.msg || "提交成功");
+        //     clearEntrySession();
+        //     emit("confirm", params);
+        // } else {
+        //     ElMessage.error(res.msg || "提交失败,请稍后再试");
+        // }
+    } catch {
+        ElMessage.error("提交失败,请稍后再试");
+    } finally {
+        submitting.value = false;
+    }
 };
 </script>
 
@@ -718,6 +913,13 @@ const handleConfirm = () => {
                 text-align: center;
                 color: rgba(0, 0, 0, 0.6);
             }
+
+            .empty-tip {
+                padding: 24px 0 10px;
+                text-align: center;
+                color: rgba(0, 0, 0, 0.35);
+                font-size: 14px;
+            }
         }
 
         .form-card {
@@ -729,7 +931,7 @@ const handleConfirm = () => {
             &__header {
                 display: flex;
                 align-items: center;
-                border-bottom: .2px solid rgba(0, 0, 0, 0.2);
+                border-bottom: .2px solid rgba(0, 0, 0, 0.05);
                 padding-bottom: 10px;
 
                 .title-icon {
@@ -786,6 +988,9 @@ const handleConfirm = () => {
 
                         :deep(.el-select__selected-item),
                         :deep(.el-select__placeholder) {
+                            color: #1D2129;
+                        }
+                        :deep(.el-select__placeholder.is-transparent) {
                             color: rgba(34, 34, 34, 0.3);
                         }
 
@@ -839,6 +1044,7 @@ const handleConfirm = () => {
                         &::placeholder {
                             color: rgba(0, 0, 0, 0.25);
                         }
+
                     }
                 }
 
@@ -870,10 +1076,13 @@ const handleConfirm = () => {
                     }
 
                     :deep(.el-select__placeholder) {
-                        color: rgba(0, 0, 0, 0.25);
+                        color: #1D2129;
                         position: static;
                         transform: none;
                     }
+                    :deep(.el-select__placeholder.is-transparent) {
+                        color: rgba(0, 0, 0, 0.25);
+                    }
 
                     :deep(.el-select__caret) {
                         color: rgba(0, 0, 0, 0.25);
@@ -927,6 +1136,11 @@ const handleConfirm = () => {
         .primary-btn {
             background: #2199f8;
             color: #fff;
+
+            &.disabled {
+                opacity: 0.6;
+                pointer-events: none;
+            }
         }
     }
 }

+ 3 - 1
src/views/old_mini/entry_information/index.vue

@@ -11,11 +11,13 @@
 
 <script setup>
 import { ref } from "vue";
+import { useRouter } from "vue-router";
 import customHeader from "@/components/customHeader.vue";
 import baInformation from "./components/baInformation.vue";
 import selectCategory from "./components/selectCategory.vue";
 import selectVariety from "./components/selectVariety.vue";
 
+const router = useRouter();
 const currentStep = ref(1);
 
 const handleNext = () => {
@@ -31,7 +33,7 @@ const handlePrev = () => {
 };
 
 const handleConfirm = () => {
-    // TODO: 提交信息
+    // router.replace("/growth_report");
 };
 </script>