这个项目将通过代码展示“数智化”如何将海量的、看似无关的“微尘”数据汇聚成有价值的“智慧”,从而实现“平安护航”的目标。
项目名称:数智之眼 - 公共安全数据可视化平台
教育意义:
- 数据可视化: 直观展示数据如何从杂乱变为有序,从孤立变为关联。
- 交互式探索: 让用户通过交互(点击、筛选)主动发现数据背后的故事和模式。
- 模拟数智化过程: 模拟从原始数据流到聚合分析,再到智能预警的全过程。
- 技术实践: 综合运用HTML、CSS、JavaScript和图表库(ECharts),是一个完整的前端项目案例。
第一步:项目准备
- 创建一个项目文件夹,如
public-security-dashboard。 - 在文件夹中创建以下文件:
index.html(页面结构)styles.css(页面样式)script.js(核心逻辑)data.js(模拟数据)
- 我们将使用 ECharts 这个强大的开源图表库。你不需要下载它,直接在HTML中通过CDN引入即可。
第二步:代码实现
1. 模拟数据 (data.js)
这个文件模拟了从不同来源汇聚的“微尘”数据。
// data.js - 模拟汇聚的异构数据源
// 模拟实时事件流 (如110报警、交通违章、社区上报)
const eventStreamData = [
{ time: '2023-10-01 08:15', type: '交通事故', location: '人民路与解放大街交叉口', level: '中' },
{ time: '2023-10-01 09:32', type: '社区求助', location: '阳光小区3号楼', level: '低' },
{ time: '2023-10-01 10:05', type: '火警预警', location: '城西工业园A区', level: '高' },
{ time: '2023-10-01 11:20', type: '交通事故', location: '高速入口南向2公里处', level: '高' },
{ time: '2023-10-01 14:00', type: '走失人员', location: '中心公园', level: '中' },
{ time: '2023-10-01 15:45', type: '社区求助', location: '幸福里社区', level: '低' },
{ time: '2023-10-01 16:30', type: '公共秩序', location: '市中心广场', level: '中' },
{ time: '2023-10-01 18:10', type: '交通事故', location: '解放大街与建设路交叉口', level: '中' },
];
// 模按区域聚合的数据 (数智化分析结果)
const regionalAnalysisData = [
{ name: '城东区', value: 125, incidentTypes: { '交通事故': 45, '社区求助': 60, '其他': 20 } },
{ name: '城西区', value: 95, incidentTypes: { '火警预警': 15, '公共秩序': 30, '社区求助': 50 } },
{ name: '中心区', value: 210, incidentTypes: { '公共秩序': 80, '走失人员': 25, '交通事故': 75, '其他': 30 } },
{ name: '开发区', value: 70, incidentTypes: { '交通事故': 35, '火警预警': 20, '其他': 15 } },
{ name: '高新区', value: 88, incidentTypes: { '社区求助': 40, '公共秩序': 28, '其他': 20 } },
];
// 模拟24小时事件分布趋势
const hourlyTrendData = [
['00:00', 5], ['01:00', 3], ['02:00', 2], ['03:00', 2], ['04:00', 4], ['05:00', 8],
['06:00', 15], ['07:00', 35], ['08:00', 65], ['09:00', 58], ['10:00', 48], ['11:00', 52],
['12:00', 61], ['13:00', 55], ['14:00', 50], ['15:00', 49], ['16:00', 53], ['17:00', 70],
['18:00', 85], ['19:00', 78], ['20:00', 60], ['21:00', 45], ['22:00', 25], ['23:00', 12]
];
// 模拟资源调度状态
const resourceStatusData = [
{ name: '巡逻警车', available: 85, total: 100 },
{ name: '消防车辆', available: 28, total: 30 },
{ name: '急救单元', available: 45, total: 50 },
{ name: '社区网格员', available: 320, total: 350 },
];
2. 页面结构 (index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数智之眼 - 公共安全可视化平台</title>
<link rel="stylesheet" href="styles.css">
<!-- 引入 ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
</head>
<body>
<header>
<h1>数智之眼</h1>
<p>微尘聚智・平安护航 - 公共安全态势感知与指挥演示</p>
</header>
<main class="dashboard">
<!-- 核心指标区 -->
<section class="kpi-section">
<div class="kpi-card">
<h3>今日事件总数</h3>
<span class="kpi-value" id="total-events">0</span>
</div>
<div class="kpi-card">
<h3>高风险事件</h3>
<span class="kpi-value critical" id="critical-events">0</span>
</div>
<div class="kpi-card">
<h3>资源可用率</h3>
<span class="kpi-value" id="resource-rate">0%</span>
</div>
</section>
<!-- 左侧:实时事件流与区域分析 -->
<div class="left-column">
<section class="card">
<h2>实时事件流</h2>
<div class="event-list" id="event-list">
<!-- JS动态填充 -->
</div>
</section>
<section class="card chart-container">
<h2>区域事件分布</h2>
<div id="regional-chart" style="width: 100%; height: 300px;"></div>
</section>
</div>
<!-- 右侧:趋势分析与资源状态 -->
<div class="right-column">
<section class="card chart-container">
<h2>24小时事件趋势</h2>
<div id="trend-chart" style="width: 100%; height: 300px;"></div>
</section>
<section class="card chart-container">
<h2>应急资源状态</h2>
<div id="resource-chart" style="width: 100%; height: 300px;"></div>
</section>
</div>
</main>
<!-- 引入数据文件和脚本 -->
<script src="data.js"></script>
<script src="script.js"></script>
</body>
</html>
3. 页面样式 (styles.css)
/* styles.css */
:root {
--bg-color: #0a192f;
--card-bg: #112240;
--text-color: #ccd6f6;
--primary-color: #64ffda;
--border-color: #233554;
--critical-color: #ff6b6b;
--warning-color: #feca57;
--success-color: #48dbfb;
}
body {
font-family: 'Helvetica Neue', Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
text-align: center;
padding: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
header h1 {
margin: 0;
font-size: 2.5rem;
color: var(--primary-color);
}
header p {
margin: 0.5rem 0 0;
color: #8892b0;
}
.dashboard {
display: flex;
flex: 1;
padding: 1.5rem;
gap: 1.5rem;
}
.left-column, .right-column {
display: flex;
flex-direction: column;
flex: 1;
gap: 1.5rem;
}
.kpi-section {
display: flex;
justify-content: space-around;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.kpi-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
text-align: center;
flex: 1;
}
.kpi-card h3 {
margin: 0 0 0.5rem;
font-size: 1rem;
color: #8892b0;
}
.kpi-value {
font-size: 2.5rem;
font-weight: bold;
color: var(--primary-color);
}
.kpi-value.critical {
color: var(--critical-color);
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
}
.card h2 {
margin-top: 0;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.5rem;
font-size: 1.2rem;
}
.event-list {
max-height: 250px;
overflow-y: auto;
}
.event-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.2s;
}
.event-item:hover {
background-color: rgba(100, 255, 218, 0.05);
}
.event-info .event-type {
font-weight: bold;
}
.event-info .event-location {
font-size: 0.8rem;
color: #8892b0;
}
.event-level {
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: bold;
}
.level-high { background-color: var(--critical-color); color: white; }
.level-medium { background-color: var(--warning-color); color: #333; }
.level-low { background-color: var(--success-color); color: #333; }
4. 核心逻辑 (script.js)
这是将数据“点石成金”的关键部分。
// script.js - 数智化大脑:数据汇聚、分析与可视化
document.addEventListener('DOMContentLoaded', function() {
// --- 1. 数据汇聚与处理 ---
function processData() {
// 计算KPI
const totalEvents = eventStreamData.length;
const criticalEvents = eventStreamData.filter(e => e.level === '高').length;
let totalResources = 0;
let availableResources = 0;
resourceStatusData.forEach(r => {
totalResources += r.total;
availableResources += r.available;
});
const resourceRate = totalResources > 0 ? (availableResources / totalResources * 100).toFixed(1) : 0;
// 更新KPI卡片
document.getElementById('total-events').innerText = totalEvents;
document.getElementById('critical-events').innerText = criticalEvents;
document.getElementById('resource-rate').innerText = `${resourceRate}%`;
}
// --- 2. 实时事件流渲染 ---
function renderEventStream() {
const eventList = document.getElementById('event-list');
eventList.innerHTML = ''; // 清空
eventStreamData.forEach(event => {
const item = document.createElement('div');
item.className = 'event-item';
const levelClass = `level-${event.level === '高' ? 'high' : event.level === '中' ? 'medium' : 'low'}`;
item.innerHTML = `
<div class="event-info">
<div class="event-type">${event.type}</div>
<div class="event-location">${event.location}</div>
</div>
<div class="event-level ${levelClass}">${event.level}</div>
`;
eventList.appendChild(item);
});
}
// --- 3. ECharts图表初始化与渲染 ---
// 3.1 区域事件分布图 (饼图)
function initRegionalChart() {
const chartDom = document.getElementById('regional-chart');
const myChart = echarts.init(chartDom);
const option = {
tooltip: { trigger: 'item' },
legend: { orient: 'vertical', left: 'left', textStyle: { color: '#ccd6f6' } },
series: [{
name: '事件数量',
type: 'pie',
radius: '50%',
data: regionalAnalysisData.map(r => ({ value: r.value, name: r.name })),
emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } },
label: { color: '#ccd6f6' }
}],
backgroundColor: 'transparent'
};
myChart.setOption(option);
}
// 3.2 24小时趋势图 (折线图)
function initTrendChart() {
const chartDom = document.getElementById('trend-chart');
const myChart = echarts.init(chartDom);
const option = {
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: hourlyTrendData.map(item => item[0]), axisLabel: { color: '#8892b0' } },
yAxis: { type: 'value', axisLabel: { color: '#8892b0' } },
series: [{
data: hourlyTrendData.map(item => item[1]),
type: 'line',
smooth: true,
areaStyle: { opacity: 0.3 },
lineStyle: { color: '#64ffda' },
itemStyle: { color: '#64ffda' }
}],
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
backgroundColor: 'transparent'
};
myChart.setOption(option);
}
// 3.3 资源状态图 (柱状图)
function initResourceChart() {
const chartDom = document.getElementById('resource-chart');
const myChart = echarts.init(chartDom);
const option = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
xAxis: { type: 'category', data: resourceStatusData.map(r => r.name), axisLabel: { color: '#8892b0' } },
yAxis: { type: 'value', axisLabel: { color: '#8892b0' } },
series: [
{
name: '可用',
type: 'bar',
stack: 'total',
data: resourceStatusData.map(r => r.available),
itemStyle: { color: '#48dbfb' }
},
{
name: '总数',
type: 'bar',
stack: 'total',
data: resourceStatusData.map(r => r.total - r.available),
itemStyle: { color: '#233554' }
}
],
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
backgroundColor: 'transparent'
};
myChart.setOption(option);
}
// --- 主函数:启动所有可视化 ---
function initDashboard() {
processData();
renderEventStream();
initRegionalChart();
initTrendChart();
initResourceChart();
}
initDashboard();
// 响应式:窗口大小改变时,重绘图表
window.addEventListener('resize', function() {
echarts.getInstanceByDom(document.getElementById('regional-chart'))?.resize();
echarts.getInstanceByDom(document.getElementById('trend-chart'))?.resize();
echarts.getInstanceByDom(document.getElementById('resource-chart'))?.resize();
});
});
如何运行与学习
- 运行: 将所有4个文件放在同一个文件夹中,然后用浏览器(推荐Chrome)打开
index.html文件即可。 - 学习与扩展:
- 理解数据流: 追踪
data.js中的数据是如何在script.js中被处理,并最终通过innerHTML和ECharts显示在index.html上的。 - 修改数据: 尝试在
data.js中增加或修改数据,刷新页面查看变化,理解数据驱动视图的核心思想。 - 自定义图表: 访问 ECharts官方文档,尝试修改图表的配置(如颜色、样式、图表类型),打造你自己的可视化效果。
- 增加交互: 尝试为“实时事件流”中的每个事件添加点击事件,点击后在地图上标记位置(这需要引入地图API,是很好的进阶练习)。
- 模拟实时更新: 使用
setInterval定时向eventStreamData中添加新事件,并调用renderEventStream()重新渲染,模拟真实的数据流。 这个项目不仅是一份代码,更是一个生动的教具,它完美诠释了“微尘聚智”的核心理念:通过技术手段,将分散的数据转化为洞察力,最终服务于“平安护航”的伟大目标。
- 理解数据流: 追踪