| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- <template>
- <div class="agri-file" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
- <!-- 天气遮罩 -->
- <div class="weather-mask" v-show="isExpanded" @click="handleMaskClick"></div>
- <!-- 天气 -->
- <weather-info ref="weatherInfoRef" :hasWeather="false" from="monitor" class="weather-info"
- @weatherExpanded="weatherExpanded" @changeGarden="changeGarden" @changeGardenTab="changeGardenTab"
- :isGarden="true" :gardenId="defaultGardenId">
- <template #types-content>
- <div class="type-tabs">
- <div class="type-item" @click="changeType(farmVarietyName)"
- :class="{ 'type-item-active': activeType === farmVarietyName }">{{ farmVarietyName }}</div>
- <!-- <div class="type-item" @click="changeType('水稻')" :class="{ 'type-item-active': activeType === '水稻' }">{{ $t('水稻') }}</div> -->
- <!-- <div class="type-item" @click="changeType('柑橘')" :class="{ 'type-item-active': activeType === '柑橘' }">{{ $t('柑橘') }}</div> -->
- <!-- <div class="type-item" @click="changeType('小麦')" :class="{ 'type-item-active': activeType === '小麦' }">{{ $t('小麦') }}</div> -->
- </div>
- </template>
- </weather-info>
- <!-- 农场列表 -->
- <div v-show="activeGardenTab === 'list'">
- <garden-list ref="gardenListRef" :garden-id="selectedGardenId" @loaded="handleGardenLoaded"
- @selectGarden="handleGardenSelected" />
- </div>
- <div class="file-content" v-show="activeGardenTab === 'current'">
- <!-- 地图图例:分类标识 + 长势等级 -->
- <div class="map-legend">
- <div
- v-for="item in mapLegendItems"
- :key="item.key"
- class="map-legend__item"
- >
- <span class="map-legend__pill" :class="item.pillClass"></span>
- <span class="map-legend__text">{{ item.label }}</span>
- </div>
- </div>
- <!-- <div class="map-legend-box">
- <div class="map-legend-box__labels">
- <span
- v-for="(label, index) in growthScaleLabels"
- :key="index"
- >{{ label }}</span>
- </div>
- <div class="map-legend-box__bar"></div>
- </div> -->
- <div class="map-tool-bar">
- <div
- v-for="(item, index) in mapToolItems"
- :key="item.key"
- class="map-tool-bar__item"
- :class="{ 'map-tool-bar__item--active': activeMapTool === index }"
- @click="changeMapTool(index)"
- >
- <img
- class="map-tool-bar__icon"
- :src="require(`@/assets/img/map/tool-${index + 1}.png`)"
- :alt="item.label"
- />
- <span class="map-tool-bar__label">{{ item.label }}</span>
- </div>
- </div>
- <div class="map-container" ref="mapContainer"></div>
- <file-float
- v-model:active-tab="activeRecordTab"
- v-model:active-sub-tab="activeRecordSubTab"
- :farm-record-data="farmRecordData"
- :loading="farmRecordLoading"
- :crop-variety="farmVarietyName"
- />
- </div>
- </div>
- </template>
- <script setup>
- import { computed, nextTick, onActivated, ref, watch } from "vue";
- import { useRoute } from "vue-router";
- import { useStore } from "vuex";
- import weatherInfo from "@/components/weatherInfo.vue";
- import gardenList from "@/components/gardenList.vue";
- import fileFloat from "./components/fileFloat.vue";
- import FileMap, { RECORD_TAB_KEYS, recordsToCenterPoint } from "./fileMap.js";
- import { useI18n } from "@/i18n";
- const { t, locale } = useI18n();
- const store = useStore();
- const route = useRoute();
- const tabBarHeight = computed(() => store.state.home.tabBarHeight);
- const isExpanded = ref(false);
- const weatherInfoRef = ref(null);
- const defaultGardenId = ref(null);
- const selectedGardenId = ref(null);
- const gardenListRef = ref(null);
- const activeGardenTab = ref("current");
- const activeType = ref(null);
- const MAP_LEGEND_CONFIG = [
- { key: "zone", labelKey: "agriFile.legendZone", type: "zone" },
- { key: "growth", labelKey: "agriFile.legendGrowth", type: "growth" },
- { key: "pest", labelKey: "agriFile.legendPest", type: "pest" },
- ];
- const GROWTH_SCALE_LABEL_KEYS = [
- "agriFile.scaleNormal",
- "agriFile.scaleGood",
- "agriFile.scaleExcellent",
- ];
- const mapLegendItems = computed(() =>
- MAP_LEGEND_CONFIG.map(({ key, labelKey, type }) => ({
- key,
- label: t(labelKey),
- pillClass: `map-legend__pill--${type}`,
- }))
- );
- const growthScaleLabels = computed(() => GROWTH_SCALE_LABEL_KEYS.map((key) => t(key)));
- const MAP_TOOL_ITEMS = [
- { key: "all", label: "底图" },
- { key: "habitat", label: "植被" },
- { key: "light", label: "水体" },
- { key: "water", label: "降水" },
- // { key: "fengshui", label: "风水" },
- // { key: "soil", label: "土壤" },
- ];
- const mapToolItems = MAP_TOOL_ITEMS;
- const activeMapTool = ref(0);
- const changeMapTool = (index) => {
- activeMapTool.value = index;
- };
- const fileMap = new FileMap();
- const farmRecordData = ref({});
- const farmRecordLoading = ref(false);
- const activeRecordTab = ref(0);
- const activeRecordSubTab = ref(0);
- const getActiveTabKey = () =>
- RECORD_TAB_KEYS[activeRecordSubTab.value] || RECORD_TAB_KEYS[0];
- const getActiveRecords = () => farmRecordData.value[getActiveTabKey()] || [];
- const syncFarmRecordMap = () => {
- if (activeGardenTab.value !== "current") return;
- nextTick(() => {
- fileMap.setRecordPolygons(getActiveRecords(), getActiveTabKey());
- });
- };
- const initAgriFileMap = async () => {
- await nextTick();
- if (!mapContainer.value) return;
- fileMap.initMap(recordsToCenterPoint(getActiveRecords()), mapContainer.value);
- };
- const getFarmRecord = async () => {
- farmRecordLoading.value = true;
- try {
- const farmData = JSON.parse(localStorage.getItem("selectedFarmData"));
- const res = await VE_API.monitor.getFarmRecord({
- farm_id: selectedGardenId.value,
- variety_code: farmData.farm_variety,
- });
- if (res.code === 200) {
- farmRecordData.value = {
- phenology: res.data?.phenology ?? [],
- abnormal: res.data?.abnormal ?? [],
- farming: res.data?.farming ?? [],
- };
- syncFarmRecordMap();
- }
- } finally {
- farmRecordLoading.value = false;
- }
- };
- watch(activeRecordSubTab, syncFarmRecordMap);
- watch(activeGardenTab, syncFarmRecordMap);
- const weatherExpanded = (isExpandedValue) => {
- isExpanded.value = isExpandedValue;
- };
- const handleMaskClick = () => {
- if (weatherInfoRef.value?.toggleExpand) {
- weatherInfoRef.value.toggleExpand();
- }
- };
- const changeGardenTab = (tab) => {
- activeGardenTab.value = tab;
- };
- const changeType = (type) => {
- activeType.value = type;
- };
- const handleGardenLoaded = ({ hasFarm }) => {
- weatherInfoRef.value?.setGardenLoaded?.(hasFarm);
- };
- const handleGardenSelected = (garden) => {
- selectedGardenId.value = garden?.id ?? null;
- weatherInfoRef.value?.setSelectedGarden?.(garden);
- };
- const farmVarietyName = ref(null);
- const changeGarden = (data) => {
- if (!data?.id) return;
- store.commit("home/SET_GARDEN_ID", data.id);
- getFarmRecord();
- activeType.value = data.variety_name;
- farmVarietyName.value = data.variety_name;
- };
- const mapContainer = ref(null);
- onActivated(async () => {
- if (route.query?.farmId) {
- defaultGardenId.value = route.query.farmId;
- await getFarmRecord();
- }
- const savedFarmId = localStorage.getItem("selectedFarmId");
- selectedGardenId.value = savedFarmId ? Number(savedFarmId) : null;
- gardenListRef.value?.refreshFarmList?.();
- await initAgriFileMap();
- });
- </script>
- <style lang="scss" scoped>
- .agri-file {
- width: 100%;
- height: 100%;
- background: #F5F7FB;
- box-sizing: border-box;
- .weather-mask {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.52);
- z-index: 11;
- }
- .weather-info {
- width: calc(100% - 20px);
- position: absolute;
- z-index: 12;
- left: 10px;
- top: 12px;
- }
- .file-content {
- position: relative;
- height: 100%;
- box-sizing: border-box;
- $map-legend-panel-bg: rgba(0, 0, 0, 0.46);
- $map-legend-text-color: #ffffff;
- $map-legend-text-size: 12px;
- $map-legend-scale-gradient: linear-gradient(
- 90deg,
- #007aff 0%,
- #00d4aa 25%,
- #ffe600 50%,
- #ff9500 75%,
- #ff3b30 100%
- );
- @mixin map-legend-panel-base {
- position: absolute;
- z-index: 15;
- background: $map-legend-panel-bg;
- backdrop-filter: blur(4px);
- box-sizing: border-box;
- }
- .map-legend {
- @include map-legend-panel-base;
- top: 110px;
- right: 12px;
- display: flex;
- align-items: center;
- justify-content: space-around;
- gap: 10px;
- padding: 8px 10px;
- border-radius: 4px;
- &__item {
- display: flex;
- align-items: center;
- gap: 5px;
- }
- &__pill {
- width: 16px;
- height: 5px;
- border-radius: 10px;
- &--zone {
- background: #1c9e80;
- }
- &--growth {
- background: #ff953d;
- }
- &--pest {
- background: #e03131;
- }
- }
- &__text {
- font-size: $map-legend-text-size;
- color: $map-legend-text-color;
- }
- }
- .map-legend-box {
- @include map-legend-panel-base;
- top: 150px;
- right: 10px;
- width: 222px;
- padding: 5px 10px;
- border-radius: 5px;
- &__labels {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 8px;
- font-size: $map-legend-text-size;
- color: $map-legend-text-color;
- }
- &__bar {
- height: 8px;
- border-radius: 4px;
- background: $map-legend-scale-gradient;
- }
- }
- .map-tool-bar {
- position: absolute;
- left: 12px;
- top: 50%;
- transform: translateY(-78%);
- z-index: 15;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 10px 6px;
- background: #ffffff;
- border-radius: 7px;
- box-shadow: 0px 2.3px 2.3px 0px #0000001A;
- box-sizing: border-box;
- &__item {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 10px 5px;
- color: #C1C1C1;
- .map-tool-bar__icon {
- width: 18px;
- height: 16px;
- margin-bottom: 3px;
- filter: grayscale(1);
- }
- &--active {
- color: #2199f8;
- .map-tool-bar__icon {
- filter: grayscale(0);
- }
- }
- }
- &__label {
- font-size: 12px;
- }
- }
- .map-container {
- width: 100%;
- height: 100%;
- }
- }
- .type-tabs {
- width: 100%;
- background: #FFF;
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 8px;
- padding: 8px;
- .type-item {
- height: 28px;
- line-height: 28px;
- text-align: center;
- padding: 0 6px;
- min-width: 78px;
- color: #9A9A9A;
- background: #EFEFEF;
- box-sizing: border-box;
- border-radius: 2px;
- &.type-item-active {
- background: #2199F8;
- color: #fff;
- }
- }
- }
- }
- </style>
|