@import 'variables.css'; /* ======================================== 页面容器 ======================================== */ .page { min-height: 100vh; background-color: var(--color-bg); padding-bottom: 60px; } .page-content { padding: 16px; padding-bottom: 80px; } /* ======================================== 顶栏 ======================================== */ .header { position: sticky; top: 0; left: 0; right: 0; z-index: 100; background-color: var(--color-primary); color: #ffffff; padding: 14px 16px; font-size: 17px; font-weight: 600; text-align: center; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); } .header-title { display: flex; align-items: center; justify-content: center; gap: 8px; } .header-icon { width: 20px; height: 20px; } /* ======================================== 卡片 ======================================== */ .card { background-color: var(--color-card); border-radius: 12px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); } .card-title { font-size: 16px; font-weight: 600; color: var(--color-text); margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--color-border); } .card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .card-body { color: var(--color-text-secondary); font-size: 14px; line-height: 1.6; } /* ======================================== 统计卡片网格 ======================================== */ .stat-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 16px; } .stat-card { background-color: var(--color-card); border-radius: 12px; padding: 16px; text-align: center; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); } .stat-label { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.5px; } .stat-value { font-size: 28px; font-weight: 700; color: var(--color-primary); line-height: 1.2; } .stat-sub { font-size: 11px; color: var(--color-text-secondary); margin-top: 4px; } .stat-icon { width: 40px; height: 40px; margin: 0 auto 8px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background-color: var(--color-bg); } /* ======================================== 按钮 ======================================== */ .btn { display: inline-flex; align-items: center; justify-content: center; padding: 10px 20px; font-size: 14px; font-weight: 500; border-radius: 8px; border: none; cursor: pointer; transition: all 0.2s ease; gap: 6px; } .btn:active { transform: scale(0.98); opacity: 0.9; } .btn-primary { background-color: var(--color-primary); color: #ffffff; } .btn-primary:hover { background-color: var(--color-primary-light); } .btn-secondary { background-color: var(--color-bg); color: var(--color-text); border: 1px solid var(--color-border); } .btn-secondary:hover { background-color: var(--color-border); } .btn-danger { background-color: var(--color-danger); color: #ffffff; } .btn-danger:hover { opacity: 0.9; } .btn-block { display: flex; width: 100%; padding: 12px 20px; font-size: 15px; } .btn-sm { padding: 6px 12px; font-size: 12px; } .btn-icon { width: 36px; height: 36px; padding: 0; border-radius: 50%; } /* ======================================== 底部Tab栏 ======================================== */ .tab-bar { position: fixed; bottom: 0; left: 0; right: 0; height: 60px; background-color: var(--color-card); display: grid; grid-template-columns: repeat(4, 1fr); box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.08); z-index: 100; } .tab-item { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; color: var(--color-text-secondary); font-size: 11px; cursor: pointer; transition: color 0.2s ease; -webkit-tap-highlight-color: transparent; } .tab-item.active { color: var(--color-primary); } .tab-item-icon { width: 22px; height: 22px; } .tab-item-text { font-weight: 500; } /* ======================================== 子Tab ======================================== */ .sub-tabs { display: flex; background-color: var(--color-card); border-bottom: 1px solid var(--color-border); padding: 0 12px; gap: 8px; overflow-x: auto; -webkit-overflow-scrolling: touch; } .sub-tabs::-webkit-scrollbar { display: none; } .sub-tab { padding: 12px 16px; font-size: 14px; color: var(--color-text-secondary); white-space: nowrap; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s ease; } .sub-tab.active { color: var(--color-primary); border-bottom-color: var(--color-primary); font-weight: 600; } .sub-tab:hover:not(.active) { color: var(--color-text); } /* ======================================== 列表项 ======================================== */ .list-item { display: flex; align-items: center; padding: 14px 16px; background-color: var(--color-card); border-bottom: 1px solid var(--color-border); cursor: pointer; transition: background-color 0.15s ease; } .list-item:first-child { border-top-left-radius: 12px; border-top-right-radius: 12px; } .list-item:last-child { border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; border-bottom: none; } .list-item:active { background-color: var(--color-bg); } .list-item-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background-color: var(--color-bg); margin-right: 12px; flex-shrink: 0; } .list-item-icon svg { width: 20px; height: 20px; color: var(--color-primary); } .list-item-content { flex: 1; min-width: 0; } .list-item-title { font-size: 15px; font-weight: 500; color: var(--color-text); margin-bottom: 2px; } .list-item-desc { font-size: 13px; color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .list-item-arrow { width: 16px; height: 16px; color: var(--color-text-secondary); margin-left: 8px; flex-shrink: 0; } /* ======================================== 设备卡片网格 ======================================== */ .device-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; padding: 12px; } .device-card { background-color: var(--color-card); border-radius: 12px; padding: 14px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease; } .device-card:active { transform: scale(0.98); } .device-card-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 10px; } .device-card-icon { width: 44px; height: 44px; border-radius: 10px; background-color: var(--color-bg); display: flex; align-items: center; justify-content: center; } .device-card-icon svg { width: 24px; height: 24px; color: var(--color-primary); } .device-card-status { padding: 3px 8px; border-radius: 10px; font-size: 11px; font-weight: 500; } .device-card-status.online { background-color: rgba(34, 197, 94, 0.12); color: var(--color-online); } .device-card-status.offline { background-color: rgba(156, 163, 175, 0.12); color: var(--color-offline); } .device-card-name { font-size: 15px; font-weight: 600; color: var(--color-text); margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .device-card-model { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 6px; } .device-card-location { font-size: 12px; color: var(--color-text-secondary); display: flex; align-items: center; gap: 4px; } .device-card-location svg { width: 12px; height: 12px; } /* ======================================== 预警列表项 ======================================== */ .alert-item { display: flex; padding: 14px 16px; background-color: var(--color-card); border-radius: 12px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06); cursor: pointer; transition: transform 0.15s ease; } .alert-item:active { transform: scale(0.99); } .alert-icon { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 12px; flex-shrink: 0; } .alert-icon svg { width: 20px; height: 20px; } .alert-icon.danger { background-color: rgba(249, 115, 22, 0.12); color: var(--color-danger); } .alert-icon.warning { background-color: rgba(234, 179, 8, 0.12); color: var(--color-warning); } .alert-icon.handled { background-color: rgba(156, 163, 175, 0.12); color: var(--color-offline); } .alert-content { flex: 1; min-width: 0; } .alert-title { font-size: 14px; font-weight: 500; color: var(--color-text); margin-bottom: 4px; line-height: 1.4; } .alert-meta { font-size: 12px; color: var(--color-text-secondary); display: flex; gap: 12px; flex-wrap: wrap; } .alert-meta-item { display: flex; align-items: center; gap: 4px; } /* ======================================== 表单 ======================================== */ .form-group { margin-bottom: 16px; } .form-label { display: block; font-size: 14px; font-weight: 500; color: var(--color-text); margin-bottom: 8px; } .form-label-required::after { content: ' *'; color: var(--color-danger); } .form-input { width: 100%; padding: 10px 14px; font-size: 14px; color: var(--color-text); background-color: var(--color-bg); border: 1px solid var(--color-border); border-radius: 8px; outline: none; transition: border-color 0.2s ease, box-shadow 0.2s ease; } .form-input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(30, 58, 95, 0.1); } .form-input::placeholder { color: var(--color-text-secondary); } .form-input:disabled { opacity: 0.6; cursor: not-allowed; } textarea.form-input { min-height: 100px; resize: vertical; line-height: 1.5; } /* ======================================== 复选框 ======================================== */ .checkbox-group { display: flex; flex-wrap: wrap; gap: 16px; } .checkbox-item { display: flex; align-items: center; gap: 6px; cursor: pointer; } .checkbox-item input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; accent-color: var(--color-primary); } .checkbox-label { font-size: 14px; color: var(--color-text); } .form-select { width: 100%; padding: 10px 36px 10px 14px; font-size: 14px; color: var(--color-text); background-color: var(--color-bg); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; border: 1px solid var(--color-border); border-radius: 8px; outline: none; cursor: pointer; -webkit-appearance: none; appearance: none; transition: border-color 0.2s ease; } .form-select:focus { border-color: var(--color-primary); } .form-hint { font-size: 12px; color: var(--color-text-secondary); margin-top: 6px; } .form-error { font-size: 12px; color: var(--color-danger); margin-top: 6px; } /* ======================================== 图片上传 ======================================== */ .photo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; } .photo-item { aspect-ratio: 1; border-radius: 8px; overflow: hidden; position: relative; background-color: var(--color-bg); } .photo-item img { width: 100%; height: 100%; object-fit: cover; } .photo-item-delete { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; border-radius: 50%; background-color: rgba(0, 0, 0, 0.5); color: #ffffff; display: flex; align-items: center; justify-content: center; cursor: pointer; } .photo-item-delete svg { width: 14px; height: 14px; } .photo-add { aspect-ratio: 1; border-radius: 8px; border: 1px dashed var(--color-border); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; cursor: pointer; transition: border-color 0.2s ease, background-color 0.2s ease; background-color: var(--color-bg); } .photo-add:hover { border-color: var(--color-primary); background-color: rgba(30, 58, 95, 0.04); } .photo-add-icon { width: 28px; height: 28px; color: var(--color-text-secondary); } .photo-add-text { font-size: 12px; color: var(--color-text-secondary); } /* ======================================== Toast提示 ======================================== */ .toast { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); z-index: 9999; padding: 12px 24px; background-color: rgba(0, 0, 0, 0.75); color: #ffffff; border-radius: 8px; font-size: 14px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); animation: toast-in 0.3s ease forwards; max-width: 80%; text-align: center; } .toast.success { background-color: var(--color-success); } .toast.warning { background-color: var(--color-warning); } .toast.error { background-color: var(--color-danger); } @keyframes toast-in { 0% { opacity: 0; transform: translateX(-50%) translateY(-20px); } 100% { opacity: 1; transform: translateX(-50%) translateY(0); } } @keyframes toast-out { 0% { opacity: 1; transform: translateX(-50%) translateY(0); } 100% { opacity: 0; transform: translateX(-50%) translateY(-20px); } } /* ======================================== 空状态 ======================================== */ .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 20px; text-align: center; } .empty-state-icon { width: 80px; height: 80px; margin-bottom: 16px; color: var(--color-text-secondary); opacity: 0.5; } .empty-state-text { font-size: 14px; color: var(--color-text-secondary); margin-bottom: 20px; } .empty-state-action { margin-top: 8px; } /* ======================================== 辅助类 ======================================== */ .text-primary { color: var(--color-primary); } .text-danger { color: var(--color-danger); } .text-warning { color: var(--color-warning); } .text-success { color: var(--color-success); } .text-secondary { color: var(--color-text-secondary); } .bg-primary { background-color: var(--color-primary); } .bg-danger { background-color: var(--color-danger); } .bg-warning { background-color: var(--color-warning); } .bg-success { background-color: var(--color-success); } .border-radius { border-radius: 8px; } .border-radius-lg { border-radius: 12px; } .border-radius-full { border-radius: 50%; } .shadow { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); } .shadow-md { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .shadow-lg { box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); } /* Flex utilities */ .flex-1 { flex: 1; } .flex-shrink-0 { flex-shrink: 0; } .flex-wrap { flex-wrap: wrap; } /* Spacing */ .mt-1 { margin-top: 4px; } .mt-4 { margin-top: 16px; } .mb-1 { margin-bottom: 4px; } .mb-2 { margin-bottom: 8px; } .mb-4 { margin-bottom: 16px; } .mx-auto { margin-left: auto; margin-right: auto; } /* Grid */ .grid-2 { grid-template-columns: repeat(2, 1fr); } .grid-3 { grid-template-columns: repeat(3, 1fr); } .grid-4 { grid-template-columns: repeat(4, 1fr); } /* Divider */ .divider { height: 1px; background-color: var(--color-border); margin: 16px 0; } .divider-vertical { width: 1px; background-color: var(--color-border); margin: 0 16px; } /* ======================================== 设备详情页 ======================================== */ .device-info { display: flex; justify-content: space-between; align-items: flex-start; } .device-info-left { display: flex; gap: 12px; } .device-info-icon { font-size: 40px; } .device-info-text { display: flex; flex-direction: column; gap: 2px; } .device-info-name { font-size: 16px; font-weight: 600; color: var(--color-text); } .device-info-model { font-size: 13px; color: var(--color-text-secondary); } .device-info-location { font-size: 12px; color: var(--color-text-secondary); display: flex; align-items: center; gap: 4px; } .device-info-right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; } .device-status-badge { padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: 500; } .device-status-badge.online { background-color: rgba(34, 197, 94, 0.12); color: var(--color-online); } .device-status-badge.offline { background-color: rgba(156, 163, 175, 0.12); color: var(--color-offline); } .device-last-seen { font-size: 11px; color: var(--color-text-secondary); } /* ======================================== 数据网格 ======================================== */ .data-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; } .data-item { background-color: var(--color-bg); border-radius: 8px; padding: 12px; } .data-label { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 4px; } .data-value { font-size: 20px; font-weight: 600; color: var(--color-text); } .data-unit { font-size: 12px; font-weight: 400; color: var(--color-text-secondary); margin-left: 2px; } /* ======================================== 告警横幅 ======================================== */ .alert-banner { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-radius: 8px; margin-bottom: 12px; font-size: 14px; } .alert-banner.hidden { display: none; } .alert-banner.alert-danger { background-color: rgba(249, 115, 22, 0.12); color: var(--color-danger); } .alert-banner.alert-warning { background-color: rgba(234, 179, 8, 0.12); color: var(--color-warning); } .alert-banner-icon { font-size: 16px; } .alert-banner-msg { flex: 1; } /* ======================================== 刷新栏 ======================================== */ .refresh-bar { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; } .refresh-time { font-size: 12px; color: var(--color-text-secondary); }