|
@@ -1,1289 +0,0 @@
|
|
|
-<template>
|
|
|
|
|
- <div class="select-variety">
|
|
|
|
|
- <div class="select-variety__content">
|
|
|
|
|
- <div class="page-header">
|
|
|
|
|
- <div class="page-title">完善种植品种</div>
|
|
|
|
|
- <div class="page-title">精准管理每一块地块<span class="page-title-tip">(单选)</span></div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- <div class="variety-card">
|
|
|
|
|
- <!-- 单品类标题 -->
|
|
|
|
|
- <div class="category-title" v-if="currentCategory?.name">
|
|
|
|
|
- <span class="category-title__line"></span>
|
|
|
|
|
- <span class="category-title__diamond"></span>
|
|
|
|
|
- <span class="category-title__text">品类 - {{ currentCategory.name }}</span>
|
|
|
|
|
- <span class="category-title__diamond"></span>
|
|
|
|
|
- <span class="category-title__line right-line"></span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-input v-model="searchKeyword" class="search-bar" placeholder="请输入品种" @keyup.enter="handleSearch">
|
|
|
|
|
- <template #prefix>
|
|
|
|
|
- <el-icon class="search-icon">
|
|
|
|
|
- <Search />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template #suffix>
|
|
|
|
|
- <span class="search-btn" @click="handleSearch">搜索</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-input>
|
|
|
|
|
-
|
|
|
|
|
- <div class="variety-grid">
|
|
|
|
|
- <div v-for="item in visibleVarieties" :key="item.id" class="variety-item"
|
|
|
|
|
- :class="{ selected: isSelected(item.id) }" @click="toggleVariety(item)">
|
|
|
|
|
- {{ item.name }}
|
|
|
|
|
- </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 ? "点击收起" : "点击展开更多" }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 默认表单卡(单选,始终一张) -->
|
|
|
|
|
- <div
|
|
|
|
|
- v-if="currentSelected"
|
|
|
|
|
- :key="currentSelected.uid"
|
|
|
|
|
- class="form-card"
|
|
|
|
|
- :class="hasVariety ? 'selected-card' : 'form-card--default'"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="form-card__header">
|
|
|
|
|
- <img class="title-icon" src="@/assets/img/home/label-icon.png" alt="" />
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="currentSelected.id"
|
|
|
|
|
- class="variety-select"
|
|
|
|
|
- placeholder="选择品种"
|
|
|
|
|
- popper-class="variety-select-popper"
|
|
|
|
|
- @change="handleFormVarietyChange"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="opt in currentVarieties"
|
|
|
|
|
- :key="opt.id"
|
|
|
|
|
- :label="opt.name"
|
|
|
|
|
- :value="opt.id"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <!-- <span v-if="hasVariety" class="delete-btn" @click="clearVariety">删除</span> -->
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div
|
|
|
|
|
- class="form-card__body"
|
|
|
|
|
- :class="{ 'form-card__body--locked': !hasVariety }"
|
|
|
|
|
- @click="onFormBodyClick"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <span class="form-label">当下物候期</span>
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="currentSelected.phenologyId"
|
|
|
|
|
- class="form-select"
|
|
|
|
|
- placeholder="选择物候期"
|
|
|
|
|
- :loading="interactLoading"
|
|
|
|
|
- placement="bottom-end"
|
|
|
|
|
- popper-class="variety-select-popper"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="opt in currentPhenologyOptions"
|
|
|
|
|
- :key="opt.id"
|
|
|
|
|
- :label="opt.name"
|
|
|
|
|
- :value="opt.id"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <span class="form-label" :class="{ muted: !hasVariety }">
|
|
|
|
|
- {{ currentStartingPointQuestion }}
|
|
|
|
|
- </span>
|
|
|
|
|
- <el-date-picker
|
|
|
|
|
- v-model="currentSelected.startTime"
|
|
|
|
|
- class="form-picker"
|
|
|
|
|
- type="date"
|
|
|
|
|
- placeholder="请选择时间"
|
|
|
|
|
- format="YYYY-MM-DD"
|
|
|
|
|
- value-format="YYYY-MM-DD"
|
|
|
|
|
- :clearable="false"
|
|
|
|
|
- :disabled-date="disableFutureDates"
|
|
|
|
|
- style="width: 118px; flex:none;"
|
|
|
|
|
- :readonly="!hasVariety"
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <span class="form-label">种植亩数</span>
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="currentSelected.area"
|
|
|
|
|
- class="form-input"
|
|
|
|
|
- placeholder="请输入亩数"
|
|
|
|
|
- type="number"
|
|
|
|
|
- :readonly="!hasVariety"
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="form-row location-row">
|
|
|
|
|
- <span class="form-label">选择种植点位</span>
|
|
|
|
|
- <div
|
|
|
|
|
- class="map"
|
|
|
|
|
- :class="{ 'map--clickable': hasVariety }"
|
|
|
|
|
- :ref="(el) => setItemMapRef(currentSelected.uid, el)"
|
|
|
|
|
- @click="goSelectItemLocation(currentSelected)"
|
|
|
|
|
- ></div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="custom-bottom-fixed-btns">
|
|
|
|
|
- <div class="bottom-btn secondary-btn" @click="handlePrev">上一步</div>
|
|
|
|
|
- <div class="bottom-btn primary-btn" @click="handleConfirm">{{ varietyNextText }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script setup>
|
|
|
|
|
-import { computed, nextTick, onActivated, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
|
|
|
-import { ElMessage } from "element-plus";
|
|
|
|
|
-import { Search } from "@element-plus/icons-vue";
|
|
|
|
|
-import { useRouter } from "vue-router";
|
|
|
|
|
-import { useStore } from "vuex";
|
|
|
|
|
-import SelectLocationMap, { POINT_ICON } from "../map/selectLocationMap.js";
|
|
|
|
|
-
|
|
|
|
|
-const emit = defineEmits(["prev", "next"]);
|
|
|
|
|
-const props = defineProps({
|
|
|
|
|
- multiple: { type: Boolean, default: false },
|
|
|
|
|
- isLastStep: { type: Boolean, default: false },
|
|
|
|
|
-});
|
|
|
|
|
-const router = useRouter();
|
|
|
|
|
-const store = useStore();
|
|
|
|
|
-
|
|
|
|
|
-const varietyNextText = computed(() => (props.isLastStep ? "提交信息" : "下一步 (3/4)"));
|
|
|
|
|
-
|
|
|
|
|
-const DEFAULT_VISIBLE_COUNT = 12;
|
|
|
|
|
-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;
|
|
|
|
|
-
|
|
|
|
|
-const itemPreviewMaps = new Map();
|
|
|
|
|
-
|
|
|
|
|
-function getResidentPoint() {
|
|
|
|
|
- try {
|
|
|
|
|
- const saved = JSON.parse(sessionStorage.getItem(LOCATION_KEY) || "null");
|
|
|
|
|
- return saved?.point || "";
|
|
|
|
|
- } catch {
|
|
|
|
|
- return "";
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function getDefaultPoint() {
|
|
|
|
|
- return (
|
|
|
|
|
- getResidentPoint() ||
|
|
|
|
|
- localStorage.getItem("MINI_USER_LOCATION_POINT") ||
|
|
|
|
|
- store.state.home.miniUserLocationPoint ||
|
|
|
|
|
- DEFAULT_POINT
|
|
|
|
|
- );
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function utilWktToCoordinate(point) {
|
|
|
|
|
- const match = String(point).match(/POINT\s*\(([\d.\-]+)\s+([\d.\-]+)\)/i);
|
|
|
|
|
- if (!match) return null;
|
|
|
|
|
- return [Number(match[1]), Number(match[2])];
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function createPreviewMap(el, point, showPoint = false) {
|
|
|
|
|
- if (!el || !point) return null;
|
|
|
|
|
- const map = new SelectLocationMap({ iconSrc: POINT_ICON.plant });
|
|
|
|
|
- map.initMap(point, el, {
|
|
|
|
|
- enableClick: false,
|
|
|
|
|
- padding: MAP_PADDING,
|
|
|
|
|
- showPoint,
|
|
|
|
|
- });
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- map.kmap?.map?.updateSize?.();
|
|
|
|
|
- });
|
|
|
|
|
- return map;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function destroyPreviewMap(map) {
|
|
|
|
|
- map?.clearLayer();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function updateItemMap(uid, point, showPoint) {
|
|
|
|
|
- const map = itemPreviewMaps.get(uid);
|
|
|
|
|
- if (!map?.kmap || !point) return;
|
|
|
|
|
- const coordinate = utilWktToCoordinate(point);
|
|
|
|
|
- if (!coordinate) return;
|
|
|
|
|
- if (showPoint) {
|
|
|
|
|
- map.showPoint(coordinate);
|
|
|
|
|
- } else {
|
|
|
|
|
- map.hidePoint();
|
|
|
|
|
- map.setMapPosition(coordinate, false);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function setItemMapRef(uid, el) {
|
|
|
|
|
- if (!el) {
|
|
|
|
|
- destroyPreviewMap(itemPreviewMaps.get(uid));
|
|
|
|
|
- itemPreviewMaps.delete(uid);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (itemPreviewMaps.has(uid)) {
|
|
|
|
|
- nextTick(() => itemPreviewMaps.get(uid)?.kmap?.map?.updateSize?.());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- const item = selectedList.value.find((i) => i.uid === uid);
|
|
|
|
|
- const point = item?.location || getDefaultPoint();
|
|
|
|
|
- const map = createPreviewMap(el, point, !!item?.location);
|
|
|
|
|
- if (map) itemPreviewMaps.set(uid, map);
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function syncItemLocationFromSession() {
|
|
|
|
|
- const uid = sessionStorage.getItem(EDIT_UID_KEY);
|
|
|
|
|
- if (!uid) return;
|
|
|
|
|
- sessionStorage.removeItem(EDIT_UID_KEY);
|
|
|
|
|
- try {
|
|
|
|
|
- const raw = sessionStorage.getItem(LOCATION_KEY);
|
|
|
|
|
- const saved = raw ? JSON.parse(raw) : null;
|
|
|
|
|
- if (!saved?.point) return;
|
|
|
|
|
- const item = selectedList.value.find((i) => i.uid === uid);
|
|
|
|
|
- if (!item) return;
|
|
|
|
|
- item.location = saved.point;
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
- nextTick(() => updateItemMap(uid, saved.point, true));
|
|
|
|
|
- } catch {
|
|
|
|
|
- // ignore
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function saveSelectedListDraft() {
|
|
|
|
|
- try {
|
|
|
|
|
- sessionStorage.setItem(
|
|
|
|
|
- SELECTED_LIST_KEY,
|
|
|
|
|
- JSON.stringify({
|
|
|
|
|
- selectedUid,
|
|
|
|
|
- activeCategoryId: activeCategoryId.value,
|
|
|
|
|
- list: selectedList.value,
|
|
|
|
|
- })
|
|
|
|
|
- );
|
|
|
|
|
- } catch {
|
|
|
|
|
- // ignore
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function restoreSelectedListDraft() {
|
|
|
|
|
- try {
|
|
|
|
|
- const raw = sessionStorage.getItem(SELECTED_LIST_KEY);
|
|
|
|
|
- if (!raw) return;
|
|
|
|
|
- const draft = JSON.parse(raw);
|
|
|
|
|
- if (draft?.activeCategoryId != null) {
|
|
|
|
|
- activeCategoryId.value = draft.activeCategoryId;
|
|
|
|
|
- }
|
|
|
|
|
- if (typeof draft?.selectedUid === "number") {
|
|
|
|
|
- selectedUid = draft.selectedUid;
|
|
|
|
|
- }
|
|
|
|
|
- if (Array.isArray(draft?.list) && draft.list.length) {
|
|
|
|
|
- selectedList.value = props.multiple ? draft.list : draft.list.slice(0, 1);
|
|
|
|
|
- }
|
|
|
|
|
- } catch {
|
|
|
|
|
- // ignore
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const goSelectItemLocation = (item) => {
|
|
|
|
|
- if (!item?.id) {
|
|
|
|
|
- ElMessage.warning("请选择品种");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- sessionStorage.setItem(EDIT_UID_KEY, item.uid);
|
|
|
|
|
- sessionStorage.setItem("ENTRY_INFORMATION_STEP", "3");
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
- router.push({
|
|
|
|
|
- path: "/entry_select_location",
|
|
|
|
|
- query: {
|
|
|
|
|
- mapCenter: item.location || getDefaultPoint(),
|
|
|
|
|
- pointType: "plant",
|
|
|
|
|
- },
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-async function bootstrapVarietyPage() {
|
|
|
|
|
- initCategoryTabsFromSession();
|
|
|
|
|
- restoreSelectedListDraft();
|
|
|
|
|
- ensureDefaultForm();
|
|
|
|
|
- syncItemLocationFromSession();
|
|
|
|
|
- await Promise.all(
|
|
|
|
|
- categoryTabs.value.map((tab) => fetchVarietiesByCrop(tab.id))
|
|
|
|
|
- );
|
|
|
|
|
- await fetchInitialInteractOptionsWithoutZone();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- bootstrapVarietyPage();
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/** keep-alive 返回时只同步点位/地图,避免重复拉接口 */
|
|
|
|
|
-onActivated(() => {
|
|
|
|
|
- syncItemLocationFromSession();
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- itemPreviewMaps.forEach((map) => map.kmap?.map?.updateSize?.());
|
|
|
|
|
- });
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-onBeforeUnmount(() => {
|
|
|
|
|
- itemPreviewMaps.forEach((map) => destroyPreviewMap(map));
|
|
|
|
|
- itemPreviewMaps.clear();
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const getSelectedCategoriesFromSession = () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const raw = sessionStorage.getItem(CATEGORY_SESSION_KEY);
|
|
|
|
|
- if (!raw) return [];
|
|
|
|
|
- const list = JSON.parse(raw);
|
|
|
|
|
- if (!Array.isArray(list)) return [];
|
|
|
|
|
- if (!props.multiple) return list;
|
|
|
|
|
- return [...list].sort((a, b) => {
|
|
|
|
|
- const rank = (item) => {
|
|
|
|
|
- if (item?.firstCrop === "大田" || item?.majorKey === "field") return 0;
|
|
|
|
|
- if (item?.firstCrop === "果树" || item?.majorKey === "fruit") return 1;
|
|
|
|
|
- return 2;
|
|
|
|
|
- };
|
|
|
|
|
- return rank(a) - rank(b);
|
|
|
|
|
- });
|
|
|
|
|
- } catch {
|
|
|
|
|
- return [];
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-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,
|
|
|
|
|
- code: item.code || cached.code || "",
|
|
|
|
|
- firstCrop: item.firstCrop || cached.firstCrop || "",
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- return {
|
|
|
|
|
- id,
|
|
|
|
|
- name: item.name || "",
|
|
|
|
|
- code: item.code || "",
|
|
|
|
|
- firstCrop: item.firstCrop || "",
|
|
|
|
|
- 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 categoryTabs = ref([]);
|
|
|
|
|
-const activeCategoryId = ref(null);
|
|
|
|
|
-const varietyLoading = ref(false);
|
|
|
|
|
-const searchKeyword = ref("");
|
|
|
|
|
-const appliedKeyword = ref("");
|
|
|
|
|
-const gridExpanded = ref(false);
|
|
|
|
|
-const selectedList = ref([]);
|
|
|
|
|
-/** 无分区初始互动选项,业务侧自行对接 */
|
|
|
|
|
-const initialInteractOptions = ref(null);
|
|
|
|
|
-/** 无分区初始互动接口 loading(表单卡) */
|
|
|
|
|
-const interactLoading = ref(false);
|
|
|
|
|
-/** 进行中的品种请求,防止并发重复 */
|
|
|
|
|
-const varietyFetchPromises = new Map();
|
|
|
|
|
-/** 进行中的初始互动请求 key */
|
|
|
|
|
-let interactFetchKey = "";
|
|
|
|
|
-let interactFetchPromise = null;
|
|
|
|
|
-
|
|
|
|
|
-function readJson(key) {
|
|
|
|
|
- try {
|
|
|
|
|
- const raw = sessionStorage.getItem(key);
|
|
|
|
|
- return raw ? JSON.parse(raw) : null;
|
|
|
|
|
- } catch {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function writeJson(key, value) {
|
|
|
|
|
- sessionStorage.setItem(key, JSON.stringify(value));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function saveCropDetailFromApi(data) {
|
|
|
|
|
- if (data?.crop_id == null || data?.crop_id === "") return;
|
|
|
|
|
- const cache = readJson("ENTRY_CROP_DETAIL") || {};
|
|
|
|
|
- cache[String(data.crop_id)] = {
|
|
|
|
|
- crop_id: data.crop_id,
|
|
|
|
|
- crop_code: data.crop_code || "",
|
|
|
|
|
- crop_group: data.crop_group || "",
|
|
|
|
|
- crop_name: data.crop_name || "",
|
|
|
|
|
- crop_type: data.crop_type || "",
|
|
|
|
|
- };
|
|
|
|
|
- writeJson("ENTRY_CROP_DETAIL", cache);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/** options_interact.interact → 物候期下拉(展示 period_name,提交 period_code) */
|
|
|
|
|
-function mapOptionsInteractToPhenology(interact) {
|
|
|
|
|
- if (!interact) return [];
|
|
|
|
|
- const list = Array.isArray(interact)
|
|
|
|
|
- ? interact
|
|
|
|
|
- : Object.keys(interact)
|
|
|
|
|
- .sort((a, b) => Number(a) - Number(b))
|
|
|
|
|
- .map((key) => interact[key]);
|
|
|
|
|
-
|
|
|
|
|
- return list
|
|
|
|
|
- .map((item) => {
|
|
|
|
|
- if (!item || typeof item !== "object") return null;
|
|
|
|
|
- const code = item.period_code ?? item.code ?? item.id;
|
|
|
|
|
- const name = item.period_name ?? item.name ?? "";
|
|
|
|
|
- if (!name) return null;
|
|
|
|
|
- return {
|
|
|
|
|
- id: code != null && code !== "" ? code : name,
|
|
|
|
|
- name,
|
|
|
|
|
- code: code != null && code !== "" ? String(code) : "",
|
|
|
|
|
- url: item.url || "",
|
|
|
|
|
- };
|
|
|
|
|
- })
|
|
|
|
|
- .filter(Boolean);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/** 将无分区初始互动结果写入当前品类:起种问题文案 + 物候期选项 */
|
|
|
|
|
-function applyInitialInteractToCurrentCategory(data) {
|
|
|
|
|
- const tab = currentCategory.value;
|
|
|
|
|
- if (!tab || !data) return;
|
|
|
|
|
-
|
|
|
|
|
- const theme = data.date_interact?.interact_theme;
|
|
|
|
|
- if (theme) {
|
|
|
|
|
- tab.startingPointQuestion = theme;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const phenology = mapOptionsInteractToPhenology(data.options_interact?.interact);
|
|
|
|
|
- if (!phenology.length) return;
|
|
|
|
|
-
|
|
|
|
|
- tab.phenology = phenology;
|
|
|
|
|
- const form = getCurrentForm();
|
|
|
|
|
- if (!form?.phenologyId) return;
|
|
|
|
|
- const stillExists = phenology.some((opt) => String(opt.id) === String(form.phenologyId));
|
|
|
|
|
- if (!stillExists) form.phenologyId = "";
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/** 组装并请求无分区初始互动选项 */
|
|
|
|
|
-async function fetchInitialInteractOptionsWithoutZone() {
|
|
|
|
|
- const category = currentCategory.value;
|
|
|
|
|
- if (!category) return;
|
|
|
|
|
-
|
|
|
|
|
- const form = getCurrentForm();
|
|
|
|
|
- const resident = readJson(LOCATION_KEY);
|
|
|
|
|
- const cropDetail = readJson("ENTRY_CROP_DETAIL") || {};
|
|
|
|
|
- const detail = cropDetail[String(category.id)] || {};
|
|
|
|
|
-
|
|
|
|
|
- const params = {
|
|
|
|
|
- crop_big_type: category.firstCrop || detail.crop_group || "",
|
|
|
|
|
- crop_code: category.code || detail.crop_code || form?.cropCode || "",
|
|
|
|
|
- crop_maturing: "P0",
|
|
|
|
|
- location: form?.location || resident?.point || getDefaultPoint(),
|
|
|
|
|
- date: form?.startTime || new Date().toISOString().split("T")[0],
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const key = `${params.crop_code}|${params.location}|${params.date}`;
|
|
|
|
|
- if (interactFetchPromise && interactFetchKey === key) {
|
|
|
|
|
- return interactFetchPromise;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- interactFetchKey = key;
|
|
|
|
|
- interactLoading.value = true;
|
|
|
|
|
- interactFetchPromise = (async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const raw = await VE_API.questionnaire.getInitialInteractionWithoutZone(params);
|
|
|
|
|
- const res = Array.isArray(raw) ? raw[0] : raw;
|
|
|
|
|
- if (res?.code === 200) {
|
|
|
|
|
- initialInteractOptions.value = res.data ?? null;
|
|
|
|
|
- applyInitialInteractToCurrentCategory(res.data);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("fetchInitialInteractOptionsWithoutZone failed", error);
|
|
|
|
|
- } finally {
|
|
|
|
|
- if (interactFetchKey === key) {
|
|
|
|
|
- interactFetchPromise = null;
|
|
|
|
|
- interactFetchKey = "";
|
|
|
|
|
- interactLoading.value = false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })();
|
|
|
|
|
-
|
|
|
|
|
- return interactFetchPromise;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-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 cacheKey = String(cropId);
|
|
|
|
|
- if (!force && varietyFetchPromises.has(cacheKey)) {
|
|
|
|
|
- return varietyFetchPromises.get(cacheKey);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const showLoading = String(cropId) === String(activeCategoryId.value);
|
|
|
|
|
- if (showLoading) varietyLoading.value = true;
|
|
|
|
|
-
|
|
|
|
|
- const request = (async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await VE_API.entry.getVarietiesByCrop({
|
|
|
|
|
- crop_id: cropId,
|
|
|
|
|
- });
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- const data = res.data;
|
|
|
|
|
- if (data.crop_code) tab.code = data.crop_code;
|
|
|
|
|
- if (data.crop_group) tab.firstCrop = tab.firstCrop || data.crop_group;
|
|
|
|
|
- tab.varieties = (data.varieties || [])
|
|
|
|
|
- .map((item) => {
|
|
|
|
|
- if (typeof item === "string") {
|
|
|
|
|
- return { id: item, name: item };
|
|
|
|
|
- }
|
|
|
|
|
- const name = item?.name || "";
|
|
|
|
|
- return {
|
|
|
|
|
- id: item?.id ?? name,
|
|
|
|
|
- name,
|
|
|
|
|
- };
|
|
|
|
|
- })
|
|
|
|
|
- .filter((item) => item.name);
|
|
|
|
|
- // 物候期 / 起种文案仅由 getInitialInteractionWithoutZone 写入
|
|
|
|
|
- tab.loaded = true;
|
|
|
|
|
-
|
|
|
|
|
- // 优先用已缓存的 crop_code,避免重复打 farmer_crop_detail
|
|
|
|
|
- if (!tab.code) {
|
|
|
|
|
- const cached = readJson("ENTRY_CROP_DETAIL") || {};
|
|
|
|
|
- const detail = cached[cacheKey];
|
|
|
|
|
- if (detail?.crop_code) {
|
|
|
|
|
- tab.code = detail.crop_code;
|
|
|
|
|
- tab.firstCrop = tab.firstCrop || detail.crop_group || "";
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (!tab.code) {
|
|
|
|
|
- try {
|
|
|
|
|
- const detailRes = await VE_API.entry.getMachines({ crop_id: cropId });
|
|
|
|
|
- const detail = detailRes?.data || {};
|
|
|
|
|
- if (detailRes?.code === 200 && detail.crop_code) {
|
|
|
|
|
- tab.code = detail.crop_code;
|
|
|
|
|
- tab.firstCrop = tab.firstCrop || detail.crop_group || "";
|
|
|
|
|
- saveCropDetailFromApi(detail);
|
|
|
|
|
- }
|
|
|
|
|
- } catch {
|
|
|
|
|
- // ignore
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- tab.varieties = [];
|
|
|
|
|
- if (showLoading) {
|
|
|
|
|
- ElMessage.error(res.msg || "获取品种列表失败");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } catch {
|
|
|
|
|
- tab.varieties = [];
|
|
|
|
|
- if (showLoading) {
|
|
|
|
|
- ElMessage.error("获取品种列表失败,请稍后再试");
|
|
|
|
|
- }
|
|
|
|
|
- } finally {
|
|
|
|
|
- if (showLoading) varietyLoading.value = false;
|
|
|
|
|
- varietyFetchPromises.delete(cacheKey);
|
|
|
|
|
- }
|
|
|
|
|
- })();
|
|
|
|
|
-
|
|
|
|
|
- varietyFetchPromises.set(cacheKey, request);
|
|
|
|
|
- return request;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-watch(
|
|
|
|
|
- selectedList,
|
|
|
|
|
- () => {
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
- },
|
|
|
|
|
- { deep: true }
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
-const currentCategory = computed(
|
|
|
|
|
- () =>
|
|
|
|
|
- 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 currentVarieties = computed(() => currentCategory.value?.varieties || []);
|
|
|
|
|
-
|
|
|
|
|
-const getVarietiesByCategory = (categoryId) => {
|
|
|
|
|
- const tab = categoryTabs.value.find((item) => String(item.id) === String(categoryId));
|
|
|
|
|
- return tab?.varieties || [];
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const filteredVarieties = computed(() => {
|
|
|
|
|
- const list = currentVarieties.value;
|
|
|
|
|
- const kw = appliedKeyword.value.trim();
|
|
|
|
|
- if (!kw) return list;
|
|
|
|
|
- return list.filter((item) => item.name.includes(kw));
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const visibleVarieties = computed(() => {
|
|
|
|
|
- if (gridExpanded.value) return filteredVarieties.value;
|
|
|
|
|
- return filteredVarieties.value.slice(0, DEFAULT_VISIBLE_COUNT);
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/** 只能选今天及之前的日期 */
|
|
|
|
|
-const disableFutureDates = (date) => {
|
|
|
|
|
- const today = new Date();
|
|
|
|
|
- today.setHours(0, 0, 0, 0);
|
|
|
|
|
- return date.getTime() > today.getTime();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const createEmptyForm = (category) => ({
|
|
|
|
|
- uid: `sel-${++selectedUid}`,
|
|
|
|
|
- id: "",
|
|
|
|
|
- name: "",
|
|
|
|
|
- categoryId: category?.id ?? null,
|
|
|
|
|
- categoryName: category?.name ?? "",
|
|
|
|
|
- cropCode: category?.code || "",
|
|
|
|
|
- firstCrop: category?.firstCrop || "",
|
|
|
|
|
- phenologyId: "",
|
|
|
|
|
- startTime: "",
|
|
|
|
|
- area: "",
|
|
|
|
|
- location: "",
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/** 始终保留可编辑表单卡:农户一张;农服按品类各一张 */
|
|
|
|
|
-const ensureDefaultForm = () => {
|
|
|
|
|
- const category = currentCategory.value;
|
|
|
|
|
- if (!category) {
|
|
|
|
|
- if (!props.multiple) selectedList.value = [];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (props.multiple) {
|
|
|
|
|
- const categoryIds = new Set(categoryTabs.value.map((tab) => String(tab.id)));
|
|
|
|
|
- selectedList.value = selectedList.value.filter((item) =>
|
|
|
|
|
- categoryIds.has(String(item.categoryId))
|
|
|
|
|
- );
|
|
|
|
|
- const exists = selectedList.value.some(
|
|
|
|
|
- (item) => String(item.categoryId) === String(category.id)
|
|
|
|
|
- );
|
|
|
|
|
- if (!exists) {
|
|
|
|
|
- selectedList.value.push(createEmptyForm(category));
|
|
|
|
|
- }
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!selectedList.value.length) {
|
|
|
|
|
- selectedList.value = [createEmptyForm(category)];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const item = selectedList.value[0];
|
|
|
|
|
- if (String(item.categoryId) !== String(category.id)) {
|
|
|
|
|
- selectedList.value = [createEmptyForm(category)];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- item.categoryId = category.id;
|
|
|
|
|
- item.categoryName = category.name;
|
|
|
|
|
- item.cropCode = category.code || item.cropCode || "";
|
|
|
|
|
- item.firstCrop = category.firstCrop || item.firstCrop || "";
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getCurrentForm = () => {
|
|
|
|
|
- if (props.multiple) {
|
|
|
|
|
- return (
|
|
|
|
|
- selectedList.value.find(
|
|
|
|
|
- (item) => String(item.categoryId) === String(activeCategoryId.value)
|
|
|
|
|
- ) || null
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- return selectedList.value[0] || null;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const currentSelected = computed(() => getCurrentForm());
|
|
|
|
|
-const hasVariety = computed(() => !!currentSelected.value?.id);
|
|
|
|
|
-const currentPhenologyOptions = computed(() =>
|
|
|
|
|
- getPhenologyOptions(currentSelected.value?.categoryId || activeCategoryId.value)
|
|
|
|
|
-);
|
|
|
|
|
-const currentStartingPointQuestion = computed(() =>
|
|
|
|
|
- getStartingPointQuestion(currentSelected.value?.categoryId || activeCategoryId.value)
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
-watch(activeCategoryId, async (id, prevId) => {
|
|
|
|
|
- searchKeyword.value = "";
|
|
|
|
|
- appliedKeyword.value = "";
|
|
|
|
|
- gridExpanded.value = false;
|
|
|
|
|
- ensureDefaultForm();
|
|
|
|
|
- // 初始化赋值由 bootstrap 拉取,避免与 onMounted 重复请求
|
|
|
|
|
- if (id == null || prevId == null) return;
|
|
|
|
|
- if (String(id) === String(prevId)) return;
|
|
|
|
|
- await fetchVarietiesByCrop(id);
|
|
|
|
|
- fetchInitialInteractOptionsWithoutZone();
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const isSelected = (id) =>
|
|
|
|
|
- !!id && String(getCurrentForm()?.id) === String(id);
|
|
|
|
|
-
|
|
|
|
|
-const handleSearch = () => {
|
|
|
|
|
- appliedKeyword.value = searchKeyword.value;
|
|
|
|
|
- gridExpanded.value = false;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const applyVarietyToForm = (variety) => {
|
|
|
|
|
- ensureDefaultForm();
|
|
|
|
|
- const item = getCurrentForm();
|
|
|
|
|
- if (!item || !variety || !currentCategory.value) return;
|
|
|
|
|
- item.id = variety.id;
|
|
|
|
|
- item.name = variety.name;
|
|
|
|
|
- item.categoryId = currentCategory.value.id;
|
|
|
|
|
- item.categoryName = currentCategory.value.name;
|
|
|
|
|
- item.cropCode = currentCategory.value.code || item.cropCode || "";
|
|
|
|
|
- item.firstCrop = currentCategory.value.firstCrop || item.firstCrop || "";
|
|
|
|
|
- item.phenologyId = "";
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handleFormVarietyChange = (varietyId) => {
|
|
|
|
|
- if (!varietyId) {
|
|
|
|
|
- clearVariety();
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const found = currentVarieties.value.find((item) => String(item.id) === String(varietyId));
|
|
|
|
|
- if (!found) return;
|
|
|
|
|
- applyVarietyToForm(found);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const clearVariety = () => {
|
|
|
|
|
- ensureDefaultForm();
|
|
|
|
|
- const item = getCurrentForm();
|
|
|
|
|
- if (!item) return;
|
|
|
|
|
- item.id = "";
|
|
|
|
|
- item.name = "";
|
|
|
|
|
- item.phenologyId = "";
|
|
|
|
|
- item.startTime = "";
|
|
|
|
|
- item.area = "";
|
|
|
|
|
- item.location = "";
|
|
|
|
|
- nextTick(() => updateItemMap(item.uid, getDefaultPoint(), false));
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const onFormBodyClick = () => {
|
|
|
|
|
- if (!hasVariety.value) {
|
|
|
|
|
- ElMessage.warning("请选择品种");
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const toggleVariety = (variety) => {
|
|
|
|
|
- const current = getCurrentForm();
|
|
|
|
|
- if (current && String(current.id) === String(variety.id)) {
|
|
|
|
|
- clearVariety();
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- applyVarietyToForm(variety);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handlePrev = () => {
|
|
|
|
|
- const idx = categoryTabs.value.findIndex(
|
|
|
|
|
- (tab) => String(tab.id) === String(activeCategoryId.value)
|
|
|
|
|
- );
|
|
|
|
|
- if (idx > 0) {
|
|
|
|
|
- activeCategoryId.value = categoryTabs.value[idx - 1].id;
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- emit("prev");
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getBaForm = () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const raw = sessionStorage.getItem("ENTRY_BA_FORM");
|
|
|
|
|
- return raw ? JSON.parse(raw) : null;
|
|
|
|
|
- } catch {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const validateItem = (item, categoryName = "") => {
|
|
|
|
|
- const label = item?.name || categoryName;
|
|
|
|
|
- if (!item?.id) {
|
|
|
|
|
- ElMessage.warning(categoryName ? `请选择「${categoryName}」的品种` : "请选择一个品种");
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (!item.phenologyId) {
|
|
|
|
|
- ElMessage.warning(`请选择「${label}」的当下物候期`);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (!item.startTime) {
|
|
|
|
|
- ElMessage.warning(`请填写「${label}」的起种点时间`);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (item.area === "" || item.area == null || Number(item.area) <= 0) {
|
|
|
|
|
- ElMessage.warning(`请填写「${label}」的种植亩数`);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (!item.location) {
|
|
|
|
|
- ElMessage.warning(`请选择「${label}」的种植点位`);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const validateSubmitData = () => {
|
|
|
|
|
- if (!categoryTabs.value.length) {
|
|
|
|
|
- ElMessage.warning("请先选择种植品类");
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (props.multiple) {
|
|
|
|
|
- for (const category of categoryTabs.value) {
|
|
|
|
|
- const item = selectedList.value.find(
|
|
|
|
|
- (row) => String(row.categoryId) === String(category.id)
|
|
|
|
|
- );
|
|
|
|
|
- if (!validateItem(item, category.name)) return false;
|
|
|
|
|
- }
|
|
|
|
|
- } else if (!validateItem(selectedList.value[0])) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- const baForm = getBaForm();
|
|
|
|
|
- if (!baForm?.name || !baForm?.phone) {
|
|
|
|
|
- ElMessage.warning("请先完善个人信息");
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handleConfirm = () => {
|
|
|
|
|
- const currentCategoryName = currentCategory.value?.name || "";
|
|
|
|
|
- if (props.multiple && !validateItem(getCurrentForm(), currentCategoryName)) return;
|
|
|
|
|
- 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;
|
|
|
|
|
- }
|
|
|
|
|
- if (!validateSubmitData()) return;
|
|
|
|
|
- selectedList.value.forEach((row) => {
|
|
|
|
|
- if (!row.id) return;
|
|
|
|
|
- const options = getPhenologyOptions(row.categoryId);
|
|
|
|
|
- const found = options.find((opt) => String(opt.id) === String(row.phenologyId));
|
|
|
|
|
- row.phenophase = found?.name || String(row.phenologyId || "");
|
|
|
|
|
- row.phenophaseCode = found?.code || String(row.phenologyId || "");
|
|
|
|
|
- });
|
|
|
|
|
- saveSelectedListDraft();
|
|
|
|
|
- emit("next");
|
|
|
|
|
-};
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style lang="scss" scoped>
|
|
|
|
|
-.select-variety {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-height: 0;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
-
|
|
|
|
|
- &__content {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-height: 0;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
- -webkit-overflow-scrolling: touch;
|
|
|
|
|
- padding: 20px 10px 100px;
|
|
|
|
|
-
|
|
|
|
|
- .page-header {
|
|
|
|
|
- margin-bottom: 20px;
|
|
|
|
|
-
|
|
|
|
|
- .page-title {
|
|
|
|
|
- font-size: 26px;
|
|
|
|
|
- color: #005599;
|
|
|
|
|
- line-height: 30px;
|
|
|
|
|
- font-family: "PangMenZhengDao";
|
|
|
|
|
- .page-title-tip {
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .page-subtitle {
|
|
|
|
|
- color: rgba(46, 46, 46, 0.4);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .category-title {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- gap: 1px;
|
|
|
|
|
- height: 24px;
|
|
|
|
|
- margin-bottom: 9px;
|
|
|
|
|
- padding: 0 6px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
-
|
|
|
|
|
- &__line {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- height: 1px;
|
|
|
|
|
- background: linear-gradient(90deg, rgba(118, 118, 118, 0) 0%, rgba(118, 118, 118, 0.4) 100%);
|
|
|
|
|
- &.right-line {
|
|
|
|
|
- background: linear-gradient(270deg, rgba(118, 118, 118, 0) 0%, rgba(118, 118, 118, 0.4) 100%);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__diamond {
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- width: 6px;
|
|
|
|
|
- height: 6px;
|
|
|
|
|
- background: rgba(61, 61, 61, 0.2);
|
|
|
|
|
- transform: rotate(45deg);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__text {
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- padding: 0 10px;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- color: #3D3D3D;
|
|
|
|
|
- line-height: 24px;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .variety-card {
|
|
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
- background: rgba(255, 255, 255, 0.5);
|
|
|
|
|
- border: 1px solid #fff;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- padding: 10px 6px;
|
|
|
|
|
-
|
|
|
|
|
- .search-bar {
|
|
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-input__wrapper) {
|
|
|
|
|
- height: 36px;
|
|
|
|
|
- padding: 0 10px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- box-shadow: none;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-input__inner) {
|
|
|
|
|
- color: #1a1a1a;
|
|
|
|
|
- line-height: 20px;
|
|
|
|
|
-
|
|
|
|
|
- &::placeholder {
|
|
|
|
|
- color: rgba(29, 33, 41, 0.2);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .search-icon {
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- color: rgba(0, 0, 0, 0.2);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .search-btn {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .variety-grid {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
-
|
|
|
|
|
- .variety-item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- padding: 7px 0;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border: 1px solid transparent;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- color: #565656;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
-
|
|
|
|
|
- &.selected {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- background: rgba(33, 153, 248, 0.1);
|
|
|
|
|
- border-color: #2199f8;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .expand-trigger {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
- 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 {
|
|
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- padding: 12px;
|
|
|
|
|
-
|
|
|
|
|
- &__header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- border-bottom: .2px solid rgba(0, 0, 0, 0.05);
|
|
|
|
|
- padding-bottom: 10px;
|
|
|
|
|
-
|
|
|
|
|
- .title-icon {
|
|
|
|
|
- width: 15px;
|
|
|
|
|
- object-fit: contain;
|
|
|
|
|
- margin-right: 5px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .variety-select {
|
|
|
|
|
- flex: 0 0 auto;
|
|
|
|
|
- width: auto !important;
|
|
|
|
|
- max-width: calc(100% - 50px);
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__wrapper) {
|
|
|
|
|
- box-shadow: none;
|
|
|
|
|
- padding: 0;
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- width: auto !important;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__selection) {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- width: auto;
|
|
|
|
|
- flex: 0 0 auto;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__placeholder) {
|
|
|
|
|
- position: static;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- color: #000;
|
|
|
|
|
- transform: none;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__caret) {
|
|
|
|
|
- color: #000;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .delete-btn {
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
- padding-left: 12px;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #f53f3f;
|
|
|
|
|
- line-height: 20px;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--default {
|
|
|
|
|
- .form-card__header {
|
|
|
|
|
- .variety-select {
|
|
|
|
|
- :deep(.el-select__selected-item),
|
|
|
|
|
- :deep(.el-select__placeholder) {
|
|
|
|
|
- color: #1d2129;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__placeholder.is-transparent) {
|
|
|
|
|
- color: rgba(34, 34, 34, 0.3);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__caret) {
|
|
|
|
|
- color: rgba(34, 34, 34, 0.3);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .form-card__body--locked {
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-
|
|
|
|
|
- .form-row {
|
|
|
|
|
- pointer-events: none;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .form-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
-
|
|
|
|
|
- .form-label {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #1D2129;
|
|
|
|
|
-
|
|
|
|
|
- &.muted {
|
|
|
|
|
- color: rgba(29, 33, 41, 0.2);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .form-input {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-input__wrapper) {
|
|
|
|
|
- box-shadow: none;
|
|
|
|
|
- background: transparent;
|
|
|
|
|
- padding: 0;
|
|
|
|
|
- justify-content: flex-end;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-input__inner) {
|
|
|
|
|
- text-align: right;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #1a1a1a;
|
|
|
|
|
-
|
|
|
|
|
- &::placeholder {
|
|
|
|
|
- color: rgba(0, 0, 0, 0.25);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .form-select {
|
|
|
|
|
- flex: 0 0 auto;
|
|
|
|
|
- width: auto;
|
|
|
|
|
- max-width: 160px;
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__wrapper) {
|
|
|
|
|
- box-shadow: none;
|
|
|
|
|
- background: transparent;
|
|
|
|
|
- padding: 0;
|
|
|
|
|
- justify-content: flex-end;
|
|
|
|
|
- min-height: auto;
|
|
|
|
|
- width: auto;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__selection) {
|
|
|
|
|
- justify-content: flex-end;
|
|
|
|
|
- flex: 0 1 auto;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__placeholder),
|
|
|
|
|
- :deep(.el-select__selected-item) {
|
|
|
|
|
- text-align: right;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #1a1a1a;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-select__placeholder) {
|
|
|
|
|
- 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);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .map {
|
|
|
|
|
- width: 98px;
|
|
|
|
|
- height: 60px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- background: linear-gradient(135deg, #e8eef3 0%, #d4dde6 100%);
|
|
|
|
|
- pointer-events: none;
|
|
|
|
|
-
|
|
|
|
|
- &--clickable {
|
|
|
|
|
- pointer-events: auto;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .custom-bottom-fixed-btns {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: baseline;
|
|
|
|
|
- padding: 10px 12px 0 12px;
|
|
|
|
|
- height: 80px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- box-shadow: 2px 2px 5px 0 rgba(0, 0, 0, 0.4);
|
|
|
|
|
-
|
|
|
|
|
- .bottom-btn {
|
|
|
|
|
- padding: 0 30px;
|
|
|
|
|
- height: 40px;
|
|
|
|
|
- line-height: 40px;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- border-radius: 25px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .secondary-btn {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border: 1px solid #2199f8;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .primary-btn {
|
|
|
|
|
- background: #2199f8;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
-
|
|
|
|
|
- &.disabled {
|
|
|
|
|
- opacity: 0.6;
|
|
|
|
|
- pointer-events: none;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|
|
|
-
|
|
|
|
|
-<style lang="scss">
|
|
|
|
|
-.variety-select-popper {
|
|
|
|
|
- width: auto !important;
|
|
|
|
|
- min-width: 100px !important;
|
|
|
|
|
-
|
|
|
|
|
- .el-select-dropdown__item.is-variety-selected {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- background: transparent;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
-
|
|
|
|
|
- &.is-hovering,
|
|
|
|
|
- &:hover {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- background: transparent;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|