xiaohuo 8 months ago
commit f01dfb2f37
  1. 17
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/equManagement/EquVlanController.java
  2. 2
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/EquInterface.java
  3. 42
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/dto/EquVlanNetworkUpdateDto.java
  4. 5
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/dto/MonitorLastDto.java
  5. 7
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/mapper/EquVlanMapper.java
  6. 10
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/IEquVlanService.java
  7. 13
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/EquVlanServiceImpl.java
  8. 2
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquInterfaceMapper.xml
  9. 10
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquVlanMapper.xml
  10. 8
      ALOps_sys_fe/alops-ui/src/App.vue
  11. 14
      ALOps_sys_fe/alops-ui/src/components/Screenfull/index.vue
  12. 2
      ALOps_sys_fe/alops-ui/src/layout/components/Navbar.vue
  13. 47
      ALOps_sys_fe/alops-ui/src/views/device/files/index.vue
  14. 30
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/cardItem.vue
  15. 2
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/detail.vue
  16. 13
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/inMonitor/message/index.vue
  17. 536
      ALOps_sys_fe/alops-ui/src/views/index.vue

@ -10,6 +10,7 @@ import com.alops.common.core.page.TableDataInfo;
import com.alops.common.enums.BusinessType;
import com.alops.common.utils.poi.ExcelUtil;
import com.alops.system.domain.EquVlan;
import com.alops.system.domain.dto.EquVlanNetworkUpdateDto;
import com.alops.system.service.IEquVlanService;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
@ -61,6 +62,22 @@ public class EquVlanController extends BaseController
return getDataTable(list);
}
/**
* 修改 VLAN 网络信息仅限 IP 与子网掩码根据 vlanId
*/
@PreAuthorize("@ss.hasPermi('device:vlan:update')")
@PutMapping("/updateNetworkByVlanId")
@ApiOperation("修改 VLAN 网络信息(仅限 IP 与子网掩码,根据 vlanId)")
public AjaxResult updateVlanNetworkByVlanId(@RequestBody EquVlanNetworkUpdateDto dto)
{
int rows = equVlanService.updateVlanNetworkByVlanId(dto);
return toAjax(rows);
}
/**
* 导出请填写功能名称列表
*/

@ -29,8 +29,6 @@ public class EquInterface
private String equId;
/** 采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "采集时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date gatherTime;
/** 端口运行与否(UP/DOWN(true/false)) */

