소스 검색

优化样式加上webkit

xiongxing 4 주 전
부모
커밋
0ebc59bd7d
2개의 변경된 파일539개의 추가작업 그리고 36개의 파일을 삭제
  1. 2 1
      .vscode/settings.json
  2. 537 35
      src/views/Home/home.vue

+ 2 - 1
.vscode/settings.json

@@ -142,5 +142,6 @@
     "package.json": "pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.eslintrc-auto-import.json,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore"
   },
   "terminal.integrated.scrollback": 10000,
-  "nuxt.isNuxtApp": false
+  "nuxt.isNuxtApp": false,
+  "vue3snippets.enable-compile-vue-file-on-did-save-code": true
 }

+ 537 - 35
src/views/Home/home.vue

@@ -49,7 +49,7 @@
             type="primary"
             size="large"
             @click="openAddElderDialog"
-            style="margin: 5px auto 0 auto; width: 95%"
+            style="width: 95%; margin: 5px auto 0"
           >
             <Icon icon="ep:plus" />
             <span>添加长者</span>
@@ -90,7 +90,7 @@
                 </div>
                 <div class="elderly-info-large">
                   <h3>{{ elderly.name }}</h3>
-                  <p>{{ elderly.age || 0 }}岁 • {{ genderMap[elderly.gender] || '未知' }}</p>
+                  <p> {{ elderly.age || 0 }}岁 • {{ genderMap[elderly.gender] || '未知' }} </p>
                   <div class="health-status-large">
                     <div class="status-dot" :class="getHealthStatusClass(elderly.healthText)"></div>
                     <span>{{ elderly.healthText || '未知' }}</span>
@@ -164,11 +164,13 @@
                     <Icon :icon="getDeviceInfo(device).icon" />
                   </div>
                   <div class="device-info-large">
-                    <h4>{{
-                      deviceTypeOptions?.find(
-                        (v: DeviceTypeVO) => v.deviceType == device.deviceType
-                      )?.deviceTypeName || '-'
-                    }}</h4>
+                    <h4>
+                      {{
+                        deviceTypeOptions?.find(
+                          (v: DeviceTypeVO) => v.deviceType == device.deviceType
+                        )?.deviceTypeName || '-'
+                      }}
+                    </h4>
                     <p>{{ device.installPosition }}</p>
                   </div>
                   <el-tag :type="getDeviceStatusInfo(device).tagType">
@@ -284,14 +286,17 @@
             <Icon :icon="getDeviceInfo(deviceDetail).icon" />
           </div>
           <div class="device-detail-info">
-            <h3>{{
-              deviceTypeOptions?.find((v: DeviceTypeVO) => v.deviceType == deviceDetail?.deviceType)
-                ?.deviceTypeName || '-'
-            }}</h3>
+            <h3>
+              {{
+                deviceTypeOptions?.find(
+                  (v: DeviceTypeVO) => v.deviceType == deviceDetail?.deviceType
+                )?.deviceTypeName || '-'
+              }}
+            </h3>
             <p>设备类型: {{ deviceDetail.deviceType }}</p>
             <p>安装位置: {{ deviceDetail.installPosition }}</p>
-            <p
-              >设备状态:
+            <p>
+              设备状态:
               <el-tag :type="textStatusMap[deviceDetail.status]">
                 {{ deviceDetail.status }}
               </el-tag>
@@ -595,7 +600,11 @@ const deviceTypeMap: Record<
   ['health_band']: { name: '健康监测手环', icon: 'mdi:watch-variant', color: '#ff6b6b' },
   ['smart_mattress']: { name: '智能床垫', icon: 'mdi:bed-queen', color: '#4ecdc4' },
   ['security_camera']: { name: '安防摄像头', icon: 'mdi:cctv', color: '#45aaf2' },
