Header.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <div class="top-nav">
  3. <div class="nav-title">中国安全生产科学研究院实验室安全智慧化管控中心</div>
  4. <div class="nav-tabs">
  5. <div
  6. v-for="tab in tabs"
  7. :key="tab.path"
  8. class="nav-tab"
  9. :class="{ active: currentPath === tab.path }"
  10. @click="handleTabClick(tab)"
  11. >{{ tab.label }}</div>
  12. </div>
  13. <div class="nav-right">
  14. <span class="weather">{{ weather }}</span>
  15. <span class="weekday">{{ dateStr }}</span>
  16. <span class="clock">{{ timeStr }}</span>
  17. </div>
  18. </div>
  19. </template>
  20. <script>
  21. import { getWeather } from '@/api'
  22. export default {
  23. name: 'ScreenHeader',
  24. data() {
  25. return {
  26. tabs: [
  27. { label: '实验室情况', path: '/lab-status' },
  28. { label: '视频监控', path: '/video-monitor' }
  29. ],
  30. dateStr: '',
  31. timeStr: '',
  32. weather: '北京 · 晴 18°C'
  33. }
  34. },
  35. computed: {
  36. currentPath() {
  37. return this.$route.path
  38. }
  39. },
  40. mounted() {
  41. this.updateTime()
  42. this.timer = setInterval(this.updateTime, 1000)
  43. this.fetchWeather()
  44. },
  45. beforeDestroy() {
  46. clearInterval(this.timer)
  47. },
  48. methods: {
  49. updateTime() {
  50. const now = new Date()
  51. const pad = n => String(n).padStart(2, '0')
  52. const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  53. this.dateStr = weekDays[now.getDay()]
  54. this.timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
  55. },
  56. handleTabClick(tab) {
  57. if (this.$route.path !== tab.path) {
  58. this.$router.push(tab.path)
  59. }
  60. },
  61. async fetchWeather() {
  62. try {
  63. const res = await getWeather()
  64. const d = res.data
  65. this.weather = `☁ ${d.city} · ${d.weather} ${d.temp}°C`
  66. } catch (e) {
  67. this.weather = '☁ 北京 · 晴 18°C'
  68. }
  69. }
  70. }
  71. }
  72. </script>
  73. <style lang="scss" scoped>
  74. .top-nav {
  75. width: 100%;
  76. height: $header-height;
  77. display: flex;
  78. align-items: center;
  79. justify-content: space-between;
  80. padding: 0 30px;
  81. background: linear-gradient(180deg, rgba(10,30,70,0.95) 0%, rgba(6,20,50,0.75) 100%);
  82. border-bottom: 1px solid $border-color;
  83. position: relative;
  84. z-index: 100;
  85. // 底部静态渐变线
  86. &::after {
  87. content: '';
  88. position: absolute;
  89. bottom: 0;
  90. left: 10%;
  91. right: 10%;
  92. height: 1px;
  93. background: linear-gradient(90deg, transparent, $accent, transparent);
  94. }
  95. // 底部流光
  96. &::before {
  97. content: '';
  98. position: absolute;
  99. bottom: -1px;
  100. left: 0;
  101. width: 120px;
  102. height: 2px;
  103. background: linear-gradient(90deg, transparent, $accent, rgba(72,215,255,0.8), transparent);
  104. animation: navFlow 4s linear infinite;
  105. z-index: 101;
  106. }
  107. }
  108. @keyframes navFlow {
  109. 0% { left: -120px; }
  110. 100% { left: calc(100% + 120px); }
  111. }
  112. .nav-title {
  113. font-size: 26px;
  114. font-weight: 700;
  115. letter-spacing: 6px;
  116. background: linear-gradient(135deg, #fff 0%, $accent 100%);
  117. -webkit-background-clip: text;
  118. -webkit-text-fill-color: transparent;
  119. text-shadow: 0 0 30px rgba(72,215,255,0.3);
  120. animation: titleGlow 3s ease-in-out infinite;
  121. }
  122. @keyframes titleGlow {
  123. 0%, 100% { filter: drop-shadow(0 0 6px rgba(72,215,255,0.2)); }
  124. 50% { filter: drop-shadow(0 0 16px rgba(72,215,255,0.5)); }
  125. }
  126. .nav-tabs {
  127. display: flex;
  128. gap: 4px;
  129. }
  130. .nav-tab {
  131. padding: 8px 28px;
  132. border-radius: 4px;
  133. cursor: pointer;
  134. font-size: 15px;
  135. letter-spacing: 2px;
  136. transition: all 0.3s;
  137. border: 1px solid transparent;
  138. color: $text-secondary;
  139. background: transparent;
  140. &:hover {
  141. color: $accent;
  142. border-color: $border-color;
  143. }
  144. &.active {
  145. background: linear-gradient(135deg, rgba(72,215,255,0.18), rgba(58,123,255,0.12));
  146. border-color: $accent;
  147. color: #fff;
  148. box-shadow: 0 0 16px rgba(72,215,255,0.2);
  149. animation: tabGlow 2.5s ease-in-out infinite;
  150. }
  151. }
  152. @keyframes tabGlow {
  153. 0%, 100% { box-shadow: 0 0 10px rgba(72,215,255,0.15); }
  154. 50% { box-shadow: 0 0 22px rgba(72,215,255,0.35), inset 0 0 10px rgba(72,215,255,0.08); }
  155. }
  156. .nav-right {
  157. display: flex;
  158. align-items: center;
  159. gap: 20px;
  160. font-size: 14px;
  161. color: $text-secondary;
  162. .clock {
  163. font-size: 22px;
  164. font-weight: 600;
  165. color: $accent;
  166. letter-spacing: 2px;
  167. animation: clockPulse 2s ease-in-out infinite;
  168. }
  169. }
  170. @keyframes clockPulse {
  171. 0%, 100% { text-shadow: 0 0 6px rgba(72,215,255,0.3); }
  172. 50% { text-shadow: 0 0 14px rgba(72,215,255,0.6), 0 0 28px rgba(72,215,255,0.2); }
  173. }
  174. </style>