| 
					
				 | 
			
			
				@@ -9,7 +9,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         :key="s.value" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         class="season-tab" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         :class="{ active: s.value === activeSeason }" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        @click="activeSeason = s.value" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        @click="handleSeasonClick(s.value)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         {{ s.label }} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     </div> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -21,79 +21,62 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            <!-- 三行循环时间线 --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            <div class="cycle-timeline-container"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                <div class="cycle-timeline"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            <div class="timeline-container" ref="timelineContainerRef"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                <div class="timeline-list" :style="getListStyle"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    <div class="timeline-middle-line"></div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    <!-- 物候期覆盖条(progress 为起点,progress2 为终点,单位 %) --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        v-for="(row, rowIndex) in timelineRows" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        :key="rowIndex" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        class="cycle-row" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        :class="{ 'odd-index': rowIndex % 2 === 1 }" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        v-for="(p, idx) in phenologyList" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        :key="p.id ?? idx" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        class="phenology-bar" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        :style="getPhenologyBarStyle(p)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            v-for="(item, itemIndex) in row.items" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            :key="itemIndex" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            class="cycle-item" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            @click="handleRowClick(item)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            :class="[item.type + '-item']" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            <!-- 节气节点 --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            <template v-if="item.type === 'term'"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                <!-- <div class="cycle-term-dot"></div> --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                <div class="cycle-term-label">{{ item.name || item.id }}</div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        <!-- 生育期名称(根据时间范围显示在对应行) --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        <div class="cycle-phenology-wrap" v-if="getPhenologyBarsForRow(rowIndex).length > 0"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        <div class="reproductive-list"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                v-for="p in getPhenologyBarsForRow(rowIndex)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                :key="p.id" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                class="cycle-label" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                :class="p.color" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                :style=" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    isOddVisualRow(rowIndex) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                        ? { right: p.left, width: p.width } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                        : { left: p.left, width: p.width } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                v-for="(r, rIdx) in Array.isArray(p.reproductiveList) ? p.reproductiveList : []" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                :key="r.id ?? rIdx" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                class="reproductive-item" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                {{ p.name }} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                <div v-if="p.arranges && p.arranges.length" class="arranges"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    <div v-for="a in p.arranges" :key="a.id" :class="['cycle-task-box', a.status]"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                        <div class="cycle-task-text">{{ a.farmWorkName || a.name }}</div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                        <!-- 任务连接器 --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                        <div class="cycle-task-connector"></div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                {{ r.name }} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                <div class="arranges"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        v-for="(fw, aIdx) in Array.isArray(r.farmWorkArrangeList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            ? r.farmWorkArrangeList 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            : []" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        :key="fw.id ?? aIdx" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        class="arrange-box" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        :class="getArrangeStatusClass(fw)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        <span class="arrange-text">{{ fw.farmWorkName }}</span> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                         <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                            v-if="a.status === 'complete' || a.status === 'warning'" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            v-if=" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                getArrangeStatusClass(fw) === 'status-complete' || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                getArrangeStatusClass(fw) === 'status-warning' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                             class="status-icon" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                            :class="a.status" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            :class="getArrangeStatusClass(fw)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                         > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                            <el-icon v-if="a.status === 'complete'" size="16" color="#1CA900" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                                ><SuccessFilled 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                            /></el-icon> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                            <el-icon v-else size="18" color="#FF953D"><WarnTriangleFilled /></el-icon> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            <el-icon 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                v-if="getArrangeStatusClass(fw) === 'status-complete'" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                size="16" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                color="#1CA900" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                <SuccessFilled /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            </el-icon> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            <el-icon v-else size="18" color="#FF953D"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                <WarnTriangleFilled /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            </el-icon> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                         </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                     </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        <!-- 行连接器 --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            v-if="rowIndex < timelineRows.length - 1" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            class="cycle-connector" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            :class="[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                rowIndex % 2 === 1 ? 'middle-connector' : 'top-connector', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                getConnectorColorClass(rowIndex), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            ]" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            <img v-if="isConnectorGray(rowIndex)" src="@/assets/img/monitor/defalut-arrow.png" alt="" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            <img v-else src="@/assets/img/monitor/arrow.png" alt="" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    <div v-for="t in solarTerms" :key="t.id" class="timeline-term" :style="getTermStyle(t)"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        <span class="term-name">{{ t.displayName }}</span> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             <div class="control-section"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 <div class="toggle-group"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     <el-switch v-model="isDefaultEnabled" /> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -113,11 +96,12 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 <script setup> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import { reactive, ref, onMounted, nextTick, onBeforeUnmount } from "vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { reactive, ref, onMounted, computed } from "vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import customHeader from "@/components/customHeader.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import { useRouter, useRoute } from "vue-router"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import detailDialog from "@/components/detailDialog.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import activeUploadPopup from "@/components/popup/activeUploadPopup.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { SuccessFilled, WarningFilled } from "@element-plus/icons-vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const router = useRouter(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const route = useRoute(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -128,7 +112,7 @@ const seasons = reactive([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { value: "autumn", label: "秋季" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { value: "winter", label: "冬季" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const activeSeason = ref("spring"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const activeSeason = ref(""); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const statusList = reactive([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { value: "pending", label: "待触发", color: "gray" }, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -137,95 +121,58 @@ const statusList = reactive([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { value: "expired", label: "已过期", color: "orange" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 切换开关状态 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const isDefaultEnabled = ref(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 时间线行数据(由接口节气生成) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const timelineRows = reactive([]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 目标定位日期(当前生育期参考点) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const targetDate = new Date("2025-04-04T00:00:00"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 每行“当前生育期”标记的位置样式(按行索引) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const phenologyPositions = ref({}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 生育期条(按行分组) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const phenologyBarsByRow = ref([]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 每一行可视区域的实际像素宽度(用于将最小像素宽度换算为百分比) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const rowWidths = ref([]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 节气 id 到对象的索引,便于通过 id 查找节气日期 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-let solarTermIdToTerm = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 接口返回的生育期数据 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const solarTerms = ref([]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const phenologyList = ref([]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 安全日期解析(兼容 'YYYY-MM-DD HH:mm:ss' / 'YYYY/MM/DD HH:mm:ss') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const parseDate = (val) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (!val) return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (val instanceof Date) return isNaN(val.getTime()) ? null : val; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (typeof val === "number") return new Date(val); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (typeof val === "string") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 统一到可被 Safari 解析的格式 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const s = val.replace(/-/g, "/").replace("T", " "); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const d = new Date(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return isNaN(d.getTime()) ? null : d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 onMounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     getFarmWorkPlan(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    window.addEventListener("resize", handleResize, { passive: true }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-onBeforeUnmount(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    window.removeEventListener("resize", handleResize); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const handleResize = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 重新测量并基于最新宽度重算条目 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    nextTick(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        measureRowWidths(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 需要基于最新数据重算 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (phenologyList.value && phenologyList.value.length && cachedValidSolarTerms.value) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            groupPhenologyBarsByRow(phenologyList.value, cachedValidSolarTerms.value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 缓存已过滤/排序后的节气用于重复计算 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const cachedValidSolarTerms = ref(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const getFarmWorkPlan = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const paramFarmId = Number(route.query.farmId) || undefined; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     VE_API.monitor 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .farmWorkPlan({ farmId: paramFarmId ?? 92844 }) // 优先使用路由传入的 farmId 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .farmWorkPlan({ farmId: route.query.farmId }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         .then(({ data, code }) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (code === 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                const solarTermsList = data.solarTermsList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 仅保留 type === 1 的节气,按需要的顺序(示例:反转) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 取 type===1 的节气,并按日期降序排序(晚到早) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                const validSolarTerms = Array.isArray(solarTermsList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? solarTermsList 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                          .filter((t) => t && t.type === 1 && t.createDate) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                          .sort((a, b) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                              const da = parseDate(a.createDate)?.getTime() ?? 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                              const db = parseDate(b.createDate)?.getTime() ?? 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                              return db - da; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                          }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                const list = Array.isArray(data?.solarTermsList) ? data.solarTermsList : []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                const filtered = list 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .filter((t) => t && t.type === 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .map((t) => ({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        id: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            t.id ?? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            t.solarTermsId ?? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            t.termId ?? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            `${t.name || t.solarTermsName || t.termName || "term"}-${t.createDate || ""}`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        displayName: t.name || t.solarTermsName || t.termName || "节气", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        createDate: t.createDate || null, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        progress: Number(t.progress) || 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    })); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                solarTerms.value = filtered; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 物候期数据 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                phenologyList.value = Array.isArray(data?.phenologyList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? data.phenologyList.map((it) => ({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          id: it.id ?? it.phenologyId ?? it.name ?? `${it.progress}-${it.progress2}`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          progress: Number(it.progress) || 0, // 起点 % 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          progress2: Number(it.progress2) || 0, // 终点 % 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          // 兼容多种可能的开始时间字段 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          startTimeMs: safeParseDate( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              it.startDate || it.beginDate || it.startTime || it.start || it.start_at 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          reproductiveList: Array.isArray(it.reproductiveList) ? it.reproductiveList : [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     : []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                cachedValidSolarTerms.value = validSolarTerms; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                generateTimelineData(validSolarTerms); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                computeCurrentPhenologyPositions(validSolarTerms, targetDate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 保存生育期数据并生成各行生育期条 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                phenologyList.value = Array.isArray(data.phenologyList) ? data.phenologyList : []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 生成 id->term 的索引 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                solarTermIdToTerm = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                validSolarTerms.forEach((t) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (t && (t.id || t.solarTermsId)) solarTermIdToTerm[t.id ?? t.solarTermsId] = t; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 先等待 DOM 渲染完成后测量每行宽度,再据此计算最小可显示宽度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                nextTick(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    measureRowWidths(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    groupPhenologyBarsByRow(phenologyList.value, validSolarTerms); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // // 测试数据:补充完成/预警案例以展示样式 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // if (phenologyList.value.length > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //     const rlist = phenologyList.value[0]?.reproductiveList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //     if (Array.isArray(rlist) && rlist.length > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //         const first = rlist[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //         if (!Array.isArray(first.farmWorkArrangeList)) first.farmWorkArrangeList = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //         first.farmWorkArrangeList.push( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //             { id: "test-complete", farmWorkName: "测试完成", farmWorkType: 5 }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //             { id: "test-warning", farmWorkName: "测试预警", farmWorkType: 6 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //         ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         .catch((error) => { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -233,333 +180,8 @@ const getFarmWorkPlan = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 测量每一行生育期容器的实际宽度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const measureRowWidths = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rows = document.querySelectorAll(".cycle-timeline .cycle-row"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const widths = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    rows.forEach((rowEl, idx) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const wrap = rowEl.querySelector(".cycle-phenology-wrap"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        widths[idx] = wrap ? wrap.offsetWidth : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    rowWidths.value = widths; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 生成时间轴数据 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const generateTimelineData = (solarTerms) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 清空 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    timelineRows.splice(0, timelineRows.length); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 无数据则给一行示例 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (!solarTerms || solarTerms.length === 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        timelineRows.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            items: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { type: "task", status: "default", taskName: "梢期", taskDesc: "杀虫" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { type: "term", name: "节气" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { type: "task", status: "default", taskName: "梢期", taskDesc: "杀虫" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { type: "term", name: "节气" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { type: "task", status: "default", taskName: "梢期", taskDesc: "杀虫" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { type: "term", name: "节气" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const itemsPerRow = 6; // 任务/节气交替 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const termsPerRow = 3; // 每行3个节气 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const totalRows = Math.ceil(solarTerms.length / termsPerRow); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const rowItems = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const startTermIndex = rowIndex * termsPerRow; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (let i = 0; i < itemsPerRow; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (i % 2 === 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 任务位 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                const taskData = getTaskDataForIndex(Math.floor(i / 2)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                rowItems.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    type: "task", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    status: taskData.status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    taskName: taskData.taskName, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    taskDesc: taskData.taskDesc, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    icon: taskData.icon, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 节气位 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                const termIndex = startTermIndex + Math.floor(i / 2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (termIndex < solarTerms.length) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    const term = solarTerms[termIndex] || {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    rowItems.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        type: "term", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        status: "default", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        name: term.name || term.solarTermsName || term.termName || "节气", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        id: term.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        createDate: term.createDate, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 不足时补任务 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    rowItems.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        type: "task", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        status: "default", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        taskName: "梢期", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        taskDesc: "杀虫", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        timelineRows.push({ items: rowItems }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 任务占位数据(可按需接后端) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const getTaskDataForIndex = (index) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const defaultTasks = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { status: "default", taskName: "梢期", taskDesc: "杀虫" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { status: "active", taskName: "梢期", taskDesc: "营养" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { status: "complete", taskName: "梢期", taskDesc: "修剪", icon: { type: "complete" } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { status: "warning", taskName: "梢期", taskDesc: "施肥", icon: { type: "warning" } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { status: "normal", taskName: "梢期", taskDesc: "灌溉", icon: { type: "normal" } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return defaultTasks[index % defaultTasks.length]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 计算“当前生育期”在各行的定位(只在包含目标日期的那一行显示) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const computeCurrentPhenologyPositions = (solarTerms, date) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    phenologyPositions.value = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (!Array.isArray(solarTerms) || solarTerms.length === 0 || !(date instanceof Date)) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const termsPerRow = 3; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const totalRows = Math.ceil(solarTerms.length / termsPerRow); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 1) 找到最接近目标日期的节气(按时间升序) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const termsAsc = solarTerms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .filter((t) => t && t.createDate) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .slice() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .sort((a, b) => (parseDate(a.createDate)?.getTime() ?? 0) - (parseDate(b.createDate)?.getTime() ?? 0)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (termsAsc.length === 0) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const targetMs = date.getTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let nearest = termsAsc[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let bestDiff = Math.abs((parseDate(nearest.createDate)?.getTime() ?? 0) - targetMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    for (let i = 1; i < termsAsc.length; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const ms = parseDate(termsAsc[i].createDate)?.getTime() ?? 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const diff = Math.abs(ms - targetMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (diff < bestDiff) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            bestDiff = diff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            nearest = termsAsc[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 2) 将该节气映射回当前(降序)数组中的索引与行 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const nearestIdxDesc = solarTerms.findIndex((t) => t && nearest && t.id === nearest.id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rowIndex = Math.max(0, Math.floor(nearestIdxDesc / termsPerRow)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const startIdx = rowIndex * termsPerRow; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const endIdx = Math.min(startIdx + termsPerRow - 1, solarTerms.length - 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (startIdx > endIdx) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rowTerms = solarTerms.slice(startIdx, endIdx + 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 视觉顺序用于方向(偶数行正向,奇数行反向),但时间范围应取该行真实最早/最晚 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rowDates = rowTerms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .map((t) => parseDate(t?.createDate)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .filter((d) => d && !isNaN(d.getTime())) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .map((d) => d.getTime()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (rowDates.length === 0) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const minMs = Math.min(...rowDates); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const maxMs = Math.max(...rowDates); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rowStart = new Date(minMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rowEnd = new Date(maxMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 3) 若目标日期不在该行范围内,则就近夹到边界(避免跨行导致丢失) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let anchorMs = targetMs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (anchorMs < minMs) anchorMs = minMs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (anchorMs > maxMs) anchorMs = maxMs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 4) 计算在该行范围内的比例 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const total = Math.max(1, maxMs - minMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const ratio = Math.max(0, Math.min(1, (anchorMs - minMs) / total)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const percent = `${(ratio * 100).toFixed(2)}%`; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 5) 偶数行用 left,奇数行用 right,与 Z 字方向一致 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (rowIndex % 2 === 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        phenologyPositions.value[rowIndex] = { right: percent }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        phenologyPositions.value[rowIndex] = { left: percent }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// moved above with other refs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 将生育期条按行计算定位与宽度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const groupPhenologyBarsByRow = (phenologyList, solarTerms) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    phenologyBarsByRow.value = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        !Array.isArray(phenologyList) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        phenologyList.length === 0 || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        !Array.isArray(solarTerms) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        solarTerms.length === 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const termsPerRow = 3; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const totalRows = Math.ceil(solarTerms.length / termsPerRow); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 行范围:使用该行包含的节气最早/最晚时间(按真实时间线性映射) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const rowRanges = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const startIdx = rowIndex * termsPerRow; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const endIdx = Math.min(startIdx + termsPerRow - 1, solarTerms.length - 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const rowTerms = solarTerms.slice(startIdx, endIdx + 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const rowDates = rowTerms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map((t) => parseDate(t?.createDate)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .filter((d) => d && !isNaN(d.getTime())) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map((d) => d.getTime()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (rowDates.length === 0) continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const minMs = Math.min(...rowDates); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const maxMs = Math.max(...rowDates); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const rowStart = new Date(minMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const rowEnd = new Date(maxMs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const totalMs = Math.max(1, rowEnd.getTime() - rowStart.getTime()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        rowRanges.push({ rowIndex, rowStart, rowEnd, totalMs }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        phenologyBarsByRow.value[rowIndex] = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 中点归属法:每条生育期归属到中点所在的行;在行内按 Z 字方向计算 left/right 与 width 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    phenologyList.forEach((p, pIndex) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const list = Array.isArray(p?.reproductiveList) ? p.reproductiveList : []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const baseColorClass = pIndex % 2 === 0 ? "blue" : "orange"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        list.forEach((r) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 优先使用节气 id 对应的节气日期 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let sTermDate = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let eTermDate = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (r?.startSolarTermId && solarTermIdToTerm[r.startSolarTermId]?.createDate) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                sTermDate = parseDate(solarTermIdToTerm[r.startSolarTermId].createDate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (r?.endSolarTermId && solarTermIdToTerm[r.endSolarTermId]?.createDate) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                eTermDate = parseDate(solarTermIdToTerm[r.endSolarTermId].createDate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const s = sTermDate || parseDate(r?.startDate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const e = eTermDate || parseDate(r?.endDate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!s || !e) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const start = new Date(Math.min(s.getTime(), e.getTime())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const end = new Date(Math.max(s.getTime(), e.getTime())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (end < start) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const mid = new Date(start.getTime() + (end.getTime() - start.getTime()) / 2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 找到中点所在行;若不在任何行,则归最近行 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let target = rowRanges.find(({ rowStart, rowEnd }) => mid >= rowStart && mid <= rowEnd); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!target) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                target = rowRanges.reduce((best, curr) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    const dist = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        mid < curr.rowStart 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            ? curr.rowStart.getTime() - mid.getTime() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            : mid.getTime() - curr.rowEnd.getTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (!best || dist < best.dist) return { dist, curr }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return best; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }, null)?.curr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!target) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 位置:基于真实的 startDate(不截断),确保相邻条的间距 = (后一个startDate - 前一个endDate) 的时间差映射 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const startRatio = (start.getTime() - target.rowStart.getTime()) / target.totalMs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 宽度:基于真实的 endDate - startDate 的时间差 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const actualDuration = end.getTime() - start.getTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const widthRatio = actualDuration / target.totalMs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 限制到行范围内 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const leftRatio = Math.max(0, Math.min(1, startRatio)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const rightRatio = Math.max(0, Math.min(1, (end.getTime() - target.rowStart.getTime()) / target.totalMs)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let clampedWidthRatio = Math.max(0.001, Math.min(widthRatio, rightRatio - leftRatio)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 强制最小显示宽度:若换算到像素后小于 CSS 中的 min-width:22px,则使用最小可见宽度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const MIN_LABEL_PX = 22; // 与样式 .cycle-label 的最小宽度保持一致 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const rowWidthPx = rowWidths.value?.[target.rowIndex] || 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let leftPercent = leftRatio * 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let widthPercent = clampedWidthRatio * 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (rowWidthPx > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                const minPercent = (MIN_LABEL_PX / rowWidthPx) * 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (widthPercent < minPercent) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    widthPercent = minPercent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 若越界则左移以保证完全可见 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (leftPercent + widthPercent > 100) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    leftPercent = Math.max(0, 100 - widthPercent); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 回填为比例供后续使用 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                clampedWidthRatio = widthPercent / 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 无法测量时,保底给一个不至于 0 的最小显示比例(以 360px 近似,22px/360≈6.1%) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (widthPercent < 6.2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    widthPercent = 6.2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (leftPercent + widthPercent > 100) leftPercent = Math.max(0, 100 - widthPercent); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    clampedWidthRatio = widthPercent / 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const isFuture = start.getTime() > Date.now(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const colorToUse = isFuture ? "" : baseColorClass; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 组装农事安排:按 reproductiveId 归属到当前生育期 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const arrangeList = Array.isArray(r.farmWorkArrangeList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ? r.farmWorkArrangeList.filter((fw) => !fw.reproductiveId || fw.reproductiveId === r.id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                : []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            const arrangeItems = arrangeList.map((fw) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let status = "default"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                const t = fw.farmWorkType; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (t == null || t === 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    status = "default"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else if (t >= 1 && t <= 4) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    status = "normal"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else if (t === 5) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    status = "complete"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else if (t === 6) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    status = "warning"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    id: fw.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    name: fw.farmWorkName, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // // 附加两条测试数据:已完成、已过期 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // arrangeItems.push( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            //     { id: `${r.id}-test-complete`, name: "测试完成", status: "complete" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            //     { id: `${r.id}-test-warning`, name: "测试过期", status: "warning" } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            phenologyBarsByRow.value[target.rowIndex].push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                id: r.id || `${p.id || "p"}-${start.getTime()}-${end.getTime()}`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                name: r.name && r.name.trim() ? r.name.trim() : r.phenologyName || "生育期", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                left: `${leftPercent.toFixed(4)}%`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                width: `${(clampedWidthRatio * 100).toFixed(4)}%`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                startTime: start.getTime(), // 用于排序,确保相邻条的顺序正确 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                color: colorToUse, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                arranges: arrangeItems, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 每行内部按 startTime 排序,确保相邻条的间距正确反映时间差 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    phenologyBarsByRow.value.forEach((rowBars) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        rowBars.sort((a, b) => (a.startTime || 0) - (b.startTime || 0)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 获取指定行的生育期条 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const getPhenologyBarsForRow = (rowIndex) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return phenologyBarsByRow.value[rowIndex] || []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 视觉奇偶:自下而上计算奇偶(与 UI Z 字一致) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const isOddVisualRow = (rowIndex) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const total = timelineRows.length; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (total <= 0) return rowIndex % 2 === 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const visualIndex = total - 1 - rowIndex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return visualIndex % 2 === 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 切换开关状态 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const isDefaultEnabled = ref(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // 新增农事 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const addNewTask = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     router.push({ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -578,66 +200,107 @@ const manageTask = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const detailDialogRef = ref(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const timelineContainerRef = ref(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const handleRowClick = (item) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (item.status === "complete") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        router.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            path: "/review_work", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            query: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                id: item.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else if (item.type !== "term" && item.status === "default") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        detailDialogRef.value.showDialog(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else if (item.status === "warning" || item.status === "normal") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        router.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            path: "/completed_work", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            query: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                id: item.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                status: item.status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // router.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //     path: "/services_agri", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //     query: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //         id: item.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //         status: item.status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //     }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 安全解析时间到时间戳(ms) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const safeParseDate = (val) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!val) return NaN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (val instanceof Date) return val.getTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (typeof val === "number") return val; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (typeof val === "string") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 兼容 "YYYY-MM-DD HH:mm:ss" -> Safari 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const s = val.replace(/-/g, "/").replace("T", " "); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const d = new Date(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return isNaN(d.getTime()) ? NaN : d.getTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NaN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 行连接器颜色:若后续生育期未开始则灰色,否则保持其颜色(蓝/橙) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const getConnectorColorClass = (rowIndex) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const nextIndex = rowIndex + 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const bars = getPhenologyBarsForRow(nextIndex); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (!bars || bars.length === 0) return ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const nextIsOddIndex = nextIndex % 2 === 1; // 奇数行为左侧连接器 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 计算节气列表容器高度与项位置 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const getListStyle = computed(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const total = (solarTerms.value?.length || 0) * 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const minH = 50 + total + 50; // 上下各 50 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return { minHeight: `${minH}px` }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const parsePercent = (val) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (typeof val !== "string") return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const n = parseFloat(val.replace("%", "")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return isNaN(n) ? 0 : n; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const getTermStyle = (t) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const p = Math.max(0, Math.min(100, Number(t?.progress) || 0)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const total = (solarTerms.value?.length || 0) * 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const top = 50 + (p / 100) * total; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        position: "absolute", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        top: `${top}px`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        left: 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        transform: "translateY(-50%)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        width: "30px", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        height: "20px", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        display: "flex", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        alignItems: "center", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let target = bars[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (nextIsOddIndex) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 左侧:选最靠左的条 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        target = bars.reduce((best, cur) => (parsePercent(cur.left) < parsePercent(best.left) ? cur : best), bars[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 右侧:选最靠右的条(left + width 最大) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        const score = (b) => parsePercent(b.left) + parsePercent(b.width); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        target = bars.reduce((best, cur) => (score(cur) > score(best) ? cur : best), bars[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 点击季节 → 滚动到对应节气(立春/立夏/立秋/立冬) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleSeasonClick = (seasonValue) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    activeSeason.value = seasonValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const mapping = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        spring: "立春", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        summer: "立夏", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        autumn: "立秋", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        winter: "立冬", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const targetName = mapping[seasonValue]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!targetName) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const target = (solarTerms.value || []).find((t) => (t?.displayName || "") === targetName); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!target) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const p = Math.max(0, Math.min(100, Number(target.progress) || 0)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const total = (solarTerms.value?.length || 0) * 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const targetTop = 50 + (p / 100) * total; // 内容内的像素位置 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const wrap = timelineContainerRef.value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!wrap) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const viewH = wrap.clientHeight || 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const maxScroll = Math.max(0, wrap.scrollHeight - viewH); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 将目标位置稍微靠上(使用 0.35 视口高度做偏移) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let scrollTop = Math.max(0, targetTop - viewH * 0.1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (scrollTop > maxScroll) scrollTop = maxScroll; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    wrap.scrollTo({ top: scrollTop, behavior: "smooth" }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // 未来(未开始)时,color 为空串;过去/当前一律显示蓝色 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const hasStarted = !!target?.color; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return hasStarted ? "" : "connector-gray"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 物候期覆盖条样式(使用像素计算,避免 100% 高度为 0 的问题) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const getPhenologyBarStyle = (item) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const p1 = Math.max(0, Math.min(100, Number(item?.progress) || 0)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const p2 = Math.max(0, Math.min(100, Number(item?.progress2) || 0)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const start = Math.min(p1, p2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const end = Math.max(p1, p2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const total = (solarTerms.value?.length || 0) * 100; // 有效绘制区高度(px) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const topPx = 50 + (start / 100) * total; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const heightPx = Math.max(2, ((end - start) / 100) * total); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const now = Date.now(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const isFuture = Number.isFinite(item?.startTimeMs) ? item.startTimeMs > now : start > 0; // 无开始时间时按起点>0近似 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 反转:大于当前时间用灰色,小于等于当前时间用蓝色 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const barColor = isFuture ? "rgba(145, 145, 145, 0.1)" : "#2199F8"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const beforeBg = isFuture ? "rgba(145, 145, 145, 0.1)" : "rgba(33, 153, 248, 0.1)"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        position: "absolute", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        left: "46px", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        width: "25px", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        top: `${topPx}px`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        height: `${heightPx}px`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        background: barColor, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        color: isFuture ? "#747778" : "#fff", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "--bar-before-bg": beforeBg, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        zIndex: 2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 行连接器是否为灰色(用于切换箭头图片) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const isConnectorGray = (rowIndex) => getConnectorColorClass(rowIndex) === "connector-gray"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 农事状态样式映射(0:默认,1-4:正常,5:完成,6:预警) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const getArrangeStatusClass = (fw) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const t = fw?.farmWorkType; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (t == null || t === 0) return "status-default"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (t >= 1 && t <= 4) return "status-normal"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (t === 5) return "status-complete"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (t === 6) return "status-warning"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return "status-default"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 </script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 <style scoped lang="scss"> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -721,243 +384,149 @@ const isConnectorGray = (rowIndex) => getConnectorColorClass(rowIndex) === "conn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 循环时间线样式 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .cycle-timeline-container { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            padding: 35px 15px 25px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            height: calc(100vh - 135px - 69px - 60px); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            overflow-y: auto; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            overflow-x: hidden; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .cycle-timeline { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .timeline-container { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            height: calc(100vh - 93px - 40px - 73px); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            overflow: auto; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            position: relative; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            padding: 0 12px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .timeline-list { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 position: relative; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                .cycle-row { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .timeline-middle-line { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                left: 15px; /* 位于节气文字列中间(列宽约30px) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                top: 50px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                bottom: 50px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                width: 2px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                background: #e8e8e8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                z-index: 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .phenology-bar { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                align-items: stretch; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                justify-content: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                &::before { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    content: ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    top: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    left: 25px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    width: calc(100vw - 100px); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    height: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    background: var(--bar-before-bg, rgba(201, 201, 201, 0.1)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    z-index: 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .reproductive-list { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    display: grid; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    grid-auto-rows: 1fr; /* 子项等高,整体等分父高度 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    align-items: stretch; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    justify-items: center; /* 子项居中 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    width: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    height: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .reproductive-item { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    font-size: 12px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text-align: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    word-break: break-all; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    writing-mode: vertical-rl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text-orientation: upright; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    letter-spacing: 3px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    width: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    line-height: 23px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    color: inherit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     position: relative; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    justify-content: space-between; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    align-items: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    margin-bottom: 60px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    padding-right: 30px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    &.odd-index { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        padding: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        padding-left: 30px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        flex-direction: row-reverse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .cycle-phenology-wrap { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            left: 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            width: calc(100% - 13px); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    &:last-child { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        margin-bottom: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .cycle-phenology-wrap { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            left: 20px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            width: calc(100% - 10px); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 水平时间线 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    &::before { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        content: ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .arranges { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        top: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        left: 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        right: 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        height: 5px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        border-left: 2px solid #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        border-right: 2px solid #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        background: #e8e8e8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        left: 48px; /* 列与中线右侧一段距离 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        top: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         transform: translateY(-50%); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        z-index: 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    .cycle-item { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        position: relative; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        z-index: 2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        top: 12px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        &.term-item { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            flex-direction: column; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            align-items: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            top: -11px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-term-dot { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                width: 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                height: 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                background: #c7c7c7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-radius: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                margin-bottom: 4px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-term-label { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                font-size: 11px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                color: #c7c7c7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                margin-top: 16px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            &.active { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                .cycle-term-dot { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    background: #858383; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                .cycle-term-label { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    color: #858383; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    .cycle-phenology-wrap { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        top: -23px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        left: 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        width: calc(100% - 10px); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         z-index: 3; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        height: 100px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        overflow: hidden; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .cycle-label { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            color: #4e4e4e; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            font-size: 12px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            min-width: 24px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            height: 20px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            line-height: 20px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            text-align: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            background: rgba(180, 182, 183, 0.1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-bottom: 6px solid #e8e8e8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .cycle-label + .cycle-label { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-right: 1px solid #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .cycle-label.blue { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            background: rgba(33, 153, 248, 0.1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-bottom-color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .cycle-label.orange { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            background: #fff2e7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-bottom-color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        .arranges { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .arrange-box { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            width: 36px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            height: 36px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            border: 1px solid rgba(199, 199, 199, 0.6); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            border-radius: 2px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            background: #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            color: #a5a7a9; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            gap: 8px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            padding-top: 26px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            align-items: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             justify-content: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            flex-wrap: nowrap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            // 使用与任务框一致的视觉风格 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border: 1px solid rgba(199, 199, 199, 0.5); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-radius: 2px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                width: 36px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                height: 36px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                min-width: 36px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                line-height: 15px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                font-size: 12px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                padding: 2px 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            position: relative; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            font-size: 12px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            .arrange-text { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                writing-mode: horizontal-tb; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                line-height: 14px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 text-align: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                position: relative; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                color: #c7c7c7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                .status-icon { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    bottom: -10px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    right: -10px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                padding-left: 3px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            .status-icon { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                top: -4px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                left: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                transform: translateX(-50%); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                right: -10px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                bottom: -10px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                z-index: 3; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            &::before { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                content: ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                left: -4px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                top: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                transform: translateY(-50%); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 width: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 height: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-left: 4px solid transparent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-right: 4px solid transparent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-bottom: 4px solid #dde1e7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.warning { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.warning .cycle-task-text { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.warning + .cycle-task-connector, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.warning .cycle-task-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-bottom-color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.complete { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-color: #1ca900; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.complete .cycle-task-text { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                color: #1ca900; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.complete + .cycle-task-connector, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.complete .cycle-task-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-bottom-color: #1ca900; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.normal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.normal .cycle-task-text { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.normal + .cycle-task-connector, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .cycle-task-box.normal .cycle-task-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                border-bottom-color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                border-top: 4px solid transparent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                border-bottom: 4px solid transparent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                border-right: 4px solid currentColor; /* 与文字/边框颜色一致 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    .cycle-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        right: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        top: 45.5px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        transform: translateY(-50%); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        width: 2px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        height: 87px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        border: 5px solid #9dcaf7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        border-left: none; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        background: transparent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        img{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            width: 13px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            height: 13px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            top: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            transform: translateY(-50%); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            left: -8px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            z-index: 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .arrange-box + .arrange-box { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            margin-right: 16px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        &.top-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-top-right-radius: 5px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-bottom-right-radius: 5px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            img{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                left: -2px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        &.middle-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-top-left-radius: 5px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-bottom-left-radius: 5px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            left: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-right: none; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-left: 5px solid #9dcaf7; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .arrange-box.status-warning { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            border-color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            color: #ff953d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        // 动态颜色 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        &.connector-gray { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-color: #c4c6c9; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .arrange-box.status-complete { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            border-color: #1ca900; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            color: #1ca900; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        &.connector-gray.middle-connector { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            border-left-color: #c4c6c9; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .arrange-box.status-normal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            border-color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            color: #2199f8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .reproductive-item + .reproductive-item { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                border-top: 2px solid #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .phenology-bar + .phenology-bar { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                border-top: 2px solid #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .timeline-term { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                width: 30px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                padding-right: 16px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                align-items: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                z-index: 2; /* 置于中线之上 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .term-name { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    display: inline-block; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    width: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    height: 46px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    line-height: 30px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    background: #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    font-size: 13px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    word-break: break-all; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    writing-mode: vertical-rl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text-orientation: upright; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    color: rgba(174, 174, 174, 0.6); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    letter-spacing: 2px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text-align: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // 控制区域样式 
			 |