| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | <template> | 
					
						
							|  |  |  |  |   <div class="header"> | 
					
						
							|  |  |  |  |     <div class="header_left"> | 
					
						
							|  |  |  |  |       <div class="header_left_img"> | 
					
						
							|  |  |  |  |         <img src="@/assets/large/secure.png" style="width: 100%; height: 100%" /> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |       <div style="font-size: 12px; padding-left: 10px">安全生产天数:</div> | 
					
						
							|  |  |  |  |       <div class="header_left_text"> | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |         {{ safetyDay }} | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |         <span style="font-size: 12px">天</span> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     </div> | 
					
						
							|  |  |  |  |     <div class="title"> | 
					
						
							|  |  |  |  |       <div>XXX智慧工地管理平台</div> | 
					
						
							|  |  |  |  |       <div>XXX Smart Construction Stic Management Dashboard</div> | 
					
						
							|  |  |  |  |     </div> | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |     <div class="header_right"> | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |       <div class="top-bar"> | 
					
						
							| 
									
										
										
										
											2025-08-22 22:17:57 +08:00
										 |  |  |  |         <!--  --> | 
					
						
							|  |  |  |  |         <div style="margin-right: 10px;cursor: pointer;" @click="emit('changePage')"> | 
					
						
							|  |  |  |  |            <el-icon size="20"><Expand /></el-icon> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |         <!-- 左侧:天气图标 + 日期文字 --> | 
					
						
							|  |  |  |  |         <div class="left-section"> | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |           <div class="weather-list" @mouseenter="requestPause" @mouseleave="resumeScroll"> | 
					
						
							|  |  |  |  |             <div v-for="(item, i) in weatherList" :key="i" class="weather-item"
 | 
					
						
							|  |  |  |  |               :style="{ transform: `translateY(-${offsetY}px)`, transition: transition }"> | 
					
						
							|  |  |  |  |               <img :src="`../../../src/assets/images/${item.icon}.png`" alt="" /> | 
					
						
							|  |  |  |  |               <div>{{ item.weather }}{{ item.tempMin }}°/{{ item.tempMax }}°</div> | 
					
						
							|  |  |  |  |               <div>{{ item.week }}({{ item.date }})</div> | 
					
						
							|  |  |  |  |             </div> | 
					
						
							|  |  |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |         </div> | 
					
						
							|  |  |  |  |         <!-- 分割线 --> | 
					
						
							|  |  |  |  |         <div class="divider"> | 
					
						
							|  |  |  |  |           <div class="top-block"></div> | 
					
						
							|  |  |  |  |           <div class="bottom-block"></div> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |         <!-- 右侧:管理系统图标 + 文字 --> | 
					
						
							| 
									
										
										
										
											2025-08-22 22:17:57 +08:00
										 |  |  |  |         <div class="right-section"> | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |           <img src="@/assets/large/setting.png" alt="设置图标" /> | 
					
						
							|  |  |  |  |           <span>管理系统</span> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     </div> | 
					
						
							|  |  |  |  |   </div> | 
					
						
							|  |  |  |  | </template> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | <script setup lang="ts"> | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | import { ref, onMounted, onUnmounted } from 'vue'; | 
					
						
							|  |  |  |  | import { getScreenSafetyDay, getScreenWeather } from '@/api/projectScreen'; | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | interface Weather { | 
					
						
							|  |  |  |  |   week: string; | 
					
						
							|  |  |  |  |   date: string; | 
					
						
							|  |  |  |  |   icon: string; | 
					
						
							|  |  |  |  |   weather: string; | 
					
						
							|  |  |  |  |   tempMax: string; | 
					
						
							|  |  |  |  |   tempMin: string; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const props = defineProps({ | 
					
						
							|  |  |  |  |   projectId: { | 
					
						
							|  |  |  |  |     type: String, | 
					
						
							|  |  |  |  |     default: '' | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | }) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const emit = defineEmits(['changePage']) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const safetyDay = ref<number>(0); | 
					
						
							|  |  |  |  | const weatherList = ref<Weather[]>([]) | 
					
						
							|  |  |  |  | const timer = ref<number | null>(0) | 
					
						
							|  |  |  |  | const offsetY = ref<number>(0) | 
					
						
							|  |  |  |  | const curIndex = ref(0) | 
					
						
							|  |  |  |  | const transition = ref('transform 0.5s ease'); | 
					
						
							|  |  |  |  | const pendingPause = ref(false); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /** | 
					
						
							|  |  |  |  |  * 判断当前时间是白天/夜晚 | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | function judgeDayOrNight(sunRise: string, sunSet: string) { | 
					
						
							|  |  |  |  |   // 将 "HH:MM" 格式转为分钟数(便于计算)
 | 
					
						
							|  |  |  |  |   const timeToMinutes = (timeStr: any) => { | 
					
						
							|  |  |  |  |     const [hours, minutes] = timeStr.split(':').map(Number); | 
					
						
							|  |  |  |  |     return isNaN(hours) || isNaN(minutes) ? 0 : hours * 60 + minutes; | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   // 转换日出、日落时间为分钟数
 | 
					
						
							|  |  |  |  |   const sunRiseMinutes = timeToMinutes(sunRise); | 
					
						
							|  |  |  |  |   const sunSetMinutes = timeToMinutes(sunSet); | 
					
						
							|  |  |  |  |   // 获取当前时间并转为分钟数
 | 
					
						
							|  |  |  |  |   const now = new Date(); | 
					
						
							|  |  |  |  |   const currentMinutes = now.getHours() * 60 + now.getMinutes(); | 
					
						
							|  |  |  |  |   // true 白天 false 夜晚
 | 
					
						
							|  |  |  |  |   return currentMinutes >= sunRiseMinutes && currentMinutes <= sunSetMinutes | 
					
						
							|  |  |  |  |     ? true | 
					
						
							|  |  |  |  |     : false; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /** | 
					
						
							|  |  |  |  |  * 设置天气周期滑动 | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | const setWeatherScroll = () => { | 
					
						
							|  |  |  |  |   curIndex.value += 1 | 
					
						
							|  |  |  |  |   transition.value = 'transform 0.3s ease'; | 
					
						
							|  |  |  |  |   offsetY.value = curIndex.value * 60 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (curIndex.value === weatherList.value.length - 1) { | 
					
						
							|  |  |  |  |     setTimeout(() => { | 
					
						
							|  |  |  |  |       transition.value = 'none'; | 
					
						
							|  |  |  |  |       curIndex.value = 0; | 
					
						
							|  |  |  |  |       offsetY.value = 0; | 
					
						
							|  |  |  |  |     }, 350); | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function startScroll() { | 
					
						
							|  |  |  |  |   if (timer.value) clearInterval(timer.value); | 
					
						
							|  |  |  |  |   timer.value = window.setInterval(setWeatherScroll, 5000); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function requestPause() { | 
					
						
							|  |  |  |  |   if(timer.value) { | 
					
						
							|  |  |  |  |     clearInterval(timer.value) | 
					
						
							|  |  |  |  |     timer.value = null | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |   pendingPause.value = true; | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | function resumeScroll() { | 
					
						
							|  |  |  |  |   console.log('resumeScroll') | 
					
						
							|  |  |  |  |   pendingPause.value = false; | 
					
						
							|  |  |  |  |   startScroll(); | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | onMounted(() => { | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 获取安全生产天数 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getScreenSafetyDay(props.projectId).then(res => { | 
					
						
							|  |  |  |  |     const { data, code } = res | 
					
						
							|  |  |  |  |     if (code === 200) { | 
					
						
							|  |  |  |  |       safetyDay.value = data.safetyDay; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }) | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 获取近三天天气 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getScreenWeather(props.projectId).then(res => { | 
					
						
							|  |  |  |  |     const { data, code } = res | 
					
						
							|  |  |  |  |     if (code === 200) { | 
					
						
							|  |  |  |  |       data.forEach(item => { | 
					
						
							|  |  |  |  |         if (judgeDayOrNight(item.sunRise, item.sunSet)) { | 
					
						
							|  |  |  |  |           item.weather = item.dayStatus | 
					
						
							|  |  |  |  |           item.icon = item.dayIcon | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |           item.weather = item.nightStatus | 
					
						
							|  |  |  |  |           item.icon = item.nightIcon | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       weatherList.value = data | 
					
						
							|  |  |  |  |       // 多添加第一项 实现无缝衔接
 | 
					
						
							|  |  |  |  |       weatherList.value = [...weatherList.value, weatherList.value[0]] | 
					
						
							|  |  |  |  |       startScroll() | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | }); | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | onUnmounted(() => { | 
					
						
							|  |  |  |  |   if (timer.value) { | 
					
						
							|  |  |  |  |     clearInterval(timer.value) | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }) | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | </script> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | <style scoped lang="scss"> | 
					
						
							|  |  |  |  | .header { | 
					
						
							|  |  |  |  |   width: 100%; | 
					
						
							|  |  |  |  |   height: 80px; | 
					
						
							|  |  |  |  |   box-sizing: border-box; | 
					
						
							|  |  |  |  |   padding: 10px; | 
					
						
							|  |  |  |  |   display: grid; | 
					
						
							|  |  |  |  |   grid-template-columns: 1fr 1fr 1fr; | 
					
						
							|  |  |  |  |   color: #fff; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | .header_left { | 
					
						
							|  |  |  |  |   display: flex; | 
					
						
							|  |  |  |  |   align-items: center; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   .header_left_img { | 
					
						
							|  |  |  |  |     width: 48px; | 
					
						
							|  |  |  |  |     height: 48px; | 
					
						
							|  |  |  |  |     box-sizing: border-box; | 
					
						
							|  |  |  |  |     // padding-right: 10px;
 | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   .header_left_text { | 
					
						
							|  |  |  |  |     font-weight: 500; | 
					
						
							|  |  |  |  |     text-shadow: 0px 1.24px 6.21px rgba(25, 179, 250, 1); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  | .header_right { | 
					
						
							|  |  |  |  |   width: 100%; | 
					
						
							|  |  |  |  |   height: 100%; | 
					
						
							|  |  |  |  |   display: flex; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | .title { | 
					
						
							|  |  |  |  |   color: #fff; | 
					
						
							|  |  |  |  |   font-family: 'AlimamaShuHeiTi', sans-serif; | 
					
						
							|  |  |  |  |   text-align: center; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | .title>div:first-child { | 
					
						
							|  |  |  |  |   /* 第一个子元素的样式 */ | 
					
						
							|  |  |  |  |   font-size: 38px; | 
					
						
							|  |  |  |  |   letter-spacing: 0.1em; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | .title>div:last-child { | 
					
						
							|  |  |  |  |   /* 最后一个子元素的样式 */ | 
					
						
							|  |  |  |  |   font-size: 14px; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* 顶部栏容器:Flex 水平布局 + 垂直居中 */ | 
					
						
							|  |  |  |  | .top-bar { | 
					
						
							|  |  |  |  |   width: 100%; | 
					
						
							|  |  |  |  |   height: 100%; | 
					
						
							|  |  |  |  |   display: flex; | 
					
						
							|  |  |  |  |   align-items: center; | 
					
						
							|  |  |  |  |   justify-content: flex-end; | 
					
						
							|  |  |  |  |   color: #fff; | 
					
						
							|  |  |  |  |   padding: 8px 16px; | 
					
						
							|  |  |  |  |   font-size: 14px; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* 左侧区域(天气 + 日期):自身也用 Flex 水平排列,确保元素在一行 */ | 
					
						
							|  |  |  |  | .left-section { | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |   height: 100%; | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |   display: flex; | 
					
						
							|  |  |  |  |   align-items: center; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |   .weather-list { | 
					
						
							|  |  |  |  |     height: 60px; | 
					
						
							|  |  |  |  |     overflow: hidden; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     .weather-item { | 
					
						
							|  |  |  |  |       height: 60px; | 
					
						
							|  |  |  |  |       line-height: 60px; | 
					
						
							|  |  |  |  |       display: flex; | 
					
						
							|  |  |  |  |       align-items: center; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       &>div:last-child { | 
					
						
							|  |  |  |  |         margin-left: 10px; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       img { | 
					
						
							|  |  |  |  |         width: 50px; | 
					
						
							|  |  |  |  |         height: 50px; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* 分割线(视觉分隔,可根据需求调整样式) */ | 
					
						
							|  |  |  |  | .divider { | 
					
						
							|  |  |  |  |   display: grid; | 
					
						
							|  |  |  |  |   grid-template-rows: 1fr 1fr; | 
					
						
							| 
									
										
										
										
											2025-08-22 17:47:58 +08:00
										 |  |  |  |   gap: 2px; | 
					
						
							| 
									
										
										
										
											2025-08-21 20:38:44 +08:00
										 |  |  |  |   padding: 14px 10px; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | .divider .top-block { | 
					
						
							|  |  |  |  |   width: 2px; | 
					
						
							|  |  |  |  |   height: 7px; | 
					
						
							|  |  |  |  |   background: #19b5fb; | 
					
						
							|  |  |  |  |   align-self: start; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | .divider .bottom-block { | 
					
						
							|  |  |  |  |   width: 2px; | 
					
						
							|  |  |  |  |   height: 7px; | 
					
						
							|  |  |  |  |   background: #19b5fb; | 
					
						
							|  |  |  |  |   align-self: end; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* 右侧区域(管理系统):图标 + 文字水平排列 */ | 
					
						
							|  |  |  |  | .right-section { | 
					
						
							|  |  |  |  |   display: flex; | 
					
						
							|  |  |  |  |   align-items: center; | 
					
						
							|  |  |  |  |   font-family: 'AlimamaShuHeiTi', sans-serif; | 
					
						
							|  |  |  |  |   font-size: 20px; | 
					
						
							|  |  |  |  |   cursor: pointer; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | .right-section img { | 
					
						
							|  |  |  |  |   width: 20px; | 
					
						
							|  |  |  |  |   height: 20px; | 
					
						
							|  |  |  |  |   margin-right: 6px; | 
					
						
							|  |  |  |  |   /* 图标与文字间距 */ | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | </style> |