告警管理页面 移动端 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>