Xazn-vue/src/views/index.vue

2521 lines
97 KiB
Vue
Raw Permalink Normal View History

2025-05-22 16:32:24 +08:00
<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>