Files
nxxmdata/datav/src/App.vue
2025-09-12 19:58:35 +08:00

1087 lines
19 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.

<script setup>
import { ref } from 'vue'
import Home from './components/Home.vue'
import Alert from './components/Alert.vue'
// 当前激活的页面
const currentPage = ref('home')
// 页面组件映射
const pageComponents = {
home: Home,
alert: Alert
}
// 切换页面函数
const switchPage = (page) => {
currentPage.value = page
}
</script>
<template>
<div class="dashboard">
<!-- 顶部标题栏 -->
<header class="dashboard-header">
<!-- 装饰性背景元素 -->
<div class="header-decoration">
<div class="deco-line deco-line-1"></div>
<div class="deco-line deco-line-2"></div>
<div class="deco-line deco-line-3"></div>
<div class="deco-corner deco-corner-left"></div>
<div class="deco-corner deco-corner-right"></div>
<div class="deco-diamond deco-diamond-1"></div>
<div class="deco-diamond deco-diamond-2"></div>
<div class="deco-glow deco-glow-1"></div>
<div class="deco-glow deco-glow-2"></div>
</div>
<nav class="header-nav">
<div class="nav-item" :class="{ active: currentPage === 'home' }" @click="switchPage('home')">
<span>首页</span>
</div>
<div class="nav-item" :class="{ active: currentPage === 'alert' }" @click="switchPage('alert')">
<span>预警监测</span>
</div>
</nav>
<div class="title-container">
<div class="title-decoration-left"></div>
<h1>宁夏智慧畜牧业大数据平台</h1>
<div class="title-decoration-right"></div>
</div>
</header>
<!-- 主体内容区域 -->
<main class="dashboard-main">
<!-- 动态组件加载 -->
<component :is="pageComponents[currentPage]" />
</main>
</div>
</template>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: auto; /* 允许滚动以防止内容被截断 */
}
body {
font-family: 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;
background: url('/data/map/bg.jpg') no-repeat center center fixed;
background-size: cover;
}
#app {
width: 100vw;
min-height: 100vh; /* 使用 min-height 而不是固定高度 */
height: 100%;
}
.dashboard {
width: 100%;
min-height: 100vh; /* 使用 min-height 以适应不同屏幕尺寸 */
display: flex;
flex-direction: column;
background: url('/data/map/bg.jpg') no-repeat center center fixed;
background-size: cover;
color: #ffffff;
position: relative;
}
.dashboard::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(12, 20, 38, 0.7); /* 半透明深色遮罩 */
z-index: 0;
}
.dashboard > * {
position: relative;
z-index: 1;
}
.dashboard-header {
height: 80px;
background: rgba(12, 20, 38, 0.9);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 40px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
border-bottom: 2px solid #00d4ff;
position: relative;
overflow: hidden;
}
/* 装饰性背景元素 */
.header-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 1;
}
/* 装饰线条 */
.deco-line {
position: absolute;
background: #00d4ff;
height: 1px;
animation: lineFlow 3s ease-in-out infinite;
opacity: 0.6;
}
.deco-line-1 {
top: 15px;
left: 0;
width: 200px;
animation-delay: 0s;
}
.deco-line-2 {
top: 25px;
right: 0;
width: 150px;
animation-delay: 1s;
}
.deco-line-3 {
bottom: 15px;
left: 50%;
transform: translateX(-50%);
width: 300px;
animation-delay: 2s;
}
/* 装饰角落 */
.deco-corner {
position: absolute;
width: 40px;
height: 40px;
border: 2px solid #00d4ff;
opacity: 0.6;
}
.deco-corner-left {
top: 10px;
left: 20px;
border-right: none;
border-bottom: none;
animation: cornerPulse 2s ease-in-out infinite;
}
.deco-corner-right {
top: 10px;
right: 20px;
border-left: none;
border-bottom: none;
animation: cornerPulse 2s ease-in-out infinite 1s;
}
/* 装饰菱形 */
.deco-diamond {
position: absolute;
width: 8px;
height: 8px;
background: #00d4ff;
transform: rotate(45deg);
animation: diamondFloat 4s ease-in-out infinite;
}
.deco-diamond-1 {
top: 20px;
left: 100px;
animation-delay: 0.5s;
}
.deco-diamond-2 {
bottom: 20px;
right: 100px;
animation-delay: 2.5s;
}
/* 发光效果 */
.deco-glow {
position: absolute;
width: 60px;
height: 60px;
border-radius: 50%;
background: rgba(0, 212, 255, 0.3);
animation: glowPulse 3s ease-in-out infinite;
}
.deco-glow-1 {
top: -30px;
left: 200px;
animation-delay: 0s;
}
.deco-glow-2 {
bottom: -30px;
right: 200px;
animation-delay: 1.5s;
}
.header-nav {
display: flex;
gap: 0;
align-items: center;
}
.nav-item {
padding: 12px 24px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(0, 212, 255, 0.3);
cursor: pointer;
transition: all 0.3s ease;
color: #ffffff;
font-size: 14px;
font-weight: 500;
border-right: none;
}
.nav-item:first-child {
border-radius: 6px 0 0 6px;
}
.nav-item:last-child {
border-radius: 0 6px 6px 0;
border-right: 1px solid rgba(0, 212, 255, 0.3);
}
.nav-item:hover {
background: rgba(0, 212, 255, 0.2);
border-color: #00d4ff;
}
.nav-item.active {
background: #00d4ff;
border-color: #00d4ff;
color: #ffffff;
box-shadow: 0 2px 8px rgba(0, 212, 255, 0.3);
}
/* 标题容器 */
.title-container {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 20px;
z-index: 2;
}
.title-container h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
color: #ffffff;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), 0 0 10px rgba(0, 212, 255, 0.3);
letter-spacing: 2px;
position: relative;
}
/* 标题装饰元素 */
.title-decoration-left,
.title-decoration-right {
width: 80px;
height: 2px;
background: #00d4ff;
position: relative;
animation: titleLineFlow 2s ease-in-out infinite;
opacity: 0.8;
}
.title-decoration-left::before,
.title-decoration-right::before {
content: '';
position: absolute;
top: -3px;
width: 8px;
height: 8px;
background: #00d4ff;
border-radius: 50%;
box-shadow: 0 0 10px #00d4ff;
animation: titleDotMove 2s ease-in-out infinite;
}
.title-decoration-left::before {
left: 0;
}
.title-decoration-right::before {
right: 0;
}
.title-decoration-left::after,
.title-decoration-right::after {
content: '';
position: absolute;
top: -1px;
width: 20px;
height: 4px;
background: #00d4ff;
animation: titleAccent 3s ease-in-out infinite;
opacity: 0.6;
}
.title-decoration-left::after {
right: -10px;
transform: skew(-20deg);
}
.title-decoration-right::after {
left: -10px;
transform: skew(20deg);
}
/* 动画关键帧 */
@keyframes lineFlow {
0%, 100% {
opacity: 0.3;
transform: scaleX(0.8);
}
50% {
opacity: 1;
transform: scaleX(1.2);
}
}
@keyframes cornerPulse {
0%, 100% {
opacity: 0.6;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.1);
}
}
@keyframes diamondFloat {
0%, 100% {
transform: rotate(45deg) translateY(0px);
opacity: 0.7;
}
50% {
transform: rotate(45deg) translateY(-5px);
opacity: 1;
}
}
@keyframes glowPulse {
0%, 100% {
opacity: 0.3;
transform: scale(0.8);
}
50% {
opacity: 0.8;
transform: scale(1.2);
}
}
@keyframes titleLineFlow {
0%, 100% {
opacity: 0.7;
transform: scaleX(0.9);
}
50% {
opacity: 1;
transform: scaleX(1.1);
}
}
@keyframes titleDotMove {
0%, 100% {
opacity: 0.8;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.3);
}
}
@keyframes titleAccent {
0%, 100% {
opacity: 0.5;
transform: scaleY(0.8);
}
50% {
opacity: 1;
transform: scaleY(1.2);
}
}
.dashboard-main {
flex: 1;
position: relative;
padding: 0;
min-height: 0;
}
.dashboard-left,
.dashboard-right {
position: fixed;
top: 80px; /* 紧贴顶部标题栏 */
width: 580px; /* 增加侧边栏宽度 */
height: calc(100vh - 80px); /* 全屏高度减去顶部标题栏高度 */
max-height: calc(100vh - 80px); /* 限制最大高度 */
display: flex;
flex-direction: column;
/* gap: 10px; */
z-index: 10; /* 确保在地图之上 */
padding: 10px;
overflow-y: auto; /* 允许垂直滚动 */
overflow-x: hidden; /* 隐藏水平滚动条 */
pointer-events: auto; /* 确保侧边栏可以交互 */
background: rgba(12, 20, 38, 0.8);
/* background: url('/data/map/1.png') no-repeat center center; */
/* background-size: cover; */
}
.dashboard-left {
left: 10px;
}
.dashboard-right {
right: 20px;
}
.dashboard-center {
position: fixed;
top: 80px; /* 顶部标题栏高度 */
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: calc(100vh - 80px);
min-height: calc(100vh - 80px); /* 确保最小高度 */
z-index: 1; /* 地图作为底层 */
pointer-events: auto;
margin: 0;
padding: 0;
}
.map-container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: transparent; /* 移除背景,让地图自然显示 */
border: none;
box-shadow: none;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.panel {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 20px;
border: 1px solid rgba(0, 212, 255, 0.4);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 20px rgba(0, 212, 255, 0.15);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
position: relative;
}
.panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: #00d4ff;
border-radius: 12px 12px 0 0;
opacity: 0.6;
}
.panel h3 {
font-size: 18px;
/* margin-bottom: 8px; */
color: #00d4ff;
border-bottom: 2px solid #00d4ff;
/* padding-bottom: 8px; */
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.stat-item {
background: rgba(0, 0, 0, 0.3);
padding: 15px;
border-radius: 8px;
text-align: center;
border: 1px solid rgba(0, 212, 255, 0.2);
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #00ff88;
margin-bottom: 5px;
}
.stat-label {
font-size: 12px;
color: #cccccc;
}
.alert-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.alert-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.2);
}
.alert-item.warning {
border-left: 4px solid #ff6b35;
}
.alert-item.normal {
border-left: 4px solid #00ff88;
}
.alert-text {
font-size: 14px;
}
.chart-placeholder {
min-height: 200px;
}
.chart-item {
text-align: center;
}
.chart-item h4 {
margin-bottom: 15px;
color: #ffffff;
}
.chart-mock {
height: 150px;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
border: 1px solid rgba(0, 212, 255, 0.2);
}
.env-data {
display: flex;
flex-direction: column;
gap: 12px;
}
.env-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: rgba(0, 0, 0, 0.2);
border-radius: 6px;
border: 1px solid rgba(0, 212, 255, 0.1);
}
.env-label {
color: #cccccc;
font-size: 14px;
}
.env-value {
color: #84acf0;
font-weight: bold;
font-size: 14px;
}
/* 响应式设计 */
/* 缩放优化 - 针对浏览器缩放90%情况 */
@media screen and (min-resolution: 110dpi), screen and (-webkit-min-device-pixel-ratio: 1.1) {
.dashboard-header h1 {
font-size: 26px; /* 90%缩放时适度增大标题字体 */
}
.panel h3 {
font-size: 19px; /* 增大面板标题字体 */
}
/* 传感器状态样式 */
.sensor-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.sensor-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
border: 1px solid rgba(0, 212, 255, 0.2);
}
.sensor-name {
color: #ffffff;
font-size: 14px;
}
.sensor-status.online {
color: #ffffff;
font-size: 12px;
font-weight: 500;
}
.sensor-status.offline {
color: #84acf0;
font-size: 12px;
font-weight: 500;
}
/* 设备列表样式 */
.device-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.device-item {
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
border: 1px solid rgba(0, 212, 255, 0.2);
}
.device-name {
color: #ffffff;
font-size: 14px;
margin-bottom: 6px;
}
.device-status.online {
color: #ffffff;
font-size: 12px;
}
.device-status.offline {
color: #84acf0;
font-size: 12px;
}
/* 预警规则样式 */
.rule-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.rule-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
border: 1px solid rgba(0, 212, 255, 0.2);
}
.rule-name {
color: #ffffff;
font-size: 14px;
}
.rule-threshold {
color: #00d4ff;
font-size: 12px;
font-weight: 500;
}
/* 预警详情样式 */
.alert-details {
display: flex;
flex-direction: column;
gap: 15px;
max-height: 500px;
overflow-y: auto;
}
.alert-detail-item {
padding: 16px;
border-radius: 8px;
border-left: 4px solid;
background: rgba(255, 255, 255, 0.05);
}
.alert-detail-item.high {
border-left-color: #ffffff;
background: rgba(255, 255, 255, 0.1);
}
.alert-detail-item.medium {
border-left-color: #84acf0;
background: rgba(132, 172, 240, 0.1);
}
.alert-detail-item.low {
border-left-color: #336699;
background: rgba(51, 102, 153, 0.1);
}
.alert-time {
color: #888;
font-size: 12px;
margin-bottom: 6px;
}
.alert-title {
color: #ffffff;
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
}
.alert-desc {
color: #cccccc;
font-size: 14px;
margin-bottom: 10px;
line-height: 1.4;
}
.alert-level {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.alert-detail-item.high .alert-level {
background: #ffffff;
color: #000000;
}
.alert-detail-item.medium .alert-level {
background: #84acf0;
color: #ffffff;
}
.alert-detail-item.low .alert-level {
background: #336699;
color: #ffffff;
}
/* 处理记录样式 */
.handle-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.handle-item {
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
border: 1px solid rgba(0, 212, 255, 0.2);
}
.handle-time {
color: #888;
font-size: 12px;
margin-bottom: 6px;
}
.handle-action {
color: #ffffff;
font-size: 14px;
margin-bottom: 6px;
}
.handle-status.resolved {
color: #00ff88;
font-size: 12px;
}
.handle-status.pending {
color: #ffaa00;
font-size: 12px;
}
.data-item {
padding: 16px; /* 适度增加内边距 */
}
.data-value {
font-size: 24px; /* 增大数据值字体 */
}
.data-label {
font-size: 15px; /* 增大标签字体 */
}
.env-label,
.env-value {
font-size: 15px; /* 增大环境数据字体 */
}
.dashboard-left,
.dashboard-right {
gap: 22px; /* 适度增加间距 */
}
.panel {
padding: 22px; /* 适度增加面板内边距 */
}
}
/* 缩放优化 - 针对浏览器缩放75%等情况 */
@media screen and (min-resolution: 120dpi), screen and (-webkit-min-device-pixel-ratio: 1.25) {
.dashboard-header h1 {
font-size: 28px; /* 缩放时增大标题字体 */
}
.panel h3 {
font-size: 20px; /* 增大面板标题字体 */
}
.data-item {
padding: 18px; /* 增加内边距 */
}
.data-value {
font-size: 26px; /* 增大数据值字体 */
}
.data-label {
font-size: 16px; /* 增大标签字体 */
}
.env-label,
.env-value {
font-size: 16px; /* 增大环境数据字体 */
}
.dashboard-left,
.dashboard-right {
gap: 25px; /* 增加间距 */
}
.panel {
padding: 25px; /* 增加面板内边距 */
}
}
/* 通用缩放优化 - 适配75%、90%等缩放比例 */
@media screen {
html {
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
text-size-adjust: 100%;
}
.dashboard-main {
min-width: 1200px;
}
.map-container {
aspect-ratio: 16/9;
max-aspect-ratio: 2/1;
}
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
/* 针对90%缩放的特殊优化 */
/* @supports (zoom: 0.9) {
@media screen and (min-width: 1400px) {
.dashboard-main {
transform-origin: center top;
}
.map-container {
min-height: 380px;
}
.dashboard-left,
.dashboard-right {
min-width: 280px;
}
}
} */
/* 大屏优化 */
/* @media (min-width: 1920px) {
.dashboard-left,
.dashboard-right {
width: 390px;
max-width: 420px;
}
.map-container {
min-height: 420px;
max-width: 1000px;
max-height: 720px;
}
.dashboard-header h1 {
font-size: 32px;
}
.dashboard-main {
max-width: 2200px;
margin: 0 auto;
}
} */
/* 超超大屏优化如8K显示器 */
/* @media (min-width: 3840px) {
.dashboard-main {
max-width: 2600px;
padding: 40px;
}
.map-container {
max-width: 1300px;
max-height: 750px;
}
.dashboard-left,
.dashboard-right {
width: 400px;
max-width: 450px;
}
} */
@media (max-width: 1400px) {
.dashboard-left,
.dashboard-right {
width: 280px;
}
}
@media (max-width: 1200px) {
.dashboard-main {
flex-direction: column;
}
.dashboard-left,
.dashboard-right {
width: 100%;
flex-direction: row;
}
.dashboard-center {
order: -1;
height: 60vh;
}
.map-container {
min-height: 400px; /* 小屏下减少最小高度 */
}
}
/* 针对非全屏模式的优化 */
@media screen and (max-height: 900px) {
.dashboard-left,
.dashboard-right {
overflow-y: auto; /* 允许垂直滚动 */
padding: 8px; /* 减少内边距 */
}
.panel {
margin-bottom: 8px; /* 减少面板间距 */
}
}
/* 小高度屏幕适配 */
@media screen and (max-height: 768px) {
.dashboard-header {
height: 60px; /* 减少标题栏高度 */
padding: 0 20px;
}
.dashboard-header h1 {
font-size: 20px; /* 减小标题字体 */
}
.dashboard-left,
.dashboard-right {
top: 60px;
height: calc(100vh - 60px);
padding: 5px;
}
.dashboard-center {
top: 60px;
height: calc(100vh - 60px);
}
}
/* 极小高度屏幕适配 */
@media screen and (max-height: 600px) {
.dashboard-header {
height: 50px;
}
.dashboard-left,
.dashboard-right {
top: 50px;
height: calc(100vh - 50px);
width: 250px; /* 减少侧边栏宽度 */
}
.dashboard-center {
top: 50px;
height: calc(100vh - 50px);
}
}
</style>