-  ['blood_pressure_monitor']: { name: '血压监测仪', icon: 'mdi:heart-pulse', color: '#a55eea' },
+  ['blood_pressure_monitor']: {
+    name: '血压监测仪',
+    icon: 'mdi:heart-pulse',
+    color: '#a55eea'
+  },
   ['emergency_button']: {
     name: '紧急呼叫按钮',
     icon: 'mdi:alarm-light',
@@ -603,7 +612,11 @@ const deviceTypeMap: Record<
   },
   ['smoke_sensor']: { name: '烟雾传感器', icon: 'mdi:smoke-detector', color: '#26de81' },
   ['water_sensor']: { name: '水浸传感器', icon: 'mdi:water-alert', color: '#26de81' },
-  ['infrared_sensor']: { name: '人体红外传感器', icon: 'mdi:motion-sensor', color: '#26de81' },
+  ['infrared_sensor']: {
+    name: '人体红外传感器',
+    icon: 'mdi:motion-sensor',
+    color: '#26de81'
+  },
   ['door_sensor']: { name: '门磁传感器', icon: 'mdi:door-closed', color: '#26de81' },
   ['gas_sensor']: { name: '燃气传感器', icon: 'mdi:gas-cylinder', color: '#26de81' },
   ['temperature_sensor']: {
@@ -933,6 +946,7 @@ const removeDevice = (elderly: any, device: DetailDevice) => {
 
 const getHealthIcon = (metric: HealthVO) => {
   return healthIconMap[metric.name] || 'mdi:help-circle-outline'
+  // return metric.icon || 'mdi:help-circle-outline'
 }
 
 const getDeviceInfo = (device: Device | DetailDevice) => {
@@ -943,6 +957,10 @@ const getDeviceInfo = (device: Device | DetailDevice) => {
       color: '#a5b1c2'
     }
   )
+  // return (
+  //   deviceTypeOptions.valur?.find((v: DeviceTypeVO) => v.deviceType == device.deviceType)?.icon ||
+  //   'mdi:help-circle-outline'
+  // )
 }
 
 const getDeviceStatusInfo = (
@@ -1139,6 +1157,38 @@ onMounted(() => {
   // 获取数据
   getStatistics()
   getElderList()
+
+  // 页面加载后自动连接
+  setTimeout(() => {
+    connect()
+  }, 1000)
+
+  // 页面可见性变化处理
+  // 页面可见性变化处理
+  document.addEventListener('visibilitychange', () => {
+    if (document.hidden) {
+      console.log('页面切换到后台')
+    } else {
+      console.log('页面回到前台')
+      // 检查连接和心跳状态
+      checkConnectionHealth()
+    }
+  })
+  // 添加定期健康检查
+  setInterval(() => {
+    checkConnectionHealth()
+  }, 30000) // 每30秒检查一次连接健康状态
+
+  // 网络状态监测
+  window.addEventListener('online', () => {
+    if (!socket.value) {
+      connect()
+    }
+  })
+
+  window.addEventListener('offline', () => {
+    console.log('网络连接断开')
+  })
 })
 
 onUnmounted(() => {
@@ -1146,7 +1196,457 @@ onUnmounted(() => {
   if (timeInterval.value) {
     clearInterval(timeInterval.value)
   }
+
+  // 清理所有定时器
+  stopHeartbeat()
+  if (reconnectTimeout) {
+    clearTimeout(reconnectTimeout)
+    reconnectTimeout = null
+  }
+
+  // 正常关闭连接
+  if (socket.value) {
+    socket.value.close(1000, '页面关闭')
+    socket.value = null
+  }
 })
+
+// websocket设备连接
+// 响应式数据
+const socket = ref(null)
+const isConnecting = ref(false)
+const connectionId = ref(null)
+const reconnectAttempts = ref(0)
+const maxReconnectAttempts = ref(10)
+const lastActivityTime = ref('-')
+const initTime = ref(new Date().toLocaleTimeString())
+const events = ref([])
+// 定时器引用
+let heartbeatInterval = null
+let heartbeatTimeout = null
+let reconnectTimeout = null
+let lastActivity = Date.now()
+const heartbeatIntervalTime = 25000 // 25秒发送一次心跳
+const heartbeatTimeoutTime = 10000 // 10秒心跳响应超时
+
+// 添加心跳状态响应式变量
+const heartbeatStatus = ref('normal') // normal: 正常, waiting: 等待响应, timeout: 超时, expired: 过期
+const lastHeartbeatTime = ref(null) // 最后一次发送心跳的时间
+const lastHeartbeatAckTime = ref(null) // 最后一次收到心跳响应的时间
+
+// 连接websocket方法
+const connect = () => {
+  // 连接已存在或连接中,跳过重复连接
+  if (isConnecting.value || socket.value) {
+    return
+  }
+  isConnecting.value = true
+  // 连接中...
+  // 清除之前的重连定时器
+  if (reconnectTimeout) {
+    clearTimeout(reconnectTimeout)
+    reconnectTimeout = null
+  }
+  try {
+    const clientId = generateClientId()
+    // 连接地址: ${wsUrl}
+    const wsUrl = import.meta.env.VITE_API_WSS_URL + clientId
+    socket.value = new WebSocket(wsUrl)
+    socket.value.onopen = handleOpen
+    socket.value.onmessage = handleMessage
+    socket.value.onclose = handleClose
+    socket.value.onerror = handleError
+  } catch (error) {
+    // 连接创建错误: ${error.message}
+    handleConnectionFailure()
+  }
+}
+
+const sendMessage = (message) => {
+  if (socket.value && socket.value.readyState === WebSocket.OPEN) {
+    try {
+      socket.value.send(JSON.stringify(message))
+      lastActivity = Date.now()
+      updateLastActivity()
+      return true
+    } catch (error) {
+      // 发送消息失败: ${error.message}
+      return false
+    }
+  } else {
+    // 无法发送消息: WebSocket未连接
+    return false
+  }
+}
+
+// 连接成功
+const handleOpen = (event) => {
+  isConnecting.value = false
+  reconnectAttempts.value = 0
+  lastActivity = Date.now()
+  // WebSocket连接成功
+  // 启动心跳机制
+  startHeartbeat()
+  // 发送身份验证消息
+  let postData = {
+    type: 'AUTH',
+    clientType: 'monitor',
+    clientId: generateClientId(),
+    timestamp: Date.now()
+  }
+  sendMessage(postData)
+}
+
+// 获取到服务器发送的消息
+const handleMessage = (event) => {
+  try {
+    lastActivity = Date.now()
+    updateLastActivity()
+
+    const data = JSON.parse(event.data)
+    processIncomingData(data)
+  } catch (error) {
+    console.error('消息解析错误:', error, '原始数据:', event.data)
+  }
+}
+
+// 处理心跳响应
+const handleHeartbeatAck = (data) => {
+  console.log('心跳消息', data)
+  // 清除超时检测
+  if (heartbeatTimeout) {
+    clearTimeout(heartbeatTimeout)
+    heartbeatTimeout = null
+  }
+
+  heartbeatStatus.value = 'normal'
+  lastHeartbeatAckTime.value = Date.now()
+  lastActivity = Date.now()
+  updateLastActivity()
+
+  console.log('💓 心跳响应正常')
+}
+
+// 新增:连接健康检查
+const checkConnectionHealth = () => {
+  if (!socket.value || socket.value.readyState !== WebSocket.OPEN) {
+    return
+  }
+
+  const now = Date.now()
+  const timeSinceLastActivity = now - lastActivity
+  const timeSinceLastHeartbeat = now - (lastHeartbeatTime.value || 0)
+  const totalTimeout = heartbeatIntervalTime + heartbeatTimeoutTime
+
+  console.log('🔍 连接健康检查:')
+  console.log('最后活动:', Math.round(timeSinceLastActivity / 1000) + '秒前')
+  console.log('最后心跳:', Math.round(timeSinceLastHeartbeat / 1000) + '秒前')
+  console.log('心跳状态:', heartbeatStatus.value)
+
+  // 如果超过总超时时间无活动,认为连接已死
+  if (timeSinceLastActivity > totalTimeout + 10000) {
+    // 额外给10秒缓冲
+    console.error('🚨 连接长时间无活动,可能已断开')
+    heartbeatStatus.value = 'expired'
+    handleHeartbeatExpired()
+    return
+  }
+
+  // 如果心跳等待时间过长,发送测试消息
+  if (heartbeatStatus.value === 'waiting' && timeSinceLastHeartbeat > heartbeatTimeoutTime + 5000) {
+    console.warn('⚠️ 心跳响应延迟,发送测试消息')
+    sendMessage({ type: 'PING', timestamp: now })
+  }
+}
+
+const handleClose = (event) => {
+  console.log(`WebSocket连接关闭: 代码 ${event.code}, 原因: ${event.reason || '未知'}`)
+
+  isConnecting.value = false
+  socket.value = null
+  connectionId.value = null
+  heartbeatStatus.value = 'expired' // 连接关闭时标记为过期
+
+  // 停止心跳检测
+  stopHeartbeat()
+
+  // 清除可能存在的重连定时器
+  if (reconnectTimeout) {
+    clearTimeout(reconnectTimeout)
+    reconnectTimeout = null
+  }
+
+  // 判断是否需要重连(非正常关闭且未超过最大重连次数)
+  if (event.code !== 1000 && reconnectAttempts.value < maxReconnectAttempts.value) {
+    reconnectAttempts.value++
+    const delay = Math.min(3000 * Math.pow(1.5, reconnectAttempts.value - 1), 30000)
+
+    console.log(
+      `${Math.round(delay / 1000)}秒后尝试重连 (${reconnectAttempts.value}/${
+        maxReconnectAttempts.value
+      })`
+    )
+
+    reconnectTimeout = setTimeout(() => {
+      // 检查是否已经重新连接
+      if (!socket.value && !isConnecting.value) {
+        connect()
+      }
+    }, delay)
+  } else if (reconnectAttempts.value >= maxReconnectAttempts.value) {
+    console.error('已达到最大重连次数,停止自动重连')
+    heartbeatStatus.value = 'expired'
+  }
+}
+
+const handleError = (event) => {
+  console.error('WebSocket错误详情:', event)
+}
+
+// 修改心跳检测逻辑
+const startHeartbeat = () => {
+  // 先停止可能存在的旧心跳
+  stopHeartbeat()
+
+  // 初始化心跳状态
+  heartbeatStatus.value = 'normal'
+  lastHeartbeatTime.value = Date.now()
+
+  // 定时发送心跳
+  heartbeatInterval = setInterval(() => {
+    // 检查连接状态
+    if (!socket.value || socket.value.readyState !== WebSocket.OPEN) {
+      console.log('连接已断开,停止心跳')
+      heartbeatStatus.value = 'expired'
+      stopHeartbeat()
+      return
+    }
+
+    // 检查上次心跳是否过期(超过间隔+超时时间未收到响应)
+    const now = Date.now()
+    const timeSinceLastHeartbeat = now - lastHeartbeatTime.value
+    const totalTimeout = heartbeatIntervalTime + heartbeatTimeoutTime
+
+    if (lastHeartbeatTime.value && timeSinceLastHeartbeat > totalTimeout) {
+      console.warn(
+        `💔 心跳已过期,距离上次心跳${Math.round(timeSinceLastHeartbeat / 1000)}秒,触发重连`
+      )
+      heartbeatStatus.value = 'expired'
+      handleHeartbeatExpired()
+      return
+    }
+
+    // 发送心跳
+    heartbeatStatus.value = 'waiting'
+    lastHeartbeatTime.value = now
+    let params = {
+      type: 'HEARTBEAT',
+      timestamp: now,
+      clientTime: now
+    }
+    console.log('params', params)
+    const success = sendMessage(params)
+
+    if (success) {
+      // 设置心跳超时检测
+      if (heartbeatTimeout) {
+        clearTimeout(heartbeatTimeout)
+      }
+
+      heartbeatTimeout = setTimeout(() => {
+        console.warn('💔 心跳响应超时,连接可能已断开')
+        heartbeatStatus.value = 'timeout'
+        handleHeartbeatTimeout()
+      }, heartbeatTimeoutTime)
+    } else {
+      console.warn('心跳发送失败,连接可能有问题')
+      heartbeatStatus.value = 'timeout'
+      handleHeartbeatTimeout()
+    }
+  }, heartbeatIntervalTime)
+}
+
+// 新增:处理心跳过期
+const handleHeartbeatExpired = () => {
+  console.error('🚨 心跳已过期,关闭连接并重新连接')
+
+  // 停止所有定时器
+  stopHeartbeat()
+
+  // 关闭当前连接
+  if (socket.value) {
+    socket.value.close(1000, '心跳过期')
+    socket.value = null
+  }
+
+  // 立即重新连接
+  reconnectAttempts.value = 0 // 重置重连计数
+  setTimeout(() => {
+    if (!socket.value && !isConnecting.value) {
+      console.log('开始心跳过期重连...')
+      connect()
+    }
+  }, 1000)
+}
+
+// 新增:处理心跳超时
+const handleHeartbeatTimeout = () => {
+  console.warn('⏰ 心跳响应超时,关闭连接触发重连')
+
+  // 停止心跳检测
+  stopHeartbeat()
+
+  // 主动关闭连接触发重连机制
+  if (socket.value) {
+    socket.value.close(1000, '心跳响应超时')
+  }
+}
+
+const stopHeartbeat = () => {
+  if (heartbeatInterval) {
+    clearInterval(heartbeatInterval)
+    heartbeatInterval = null
+  }
+  if (heartbeatTimeout) {
+    clearTimeout(heartbeatTimeout)
+    heartbeatTimeout = null
+  }
+}
+
+const processIncomingData = (data) => {
+  if (!data || !data.type) {
+    // 收到无效消息格式
+    return
+  }
+
+  switch (data.type) {
+    case 'CONNECT_SUCCESS':
+      handleConnectSuccess(data)
+      break
+    case 'AUTH_SUCCESS':
+      handleAuthSuccess(data)
+      break
+    case 'SOS_ALERT':
+      // 需要展示的数据
+      handleSOSAlert(data)
+      break
+    case 'DEVICE_DATA_UPDATE':
+      handleDeviceData(data)
+      break
+    case 'SYSTEM_STATS_UPDATE':
+      handleStatsUpdate(data)
+      break
+    case 'HEARTBEAT_ACK':
+      handleHeartbeatAck(data)
+      break
+    default:
+      handleGenericMessage(data)
+  }
+}
+
+const handleConnectSuccess = (data) => {
+  console.log('WebSocket连接成功')
+  // 连接成功,连接ID: ${connectionId.value}
+  connectionId.value = data.connectionId
+}
+
+const handleAuthSuccess = (data) => {
+  console.log('身份验证成功')
+}
+
+const handleSOSAlert = (alertData) => {
+  console.log('alertData', alertData)
+  try {
+    const alert = alertData.data || alertData
+    // 发送确认消息
+    sendMessage({
+      type: 'SOS_ACK',
+      alertId: alertData.timestamp,
+      timestamp: Date.now()
+    })
+    // notification.warning({
+    //   message: h('div', {
+    //     style: {
+    //       color: '#fff',
+    //       // fontSize: '18px'
+    //       fontSize: '0.1rem'
+    //     },
+    //     innerHTML: `
+    // 			<div>🚨🚨 SOS紧急预警 🚨🚨</div>
+    // 			`
+    //   }),
+    //   duration: 10,
+    //   // duration: null,
+    //   class: 'my-warning-notification',
+    //   style: {
+    //     background: 'linear-gradient(135deg, #1e4184 0%, #3469e3 100%) !important',
+    //     border: '1px solid rgba(42, 157, 143, 0.3) !important',
+    //     'box-shadow': '0 4px 20px rgba(0, 0, 0, 0.5) !important',
+    //     color: '#fff !important',
+    //     width: '2rem !important'
+    //   },
+    //   description: h('div', {
+    //     style: {
+    //       color: '#fff',
+    //       fontSize: '0.1rem'
+    //     },
+    //     innerHTML: `
+    //        <div>院区名称: ${alert.organizationName || '未知'}</div>
+    //        <div>长者姓名: ${alert.elderName || '未知'}</div>
+    //        <div>长者房间: ${alert.roomName || '未知'}</div>
+    // 	 <div>设备类型: ${alert.deviceType || '未知'}</div>
+    // 	 <div>设备电量: ${alert.batteryLevel || '0%'}</div>
+    //        <div>时间: ${new Date(alertData.timestamp).toLocaleString()}</div>
+    //      `
+    //   })
+    // })
+  } catch (error) {
+    console.error('处理SOS告警错误:', error)
+  }
+}
+
+const handleDeviceData = (deviceData) => {
+  console.log('deviceData', deviceData)
+}
+
+const handleStatsUpdate = (statsData) => {
+  console.log('statsData', statsData)
+}
+
+// 修改普通消息处理,不干扰心跳检测
+const handleGenericMessage = (data) => {
+  console.log('收到普通消息:', data)
+  // 这里只更新最后活动时间,但不影响心跳超时检测
+  lastActivity = Date.now()
+  updateLastActivity()
+}
+
+const updateLastActivity = () => {
+  lastActivityTime.value = new Date().toLocaleTimeString()
+}
+
+const generateClientId = () => {
+  const timestamp = Date.now()
+  const random = Math.random().toString(36).substr(2, 9)
+  return `monitor_${timestamp}_${random}`
+}
+
+const handleConnectionFailure = () => {
+  isConnecting.value = false
+  socket.value = null
+
+  // 连接创建失败也尝试重连
+  if (reconnectAttempts.value < maxReconnectAttempts.value) {
+    reconnectAttempts.value++
+    const delay = Math.min(3000 * Math.pow(1.5, reconnectAttempts.value - 1), 30000)
+
+    reconnectTimeout = setTimeout(() => {
+      if (!socket.value && !isConnecting.value) {
+        connect()
+      }
+    }, delay)
+  }
+}
 </script>
 
 <style lang="scss" scoped>
@@ -1184,49 +1684,49 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 
 @keyframes auroraShift {
   0% {
-    transform: translate(-10%, -10%) scale(1);
     opacity: 0.6;
+    transform: translate(-10%, -10%) scale(1);
   }
 
   50% {
-    transform: translate(5%, 10%) scale(1.1);
     opacity: 0.9;
+    transform: translate(5%, 10%) scale(1.1);
   }
 
   100% {
-    transform: translate(15%, -5%) scale(1.05);
     opacity: 0.6;
+    transform: translate(15%, -5%) scale(1.05);
   }
 }
 
 /* 大屏专用样式 */
 .large-screen {
+  position: relative;
   padding: 20px;
+  overflow: hidden;
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
     sans-serif;
   color: $text-light;
   background: radial-gradient(circle at 10% 20%, rgb(15 88 255 / 25%) 0%, transparent 35%),
     radial-gradient(circle at 90% 10%, rgb(20 201 201 / 18%) 0%, transparent 30%),
     linear-gradient(135deg, $bg-gradient-start 0%, $bg-gradient-mid 45%, $bg-gradient-end 100%);
-  position: relative;
-  overflow: hidden;
 
   &::before,
   &::after {
-    content: '';
     position: absolute;
-    inset: -30%;
+    pointer-events: none;
     background: radial-gradient(circle, $bg-accent-1 0%, transparent 60%);
-    filter: blur(80px);
+    content: '';
     opacity: 0.7;
+    filter: blur(80px);
     animation: auroraShift 18s ease-in-out infinite alternate;
-    pointer-events: none;
+    inset: -30%;
   }
 
   &::after {
     background: radial-gradient(circle, $bg-accent-2 0%, transparent 60%);
-    animation-duration: 22s;
     animation-delay: 4s;
+    animation-duration: 22s;
   }
 
   .my-container {
@@ -1260,8 +1760,8 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
   height: 100%;
   background-image: linear-gradient(rgb(255 255 255 / 5%) 1px, transparent 1px),
     linear-gradient(90deg, rgb(255 255 255 / 5%) 1px, transparent 1px);
-  background-size: 40px 40px;
   background-position: center;
+  background-size: 40px 40px;
   opacity: 0.35;
   mix-blend-mode: screen;
 }
@@ -1284,8 +1784,8 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     font-size: 36px;
     font-weight: 700;
     background: linear-gradient(90deg, $primary-color, $secondary-color, $accent-color);
+    /* stylelint-disable-next-line property-no-vendor-prefix */
     -webkit-background-clip: text;
-    background-clip: text;
     -webkit-text-fill-color: transparent;
   }
 
@@ -1368,8 +1868,8 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
   font-size: 42px;
   font-weight: 700;
   background: linear-gradient(90deg, $primary-color, $secondary-color);
+  /* stylelint-disable-next-line property-no-vendor-prefix */
   -webkit-background-clip: text;
-  background-clip: text;
   -webkit-text-fill-color: transparent;
 }
 
@@ -1638,12 +2138,12 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 .device-card-large {
   display: flex;
   padding: 20px;
+  cursor: default;
   background: rgb(255 255 255 / 5%);
   border: 1px solid rgb(255 255 255 / 8%);
   border-radius: 12px;
   flex-direction: column;
   gap: 15px;
-  cursor: default;
 
   &.online {
     border-left: 4px solid $success-color;
@@ -1654,8 +2154,8 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
   }
 
   &.warning {
-    border-left: 4px solid $warning-color;
     cursor: pointer;
+    border-left: 4px solid $warning-color;
     box-shadow: 0 0 20px rgb(253 150 68 / 25%);
   }
 }
@@ -1887,12 +2387,12 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 }
 
 .warning-drawer {
-  background: linear-gradient(135deg, #111a3a, #0a1124);
   color: $text-light;
+  background: linear-gradient(135deg, #111a3a, #0a1124);
 
   .el-drawer__header {
-    margin-bottom: 0;
     padding-bottom: 10px;
+    margin-bottom: 0;
     border-bottom: 1px solid rgb(255 255 255 / 10%);
   }
 
@@ -2079,17 +2579,18 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 :deep(.el-empty__description) {
   color: $text-gray !important;
 }
+
 :deep(.el-empty) {
   padding: 10px !important;
 }
 </style>
 <style lang="scss">
 .large-screen-dialog {
+  margin-top: 8vh !important;
   background: #1a1f2e !important;
   border: 1px solid rgb(255 255 255 / 10%) !important;
   border-radius: 16px !important;
   box-shadow: 0 20px 60px rgb(0 0 0 / 40%) !important;
-  margin-top: 8vh !important;
 
   .el-dialog__header {
     padding: 25px !important;
@@ -2111,11 +2612,12 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     color: #fff !important;
   }
 }
+
 .my-warning-notification {
+  width: '2rem !important';
+  color: '#fff !important';
   background: 'linear-gradient(135deg, #1e4184 0%, #3469e3 100%) !important';
   border: '1px solid rgba(42, 157, 143, 0.3) !important';
   box-shadow: '0 4px 20px rgba(0, 0, 0, 0.5) !important';
-  color: '#fff !important';
-  width: '2rem !important';
 }
 </style>