告警管理页面 移动端 html

告警管理页面 移动端 html

告警管理页面,采用移动端优先的卡片式设计,还原搜索栏、筛选器、状态标签和卡片布局
核心特性:

  • 搜索栏:圆角输入框 + 深色搜索按钮,支持回车触发

  • 筛选器:状态筛选、时间排序、升降序切换、一键重置

  • 告警卡片:左侧状态色条(绿色=未复核/低级别、蓝色=已复核、红色=高级别)、状态标签、信息网格布局

  • 交互:卡片点击反馈、搜索实时过滤、排序切换、下拉刷新手势支持

  • 响应式:在桌面端自动居中显示为移动端预览效果(430px 宽度)

视觉还原点:

  • 卡片阴影与圆角、日期与ID的配色、告警级别的颜色区分(绿色低/红色高)

  • "查看详情" 的蓝色链接样式、日历图标、箭头指示

  • 字体层级与间距精确对应原图

该移动端告警管理页面采用卡片式设计,核心功能包括:顶部搜索栏(圆角输入框+深色按钮)、多条件筛选器(状态/时间排序/重置)和可视化告警卡片(左色条标识状态等级:绿-未处理/低危、蓝-已复核、红-高危)。交互支持点击反馈、实时搜索过滤、手势刷新,并适配桌面端居中显示(430px宽)。设计细节还原卡片阴影、状态色标、日期/ID配色(红/绿区分级别)、蓝色详情链接等视觉元素,严格遵循原图字体层级与间距规范。

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>告警管理</title>
    <link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            background: #f5f7fa;
            color: #333;
            -webkit-font-smoothing: antialiased;
        }

        /* 顶部搜索栏 */
        .search-bar {
            background: #fff;
            padding: 12px 16px;
            display: flex;
            align-items: center;
            gap: 10px;
            border-bottom: 1px solid #eee;
            position: sticky;
            top: 0;
            z-index: 100;
        }

        .search-input-wrapper {
            flex: 1;
            position: relative;
        }

        .search-input-wrapper input {
            width: 100%;
            height: 38px;
            border: none;
            background: #f0f2f5;
            border-radius: 19px;
            padding: 0 16px;
            font-size: 14px;
            color: #333;
            outline: none;
        }

        .search-input-wrapper input::placeholder {
            color: #999;
        }

        .search-btn {
            width: 38px;
            height: 38px;
            background: #1a1a2e;
            border: none;
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            flex-shrink: 0;
        }

        .search-btn i {
            color: #fff;
            font-size: 18px;
        }

        /* 筛选栏 */
        .filter-bar {
            background: #fff;
            padding: 10px 16px;
            display: flex;
            align-items: center;
            gap: 8px;
            border-bottom: 1px solid #eee;
        }

        .filter-item {
            display: flex;
            align-items: center;
            gap: 4px;
            padding: 6px 12px;
            background: #f5f7fa;
            border-radius: 6px;
            font-size: 13px;
            color: #666;
            cursor: pointer;
            transition: all 0.2s;
        }

        .filter-item:hover {
            background: #e8ecf1;
        }

        .filter-item i {
            font-size: 12px;
            color: #999;
        }

        .filter-actions {
            margin-left: auto;
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .filter-action {
            display: flex;
            align-items: center;
            gap: 4px;
            font-size: 13px;
            color: #666;
            cursor: pointer;
        }

        .filter-action i {
            font-size: 14px;
        }

        .filter-action.reset {
            color: #1890ff;
        }

        /* 卡片列表 */
        .card-list {
            padding: 12px 16px;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        /* 告警卡片 */
        .alarm-card {
            background: #fff;
            border-radius: 12px;
            padding: 16px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.04);
            position: relative;
            overflow: hidden;
            transition: transform 0.2s, box-shadow 0.2s;
        }

        .alarm-card:active {
            transform: scale(0.98);
        }

        /* 左侧状态条 */
        .alarm-card::before {
            content: '';
            position: absolute;
            left: 0;
            top: 16px;
            bottom: 16px;
            width: 3px;
            border-radius: 0 2px 2px 0;
        }

        .alarm-card.status-pending::before {
            background: #52c41a;
        }

        .alarm-card.status-done::before {
            background: #1890ff;
        }

        .alarm-card.status-high::before {
            background: #ff4d4f;
        }

        /* 卡片头部 */
        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            margin-bottom: 12px;
        }

        .card-title-section {
            flex: 1;
        }

        .card-title {
            font-size: 16px;
            font-weight: 600;
            color: #1a1a2e;
            line-height: 1.4;
            margin-bottom: 6px;
        }

        .card-meta {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 12px;
            color: #999;
        }

        .card-meta .id {
            color: #1890ff;
            font-weight: 500;
        }

        .status-tag {
            padding: 3px 10px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
            flex-shrink: 0;
        }

        .status-tag.pending {
            background: #fff7e6;
            color: #fa8c16;
            border: 1px solid #ffd591;
        }

        .status-tag.done {
            background: #f6ffed;
            color: #52c41a;
            border: 1px solid #b7eb8f;
        }

        /* 卡片信息网格 */
        .card-info-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px 16px;
            margin-bottom: 12px;
        }

        .info-item {
            display: flex;
            flex-direction: column;
            gap: 3px;
        }

        .info-label {
            font-size: 12px;
            color: #999;
        }

        .info-value {
            font-size: 13px;
            color: #333;
            font-weight: 500;
        }

        .info-value.level-low {
            color: #52c41a;
        }

        .info-value.level-high {
            color: #ff4d4f;
        }

        /* 卡片底部 */
        .card-footer {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding-top: 10px;
            border-top: 1px solid #f0f0f0;
        }

        .card-date {
            display: flex;
            align-items: center;
            gap: 4px;
            font-size: 12px;
            color: #999;
        }

        .card-date i {
            font-size: 14px;
        }

        .view-detail {
            display: flex;
            align-items: center;
            gap: 2px;
            font-size: 13px;
            color: #1890ff;
            font-weight: 500;
            cursor: pointer;
        }

        .view-detail i {
            font-size: 14px;
        }

        /* 空状态 */
        .empty-state {
            text-align: center;
            padding: 60px 20px;
            color: #999;
        }

        .empty-state i {
            font-size: 48px;
            margin-bottom: 12px;
            display: block;
        }

        /* 加载更多 */
        .load-more {
            text-align: center;
            padding: 16px;
            color: #999;
            font-size: 13px;
        }

        /* 响应式 */
        @media (min-width: 768px) {
            body {
                max-width: 430px;
                margin: 0 auto;
                background: #e8e8e8;
                min-height: 100vh;
            }

            .search-bar, .filter-bar {
                border-radius: 0;
            }

            .card-list {
                padding-bottom: 30px;
            }
        }
    </style>
