|
|
@@ -1,212 +1,239 @@
|
|
|
<template>
|
|
|
- <div class="dashboard-container">
|
|
|
- <!-- 页面头部 -->
|
|
|
+ <div class="cockpit-dashboard" :class="{ 'fullscreen-mode': isFullscreenMode }">
|
|
|
+ <!-- 顶部区域 -->
|
|
|
<header class="dashboard-header">
|
|
|
- <div class="title-section">
|
|
|
- <h1 class="dashboard-title">智慧农业数据看板</h1>
|
|
|
- <p class="dashboard-subtitle">{{ getCurrentFarmDescription() }}</p>
|
|
|
+ <div class="header-left">
|
|
|
+ <div class="title-section">
|
|
|
+ <h1 class="dashboard-title">智慧农业驾驶舱</h1>
|
|
|
+ <p class="dashboard-subtitle">{{ getCurrentFarmDescription() }}</p>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="action-buttons">
|
|
|
- <div class="location-selector-group">
|
|
|
- <el-cascader
|
|
|
- v-model="selectedLocation"
|
|
|
- :options="locationOptions"
|
|
|
- :props="cascaderProps"
|
|
|
- size="medium"
|
|
|
- style="width: 240px"
|
|
|
- placeholder="请选择农场或地块"
|
|
|
- clearable>
|
|
|
- </el-cascader>
|
|
|
+
|
|
|
+ <div class="header-right">
|
|
|
+ <div class="action-buttons">
|
|
|
+ <div class="location-selector-group">
|
|
|
+ <el-cascader
|
|
|
+ v-model="selectedLocation"
|
|
|
+ :options="locationOptions"
|
|
|
+ :props="cascaderProps"
|
|
|
+ size="small"
|
|
|
+ style="width: 200px"
|
|
|
+ placeholder="选择农场/地块"
|
|
|
+ clearable>
|
|
|
+ </el-cascader>
|
|
|
+ <el-button
|
|
|
+ icon="el-icon-check"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ class="btn-confirm"
|
|
|
+ @click="confirmLocationChange"
|
|
|
+ :disabled="!hasLocationChanged">
|
|
|
+ 确认
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
<el-button
|
|
|
- icon="el-icon-check"
|
|
|
- type="primary"
|
|
|
- size="medium"
|
|
|
- @click="confirmLocationChange"
|
|
|
- :disabled="!hasLocationChanged">
|
|
|
- 确认
|
|
|
+ icon="el-icon-refresh"
|
|
|
+ type="success"
|
|
|
+ size="small"
|
|
|
+ class="btn-refresh"
|
|
|
+ @click="refreshData">
|
|
|
+ 刷新
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
- icon="el-icon-close"
|
|
|
- size="medium"
|
|
|
- @click="clearLocation">
|
|
|
- 清空
|
|
|
+ icon="el-icon-full-screen"
|
|
|
+ type="warning"
|
|
|
+ size="small"
|
|
|
+ class="btn-fullscreen"
|
|
|
+ @click="openFullscreenCockpit">
|
|
|
+ 全屏驾驶舱
|
|
|
</el-button>
|
|
|
</div>
|
|
|
- <el-button icon="el-icon-refresh" type="success" size="medium" @click="refreshData">
|
|
|
- 刷新数据
|
|
|
- </el-button>
|
|
|
- <el-button icon="el-icon-monitor" type="warning" size="medium" @click="goToDigitalDashboard" style="font-weight: bold;">
|
|
|
- 🚀 农业数字化决策大屏
|
|
|
- </el-button>
|
|
|
</div>
|
|
|
</header>
|
|
|
|
|
|
- <!-- 数据概览卡片 -->
|
|
|
- <div class="overview-section">
|
|
|
- <div class="overview-grid">
|
|
|
- <div class="overview-card" v-for="(item, index) in overviewData" :key="index">
|
|
|
- <div class="card-icon" :style="{ backgroundColor: item.color }">
|
|
|
- <i :class="item.icon"></i>
|
|
|
+ <!-- 主体区域 -->
|
|
|
+ <div class="dashboard-body">
|
|
|
+ <!-- 左侧面板 -->
|
|
|
+ <div class="dashboard-left">
|
|
|
+ <!-- 农机数量统计 -->
|
|
|
+ <div class="panel-card">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3 class="panel-title">葡萄生长状况</h3>
|
|
|
+ <el-radio-group v-model="cropTimeRange" size="mini">
|
|
|
+ <el-radio-button label="week">本周</el-radio-button>
|
|
|
+ <el-radio-button label="month">本月</el-radio-button>
|
|
|
+ <el-radio-button label="season">本季</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
</div>
|
|
|
- <div class="card-content">
|
|
|
- <div class="card-value">{{ item.value }}</div>
|
|
|
- <div class="card-label">{{ item.label }}</div>
|
|
|
- <div class="card-change" :class="item.trend">
|
|
|
- <i :class="item.trend === 'up' ? 'el-icon-top' : 'el-icon-bottom'"></i>
|
|
|
- {{ item.change }}
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="chart-wrapper">
|
|
|
+ <div id="cropStatusChart"></div>
|
|
|
+ </div>
|
|
|
+ <div class="chart-legend-mini">
|
|
|
+ <div class="legend-item-mini" v-for="item in getCurrentCropLegend()" :key="item.name">
|
|
|
+ <span class="legend-dot" :style="{ background: item.color }"></span>
|
|
|
+ <span class="legend-text">{{ item.name }} ({{ item.value }}亩)</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 主要内容区域 -->
|
|
|
- <div class="main-content">
|
|
|
- <!-- 农场分布地图 -->
|
|
|
- <div class="content-section">
|
|
|
- <div class="section-header">
|
|
|
- <h2 class="section-title">农场分布图</h2>
|
|
|
- </div>
|
|
|
- <div class="map-container">
|
|
|
- <div id="farmMap"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 环境监测区域 -->
|
|
|
- <div class="content-section">
|
|
|
- <div class="section-header">
|
|
|
- <h2 class="section-title">环境监测</h2>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="environment-grid">
|
|
|
- <div class="env-card">
|
|
|
- <div class="env-header">
|
|
|
- <span class="env-title">温度监测</span>
|
|
|
- <span class="env-value">26.5°C</span>
|
|
|
- </div>
|
|
|
- <div class="env-chart" id="temperatureChart"></div>
|
|
|
- <div class="env-status normal">正常</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="env-card">
|
|
|
- <div class="env-header">
|
|
|
- <span class="env-title">湿度监测</span>
|
|
|
- <span class="env-value">68%</span>
|
|
|
+ <!-- 设备运行状态 -->
|
|
|
+ <div class="panel-card">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3 class="panel-title">设备运行状态</h3>
|
|
|
+ <div class="device-summary-mini">
|
|
|
+ <span>总: {{ deviceStats.total }}</span>
|
|
|
+ <span class="online-text">在线: {{ deviceStats.online }}</span>
|
|
|
</div>
|
|
|
- <div class="env-chart" id="humidityChart"></div>
|
|
|
- <div class="env-status normal">正常</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="env-card">
|
|
|
- <div class="env-header">
|
|
|
- <span class="env-title">土壤监测</span>
|
|
|
- <span class="env-value">良好</span>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="chart-wrapper">
|
|
|
+ <div id="deviceStatusChart"></div>
|
|
|
</div>
|
|
|
- <div class="env-chart" id="soilChart"></div>
|
|
|
- <div class="env-status good">良好</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="env-card">
|
|
|
- <div class="env-header">
|
|
|
- <span class="env-title">光照监测</span>
|
|
|
- <span class="env-value">85%</span>
|
|
|
+ <div class="device-stats-mini">
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-label">在线率</span>
|
|
|
+ <span class="stat-value success">{{ Math.round(deviceStats.online / deviceStats.total * 100) }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-label">离线</span>
|
|
|
+ <span class="stat-value danger">{{ deviceStats.offline }}台</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="env-chart" id="lightChart"></div>
|
|
|
- <div class="env-status normal">正常</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 葡萄与设备状态 -->
|
|
|
- <div class="content-row">
|
|
|
- <div class="content-card half-width">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">葡萄生长状况</h3>
|
|
|
- <div class="time-range">
|
|
|
- <el-radio-group v-model="cropTimeRange" size="mini">
|
|
|
- <el-radio-button label="week">本周</el-radio-button>
|
|
|
- <el-radio-button label="month">本月</el-radio-button>
|
|
|
- <el-radio-button label="season">本季</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="chart-container">
|
|
|
- <div id="cropStatusChart"></div>
|
|
|
+ <!-- 环境监测 -->
|
|
|
+ <div class="panel-card">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3 class="panel-title">环境监测</h3>
|
|
|
</div>
|
|
|
- <div class="chart-legend">
|
|
|
- <div class="legend-item" v-for="item in getCurrentCropLegend()" :key="item.name">
|
|
|
- <span class="legend-color" :style="{ background: item.color }"></span>
|
|
|
- {{ item.name }} ({{ item.value }}亩)
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="env-grid-mini">
|
|
|
+ <div class="env-item">
|
|
|
+ <div class="env-name">温度</div>
|
|
|
+ <div class="env-value-text">26.5°C</div>
|
|
|
+ <div class="env-chart-mini" id="temperatureChart"></div>
|
|
|
+ </div>
|
|
|
+ <div class="env-item">
|
|
|
+ <div class="env-name">湿度</div>
|
|
|
+ <div class="env-value-text">68%</div>
|
|
|
+ <div class="env-chart-mini" id="humidityChart"></div>
|
|
|
+ </div>
|
|
|
+ <div class="env-item">
|
|
|
+ <div class="env-name">土壤</div>
|
|
|
+ <div class="env-value-text">良好</div>
|
|
|
+ <div class="env-chart-mini" id="soilChart"></div>
|
|
|
+ </div>
|
|
|
+ <div class="env-item">
|
|
|
+ <div class="env-name">光照</div>
|
|
|
+ <div class="env-value-text">85%</div>
|
|
|
+ <div class="env-chart-mini" id="lightChart"></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="content-card half-width">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">设备运行状态</h3>
|
|
|
- <div class="device-summary">
|
|
|
- <span class="device-count">总设备: {{ deviceStats.total }}</span>
|
|
|
- <span class="device-online">在线: {{ deviceStats.online }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="chart-container">
|
|
|
- <div id="deviceStatusChart"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 中央地图区域 -->
|
|
|
+ <div class="dashboard-center">
|
|
|
+ <div class="map-wrapper">
|
|
|
+ <div class="map-header">
|
|
|
+ <h3 class="map-title">农场地图</h3>
|
|
|
</div>
|
|
|
- <div class="device-stats">
|
|
|
- <div class="stats-item">
|
|
|
- <span class="stats-label">在线率</span>
|
|
|
- <span class="stats-value">{{ Math.round(deviceStats.online / deviceStats.total * 100) }}%</span>
|
|
|
- </div>
|
|
|
- <div class="stats-item">
|
|
|
- <span class="stats-label">离线设备</span>
|
|
|
- <span class="stats-value offline">{{ deviceStats.offline }}</span>
|
|
|
- </div>
|
|
|
+ <div class="map-container-full">
|
|
|
+ <div id="farmMap"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 生产数据与告警信息 -->
|
|
|
- <div class="content-row">
|
|
|
- <div class="content-card half-width">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">生产数据统计</h3>
|
|
|
- <div class="time-range">
|
|
|
- <el-radio-group v-model="productionTimeRange" size="mini">
|
|
|
- <el-radio-button label="month">本月</el-radio-button>
|
|
|
- <el-radio-button label="quarter">本季</el-radio-button>
|
|
|
- <el-radio-button label="year">本年</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- </div>
|
|
|
+ <!-- 右侧面板 -->
|
|
|
+ <div class="dashboard-right">
|
|
|
+ <!-- 生产数据统计 -->
|
|
|
+ <div class="panel-card">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3 class="panel-title">生产数据统计</h3>
|
|
|
+ <el-radio-group v-model="productionTimeRange" size="mini">
|
|
|
+ <el-radio-button label="month">月</el-radio-button>
|
|
|
+ <el-radio-button label="quarter">季</el-radio-button>
|
|
|
+ <el-radio-button label="year">年</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
</div>
|
|
|
- <div class="chart-container">
|
|
|
- <div id="productionChart"></div>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="chart-wrapper">
|
|
|
+ <div id="productionChart"></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="content-card half-width">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">系统告警</h3>
|
|
|
- <el-badge :value="alertCount" class="alert-badge" type="warning">
|
|
|
- <span class="alert-text">待处理告警</span>
|
|
|
+
|
|
|
+ <!-- 系统告警 -->
|
|
|
+ <div class="panel-card">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3 class="panel-title">系统告警</h3>
|
|
|
+ <el-badge :value="alertCount" class="badge-mini" type="danger">
|
|
|
+ <span class="badge-text">待处理</span>
|
|
|
</el-badge>
|
|
|
</div>
|
|
|
- <div class="alert-list">
|
|
|
- <div class="alert-item" v-for="alert in alertList" :key="alert.id" :class="alert.level">
|
|
|
- <div class="alert-icon">
|
|
|
- <i :class="getAlertIcon(alert.level)"></i>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="alert-list-mini">
|
|
|
+ <div class="alert-item-mini" v-for="alert in alertList" :key="alert.id" :class="alert.level">
|
|
|
+ <div class="alert-icon-mini">
|
|
|
+ <i :class="getAlertIcon(alert.level)"></i>
|
|
|
+ </div>
|
|
|
+ <div class="alert-info">
|
|
|
+ <div class="alert-title-mini">{{ alert.title }}</div>
|
|
|
+ <div class="alert-time-mini">{{ alert.time }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="alert-status-mini" :class="alert.status">
|
|
|
+ {{ getAlertStatusText(alert.status) }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="alert-content">
|
|
|
- <div class="alert-title">{{ alert.title }}</div>
|
|
|
- <div class="alert-time">{{ alert.time }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 农事任务执行进度 -->
|
|
|
+ <div class="panel-card">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3 class="panel-title">农事任务执行进度</h3>
|
|
|
+ </div>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="task-progress-wrapper">
|
|
|
+ <div class="task-chart-container">
|
|
|
+ <div id="taskProgressChart"></div>
|
|
|
</div>
|
|
|
- <div class="alert-status" :class="alert.status">
|
|
|
- {{ getAlertStatusText(alert.status) }}
|
|
|
+ <div class="task-stats-grid">
|
|
|
+ <div class="task-stat-item">
|
|
|
+ <div class="stat-value total">{{ taskStats.total }}</div>
|
|
|
+ <div class="stat-label">总任务数</div>
|
|
|
+ </div>
|
|
|
+ <div class="task-stat-item">
|
|
|
+ <div class="stat-value completed">{{ taskStats.completed }}</div>
|
|
|
+ <div class="stat-label">已完成</div>
|
|
|
+ </div>
|
|
|
+ <div class="task-stat-item">
|
|
|
+ <div class="stat-value pending">{{ taskStats.pending }}</div>
|
|
|
+ <div class="stat-label">待执行</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 底部汇总数据卡片 - 浮在地图上方 -->
|
|
|
+ <div class="dashboard-summary-bottom">
|
|
|
+ <div class="overview-card-mini" v-for="(item, index) in overviewData" :key="index">
|
|
|
+ <div class="card-icon-mini" :style="{ backgroundColor: item.color }">
|
|
|
+ <i :class="item.icon"></i>
|
|
|
+ </div>
|
|
|
+ <div class="card-info">
|
|
|
+ <div class="card-value-mini">{{ item.value }}</div>
|
|
|
+ <div class="card-label-mini">{{ item.label }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -225,6 +252,17 @@ export default {
|
|
|
|
|
|
alertCount: 5,
|
|
|
|
|
|
+ // 农事任务统计数据
|
|
|
+ taskStats: {
|
|
|
+ total: 12, // 总任务数
|
|
|
+ completed: 9, // 已完成
|
|
|
+ pending: 3, // 待执行
|
|
|
+ completionRate: 75 // 完成率
|
|
|
+ },
|
|
|
+
|
|
|
+ // 全屏模式标志
|
|
|
+ isFullscreenMode: false,
|
|
|
+
|
|
|
// 级联选择器配置
|
|
|
cascaderProps: {
|
|
|
checkStrictly: true, // 允许选择任意一级
|
|
|
@@ -373,6 +411,9 @@ export default {
|
|
|
// 初始化确认的位置为默认状态(全部农场)
|
|
|
this.confirmedLocation = [];
|
|
|
|
|
|
+ // 检测是否为全屏模式
|
|
|
+ this.isFullscreenMode = this.$route.query.fullscreen === 'true';
|
|
|
+
|
|
|
this.$nextTick(() => {
|
|
|
// 等待页面完全渲染后再初始化图表
|
|
|
this.waitForElementsAndInit();
|
|
|
@@ -435,6 +476,14 @@ export default {
|
|
|
window.open(`${baseUrl}/agri-digital/dashboard`, '_blank');
|
|
|
},
|
|
|
|
|
|
+ openFullscreenCockpit() {
|
|
|
+ // 打开全屏驾驶舱
|
|
|
+ const baseUrl = window.location.origin;
|
|
|
+ const currentPath = this.$route.path;
|
|
|
+ // 添加 fullscreen=true 参数
|
|
|
+ window.open(`${baseUrl}${currentPath}?fullscreen=true`, '_blank');
|
|
|
+ },
|
|
|
+
|
|
|
getCurrentFarmDescription() {
|
|
|
const currentSelection = this.getCurrentSelection();
|
|
|
|
|
|
@@ -873,6 +922,7 @@ export default {
|
|
|
// 初始化其他图表
|
|
|
this.initEnvironmentCharts();
|
|
|
this.initProductionChart();
|
|
|
+ this.initTaskProgressChart();
|
|
|
this.initFarmMap();
|
|
|
},
|
|
|
|
|
|
@@ -921,6 +971,7 @@ export default {
|
|
|
this.initCropStatusChart();
|
|
|
this.initDeviceStatusChart();
|
|
|
this.initProductionChart();
|
|
|
+ this.initTaskProgressChart();
|
|
|
this.initFarmMap();
|
|
|
|
|
|
console.log('Charts initialization completed');
|
|
|
@@ -1170,6 +1221,73 @@ export default {
|
|
|
});
|
|
|
},
|
|
|
|
|
|
+ initTaskProgressChart() {
|
|
|
+ const chartElement = document.getElementById('taskProgressChart');
|
|
|
+ if (!chartElement) {
|
|
|
+ console.error('taskProgressChart element not found');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const chart = this.$echarts.init(chartElement);
|
|
|
+ chart.setOption({
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ formatter: '{b}: {c}个 ({d}%)'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ series: [{
|
|
|
+ name: '任务进度',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['50%', '75%'],
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'center',
|
|
|
+ formatter: '{d}%',
|
|
|
+ fontSize: 24,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ color: '#52c41a'
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ fontSize: 28,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ value: this.taskStats.completed,
|
|
|
+ name: '已完成',
|
|
|
+ itemStyle: {
|
|
|
+ color: {
|
|
|
+ type: 'linear',
|
|
|
+ x: 0, y: 0, x2: 0, y2: 1,
|
|
|
+ colorStops: [
|
|
|
+ { offset: 0, color: '#52c41a' },
|
|
|
+ { offset: 1, color: '#73d13d' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: this.taskStats.pending,
|
|
|
+ name: '待执行',
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(255, 255, 255, 0.15)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }]
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
initFarmMap() {
|
|
|
const mapElement = document.getElementById('farmMap');
|
|
|
if (!mapElement) {
|
|
|
@@ -1185,13 +1303,20 @@ export default {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 初始化高德地图
|
|
|
+ // 初始化高德地图 - 使用纯卫星影像图层(无路网)
|
|
|
+ // 图层说明:
|
|
|
+ // - TileLayer.Satellite: 卫星影像底图(纯影像,无道路标注)
|
|
|
+ // 如需添加路网图层,可添加: new AMap.TileLayer.RoadNet()
|
|
|
+ // 如需切换回矢量地图,可使用 mapStyle: 'amap://styles/whitesmoke' 并移除 layers 配置
|
|
|
this.farmMap = new AMap.Map('farmMap', {
|
|
|
zoom: 10,
|
|
|
center: [117.015029, 34.197274], // 默认中心点
|
|
|
- mapStyle: 'amap://styles/whitesmoke', // 清新风格
|
|
|
viewMode: '2D',
|
|
|
- resizeEnable: true
|
|
|
+ resizeEnable: true,
|
|
|
+ layers: [
|
|
|
+ new AMap.TileLayer.Satellite() // 卫星影像底图(纯影像)
|
|
|
+ // new AMap.TileLayer.RoadNet() // 路网图层(已注释,如需显示道路名称可取消注释)
|
|
|
+ ]
|
|
|
});
|
|
|
|
|
|
// 等待地图加载完成
|
|
|
@@ -2472,480 +2597,654 @@ export default {
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-.dashboard-container {
|
|
|
- padding: 24px;
|
|
|
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
|
|
- min-height: 100vh;
|
|
|
+// ============ 驾驶舱整体布局 ============
|
|
|
+.cockpit-dashboard {
|
|
|
+ width: 100%;
|
|
|
+ height: 100vh;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #001528;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ color: #fff;
|
|
|
+
|
|
|
+ &.fullscreen-mode {
|
|
|
+ // 全屏模式下可以进一步定制样式
|
|
|
+ .dashboard-header {
|
|
|
+ height: 80px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+// ============ 头部区域 ============
|
|
|
.dashboard-header {
|
|
|
+ height: 100px;
|
|
|
+ background: linear-gradient(180deg, #002140 0%, #001528 100%);
|
|
|
+ border-bottom: 2px solid rgba(24, 144, 255, 0.3);
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: flex-start;
|
|
|
- margin-bottom: 32px;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 20px;
|
|
|
+ gap: 20px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
|
|
|
|
- .title-section {
|
|
|
- .dashboard-title {
|
|
|
- font-size: 32px;
|
|
|
- font-weight: 700;
|
|
|
- color: #1f2937;
|
|
|
- margin: 0 0 8px 0;
|
|
|
- background: linear-gradient(135deg, #10b981, #059669);
|
|
|
- -webkit-background-clip: text;
|
|
|
- -webkit-text-fill-color: transparent;
|
|
|
- background-clip: text;
|
|
|
- }
|
|
|
+ .header-left {
|
|
|
+ flex: 1;
|
|
|
|
|
|
- .dashboard-subtitle {
|
|
|
- font-size: 16px;
|
|
|
- color: #6b7280;
|
|
|
- margin: 0;
|
|
|
+ .title-section {
|
|
|
+ .dashboard-title {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #fff;
|
|
|
+ margin: 0 0 4px 0;
|
|
|
+ background: linear-gradient(135deg, #1890ff, #36cfc9);
|
|
|
+ -webkit-background-clip: text;
|
|
|
+ -webkit-text-fill-color: transparent;
|
|
|
+ background-clip: text;
|
|
|
+ text-shadow: 0 0 20px rgba(24, 144, 255, 0.5);
|
|
|
+ }
|
|
|
+
|
|
|
+ .dashboard-subtitle {
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(255, 255, 255, 0.65);
|
|
|
+ margin: 0;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .action-buttons {
|
|
|
- display: flex;
|
|
|
- gap: 12px;
|
|
|
- align-items: center;
|
|
|
+ .header-right {
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
|
- .location-selector-group {
|
|
|
+ .action-buttons {
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
align-items: center;
|
|
|
- padding: 8px;
|
|
|
- background: #f8fafc;
|
|
|
- border-radius: 8px;
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
|
|
|
- .el-cascader {
|
|
|
- background: white;
|
|
|
+ .location-selector-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 6px;
|
|
|
+ align-items: center;
|
|
|
+ padding: 6px 10px;
|
|
|
+ background: rgba(24, 144, 255, 0.08);
|
|
|
+ border-radius: 6px;
|
|
|
+ border: 1px solid rgba(24, 144, 255, 0.3);
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.overview-section {
|
|
|
- margin-bottom: 32px;
|
|
|
-
|
|
|
- .overview-grid {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(4, 1fr);
|
|
|
- gap: 24px;
|
|
|
- }
|
|
|
-
|
|
|
- .overview-card {
|
|
|
- background: white;
|
|
|
- border-radius: 16px;
|
|
|
- padding: 24px;
|
|
|
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
|
- transition: all 0.3s ease;
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- transform: translateY(-4px);
|
|
|
- box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.15);
|
|
|
- }
|
|
|
-
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 16px;
|
|
|
-
|
|
|
- .card-icon {
|
|
|
- width: 64px;
|
|
|
- height: 64px;
|
|
|
- border-radius: 16px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- color: white;
|
|
|
- font-size: 24px;
|
|
|
- }
|
|
|
-
|
|
|
- .card-content {
|
|
|
- flex: 1;
|
|
|
|
|
|
- .card-value {
|
|
|
- font-size: 28px;
|
|
|
- font-weight: 700;
|
|
|
- color: #1f2937;
|
|
|
- line-height: 1;
|
|
|
- margin-bottom: 4px;
|
|
|
+ // ============ 自定义按钮样式 ============
|
|
|
+
|
|
|
+ // 确认按钮 - 青绿色
|
|
|
+ .btn-confirm {
|
|
|
+ background: #00C48F !important;
|
|
|
+ border-color: #00C48F !important;
|
|
|
+ color: #ffffff !important;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover:not(:disabled) {
|
|
|
+ background: #00E5A0 !important;
|
|
|
+ border-color: #00E5A0 !important;
|
|
|
+ box-shadow: 0 0 12px rgba(0, 196, 143, 0.5);
|
|
|
+ transform: translateY(-1px);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active:not(:disabled) {
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:disabled {
|
|
|
+ background: rgba(0, 196, 143, 0.3) !important;
|
|
|
+ border-color: rgba(0, 196, 143, 0.3) !important;
|
|
|
+ color: rgba(255, 255, 255, 0.5) !important;
|
|
|
+ cursor: not-allowed;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .card-label {
|
|
|
- font-size: 14px;
|
|
|
- color: #6b7280;
|
|
|
- margin-bottom: 8px;
|
|
|
+ // 刷新按钮 - 科技蓝
|
|
|
+ .btn-refresh {
|
|
|
+ background: #409EFF !important;
|
|
|
+ border-color: #409EFF !important;
|
|
|
+ color: #ffffff !important;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #66b1ff !important;
|
|
|
+ border-color: #66b1ff !important;
|
|
|
+ box-shadow: 0 0 12px rgba(64, 158, 255, 0.5);
|
|
|
+ transform: translateY(-1px);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .card-change {
|
|
|
- font-size: 12px;
|
|
|
- font-weight: 600;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
+ // 全屏驾驶舱按钮 - 高亮蓝色
|
|
|
+ .btn-fullscreen {
|
|
|
+ background: linear-gradient(135deg, #2E65F3 0%, #00A6FF 100%) !important;
|
|
|
+ border-color: transparent !important;
|
|
|
+ color: #ffffff !important;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ box-shadow: 0 2px 8px rgba(46, 101, 243, 0.3);
|
|
|
|
|
|
- &.up {
|
|
|
- color: #10b981;
|
|
|
+ &:hover {
|
|
|
+ background: linear-gradient(135deg, #4578ff 0%, #1eb8ff 100%) !important;
|
|
|
+ box-shadow: 0 4px 16px rgba(46, 101, 243, 0.6);
|
|
|
+ transform: translateY(-1px);
|
|
|
}
|
|
|
|
|
|
- &.down {
|
|
|
- color: #ef4444;
|
|
|
+ &:active {
|
|
|
+ transform: translateY(0);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.main-content {
|
|
|
- .content-section {
|
|
|
- margin-bottom: 32px;
|
|
|
+// ============ 主体区域 ============
|
|
|
+.dashboard-body {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 12px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ // 左侧面板
|
|
|
+ .dashboard-left {
|
|
|
+ width: 320px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ overflow-y: auto;
|
|
|
|
|
|
- .section-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 20px;
|
|
|
-
|
|
|
- .section-title {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
- margin: 0;
|
|
|
- }
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: rgba(24, 144, 255, 0.3);
|
|
|
+ border-radius: 2px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .content-row {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: 1fr 1fr;
|
|
|
- gap: 24px;
|
|
|
- margin-bottom: 32px;
|
|
|
+ // 中央地图区域
|
|
|
+ .dashboard-center {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
- .content-card {
|
|
|
- background: white;
|
|
|
- border-radius: 16px;
|
|
|
- padding: 24px;
|
|
|
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
-
|
|
|
- &.half-width {
|
|
|
- width: 100%;
|
|
|
+ // 右侧面板
|
|
|
+ .dashboard-right {
|
|
|
+ width: 320px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ overflow-y: auto;
|
|
|
+
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: rgba(24, 144, 255, 0.3);
|
|
|
+ border-radius: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ============ 面板卡片通用样式 ============
|
|
|
+.panel-card {
|
|
|
+ background: rgba(0, 33, 64, 0.6);
|
|
|
+ border: 1px solid rgba(24, 144, 255, 0.3);
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+
|
|
|
+ .panel-header {
|
|
|
+ background: rgba(24, 144, 255, 0.1);
|
|
|
+ border-bottom: 1px solid rgba(24, 144, 255, 0.3);
|
|
|
+ padding: 10px 16px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .panel-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1890ff;
|
|
|
+ margin: 0;
|
|
|
+ text-shadow: 0 0 10px rgba(24, 144, 255, 0.5);
|
|
|
}
|
|
|
|
|
|
- .card-header {
|
|
|
+ .device-summary-mini {
|
|
|
+ font-size: 11px;
|
|
|
+ color: rgba(255, 255, 255, 0.65);
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 20px;
|
|
|
-
|
|
|
- .card-title {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
- margin: 0;
|
|
|
- }
|
|
|
+ gap: 8px;
|
|
|
|
|
|
- .device-summary {
|
|
|
- display: flex;
|
|
|
- gap: 16px;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- .device-count {
|
|
|
- color: #6b7280;
|
|
|
- }
|
|
|
-
|
|
|
- .device-online {
|
|
|
- color: #10b981;
|
|
|
- font-weight: 600;
|
|
|
- }
|
|
|
+ .online-text {
|
|
|
+ color: #52c41a;
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ .panel-content {
|
|
|
+ padding: 12px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ============ 图表容器 ============
|
|
|
+.chart-wrapper {
|
|
|
+ height: 180px;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ #cropStatusChart,
|
|
|
+ #deviceStatusChart,
|
|
|
+ #productionChart {
|
|
|
+ width: 100% !important;
|
|
|
+ height: 100% !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.chart-legend-mini {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ gap: 8px;
|
|
|
+ margin-top: 10px;
|
|
|
+
|
|
|
+ .legend-item-mini {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ font-size: 11px;
|
|
|
+ color: rgba(255, 255, 255, 0.85);
|
|
|
+
|
|
|
+ .legend-dot {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ border-radius: 50%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
|
|
|
- .chart-container {
|
|
|
- height: 300px;
|
|
|
- width: 100%;
|
|
|
- min-height: 300px;
|
|
|
-
|
|
|
- #cropStatusChart,
|
|
|
- #deviceStatusChart,
|
|
|
- #productionChart {
|
|
|
- width: 100% !important;
|
|
|
- height: 100% !important;
|
|
|
- min-height: 300px;
|
|
|
- }
|
|
|
+ .legend-text {
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.environment-grid {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(4, 1fr);
|
|
|
- gap: 20px;
|
|
|
+// 设备统计
|
|
|
+.device-stats-mini {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ margin-top: 10px;
|
|
|
+ padding-top: 10px;
|
|
|
+ border-top: 1px solid rgba(24, 144, 255, 0.2);
|
|
|
|
|
|
- .env-card {
|
|
|
- background: white;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 20px;
|
|
|
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
-
|
|
|
- .env-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 16px;
|
|
|
+ .stat-item {
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ display: block;
|
|
|
+ font-size: 11px;
|
|
|
+ color: rgba(255, 255, 255, 0.65);
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-value {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
|
|
|
- .env-title {
|
|
|
- font-size: 14px;
|
|
|
- color: #6b7280;
|
|
|
- font-weight: 500;
|
|
|
+ &.success {
|
|
|
+ color: #52c41a;
|
|
|
}
|
|
|
|
|
|
- .env-value {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 700;
|
|
|
- color: #1f2937;
|
|
|
+ &.danger {
|
|
|
+ color: #ff4d4f;
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 环境监测网格
|
|
|
+.env-grid-mini {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ .env-item {
|
|
|
+ background: rgba(24, 144, 255, 0.05);
|
|
|
+ border: 1px solid rgba(24, 144, 255, 0.2);
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 10px;
|
|
|
+ text-align: center;
|
|
|
|
|
|
- .env-chart {
|
|
|
- height: 80px;
|
|
|
- margin-bottom: 16px;
|
|
|
+ .env-name {
|
|
|
+ font-size: 11px;
|
|
|
+ color: rgba(255, 255, 255, 0.65);
|
|
|
+ margin-bottom: 4px;
|
|
|
}
|
|
|
|
|
|
- .env-status {
|
|
|
- text-align: center;
|
|
|
- padding: 6px 12px;
|
|
|
- border-radius: 20px;
|
|
|
- font-size: 12px;
|
|
|
+ .env-value-text {
|
|
|
+ font-size: 14px;
|
|
|
font-weight: 600;
|
|
|
-
|
|
|
- &.normal {
|
|
|
- background: #d1fae5;
|
|
|
- color: #065f46;
|
|
|
- }
|
|
|
-
|
|
|
- &.good {
|
|
|
- background: #dbeafe;
|
|
|
- color: #1e40af;
|
|
|
- }
|
|
|
-
|
|
|
- &.warning {
|
|
|
- background: #fef3c7;
|
|
|
- color: #92400e;
|
|
|
- }
|
|
|
+ color: #1890ff;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .env-chart-mini {
|
|
|
+ height: 40px;
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.alert-list {
|
|
|
- max-height: 300px;
|
|
|
+// ============ 告警列表 ============
|
|
|
+.alert-list-mini {
|
|
|
+ max-height: 320px;
|
|
|
overflow-y: auto;
|
|
|
|
|
|
- .alert-item {
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: rgba(24, 144, 255, 0.3);
|
|
|
+ border-radius: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-item-mini {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 12px;
|
|
|
- padding: 16px;
|
|
|
- border-bottom: 1px solid #f3f4f6;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ border-bottom: 1px solid rgba(24, 144, 255, 0.1);
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
|
&:hover {
|
|
|
- background: #f9fafb;
|
|
|
+ background: rgba(24, 144, 255, 0.08);
|
|
|
}
|
|
|
|
|
|
&:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
- .alert-icon {
|
|
|
- width: 32px;
|
|
|
- height: 32px;
|
|
|
- border-radius: 8px;
|
|
|
+ .alert-icon-mini {
|
|
|
+ width: 28px;
|
|
|
+ height: 28px;
|
|
|
+ border-radius: 6px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
+ font-size: 14px;
|
|
|
color: white;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
- &.warning .alert-icon {
|
|
|
- background: #f59e0b;
|
|
|
+ &.warning .alert-icon-mini {
|
|
|
+ background: #faad14;
|
|
|
}
|
|
|
|
|
|
- &.error .alert-icon {
|
|
|
- background: #ef4444;
|
|
|
+ &.error .alert-icon-mini {
|
|
|
+ background: #ff4d4f;
|
|
|
}
|
|
|
|
|
|
- &.info .alert-icon {
|
|
|
- background: #3b82f6;
|
|
|
+ &.info .alert-icon-mini {
|
|
|
+ background: #1890ff;
|
|
|
}
|
|
|
|
|
|
- .alert-content {
|
|
|
+ .alert-info {
|
|
|
flex: 1;
|
|
|
+ min-width: 0;
|
|
|
|
|
|
- .alert-title {
|
|
|
- font-size: 14px;
|
|
|
- color: #1f2937;
|
|
|
+ .alert-title-mini {
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(255, 255, 255, 0.85);
|
|
|
font-weight: 500;
|
|
|
- margin-bottom: 4px;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
- .alert-time {
|
|
|
- font-size: 12px;
|
|
|
- color: #6b7280;
|
|
|
+ .alert-time-mini {
|
|
|
+ font-size: 10px;
|
|
|
+ color: rgba(255, 255, 255, 0.45);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .alert-status {
|
|
|
- font-size: 12px;
|
|
|
- padding: 4px 8px;
|
|
|
- border-radius: 12px;
|
|
|
+ .alert-status-mini {
|
|
|
+ font-size: 10px;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 10px;
|
|
|
font-weight: 500;
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
|
&.pending {
|
|
|
- background: #fef3c7;
|
|
|
- color: #92400e;
|
|
|
+ background: rgba(250, 173, 20, 0.2);
|
|
|
+ color: #faad14;
|
|
|
}
|
|
|
|
|
|
&.processing {
|
|
|
- background: #dbeafe;
|
|
|
- color: #1e40af;
|
|
|
+ background: rgba(24, 144, 255, 0.2);
|
|
|
+ color: #1890ff;
|
|
|
}
|
|
|
|
|
|
&.resolved {
|
|
|
- background: #d1fae5;
|
|
|
- color: #065f46;
|
|
|
+ background: rgba(82, 196, 26, 0.2);
|
|
|
+ color: #52c41a;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.map-container {
|
|
|
- background: white;
|
|
|
- border-radius: 16px;
|
|
|
- padding: 24px;
|
|
|
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
- height: 400px;
|
|
|
-
|
|
|
- #farmMap {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
+.badge-mini {
|
|
|
+ .badge-text {
|
|
|
+ color: rgba(255, 255, 255, 0.65);
|
|
|
+ font-size: 11px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.chart-legend {
|
|
|
+// ============ 地图区域 ============
|
|
|
+.map-wrapper {
|
|
|
+ height: 100%;
|
|
|
display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 16px;
|
|
|
- margin-top: 16px;
|
|
|
- justify-content: center;
|
|
|
+ flex-direction: column;
|
|
|
+ background: rgba(0, 33, 64, 0.6);
|
|
|
+ border: 1px solid rgba(24, 144, 255, 0.3);
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
|
|
|
- .legend-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 6px;
|
|
|
- font-size: 12px;
|
|
|
- color: #6b7280;
|
|
|
+ .map-header {
|
|
|
+ background: rgba(24, 144, 255, 0.1);
|
|
|
+ border-bottom: 1px solid rgba(24, 144, 255, 0.3);
|
|
|
+ padding: 10px 16px;
|
|
|
|
|
|
- .legend-color {
|
|
|
- width: 12px;
|
|
|
- height: 12px;
|
|
|
- border-radius: 2px;
|
|
|
+ .map-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1890ff;
|
|
|
+ margin: 0;
|
|
|
+ text-shadow: 0 0 10px rgba(24, 144, 255, 0.5);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .map-container-full {
|
|
|
+ flex: 1;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ #farmMap {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: #0a1929;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.device-stats {
|
|
|
+// ============ 底部汇总数据卡片 ============
|
|
|
+.dashboard-summary-bottom {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 24px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ z-index: 2;
|
|
|
display: flex;
|
|
|
- justify-content: space-around;
|
|
|
- margin-top: 16px;
|
|
|
- padding-top: 16px;
|
|
|
- border-top: 1px solid #f3f4f6;
|
|
|
+ gap: 16px;
|
|
|
+ max-width: calc(100% - 680px); // 左右各留出340px空间(面板320px + 间距20px)
|
|
|
+ pointer-events: auto;
|
|
|
|
|
|
- .stats-item {
|
|
|
- text-align: center;
|
|
|
+ .overview-card-mini {
|
|
|
+ background: rgba(0, 33, 64, 0.85);
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+ border: 1px solid rgba(24, 144, 255, 0.4);
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 14px 18px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ min-width: 160px;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
|
|
|
|
- .stats-label {
|
|
|
- display: block;
|
|
|
- font-size: 12px;
|
|
|
- color: #6b7280;
|
|
|
- margin-bottom: 4px;
|
|
|
+ &:hover {
|
|
|
+ background: rgba(0, 33, 64, 0.95);
|
|
|
+ border-color: rgba(24, 144, 255, 0.6);
|
|
|
+ box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);
|
|
|
+ transform: translateY(-2px);
|
|
|
}
|
|
|
|
|
|
- .stats-value {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: 600;
|
|
|
- color: #10b981;
|
|
|
+ .card-icon-mini {
|
|
|
+ width: 44px;
|
|
|
+ height: 44px;
|
|
|
+ border-radius: 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 20px;
|
|
|
+ color: #fff;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-info {
|
|
|
+ flex: 1;
|
|
|
|
|
|
- &.offline {
|
|
|
- color: #ef4444;
|
|
|
+ .card-value-mini {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #fff;
|
|
|
+ line-height: 1;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-label-mini {
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(255, 255, 255, 0.75);
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.alert-badge {
|
|
|
- .alert-text {
|
|
|
- color: #6b7280;
|
|
|
- font-size: 14px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 响应式设计
|
|
|
-@media (max-width: 1200px) {
|
|
|
- .overview-grid {
|
|
|
- grid-template-columns: repeat(2, 1fr);
|
|
|
- }
|
|
|
-
|
|
|
- .environment-grid {
|
|
|
- grid-template-columns: repeat(2, 1fr);
|
|
|
+// 响应式:小屏幕下调整卡片布局
|
|
|
+@media (max-width: 1400px) {
|
|
|
+ .dashboard-summary-bottom {
|
|
|
+ max-width: calc(100% - 100px);
|
|
|
+ flex-wrap: wrap;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ .overview-card-mini {
|
|
|
+ min-width: 140px;
|
|
|
+ padding: 12px 14px;
|
|
|
+
|
|
|
+ .card-icon-mini {
|
|
|
+ width: 38px;
|
|
|
+ height: 38px;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-value-mini {
|
|
|
+ font-size: 18px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-label-mini {
|
|
|
+ font-size: 11px !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
- .dashboard-container {
|
|
|
- padding: 16px;
|
|
|
- }
|
|
|
+// ============ 农事任务执行进度 ============
|
|
|
+.task-progress-wrapper {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
|
|
|
- .dashboard-header {
|
|
|
- flex-direction: column;
|
|
|
- gap: 16px;
|
|
|
+ .task-chart-container {
|
|
|
+ height: 140px;
|
|
|
+ width: 100%;
|
|
|
|
|
|
- .title-section .dashboard-title {
|
|
|
- font-size: 24px;
|
|
|
+ #taskProgressChart {
|
|
|
+ width: 100% !important;
|
|
|
+ height: 100% !important;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ .task-stats-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 8px;
|
|
|
|
|
|
- .action-buttons {
|
|
|
- flex-direction: column;
|
|
|
- width: 100%;
|
|
|
- gap: 16px;
|
|
|
+ .task-stat-item {
|
|
|
+ text-align: center;
|
|
|
+ padding: 8px;
|
|
|
+ background: rgba(24, 144, 255, 0.05);
|
|
|
+ border: 1px solid rgba(24, 144, 255, 0.15);
|
|
|
+ border-radius: 6px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(24, 144, 255, 0.1);
|
|
|
+ border-color: rgba(24, 144, 255, 0.3);
|
|
|
+ }
|
|
|
|
|
|
- .location-selector-group {
|
|
|
- flex-direction: column;
|
|
|
- width: 100%;
|
|
|
+ .stat-value {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 1;
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
|
- .el-cascader {
|
|
|
- width: 100% !important;
|
|
|
+ &.total {
|
|
|
+ color: #1890ff;
|
|
|
}
|
|
|
|
|
|
- .el-button {
|
|
|
- width: 100%;
|
|
|
+ &.completed {
|
|
|
+ color: #52c41a;
|
|
|
}
|
|
|
+
|
|
|
+ &.pending {
|
|
|
+ color: #faad14;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ font-size: 11px;
|
|
|
+ color: rgba(255, 255, 255, 0.65);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- .overview-grid,
|
|
|
- .environment-grid {
|
|
|
- grid-template-columns: 1fr;
|
|
|
- }
|
|
|
-
|
|
|
- .content-row {
|
|
|
- grid-template-columns: 1fr;
|
|
|
- }
|
|
|
}
|
|
|
</style>
|
|
|
|