Files
td_official/src/views/ProjectScreen/components/header.vue
2025-08-22 17:47:58 +08:00

302 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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">
{{ safetyDay }}
<span style="font-size: 12px"></span>
</div>
</div>
<div class="title">
<div>XXX智慧工地管理平台</div>
<div>XXX Smart Construction Stic Management Dashboard</div>
</div>
<div class="header_right">
<div class="top-bar">
<!-- 左侧天气图标 + 日期文字 -->
<div class="left-section">
<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>
</div>
<!-- 分割线 -->
<div class="divider">
<div class="top-block"></div>
<div class="bottom-block"></div>
</div>
<!-- 右侧管理系统图标 + 文字 -->
<div class="right-section" @click="emit('changePage')">
<img src="@/assets/large/setting.png" alt="设置图标" />
<span>管理系统</span>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { getScreenSafetyDay, getScreenWeather } from '@/api/projectScreen';
interface Weather {
week: string;
date: string;
icon: string;
weather: string;
tempMax: string;
tempMin: string;
}
const props = defineProps({
projectId: {
type: String,
default: ''
}
})
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);
}
}
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
}
pendingPause.value = true;
}
function resumeScroll() {
console.log('resumeScroll')
pendingPause.value = false;
startScroll();
}
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()
}
})
});
onUnmounted(() => {
if (timer.value) {
clearInterval(timer.value)
}
})
</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);
}
}
.header_right {
width: 100%;
height: 100%;
display: flex;
}
.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 {
height: 100%;
display: flex;
align-items: center;
.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;
}
}
}
}
/* 分割线(视觉分隔,可根据需求调整样式) */
.divider {
display: grid;
grid-template-rows: 1fr 1fr;
gap: 2px;
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>