| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- <template>
- <custom-header v-if="isHeaderShow" name="农场详情"></custom-header>
- <div class="monitor-index" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
- <!-- 天气遮罩 -->
- <div class="weather-mask" v-show="isExpanded" @click="handleMaskClick"></div>
- <!-- 天气 -->
- <weather-info ref="weatherInfoRef" from="agri_record" class="weather-info" @weatherExpanded="weatherExpanded"
- @changeGarden="changeGarden" @changeGardenTab="changeGardenTab" :isGarden="true"
- :gardenId="defaultGardenId"></weather-info>
- <!-- 农场列表 -->
- <div v-show="activeGardenTab === 'list'">
- <garden-list ref="gardenListRef" :garden-id="selectedGardenId" @loaded="handleGardenLoaded"
- @selectGarden="handleGardenSelected" />
- </div>
- <!-- 作物档案:外层可滚动,滚动位置单独缓存以便路由/Tab 返回后恢复 -->
- <div ref="archivesScrollAreaRef" class="archives-time-line" v-show="activeGardenTab === 'current'">
- <div class="trend-monitor-list">
- <div class="trend-monitor-card" @click="handleTrendMonitorCardClick(item)"
- v-for="(item, index) in trendMonitorMockList" :key="index">
- <div class="card-header">
- <div class="header-left">
- <span class="title">{{ item?.first_work?.work_name }}</span>
- <span class="level-tag">二级</span>
- </div>
- <div class="status-tag" v-if="item?.risk_level">待记录</div>
- </div>
- <div class="card-row">
- <div class="reason-text">{{ item?.first_work?.work_reason_short }}</div>
- <div class="question-tag">{{ item?.first_work?.interaction_issue }}</div>
- </div>
- <!-- <div class="record-list">
- <div class="record-item" v-for="(record, recordIndex) in item.records" :key="recordIndex">
- {{ record }}
- </div>
- </div> -->
- </div>
- </div>
- <div class="archives-time-line-content">
- <archives-farm-time-line :farmId="gardenId"></archives-farm-time-line>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import customHeader from "@/components/customHeader.vue";
- import { ref, computed, onActivated, onDeactivated, watch, nextTick } from "vue";
- import { useStore } from "vuex";
- import weatherInfo from "@/components/weatherInfo.vue";
- import { useRoute, useRouter, onBeforeRouteLeave } from "vue-router";
- import ArchivesFarmTimeLine from "@/components/pageComponents/ArchivesFarmTimeLine.vue";
- import gardenList from "@/components/gardenList.vue";
- const router = useRouter();
- const route = useRoute();
- const archivesScrollAreaRef = ref(null);
- /** 从趋势卡跳转详情时已主动保存滚动;避免 leave/deactivate 阶段读到异常 scrollTop 覆盖 sessionStorage */
- const blockArchivesScrollSaveOnce = ref(false);
- const getArchivesOuterScrollKey = () =>
- `agriRecordArchivesOuterScroll:${gardenId?.value ?? "none"}:${route.path}`;
- const saveArchivesOuterScrollTop = () => {
- if (!archivesScrollAreaRef.value || activeGardenTab.value !== "current") return;
- const scrollTop = archivesScrollAreaRef.value.scrollTop || 0;
- sessionStorage.setItem(getArchivesOuterScrollKey(), String(scrollTop));
- };
- const restoreArchivesOuterScrollTop = () => {
- if (!archivesScrollAreaRef.value || activeGardenTab.value !== "current") return false;
- const raw = sessionStorage.getItem(getArchivesOuterScrollKey());
- if (raw == null) return false;
- const scrollTop = Number(raw);
- if (Number.isNaN(scrollTop)) return false;
- const el = archivesScrollAreaRef.value;
- const maxScrollTop = Math.max(0, (el.scrollHeight || 0) - (el.clientHeight || 0));
- el.scrollTop = Math.min(scrollTop, maxScrollTop);
- return true;
- };
- const restoreArchivesOuterScrollTopWithRetry = (retryCount = 6) => {
- const ok = restoreArchivesOuterScrollTop();
- if (ok || retryCount <= 0) return;
- setTimeout(() => restoreArchivesOuterScrollTopWithRetry(retryCount - 1), 50);
- };
- const defaultGardenId = ref(null);
- const selectedGardenId = ref(null);
- const gardenListRef = ref(null);
- const activeGardenTab = ref('current');
- const trendMonitorMockList = ref([]);
- const changeGardenTab = (tab) => {
- activeGardenTab.value = tab;
- };
- const getFarmRiskAndTracking = async () => {
- trendMonitorMockList.value = [];
- // const res = await VE_API.monitor.getFarmRiskAndTracking({
- // farm_id: gardenId.value,
- // crop_type: JSON.parse(localStorage.getItem("selectedFarmData")).farm_variety,
- // });
- const res = {
- "code": 200,
- "data": {
- "growth_abnormal_tracking": {
- "first_work": {
- "interaction_issue": "是否有10%的荔枝出现落果裂果?",
- "work_name": "长势异常态势跟踪",
- "work_reason_short": "缺锌导致生长缓慢"
- },
- "risk_level": 2,
- "work_id": 4393
- },
- "pest_risk_assessment": {
- "first_work": {
- "interaction_issue": "是否有10%的荔枝发现蒂蛀虫?",
- "work_name": "病虫害态势监控",
- "work_reason_short": "病虫害影响作物生长"
- },
- "risk_level": 2,
- "work_id": 4394
- },
- "phenology_tracking": {
- "first_work": {
- "interaction_issue": "是否有60%的荔枝进入转色期",
- "work_name": "物候跟踪记录",
- "work_reason_short": "预计到达病虫防控关键期"
- },
- "work_id": 4393
- }
- }
- }
- if (res.code === 200) {
- trendMonitorMockList.value.push(res.data?.growth_abnormal_tracking);
- trendMonitorMockList.value.push(res.data?.pest_risk_assessment);
- if (res.data?.phenology_tracking) {
- trendMonitorMockList.value.push(res.data?.phenology_tracking);
- }
- }
- }
- const handleGardenLoaded = ({ hasFarm }) => {
- weatherInfoRef.value?.setGardenLoaded?.(hasFarm);
- };
- const handleGardenSelected = (garden) => {
- selectedGardenId.value = garden?.id ?? null;
- weatherInfoRef.value?.setSelectedGarden?.(garden);
- };
- const isHeaderShow = ref(false);
- const weatherInfoRef = ref(null);
- onActivated(() => {
- blockArchivesScrollSaveOnce.value = false;
- // 用来接收我的农场跳转过来的农场详情逻辑
- if (route.query.isHeaderShow) {
- isHeaderShow.value = true;
- defaultGardenId.value = route.query.farmId;
- }
- const savedFarmId = localStorage.getItem("selectedFarmId");
- selectedGardenId.value = savedFarmId ? Number(savedFarmId) : null;
- gardenListRef.value?.refreshFarmList?.();
- nextTick(() => {
- requestAnimationFrame(() => {
- restoreArchivesOuterScrollTopWithRetry();
- });
- });
- });
- onDeactivated(() => {
- if (blockArchivesScrollSaveOnce.value) return;
- saveArchivesOuterScrollTop();
- });
- onBeforeRouteLeave(() => {
- if (blockArchivesScrollSaveOnce.value) return;
- saveArchivesOuterScrollTop();
- });
- watch(activeGardenTab, (val, oldVal) => {
- if (oldVal === "current" && val !== "current") {
- saveArchivesOuterScrollTop();
- }
- if (val === "current") {
- nextTick(() => {
- requestAnimationFrame(() => {
- restoreArchivesOuterScrollTopWithRetry();
- });
- });
- }
- });
- const store = useStore();
- const tabBarHeight = computed(() => store.state.home.tabBarHeight);
- const isExpanded = ref(false);
- const weatherExpanded = (isExpandedValue) => {
- isExpanded.value = isExpandedValue;
- };
- // 点击遮罩时收起天气
- const handleMaskClick = () => {
- if (weatherInfoRef.value && weatherInfoRef.value.toggleExpand) {
- weatherInfoRef.value.toggleExpand();
- }
- };
- const gardenId = ref(store.state.home.gardenId);
- // 初始化加载数据
- // onMounted(() => {
- // if (gardenId.value) {
- // getFarmRiskAndTracking();
- // }
- // });
- const changeGarden = ({ id }) => {
- gardenId.value = id;
- getFarmRiskAndTracking();
- // 更新 store 中的状态
- store.commit("home/SET_GARDEN_ID", id);
- };
- const handleTrendMonitorCardClick = (item) => {
- saveArchivesOuterScrollTop();
- blockArchivesScrollSaveOnce.value = true;
- router.push({
- path: "/record_details",
- query: { workId: item.work_id, type: item.first_work.work_name },
- });
- };
- </script>
- <style scoped lang="scss">
- .monitor-index {
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- background: linear-gradient(180deg, #2199F8 8%, #F5F5F5 19%, #F5F5F5 73%, #F5F5F5 100%);
- .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;
- top: 10px;
- left: 10px;
- z-index: 12;
- }
- .trend-monitor-list {
- display: flex;
- flex-direction: column;
- gap: 12px;
- background: #fff;
- border: 1px solid #2199F8;
- border-radius: 8px;
- padding: 10px;
- .trend-monitor-card {
- border: 0.5px solid rgba(33, 153, 248, 0.5);
- border-radius: 4px;
- padding: 6px 8px;
- position: relative;
- .card-header {
- display: flex;
- align-items: center;
- margin-bottom: 6px;
- .header-left {
- display: flex;
- align-items: center;
- gap: 5px;
- .title {
- font-size: 16px;
- }
- .level-tag {
- padding: 1px 8px;
- border-radius: 2px;
- background: rgba(255, 78, 78, 0.1);
- color: #FF4E4E;
- font-size: 12px;
- }
- }
- .status-tag {
- background: #FF953D;
- color: #ffffff;
- font-size: 12px;
- border-radius: 0 4px 0 4px;
- padding: 2px 5px;
- position: absolute;
- top: 0;
- right: 0;
- }
- }
- .card-row {
- background: #F7F7F7;
- border-radius: 5px;
- padding: 5px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 6px;
- font-size: 12px;
- color: #626262;
- .question-tag {
- background: #2b9af6;
- color: #ffffff;
- border-radius: 2px;
- padding: 2px 6px;
- }
- }
- .record-list {
- display: flex;
- gap: 6px;
- .record-item {
- border: 1px solid #2199F8;
- border-radius: 2px;
- color: #2199F8;
- text-align: center;
- font-size: 12px;
- padding: 0 5px;
- }
- }
- }
- }
- .archives-time-line {
- position: relative;
- height: calc(100% - 160px);
- padding: 12px;
- display: flex;
- flex-direction: column;
- min-height: 0;
- padding-top: 150px;
- overflow-y: auto;
- -webkit-overflow-scrolling: touch;
- .archives-time-line-content {
- margin-top: 10px;
- flex: none;
- min-height: 0;
- background: #fff;
- border-radius: 8px;
- padding: 10px;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- }
- }
- }
- </style>
|