|
@@ -1,158 +1,350 @@
|
|
|
|
|
+<!-- 设备列表组件(左侧面板):展示光谱摄像机设备列表,支持搜索、分类导航和设备操作 -->
|
|
|
<template>
|
|
<template>
|
|
|
|
|
+ <!-- 设备列表主容器 -->
|
|
|
<div class="long-list">
|
|
<div class="long-list">
|
|
|
|
|
+ <!-- 列表标题栏:显示标题和设备总数 -->
|
|
|
<div class="list-title">
|
|
<div class="list-title">
|
|
|
<span>设备列表</span>
|
|
<span>设备列表</span>
|
|
|
<span style="margin-left: 5px;">{{ count }}</span>
|
|
<span style="margin-left: 5px;">{{ count }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 列表内容区域:包含搜索框、导航栏和设备卡片 -->
|
|
|
<div class="list-content">
|
|
<div class="list-content">
|
|
|
|
|
+ <!-- 搜索框区域:支持按设备编号或名称搜索 -->
|
|
|
<div class="list-search">
|
|
<div class="list-search">
|
|
|
<a-input v-model:value="searchData" placeholder="搜索设备编号,名称" size="small" />
|
|
<a-input v-model:value="searchData" placeholder="搜索设备编号,名称" size="small" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 内容容器:包含分类导航和设备列表 -->
|
|
|
<div class="content-container">
|
|
<div class="content-container">
|
|
|
|
|
+ <!-- 分类导航栏:支持不同类型设备的切换(当前仅支持光谱摄像机) -->
|
|
|
<div class="content-nav">
|
|
<div class="content-nav">
|
|
|
|
|
+ <!-- 导航项:动态绑定激活状态样式,点击触发切换事件 -->
|
|
|
<div :class="activeIndex == index ? 'nav-item-active' : 'nav-item'" v-for="(item, index) in navList"
|
|
<div :class="activeIndex == index ? 'nav-item-active' : 'nav-item'" v-for="(item, index) in navList"
|
|
|
- :key="index" @click="changeNav(item, index)">{{ item.label }}</div>
|
|
|
|
|
|
|
+ :key="index" @click="changeNav(item, index)">
|
|
|
|
|
+ {{ item.label }}
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 设备列表容器:可滚动区域显示所有设备卡片 -->
|
|
|
<div class="content-box">
|
|
<div class="content-box">
|
|
|
- <basicDevice v-for="(item, index) in listData" :key="index" :deviceData="item" @detail="handlerDetail">
|
|
|
|
|
- </basicDevice>
|
|
|
|
|
|
|
+ <!-- 单个设备卡片:遍历listData数组渲染每个设备的信息 -->
|
|
|
|
|
+ <div class="basic-device" v-for="(item, index) in listData" :key="index">
|
|
|
|
|
+ <!-- 左侧状态指示器区域 -->
|
|
|
|
|
+ <div class="icon-left">
|
|
|
|
|
+ <!-- 状态指示圆点:根据报警级别显示不同颜色(绿色正常/红色报警) -->
|
|
|
|
|
+ <div :class="item.syswarnLevel ? 'icon-item-warn' : 'icon-item'"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 设备标题区域:包含序列号和操作按钮 -->
|
|
|
|
|
+ <div class="basic-title">
|
|
|
|
|
+ <!-- 设备序列号显示 -->
|
|
|
|
|
+ <div class="title-text">{{ item.strserno || '-' }}</div>
|
|
|
|
|
+ <!-- 操作按钮组:详情、配置、重启三种操作 -->
|
|
|
|
|
+ <div class="title-btn">
|
|
|
|
|
+ <div :class="active == index ? 'btn-active' : 'btn'" @click="handlerClick('detail', item, index)">详情
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="btn" @click="handlerClick('config', item, index)">配置</div>
|
|
|
|
|
+ <div class="btn" @click="handlerClick('restart', item, index)">重启</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 设备详细信息区域:2x2网格布局展示关键信息 -->
|
|
|
|
|
+ <div class="basic-content">
|
|
|
|
|
+ <!-- 第一行左:设备名称 -->
|
|
|
|
|
+ <div class="content-item">{{ item.strname || '-' }}</div>
|
|
|
|
|
+ <!-- 第一行右:运行状态(带状态图标) -->
|
|
|
|
|
+ <div class="content-item">
|
|
|
|
|
+ <span class="icon-pie">
|
|
|
|
|
+ <span class="pie-item"></span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span>{{ item.syswarnLevel ? '报警' : '正常' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 第二行左:安装位置 -->
|
|
|
|
|
+ <div class="content-item">{{ item.strinstallpos || '-' }}</div>
|
|
|
|
|
+ <!-- 第二行右:温度数据 -->
|
|
|
|
|
+ <div class="content-item">
|
|
|
|
|
+ <span>{{ item.readData.temperature || '-' }}</span>
|
|
|
|
|
+ <span>℃</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
|
|
+// 导入Vue 3组合式API:生命周期钩子和响应式数据工具
|
|
|
import { onMounted, onUnmounted, ref } from 'vue'
|
|
import { onMounted, onUnmounted, ref } from 'vue'
|
|
|
-import basicDevice from './basicDevice.vue'
|
|
|
|
|
|
|
+// 导入API接口函数:管理系统列表查询、系统监控数据获取
|
|
|
import { managesysList, monitorSystem } from '../hsqHome.api.js'
|
|
import { managesysList, monitorSystem } from '../hsqHome.api.js'
|
|
|
|
|
|
|
|
-//场景ID
|
|
|
|
|
-let systemId = ref('')
|
|
|
|
|
-//设备列表数量
|
|
|
|
|
-let count = ref(0)
|
|
|
|
|
-//搜索条件
|
|
|
|
|
-let searchData = ref('')
|
|
|
|
|
-//当前激活nav选项
|
|
|
|
|
-let activeIndex = ref(0)
|
|
|
|
|
-let navList = ref<any[]>([
|
|
|
|
|
|
|
+let active = ref(0)
|
|
|
|
|
+/** 响应式数据:当前场景/系统的唯一标识ID,用于API请求参数 */
|
|
|
|
|
+const systemId = ref('')
|
|
|
|
|
+
|
|
|
|
|
+/** 响应式数据:设备列表总数,显示在标题栏中 */
|
|
|
|
|
+const count = ref(0)
|
|
|
|
|
+
|
|
|
|
|
+/** 响应式数据:搜索框的绑定值,用于过滤设备(当前未实现过滤逻辑) */
|
|
|
|
|
+const searchData = ref('')
|
|
|
|
|
+
|
|
|
|
|
+/** 响应式数据:当前激活的导航项索引,用于控制导航栏高亮状态 */
|
|
|
|
|
+const activeIndex = ref(0)
|
|
|
|
|
+
|
|
|
|
|
+/** 响应式数据:导航栏配置数组,定义可切换的设备类型分类 */
|
|
|
|
|
+const navList = ref<any[]>([
|
|
|
{ label: '光谱摄像机', value: '0' },
|
|
{ label: '光谱摄像机', value: '0' },
|
|
|
])
|
|
])
|
|
|
-let listData = ref<any[]>([])
|
|
|
|
|
-let $emit = defineEmits(['details'])
|
|
|
|
|
-// https获取监测数据
|
|
|
|
|
|
|
+
|
|
|
|
|
+/** 响应式数据:设备列表数据源,存储从后端获取的所有设备信息 */
|
|
|
|
|
+const listData = ref<any[]>([])
|
|
|
|
|
+
|
|
|
|
|
+/** 定义组件事件:向父组件传递details事件,用于通信选中设备的操作类型和数据 */
|
|
|
|
|
+const $emit = defineEmits(['details'])
|
|
|
|
|
+
|
|
|
|
|
+/** 定时器变量:用于存储定时获取数据的定时器ID,便于在组件卸载时清理 */
|
|
|
let timer: null | NodeJS.Timeout = null;
|
|
let timer: null | NodeJS.Timeout = null;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 定时获取监测数据
|
|
|
|
|
- * 通过定时器循环调用数据获取方法,更新表格数据
|
|
|
|
|
|
|
+ * 定时轮询获取监测数据的核心函数
|
|
|
|
|
+ * 通过递归调用setTimeout实现每30秒自动刷新一次设备数据
|
|
|
|
|
+ * 采用异步方式避免阻塞主线程,确保UI响应流畅
|
|
|
*
|
|
*
|
|
|
- * @param flag - 是否立即执行(首次调用时传true跳过延时)
|
|
|
|
|
|
|
+ * @param flag - 可选参数,控制是否立即执行
|
|
|
|
|
+ * - true(首次调用):立即执行,不等待延时
|
|
|
|
|
+ * - false/undefined(后续调用):等待30秒后执行
|
|
|
*/
|
|
*/
|
|
|
-function getMonitor(flag?: boolean) {
|
|
|
|
|
|
|
+async function getMonitor(flag?: boolean) {
|
|
|
|
|
+ // 设置定时器:根据flag决定是否立即执行
|
|
|
timer = setTimeout(async () => {
|
|
timer = setTimeout(async () => {
|
|
|
|
|
+ // 调用数据获取接口,await确保数据加载完成后再继续
|
|
|
await getData()
|
|
await getData()
|
|
|
|
|
+ // 清空定时器引用,防止内存泄漏
|
|
|
if (timer) {
|
|
if (timer) {
|
|
|
timer = null;
|
|
timer = null;
|
|
|
}
|
|
}
|
|
|
|
|
+ // 递归调用自身,形成定时轮询机制
|
|
|
getMonitor();
|
|
getMonitor();
|
|
|
- }, flag ? 0 : 30000);
|
|
|
|
|
|
|
+ }, flag ? 0 : 30000); // 首次立即执行,之后每隔30秒执行一次
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-//nav选项切换
|
|
|
|
|
-function changeNav(item, index) {
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 导航栏切换处理函数
|
|
|
|
|
+ * 当用户点击不同的设备类型标签时触发,更新当前激活的导航索引
|
|
|
|
|
+ * (当前实现仅更新UI状态,未实现实际的数据筛选逻辑)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param item - 被点击的导航项对象,包含label和value属性
|
|
|
|
|
+ * @param index - 被点击导航项在navList数组中的索引位置
|
|
|
|
|
+ */
|
|
|
|
|
+function changeNav(item: any, index: number) {
|
|
|
|
|
+ // 更新激活状态索引,触发导航栏样式重新渲染
|
|
|
activeIndex.value = index
|
|
activeIndex.value = index
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 设备操作按钮点击事件处理函数
|
|
|
|
|
+ * 统一处理详情、配置、重启三种操作按钮的点击事件
|
|
|
|
|
+ * 将操作类型和设备数据封装成事件对象传递给父组件
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param type - 操作类型字符串
|
|
|
|
|
+ * - 'detail': 查看设备详细信息
|
|
|
|
|
+ * - 'config': 查看或修改设备配置
|
|
|
|
|
+ * - 'restart': 执行设备重启操作
|
|
|
|
|
+ * @param item - 被操作的目标设备完整数据对象
|
|
|
|
|
+ */
|
|
|
|
|
+function handlerClick(type: string, item: any, index: number) {
|
|
|
|
|
+ // 触发details事件,将操作类型和设备数据传递给父组件(deviceManger.vue)
|
|
|
|
|
+ $emit('details', { type: type, value: item })
|
|
|
|
|
+ active.value = index
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 异步获取设备监控数据的API调用函数
|
|
|
|
|
+ * 向后端请求指定系统的所有光谱摄像机设备信息
|
|
|
|
|
+ * 成功后将设备列表数据和数量分别赋值给对应的响应式变量
|
|
|
|
|
+ */
|
|
|
async function getData() {
|
|
async function getData() {
|
|
|
|
|
+ // 调用监控系统API,传入查询参数:
|
|
|
|
|
+ // - type: 'all' 表示获取全部设备
|
|
|
|
|
+ // - systemId: 当前系统ID(从getManagesysList获取)
|
|
|
|
|
+ // - devicetype: '' 空字符串表示不限制设备类型
|
|
|
const res = await monitorSystem({ type: 'all', systemID: systemId.value, devicetype: '' });
|
|
const res = await monitorSystem({ type: 'all', systemID: systemId.value, devicetype: '' });
|
|
|
|
|
+
|
|
|
|
|
+ // 控制台输出调试信息,便于开发阶段排查问题
|
|
|
console.log(res, '设备列表数据')
|
|
console.log(res, '设备列表数据')
|
|
|
|
|
+
|
|
|
|
|
+ // 提取光谱摄像机设备列表数据,使用空数组作为默认值防止undefined错误
|
|
|
listData.value = res.deviceInfo.duaSpeCamera.datalist || [];
|
|
listData.value = res.deviceInfo.duaSpeCamera.datalist || [];
|
|
|
- count.value = res.deviceInfo.duaSpeCamera.datalist.length;
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-function handlerDetail(item) {
|
|
|
|
|
- $emit('details', item)
|
|
|
|
|
|
|
+ // 统计并记录设备总数量,用于标题栏显示
|
|
|
|
|
+ count.value = res.deviceInfo.duaSpeCamera.datalist.length;
|
|
|
|
|
+ // 初始化时,默认选中第一个设备
|
|
|
|
|
+ active.value = 0
|
|
|
|
|
+ handlerClick('detail', listData.value[0], 0)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 异步获取管理系统列表的初始化函数
|
|
|
|
|
+ * 在组件挂载时首先调用此函数,获取当前用户有权限访问的系统列表
|
|
|
|
|
+ * 从返回结果中提取第一个系统的ID作为后续数据查询的基础参数
|
|
|
|
|
+ *
|
|
|
|
|
+ * API参数说明:
|
|
|
|
|
+ * - strtype: 'sys_openair_fire' 系统类型标识(开放空间火灾监测)
|
|
|
|
|
+ * - pagetype: 'normal' 页面类型(普通页面模式)
|
|
|
|
|
+ */
|
|
|
async function getManagesysList() {
|
|
async function getManagesysList() {
|
|
|
|
|
+ // 调用管理系统列表API,传入系统类型和页面类型参数
|
|
|
const res = await managesysList({ strtype: 'sys_openair_fire', pagetype: 'normal' });
|
|
const res = await managesysList({ strtype: 'sys_openair_fire', pagetype: 'normal' });
|
|
|
|
|
+
|
|
|
|
|
+ // 安全检查:确保返回数据存在且包含records数组
|
|
|
if (res && res.records) {
|
|
if (res && res.records) {
|
|
|
|
|
+ // 取第一个系统的ID作为当前工作系统ID(通常用户只关注一个主要系统)
|
|
|
systemId.value = res.records[0].id
|
|
systemId.value = res.records[0].id
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 组件挂载完成后的生命周期钩子
|
|
|
|
|
+ * 执行组件初始化流程:
|
|
|
|
|
+ * 1. 先获取系统ID(必须先于数据获取,因为systemId是getData的必要参数)
|
|
|
|
|
+ * 2. 然后启动定时数据刷新机制(传true表示首次立即执行)
|
|
|
|
|
+ */
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
|
|
+ // 等待系统ID获取完成,确保后续API调用有正确的参数
|
|
|
await getManagesysList()
|
|
await getManagesysList()
|
|
|
|
|
+ // 启动定时监控,true表示首次立即执行一次
|
|
|
getMonitor(true)
|
|
getMonitor(true)
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 组件卸载前的生命周期钩子
|
|
|
|
|
+ * 执行资源清理工作,主要是清除定时器以防止内存泄漏
|
|
|
|
|
+ * 如果不清除,即使组件已销毁,定时器仍会继续运行并尝试更新已不存在的响应式数据
|
|
|
|
|
+ */
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
|
|
|
+ // 检查定时器是否存在,存在则清除
|
|
|
if (timer) {
|
|
if (timer) {
|
|
|
clearTimeout(timer)
|
|
clearTimeout(timer)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+<!-- 样式部分:使用Less预处理器,scoped限制样式作用域仅作用于当前组件 -->
|
|
|
<style lang="less" scoped>
|
|
<style lang="less" scoped>
|
|
|
|
|
+// 导入全局主题配置文件,包含颜色变量、主题切换等样式定义
|
|
|
@import '/@/design/theme.less';
|
|
@import '/@/design/theme.less';
|
|
|
|
|
|
|
|
|
|
+/* 深蓝主题模式下的设备列表样式覆盖(预留扩展) */
|
|
|
@{theme-deepblue} {
|
|
@{theme-deepblue} {
|
|
|
.long-list {}
|
|
.long-list {}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* 设备列表主容器:左侧面板的根元素 */
|
|
|
.long-list {
|
|
.long-list {
|
|
|
|
|
+ /* ==================== CSS自定义属性:背景图片资源定义 ==================== */
|
|
|
|
|
+ /* 列表标题栏背景图 */
|
|
|
--image-box-bg: url('@/assets/images/home-container/configurable/hsq/2-5.png');
|
|
--image-box-bg: url('@/assets/images/home-container/configurable/hsq/2-5.png');
|
|
|
|
|
+ /* 搜索输入框背景图 */
|
|
|
--image-box-bg1: url('@/assets/images/home-container/configurable/hsq/2-6.png');
|
|
--image-box-bg1: url('@/assets/images/home-container/configurable/hsq/2-6.png');
|
|
|
|
|
+ /* (预留)其他UI元素背景图 */
|
|
|
--image-box-bg2: url('@/assets/images/home-container/configurable/hsq/2-2.png');
|
|
--image-box-bg2: url('@/assets/images/home-container/configurable/hsq/2-2.png');
|
|
|
|
|
+ /* 设备卡片背景图 */
|
|
|
|
|
+ --image-box-bg3: url('@/assets/images/home-container/configurable/hsq/2-7.png');
|
|
|
|
|
+ /* 状态指示器外框背景图 */
|
|
|
|
|
+ --image-box-bg4: url('@/assets/images/home-container/configurable/hsq/2-8.png');
|
|
|
|
|
+ /* 设备标题区域背景图 */
|
|
|
|
|
+ --image-box-bg5: url('@/assets/images/home-container/configurable/hsq/2-9.png');
|
|
|
|
|
|
|
|
|
|
+ /* 基础布局样式 */
|
|
|
position: relative;
|
|
position: relative;
|
|
|
|
|
+ /* 相对定位:作为内部绝对定位元素的参考容器 */
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
|
|
+ /* 宽度撑满父容器(由deviceManger.vue的430px宽度约束) */
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
|
|
+ /* 高度撑满父容器 */
|
|
|
padding: 15px;
|
|
padding: 15px;
|
|
|
|
|
+ /* 内边距:内容与容器边缘保持15px间距 */
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
|
|
+ /* 盒模型:padding计入总宽高,避免溢出 */
|
|
|
|
|
|
|
|
|
|
+ /* ========== 标题栏样式:显示"设备列表"文字和数量 ========== */
|
|
|
.list-title {
|
|
.list-title {
|
|
|
width: 177px;
|
|
width: 177px;
|
|
|
|
|
+ /* 固定宽度:匹配背景图尺寸 */
|
|
|
height: 30px;
|
|
height: 30px;
|
|
|
|
|
+ /* 固定高度 */
|
|
|
line-height: 22px;
|
|
line-height: 22px;
|
|
|
|
|
+ /* 行高:垂直居中文字 */
|
|
|
padding-left: 16px;
|
|
padding-left: 16px;
|
|
|
|
|
+ /* 左内边距:文字缩进 */
|
|
|
color: #01fefc;
|
|
color: #01fefc;
|
|
|
|
|
+ /* 青色字体:科技感配色方案 */
|
|
|
font-weight: bolder;
|
|
font-weight: bolder;
|
|
|
|
|
+ /* 加粗字体:突出标题重要性 */
|
|
|
background: var(--image-box-bg) no-repeat;
|
|
background: var(--image-box-bg) no-repeat;
|
|
|
|
|
+ /* 使用标题背景图,不重复平铺 */
|
|
|
background-size: 100% 100%;
|
|
background-size: 100% 100%;
|
|
|
|
|
+ /* 背景图完全填充容器 */
|
|
|
margin-bottom: 5px;
|
|
margin-bottom: 5px;
|
|
|
|
|
+ /* 底部间距:与下方内容区分离 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* ========== 内容区主容器:包含搜索框、导航和列表 ========== */
|
|
|
.list-content {
|
|
.list-content {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
|
|
+ /* 宽度撑满 */
|
|
|
height: calc(100% - 35px);
|
|
height: calc(100% - 35px);
|
|
|
|
|
+ /* 高度计算:总高度减去标题栏高度和间距 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 搜索框容器 */
|
|
|
.list-search {
|
|
.list-search {
|
|
|
margin-bottom: 5px;
|
|
margin-bottom: 5px;
|
|
|
|
|
+ /* 底部间距:与导航栏分离 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 内容容器:包含导航栏和设备列表 */
|
|
|
.content-container {
|
|
.content-container {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
|
|
+ /* 宽度撑满 */
|
|
|
height: calc(100% - 47px);
|
|
height: calc(100% - 47px);
|
|
|
|
|
+ /* 高度计算:减去搜索框高度和间距 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* ========== 导航栏样式:设备类型分类标签栏 ========== */
|
|
|
.content-nav {
|
|
.content-nav {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
|
|
+ /* 弹性布局:导航项水平排列 */
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
|
|
+ /* 垂直居中对齐 */
|
|
|
height: 32px;
|
|
height: 32px;
|
|
|
|
|
+ /* 固定高度 */
|
|
|
margin: 0px 5px 10px 5px;
|
|
margin: 0px 5px 10px 5px;
|
|
|
|
|
+ /* 外边距:上下左右各留间距 */
|
|
|
background-color: #15517b;
|
|
background-color: #15517b;
|
|
|
|
|
+ /* 深蓝色背景:与整体风格统一 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 导航项默认状态(未激活) */
|
|
|
.nav-item {
|
|
.nav-item {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
|
|
+ /* 弹性布局 */
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
|
|
+ /* 水平居中 */
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
|
|
+ /* 垂直居中 */
|
|
|
width: 120px;
|
|
width: 120px;
|
|
|
|
|
+ /* 固定宽度:每个标签等宽 */
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
|
|
+ /* 高度撑满父容器 */
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
|
|
+ /* 鼠标指针:提示可点击交互 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 导航项激活状态(当前选中) */
|
|
|
.nav-item-active {
|
|
.nav-item-active {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
@@ -161,29 +353,260 @@ onUnmounted(() => {
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
background-color: #1d73a8;
|
|
background-color: #1d73a8;
|
|
|
|
|
+ /* 较亮的蓝色背景:视觉上区分选中状态 */
|
|
|
border-bottom: 2px solid #36c7ff;
|
|
border-bottom: 2px solid #36c7ff;
|
|
|
|
|
+ /* 底部亮蓝色边框:强化选中指示效果 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* ========== 设备列表滚动容器 ========== */
|
|
|
.content-box {
|
|
.content-box {
|
|
|
height: calc(100% - 42px);
|
|
height: calc(100% - 42px);
|
|
|
|
|
+ /* 高度计算:减去导航栏高度和间距 */
|
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
|
|
|
+ /* 垂直方向超出时显示滚动条 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ========== 单个设备卡片样式 ========== */
|
|
|
|
|
+ .basic-device {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ /* 相对定位:作为内部绝对定位元素的参考 */
|
|
|
|
|
+ height: 106px;
|
|
|
|
|
+ /* 固定高度:每个卡片统一尺寸 */
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+ /* 右侧间距 */
|
|
|
|
|
+ background: var(--image-box-bg3) no-repeat;
|
|
|
|
|
+ /* 设备卡片背景图 */
|
|
|
|
|
+ background-size: 100% 100%;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ /* 卡片之间的垂直间距 */
|
|
|
|
|
+
|
|
|
|
|
+ /* 左上角状态指示器外框容器 */
|
|
|
|
|
+ .icon-left {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ /* 绝对定位:相对于卡片容器 */
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ /* 紧贴左边缘 */
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ /* 紧贴顶部边缘 */
|
|
|
|
|
+ width: 29px;
|
|
|
|
|
+ /* 外框尺寸 */
|
|
|
|
|
+ height: 29px;
|
|
|
|
|
+ background: var(--image-box-bg4) no-repeat;
|
|
|
|
|
+ /* 外框装饰背景图 */
|
|
|
|
|
+ background-size: 100% 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 正常状态圆点(绿色):表示设备运行正常 */
|
|
|
|
|
+ .icon-item {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ /* 绝对定位:相对于.icon-left容器 */
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ /* 水平居中 */
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ /* 垂直居中 */
|
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
|
+ /* 偏移自身宽高的50%实现完全居中 */
|
|
|
|
|
+ width: 10px;
|
|
|
|
|
+ /* 圆点直径 */
|
|
|
|
|
+ height: 10px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ /* 圆形:border-radius设为50%形成正圆 */
|
|
|
|
|
+ background-color: #4fffad;
|
|
|
|
|
+ /* 荧光绿:正常状态的视觉暗示 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 报警状态圆点(红色):表示设备存在告警 */
|
|
|
|
|
+ .icon-item-warn {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
|
+ width: 10px;
|
|
|
|
|
+ height: 10px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background-color: #ff4d4f;
|
|
|
|
|
+ /* 警示红:报警状态的强烈视觉提醒 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 设备标题行:显示序列号和操作按钮 */
|
|
|
|
|
+ .basic-title {
|
|
|
|
|
+ height: 30px;
|
|
|
|
|
+ /* 固定高度 */
|
|
|
|
|
+ margin: 0px 20px 5px 32px;
|
|
|
|
|
+ /* 外边距:避开左侧图标区域 */
|
|
|
|
|
+ padding-left: 10px;
|
|
|
|
|
+ /* 左内边距:文字缩进 */
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ /* 弹性布局:序列号和按钮组两端对齐 */
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ /* 两端对齐:序列号靠左,按钮靠右 */
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ /* 垂直居中 */
|
|
|
|
|
+ background: var(--image-box-bg5) no-repeat bottom;
|
|
|
|
|
+ /* 标题行底部装饰线 */
|
|
|
|
|
+ background-size: 100% auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 操作按钮组容器 */
|
|
|
|
|
+ .title-btn {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ /* 高度撑满父容器 */
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ /* 弹性布局:按钮水平排列 */
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ /* 垂直居中 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 单个操作按钮样式(详情/配置/重启共用) */
|
|
|
|
|
+ .btn {
|
|
|
|
|
+ width: 40px;
|
|
|
|
|
+ /* 固定宽度 */
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ /* 弹性布局 */
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ /* 文字水平居中 */
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ /* 文字垂直居中 */
|
|
|
|
|
+ background-color: #1e7db4;
|
|
|
|
|
+ /* 深蓝色按钮背景 */
|
|
|
|
|
+ margin: 0px 2px;
|
|
|
|
|
+ /* 按钮之间的水平间距 */
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ /* 鼠标指针:提示可点击 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .btn-active {
|
|
|
|
|
+ width: 40px;
|
|
|
|
|
+ /* 固定宽度 */
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ /* 弹性布局 */
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ /* 文字水平居中 */
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ /* 文字垂直居中 */
|
|
|
|
|
+ margin: 0px 2px;
|
|
|
|
|
+ /* 按钮之间的水平间距 */
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ background-color: #1e7db4;
|
|
|
|
|
+ /* 深蓝色按钮背景 */
|
|
|
|
|
+ border: 1px solid #90cff3;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 设备详细信息区域:2x2网格布局展示四项关键数据 */
|
|
|
|
|
+ .basic-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ /* 弹性布局 */
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ /* 允许换行:实现网格布局 */
|
|
|
|
|
+ height: calc(100% - 30px);
|
|
|
|
|
+ /* 高度计算:卡片总高度减去标题行高度 */
|
|
|
|
|
+ padding-left: 20px;
|
|
|
|
|
+ /* 左内边距:与左侧图标对齐 */
|
|
|
|
|
+ padding-bottom: 10px;
|
|
|
|
|
+ /* 底部内边距:防止内容贴底 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 单个信息项:网格中的单元格 */
|
|
|
|
|
+ .content-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ /* 弹性布局 */
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ /* 垂直居中对齐 */
|
|
|
|
|
+ width: 50%;
|
|
|
|
|
+ /* 宽度占一半:每行显示两个项目 */
|
|
|
|
|
+ height: calc(50% - 10px);
|
|
|
|
|
+ /* 高度占一半减去margin:两行布局 */
|
|
|
|
|
+ margin: 5px 0px;
|
|
|
|
|
+ /* 垂直间距:行与行之间分隔 */
|
|
|
|
|
+ padding-left: 15px;
|
|
|
|
|
+ /* 左内边距:文字缩进效果 */
|
|
|
|
|
+
|
|
|
|
|
+ /* 第一个单元格(设备名称):深蓝色渐变背景 */
|
|
|
|
|
+ &:nth-child(1) {
|
|
|
|
|
+ background: linear-gradient(to right, #0e5183, transparent);
|
|
|
|
|
+ /* 从左到右渐变至透明 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 第二个单元格(运行状态):稍浅的蓝色渐变 */
|
|
|
|
|
+ &:nth-child(2) {
|
|
|
|
|
+ background: linear-gradient(to right, #104974, transparent);
|
|
|
|
|
+ /* 与第一行形成层次感 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 第三个单元格(安装位置):同第一行的深色调 */
|
|
|
|
|
+ &:nth-child(3) {
|
|
|
|
|
+ background: linear-gradient(to right, #0e5183, transparent);
|
|
|
|
|
+ /* 保持奇偶行交替效果 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 第四个单元格(温度数据):同第二行的浅色调 */
|
|
|
|
|
+ &:nth-child(4) {
|
|
|
|
|
+ background: linear-gradient(to right, #104974, transparent);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 状态指示图标(正常状态的圆形标记) */
|
|
|
|
|
+ .icon-pie {
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ /* 行内块级元素:与文字同行显示 */
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ /* 相对定位:作为内部绝对定位元素的参考 */
|
|
|
|
|
+ width: 12px;
|
|
|
|
|
+ /* 外圈直径 */
|
|
|
|
|
+ height: 12px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ /* 圆形 */
|
|
|
|
|
+ background-color: rgba(79, 255, 173, .3);
|
|
|
|
|
+ /* 半透明绿色:柔和的外圈效果 */
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+ /* 右侧间距:与文字分离 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 状态指示图标的内圈实心点 */
|
|
|
|
|
+ .pie-item {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ /* 绝对定位:相对于.icon-pie容器 */
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ /* 水平居中 */
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ /* 垂直居中 */
|
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
|
+ /* 居中偏移 */
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ /* 内圈直径:比外圈小,形成环形效果 */
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ /* 圆形 */
|
|
|
|
|
+ background-color: rgba(79, 255, 173);
|
|
|
|
|
+ /* 实心荧光绿:清晰的中心点 */
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 自定义搜索框样式(覆盖Ant Design默认样式) */
|
|
|
.zxm-input {
|
|
.zxm-input {
|
|
|
height: 42px;
|
|
height: 42px;
|
|
|
|
|
+ /* 固定高度 */
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
|
|
+ /* 白色文字:在深色背景上清晰可见 */
|
|
|
background-color: transparent;
|
|
background-color: transparent;
|
|
|
|
|
+ /* 透明背景:使用自定义背景图 */
|
|
|
border: none;
|
|
border: none;
|
|
|
|
|
+ /* 移除默认边框 */
|
|
|
background: var(--image-box-bg1) no-repeat;
|
|
background: var(--image-box-bg1) no-repeat;
|
|
|
|
|
+ /* 搜索框专用背景图 */
|
|
|
background-size: 100% 100%;
|
|
background-size: 100% 100%;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 小号输入框变体 */
|
|
|
.zxm-input-sm {
|
|
.zxm-input-sm {
|
|
|
padding: 0px 20px;
|
|
padding: 0px 20px;
|
|
|
|
|
+ /* 左右内边距:文字不紧贴边缘 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* 隐藏原生滚动条:使用自定义滚动体验或完全隐藏 */
|
|
|
::-webkit-scrollbar {
|
|
::-webkit-scrollbar {
|
|
|
display: none;
|
|
display: none;
|
|
|
|
|
+ /* 隐藏WebKit内核浏览器的滚动条 */
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|