|
@@ -0,0 +1,832 @@
|
|
|
|
+<template>
|
|
|
|
+ <div class="radar-monitoring-screen">
|
|
|
|
+ <!-- 顶部区域 -->
|
|
|
|
+ <div class="header">
|
|
|
|
+ <div class="system-name">雷能社区智慧大屏</div>
|
|
|
|
+ <div class="running-days">已安全守护 {{ runningDays }} 天</div>
|
|
|
|
+ <div class="time-info">{{ currentTime }}</div>
|
|
|
|
+ <div class="data-flow header-flow"></div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 内容区域 -->
|
|
|
|
+ <div class="content-area">
|
|
|
|
+ <!-- 左侧面板 -->
|
|
|
|
+ <div class="panel">
|
|
|
|
+ <div class="panel-title">今日监测概览</div>
|
|
|
|
+ <div class="panel-content">
|
|
|
|
+ <div class="data-grid">
|
|
|
|
+ <DataCard title="检测到人数" :value="todayData.detectedCount" />
|
|
|
|
+ <DataCard title="长者活跃度" :value="`${todayData.activeRate}%`" />
|
|
|
|
+ <DataCard title="跌倒统计次数" :value="todayData.alarmCount" />
|
|
|
|
+ <DataCard title="告警统计次数" :value="todayData.fallingCount" />
|
|
|
|
+ <DataCard
|
|
|
|
+ title="设备在线率"
|
|
|
|
+ :value="`${((todayData.onlineCount / todayData.deviceCount) * 100).toFixed(2)}%`"
|
|
|
|
+ :change="`${todayData.onlineCount} / ${todayData.deviceCount} 台`"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="panel-title" style="margin-top: 15px">设备分布情况</div>
|
|
|
|
+ <div class="chart-container" ref="deviceChartRef"></div>
|
|
|
|
+
|
|
|
|
+ <div class="panel-title">设备年龄层次</div>
|
|
|
|
+ <div class="chart-container" ref="ageChartRef"></div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="data-flow"></div>
|
|
|
|
+ <div class="glow-effect"></div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 中间面板 -->
|
|
|
|
+ <div class="panel center-panel">
|
|
|
|
+ <div class="map-container">
|
|
|
|
+ <img class="map-img" src="../../assets/img/map.jpg" alt="小区地图" />
|
|
|
|
+ <!-- <div class="map-label">雷能小区</div> -->
|
|
|
|
+ <div class="map-label building-1">1号楼</div>
|
|
|
|
+ <div class="map-label building-2">2号楼</div>
|
|
|
|
+ <div class="map-label building-3">物业中心</div>
|
|
|
|
+ <div class="map-label building-4">3号楼</div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 右侧面板 -->
|
|
|
|
+ <div class="panel">
|
|
|
|
+ <div class="panel-title" style="margin-top: 15px">检测对象分布</div>
|
|
|
|
+ <div class="chart-container" ref="objectChartRef"></div>
|
|
|
|
+
|
|
|
|
+ <div class="panel-title" style="margin-top: 15px">历史告警统计</div>
|
|
|
|
+ <div class="chart-container" ref="alarmHistoryRef"></div>
|
|
|
|
+
|
|
|
|
+ <div class="panel-title" style="margin-top: 15px">历史跌倒统计</div>
|
|
|
|
+ <div class="chart-container" ref="fallingHistoryRef"></div>
|
|
|
|
+ <div class="data-flow"></div>
|
|
|
|
+ <div class="glow-effect"></div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 底部区域 -->
|
|
|
|
+ <!-- <div class="footer">
|
|
|
|
+ <div>智慧养老雷达监控系统 © 2023 版本 v2.5.1 | 技术支持:400-123-4567</div>
|
|
|
|
+ </div> -->
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
|
+import * as echarts from 'echarts'
|
|
|
|
+import DataCard from '../dataCard/index.vue'
|
|
|
|
+import { type TodayData } from '../../types'
|
|
|
|
+
|
|
|
|
+defineOptions({
|
|
|
|
+ name: 'ScreenPage',
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// 响应式数据
|
|
|
|
+const currentTime = ref('')
|
|
|
|
+const runningDays = ref(328)
|
|
|
|
+const todayData = ref<TodayData>({
|
|
|
|
+ deviceCount: 30,
|
|
|
|
+ onlineCount: 25,
|
|
|
|
+ systemGuardDay: 328,
|
|
|
|
+ alarmCount: 3,
|
|
|
|
+ fallingCount: 1,
|
|
|
|
+ detectedCount: 142,
|
|
|
|
+ activeRate: 78,
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// 历史告警统计数据(最近7天)
|
|
|
|
+const alarmHistoryData = ref({
|
|
|
|
+ dates: ['1月1日', '1月2日', '1月3日', '1月4日', '1月5日', '1月6日', '1月7日'],
|
|
|
|
+ values: [3, 5, 2, 7, 4, 6, 3],
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// 历史跌倒统计数据(最近7天)
|
|
|
|
+const fallingHistoryData = ref({
|
|
|
|
+ dates: ['1月1日', '1月2日', '1月3日', '1月4日', '1月5日', '1月6日', '1月7日'],
|
|
|
|
+ values: [1, 2, 0, 1, 3, 1, 0],
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// 图表引用
|
|
|
|
+const deviceChartRef = ref<HTMLElement | null>(null)
|
|
|
|
+const ageChartRef = ref<HTMLElement | null>(null)
|
|
|
|
+const objectChartRef = ref<HTMLElement | null>(null)
|
|
|
|
+const alarmHistoryRef = ref<HTMLElement | null>(null)
|
|
|
|
+const fallingHistoryRef = ref<HTMLElement | null>(null)
|
|
|
|
+
|
|
|
|
+let deviceChart: echarts.ECharts | null = null
|
|
|
|
+let ageChart: echarts.ECharts | null = null
|
|
|
|
+let objectChart: echarts.ECharts | null = null
|
|
|
|
+let alarmHistoryChart: echarts.ECharts | null = null
|
|
|
|
+let fallingHistoryChart: echarts.ECharts | null = null
|
|
|
|
+const updateInterval: ReturnType<typeof setInterval> | null = null
|
|
|
|
+
|
|
|
|
+// 更新时间
|
|
|
|
+const updateTime = () => {
|
|
|
|
+ const now = new Date()
|
|
|
|
+ currentTime.value = now.toLocaleString('zh-CN', {
|
|
|
|
+ year: 'numeric',
|
|
|
|
+ month: '2-digit',
|
|
|
|
+ day: '2-digit',
|
|
|
|
+ hour: '2-digit',
|
|
|
|
+ minute: '2-digit',
|
|
|
|
+ second: '2-digit',
|
|
|
|
+ hour12: false,
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 初始化图表
|
|
|
|
+const initCharts = () => {
|
|
|
|
+ if (!deviceChartRef.value) return
|
|
|
|
+
|
|
|
|
+ // 设备分布情况图表(安装位置)
|
|
|
|
+ deviceChart = echarts.init(deviceChartRef.value)
|
|
|
|
+
|
|
|
|
+ deviceChart.setOption({
|
|
|
|
+ grid: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)',
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: '安装位置',
|
|
|
|
+ type: 'pie',
|
|
|
|
+ radius: ['20%', '70%'],
|
|
|
|
+ roseType: 'area',
|
|
|
|
+ itemStyle: {
|
|
|
|
+ borderColor: '#0a0e17',
|
|
|
|
+ borderWidth: 2,
|
|
|
|
+ },
|
|
|
|
+ label: {
|
|
|
|
+ show: true,
|
|
|
|
+ formatter: '{b}: {c}',
|
|
|
|
+ color: '#fff', // 设置文字颜色为白色
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
+ textBorderColor: 'rgba(0, 0, 0, 0.8)', // 添加黑色描边
|
|
|
|
+ textBorderWidth: 2, // 描边宽度
|
|
|
|
+ textShadowColor: 'rgba(0, 0, 0, 0.5)', // 添加文字阴影
|
|
|
|
+ textShadowBlur: 4, // 阴影模糊程度
|
|
|
|
+ textShadowOffsetX: 1, // 阴影X偏移
|
|
|
|
+ textShadowOffsetY: 1, // 阴影Y偏移
|
|
|
|
+ },
|
|
|
|
+ emphasis: {
|
|
|
|
+ label: {
|
|
|
|
+ show: true,
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
+ fontSize: 16, // 放大强调时的文字
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ data: [
|
|
|
|
+ { value: 12, name: '卫生间', itemStyle: { color: '#4dc9e6' } },
|
|
|
|
+ { value: 8, name: '卧室', itemStyle: { color: '#6de4ff' } },
|
|
|
|
+ { value: 6, name: '客厅', itemStyle: { color: '#2572ed' } },
|
|
|
|
+ { value: 4, name: '餐厅', itemStyle: { color: '#1a57c9' } },
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // 设备年龄层次图表
|
|
|
|
+ if (ageChartRef.value) {
|
|
|
|
+ ageChart = echarts.init(ageChartRef.value)
|
|
|
|
+ ageChart.setOption({
|
|
|
|
+ grid: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)',
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: '设备年龄',
|
|
|
|
+ type: 'pie',
|
|
|
|
+ radius: ['30%', '70%'],
|
|
|
|
+ itemStyle: {
|
|
|
|
+ borderColor: '#0a0e17',
|
|
|
|
+ borderWidth: 2,
|
|
|
|
+ },
|
|
|
|
+ label: {
|
|
|
|
+ show: true,
|
|
|
|
+ formatter: '{b}: {c}',
|
|
|
|
+ color: '#fff',
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
+ },
|
|
|
|
+ emphasis: {
|
|
|
|
+ label: {
|
|
|
|
+ show: true,
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
+ fontSize: 16,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ data: [
|
|
|
|
+ { value: 15, name: '1年内', itemStyle: { color: '#4dc9e6' } },
|
|
|
|
+ { value: 10, name: '1-2年', itemStyle: { color: '#2572ed' } },
|
|
|
|
+ { value: 5, name: '2-3年', itemStyle: { color: '#6de4ff' } },
|
|
|
|
+ { value: 2, name: '3年以上', itemStyle: { color: '#1a57c9' } },
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 检测对象图表
|
|
|
|
+ if (objectChartRef.value) {
|
|
|
|
+ objectChart = echarts.init(objectChartRef.value)
|
|
|
|
+ objectChart.setOption({
|
|
|
|
+ grid: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)',
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: '检测对象',
|
|
|
|
+ type: 'pie',
|
|
|
|
+ radius: ['30%', '70%'],
|
|
|
|
+ itemStyle: {
|
|
|
|
+ borderColor: '#0a0e17',
|
|
|
|
+ borderWidth: 2,
|
|
|
|
+ },
|
|
|
|
+ label: {
|
|
|
|
+ show: true,
|
|
|
|
+ formatter: '{b}: {c}',
|
|
|
|
+ color: '#fff',
|
|
|
|
+ fontSize: 14,
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
+ },
|
|
|
|
+ emphasis: {
|
|
|
|
+ label: {
|
|
|
|
+ show: true,
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
+ fontSize: 16,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ data: [
|
|
|
|
+ { value: 45, name: '长者', itemStyle: { color: '#2ecc71' } },
|
|
|
|
+ { value: 20, name: '访客', itemStyle: { color: '#f39c12' } },
|
|
|
|
+ { value: 35, name: '工作人员', itemStyle: { color: '#e74c3c' } },
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 历史告警统计图表
|
|
|
|
+ if (alarmHistoryRef.value) {
|
|
|
|
+ alarmHistoryChart = echarts.init(alarmHistoryRef.value)
|
|
|
|
+ alarmHistoryChart.setOption({
|
|
|
|
+ grid: { top: 10, right: 20, bottom: 30, left: 30 },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'axis',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: alarmHistoryData.value.dates,
|
|
|
|
+ axisLine: {
|
|
|
|
+ lineStyle: {
|
|
|
|
+ color: '#2a3b5a',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ color: '#9cc5e0',
|
|
|
|
+ fontSize: 12,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ axisLine: {
|
|
|
|
+ lineStyle: {
|
|
|
|
+ color: '#2a3b5a',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ color: '#9cc5e0',
|
|
|
|
+ fontSize: 12,
|
|
|
|
+ },
|
|
|
|
+ splitLine: {
|
|
|
|
+ lineStyle: {
|
|
|
|
+ color: 'rgba(42, 59, 90, 0.3)',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: '告警次数',
|
|
|
|
+ type: 'line',
|
|
|
|
+ smooth: true,
|
|
|
|
+ symbol: 'circle',
|
|
|
|
+ symbolSize: 8,
|
|
|
|
+ data: alarmHistoryData.value.values,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: '#f39c12',
|
|
|
|
+ },
|
|
|
|
+ lineStyle: {
|
|
|
|
+ width: 3,
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
|
|
|
+ { offset: 0, color: '#f39c12' },
|
|
|
|
+ { offset: 1, color: '#e74c3c' },
|
|
|
|
+ ]),
|
|
|
|
+ },
|
|
|
|
+ areaStyle: {
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
+ { offset: 0, color: 'rgba(243, 156, 18, 0.3)' },
|
|
|
|
+ { offset: 1, color: 'rgba(243, 156, 18, 0.1)' },
|
|
|
|
+ ]),
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 历史跌倒统计图表
|
|
|
|
+ if (fallingHistoryRef.value) {
|
|
|
|
+ fallingHistoryChart = echarts.init(fallingHistoryRef.value)
|
|
|
|
+ fallingHistoryChart.setOption({
|
|
|
|
+ grid: { top: 10, right: 20, bottom: 30, left: 30 },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'axis',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: fallingHistoryData.value.dates,
|
|
|
|
+ axisLine: {
|
|
|
|
+ lineStyle: {
|
|
|
|
+ color: '#2a3b5a',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ color: '#9cc5e0',
|
|
|
|
+ fontSize: 12,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ axisLine: {
|
|
|
|
+ lineStyle: {
|
|
|
|
+ color: '#2a3b5a',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ axisLabel: {
|
|
|
|
+ color: '#9cc5e0',
|
|
|
|
+ fontSize: 12,
|
|
|
|
+ },
|
|
|
|
+ splitLine: {
|
|
|
|
+ lineStyle: {
|
|
|
|
+ color: 'rgba(42, 59, 90, 0.3)',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: '跌倒次数',
|
|
|
|
+ type: 'line',
|
|
|
|
+ smooth: true,
|
|
|
|
+ symbol: 'circle',
|
|
|
|
+ symbolSize: 8,
|
|
|
|
+ data: fallingHistoryData.value.values,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: '#e74c3c',
|
|
|
|
+ },
|
|
|
|
+ lineStyle: {
|
|
|
|
+ width: 3,
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
|
|
|
+ { offset: 0, color: '#e74c3c' },
|
|
|
|
+ { offset: 1, color: '#c0392b' },
|
|
|
|
+ ]),
|
|
|
|
+ },
|
|
|
|
+ areaStyle: {
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
+ { offset: 0, color: 'rgba(231, 76, 60, 0.3)' },
|
|
|
|
+ { offset: 1, color: 'rgba(231, 76, 60, 0.1)' },
|
|
|
|
+ ]),
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 更新图表数据
|
|
|
|
+// const updateChartData = () => {
|
|
|
|
+// // 模拟数据更新
|
|
|
|
+// todayData.value = {
|
|
|
|
+// detected: Math.max(
|
|
|
|
+// 100,
|
|
|
|
+// Math.min(200, todayData.value.detected + Math.floor(Math.random() * 10 - 5))
|
|
|
|
+// ),
|
|
|
|
+// activity: Math.max(
|
|
|
|
+// 60,
|
|
|
|
+// Math.min(95, todayData.value.activity + Math.floor(Math.random() * 5 - 2))
|
|
|
|
+// ),
|
|
|
|
+// alerts: Math.max(0, Math.min(10, todayData.value.alerts + Math.floor(Math.random() * 3 - 1))),
|
|
|
|
+// onlineRate: Math.max(
|
|
|
|
+// 90,
|
|
|
|
+// Math.min(100, todayData.value.onlineRate + Math.floor(Math.random() * 3 - 1))
|
|
|
|
+// ),
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+// 组件挂载时初始化
|
|
|
|
+onMounted(() => {
|
|
|
|
+ updateTime()
|
|
|
|
+ initCharts()
|
|
|
|
+
|
|
|
|
+ // 设置定时器
|
|
|
|
+ setInterval(updateTime, 1000)
|
|
|
|
+
|
|
|
|
+ // 移除对已注释函数的调用
|
|
|
|
+ // updateInterval = setInterval(updateChartData, 5000)
|
|
|
|
+
|
|
|
|
+ // 窗口大小变化时调整图表大小
|
|
|
|
+ const handleResize = () => {
|
|
|
|
+ // 调整安装位置图表大小
|
|
|
|
+ if (deviceChart && deviceChartRef.value) {
|
|
|
|
+ // 强制重新计算容器大小
|
|
|
|
+ const containerWidth = deviceChartRef.value.offsetWidth
|
|
|
|
+ const containerHeight = deviceChartRef.value.offsetHeight
|
|
|
|
+
|
|
|
|
+ // 确保有有效的尺寸
|
|
|
|
+ if (containerWidth > 0 && containerHeight > 0) {
|
|
|
|
+ deviceChart.resize()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 调整设备年龄层次图表大小
|
|
|
|
+ if (ageChart && ageChartRef.value) {
|
|
|
|
+ const containerWidth = ageChartRef.value.offsetWidth
|
|
|
|
+ const containerHeight = ageChartRef.value.offsetHeight
|
|
|
|
+
|
|
|
|
+ if (containerWidth > 0 && containerHeight > 0) {
|
|
|
|
+ ageChart.resize()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 调整检测对象图表大小
|
|
|
|
+ if (objectChart && objectChartRef.value) {
|
|
|
|
+ const containerWidth = objectChartRef.value.offsetWidth
|
|
|
|
+ const containerHeight = objectChartRef.value.offsetHeight
|
|
|
|
+
|
|
|
|
+ if (containerWidth > 0 && containerHeight > 0) {
|
|
|
|
+ objectChart.resize()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 调整历史告警统计图表大小
|
|
|
|
+ if (alarmHistoryChart && alarmHistoryRef.value) {
|
|
|
|
+ const containerWidth = alarmHistoryRef.value.offsetWidth
|
|
|
|
+ const containerHeight = alarmHistoryRef.value.offsetHeight
|
|
|
|
+
|
|
|
|
+ if (containerWidth > 0 && containerHeight > 0) {
|
|
|
|
+ alarmHistoryChart.resize()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 调整历史跌倒统计图表大小
|
|
|
|
+ if (fallingHistoryChart && fallingHistoryRef.value) {
|
|
|
|
+ const containerWidth = fallingHistoryRef.value.offsetWidth
|
|
|
|
+ const containerHeight = fallingHistoryRef.value.offsetHeight
|
|
|
|
+
|
|
|
|
+ if (containerWidth > 0 && containerHeight > 0) {
|
|
|
|
+ fallingHistoryChart.resize()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ window.addEventListener('resize', handleResize)
|
|
|
|
+
|
|
|
|
+ // 组件挂载后立即触发一次调整,确保图表正确显示
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ handleResize()
|
|
|
|
+ }, 100)
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// 组件卸载时清理
|
|
|
|
+onUnmounted(() => {
|
|
|
|
+ if (updateInterval) {
|
|
|
|
+ clearInterval(updateInterval)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (deviceChart) {
|
|
|
|
+ deviceChart.dispose()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ageChart) {
|
|
|
|
+ ageChart.dispose()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (objectChart) {
|
|
|
|
+ objectChart.dispose()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (alarmHistoryChart) {
|
|
|
|
+ alarmHistoryChart.dispose()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (fallingHistoryChart) {
|
|
|
|
+ fallingHistoryChart.dispose()
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="less" scoped>
|
|
|
|
+@bg-color: #22284a;
|
|
|
|
+@text-color: #e0e0e0;
|
|
|
|
+@panel-bg: #0b173f;
|
|
|
|
+@border-color: #2a3b5a;
|
|
|
|
+@primary-color: #4dc9e6;
|
|
|
|
+@secondary-color: #6de4ff;
|
|
|
|
+@accent-color: #2572ed;
|
|
|
|
+@success-color: #2ecc71;
|
|
|
|
+@warning-color: #f39c12;
|
|
|
|
+@danger-color: #e74c3c;
|
|
|
|
+@gradient-start: #181c41;
|
|
|
|
+@gradient-end: #22284a;
|
|
|
|
+@glow-color: rgba(77, 201, 230, 0.2);
|
|
|
|
+@data-flow-color: rgba(77, 201, 230, 0.7);
|
|
|
|
+
|
|
|
|
+* {
|
|
|
|
+ margin: 0;
|
|
|
|
+ padding: 0;
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ font-family: 'Microsoft YaHei', Arial, sans-serif;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.radar-monitoring-screen {
|
|
|
|
+ background-color: @bg-color;
|
|
|
|
+ color: @text-color;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ height: 100vh;
|
|
|
|
+ padding: 12px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 顶部区域 */
|
|
|
|
+.header {
|
|
|
|
+ height: 70px;
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ align-items: center;
|
|
|
|
+ padding: 0 20px;
|
|
|
|
+ // background: linear-gradient(90deg, rgba(19, 28, 51, 0.8) 0%, rgba(32, 40, 65, 0.8) 100%);
|
|
|
|
+ background-color: @panel-bg;
|
|
|
|
+ border: 1px solid @border-color;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ box-shadow: 0 0 15px rgba(0, 180, 255, 0.2);
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
+ position: relative;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.system-name {
|
|
|
|
+ font-size: 24px;
|
|
|
|
+ background: linear-gradient(90deg, @primary-color, @secondary-color);
|
|
|
|
+ -webkit-background-clip: text;
|
|
|
|
+ -webkit-text-fill-color: transparent;
|
|
|
|
+ text-shadow: 0 0 10px rgba(109, 228, 255, 0.5);
|
|
|
|
+ letter-spacing: 2px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.running-days {
|
|
|
|
+ font-size: 22px;
|
|
|
|
+ color: @secondary-color;
|
|
|
|
+ text-shadow: 0 0 8px rgba(109, 228, 255, 0.7);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.time-info {
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ color: @primary-color;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 内容区域 */
|
|
|
|
+.content-area {
|
|
|
|
+ display: flex;
|
|
|
|
+ height: calc(100vh - 110px);
|
|
|
|
+ gap: 12px;
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.panel {
|
|
|
|
+ flex: 1;
|
|
|
|
+ background: @panel-bg;
|
|
|
|
+ border: 1px solid @border-color;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ padding: 15px;
|
|
|
|
+ box-shadow: 0 0 15px rgba(0, 180, 255, 0.1);
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ position: relative;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+
|
|
|
|
+ &::after {
|
|
|
|
+ content: '';
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ right: 0;
|
|
|
|
+ height: 3px;
|
|
|
|
+ background: linear-gradient(90deg, @primary-color, @accent-color);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.panel-title {
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ color: @secondary-color;
|
|
|
|
+ margin-bottom: 15px;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ padding-bottom: 8px;
|
|
|
|
+ border-bottom: 1px solid rgba(42, 59, 90, 0.5);
|
|
|
|
+
|
|
|
|
+ i {
|
|
|
|
+ margin-right: 8px;
|
|
|
|
+ font-size: 18px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.panel-content {
|
|
|
|
+ flex: 1;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.center-panel {
|
|
|
|
+ flex: 1.5;
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
|
|
+ background: #0b173f;
|
|
|
|
+ min-width: min-content;
|
|
|
|
+ height: 100%;
|
|
|
|
+ position: relative;
|
|
|
|
+ padding: 10px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 地图容器 - 作为标记点的相对定位参考系 */
|
|
|
|
+.map-container {
|
|
|
|
+ position: relative;
|
|
|
|
+ display: inline-block;
|
|
|
|
+ // width: 100%;
|
|
|
|
+ // height: 100%;
|
|
|
|
+ // max-width: 600px;
|
|
|
|
+ // max-height: 400px;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ background-color: #2ecc71;
|
|
|
|
+ /* 确保容器有明确的宽高,以便内部元素可以基于百分比定位 */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 地图图片 - 确保图片始终完全显示在容器内 */
|
|
|
|
+.map-img {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ object-fit: contain;
|
|
|
|
+ display: block;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ /* 确保图片不会超出容器,同时保持原始比例 */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 地图标签基础样式 - 确保基于地图容器的相对定位 */
|
|
|
|
+.map-label {
|
|
|
|
+ position: absolute;
|
|
|
|
+ background: rgba(32, 40, 65, 0.9);
|
|
|
|
+ border: 1px solid @border-color;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ color: @secondary-color;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
|
|
|
+ z-index: 10;
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
+ animation: pulse 2s infinite;
|
|
|
|
+ pointer-events: none;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 小区名称特殊样式 */
|
|
|
|
+.map-label:not([class*='building-']) {
|
|
|
|
+ top: 10%;
|
|
|
|
+ left: 50%;
|
|
|
|
+ background: rgba(77, 201, 230, 0.9);
|
|
|
|
+ color: #000;
|
|
|
|
+ font-size: 18px;
|
|
|
|
+ padding: 6px 12px;
|
|
|
|
+ border: 2px solid #fff;
|
|
|
|
+ animation: glow 2s infinite alternate;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 建筑位置 - 使用百分比定位确保基于图片相对位置 */
|
|
|
|
+.building-1 {
|
|
|
|
+ top: 30%;
|
|
|
|
+ left: 30%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.building-2 {
|
|
|
|
+ top: 50%;
|
|
|
|
+ left: 70%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.building-3 {
|
|
|
|
+ top: 70%;
|
|
|
|
+ left: 50%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.building-4 {
|
|
|
|
+ top: 40%;
|
|
|
|
+ left: 20%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 位置标记动画效果 */
|
|
|
|
+@keyframes pulse {
|
|
|
|
+ 0% {
|
|
|
|
+ transform: translate(-50%, -50%) scale(1);
|
|
|
|
+ box-shadow: 0 0 0 0 rgba(77, 201, 230, 0.4);
|
|
|
|
+ }
|
|
|
|
+ 70% {
|
|
|
|
+ transform: translate(-50%, -50%) scale(1.05);
|
|
|
|
+ box-shadow: 0 0 0 5px rgba(77, 201, 230, 0);
|
|
|
|
+ }
|
|
|
|
+ 100% {
|
|
|
|
+ transform: translate(-50%, -50%) scale(1);
|
|
|
|
+ box-shadow: 0 0 0 0 rgba(77, 201, 230, 0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 小区名称发光效果 */
|
|
|
|
+@keyframes glow {
|
|
|
|
+ from {
|
|
|
|
+ box-shadow: 0 0 5px rgba(77, 201, 230, 0.7);
|
|
|
|
+ }
|
|
|
|
+ to {
|
|
|
|
+ box-shadow:
|
|
|
|
+ 0 0 15px rgba(77, 201, 230, 1),
|
|
|
|
+ 0 0 20px rgba(77, 201, 230, 0.7);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.data-grid {
|
|
|
|
+ display: grid;
|
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
|
+ gap: 12px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.chart-container {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 200px; /* 设置固定高度 */
|
|
|
|
+ min-height: 200px; /* 确保最小高度 */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.status-list {
|
|
|
|
+ list-style: none;
|
|
|
|
+ max-height: 250px;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 底部区域 */
|
|
|
|
+.footer {
|
|
|
|
+ height: 50px;
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
|
|
+ background: @panel-bg;
|
|
|
|
+ border: 1px solid @border-color;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ color: @primary-color;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.glow-effect {
|
|
|
|
+ position: absolute;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ pointer-events: none;
|
|
|
|
+ background:
|
|
|
|
+ radial-gradient(circle at 20% 30%, rgba(37, 114, 237, 0.1) 0%, transparent 50%),
|
|
|
|
+ radial-gradient(circle at 80% 70%, rgba(77, 201, 230, 0.1) 0%, transparent 50%);
|
|
|
|
+ z-index: -1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.data-flow {
|
|
|
|
+ position: absolute;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 3px;
|
|
|
|
+ background: linear-gradient(90deg, transparent, rgba(77, 201, 230, 0.7), transparent);
|
|
|
|
+ animation: dataFlow 3s linear infinite;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@keyframes dataFlow {
|
|
|
|
+ 0% {
|
|
|
|
+ transform: translateX(-100%);
|
|
|
|
+ }
|
|
|
|
+ 100% {
|
|
|
|
+ transform: translateX(100%);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|