|
|
|
|
@ -1,28 +1,501 @@
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
// 引入 ECharts 相关组件
|
|
|
|
|
import type { EchartsUIType } from '@vben/plugins/echarts';
|
|
|
|
|
|
|
|
|
|
import { onMounted, ref } from 'vue';
|
|
|
|
|
import { useRouter } from 'vue-router';
|
|
|
|
|
|
|
|
|
|
import { Page } from '@vben/common-ui';
|
|
|
|
|
import { VbenIcon } from '@vben/icons';
|
|
|
|
|
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
|
|
|
|
|
|
|
|
|
import { Card, Col, Flex, Row } from 'ant-design-vue';
|
|
|
|
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
|
|
// 统计数据
|
|
|
|
|
const statisticsData = ref([
|
|
|
|
|
{
|
|
|
|
|
title: '设备数量',
|
|
|
|
|
value: 702,
|
|
|
|
|
icon: 'solar:bedside-table-2-line-duotone',
|
|
|
|
|
color: '#40c9c6',
|
|
|
|
|
url: '/device/device',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '传感器数量',
|
|
|
|
|
value: 700,
|
|
|
|
|
icon: 'solar:box-line-duotone',
|
|
|
|
|
color: '#36a3f7',
|
|
|
|
|
url: '/device/sensor',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '监测数据',
|
|
|
|
|
value: 359_660,
|
|
|
|
|
icon: 'solar:course-up-broken',
|
|
|
|
|
color: '#34bfa3',
|
|
|
|
|
url: '/monitoring-data',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '告警数量',
|
|
|
|
|
value: 280_625,
|
|
|
|
|
icon: 'bellBingLineDuotone',
|
|
|
|
|
color: '#f7883a',
|
|
|
|
|
url: '/alarm-list',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// 天气信息
|
|
|
|
|
const weatherInfo = ref({
|
|
|
|
|
city: '香港',
|
|
|
|
|
date: '2025-12-16',
|
|
|
|
|
temperature: '17°C / 23°C',
|
|
|
|
|
description: '多云',
|
|
|
|
|
windDirection: '东',
|
|
|
|
|
windSpeed: '23.4 级',
|
|
|
|
|
icon: 'solar:sun-linear',
|
|
|
|
|
class: 'low-temperature text-2xl',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 使用率数据
|
|
|
|
|
const usageData = ref({
|
|
|
|
|
cpu: 65,
|
|
|
|
|
memory: 72,
|
|
|
|
|
disk: 45,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ECharts 引用
|
|
|
|
|
const cpuChartRef = ref<EchartsUIType>();
|
|
|
|
|
const memoryChartRef = ref<EchartsUIType>();
|
|
|
|
|
const diskChartRef = ref<EchartsUIType>();
|
|
|
|
|
|
|
|
|
|
import { Card, Flex } from 'ant-design-vue';
|
|
|
|
|
const { renderEcharts: renderCpuChart } = useEcharts(cpuChartRef);
|
|
|
|
|
const { renderEcharts: renderMemoryChart } = useEcharts(memoryChartRef);
|
|
|
|
|
const { renderEcharts: renderDiskChart } = useEcharts(diskChartRef);
|
|
|
|
|
|
|
|
|
|
// 初始化 ECharts 图表
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
// CPU 使用率图表 (Ring Gauge)
|
|
|
|
|
renderCpuChart({
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
type: 'gauge',
|
|
|
|
|
startAngle: 90,
|
|
|
|
|
endAngle: -270,
|
|
|
|
|
pointer: {
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
progress: {
|
|
|
|
|
show: true,
|
|
|
|
|
overlap: false,
|
|
|
|
|
roundCap: true,
|
|
|
|
|
clip: false,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: '#464646',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
axisLine: {
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 16,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: false,
|
|
|
|
|
distance: 0,
|
|
|
|
|
length: 10,
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
show: false,
|
|
|
|
|
distance: 50,
|
|
|
|
|
},
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value: usageData.value.cpu,
|
|
|
|
|
name: 'CPU',
|
|
|
|
|
title: {
|
|
|
|
|
offsetCenter: ['0%', '-10%'], // 调整标题位置
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
valueAnimation: true,
|
|
|
|
|
offsetCenter: ['0%', '10%'],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
title: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: '#666',
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
color: '#333',
|
|
|
|
|
formatter: '{value}%',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 内存使用率图表 (Ring Gauge)
|
|
|
|
|
renderMemoryChart({
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
type: 'gauge',
|
|
|
|
|
startAngle: 90,
|
|
|
|
|
endAngle: -270,
|
|
|
|
|
pointer: {
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
progress: {
|
|
|
|
|
show: true,
|
|
|
|
|
overlap: false,
|
|
|
|
|
roundCap: true,
|
|
|
|
|
clip: false,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: '#464646',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
axisLine: {
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 16,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: false,
|
|
|
|
|
distance: 0,
|
|
|
|
|
length: 10,
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
show: false,
|
|
|
|
|
distance: 50,
|
|
|
|
|
},
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value: usageData.value.memory,
|
|
|
|
|
name: '内存',
|
|
|
|
|
title: {
|
|
|
|
|
offsetCenter: ['0%', '-10%'], // 调整标题位置
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
valueAnimation: true,
|
|
|
|
|
offsetCenter: ['0%', '10%'],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
title: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: '#666',
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
color: '#333',
|
|
|
|
|
formatter: '{value}%',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 磁盘使用率图表 (Ring Gauge)
|
|
|
|
|
renderDiskChart({
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
type: 'gauge',
|
|
|
|
|
startAngle: 90,
|
|
|
|
|
endAngle: -270,
|
|
|
|
|
pointer: {
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
progress: {
|
|
|
|
|
show: true,
|
|
|
|
|
overlap: false,
|
|
|
|
|
roundCap: true,
|
|
|
|
|
clip: false,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: '#464646',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
axisLine: {
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 16,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: false,
|
|
|
|
|
distance: 0,
|
|
|
|
|
length: 10,
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
show: false,
|
|
|
|
|
distance: 50,
|
|
|
|
|
},
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value: usageData.value.disk,
|
|
|
|
|
name: '磁盘',
|
|
|
|
|
title: {
|
|
|
|
|
offsetCenter: ['0%', '-10%'], // 调整标题位置
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
valueAnimation: true,
|
|
|
|
|
offsetCenter: ['0%', '10%'],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
title: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: '#666',
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
color: '#333',
|
|
|
|
|
formatter: '{value}%',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 处理卡片点击事件
|
|
|
|
|
const handleCardClick = (url?: string) => {
|
|
|
|
|
if (url) {
|
|
|
|
|
router.push(url);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<Page :auto-content-height="true">
|
|
|
|
|
<Flex justify="space-between" align="flex-start">
|
|
|
|
|
<Card class="w-full">
|
|
|
|
|
<Flex justify="space-between" align="center">
|
|
|
|
|
<span> hhh </span>
|
|
|
|
|
<VbenIcon icon="solar:home-broken" class="size-8 flex-shrink-0" />
|
|
|
|
|
</Flex>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card class="w-full">
|
|
|
|
|
<p>card content</p>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card class="w-full">
|
|
|
|
|
<p>card content</p>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card class="w-full">
|
|
|
|
|
<p>card content</p>
|
|
|
|
|
</Card>
|
|
|
|
|
</Flex>
|
|
|
|
|
<Row :gutter="[20, 20]" class="mb-5">
|
|
|
|
|
<!-- 左侧 -->
|
|
|
|
|
<Col :span="24" :lg="16">
|
|
|
|
|
<!-- 统计数据 -->
|
|
|
|
|
<div class="mb-5 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
|
|
|
<Card
|
|
|
|
|
v-for="(item, index) in statisticsData"
|
|
|
|
|
:key="index"
|
|
|
|
|
class="shadow-md transition-all duration-300 hover:shadow-lg"
|
|
|
|
|
:class="{
|
|
|
|
|
'cursor-pointer hover:-translate-y-1 hover:scale-105': item.url,
|
|
|
|
|
}"
|
|
|
|
|
@click="handleCardClick(item.url)"
|
|
|
|
|
>
|
|
|
|
|
<Flex justify="flex-start" align="center" class="gap-5">
|
|
|
|
|
<VbenIcon
|
|
|
|
|
:icon="item.icon"
|
|
|
|
|
class="size-8 flex-shrink-0"
|
|
|
|
|
:style="{ color: item.color }"
|
|
|
|
|
/>
|
|
|
|
|
<Flex justify="center" :vertical="true">
|
|
|
|
|
<span class="text-gray-500">{{ item.title }}</span>
|
|
|
|
|
<span class="text-2xl font-medium">{{
|
|
|
|
|
item.value.toLocaleString()
|
|
|
|
|
}}</span>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Card>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Card title="设备分布地图" class="shadow-md">
|
|
|
|
|
<div
|
|
|
|
|
class="flex h-96 items-center justify-center rounded bg-gray-100"
|
|
|
|
|
>
|
|
|
|
|
<span class="text-gray-500">地图展示区域</span>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<!-- 使用率图表 -->
|
|
|
|
|
<Card title="系统资源使用率" class="mt-5 shadow-md">
|
|
|
|
|
<Row :gutter="20">
|
|
|
|
|
<Col :span="8">
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<EchartsUI ref="cpuChartRef" height="250px" width="100%" />
|
|
|
|
|
<p class="mt-2 font-medium">CPU使用率</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col :span="8">
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<EchartsUI ref="memoryChartRef" height="250px" width="100%" />
|
|
|
|
|
<p class="mt-2 font-medium">内存使用率</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col :span="8">
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<EchartsUI ref="diskChartRef" height="250px" width="100%" />
|
|
|
|
|
<p class="mt-2 font-medium">磁盘使用率</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
<!-- 右侧:天气信息 -->
|
|
|
|
|
<Col :span="24" :lg="8">
|
|
|
|
|
<Card
|
|
|
|
|
class="weather-card cursor-pointer bg-gradient-to-r from-blue-50 to-indigo-50 shadow-md transition-shadow hover:shadow-lg"
|
|
|
|
|
>
|
|
|
|
|
<div class="flex flex-col md:flex-row">
|
|
|
|
|
<!-- 天气图标区域 -->
|
|
|
|
|
<div class="mb-4 flex items-center justify-center md:mb-0 md:w-2/5">
|
|
|
|
|
<div class="weather-main">
|
|
|
|
|
<VbenIcon :icon="weatherInfo.icon" class="text-6xl" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 天气信息区域 -->
|
|
|
|
|
<div class="md:w-3/5">
|
|
|
|
|
<div class="weather-header">
|
|
|
|
|
<h2 class="text-2xl font-bold">{{ weatherInfo.city }}</h2>
|
|
|
|
|
<div class="date-week text-gray-500">
|
|
|
|
|
<span>{{ weatherInfo.date }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Flex align="center">
|
|
|
|
|
<Flex :vertical="true">
|
|
|
|
|
<div :class="weatherInfo.class">
|
|
|
|
|
{{ weatherInfo.temperature.split(' / ')[0] }}
|
|
|
|
|
</div>
|
|
|
|
|
</Flex>
|
|
|
|
|
<Flex :vertical="true" align="center" class="w-1/2">
|
|
|
|
|
<div class="">
|
|
|
|
|
{{ weatherInfo.temperature.split(' / ')[1] }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="">
|
|
|
|
|
{{ weatherInfo.description }}
|
|
|
|
|
</div>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Flex>
|
|
|
|
|
|
|
|
|
|
<Flex align="center" class="gap-2">
|
|
|
|
|
<Flex align="center">
|
|
|
|
|
<VbenIcon icon="solar:black-hole-3-linear" />
|
|
|
|
|
<span>{{ weatherInfo.windDirection }}风</span>
|
|
|
|
|
</Flex>
|
|
|
|
|
<Flex align="center">
|
|
|
|
|
<span>{{ weatherInfo.windSpeed }}</span>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Flex>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<!-- 信息栏 -->
|
|
|
|
|
<Card title="信息栏" class="mt-5 shadow-md">
|
|
|
|
|
<div class="space-y-3">
|
|
|
|
|
<div
|
|
|
|
|
class="flex items-center justify-between rounded p-2 hover:bg-gray-50"
|
|
|
|
|
>
|
|
|
|
|
<div>
|
|
|
|
|
<span
|
|
|
|
|
class="mr-2 rounded bg-orange-100 px-2 py-1 text-xs text-orange-800"
|
|
|
|
|
>公告</span
|
|
|
|
|
>
|
|
|
|
|
<span>涉尘涉爆管理平台V1.0发布</span>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="text-sm text-gray-400">2025-12-20</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="flex items-center justify-between rounded p-2 hover:bg-gray-50"
|
|
|
|
|
>
|
|
|
|
|
<div>
|
|
|
|
|
<span
|
|
|
|
|
class="mr-2 rounded bg-blue-100 px-2 py-1 text-xs text-blue-800"
|
|
|
|
|
>信息</span
|
|
|
|
|
>
|
|
|
|
|
<span>设备现已支持4G+MQTT</span>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="text-sm text-gray-400">2021-12-15</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<!-- 设备状态分布 -->
|
|
|
|
|
<Card title="设备状态分布" class="mt-5 shadow-md">
|
|
|
|
|
<div
|
|
|
|
|
class="flex h-80 items-center justify-center rounded bg-gray-100"
|
|
|
|
|
>
|
|
|
|
|
<span class="text-gray-500">饼图展示区域</span>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
<!-- 第三行:图表展示 -->
|
|
|
|
|
<Row :gutter="[20, 20]" class="mb-5">
|
|
|
|
|
<Col :span="24" :lg="12">
|
|
|
|
|
<!-- 移除了设备状态分布卡片 -->
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</Page>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
0% {
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
50% {
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
100% {
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-enter-active,
|
|
|
|
|
.card-leave-active {
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-enter-from,
|
|
|
|
|
.card-leave-to {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateY(10px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 天气信息卡片样式优化 */
|
|
|
|
|
.weather-card {
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-card:hover {
|
|
|
|
|
transform: translateY(-5px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-main {
|
|
|
|
|
animation: pulse 2s infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.low-temperature {
|
|
|
|
|
font-size: 1.5rem /* 24px */;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
line-height: 2rem /* 32px */;
|
|
|
|
|
color: #00a2ff;
|
|
|
|
|
text-shadow: 0 2px 4px rgb(59 130 246 / 30%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.high-temperature {
|
|
|
|
|
font-size: 1.5rem /* 24px */;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
line-height: 2rem /* 32px */;
|
|
|
|
|
color: #dd4a68;
|
|
|
|
|
text-shadow: 0 2px 4px rgb(239 68 68 / 30%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 添加过渡效果 */
|
|
|
|
|
</style>
|
|
|
|
|
|