|
|
@@ -56,7 +56,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, nextTick, watch } from "vue";
|
|
|
+import { ref, nextTick, watch, onMounted, onUnmounted } from "vue";
|
|
|
import interactPopup from "@/components/popup/interactPopup.vue";
|
|
|
import { ElMessage } from "element-plus";
|
|
|
import { WarningFilled } from "@element-plus/icons-vue";
|
|
|
@@ -112,6 +112,8 @@ const isInitialLoad = ref(true);
|
|
|
const timelineListHeight = ref(0);
|
|
|
// 生成唯一的时间戳,用于确保key的唯一性
|
|
|
const uniqueTimestamp = ref(Date.now());
|
|
|
+// ResizeObserver 实例,用于监听高度变化
|
|
|
+let resizeObserver = null;
|
|
|
|
|
|
// 获取当前季节
|
|
|
const getCurrentSeason = () => {
|
|
|
@@ -251,43 +253,23 @@ const calculatePhenologyPositions = () => {
|
|
|
const calculateTotalHeightByFarmWorks = () => {
|
|
|
const { totalHeight } = calculatePhenologyPositions();
|
|
|
|
|
|
- // 计算最后一个物候期的底部位置
|
|
|
- let lastPhenologyBottom = 0;
|
|
|
- if (phenologyList.value && phenologyList.value.length > 0) {
|
|
|
- const sortedPhenologyList = [...phenologyList.value].sort((a, b) => {
|
|
|
- const aProgress = Math.min(Number(a?.progress) || 0, Number(a?.progress2) || 0);
|
|
|
- const bProgress = Math.min(Number(b?.progress) || 0, Number(b?.progress2) || 0);
|
|
|
- return aProgress - bProgress;
|
|
|
- });
|
|
|
-
|
|
|
- let currentTop = 10; // 起始位置
|
|
|
- sortedPhenologyList.forEach((phenology) => {
|
|
|
- const height = getPhenologyRequiredHeight(phenology);
|
|
|
- currentTop += height;
|
|
|
- });
|
|
|
- lastPhenologyBottom = currentTop; // 最后一个物候期的底部位置
|
|
|
- }
|
|
|
-
|
|
|
- // 直接使用最后一个物候期的底部位置作为总高度,不添加额外余量
|
|
|
- // 在getTermStyle中,我们已经调整了最后一个节气的top位置(total - 46)
|
|
|
- // 这样最后一个节气的底部(total - 46 + 46 = total)就能与物候期底部对齐
|
|
|
- if (lastPhenologyBottom > 0) {
|
|
|
+ // 如果有物候期数据,直接使用计算出的总高度
|
|
|
+ // totalHeight 已经包含了从 10 开始的起始位置和所有物候期的高度
|
|
|
+ if (totalHeight > 10) {
|
|
|
+ // 确保总高度至少能容纳所有节气(每个节气至少50px)
|
|
|
const baseHeight = (solarTerms.value?.length || 0) * 50;
|
|
|
- // 总高度 = 最后一个物候期的底部位置,精确匹配,不添加额外余量
|
|
|
- return Math.max(lastPhenologyBottom, totalHeight, baseHeight);
|
|
|
+ // 返回物候期总高度和基础高度的较大值,确保节气能正常显示
|
|
|
+ return Math.max(totalHeight, baseHeight);
|
|
|
}
|
|
|
|
|
|
- // 基础高度:每个节气至少需要一定高度,确保节气标签能显示
|
|
|
+ // 如果没有物候期数据,返回基础高度
|
|
|
const baseHeight = (solarTerms.value?.length || 0) * 50;
|
|
|
-
|
|
|
- // 返回物候期总高度和基础高度的较大值,不添加最小高度限制
|
|
|
- return Math.max(totalHeight, baseHeight);
|
|
|
+ return baseHeight || 100; // 至少返回100px,避免为0
|
|
|
};
|
|
|
|
|
|
const getTermStyle = (t, index) => {
|
|
|
// 优先使用实际测量的timeline-list高度,如果没有测量到则使用计算值作为后备
|
|
|
const totalHeight = timelineListHeight.value > 0 ? timelineListHeight.value : calculateTotalHeightByFarmWorks();
|
|
|
-
|
|
|
// 获取节气总数
|
|
|
const termCount = solarTerms.value?.length || 1;
|
|
|
|
|
|
@@ -423,34 +405,45 @@ const getFarmWorkPlan = () => {
|
|
|
})
|
|
|
: [];
|
|
|
|
|
|
+ // 使用多次 nextTick 和 requestAnimationFrame 确保DOM完全渲染
|
|
|
nextTick(() => {
|
|
|
- // 测量timeline-list的实际渲染高度
|
|
|
- if (timelineListRef.value) {
|
|
|
- requestAnimationFrame(() => {
|
|
|
- const height = timelineListRef.value.offsetHeight || timelineListRef.value.clientHeight;
|
|
|
- if (height > 0) {
|
|
|
- timelineListHeight.value = height;
|
|
|
-
|
|
|
- // 如果是首次加载,滚动到当前季节对应的节气
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ nextTick(() => {
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ // 测量timeline-list的实际渲染高度
|
|
|
+ if (timelineListRef.value) {
|
|
|
+ const height = timelineListRef.value.offsetHeight || timelineListRef.value.clientHeight;
|
|
|
+ if (height > 0) {
|
|
|
+ timelineListHeight.value = height;
|
|
|
+
|
|
|
+ // 如果是首次加载,滚动到当前季节对应的节气
|
|
|
+ if (isInitialLoad.value) {
|
|
|
+ const currentSeason = getCurrentSeason();
|
|
|
+ handleSeasonClick(currentSeason);
|
|
|
+ isInitialLoad.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (isInitialLoad.value) {
|
|
|
- const currentSeason = getCurrentSeason();
|
|
|
- handleSeasonClick(currentSeason);
|
|
|
- isInitialLoad.value = false;
|
|
|
+ // 如果测量失败,延迟一下再尝试滚动
|
|
|
+ setTimeout(() => {
|
|
|
+ if (timelineListRef.value) {
|
|
|
+ const height = timelineListRef.value.offsetHeight || timelineListRef.value.clientHeight;
|
|
|
+ if (height > 0) {
|
|
|
+ timelineListHeight.value = height;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const currentSeason = getCurrentSeason();
|
|
|
+ handleSeasonClick(currentSeason);
|
|
|
+ isInitialLoad.value = false;
|
|
|
+ }, 200);
|
|
|
+ } else if (timelineContainerRef.value && savedScrollTop > 0) {
|
|
|
+ timelineContainerRef.value.scrollTop = savedScrollTop;
|
|
|
}
|
|
|
- }
|
|
|
+ });
|
|
|
});
|
|
|
- }
|
|
|
-
|
|
|
- if (isInitialLoad.value) {
|
|
|
- // 如果测量失败,延迟一下再尝试滚动
|
|
|
- setTimeout(() => {
|
|
|
- const currentSeason = getCurrentSeason();
|
|
|
- handleSeasonClick(currentSeason);
|
|
|
- isInitialLoad.value = false;
|
|
|
- }, 100);
|
|
|
- } else if (timelineContainerRef.value && savedScrollTop > 0) {
|
|
|
- timelineContainerRef.value.scrollTop = savedScrollTop;
|
|
|
- }
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
// 收集所有farmWorkId
|
|
|
@@ -509,6 +502,60 @@ watch(
|
|
|
}
|
|
|
}
|
|
|
);
|
|
|
+
|
|
|
+// 使用 ResizeObserver 监听高度变化,确保在DOM完全渲染后获取准确高度
|
|
|
+const setupResizeObserver = () => {
|
|
|
+ if (!timelineListRef.value || typeof ResizeObserver === 'undefined') {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已经存在观察者,先断开
|
|
|
+ if (resizeObserver) {
|
|
|
+ resizeObserver.disconnect();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新的观察者
|
|
|
+ resizeObserver = new ResizeObserver((entries) => {
|
|
|
+ for (const entry of entries) {
|
|
|
+ const height = entry.contentRect.height;
|
|
|
+ if (height > 0 && height !== timelineListHeight.value) {
|
|
|
+ timelineListHeight.value = height;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 开始观察
|
|
|
+ resizeObserver.observe(timelineListRef.value);
|
|
|
+};
|
|
|
+
|
|
|
+// 组件挂载后设置 ResizeObserver
|
|
|
+onMounted(() => {
|
|
|
+ nextTick(() => {
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ setupResizeObserver();
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+// 组件卸载前清理 ResizeObserver
|
|
|
+onUnmounted(() => {
|
|
|
+ if (resizeObserver) {
|
|
|
+ resizeObserver.disconnect();
|
|
|
+ resizeObserver = null;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 在数据更新后重新设置 ResizeObserver
|
|
|
+watch(
|
|
|
+ () => phenologyList.value.length,
|
|
|
+ () => {
|
|
|
+ nextTick(() => {
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ setupResizeObserver();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+);
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|