Files
iot_web/src/views/index.vue
2026-03-23 13:51:00 +08:00

2548 lines
90 KiB
Vue
Raw 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 style="padding: 10px; background-color: #f8f8f8">
<!-- 天气以及设备统计信息 -->
<el-row :gutter="20" style="margin: 10px 0px 20px 0px">
<el-col :xs="24" :sm="24" :md="24" :lg="6" :xl="6">
<!-- 天气 -->
<el-card
class="weather-card"
shadow="hover"
:style="{
'--background-start': getBackgroundColor(weatherData.data.type).start,
'--background-end': getBackgroundColor(weatherData.data.type).end,
}"
>
<!-- 头部城市名称日期与星期 -->
<div class="weather-header">
<h2>{{ weatherData.city }}</h2>
<div class="date-week">
<span>{{ weatherData.data.date }}</span>
<span>{{ weatherData.data.week }}</span>
</div>
<p class="weather-description">{{ weatherData.data.type }}</p>
</div>
<!-- 主体天气图标与温度 -->
<div class="weather-main">
<img :src="weatherData.data.typeIcon" alt="天气图标" class="weather-icon" />
<div class="temperature">{{ weatherData.data.low }} - {{ weatherData.data.high }}</div>
</div>
<!-- 详情信息风向风力 -->
<div class="weather-details">
<div class="detail-item">
<i class="el-icon-cloudy-and-sunny"></i>
<span class="detail-text">{{ weatherData.data.fengxiang }}</span>
</div>
<div class="detail-item">
<i class="el-icon-s-operation"></i>
<span class="detail-text">{{ weatherData.data.fengli }}</span>
</div>
</div>
<!-- 空气质量信息 -->
<div class="air-quality">
<div class="aqi-info">
<span class="aqi-value" :class="aqiClass">空气质量</span>
<span class="aqi-level" :class="aqiClass">{{ weatherData.air.aqi }}</span>
<span class="aqi-level">{{ weatherData.air.aqi_name }}</span>
</div>
</div>
<!-- 天气提示 -->
<div class="weather-tip">
<i class="el-icon-info"></i>
<span>{{ weatherData.tip }}</span>
</div>
</el-card>
</el-col>
<!-- 设备统计 -->
<el-col :xs="24" :sm="24" :md="24" :lg="10" :xl="10">
<el-row :gutter="20" style="height: 420px">
<el-col :span="8" style="margin-bottom: 20px">
<!-- 设备数量 -->
<div class="card-panel" style="background: linear-gradient(135deg, #f0fff0 0%, #f5fffa 100%)">
<div class="card-content device">
<div class="card-icon">
<svg-icon icon-class="device" class-name="card-panel-icon" />
</div>
<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>
</div>
</div>
</el-col>
<el-col :span="8" style="margin-bottom: 20px">
<!-- 产品数量 -->
<div class="card-panel" style="background: linear-gradient(135deg, #fffaf0 0%, #fff0f5 100%)">
<div class="card-content product">
<div class="card-icon">
<svg-icon icon-class="model" class-name="card-panel-icon" />
</div>
<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>
</div>
</div>
</el-col>
<el-col :span="8" style="margin-bottom: 20px">
<!-- 操作记录 -->
<div class="card-panel" style="background: linear-gradient(135deg, #fffef3 0%, #fffdf0 100%)">
<div class="card-content function">
<div class="card-icon">
<svg-icon icon-class="log-a" class-name="card-panel-icon" />
</div>
<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>
</div>
</div>
</el-col>
<el-col :span="8">
<!-- 监测数据 -->
<div class="card-panel" style="background: linear-gradient(135deg, #e3f2fd 0%, #e1f5fe 100%)">
<div class="card-content monitor">
<div class="card-icon">
<svg-icon icon-class="monitor-a" class-name="card-panel-icon" />
</div>
<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>
</div>
</div>
</el-col>
<el-col :span="8">
<!-- 告警数量 -->
<div class="card-panel" style="background: linear-gradient(135deg, #f0e7ff 0%, #f6f0ff 100%)">
<div class="card-content alert">
<div class="card-icon">
<svg-icon icon-class="alert" class-name="card-panel-icon" />
</div>
<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>
</div>
</div>
</el-col>
<el-col :span="8">
<!-- 上报事件 -->
<div class="card-panel" style="background: linear-gradient(135deg, #f3f9ec 0%, #edfaea 100%)">
<div class="card-content reports">
<div class="card-icon">
<svg-icon icon-class="event-a" class-name="card-panel-icon" />
</div>
<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>
</div>
</div>
</el-col>
</el-row>
<!-- 统计信息 -->
<!-- <el-card shadow="none" style="height: 220px; background-color: #ccc; margin-bottom: 10px"></el-card>
<el-card shadow="none" style="height: 220px; background-color: #ccc"></el-card> -->
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<!-- 一个折线图 -->
<el-card class="line-card" shadow="hover">
<div ref="line" style="height: 400px; width: 100%"></div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin: 10px 0px 20px 0px">
<!-- 地图部分 -->
<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>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<!-- mqtt状态数据 -->
<div class="card-container">
<div ref="statsChart" style="height: 240px; width: 100%"></div>
</div>
</el-col>
</el-row>
<el-card class="rate" shadow="hover" style="margin: 10px 10px 20px 10px">
<el-row :gutter="80" v-if="isAdmin">
<!-- cpu使用率 -->
<el-col :xs="24" :sm="24" :md="24" :lg="14" :xl="12">
<div style="">
<div ref="pieCpu" class="pieCpu"></div>
</div>
</el-col>
<!-- 内存使用率 -->
<el-col :xs="24" :sm="24" :md="24" :lg="5" :xl="6">
<div style="">
<div ref="pieMemery" style="height: 161px"></div>
</div>
</el-col>
<!-- 系统盘使用率 -->
<el-col :xs="24" :sm="24" :md="24" :lg="5" :xl="6">
<div style="">
<div ref="pieDisk" style="height: 161px"></div>
</div>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script>
import axios from 'axios';
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 { color, time } from 'echarts';
import { searchUserCount } from '@/api/monitor/jobLog';
import { getNettyMqttStats, statisticNettyMqtt } from '@/api/iot/netty';
// import { count } from 'echarts/types/src/component/dataZoom/history.js';
export default {
name: 'Index',
components: {
CountTo,
},
data() {
return {
//天气信息
weatherData: {
success: true,
city: '北京市',
data: {
date: '2024-03-14',
week: '星期四',
type: '晴',
typeIcon: require('../../src/assets/icons/qing.png'), // 确保路径正确
low: '5°C',
high: '21°C',
fengxiang: '西南风',
fengli: '3-4级',
},
air: {
aqi: 85,
aqi_level: 2,
aqi_name: '良',
},
tip: '天有点冷,注意保暖~',
},
// 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, // 公告间隔时间(单位:毫秒)
// 服务器信息
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();
},
beforeDestroy() {
this.stopScroll();
},
created() {
this.init();
this.getAllDevice();
this.getNoticeList();
this.getDeviceStatistic();
this.fetchWeather();
this.getMqttStats();
this.statisticMqtt();
},
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);
});
console.log('折线图数据', this.linechart);
this.drawLine();
// this.responseData.data.forEach(item => {
// this.chartData.xAxisData.push(item.datetime);
// this.chartData.yAxisData.push(item.user_count);
//});
});
},
// mqtt统计数据
statisticMqtt() {
statisticNettyMqtt().then((response) => {
this.static = response.data;
});
},
/** 查询mqtt状态数据*/
getMqttStats() {
getNettyMqttStats().then((response) => {
this.stats = response.data;
this.drawStats();
});
},
// 绘制柱状图
drawStats() {
// 基于准备好的dom初始化echarts实例
let myChart = this.$echarts.init(this.$refs.statsChart);
var option;
option = {
title: {
text: this.$t('netty.mqtt.564432-12'),
textStyle: {
fontSize: 19,
color: '#4A4A4A',
fontWeight: 'bold',
fontFamily: 'Arial, sans-serif', // 现代字体
},
left: 'left',
top: '5%',
},
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(255, 255, 255, 0.95)', // 更加柔和的背景
borderColor: '#FF6F61', // 边框颜色
borderWidth: 1,
textStyle: {
color: '#333', // 文字颜色
},
},
legend: {
textStyle: {
color: '#4A4A4A',
fontFamily: 'Arial, sans-serif',
},
orient: 'vertical',
right: '10%',
top: '15%',
itemGap: 10, // 图例之间的间距
},
grid: {
left: '10%',
right: '10%',
bottom: '10%',
containLabel: true,
},
xAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#B0BEC5', // 和谐的轴线颜色
},
},
splitNumber: 3,
splitLine: {
lineStyle: {
type: 'dashed',
color: '#E0E0E0',
},
},
},
yAxis: {
type: 'category',
axisLabel: {
fontSize: 14,
color: '#455A64', // 标签颜色
fontFamily: 'Arial, sans-serif', // 现代字体
},
splitNumber: 3,
axisLine: {
lineStyle: {
color: '#B0BEC5',
},
},
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'),
type: 'bar',
data: [this.stats['connection_count'], this.stats['session_count'], this.stats['subscription_count'], this.stats['retain_count'], this.stats['retain_count']],
itemStyle: {
color: new this.$echarts.graphic.LinearGradient(
0,
0,
1,
0, // 横向渐变
[
{ offset: 0, color: '#FFE3E3' }, // 更加浅的粉色
{ offset: 1, color: '#FFD1D1' }, // 更加柔和的粉色
]
),
borderRadius: [20, 20, 20, 20], // 圆角柱子
shadowColor: 'rgba(255, 105, 180, 0.5)', // 柔和的阴影
shadowBlur: 10, // 阴影模糊度
shadowOffsetX: 5, // 阴影偏移
shadowOffsetY: 5, // 阴影偏移
},
globalCoord: false, // 缺省为 false
},
{
name: this.$t('netty.mqtt.564432-19'),
type: 'bar',
data: [this.stats['connection_total'], this.stats['session_total'], this.stats['subscription_total'], this.stats['retain_total'], this.stats['retain_total']],
itemStyle: {
color: new this.$echarts.graphic.LinearGradient(
0,
0,
1,
0, // 横向渐变
[
{ offset: 0, color: '#85E3FF' }, // 清新的蓝色
{ offset: 1, color: '#B9FBC0' }, // 更加柔和的绿色
]
),
borderRadius: [20, 20, 20, 20], // 圆角柱子
shadowColor: 'rgba(0, 191, 255, 0.5)', // 柔和的阴影
shadowBlur: 10, // 阴影模糊度
shadowOffsetX: 5, // 阴影偏移
shadowOffsetY: 5, // 阴影偏移
},
globalCoord: false, // 缺省为 false
},
],
};
option && myChart.setOption(option);
},
// 天气
async fetchWeather() {
this.isLoading = true;
this.error = null;
try {
const response = await axios.get('https://api.vvhan.com/api/weather');
if (response.data && response.data.success) {
console.log(response.data);
this.weatherData = {
success: true,
city: response.data.city,
data: {
date: response.data.data.date,
week: response.data.data.week,
type: response.data.data.type,
typeIcon: this.getTypeIcon(response.data.data.type),
low: response.data.data.low,
high: response.data.data.high,
fengxiang: response.data.data.fengxiang,
fengli: response.data.data.fengli,
night: {
type: response.data.data.night.type,
fengxiang: response.data.data.night.fengxiang,
fengli: response.data.data.night.fengli,
},
},
air: {
aqi: response.data.air.aqi,
aqi_level: response.data.air.aqi_level,
aqi_name: response.data.air.aqi_name,
co: response.data.air.co,
no2: response.data.air.no2,
o3: response.data.air.o3,
pm10: response.data.air.pm10,
pm2_5: response.data.air.pm2_5,
so2: response.data.air.so2,
},
tip: response.data.tip,
};
// 动态设置背景颜色
const background = this.getBackgroundColor(response.data.data.type);
document.documentElement.style.setProperty('--background-start', background.start);
document.documentElement.style.setProperty('--background-end', background.end);
} else {
this.error = '获取天气数据失败,请稍后重试。';
}
} catch (err) {
console.error(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', // 柔和的灰色
},
};
return backgrounds[type] || { start: '#F5F9FF', 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'),
// 添加更多天气类型和对应图标
};
return icons[type] || require('../../src/assets/icons/qing.png');
},
init() {
if (this.$store.state.user.roles.indexOf('tenant') === -1 && this.$store.state.user.roles.indexOf('general') === -1) {
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();
});
});
},
/** 查询服务器信息 */
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() {
var myChart = this.$echarts.init(this.$refs.map);
var option;
// 单击事件
myChart.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: 'Lidee open source iot platform',
sublink: 'https://iot.lidee.cn',
target: '_blank',
textStyle: {
color: '#333',
textBorderColor: '#fff',
textBorderWidth: 10,
},
top: 10,
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: function (params) {
var htmlStr = '<div style="padding:5px;line-height:28px;">';
htmlStr += "设备名称: <span style='color:#409EFF'>" + 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: [133, 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 && myChart.setOption(option);
},
drawPieCpu() {
// 基于准备好的dom初始化echarts实例
let myChart = this.$echarts.init(this.$refs.pieCpu);
var option;
// 水球图
option = {
backgroundColor: '#fff',
title: {
text: this.$t('home.usage'),
left: 'left',
textStyle: {
fontSize: 16,
},
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c}',
},
grid: {
left: '10%',
right: '10%',
top: '10%',
bottom: '10%',
},
series: [
// CPU 用户
{
type: 'liquidFill',
name: this.$t('home.user'),
radius: '85%',
center: ['25%', '55%'],
amplitude: 10,
backgroundStyle: {
borderWidth: 1,
color: 'rgba(246, 65, 108,0.1)',
},
itemStyle: {
shadowColor: 'rgba(0, 0, 0, 0)',
},
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 1, color: '#FFF6B7' },
{ offset: 0, color: '#fa9eb3' },
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#FFD3A5',
},
{
offset: 0,
color: '#FFD3A5',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#feada6',
},
{
offset: 0,
color: '#f5efef',
},
],
globalCoord: false,
},
],
data: [
{
value: (this.server.cpu.used / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.user'),
},
{
value: (this.server.cpu.used / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.user'),
direction: 'left',
},
{
value: (this.server.cpu.used / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.user'),
},
],
label: {
normal: {
formatter: (value) => {
return (value.value * 100).toFixed(0) + '%';
},
textStyle: {
fontSize: 20,
color: '#333',
},
},
},
outline: {
show: true,
itemStyle: {
borderWidth: 0,
},
borderDistance: 0,
},
},
// CPU 系统
{
type: 'liquidFill',
name: this.$t('home.system'),
radius: '85%',
center: ['50%', '55%'],
amplitude: 10,
backgroundStyle: {
borderWidth: 1,
color: 'rgba(201,219,252, 0.5)',
},
itemStyle: {
shadowColor: 'rgba(0, 0, 0, 0)',
},
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 1, color: '#3C8CE7' },
{ offset: 0, color: '#00EAFF' },
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#e0c3fc',
},
{
offset: 0,
color: '#8ec5fc',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#accbee',
},
{
offset: 0,
color: '#e7f0fd',
},
],
globalCoord: false,
},
],
data: [
{
value: (this.server.cpu.sys / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.system'),
},
{
value: (this.server.cpu.sys / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.system'),
direction: 'left',
},
{
value: (this.server.cpu.sys / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.system'),
},
],
label: {
normal: {
formatter: (value) => {
return (value.value * 100).toFixed(0) + '%';
},
textStyle: {
fontSize: 20,
color: '#333',
},
},
},
outline: {
show: true,
itemStyle: {
borderWidth: 0,
},
borderDistance: 0,
},
},
// CPU 空闲
{
type: 'liquidFill',
name: this.$t('home.free'),
radius: '85%',
center: ['75%', '55%'],
amplitude: 10,
backgroundStyle: {
borderWidth: 1,
color: 'rgba(216, 218, 62, 0.1)',
},
itemStyle: {
shadowColor: 'rgba(0, 0, 0, 0)',
},
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#c4f4fe',
},
{
offset: 0,
color: '#fdefbe',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#FFFEFF',
},
{
offset: 0,
color: '#D7FFFE',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#abecd6',
},
{
offset: 0,
color: '#fbed96',
},
],
globalCoord: false,
},
],
data: [
{
value: (this.server.cpu.free / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.free'),
},
{
value: (this.server.cpu.free / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.free'),
direction: 'left',
},
{
value: (this.server.cpu.free / (this.server.cpu.used + this.server.cpu.sys + this.server.cpu.free)).toFixed(2),
name: this.$t('home.free'),
},
],
label: {
normal: {
formatter: (value) => {
return (value.value * 100).toFixed(0) + '%';
},
textStyle: {
fontSize: 20,
color: '#333',
},
},
},
outline: {
show: true,
itemStyle: {
borderWidth: 0,
},
borderDistance: 0,
},
},
],
legend: {
orient: 'vertical',
right: 20,
data: [this.$t('home.user'), this.$t('home.system'), this.$t('home.free')],
textStyle: {
color: '#333',
fontSize: 14,
},
},
};
option && myChart.setOption(option);
},
drawPieMemery() {
// 基于准备好的dom初始化echarts实例
let myChart = this.$echarts.init(this.$refs.pieMemery);
var option;
// 水球图
var value = this.server.mem.used / 10;
option = {
backgroundColor: '#fff',
title: [
{
text: this.$t('home.memory'),
left: 'left',
textStyle: {
fontSize: 16,
},
},
{
text: '剩余:' + this.server.mem.free + 'G',
left: '50%',
top: '55%',
textAlign: 'center',
textStyle: {
fontSize: '13',
fontWeight: '400',
color: '#fff',
textAlign: 'center',
},
},
{
text: '已用:' + this.server.mem.used + 'G',
left: '50%',
top: '67%',
textAlign: 'center',
textStyle: {
fontSize: '13',
fontWeight: '400',
color: '#fff',
textAlign: 'center',
},
},
],
tooltip: {
trigger: 'item',
formatter: (value) => {
return '已使用:' + value.value.toFixed(2);
},
},
legend: {
orient: 'vertical',
left: 'right',
},
series: [
{
type: 'liquidFill',
radius: '85%',
z: 6,
center: ['50%', '55%'],
amplitude: 10,
backgroundStyle: {
borderWidth: 1,
color: 'rgba(201,219,252, 0.5)', // 球体
},
itemStyle: {
shadowColor: 'rgba(0, 0, 0, 0)',
},
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#fff1eb',
},
{
offset: 0,
color: '#ace0f9',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#3C8CE7',
},
{
offset: 0,
color: '#00EAFF',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#e0c3fc',
},
{
offset: 0,
color: '#8ec5fc',
},
],
globalCoord: false,
},
],
data: [
{
value: value,
name: '已使用',
},
{
value: value,
direction: 'left',
name: '已使用',
},
{
value: value,
name: '已使用',
},
],
label: {
normal: {
formatter: (value) => {
return (value.value * 100).toFixed(0) + '%';
},
position: ['53.3%', '40%'],
textStyle: {
fontSize: 21,
color: '#233d7d',
},
},
},
outline: {
show: true,
itemStyle: {
borderWidth: 0,
},
borderDistance: 0,
},
},
],
};
option && myChart.setOption(option);
},
drawPieDisk() {
// 基于准备好的dom初始化echarts实例
let myChart = 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));
// option = {
// title: {
// text: this.$t('home.disk'),
// left: 'left',
// textStyle: {
// fontSize: 16,
// },
// },
// tooltip: {
// trigger: 'item',
// },
// legend: {
// orient: 'vertical',
// left: 'right',
// },
// color: ['#F56C6C', '#DDD'],
// series: [
// {
// name: this.$t('home.diskStatus'),
// type: 'pie',
// radius: '55%',
// label: {
// show: false,
// },
// labelLine: {
// normal: {
// position: 'inner',
// show: false,
// },
// },
// data: [
// {
// value: one,
// name: this.$t('home.used'),
// },
// {
// value: two,
// name: this.$t('home.available'),
// },
// ],
// },
// ],
// };
// 水球图
var value = values;
option = {
backgroundColor: '#fff',
title: [
{
text: this.$t('home.disk'),
left: 'left',
textStyle: {
fontSize: 16,
},
},
{
text: '剩余:' + two + 'G',
left: '50%',
top: '55%',
textAlign: 'center',
textStyle: {
fontSize: '13',
fontWeight: '400',
color: '#fff',
textAlign: 'center',
},
},
{
text: '已用:' + one + 'G',
left: '50%',
top: '67%',
textAlign: 'center',
textStyle: {
fontSize: '13',
fontWeight: '400',
color: '#fff',
textAlign: 'center',
},
},
],
tooltip: {
trigger: 'item',
formatter: (value) => {
return '已使用:' + value.value.toFixed(2);
},
},
legend: {
orient: 'vertical',
left: 'right',
},
series: [
{
type: 'liquidFill',
radius: '85%',
z: 6,
center: ['50%', '55%'],
amplitude: 10,
backgroundStyle: {
borderWidth: 1,
color: 'rgba(245, 239, 239,0.5)', // 球体
},
itemStyle: {
shadowColor: 'rgba(0, 0, 0, 0)',
},
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#feada6',
},
{
offset: 0,
color: '#feada6',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#FFF6B7',
},
{
offset: 0,
color: '#FFF6B7',
},
],
globalCoord: false,
},
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#ff9a9e',
},
{
offset: 0,
color: '#fecfef',
},
],
globalCoord: false,
},
],
data: [
{
value: value,
name: '已使用',
},
{
value: value,
direction: 'left',
name: '已使用',
},
{
value: value,
name: '已使用',
},
],
label: {
normal: {
formatter: (value) => {
return (value.value * 100).toFixed(0) + '%';
},
position: ['53.3%', '40%'],
textStyle: {
fontSize: 21,
color: '#73545c',
},
},
},
outline: {
show: true,
itemStyle: {
borderWidth: 0,
borderColor: '#ff9a9e',
},
borderDistance: 0,
},
},
],
};
option && myChart.setOption(option);
},
// 折线图的初始化
drawLine() {
let myChart = this.$echarts.init(this.$refs.line);
var option;
option = {
title: {
text: this.$t('views.index.394840-16'),
left: 'left',
textStyle: {
fontSize: 19,
},
},
tooltip: {
trigger: 'axis',
},
legend: {
orient: 'vertical',
left: 'right',
},
xAxis: [
{
type: 'category',
data: this.linechart.date,
axisLine: {
lineStyle: {
color: '#999',
},
},
},
],
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'),
type: 'line',
data: this.linechart.counts,
radius: '55%',
lineStyle: {
normal: {
width: 8,
color: {
type: 'linear',
colorStops: [
{
offset: 0,
color: '#A9F387', // 0% 处的颜色
},
{
offset: 1,
color: '#48D8BF', // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
shadowColor: 'rgba(72,216,191, 0.3)',
shadowBlur: 10,
shadowOffsetY: 20,
},
},
itemStyle: {
normal: {
color: 'rgba(72,216,191, 0.3)',
borderWidth: 10,
/*shadowColor: 'rgba(72,216,191, 0.3)',
shadowBlur: 100,*/
borderColor: '#A9F387',
},
},
smooth: true,
},
],
};
option && myChart.setOption(option);
},
},
};
</script>
<style lang="scss" scoped>
// 天气
$primary-gradient-start: #a1c4fd;
$primary-gradient-end: #c2e9fb;
$secondary-color: #ffffff;
$icon-color: #ffffff;
$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: 420px;
background: linear-gradient(135deg, var(--background-start) 0%, var(--background-end) 100%);
border-radius: 15px;
padding: 16px;
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;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px $hover-shadow-color;
}
.weather-header {
text-align: center;
margin-bottom: 10px;
h2 {
margin: 0;
font-size: 26px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $secondary-color;
}
.date-week {
display: flex;
justify-content: center;
gap: 10px;
font-size: 14px;
color: rgba($secondary-color, 0.8);
margin-top: 5px;
}
.weather-description {
margin-top: 5px;
font-size: 16px;
color: rgba($secondary-color, 0.9);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.weather-main {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
.weather-icon {
width: 100px; // 增大图标尺寸
height: 100px;
flex-shrink: 0;
}
.temperature {
font-size: 23px; // 增大温度字体
font-weight: bold;
color: $secondary-color;
margin-left: 20px;
display: flex;
align-items: center;
}
}
.weather-details {
display: flex;
justify-content: space-around;
margin-top: 10px;
.detail-item {
display: flex;
align-items: center;
font-size: 14px;
color: $secondary-color;
i {
font-size: 18px;
margin-right: 5px;
color: $icon-color;
}
.detail-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.air-quality {
margin-top: 15px;
padding: 10px 15px;
background-color: rgba($secondary-color, 0.15);
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
.aqi-info {
display: flex;
align-items: center;
gap: 10px;
.aqi-value {
font-size: 18px;
font-weight: bold;
padding: 5px 10px;
border-radius: 5px;
color: #fff;
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: 14px;
font-weight: bold;
color: rgba($secondary-color, 0.9);
}
}
}
/* 天气提示 */
.weather-tip {
margin-top: 15px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
background-color: rgba($secondary-color, 0.2);
padding: 10px 15px;
border-radius: 10px;
color: $secondary-color;
i {
font-size: 18px;
margin-right: 10px;
color: $icon-color;
}
span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.weather-card {
padding: 15px;
.weather-header {
h2 {
font-size: 22px;
}
.date-week {
font-size: 12px;
}
.weather-description {
font-size: 14px;
}
}
.weather-main {
flex-direction: column;
.temperature {
margin-left: 0;
margin-top: 10px;
font-size: 22px;
}
.weather-icon {
width: 80px;
height: 80px;
}
}
.weather-details {
flex-direction: column;
align-items: center;
gap: 10px;
}
.air-quality {
padding: 8px 12px;
.aqi-info {
flex-direction: row;
gap: 8px;
.aqi-value {
font-size: 16px;
padding: 4px 8px;
}
.aqi-level {
font-size: 12px;
}
}
}
.weather-tip {
font-size: 12px;
}
}
}
}
//天气完
// 设备数据统计显示
.card-panel {
height: 200px;
border-radius: 35px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 20px;
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: #a7de87;
}
.card-icon {
&:hover .card-panel-icon {
fill: #007aff;
}
}
.card-data {
.card-panel-num {
color: #a7de87;
}
}
}
.product {
.card-panel-icon {
fill: #f3728c;
}
.card-icon {
&:hover .card-panel-icon {
fill: #007aff;
}
}
.card-data {
.card-panel-num {
color: #f3728c;
}
}
}
.function {
.card-panel-icon {
fill: #ffc766;
}
.card-icon {
&:hover .card-panel-icon {
fill: #007aff;
}
}
.card-data {
.card-panel-num {
color: #ffc766;
}
}
}
.monitor {
.card-panel-icon {
fill: #66c9ff;
}
.card-icon {
&:hover .card-panel-icon {
fill: #007aff;
}
}
.card-data {
.card-panel-num {
color: #66c9ff;
}
}
}
.alert {
.card-panel-icon {
fill: #a076ef;
}
.card-icon {
&:hover .card-panel-icon {
fill: #007aff;
}
}
.card-data {
.card-panel-num {
color: #a076ef;
}
}
}
.reports {
.card-panel-icon {
fill: #5dd5d1;
}
.card-icon {
&:hover .card-panel-icon {
fill: #007aff;
}
}
.card-data {
.card-panel-num {
color: #5dd5d1;
}
}
}
.card-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
.card-icon {
display: flex;
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
margin-bottom: 15px;
background-color: rgba(#ffffff, 0.65);
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: center;
align-items: center;
/* 标题样式 */
.card-title {
font-size: 1.05rem;
color: #4d4d4d;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 600;
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 使用省略号表示超出部分 */
}
/* 数字样式 */
.card-panel-num {
font-size: 1.5rem;
font-weight: bold;
position: relative;
animation: countAnimation 3s ease-out forwards;
}
}
}
/* 响应式设计当屏幕宽度小于768px时 */
@media (max-width: 768px) {
height: 150px; /* 减小卡片高度 */
padding: 15px; /* 减小内边距 */
.card-content {
.card-icon {
width: 40px;
height: 40px;
margin-bottom: 10px;
.card-panel-icon {
width: 80%;
height: 80%;
}
}
.card-data {
.card-title {
font-size: 1rem;
margin-bottom: 8px;
}
.card-panel-num {
font-size: 1.5rem;
}
}
}
}
/* 数字递增动画关键帧 */
@keyframes countAnimation {
0% {
opacity: 0;
transform: translateY(-10px);
}
50% {
opacity: 1;
transform: translateY(5px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
//数据完
// 折线图卡片
.line-card {
height: 420px;
border-radius: 15px;
padding: 16px;
color: $secondary-color;
box-shadow: 0 4px 20px $shadow-color;
transition:
transform 0.3s,
box-shadow 0.3s;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px $hover-shadow-color;
}
}
// 折线图完
// 地图
.map-card {
background: #ffffff; /* 使用纯色背景,增加清新感 */
border-radius: 15px; /* 略微减小圆角 */
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); /* 更柔和的阴影 */
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
height: 500px;
&:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.card-header {
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px dashed #e0e0e0;
.card-title {
margin: 0;
color: #333333;
font-size: 18px;
font-weight: 600;
display: flex;
align-items: center;
i {
margin-right: 8px;
color: #409eff; /* 主题色图标 */
font-size: 20px;
}
}
}
.map-container {
width: 100%;
height: 500px; /* 减少高度 */
position: relative;
border-radius: 15px;
overflow: hidden;
.map {
width: 100%;
height: 100%;
/* 可根据需要添加地图相关样式 */
}
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.map-card {
padding: 12px;
}
.card-header {
padding: 18px 12px;
.card-title {
font-size: 16px;
i {
font-size: 18px;
}
}
}
.map-container {
height: 250px;
}
}
@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: 200px;
}
}
// 地图完
// 信息栏
.message-card {
height: 240px;
margin-bottom: 20px;
border-radius: 15px;
box-shadow: 0 4px 20px $shadow-color;
transition:
transform 0.3s,
box-shadow 0.3s;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px $hover-shadow-color;
}
}
// 柱状图
.card-container {
height: 240px;
width: 100%;
padding: 16px;
background: #ffffff;
border-radius: 15px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.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 {
border-radius: 15px;
padding: 16px;
color: $secondary-color;
box-shadow: 0 4px 20px $shadow-color;
transition:
transform 0.3s,
box-shadow 0.3s;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px $hover-shadow-color;
}
.pieCpu {
height: 161px;
}
@media (max-width: 768px) {
height: 100%;
}
}
// 使用率完
.phone-card {
border-radius: 15px;
box-shadow: 0 4px 20px $shadow-color;
transition:
transform 0.3s,
box-shadow 0.3s;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px $hover-shadow-color;
}
}
.phone {
height: 729px;
width: 370px;
background-image: url('../assets/images/phone.png');
background-size: cover;
margin: 0 auto;
}
.phone-container {
height: 618px;
width: 343px;
position: relative;
top: 46px;
left: 12px;
background-color: #fff;
}
.content {
line-height: 24px;
padding: 10px;
border: 1px solid #eee;
border-radius: 10px;
}
.description {
font-size: 12px;
tr {
line-height: 20px;
}
}
.notice-bar {
overflow: hidden;
position: relative;
}
.animating {
animation: scroll 10s linear infinite;
}
</style>