@ -0,0 +1,42 @@
package com.alops.system.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel("VLAN网络修改DTO,仅用于修改IP和子网掩码")
public class EquVlanNetworkUpdateDto {
@ApiModelProperty(value = "唯一VLAN ID", required = true, example = "VLAN")
private String id;
@ApiModelProperty(value = "IPv4地址", example = "")
private String ipAddress;
@ApiModelProperty(value = "子网掩码", example = "")
private String subnetMask;
public String getid() {
return id;
}
public void setid(String id) {
this.id = id;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getSubnetMask() {
return subnetMask;
}
public void setSubnetMask(String subnetMask) {
this.subnetMask = subnetMask;
}
}

@ -1,5 +1,7 @@
package com.alops.system.domain.dto;
import com.alops.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.sql.Blob;
@ -25,6 +27,9 @@ public class MonitorLastDto {
private String deviceLevel;
private String ip;
private String location;
/** 安装日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "安装日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date installDate;
private String inCharge;
private String status;

@ -1,6 +1,7 @@
package com.alops.system.mapper;
import com.alops.system.domain.EquVlan;
import com.alops.system.domain.dto.EquVlanNetworkUpdateDto;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@ -33,6 +34,12 @@ public interface EquVlanMapper
*/
public List<EquVlan> selectEquVlanList(EquVlan equVlan);
public List<EquVlan> selectEquVlanListByEquId(String equId);
/**
* 修改 VLAN 网络信息仅限 IP 与子网掩码根据 vlanId
*/
int updateVlanNetworkByVlanId(EquVlanNetworkUpdateDto dto);
/**
* 新增请填写功能名称
*

@ -1,6 +1,7 @@
package com.alops.system.service;
import com.alops.system.domain.EquVlan;
import com.alops.system.domain.dto.EquVlanNetworkUpdateDto;
import java.util.List;
@ -28,6 +29,15 @@ public interface IEquVlanService
*/
public List<EquVlan> selectEquVlanList(EquVlan equVlan);
public List<EquVlan> selectEquVlanListByEquId(String equId);
/**
* 修改 VLAN 网络信息仅限 IP 与子网掩码根据 vlanId
*
* @param dto VLAN更新信息
* @return 影响行数
*/
int updateVlanNetworkByVlanId(EquVlanNetworkUpdateDto dto);
/**
* 新增请填写功能名称
*

@ -4,6 +4,7 @@ import java.util.List;
import com.alops.common.utils.DateUtils;
import com.alops.system.domain.EquVlan;
import com.alops.system.domain.dto.EquVlanNetworkUpdateDto;
import com.alops.system.mapper.EquVlanMapper;
import com.alops.system.service.IEquVlanService;
@ -58,6 +59,18 @@ public class EquVlanServiceImpl implements IEquVlanService
return equVlanMapper.selectEquVlanListByEquId(equId);
}
/**
* 修改vlan信息接口
* **/
@Override
public int updateVlanNetworkByVlanId(EquVlanNetworkUpdateDto dto)
{
return equVlanMapper.updateVlanNetworkByVlanId(dto);
}
/**
* 新增请填写功能名称
*

@ -42,7 +42,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</sql>
<sql id="selectEquInterfaceVoJoinGather">
select i.id, g.equ_id, i.gather_time, running, rate, commuicate_mode, out_flow, in_flow, in_bandwidth, loss_rate, error_rate, equ_gather_id, interface_id, out_bandwidth from equ_interface i LEFT JOIN equ_gather g ON i.equ_gather_id = g.id
select i.id, g.equ_id, g.gather_time, running, rate, commuicate_mode, out_flow, in_flow, in_bandwidth, loss_rate, error_rate, equ_gather_id, interface_id, out_bandwidth from equ_interface i LEFT JOIN equ_gather g ON i.equ_gather_id = g.id
</sql>
<select id="selectEquInterfaceList" parameterType="EquInterface" resultMap="EquInterfaceResult">

@ -59,6 +59,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where g.equ_id = #{equid}
</select>
<update id="updateVlanNetworkByVlanId" parameterType="com.alops.system.domain.dto.EquVlanNetworkUpdateDto">
UPDATE equ_vlan
SET
ip_address = #{ipAddress},
subnet_mask = #{subnetMask}
WHERE id = #{id}
</update>
<insert id="insertEquVlan" parameterType="EquVlan">
insert into equ_vlan
<trim prefix="(" suffix=")" suffixOverrides=",">

@ -14,6 +14,14 @@ export default {
}
</script>
<style scoped>
#app {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
#app .theme-picker {
display: none;
}

@ -26,7 +26,19 @@ export default {
this.$message({ message: '你的浏览器不支持全屏', type: 'warning' })
return false
}
screenfull.toggle()
// main-content
// headerfooter
const contentElement = document.querySelector('.index-box');
if (contentElement) {
if (screenfull.isFullscreen) {
screenfull.exit();
} else {
screenfull.request(contentElement);
}
} else {
// main-content退body
screenfull.toggle();
}
},
change() {
this.isFullscreen = screenfull.isFullscreen

@ -89,7 +89,7 @@ export default {
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$store.dispatch('LogOut').then(() => {
this.$store.dispatch('user/LogOut').then(() => {
location.href = '/index'
})
}).catch(() => {})

@ -7,7 +7,7 @@
<el-col>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="安装日期">
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd hh:mm:ss" type="datetimerange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="datetimerange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item label="设备名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入设备名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
@ -17,8 +17,13 @@
<el-option v-for="dict in dict.type.device_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="安装楼层" prop="floor">
<el-select v-model="queryParams.floor" placeholder="安装楼层" clearable style="width: 240px">
<el-option v-for="dict in dict.type.floor_data" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="安装位置" prop="location">
<el-input v-model="queryParams.phonenumber" placeholder="请输入安装位置" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
<el-input v-model="queryParams.location" placeholder="请输入安装位置" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@ -56,14 +61,15 @@
</template>
</el-table-column>
<el-table-column label="带外管理IP" align="center" key="ip" prop="ip" width="120" />
<el-table-column label="安装楼层" align="center" key="floor" prop="floor" width="120" />
<el-table-column label="安装位置" align="center" key="location" prop="location" width="120" />
<el-table-column label="安装时间" align="center" prop="installDate" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.installDate) }}</span>
</template>
</el-table-column>
<el-table-column label="设备厂商" align="center" key="manufacture" prop="manufacture" width="120" />
<el-table-column label="品牌型号" align="center" key="brand" prop="brand" width="120" />
<el-table-column label="设备供应商" align="center" key="supplierName" prop="supplierName" width="120" />
<el-table-column label="设备管理员" align="center" key="inCharge" prop="inCharge" width="120" />
<el-table-column label="预计年限" align="center" key="lifeCycle" prop="lifeCycle" width="120" />
@ -161,6 +167,11 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="品牌型号" prop="brand">
<el-input v-model="form.brand" placeholder="请输入品牌型号" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备供应商" prop="supplierId">
<el-select
@ -181,6 +192,23 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="安装日期" prop="installDate">
<el-date-picker
v-model="form.installDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="安装楼层" prop="floor">
<el-select v-model="form.floor" placeholder="安装楼层" clearable style="width: 240px">
<el-option v-for="dict in dict.type.floor_data" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="安装位置" prop="location">
<el-input v-model="form.location" placeholder="请输入安装位置" maxlength="30" />
@ -249,7 +277,7 @@ import "splitpanes/dist/splitpanes.css"
export default {
name: "User",
dicts: ['device_disable', 'sys_user_sex', 'warning_level'],
dicts: ['device_disable', 'sys_user_sex', 'warning_level', 'floor_data'],
components: { Treeselect, Splitpanes, Pane },
data() {
return {
@ -346,6 +374,15 @@ export default {
supplierId: [
{ required: true, message: "设备供应商不能为空", trigger: "blur" },
],
brand: [
{ required: true, message: "品牌型号不能为空", trigger: "blur" }
],
installDate: [
{ required: true, message: "安装日期不能为空", trigger: "blur" }
],
floor: [
{ required: true, message: "安装楼层不能为空", trigger: "blur" }
],
location: [
{ required: true, message: "安装位置不能为空", trigger: "blur" }
],
@ -459,7 +496,7 @@ export default {
name: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
location: undefined,
email: undefined,
sex: undefined,
status: "0",

@ -43,7 +43,7 @@
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 带外IP
<i class="fas fa-heartbeat"></i> 带内IP
<!-- <i class="fas fa-heartbeat"></i> 带内IP -->
</span>
<span class="info-value">{{cardData.ip || '暂无'}}</span>
</div>
@ -61,18 +61,22 @@
</div>
<div class="metric-card">
<div class="metric-value cpu-usage">{{getPercentage(cardData.health)||0}}%</div>
<div class="metric-value cpu-usage">
<span :class="['health-icon', `health-${Number(cardData.health||0)}`]">
{{ cardData.health == 1?'健康':cardData.health == 2?'良好':cardData.health == 3?'一般':cardData.health == 4?'需维护':'未知' }}
</span>
</div>
<div class="metric-label">健康度</div>
<div class="progress-bar">
<!-- <div class="progress-bar">
<div class="progress-fill" :style="{width: getPercentage(cardData.health)||0+'%'}"></div>
</div>
</div> -->
</div>
<div class="metric-card">
<div class="metric-value alert-pending">{{cardData.unhandledAlertCount||0}}</div>
<div class="metric-label">待处理预警数</div>
</div>
<div class="metric-card">
<div class="metric-value alert-total">{{cardData.handledAlertCount||0}}</div>
<div class="metric-value alert-total">{{cardData.alertCount||0}}</div>
<div class="metric-label">预警总数</div>
</div>
</div>
@ -498,4 +502,20 @@ export default {
grid-template-columns: 1fr;
}
}
.health-1{
color: #4CAF50;
}
.health-2{
color: #FF9800;
}
.health-4{
color: #F44336;
}
.health-3{
color: #FFEB3B;
}
.health-0{
color: #9E9E9E;
}
</style>

@ -50,7 +50,7 @@
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
</span>
<span class="info-value">{{details.manufacture || '暂无'}}</span>
<span class="info-value">{{details.brand || '暂无'}}</span>
</div>
</div>

@ -25,17 +25,17 @@
</el-form-item>
</el-form>
<!-- <el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['device:files:add']">新增</el-button>
</el-col>
</el-col> -->
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['device:files:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<!-- <el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['device:files:import']">导入</el-button>
</el-col>
</el-row> -->
</el-col> -->
</el-row>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
@ -71,6 +71,7 @@
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" v-if="scope.row.status == 'UNHANDLED'" type="text" @click="handleUpdate(scope.row)" v-hasPermi="['device:files:edit']">去处理</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:files:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>

@ -3,7 +3,7 @@
<div id="app" class="dashboard" style="overflow-x: hidden;">
<div class="header">
<h1>唐山市设备监控中心大屏 - 智能管理版</h1>
<h1>设备监控中心大屏 - 智能管理版</h1>
<div class="time-display">{{ currentTime }}</div>
</div>
@ -13,18 +13,19 @@
<div class="panel">
<div class="panel-title">
设备状态监控
<div class="panel-actions">
<div class="panel-btn" @click="refreshDeviceStatus">刷新</div>
</div>
</div>
<div class="device-list" style="height: calc(100% - 50px); overflow-y: auto;">
<div class="device-list" style="height: calc(100% - 50px); overflow-y: auto; overflow-x: hidden;">
<div v-for="device in deviceStatus || []"
:key="device.id || Math.random()"
class="device-item"
@click="showDeviceDetail(device)">
<div :class="['device-status', `status-${device.status || 'normal'}`]"></div>
<div :class="['device-status', `status-${Number(device.health||0)}`]"></div>
<div class="device-name">{{ device.equManuName || device.name || '未知设备' }}</div>
<div class="device-value">{{ device.cpuUtilization || 0 }}%</div>
<div class="device-value">
<span :class="['health-icon', `health-${Number(device.health||0)}`]">
{{ device.health == 1?'健康':device.health == 2?'良好':device.health == 3?'一般':device.health == 4?'需维护':'未知' }}
</span>
</div>
</div>
</div>
</div>
@ -46,7 +47,7 @@
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<div class="center-panel">
<div class="panel">
<div class="panel-title">设备信息统计</div>
@ -97,7 +98,7 @@
</div>
<!-- 中间面板 - 楼层房间布局展示 -->
<div class="center-panel">
<div class="right-panel">
<div class="panel">
<div class="panel-title">
近期预警信息
@ -105,14 +106,14 @@
<el-scrollbar class="warning-scrollbar">
<div v-for="warning in filteredWarnings"
:key="warning.id"
class="warning-item"
@click="showDeviceDetail(warning)">
class="warning-item" >
<div class="warning-header">
<span :class="['warning-type', `level-${warning.displayLevel}`]">{{ warning.name }}</span>
</div>
<div class="warning-time">预警时间: {{ warning.time }}</div>
<div class="warning-position">设备位置: {{ warning.location }}</div>
<div class="warning-detail">预警信息: {{ warning.ruleName }}</div>
<div class="warning-detail">预警规则: {{ warning.ruleName }}</div>
<div class="warning-detail">预警详情: {{ warning.message }}</div>
</div>
</el-scrollbar>
</div>
@ -121,7 +122,7 @@
</div>
<div class="footer">
<div>唐山市监控系统 v4.0 - 智能管理版</div>
<div>监控系统 v4.0 - 智能管理版</div>
<div>最后更新: <span class="update-time">{{ updateTime }}</span></div>
<div>监控设备总数: {{ totalDevices }} | 在线: {{ onlineDevices }} </div>
</div>
@ -191,47 +192,27 @@
<span class="detail-label">最后维护:</span>
<span class="detail-value">{{ selectedDevice.lastMaintenance || '2024-09-15' }}</span>
</div> -->
<div class="control-buttons">
<button class="control-btn btn-restart" @click="controlDevice('restart')">重启设备</button>
<button class="control-btn btn-shutdown" @click="controlDevice('shutdown')">关机</button>
<button class="control-btn btn-reset" @click="controlDevice('reset')">重置配置</button>
</div>
</div>
</div>
<div class="detail-section">
<h3>历史数据
<div class="detail-section-tit">
<h3>历史数据</h3>
<div class="search-controls">
<input type="date" class="date-input" v-model="historyStartDate">
<span></span>
<input type="date" class="date-input" v-model="historyEndDate">
<button class="panel-btn" @click="loadHistoryData">查询</button>
</div>
</h3>
<div class="history-chart" ref="historyChart"></div>
</div>
</div>
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
@change="handleDateRangeChange"
class="date-picker"
/>
<el-button type="primary" @click="loadHistoryData">查询</el-button>
</div>
</div>
<!-- 房间设备列表模态框 -->
<div class="modal-overlay" v-if="showRoomModal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">房间设备 - {{ selectedRoom.name }}</h2>
<button class="modal-close" @click="closeRoomModal">&times;</button>
</div>
<div class="modal-body">
<div class="device-list">
<div v-for="device in getRoomDevices()"
:key="device.id"
class="device-item"
@click="showDeviceDetail(device)">
<div :class="['device-status', `status-${device.status}`]"></div>
<div class="device-name">{{ device.name }}</div>
<div class="device-value">{{ device.value || 'N/A' }}</div>
</div>
<div class="history-chart" ref="historyChart"></div>
</div>
</div>
</div>
@ -265,8 +246,10 @@ export default {
showWarningHistory: false,
selectedDevice: {},
selectedRoom: {},
historyStartDate: '',
historyEndDate: '',
dateRange: [],
maxTime: '',
minTime: '',
historyData: null,
warningLevelFilter: 'all',
warningStats: {
annual: 156,
@ -288,107 +271,7 @@ export default {
floorDeviceDistribution: [
{ floor: "1层", count: 6 },
],
floors: [
{
level: 1,
name: '一层 - 数据中心',
totalDevices: 32,
onlineDevices: 30,
warningDevices: 2,
rooms: [
{ id: 101, name: '服务器机房A', position: { left: 5, top: 10, width: 25, height: 35 } },
{ id: 102, name: '服务器机房B', position: { left: 35, top: 10, width: 25, height: 35 } },
{ id: 103, name: '存储机房', position: { left: 65, top: 10, width: 30, height: 35 } },
{ id: 104, name: '网络设备间', position: { left: 5, top: 50, width: 25, height: 25 } },
{ id: 105, name: 'UPS电源室', position: { left: 35, top: 50, width: 25, height: 25 } },
{ id: 106, name: '空调机房', position: { left: 65, top: 50, width: 30, height: 25 } },
{ id: 107, name: '走廊', position: { left: 0, top: 80, width: 100, height: 15 } }
],
devices: [
{ id: 101, name: '核心服务器1', status: 'normal', room: '服务器机房A', position: { x: 15, y: 20 }, type: '服务器' },
{ id: 102, name: '核心服务器2', status: 'normal', room: '服务器机房A', position: { x: 15, y: 30 }, type: '服务器' },
{ id: 103, name: '存储阵列A', status: 'warning', room: '存储机房', position: { x: 75, y: 20 }, type: '存储设备' },
{ id: 104, name: '网络交换机', status: 'normal', room: '网络设备间', position: { x: 15, y: 60 }, type: '网络设备' },
{ id: 105, name: '安全审计系统', status: 'error', room: '服务器机房B', position: { x: 45, y: 20 }, type: '安全设备' },
{ id: 106, name: 'UPS电源A', status: 'normal', room: 'UPS电源室', position: { x: 45, y: 60 }, type: '电源设备' },
{ id: 107, name: '备份服务器', status: 'normal', room: '服务器机房B', position: { x: 45, y: 30 }, type: '服务器' },
{ id: 108, name: '数据库集群', status: 'normal', room: '存储机房', position: { x: 75, y: 30 }, type: '服务器' }
]
},
{
level: 2,
name: '二层 - 网络中心',
totalDevices: 28,
onlineDevices: 26,
warningDevices: 2,
rooms: [
{ id: 201, name: '核心交换机房', position: { left: 10, top: 10, width: 35, height: 40 } },
{ id: 202, name: '防火墙区域', position: { left: 50, top: 10, width: 40, height: 40 } },
{ id: 203, name: '监控区域', position: { left: 10, top: 55, width: 35, height: 30 } },
{ id: 204, name: '设备维护室', position: { left: 50, top: 55, width: 40, height: 30 } },
{ id: 205, name: '走廊', position: { left: 0, top: 90, width: 100, height: 5 } }
],
devices: [
{ id: 201, name: '路由器主', status: 'normal', room: '核心交换机房', position: { x: 25, y: 25 }, type: '网络设备' },
{ id: 202, name: '防火墙1', status: 'normal', room: '防火墙区域', position: { x: 65, y: 25 }, type: '安全设备' },
{ id: 203, name: '负载均衡器', status: 'warning', room: '核心交换机房', position: { x: 25, y: 35 }, type: '网络设备' },
{ id: 204, name: 'VPN网关', status: 'normal', room: '防火墙区域', position: { x: 65, y: 35 }, type: '安全设备' },
{ id: 205, name: 'IDS系统', status: 'warning', room: '监控区域', position: { x: 25, y: 70 }, type: '安全设备' },
{ id: 206, name: '网络监控', status: 'normal', room: '监控区域', position: { x: 25, y: 60 }, type: '监控设备' },
{ id: 207, name: '交换机A', status: 'normal', room: '设备维护室', position: { x: 65, y: 70 }, type: '网络设备' },
{ id: 208, name: '交换机B', status: 'normal', room: '设备维护室', position: { x: 75, y: 70 }, type: '网络设备' }
]
},
{
level: 3,
name: '三层 - 监控中心',
totalDevices: 24,
onlineDevices: 22,
warningDevices: 2,
rooms: [
{ id: 301, name: '大屏监控区', position: { left: 5, top: 5, width: 60, height: 45 } },
{ id: 302, name: '操作台区域', position: { left: 70, top: 5, width: 25, height: 45 } },
{ id: 303, name: '存储区', position: { left: 5, top: 55, width: 40, height: 35 } },
{ id: 304, name: '设备间', position: { left: 50, top: 55, width: 45, height: 35 } },
{ id: 305, name: '走廊', position: { left: 0, top: 95, width: 100, height: 5 } }
],
devices: [
{ id: 301, name: '监控服务器', status: 'normal', room: '大屏监控区', position: { x: 30, y: 25 }, type: '服务器' },
{ id: 302, name: '录像设备', status: 'error', room: '存储区', position: { x: 20, y: 70 }, type: '存储设备' },
{ id: 303, name: '分析服务器', status: 'normal', room: '大屏监控区', position: { x: 45, y: 25 }, type: '服务器' },
{ id: 304, name: '报警主机', status: 'normal', room: '操作台区域', position: { x: 80, y: 25 }, type: '安全设备' },
{ id: 305, name: '门禁控制器', status: 'normal', room: '设备间', position: { x: 65, y: 70 }, type: '安防设备' },
{ id: 306, name: '环境监测', status: 'warning', room: '设备间', position: { x: 75, y: 70 }, type: '监控设备' },
{ id: 307, name: '摄像头控制', status: 'normal', room: '操作台区域', position: { x: 80, y: 35 }, type: '安防设备' },
{ id: 308, name: '报警系统', status: 'normal', room: '设备间', position: { x: 65, y: 80 }, type: '安全设备' }
]
},
{
level: 4,
name: '四层 - 办公区域',
totalDevices: 20,
onlineDevices: 18,
warningDevices: 2,
rooms: [
{ id: 401, name: '办公区A', position: { left: 5, top: 10, width: 40, height: 35 } },
{ id: 402, name: '办公区B', position: { left: 50, top: 10, width: 45, height: 35 } },
{ id: 403, name: '会议室', position: { left: 5, top: 50, width: 40, height: 35 } },
{ id: 404, name: '打印区', position: { left: 50, top: 50, width: 20, height: 35 } },
{ id: 405, name: '茶水间', position: { left: 75, top: 50, width: 20, height: 35 } },
{ id: 406, name: '走廊', position: { left: 0, top: 90, width: 100, height: 5 } }
],
devices: [
{ id: 401, name: '办公服务器', status: 'normal', room: '办公区A', position: { x: 25, y: 25 }, type: '服务器' },
{ id: 402, name: '打印机', status: 'warning', room: '打印区', position: { x: 60, y: 65 }, type: '办公设备' },
{ id: 403, name: '网络终端', status: 'normal', room: '办公区B', position: { x: 70, y: 25 }, type: '网络设备' },
{ id: 404, name: '视频会议', status: 'normal', room: '会议室', position: { x: 25, y: 65 }, type: '会议设备' },
{ id: 405, name: '电话系统', status: 'warning', room: '办公区A', position: { x: 25, y: 35 }, type: '通信设备' },
{ id: 406, name: '考勤系统', status: 'normal', room: '办公区B', position: { x: 70, y: 35 }, type: '安防设备' },
{ id: 407, name: '门禁系统', status: 'normal', room: '走廊', position: { x: 50, y: 92 }, type: '安防设备' },
{ id: 408, name: '投影设备', status: 'normal', room: '会议室', position: { x: 25, y: 55 }, type: '会议设备' }
]
}
],
floors: [],
dataFlows: [],
barChart: null,
pieChart: null,
@ -440,18 +323,38 @@ export default {
this.updateTimeDisplay();
setInterval(this.updateTimeDisplay, 1000);
//
const today = new Date();
this.historyEndDate = today.toISOString().split('T')[0];
const oneWeekAgo = new Date(today);
oneWeekAgo.setDate(today.getDate() - 7);
this.historyStartDate = oneWeekAgo.toISOString().split('T')[0];
//
// const today = new Date();
// const oneWeekAgo = new Date(today);
// oneWeekAgo.setDate(today.getDate() - 7);
// // YYYY-MM-DD HH:mm:ss
// const formatDate = (date) => {
// const year = date.getFullYear();
// const month = String(date.getMonth() + 1).padStart(2, '0');
// const day = String(date.getDate()).padStart(2, '0');
// const hours = String(date.getHours()).padStart(2, '0');
// const minutes = String(date.getMinutes()).padStart(2, '0');
// const seconds = String(date.getSeconds()).padStart(2, '0');
// return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
// };
// this.dateRange = [
// formatDate(oneWeekAgo),
// formatDate(today)
// ];
// console.log("%c 🤸: mounted -> this.dateRange ", "font-size:16px;background-color:#1a9bd6;color:white;", this.dateRange)
// // maxTimeminTime
// this.minTime = this.dateRange[0];
// this.maxTime = this.dateRange[1];
this.generateDataFlows();
// 使nextTickDOM
this.$nextTick(() => {
this.initPieChart();
//
// this.initHistoryChart();
});
//
@ -473,9 +376,6 @@ export default {
setInterval(() => {
this.refreshDeviceStatus();
}, 60000);
// WebSocket
this.simulateWebSocket();
},
beforeDestroy() {
//
@ -485,7 +385,32 @@ export default {
}
},
methods: {
// 23:59:59
handleDateRangeChange(range) {
if (range && range.length === 2) {
// 23:59:59
const endDate = new Date(range[1]);
endDate.setHours(23, 59, 59, 999);
// yyyy-MM-dd HH:mm:ss
const endDateTime = endDate.getFullYear() + '-' +
String(endDate.getMonth() + 1).padStart(2, '0') + '-' +
String(endDate.getDate()).padStart(2, '0') + ' ' +
'23:59:59';
// dateRange
this.dateRange = [range[0], endDateTime];
// minTimemaxTime
this.minTime = this.dateRange[0];
this.maxTime = this.dateRange[1];
}
},
formatDuration(input) {
if (!input) {
return "无数据";
}
const regex = /(\d+)\s*days,\s*(\d+):/;
const match = input.match(regex);
return match
@ -695,18 +620,52 @@ export default {
this.pieChart.setOption(option);
},
//
loadHistoryData() {
if (!this.dateRange || this.dateRange.length !== 2) {
this.$message.warning('请选择日期范围');
return;
}
// maxTimeminTime
this.minTime = this.dateRange[0];
this.maxTime = this.dateRange[1];
const params = {
id: this.selectedDevice.id,
minTime: this.minTime,
maxTime: this.maxTime,
pageNum: 1,
pageSize: 1000
};
historyQuery(params).then(response => {
if (response.code === 200 && response.data) {
this.historyData = response.data;
this.initHistoryChart();
}
}).catch(error => {
console.error('获取历史数据失败:', error);
this.$message.error('获取历史数据失败');
});
},
initHistoryChart() {
if (!this.$refs.historyChart) return;
this.historyChart = echarts.init(this.$refs.historyChart);
// 使
const timeList = this.historyData?.timeList || ['10-18', '10-19', '10-20', '10-21', '10-22', '10-23', '10-24'];
const cpuList = this.historyData?.cpuList || [45, 52, 48, 60, 55, 58, 62];
const memoryList = this.historyData?.memoryList || [62, 58, 65, 70, 68, 72, 75];
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis'
},
legend: {
data: ['CPU使用率', '内存使用率', '网络流量'],
data: ['CPU使用率', '内存使用率'],
textStyle: {
color: '#a0aec0'
}
@ -720,14 +679,21 @@ export default {
xAxis: {
type: 'category',
boundaryGap: false,
data: ['10-18', '10-19', '10-20', '10-21', '10-22', '10-23', '10-24'],
data: timeList,
axisLine: {
lineStyle: {
color: '#4dabff'
}
},
axisLabel: {
color: '#a0aec0'
color: '#a0aec0',
//
formatter: function(value) {
if (typeof value === 'string' && value.includes(' ')) {
return value.split(' ')[1].substring(0, 5); //
}
return value;
}
}
},
yAxis: {
@ -752,7 +718,7 @@ export default {
name: 'CPU使用率',
type: 'line',
smooth: true,
data: [45, 52, 48, 60, 55, 58, 62],
data: cpuList,
itemStyle: {
color: '#4dabff'
},
@ -775,7 +741,7 @@ export default {
name: '内存使用率',
type: 'line',
smooth: true,
data: [62, 58, 65, 70, 68, 72, 75],
data: memoryList,
itemStyle: {
color: '#62f7ff'
},
@ -793,29 +759,6 @@ export default {
}]
}
}
},
{
name: '网络流量',
type: 'line',
smooth: true,
data: [78, 82, 75, 90, 85, 88, 95],
itemStyle: {
color: '#ffa726'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(255, 167, 38, 0.3)'
}, {
offset: 1, color: 'rgba(255, 167, 38, 0.1)'
}]
}
}
}
]
};
@ -889,7 +832,7 @@ export default {
};
historyQuery({ id: device.id, ...historyQueryList }).then(response => {
this.historyQueryData = response.data;
this.historyData = response.data;
this.$nextTick(() => {
this.initHistoryChart();
});
@ -913,19 +856,12 @@ export default {
getRoomDevices() {
const currentFloor = this.getCurrentFloor();
return currentFloor.devices.filter(device => device.room === this.selectedRoom.name);
},
controlDevice(action) {
alert(`执行设备控制: ${action} - ${this.selectedDevice.name}`);
//
},
loadHistoryData() {
alert(`加载历史数据: ${this.historyStartDate}${this.historyEndDate}`);
//
},
filterByWarningLevel(level) {
if (this.warningLevelFilter === level) {
this.warningLevelFilter = 'all';
@ -1009,25 +945,6 @@ export default {
}
},
simulateWebSocket() {
// WebSocket
setInterval(() => {
//
const floorIndex = Math.floor(Math.random() * this.floors.length);
const deviceIndex = Math.floor(Math.random() * this.floors[floorIndex].devices.length);
const device = this.floors[floorIndex].devices[deviceIndex];
const oldStatus = device.status;
if (Math.random() > 0.7) {
device.status = Math.random() > 0.5 ? 'warning' : 'error';
} else if (oldStatus !== 'normal' && Math.random() > 0.8) {
device.status = 'normal';
}
}, 3000);
},
//
switchFloor(floorLevel) {
this.currentFloor = floorLevel;
@ -1045,9 +962,6 @@ export default {
return this.floors.find(floor => floor.level === this.currentFloor) || this.floors[0];
},
getCurrentFloorDevices() {
return this.getCurrentFloor().devices;
}
}
}
@ -1061,30 +975,38 @@ export default {
font-family: "Microsoft YaHei", sans-serif;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
.index-box {
background-color: #0a1633;
color: #e6e6e6;
overflow: hidden;
padding: 1.5vh 1.5vw;
overflow: auto;
padding: 16px;
min-height: 100%;
position: relative;
box-sizing: border-box;
}
.dashboard {
width: 100vw;
height: 100vh;
padding: 1.5vh 1.5vw;
width: 100%;
min-height: 100%;
padding: 16px;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
min-width: 1200px;
min-height: 700px;
background-color: #0a1633;
color: #e6e6e6;
overflow: hidden;
}
.header {
height: 10vh;
height: 10%;
min-height: 70px;
max-height: 100px;
background: linear-gradient(90deg, rgba(10, 26, 58, 0.8), rgba(13, 36, 82, 0.8), rgba(10, 26, 58, 0.8));
@ -1135,34 +1057,37 @@ export default {
.main-content {
flex: 1;
display: flex;
gap: 1.5vh;
gap: 16px;
min-height: 0;
height: 100%;
align-items: stretch;
}
.left-panel, .right-panel {
width: 35%;
min-width: 280px;
.left-panel, .right-panel, .center-panel {
display: flex;
flex-direction: column;
gap: 1.5vh;
min-height: 0;
gap: 16px;
min-height: 100%;
max-height: 100vh;
box-sizing: border-box;
flex-shrink: 0;
}
.left-panel, .right-panel {
width: 30%;
}
.center-panel {
flex: 1;
display: flex;
flex-direction: column;
gap: 1.5vh;
min-height: 0;
min-width: 500px;
}
.panel {
background: rgba(16, 31, 63, 0.7);
border-radius: 1vh;
border-radius: 8px;
border: 1px solid #1a3a7a;
box-shadow: 0 0 10px rgba(26, 58, 122, 0.3);
padding: 1.5vh;
padding: 16px;
position: relative;
overflow: hidden;
display: flex;
@ -1188,7 +1113,7 @@ export default {
margin-bottom: 1.5vh;
display: flex;
align-items: center;
justify-content: space-between;
justify-content: left;
flex-shrink: 0;
}
@ -1228,11 +1153,13 @@ export default {
display: flex;
flex-direction: column;
gap: 15px;
height: 100%;
}
.warning-row {
display: flex;
gap: 15px;
height: 100%;
}
.time-item {
@ -1327,6 +1254,7 @@ export default {
align-items: center;
transition: all 0.3s;
cursor: pointer;
justify-content: center;
}
.stat-item:hover {
@ -1367,7 +1295,7 @@ export default {
}
.device-list {
height: 30vh;
height: 30%;
min-height: 200px;
overflow-y: auto;
flex-shrink: 0;
@ -1406,7 +1334,9 @@ export default {
.warning-scrollbar {
height: 100%;
min-height: 100px;
flex-shrink: 0;
max-height: 100vh;
/* flex-shrink: 0; */
/* height: cale(100vh - 200px); */
}
.warning-scrollbar .el-scrollbar__wrap {
@ -1468,6 +1398,27 @@ export default {
margin-bottom: 5px;
line-height: 1.5;
}
.status-0 {
background: #9E9E9E;
box-shadow: 0 0 5px #9E9E9E;
}
.status-1 {
background: #4CAF50;
box-shadow: 0 0 5px #4CAF50;
}
.status-2 {
background: #FF9800;
box-shadow: 0 0 5px #FF9800;
}
.status-4 {
background: #F44336;
box-shadow: 0 0 5px #F44336;
}
.status-3 {
background: #FFEB3B;
box-shadow: 0 0 5px #FFEB3B;
}
.status-normal {
background: #4CAF50;
@ -1500,13 +1451,29 @@ export default {
font-weight: bold;
font-size: clamp(0.8rem, 1vw, 1rem);
}
.health-1{
color: #4CAF50;
}
.health-2{
color: #FF9800;
}
.health-4{
color: #F44336;
}
.health-3{
color: #FFEB3B;
}
.health-0{
color: #9E9E9E;
}
/* 楼宇容器样式 */
.building-container {
flex: 1;
position: relative;
display: flex;
min-height: 40vh;
min-height: 40%;
}
.floor-plans-container {
@ -1764,13 +1731,13 @@ export default {
}
.chart-container {
height: 30vh;
height: 30%;
min-height: 200px;
flex-shrink: 0;
}
.footer {
height: 5vh;
height: 5%;
min-height: 30px;
max-height: 50px;
display: flex;
@ -1778,7 +1745,7 @@ export default {
align-items: center;
color: #a0aec0;
font-size: clamp(0.7rem, 0.9vw, 0.9rem);
margin-top: 1vh;
margin-top: 8px;
flex-shrink: 0;
}
@ -1975,83 +1942,4 @@ export default {
color: #e6e6e6;
}
/* 响应式设计 - 中等屏幕 */
@media screen and (max-width: 1400px) {
.left-panel, .right-panel {
width: 28%;
}
.center-panel {
width: 44%;
}
}
/* 响应式设计 - 小屏幕 */
@media screen and (max-width: 1200px) {
.dashboard {
min-width: 1000px;
}
.left-panel, .right-panel {
min-width: 250px;
}
.center-panel {
min-width: 450px;
}
}
/* 响应式设计 - 超小屏幕 */
@media screen and (max-width: 1000px) {
.dashboard {
min-width: 800px;
padding: 1vh 1vw;
}
.main-content {
flex-direction: column;
gap: 1vh;
}
.left-panel, .right-panel, .center-panel {
width: 100%;
min-width: auto;
}
.left-panel, .right-panel {
flex-direction: row;
}
.panel {
flex: 1;
}
}
/* 响应式设计 - 移动端 */
@media screen and (max-width: 768px) {
.dashboard {
min-width: auto;
min-height: auto;
padding: 0.5vh 0.5vw;
}
.header {
height: 8vh;
padding: 0 1vw;
margin-bottom: 1vh;
}
.main-content {
gap: 1vh;
}
.left-panel, .right-panel {
flex-direction: column;
}
.footer {
height: 4vh;
font-size: 0.7rem;
}
}
</style>
Loading…
Cancel
Save