|
|
@@ -30,7 +30,7 @@
|
|
|
<div class="search-btn" @click="handleSearch">搜索</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="category-list">
|
|
|
+ <div class="category-list" v-loading="loading">
|
|
|
<div
|
|
|
v-for="group in displayGroups"
|
|
|
:key="group.name"
|
|
|
@@ -52,7 +52,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div v-if="!displayGroups.length" class="empty-tip">暂无匹配品类</div>
|
|
|
+ <div v-if="!loading && !displayGroups.length" class="empty-tip">暂无匹配品类</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -64,7 +64,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { computed, onMounted, ref } from "vue";
|
|
|
+import { computed, ref, watch } from "vue";
|
|
|
import { ElMessage } from "element-plus";
|
|
|
import { Search } from "@element-plus/icons-vue";
|
|
|
|
|
|
@@ -72,109 +72,17 @@ const SESSION_KEY = "ENTRY_SELECTED_CATEGORY";
|
|
|
|
|
|
const emit = defineEmits(["prev", "next"]);
|
|
|
|
|
|
+/** first_crop 与接口约定一致:果树 / 大田 / 蔬菜 */
|
|
|
const majorTabs = ref([
|
|
|
- {
|
|
|
- key: "fruit",
|
|
|
- label: "果树类",
|
|
|
- groups: [
|
|
|
- {
|
|
|
- name: "热带亚热带",
|
|
|
- items: [
|
|
|
- { id: "mango", name: "芒果", selected: false },
|
|
|
- { id: "litchi", name: "荔枝", selected: false },
|
|
|
- { id: "longan", name: "龙眼", selected: false },
|
|
|
- { id: "banana", name: "香蕉", selected: false },
|
|
|
- { id: "pineapple", name: "菠萝", selected: false },
|
|
|
- { id: "coconut", name: "椰子", selected: false },
|
|
|
- { id: "papaya", name: "木瓜", selected: false },
|
|
|
- { id: "jackfruit", name: "菠萝蜜", selected: false },
|
|
|
- { id: "passion", name: "百香果", selected: false },
|
|
|
- { id: "durian", name: "榴莲", selected: false },
|
|
|
- { id: "rambutan", name: "红毛丹", selected: false },
|
|
|
- { id: "waxapple", name: "莲雾", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "柑果类",
|
|
|
- items: [
|
|
|
- { id: "mandarin", name: "柑橘", selected: false },
|
|
|
- { id: "orange", name: "橙子", selected: false },
|
|
|
- { id: "pomelo", name: "柚子", selected: false },
|
|
|
- { id: "lemon", name: "柠檬", selected: false },
|
|
|
- { id: "kumquat", name: "金桔", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "仁果类",
|
|
|
- items: [
|
|
|
- { id: "apple", name: "苹果", selected: false },
|
|
|
- { id: "pear", name: "梨", selected: false },
|
|
|
- { id: "hawthorn", name: "山楂", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "核果类",
|
|
|
- items: [
|
|
|
- { id: "tao", name: "桃", selected: false },
|
|
|
- { id: "yt", name: "樱桃", selected: false },
|
|
|
- { id: "li", name: "李", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- key: "field",
|
|
|
- label: "大田类",
|
|
|
- groups: [
|
|
|
- {
|
|
|
- name: "粮食作物",
|
|
|
- items: [
|
|
|
- { id: "rice", name: "水稻", selected: false },
|
|
|
- { id: "wheat", name: "小麦", selected: false },
|
|
|
- { id: "corn", name: "玉米", selected: false },
|
|
|
- { id: "soybean", name: "大豆", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "经济作物",
|
|
|
- items: [
|
|
|
- { id: "cotton", name: "棉花", selected: false },
|
|
|
- { id: "peanut", name: "花生", selected: false },
|
|
|
- { id: "rape", name: "油菜", selected: false },
|
|
|
- { id: "sugarcane", name: "甘蔗", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- key: "vegetable",
|
|
|
- label: "蔬菜类",
|
|
|
- groups: [
|
|
|
- {
|
|
|
- name: "叶菜类",
|
|
|
- items: [
|
|
|
- { id: "cabbage", name: "白菜", selected: false },
|
|
|
- { id: "spinach", name: "菠菜", selected: false },
|
|
|
- { id: "lettuce", name: "生菜", selected: false },
|
|
|
- { id: "celery", name: "芹菜", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "瓜果类",
|
|
|
- items: [
|
|
|
- { id: "tomato", name: "番茄", selected: false },
|
|
|
- { id: "cucumber", name: "黄瓜", selected: false },
|
|
|
- { id: "eggplant", name: "茄子", selected: false },
|
|
|
- { id: "pepper", name: "辣椒", selected: false },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
+ { key: "fruit", label: "果树类", firstCrop: "果树", groups: [], loaded: false },
|
|
|
+ { key: "field", label: "大田类", firstCrop: "大田", groups: [], loaded: false },
|
|
|
+ { key: "vegetable", label: "蔬菜类", firstCrop: "蔬菜", groups: [], loaded: false },
|
|
|
]);
|
|
|
|
|
|
const activeTabKey = ref("fruit");
|
|
|
const searchKeyword = ref("");
|
|
|
const appliedKeyword = ref("");
|
|
|
+const loading = ref(false);
|
|
|
|
|
|
const activeTab = computed(() =>
|
|
|
majorTabs.value.find((tab) => tab.key === activeTabKey.value) || majorTabs.value[0]
|
|
|
@@ -204,6 +112,7 @@ const selectedItems = computed(() => {
|
|
|
groupName: group.name,
|
|
|
majorKey: tab.key,
|
|
|
majorLabel: tab.label,
|
|
|
+ firstCrop: tab.firstCrop,
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
@@ -212,6 +121,66 @@ const selectedItems = computed(() => {
|
|
|
return list;
|
|
|
});
|
|
|
|
|
|
+const mapHierarchyToGroups = (list) => {
|
|
|
+ if (!Array.isArray(list)) return [];
|
|
|
+ return list
|
|
|
+ .map((group) => ({
|
|
|
+ name: group.second_type || group.name || "",
|
|
|
+ items: (group.third_crops || []).map((crop) => ({
|
|
|
+ id: crop.id,
|
|
|
+ name: crop.name,
|
|
|
+ selected: false,
|
|
|
+ })),
|
|
|
+ }))
|
|
|
+ .filter((group) => group.name && group.items.length);
|
|
|
+};
|
|
|
+
|
|
|
+const getSelectedIdSet = () => {
|
|
|
+ try {
|
|
|
+ const raw = sessionStorage.getItem(SESSION_KEY);
|
|
|
+ if (!raw) return new Set();
|
|
|
+ return new Set(JSON.parse(raw).map((item) => String(item.id)));
|
|
|
+ } catch {
|
|
|
+ return new Set();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const applySelectionToTab = (tab) => {
|
|
|
+ const selectedIds = getSelectedIdSet();
|
|
|
+ if (!selectedIds.size) return;
|
|
|
+ tab.groups.forEach((group) => {
|
|
|
+ group.items.forEach((item) => {
|
|
|
+ item.selected = selectedIds.has(String(item.id));
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const fetchTabGroups = async (tabKey, force = false) => {
|
|
|
+ const tab = majorTabs.value.find((item) => item.key === tabKey);
|
|
|
+ if (!tab) return;
|
|
|
+ if (tab.loaded && !force) return;
|
|
|
+
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await VE_API.entry.getCropHierarchy({
|
|
|
+ first_crop: tab.firstCrop,
|
|
|
+ });
|
|
|
+ if (res.code === 200) {
|
|
|
+ tab.groups = mapHierarchyToGroups(res.data);
|
|
|
+ tab.loaded = true;
|
|
|
+ applySelectionToTab(tab);
|
|
|
+ } else {
|
|
|
+ tab.groups = [];
|
|
|
+ ElMessage.error(res.msg || "获取种植品类失败");
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ tab.groups = [];
|
|
|
+ ElMessage.error("获取种植品类失败,请稍后再试");
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const handleSearch = () => {
|
|
|
appliedKeyword.value = searchKeyword.value.trim();
|
|
|
};
|
|
|
@@ -220,35 +189,47 @@ const handleSelect = (item) => {
|
|
|
item.selected = !item.selected;
|
|
|
};
|
|
|
|
|
|
-const restoreSelection = () => {
|
|
|
+const getCachedSelectedItems = () => {
|
|
|
try {
|
|
|
const raw = sessionStorage.getItem(SESSION_KEY);
|
|
|
- if (!raw) return;
|
|
|
- const selectedIds = new Set(JSON.parse(raw).map((item) => item.id));
|
|
|
- majorTabs.value.forEach((tab) => {
|
|
|
- tab.groups.forEach((group) => {
|
|
|
- group.items.forEach((item) => {
|
|
|
- item.selected = selectedIds.has(item.id);
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
+ if (!raw) return [];
|
|
|
+ const list = JSON.parse(raw);
|
|
|
+ return Array.isArray(list) ? list : [];
|
|
|
} catch {
|
|
|
- // ignore
|
|
|
+ return [];
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+const getSubmitSelectedItems = () => {
|
|
|
+ const current = selectedItems.value;
|
|
|
+ const cached = getCachedSelectedItems();
|
|
|
+ const loadedKeys = new Set(
|
|
|
+ majorTabs.value.filter((tab) => tab.loaded).map((tab) => tab.key)
|
|
|
+ );
|
|
|
+ // 已加载 Tab 以当前勾选为准;未加载 Tab 保留缓存里的选择
|
|
|
+ const fromCache = cached.filter((item) => !loadedKeys.has(item.majorKey));
|
|
|
+ return [...current, ...fromCache];
|
|
|
+};
|
|
|
+
|
|
|
const handleNext = () => {
|
|
|
- if (!selectedItems.value.length) {
|
|
|
+ const list = getSubmitSelectedItems();
|
|
|
+ if (!list.length) {
|
|
|
ElMessage.warning("请至少选择一个种植品类");
|
|
|
return;
|
|
|
}
|
|
|
- sessionStorage.setItem(SESSION_KEY, JSON.stringify(selectedItems.value));
|
|
|
- emit("next", selectedItems.value);
|
|
|
+ sessionStorage.setItem(SESSION_KEY, JSON.stringify(list));
|
|
|
+ emit("next", list);
|
|
|
};
|
|
|
|
|
|
-onMounted(() => {
|
|
|
- restoreSelection();
|
|
|
-});
|
|
|
+watch(
|
|
|
+ activeTabKey,
|
|
|
+ (key) => {
|
|
|
+ appliedKeyword.value = "";
|
|
|
+ searchKeyword.value = "";
|
|
|
+ fetchTabGroups(key);
|
|
|
+ },
|
|
|
+ { immediate: true },
|
|
|
+);
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|