Xazn-vue/src/views/index.vue
2025-05-22 16:32:24 +08:00

2521 lines
97 KiB
Vue
Raw Permalink 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.

<template>
<div class="home-page-container">
<!-- 设备统计信息以及天气 -->
<el-row :gutter="20" class="statistics-container">
<!-- 设备统计 -->
<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
<el-row :gutter="20">
<el-col :span="8" class="statistics-item">
<!-- 设备数量 -->
<div class="card-panel">
<div class="card-content device">
<el-row :gutter="20">
<el-col :span="12">
<div class="card-icon">
<svg-icon icon-class="device" class-name="card-panel-icon" />
</div>
</el-col>
<el-col :span="12">
<div class="card-data">
<div class="card-title">{{ $t('home.number') }}</div>
<count-to :start-val="0" :end-val="deviceStatistic.deviceCount" :duration="3000" class="card-panel-num" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="8" class="statistics-item">
<!-- 产品数量 -->
<div class="card-panel">
<div class="card-content product">
<el-row :gutter="20">
<el-col :span="12">
<div class="card-icon">
<svg-icon icon-class="model" class-name="card-panel-icon" />
</div>
</el-col>
<el-col :span="12">
<div class="card-data">
<div class="card-title">{{ $t('home.product') }}</div>
<count-to :start-val="0" :end-val="deviceStatistic.productCount" :duration="3000" class="card-panel-num" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="8" class="statistics-item">
<!-- 操作记录 -->
<div class="card-panel">
<div class="card-content function">
<el-row :gutter="20">
<el-col :span="12">
<div class="card-icon">
<svg-icon icon-class="log-a" class-name="card-panel-icon" />
</div>
</el-col>
<el-col :span="12">
<div class="card-data">
<div class="card-title">{{ $t('home.records') }}</div>
<count-to :start-val="0" :end-val="deviceStatistic.functionCount" :duration="3000" class="card-panel-num" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="8">
<!-- 监测数据 -->
<div class="card-panel">
<div class="card-content monitor">
<el-row :gutter="20">
<el-col :span="12">
<div class="card-icon">
<svg-icon icon-class="monitor-a" class-name="card-panel-icon" />
</div>
</el-col>
<el-col :span="12">
<div class="card-data">
<div class="card-title">{{ $t('home.monitoring') }}</div>
<count-to :start-val="0" :end-val="deviceStatistic.monitorCount" :duration="3000" class="card-panel-num" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="8">
<!-- 告警数量 -->
<div class="card-panel">
<div class="card-content alert">
<el-row :gutter="20">
<el-col :span="12">
<div class="card-icon">
<svg-icon icon-class="alert" class-name="card-panel-icon" />
</div>
</el-col>
<el-col :span="12">
<div class="card-data">
<div class="card-title">{{ $t('home.alarm') }}</div>
<count-to :start-val="0" :end-val="deviceStatistic.alertCount" :duration="3000" class="card-panel-num" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="8">
<!-- 上报事件 -->
<div class="card-panel">
<div class="card-content reports">
<el-row :gutter="20">
<el-col :span="12">
<div class="card-icon">
<svg-icon icon-class="event-a" class-name="card-panel-icon" />
</div>
</el-col>
<el-col :span="12">
<div class="card-data">
<div class="card-title">{{ $t('home.reports') }}</div>
<count-to :start-val="0" :end-val="deviceStatistic.eventCount" :duration="3000" class="card-panel-num" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-col>
</el-row>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<!-- 天气 -->
<el-card
class="weather-card"
shadow="hover"
:style="{
'--background-start': getBackgroundColor(weatherData.data.type).start,
'--background-end': getBackgroundColor(weatherData.data.type).end,
}"
>
<el-row :gutter="20">
<el-col :span="10">
<div class="weather-main">
<img :src="weatherData.data.typeIcon" alt="天气图标" class="weather-icon" />
</div>
</el-col>
<el-col :span="14">
<!-- 头部城市名称日期与星期 -->
<div class="weather-header">
<h2>{{ weatherData.city }}</h2>
<div class="date-week">
<span>{{ weatherData.data.date }}</span>
&nbsp;
<span>{{ weatherData.data.week }}</span>
</div>
</div>
<el-row :gutter="10" style="margin-top: 10px">
<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
<div class="low-temperature">{{ weatherData.data.low }}</div>
</el-col>
<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
<!-- 温度 -->
<div class="high-temperature">/ {{ weatherData.data.high }}</div>
<p class="weather-description">{{ weatherData.data.type }}</p>
</el-col>
</el-row>
<!-- 详情信息风向风力 -->
<el-row :gutter="10">
<div class="weather-details">
<el-col :span="12">
<div class="detail-item">
<svg-icon icon-class="wind_direction" />
<span class="detail-text">{{ weatherData.data.fengxiang }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<svg-icon icon-class="wind_speed" />
<span class="detail-text">{{ weatherData.data.fengli }}</span>
</div>
</el-col>
</div>
</el-row>
<!-- 空气质量信息 -->
<!-- <div class="air-quality">
<div class="aqi-info">
<span class="aqi-level" :class="aqiClass">{{ weatherData.air.aqi }}</span>
<span class="aqi-value" :class="aqiClass">{{ $t('views.index.394840-18') }}</span>
<span class="aqi-level">{{ weatherData.air.aqi_name }}</span>
</div>
</div> -->
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<!-- 地图 -->
<el-row :gutter="20" class="statistics-container">
<!-- 地图 -->
<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
<div class="map-card" shadow="hover">
<div class="map-container">
<div ref="map" class="map"></div>
</div>
</div>
<!-- cpu使用率 -->
<el-card class="rate" shadow="hover" style="margin: 20px 0px">
<div v-if="isAdmin">
<div style="">
<div class="chart-title">{{ $t('home.usage') }}</div>
<el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<div ref="pieCpu" class="pieCpu"></div>
</el-col>
</el-row>
</div>
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
<div style="">
<div class="chart-title">{{ $t('home.memoryRate') }}</div>
<div ref="pieMemery" class="pieMemery"></div>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
<div style="">
<div class="chart-title">{{ $t('home.disk') }}</div>
<div ref="pieDisk" class="pieDisk"></div>
</div>
</el-col>
</el-row>
</div>
<div v-else>
<el-empty style="height: 500px" :description="$t('dataCenter.analysis.349202-7')"></el-empty>
</div>
</el-card>
</el-col>
<!-- 折线图 -->
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<!-- 信息栏 -->
<el-card shadow="hover" class="message-card">
<div class="message-title">{{ $t('home.information') }}</div>
<div class="notice-bar" :class="{ animating: animate }" v-if="noticeList.length > 0">
<div class="item-wrap" @click="openDetail(item.noticeId)" v-for="item in noticeList" :key="item.noticeId">
<div class="left-wrap">
<el-tag size="mini" effect="dark" type="warning" v-if="item.noticeType == 2">{{ $t('home.announcement') }}</el-tag>
<el-tag size="mini" effect="dark" v-else>{{ $t('home.message') }}</el-tag>
<span style="margin-left: 8px">{{ item.noticeTitle }}</span>
</div>
<div class="right-wrap">
{{ parseTime(item.createTime, '{y}-{m}-{d}') }}
</div>
</div>
</div>
<div v-else>
<el-empty style="height: 400px" :description="$t('dataCenter.analysis.349202-7')"></el-empty>
</div>
</el-card>
<!-- </div> -->
<div class="">
<!-- 登录用户数量 -->
<el-card class="line-card" shadow="hover">
<div ref="lineChart" style="height: 300px; width: 100%; margin: 0 10px 24px 0" v-if="isAdmin && linechart.counts"></div>
<div v-else class="message-title" style="margin: 0px 0 10px">
{{ $t('views.index.394840-16') }}
<el-empty style="height: 250px" :description="$t('dataCenter.analysis.349202-7')"></el-empty>
</div>
</el-card>
</div>
<div class="">
<!-- mqtt状态数据 -->
<el-card class="card-container" shadow="hover">
<div class="">
<div ref="statsChart" style="height: 300px"></div>
</div>
</el-card>
</div>
</el-col>
</el-row>
<!-- h5手机显示页面 -->
<!-- <el-card shadow="hover" style="margin: -20px 10px 20px 10px" class="phone-card">
<el-row :gutter="40">
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" style="padding: 10px">
<div style="padding: 30px; margin: 20px 0; font-size: 14px">
<div style="margin-bottom: 20px; font-family: PingFangSC, PingFang SC; font-weight: 600; font-size: 35px; color: #303133; line-height: 49px; text-align: left; font-style: normal">
{{ $t('views.index.394840-0') }}
</div>
<div style="display: table; font-size: 14px; margin-bottom: 10px">
<div style="display: table-cell; line-height: 22px">
<b style="margin-right: 10px; font-family: PingFangSC, PingFang SC; font-weight: 500; font-size: 13px; color: #67c23a; line-height: 18px; text-align: left; font-style: normal">
{{ $t('views.index.394840-1') }}
</b>
</div>
</div>
<div style="margin-bottom: 10px">
<div style="width: 70px; font-weight: bold; display: table-cell; padding: 5px 0">{{ $t('views.index.394840-2') }}</div>
<div style="line-height: 22px">{{ $t('views.index.394840-3') }}</div>
</div>
<div style="margin: 10px 0">
<div style="width: 70px; font-weight: bold; display: table-cell; padding: 5px 0">{{ $t('views.index.394840-4') }}</div>
<div style="line-height: 22px">
{{ $t('views.index.394840-5') }}
<br />
<el-link target="_blank" href="https://fastbee.cn/doc/pages/sponsor">{{ $t('views.index.394840-6') }}</el-link>
</div>
</div>
</div>
<div style="padding: 70px 30px 0 20px; font-size: 14px">
<div style="float: left; width: 200px">
<el-image style="width: 180px" :src="require('@/assets/images/code.jpg')"></el-image>
</div>
<div style="float: left">
<div class="mini-program">{{ $t('views.index.394840-7') }}</div>
<div style="display: table; margin-bottom: 5px">
<div class="web-site">{{ $t('views.index.394840-9') }}</div>
<div class="other-site">
<el-link target="_blank" href="https://fastbee.cn/">www.fastbee.cn</el-link>
</div>
</div>
<div style="display: table; margin-bottom: 5px">
<div class="web-site">{{ $t('views.index.394840-10') }}</div>
<div class="other-site">
<el-link target="_blank" href="https://fastbee.cn/doc">www.fastbee.cn/doc</el-link>
</div>
</div>
<div style="display: table; margin: 5px 0">
<div class="web-site">{{ $t('views.index.394840-11') }}</div>
<div class="other-site">
<span>QQ 164770707</span>
</div>
</div>
<div style="display: table; margin-bottom: 10px">
<div class="web-site">{{ $t('views.index.394840-12') }}</div>
<div class="other-site">
<el-link target="_blank" href="https://gitee.com/kerwincui/wumei-smart" style="font-size: 12px">{{ $t('views.index.394840-13') }}</el-link>
<el-link target="_blank" href="https://github.com/kerwincui/fastbee" style="margin-left: 20px; font-size: 12px">{{ $t('views.index.394840-14') }}</el-link>
</div>
</div>
</div>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" style="padding: 30px">
<div class="phone">
<iframe src="https://iot.fastbee.cn/h5" id="iframe" frameborder="0" scrolling="auto" class="phone-container"></iframe>
<div class="frame-remark">{{ $t('views.index.394840-8') }}</div>
</div>
</el-col>
</el-row>
</el-card> -->
<!--通知公告详情 -->
<el-dialog :title="notice.noticeTitle" :visible.sync="open" width="800px" append-to-body>
<div style="margin-top: -20px; margin-bottom: 10px">
<el-tag size="mini" effect="dark" type="warning" v-if="notice.noticeType == 2">{{ $t('home.announcement') }}</el-tag>
<el-tag size="mini" effect="dark" v-else>{{ $t('home.message') }}</el-tag>
<span style="margin-left: 20px">{{ notice.createTime }}</span>
</div>
<div v-loading="loading" class="content">
<div v-html="notice.noticeContent"></div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="closeDetail">{{ $t('home.close') }}</el-button>
</div>
</el-dialog>
<!-- <div class="footer-container">
<span>
Copyright © 2021-2025
<a href="https://fastbee.cn/" target="_blank">FastBee</a>
|
<a href="https://fastbee.cn/" target="_blank">{{ $t('views.index.394840-15') }}</a>
| Apache License
</span>
<br />
<span>
{{ $t('views.index.394840-17') }}
<a href="https://fastbee.cn/doc/" target="_blank">https://fastbee.cn/doc/</a>
</span>
</div> -->
</div>
</template>
<script>
import axios from 'axios';
import moment from 'moment';
import { getDeviceStatistic } from '@/api/iot/device';
import { listNotice, getNotice } from '@/api/system/notice';
import CountTo from 'vue-count-to';
import { loadBMap } from '@/utils/map.js';
//安装的是echarts完整包里面包含百度地图扩展路径为 echarts/extension/bmap/bmap.js将其引入
//ECharts的百度地图扩展可以在百度地图上展现点图线图热力图等可视化
require('echarts/extension/bmap/bmap');
import { getServer } from '@/api/monitor/server';
import { listAllDeviceShort } from '@/api/iot/device';
import 'echarts-liquidfill';
import * as echarts from 'echarts';
import { searchUserCount } from '@/api/monitor/jobLog';
import { getNettyMqttStats, statisticNettyMqtt } from '@/api/iot/netty';
export default {
name: 'Index',
components: {
CountTo,
},
data() {
return {
//天气信息
weatherData: {
success: true,
city: '---',
data: {
date: '---',
week: '--',
type: '-',
typeIcon: require('../../src/assets/icons/qing.png'),
low: '-°C',
high: '-°C',
fengxiang: '---',
fengli: '--',
},
air: {
aqi: 85,
aqi_level: 2,
aqi_name: '良',
},
},
radius: ['55%', '75%'], // 默认半径
labelFontSize: '24', // 默认圆环内字体大小
// mqtt状态数据
stats: {},
// mqtt统计信息
static: {},
// 折线图信息
linechart: {
date: [],
counts: [],
},
// 遮罩层
loading: true,
// 是否显示弹出层
open: false,
// 信息列表
noticeList: [],
// 信息详情
notice: {},
// 是否为管理员
isAdmin: false,
// 设备列表
deviceList: [],
// 设备统计信息
deviceStatistic: {},
// 设备总数
deviceCount: 0,
// 版本号
version: '3.8.0',
animate: true,
duration: 10, // 公告滚动一次的时长(单位:秒)
interval: 2000, // 公告间隔时间(单位:毫秒)
mapChart: null, //地图
mqttChart: null, // mqtt统计图(为了解决图表切换屏幕后显示问题)
pieCpuChart: null, //cpu使用率图
rateChart: null,
sysChart: null,
loginUserChart: null,
// 服务器信息
server: {
jvm: {
name: '',
version: '',
startTime: '',
runTime: '',
used: '',
total: 100,
},
sys: {
computerName: '',
osName: '',
computerIp: '',
osArch: '',
},
cpu: {
cpuNum: 1,
},
mem: {
total: 2,
},
},
tableData: [],
};
},
computed: {
aqiClass() {
switch (this.weatherData.air.aqi_level) {
case 1:
return 'aqi-good';
case 2:
return 'aqi-moderate';
case 3:
return 'aqi-unhealthy';
default:
return '';
}
},
},
mounted() {
this.startScroll();
// this.getLocation(); //获取位置信息
this.$nextTick(() => {
this.drawPieCpu();
// 监听窗口大小变化以重新渲染饼图
window.addEventListener('resize', this.handleResize);
});
},
beforeDestroy() {
this.stopScroll();
// 清除监听器
window.removeEventListener('resize', this.handleResize);
},
created() {
this.init();
this.getAllDevice(); //获取设备数据
this.getNoticeList(); //获取公告列表
this.getDeviceStatistic(); //获取设备统计信息
this.getMqttStats(); //获取mqtt统计信息
this.statisticMqtt(); //mqtt统计数据
},
methods: {
// 公告栏滚动
startScroll() {
this.intervalId = setInterval(() => {
const firstNotice = this.noticeList.shift();
this.noticeList.push(firstNotice);
}, this.interval);
},
stopScroll() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
},
// 折线图,登录用户数量
getChartData() {
searchUserCount().then((response) => {
console.log('折线图信息', response);
response.data.reverse().forEach((item) => {
this.linechart.date.push(item.datetime);
this.linechart.counts.push(item.user_count);
});
this.drawLine();
});
},
// 检测屏幕宽度并更新 radius
updateRadius() {
const isMobile = window.matchMedia('(max-width: 430px)').matches;
this.radius = isMobile ? ['35%', '45%'] : ['55%', '75%'];
this.labelFontSize = isMobile ? '16' : '28';
this.drawPieCpu(); // 更新图表以应用新半径
},
// mqtt统计数据
statisticMqtt() {
statisticNettyMqtt().then((response) => {
this.static = response.data;
});
},
/** 查询mqtt状态数据*/
getMqttStats() {
getNettyMqttStats().then((response) => {
this.stats = response.data;
console.log(this.stats);
this.drawStats();
});
},
// 绘制柱状图Mqtt状态数据
drawStats() {
// 基于准备好的dom初始化echarts实例
this.mqttChart = this.$echarts.init(this.$refs.statsChart);
var option;
option = {
title: {
text: this.$t('views.index.394840-19'), //Mqtt状态数据
textStyle: {
ontFamily: 'PingFangSC, PingFang SC',
lineHeight: 22,
fontWeight: 600,
fontSize: 16,
color: '#303133',
fontStyle: 'normal',
textAlign: 'left',
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {
data: [this.$t('netty.mqtt.564432-18'), this.$t('netty.mqtt.564432-19')],
right: '15',
icon: 'rect',
itemWidth: 10,
itemHeight: 10,
borderRadius: 20,
textStyle: {
color: 'rgba(0,0,0,0.65)',
},
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
},
},
splitNumber: 3, // 分割线
},
yAxis: {
type: 'category',
data: [this.$t('netty.mqtt.564432-13'), this.$t('netty.mqtt.564432-14'), this.$t('netty.mqtt.564432-15'), this.$t('netty.mqtt.564432-16'), this.$t('netty.mqtt.564432-17')],
},
series: [
{
name: this.$t('netty.mqtt.564432-18'),
color: '#0bb9ff',
type: 'bar',
data: [this.stats['connection_count'], this.stats['session_count'], this.stats['subscription_count'], this.stats['retain_count'], this.stats['retain_count']],
},
{
name: this.$t('netty.mqtt.564432-19'),
color: '#4a6ff8',
type: 'bar',
data: [this.stats['connection_total'], this.stats['session_total'], this.stats['subscription_total'], this.stats['retain_total'], this.stats['retain_total']],
},
],
};
option && this.mqttChart.setOption(option);
},
// 天气
getLocation() {
const geolocation = new BMap.Geolocation();
geolocation.getCurrentPosition(
async (r) => {
const { latitude, longitude } = r;
await this.fetchWeather1(latitude, longitude);
},
(err) => {
console.log('bai-loc-err', err);
}
);
},
async fetchWeather1(lat, lon) {
this.isLoading = true;
this.error = null;
const key = process.env.VUE_APP_XIN_ZHI_KEY; // 心知天气API密钥
const url = `https://api.seniverse.com/v3/weather/daily.json?key=${key}&location=${lat}:${lon}&language=zh-Hans&unit=c`;
try {
const response = await axios.get(url);
const weather = response.data.results[0];
if (response.data) {
this.weatherData = {
success: true,
city: weather.location.name,
data: {
date: weather.daily[0].date,
week: '',
type: weather.daily[0].text_day,
typeIcon: this.getTypeIcon(weather.daily[0].text_day),
low: weather.daily[0].low + '°C',
high: weather.daily[0].high + '°C',
fengxiang: weather.daily[0].wind_direction,
fengli: weather.daily[0].wind_speed + ' 级',
night: {
type: weather.daily[0].text_night,
fengxiang: weather.daily[0].wind_direction,
fengli: weather.daily[0].wind_speed,
},
},
air: {
aqi: 85,
aqi_level: 2,
aqi_name: '良',
},
};
const background = this.getBackgroundColor(weather.daily[0].text_day);
document.documentElement.style.setProperty('--background-start', background.start);
document.documentElement.style.setProperty('--background-end', background.end);
} else {
this.error = '获取天气数据失败,请稍后重试。';
}
} catch (err) {
console.log(err);
this.error = '无法连接到天气服务,请检查网络或稍后再试。';
} finally {
this.isLoading = false;
}
},
getBackgroundColor(type) {
const backgrounds = {
: {
start: '#FFF0C1', // 浅黄色(柔和的阳光色)
end: '#FFD1D1', // 柔和的粉色(温暖可爱)
},
多云: {
start: '#F3F6FF', // 极浅的柔和蓝色
end: '#DCE6FA', // 柔和的蓝灰色(轻盈的云朵感)
},
: {
start: '#F5F5F5', // 近乎白色的浅灰色
end: '#D3D3D3', // 柔和的浅灰色(朦胧感)
},
小雨: {
start: '#D8F0FF', // 非常柔和的浅蓝色(水滴感)
end: '#BCE0FF', // 更加可爱的浅蓝色
},
中雨: {
start: '#C4DFFF', // 轻柔的灰蓝色
end: '#A1C4F8', // 带点温柔感的蓝色
},
大雨: {
start: '#A8E3FF', // 清新的浅蓝色
end: '#8CCFFF', // 柔和的蓝色渐变
},
雷阵雨: {
start: '#F2E7FF', // 柔和的浅紫色(雷电的闪烁感)
end: '#C2C2C2', // 柔和的灰色(符合雷阵雨的阴沉感)
},
暴雨: {
start: '#D8F0FF', // 非常柔和的浅蓝色(水滴感)
end: '#C2C2C2', // 柔和的灰色
},
阵雨: {
start: '#D8F0FF', // 非常柔和的浅蓝色(水滴感)
end: '#C2C2C2', // 柔和的灰色
},
};
return backgrounds[type] || { start: '#FFF0C1', end: '#FBFBFD' }; // 默认柔和的蓝白色背景
},
getTypeIcon(type) {
console.log(type);
// 根据天气类型返回对应的图标路径
const icons = {
: require('../../src/assets/icons/qing.png'),
多云: require('../../src/assets/icons/duoyun.png'),
: require('../../src/assets/icons/yin.png'),
小雨: require('../../src/assets/icons/xiaoyu.png'),
中雨: require('../../src/assets/icons/zhongyu.png'),
大雨: require('../../src/assets/icons/dayu.png'),
雷阵雨: require('../../src/assets/icons/leizhenyu.png'),
暴雨: require('../../src/assets/icons/dayu.png'),
阵雨: require('../../src/assets/icons/xiaoyu.png'),
// 添加更多天气类型和对应图标
};
return icons[type] || require('../../src/assets/icons/qing.png');
},
init() {
if (this.$store.state.user.roles.includes('admin') || this.$store.state.user.roles.includes('manager') || this.$store.state.user.dept.userName === 'fastbee') {
this.isAdmin = true;
this.getServer();
this.getChartData();
}
},
//刷新iframe
flushIframe() {
var iframe = window.parent.document.getElementById('iframe');
iframe.contentWindow.location.reload(true);
},
/** 查询设备统计信息 */
getDeviceStatistic() {
getDeviceStatistic().then((response) => {
this.deviceStatistic = response.data;
});
},
/** 查询公告列表 */
getNoticeList() {
let queryParams = {
pageNum: 1,
pageSize: 6,
};
listNotice(queryParams).then((response) => {
this.noticeList = response.rows.splice(0, 6);
});
},
// 打开信息详情
openDetail(id) {
this.open = true;
this.loading = true;
getNotice(id).then((response) => {
this.notice = response.data;
this.open = true;
this.loading = false;
});
},
// 取消按钮
closeDetail() {
this.title = '';
this.open = false;
},
/**查询所有设备 */
getAllDevice() {
listAllDeviceShort(this.queryParams).then((response) => {
this.deviceList = response.rows;
this.deviceCount = response.total;
this.loadMap();
});
},
/**加载地图*/
loadMap() {
this.$nextTick(() => {
loadBMap().then(() => {
this.getmap(); // 获取地图
this.getLocation(); // 获取位置
});
});
},
/** 查询服务器信息 */
getServer() {
getServer().then((response) => {
this.server = response.data;
console.log(response.data);
this.tableData = [
{
server: this.$t('home.serverName'),
serverContent: this.server.sys.computerName,
java: this.$t('home.javaName'),
javaContent: this.server.jvm.name,
},
{
server: this.$t('home.serverIp'),
serverContent: this.server.sys.computerIp,
java: this.$t('home.startTime'),
javaContent: this.server.jvm.startTime,
},
{
server: this.$t('home.system'),
serverContent: this.server.sys.osName,
java: this.$t('home.javaVer'),
javaContent: this.server.jvm.version,
},
{
server: this.$t('home.architecture'),
serverContent: this.server.sys.osArch,
java: this.$t('home.runtime'),
javaContent: this.server.jvm.runTime,
},
{
server: this.$t('home.core'),
serverContent: this.server.cpu.cpuNum,
java: this.$t('home.memory'),
javaContent: this.server.jvm.used,
},
{
server: this.$t('home.size'),
serverContent: this.server.mem.total,
java: this.$t('home.JVM'),
javaContent: this.server.jvm.total,
},
];
this.$nextTick(() => {
this.drawPieCpu();
this.drawPieMemery();
this.drawPieDisk();
});
});
},
/** 地图 */
getmap() {
this.mapChart = this.$echarts.init(this.$refs.map);
var option;
// 单击事件
this.mapChart.on('click', (params) => {
if (params.data.deviceId) {
this.$router.push({
path: '/iot/device-edit',
query: {
t: Date.now(),
deviceId: params.data.deviceId,
},
});
}
});
// 格式化数据
let convertData = function (data, status) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = [data[i].longitude, data[i].latitude];
if (geoCoord && data[i].status == status) {
res.push({
name: data[i].deviceName,
value: geoCoord,
status: data[i].status,
isShadow: data[i].isShadow,
firmwareVersion: data[i].firmwareVersion,
networkAddress: data[i].networkAddress,
productName: data[i].productName,
activeTime: data[i].activeTime == null ? '' : data[i].activeTime,
deviceId: data[i].deviceId,
serialNumber: data[i].serialNumber,
locationWay: data[i].locationWay,
});
}
}
return res;
};
option = {
title: {
text: this.$t('home.onlineDevice') + this.deviceList.filter((x) => x.status == 3).length + '',
subtext: 'Fastbee open source iot platform',
sublink: 'https://iot.fastbee.cn',
target: '_blank',
textStyle: {
color: '#303133',
textBorderColor: '#fff',
textBorderWidth: 10,
fontSize: 16,
},
top: 10,
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: function (params) {
var htmlStr = '<div style="padding:5px;line-height:28px;">';
htmlStr += "设备名称: <span style='color:#486FF2'>" + params.data.name + '</span><br />';
htmlStr += '设备编号: ' + params.data.serialNumber + '<br />';
htmlStr += '设备状态: ';
if (params.data.status == 1) {
htmlStr += "<span style='color:#E6A23C'>未激活</span>" + '<br />';
} else if (params.data.status == 2) {
htmlStr += "<span style='color:#F56C6C'>禁用</span>" + '<br />';
} else if (params.data.status == 3) {
htmlStr += "<span style='color:#67C23A'>在线</span>" + '<br />';
} else if (params.data.status == 4) {
htmlStr += "<span style='color:#909399'>离线</span>" + '<br />';
}
if (params.data.isShadow == 1) {
htmlStr += '设备影子: ' + "<span style='color:#67C23A'>启用</span>" + '<br />';
} else {
htmlStr += '设备影子: ' + "<span style='color:#909399'>未启用</span>" + '<br />';
}
htmlStr += '产品名称: ' + params.data.productName + '<br />';
htmlStr += '固件版本: Version ' + params.data.firmwareVersion + '<br />';
htmlStr += '激活时间: ' + params.data.activeTime + '<br />';
htmlStr += '定位方式: ';
if (params.data.locationWay == 1) {
htmlStr += '自动定位' + '<br />';
} else if (params.data.locationWay == 2) {
htmlStr += '设备定位' + '<br />';
} else if (params.data.locationWay == 3) {
htmlStr += '自定义位置' + '<br />';
} else {
htmlStr += '未知' + '<br />';
}
htmlStr += '所在地址: ' + params.data.networkAddress + '<br />';
htmlStr += '</div>';
return htmlStr;
},
},
bmap: {
center: [105, 38],
zoom: 5,
roam: true,
mapStyle: {
styleJson: [
{
featureType: 'water',
elementType: 'all',
stylers: {
color: '#a0cfff',
},
},
{
featureType: 'land',
elementType: 'all',
stylers: {
color: '#fafafa', // #fffff8 淡黄色
},
},
{
featureType: 'railway',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'highway',
elementType: 'all',
stylers: {
color: '#fdfdfd',
},
},
{
featureType: 'highway',
elementType: 'labels',
stylers: {
visibility: 'off',
},
},
{
featureType: 'arterial',
elementType: 'geometry',
stylers: {
color: '#fefefe',
},
},
{
featureType: 'arterial',
elementType: 'geometry.fill',
stylers: {
color: '#fefefe',
},
},
{
featureType: 'poi',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'green',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'subway',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'manmade',
elementType: 'all',
stylers: {
color: '#d1d1d1',
},
},
{
featureType: 'local',
elementType: 'all',
stylers: {
color: '#d1d1d1',
},
},
{
featureType: 'arterial',
elementType: 'labels',
stylers: {
visibility: 'off',
},
},
{
featureType: 'boundary',
elementType: 'all',
stylers: {
color: '#999999',
},
},
{
featureType: 'building',
elementType: 'all',
stylers: {
color: '#d1d1d1',
},
},
{
featureType: 'label',
elementType: 'labels.text.fill',
stylers: {
color: '#999999',
},
},
],
},
},
series: [
{
type: 'scatter',
coordinateSystem: 'bmap',
data: convertData(this.deviceList, 1),
symbolSize: 15,
itemStyle: {
color: '#E6A23C',
},
},
{
type: 'scatter',
coordinateSystem: 'bmap',
data: convertData(this.deviceList, 2),
symbolSize: 15,
itemStyle: {
color: '#F56C6C',
},
},
{
type: 'scatter',
coordinateSystem: 'bmap',
data: convertData(this.deviceList, 4),
symbolSize: 15,
itemStyle: {
color: '#909399',
},
},
{
type: 'effectScatter',
coordinateSystem: 'bmap',
data: convertData(this.deviceList, 3),
symbolSize: 15,
showEffectOn: 'render',
rippleEffect: {
brushType: 'stroke',
scale: 5,
},
label: {
formatter: '{b}',
position: 'right',
show: false,
},
itemStyle: {
color: '#67C23A',
shadowBlur: 100,
shadowColor: '#333',
},
zlevel: 1,
},
],
};
option && this.mapChart.setOption(option);
},
handleResize() {
// 在大小变化时重新渲染图表
if (this.mapChart) {
this.mapChart.resize();
}
if (this.pieCpuChart) {
this.pieCpuChart.resize();
this.updateRadius();
}
if (this.rateChart) {
this.rateChart.resize();
}
if (this.sysChart) {
this.sysChart.resize();
}
if (this.loginUserChart) {
this.loginUserChart.resize();
}
if (this.mqttChart) {
this.mqttChart.resize();
}
},
// cpu使用率
drawPieCpu() {
this.pieCpuChart = this.$echarts.init(this.$refs.pieCpu);
// 基于准备好的dom初始化echarts实例
var option;
let userd = ((this.server.cpu.used / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)) * 100).toFixed(0);
let sys = ((this.server.cpu.sys / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)) * 100).toFixed(0);
let free = ((this.server.cpu.free / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)) * 100).toFixed(0);
option = {
title: {
text: '',
left: 'center',
top: '20px',
textStyle: {
fontSize: 18,
color: '#333',
},
},
series: [
{
type: 'pie',
clockWise: false,
startAngle: 90,
radius: this.radius,
center: ['15%', '50%'],
data: [100],
itemStyle: {
color: '#eee',
},
animation: false,
},
{
type: 'pie',
labelLine: {
show: false,
},
radius: this.radius,
center: ['15%', '50%'],
itemStyle: {
color: '#000',
},
data: [
{
value: userd,
label: {
normal: {
formatter: `{label|${userd}%}`,
position: 'center',
show: true,
textStyle: {
fontSize: '20',
fontWeight: 'bold',
color: '#eee',
lineHeight: 20,
rich: {
label: {
fontFamily: ' Roboto, Roboto',
textAlign: 'center',
fontStyle: 'normal',
fontSize: this.labelFontSize,
fontWeight: 'bold',
color: '#303133',
},
},
},
},
},
itemStyle: {
normal: {
// 渐变色
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0,
color: '#4474ec',
},
{
offset: 1,
color: '#92b3fa',
},
],
false
),
borderRadius: ['50%', '50%'],
},
},
},
{
value: 100 - userd,
itemStyle: {
normal: {
color: 'transparent',
borderCap: 'round',
},
},
},
],
},
{
type: 'pie',
clockWise: false,
startAngle: 90,
radius: this.radius,
center: ['48%', '50%'],
data: [100],
itemStyle: {
color: '#eee',
},
animation: false,
},
{
type: 'pie',
labelLine: {
show: false,
},
radius: this.radius,
center: ['48%', '50%'],
itemStyle: {
color: '#000',
},
data: [
{
value: sys,
label: {
normal: {
formatter: `{label|${sys}%}`,
position: 'center',
show: true,
textStyle: {
fontSize: '20',
fontWeight: 'bold',
color: '#eee',
lineHeight: 20,
rich: {
label: {
fontFamily: ' Roboto, Roboto',
textAlign: 'center',
fontStyle: 'normal',
fontSize: this.labelFontSize,
fontWeight: 'bold',
color: '#303133',
},
},
},
},
},
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(
0,
0,
1,
0,
[
{
offset: 0,
color: '#10bcff',
},
{
offset: 1,
color: '#81e1fd',
},
],
false
),
borderRadius: ['50%', '50%'],
},
},
},
{
value: 100 - sys,
itemStyle: {
normal: {
color: 'transparent',
borderCap: 'round',
},
},
},
],
},
{
type: 'pie',
clockWise: false,
startAngle: 90,
radius: this.radius,
center: ['78%', '50%'],
data: [100],
itemStyle: {
color: '#eee',
},
animation: false,
},
{
type: 'pie',
labelLine: {
show: false,
},
radius: this.radius,
center: ['78%', '50%'],
data: [
{
value: free,
label: {
normal: {
formatter: `{label|${free}%}`,
position: 'center',
show: true,
textStyle: {
fontSize: '20',
fontWeight: 'bold',
color: '#eee',
lineHeight: 20,
rich: {
label: {
fontFamily: ' Roboto, Roboto',
textAlign: 'center',
fontStyle: 'normal',
fontSize: this.labelFontSize,
fontWeight: 'bold',
color: '#303133',
},
},
},
},
},
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#17bcb8' }, // 渐变色
{ offset: 1, color: '#75e2dc' },
]),
borderRadius: ['50%', '50%'],
},
},
},
{
value: 100 - free,
itemStyle: {
normal: {
color: 'transparent',
borderCap: 'round',
},
},
},
],
},
],
graphic: [
{
type: 'text',
left: 10, // 第一个图的名称位置
top: '15%', // 第一个图的名称位置
style: {
text: '用户',
font: ' 400 12px PingFangSC, PingFang SC',
fill: '#606266',
},
},
{
type: 'text',
left: '30%',
top: '15%',
style: {
text: '系统',
font: ' 400 12px PingFangSC, PingFang SC',
fill: '#606266',
},
},
{
type: 'text',
left: '60%',
top: '15%',
style: {
text: '空闲',
font: ' 400 12px PingFangSC, PingFang SC',
fill: '#606266',
},
},
],
};
option && this.pieCpuChart.setOption(option);
},
// 内存使用率
drawPieMemery() {
// 初始化echarts实例
this.rateChart = this.$echarts.init(this.$refs.pieMemery);
var option;
var value = this.server.mem.used / (this.server.mem.used + this.server.mem.free);
option = {
title: [
{
text: (value * 100).toFixed(0) + '%',
x: 'center',
y: '35%',
textStyle: {
fontFamily: ' Roboto, Roboto',
fontWeight: 'bold',
fontStyle: 'normal',
fontSize: 24,
lineHeight: 28,
color: '#303133',
alignment: 'center',
},
},
{
text: `已用:{a|${this.server.mem.used}}GB\n剩余{a|${this.server.mem.free}} GB`,
x: 'center',
y: '50%',
borderColor: '#fff',
textStyle: {
fontWeight: 'normal',
fontSize: 12,
color: '#444',
rich: {
a: {
fontFamily: 'PingFangSC, PingFang SC',
fontStyle: 'normal',
textAlign: 'center',
fontWeight: '400',
fontSize: 12,
lineHeight: 17,
color: '#606266',
},
},
},
},
],
polar: {
center: ['50%', '50%'],
radius: ['65%', '90%'],
},
angleAxis: {
max: 100,
show: false,
},
radiusAxis: {
type: 'category',
show: true,
axisLabel: {
show: false,
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
},
series: [
{
data: [
{
value: (this.server.mem.used / (this.server.mem.used + this.server.mem.free)) * 100,
name: '已使用',
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#887BF2' }, // 渐变色
{ offset: 1, color: '#BDB2FA' },
]),
borderRadius: ['50%', '50%'],
},
},
},
],
name: '',
type: 'bar',
roundCap: true,
showBackground: true,
backgroundStyle: {
color: '#eaeaf4',
},
coordinateSystem: 'polar',
},
],
};
option && this.rateChart.setOption(option);
},
//系统盘使用率
drawPieDisk() {
// 基于准备好的dom初始化echarts实例
this.sysChart = this.$echarts.init(this.$refs.pieDisk);
var option;
let one = this.server.sysFiles[0].used.replace('GB', '');
let two = this.server.sysFiles[0].free.replace('GB', '');
let values = parseFloat(one) / (parseFloat(one) + parseFloat(two));
var value = values;
option = {
title: [
{
text: (value * 100).toFixed(0) + '%',
x: 'center',
y: '35%',
textStyle: {
fontFamily: ' Roboto, Roboto',
fontWeight: 'bold',
fontStyle: 'normal',
fontSize: 24,
lineHeight: 28,
color: '#303133',
alignment: 'center',
},
},
{
text: `{a|已用:${one}}GB\n{a|剩余:${two}} GB`,
x: 'center',
y: '50%',
borderColor: '#fff',
textStyle: {
fontWeight: 'normal',
fontSize: 12,
color: '#444',
rich: {
a: {
fontFamily: 'PingFangSC, PingFang SC',
fontStyle: 'normal',
textAlign: 'center',
fontWeight: '400',
fontSize: 12,
lineHeight: 17,
color: '#606266',
},
},
},
},
],
polar: {
center: ['50%', '50%'],
radius: ['65%', '90%'],
},
angleAxis: {
max: 100,
show: false,
},
radiusAxis: {
type: 'category',
show: true,
axisLabel: {
show: false,
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
},
series: [
{
data: [
{
value: value * 100,
name: '已使用',
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#F286D8' }, // 渐变色
{ offset: 1, color: '#FFC3F1' },
]),
borderRadius: ['50%', '50%'],
},
},
},
],
name: '',
type: 'bar',
roundCap: true,
showBackground: true,
backgroundStyle: {
color: '#eaeaf4',
},
coordinateSystem: 'polar',
},
],
};
option && this.sysChart.setOption(option);
},
// 登录用户数量折线图的初始化
drawLine() {
this.loginUserChart = this.$echarts.init(this.$refs.lineChart);
var option;
option = {
title: {
text: this.$t('views.index.394840-16'),
textStyle: {
fontFamily: 'PingFangSC, PingFang SC',
lineHeight: 22,
fontWeight: 600,
fontSize: 16,
color: '#303133',
fontStyle: 'normal',
textAlign: 'left',
},
},
tooltip: {
trigger: 'axis',
},
xAxis: {
type: 'category',
boundaryGap: false,
data: this.linechart.date,
axisLabel: {
formatter: function (value) {
return moment(value).format('YYYY.MM.DD');
},
},
},
yAxis: [
{
type: 'value',
splitNumber: 4,
splitLine: {
lineStyle: {
type: 'dashed',
color: '#DDD',
},
},
axisLine: {
show: false,
lineStyle: {
color: '#333',
},
},
nameTextStyle: {
color: '#999',
},
splitArea: {
show: false,
},
},
],
series: [
{
name: this.$t('views.index.394840-16'),
data: this.linechart.counts,
radius: '55%',
type: 'line',
smooth: true, // 设置为平滑折线
showSymbol: false,
lineStyle: {
width: 3,
color: '#8095d8', // 折线颜色为蓝色
},
areaStyle: {
// 创建从蓝色渐变到白色的效果
color: {
type: 'linear',
x: 0,
y: 1,
x2: 0,
y2: 0,
colorStops: [
{
offset: 0,
color: 'rgba(96, 116, 208, 0)', // 深蓝色
},
{
offset: 1,
color: 'rgba(70, 130, 180, 0.3)', // 透明白色
},
],
global: false, // 不使用全局渐变
},
},
},
],
};
option && this.loginUserChart.setOption(option);
},
},
};
</script>
<style lang="scss" scoped>
.home-page-container {
padding: 10px;
.statistics-container {
margin: 10px 0px 20px !important;
.statistics-item {
margin-bottom: 10px;
}
}
// 设备数据统计显示
.card-panel {
height: 98px;
border-radius: 8px;
background: #fff;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1);
padding: 24px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
/* 悬停效果,增加交互性 */
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}
.device {
.card-panel-icon {
fill: #08bdff;
}
.card-icon {
background-color: #e5f8ff;
&:hover .card-panel-icon {
fill: #007aff;
}
}
}
.product {
.card-panel-icon {
fill: #f385d9;
}
.card-icon {
background-color: #ffeef8;
&:hover .card-panel-icon {
fill: #007aff;
}
}
}
.function {
.card-panel-icon {
fill: #feaf31;
}
.card-icon {
background-color: #fdf0d7;
&:hover .card-panel-icon {
fill: #007aff;
}
}
}
.monitor {
.card-panel-icon {
fill: #1cbdb5;
}
.card-icon {
background-color: #dbf9f7;
&:hover .card-panel-icon {
fill: #007aff;
}
}
}
.alert {
.card-panel-icon {
fill: #f66b6c;
}
.card-icon {
background-color: #ffe8e8;
&:hover .card-panel-icon {
fill: #007aff;
}
}
}
.reports {
.card-panel-icon {
fill: #a076ef;
}
.card-icon {
background-color: #eae8fe;
&:hover .card-panel-icon {
fill: #007aff;
}
}
}
.card-content {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
height: 100%;
.card-icon {
display: flex;
justify-content: center;
align-items: center;
width: 48px;
height: 48px;
margin-bottom: 15px;
padding: 10px;
border-radius: 10px;
/* 图标样式 */
.card-panel-icon {
width: 80%;
height: 80%;
transition: fill 0.3s ease;
}
}
/* 数据区域 */
.card-data {
display: flex;
flex-direction: column;
justify-content: left;
float: left;
margin-left: -10px;
/* 标题样式 */
.card-title {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 13px;
color: #606266;
line-height: 18px;
text-align: left;
font-style: normal;
}
/* 数字样式 */
.card-panel-num {
width: 14px;
height: 28px;
font-size: 24px;
font-weight: 700;
text-align: left;
margin-top: 2px;
font-family: Roboto, Roboto;
font-weight: bold;
color: #303133;
line-height: 28px;
font-style: normal;
}
}
}
/* 响应式设计当屏幕宽度小于968px时 */
@media screen and (max-width: 968px) {
height: 100px; /* 减小卡片高度 */
padding: 10px; /* 减小内边距 */
margin-bottom: 10px;
.card-content {
.card-icon {
width: 40px;
height: 40px;
margin: 20px 0;
}
.card-data {
.card-title {
font-size: 12px;
margin-top: 20px;
}
.card-panel-num {
margin-bottom: 10px;
font-size: 10px;
}
}
}
}
/* 响应式设计当屏幕宽度小于1920px时 */
@media screen and (min-width: 1920px) {
height: 125px;
padding: 10px;
margin-bottom: 10px;
.card-content {
.card-icon {
width: 60px;
height: 60px;
margin: 20px;
}
.card-data {
.card-title {
font-size: 22px;
margin: 30px 0 5px 10px;
}
.card-panel-num {
margin: 0px 0 5px 10px;
font-size: 22px;
}
}
}
}
@media (max-width: 1180px) {
margin-bottom: 15px;
}
/* 数字递增动画关键帧 */
@keyframes countAnimation {
0% {
opacity: 0;
transform: translateY(-10px);
}
50% {
opacity: 1;
transform: translateY(5px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
//数据完
// 天气
$primary-gradient-start: #a1c4fd;
$primary-gradient-end: #c2e9fb;
$secondary-color: #000;
$icon-color: #000;
$shadow-color: rgba(0, 0, 0, 0.1);
$hover-shadow-color: rgba(0, 0, 0, 0.2);
$aqi-good-color: #4caf50;
$aqi-moderate-color: #ffeb3b;
$aqi-unhealthy-color: #f44336;
.weather-card {
height: 208px;
background: linear-gradient(135deg, var(--background-start) 0%, var(--background-end) 100%);
border-radius: 10px;
color: $secondary-color;
box-shadow: 0 4px 20px $shadow-color;
transition: transform 0.3s, box-shadow 0.3s;
display: flex;
flex-direction: column;
justify-content: space-between;
.weather-header {
text-align: left;
margin-bottom: 10px;
h2 {
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 18px;
color: #303133;
line-height: 10px;
text-align: left;
font-style: normal;
}
.date-week {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 13px;
color: #606266;
line-height: 18px;
text-align: left;
font-style: normal;
}
}
@media screen and (min-width: 1920px) {
height: 270px;
padding: 10px;
margin-bottom: 10px;
}
.weather-main {
//天气图标
display: flex;
justify-content: center;
align-items: center;
margin: 30px 0;
.weather-icon {
width: 100px; // 增大图标尺寸
height: 100px;
flex-shrink: 0;
}
}
.weather-details {
.detail-item {
display: flex;
align-items: center;
gap: 2px;
color: #606266;
.detail-text {
font-weight: 500;
font-size: 12px;
line-height: 18px;
text-align: left;
font-style: normal;
}
}
}
.air-quality {
margin-top: 15px;
padding: 5px 10px;
display: flex;
justify-content: center;
align-items: center;
width: 140px;
height: 22px;
border-radius: 2px;
border: 1px solid #67c23a;
.aqi-info {
display: flex;
align-items: center;
gap: 10px;
width: 105px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 13px;
color: #67c23a;
line-height: 18px;
text-align: left;
font-style: normal;
.aqi-value {
font-size: 12px;
font-weight: bold;
padding: 5px;
border-radius: 5px;
color: #4caf50;
background-color: inherit; // 继承父元素的背景色
}
&.aqi-good {
background-color: $aqi-good-color;
}
&.aqi-moderate {
background-color: $aqi-moderate-color;
}
&.aqi-unhealthy {
background-color: $aqi-unhealthy-color;
}
.aqi-level {
font-size: 12px;
font-weight: bold;
color: #4caf50;
background-color: inherit; // 继承父元素的背景色
}
}
}
//温度
.low-temperature {
font-weight: bold;
font-size: 40px;
color: #303133;
line-height: 47px;
text-align: left;
font-style: normal;
}
.high-temperature {
font-weight: 400;
font-size: 14px;
color: #606266;
line-height: 16px;
text-align: left;
font-style: normal;
}
.weather-description {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 14px;
color: #303133;
line-height: 20px;
text-align: left;
font-style: normal;
margin: 0px 2px;
}
/* 响应式设计 */
@media screen and (max-width: 968px) {
.weather-card {
padding: 10px;
height: 400px;
}
}
}
//天气完
// 地图
.map-card {
background: #ffffff; /* 使用纯色背景,增加清新感 */
border-radius: 10px; /* 略微减小圆角 */
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); /* 更柔和的阴影 */
transition: transform 0.3s ease, box-shadow 0.3s ease;
height: 460px;
.map-container {
width: 100%;
height: 460px; /* 减少高度 */
position: relative;
border-radius: 10px;
overflow: hidden;
.map {
width: 100%;
height: 100%;
/* 可根据需要添加地图相关样式 */
}
}
}
/* 响应式设计 */
@media screen and (max-width: 968px) {
.map-card {
padding: 2px;
}
.card-header {
padding: 18px 12px;
.card-title {
font-size: 16px;
i {
font-size: 18px;
}
}
}
.map-container {
height: 250px;
width: 100%;
}
}
@media (max-width: 480px) {
.map-card {
padding: 10px;
border-radius: 10px;
}
.card-header {
padding: 15px 10px;
.card-title {
font-size: 14px;
i {
font-size: 16px;
}
}
}
.map-container {
height: 220px;
}
}
// 地图完
// 信息栏
.message-card {
height: 300px;
margin-bottom: 20px;
border-radius: 10px;
padding: 4px;
box-shadow: 0 4px 20px $shadow-color;
transition: transform 0.3s, box-shadow 0.3s;
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 使用省略号 */
.notice-bar {
overflow: hidden;
position: relative;
.item-wrap {
display: flex;
flex-direction: row;
margin-top: 18px;
.left-wrap {
flex: 1;
font-weight: 400;
font-size: 14px;
color: #303133;
line-height: 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.right-wrap {
font-weight: 400;
font-size: 12px;
color: #909399;
line-height: 17px;
margin-left: 16px;
}
}
}
//滚动效果
.animating {
animation: scroll 10s linear infinite;
}
//信息栏标题样式
.message-title {
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 18px;
color: #303133;
line-height: 22px;
text-align: left;
font-style: normal;
margin: 0px 0 6px;
}
}
//信息完
// 登录用户数量
.line-card {
height: 320px;
width: 100%;
border-radius: 10px;
padding: 4px;
color: $secondary-color;
margin-bottom: 20px;
box-shadow: 0 4px 20px $shadow-color;
transition: transform 0.3s, box-shadow 0.3s;
}
// 折线图完
// 柱状图mqtt状态数据
.card-container {
height: 360px;
padding: 4px;
margin-bottom: 20px;
border-radius: 10px;
box-shadow: 0 4px 20px $shadow-color;
overflow: hidden;
transition: transform 0.3s, box-shadow 0.3s;
}
/* 响应式设计 */
@media screen and (max-width: 968px) {
.card-container {
.card {
flex-direction: column;
height: auto;
.card-image {
width: 100%;
height: auto;
}
.card-content {
width: 100%;
}
}
}
}
@media (max-width: 480px) {
.card-container {
.card {
.card-content {
padding: 10px;
.author {
font-size: 1em;
}
.content {
font-size: 0.9em;
}
.footer {
font-size: 0.8em;
}
}
.card-image {
.overlay .thumb {
width: 40px;
height: 40px;
}
}
}
}
}
// 使用率
.rate {
height: 440px;
padding: 4px;
width: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
.chart-title {
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 16px;
color: #303133;
line-height: 22px;
text-align: left;
font-style: normal;
}
.pieCpu {
height: 190px;
width: 100%;
margin: 20px 0px;
display: flex;
flex: 1;
background: #f6f7fb;
}
@media screen and (max-width: 968px) {
height: 100%;
width: 100%;
margin: 20px 0px;
.pieCpu {
height: 176px;
margin: 20px 0px;
background: #f6f7fb;
display: flex;
flex-direction: row;
flex-wrap: wrap; /* 允许换行 */
justify-content: space-around; /* 对齐分布 */
}
}
@media (min-width: 968px) {
height: 100%;
width: 100%;
margin: 20px 0px;
}
.pieMemery {
background: #f6f7fb;
height: 176px;
margin: 20px 20px 20px 0px;
}
.pieDisk {
background: #f6f7fb;
height: 176px;
margin: 20px 0px 20px 0px;
}
}
// 使用率完
//小程序h5
.phone-card {
border-radius: 10px;
box-shadow: 0 4px 20px $shadow-color;
transition: transform 0.3s, box-shadow 0.3s;
.mini-program {
margin: 8px 0px 35px;
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 14px;
color: #303133;
line-height: 20px;
text-align: left;
font-style: normal;
}
.web-site {
width: 70px;
font-weight: bold;
display: table-cell;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 12px;
color: #606266;
line-height: 24px;
text-align: left;
font-style: normal;
}
.other-site {
display: table-cell;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #606266;
line-height: 24px;
text-align: left;
font-style: normal;
}
}
.phone {
height: 650px;
width: 320px;
margin-left: 100px;
background-image: url('../assets/images/phone.png');
background-size: cover;
.frame-remark {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #909399;
line-height: 17px;
text-align: center;
font-style: normal;
margin: 20px auto;
}
@media screen and (max-width: 968px) {
height: 530px;
width: 260px;
background-image: url('../assets/images/phone.png');
background-size: cover;
}
}
.phone-container {
height: 635px;
width: 291px;
position: relative;
border-radius: 35px;
top: 8px;
left: 15px;
background-color: #fff;
padding-bottom: 0%;
@media screen and (max-width: 968px) {
height: 519px;
width: 240px;
position: relative;
border-radius: 30px;
top: 5px;
bottom: 10px;
left: 10px;
background-color: #fff;
padding-bottom: 0%;
}
}
.content {
line-height: 24px;
padding: 10px;
border: 1px solid #eee;
border-radius: 10px;
}
.description {
font-size: 12px;
tr {
line-height: 20px;
}
}
//底部
.footer-container {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #909399;
line-height: 24px;
text-align: center;
font-style: normal;
margin: 10px 10px 0px 10px;
}
}
</style>