<base target="_blank">
</head>
<body>
    <!-- 搜索栏 -->
    <div class="search-bar">
        <div class="search-input-wrapper">
            <input type="text" placeholder="搜索告警名称" id="searchInput">
        </div>
        <button class="search-btn" onclick="handleSearch()">
            <i class="ri-search-line"></i>
        </button>
    </div>

    <!-- 筛选栏 -->
    <div class="filter-bar">
        <div class="filter-item" onclick="toggleFilter('status')">
            <span>全部状态</span>
            <i class="ri-arrow-down-s-line"></i>
        </div>
        <div class="filter-item" onclick="toggleFilter('sort')">
            <span>时间排序</span>
            <i class="ri-arrow-down-s-line"></i>
        </div>
        <div class="filter-actions">
            <div class="filter-action" onclick="toggleSortOrder()">
                <i class="ri-arrow-up-down-line"></i>
                <span>降序</span>
            </div>
            <div class="filter-action reset" onclick="resetFilters()">
                <span>重置</span>
            </div>
        </div>
    </div>

    <!-- 卡片列表 -->
    <div class="card-list" id="cardList">
        <!-- 卡片由JS动态生成 -->
    </div>

    <div class="load-more" id="loadMore">上拉加载更多</div>

    <script>
        // 模拟数据
        const alarmData = [
            {
                id: '#10226',
                title: '违规施工、异物侵占',
                status: 'pending',
                statusText: '未复核',
                level: '低',
                levelClass: 'level-low',
                task: '官浔地保',
                area: '暂无区域',
                records: '1 条',
                date: '2026-03-09 10:40:50',
                dateShort: '2026-03-09'
            },
            {
                id: '#10225',
                title: '违规施工、异物侵占',
                status: 'pending',
                statusText: '未复核',
                level: '低',
                levelClass: 'level-low',
                task: '官浔地保',
                area: '暂无区域',
                records: '5 条',
                date: '2026-03-09 10:39:05',
                dateShort: '2026-03-09'
            },
            {
                id: '#10224',
                title: '违规施工、异物侵占',
                status: 'done',
                statusText: '已复核',
                level: '低',
                levelClass: 'level-low',
                task: '官浔起飞测试',
                area: '暂无区域',
                records: '2 条',
                date: '2026-03-09 10:34:48',
                dateShort: '2026-03-09'
            },
            {
                id: '#3367',
                title: '违规施工、异物侵占',
                status: 'done',
                statusText: '已复核',
                level: '高',
                levelClass: 'level-high',
                task: '官浔地保',
                area: '暂无区域',
                records: '1 条',
                date: '2026-02-13 20:50:59',
                dateShort: '2026-02-13'
            }
        ];

        let currentData = [...alarmData];
        let sortDesc = true;

        // 渲染卡片
        function renderCards(data) {
            const container = document.getElementById('cardList');

            if (data.length === 0) {
                container.innerHTML = `
                    <div class="empty-state">
                        <i class="ri-inbox-line"></i>
                        <p>暂无告警数据</p>
                    </div>
                `;
                return;
            }

            container.innerHTML = data.map(item => `
                <div class="alarm-card status-${item.status === 'pending' ? 'pending' : (item.level === '高' ? 'high' : 'done')}" 
                     onclick="viewDetail('${item.id}')">
                    <div class="card-header">
                        <div class="card-title-section">
                            <div class="card-title">${item.title}</div>
                            <div class="card-meta">
                                <span class="id">${item.id}</span>
                                <span>${item.date}</span>
                            </div>
                        </div>
                        <span class="status-tag ${item.status}">${item.statusText}</span>
                    </div>

                    <div class="card-info-grid">
                        <div class="info-item">
                            <span class="info-label">告警级别</span>
                            <span class="info-value ${item.levelClass}">${item.level}</span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">所属任务</span>
                            <span class="info-value">${item.task}</span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">告警区域</span>
                            <span class="info-value">${item.area}</span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">告警记录</span>
                            <span class="info-value">${item.records}</span>
                        </div>
                    </div>

                    <div class="card-footer">
                        <div class="card-date">
                            <i class="ri-calendar-line"></i>
                            <span>${item.dateShort}</span>
                        </div>
                        <div class="view-detail">
                            <span>查看详情</span>
                            <i class="ri-arrow-right-s-line"></i>
                        </div>
                    </div>
                </div>
            `).join('');
        }

        // 搜索功能
        function handleSearch() {
            const keyword = document.getElementById('searchInput').value.trim().toLowerCase();
            if (!keyword) {
                currentData = [...alarmData];
            } else {
                currentData = alarmData.filter(item => 
                    item.title.toLowerCase().includes(keyword) ||
                    item.id.toLowerCase().includes(keyword) ||
                    item.task.toLowerCase().includes(keyword)
                );
            }
            sortData();
            renderCards(currentData);
        }

        // 监听回车
        document.getElementById('searchInput').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') handleSearch();
        });

        // 筛选功能
        function toggleFilter(type) {
            // 模拟筛选弹窗
            console.log('打开' + type + '筛选');
        }

        // 排序切换
        function toggleSortOrder() {
            sortDesc = !sortDesc;
            const btn = document.querySelector('.filter-action span');
            btn.textContent = sortDesc ? '降序' : '升序';
            sortData();
            renderCards(currentData);
        }

        // 排序数据
        function sortData() {
            currentData.sort((a, b) => {
                const timeA = new Date(a.date).getTime();
                const timeB = new Date(b.date).getTime();
                return sortDesc ? timeB - timeA : timeA - timeB;
            });
        }

        // 重置筛选
        function resetFilters() {
            document.getElementById('searchInput').value = '';
            currentData = [...alarmData];
            sortDesc = true;
            document.querySelector('.filter-action span').textContent = '降序';
            sortData();
            renderCards(currentData);
        }

        // 查看详情
        function viewDetail(id) {
            console.log('查看详情:', id);
            // 可扩展为跳转详情页或打开弹窗
        }

        // 初始化
        sortData();
        renderCards(currentData);

        // 模拟下拉刷新
        let startY = 0;
        document.addEventListener('touchstart', e => {
            startY = e.touches[0].clientY;
        });

        document.addEventListener('touchend', e => {
            const endY = e.changedTouches[0].clientY;
            const diff = endY - startY;

            // 下拉超过100px且在最顶部时刷新
            if (diff > 100 && window.scrollY <= 0) {
                console.log('下拉刷新');
                // 这里可以添加刷新逻辑
            }
        });
    </script>
</body>
</html>