2023年微尘公共线上班

32 阅读7分钟

这个项目将通过代码展示“数智化”如何将海量的、看似无关的“微尘”数据汇聚成有价值的“智慧”,从而实现“平安护航”的目标。

项目名称:数智之眼 - 公共安全数据可视化平台

教育意义:

  1. 数据可视化: 直观展示数据如何从杂乱变为有序,从孤立变为关联。
  2. 交互式探索: 让用户通过交互(点击、筛选)主动发现数据背后的故事和模式。
  3. 模拟数智化过程: 模拟从原始数据流到聚合分析,再到智能预警的全过程。
  4. 技术实践: 综合运用HTML、CSS、JavaScript和图表库(ECharts),是一个完整的前端项目案例。

第一步:项目准备

  1. 创建一个项目文件夹,如 public-security-dashboard
  2. 在文件夹中创建以下文件:
    • index.html (页面结构)
    • styles.css (页面样式)
    • script.js (核心逻辑)
    • data.js (模拟数据)
  3. 我们将使用 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();
    });
});

如何运行与学习

  1. 运行: 将所有4个文件放在同一个文件夹中,然后用浏览器(推荐Chrome)打开 index.html 文件即可。
  2. 学习与扩展:
    • 理解数据流: 追踪 data.js 中的数据是如何在 script.js 中被处理,并最终通过 innerHTMLECharts 显示在 index.html 上的。
    • 修改数据: 尝试在 data.js 中增加或修改数据,刷新页面查看变化,理解数据驱动视图的核心思想。
    • 自定义图表: 访问 ECharts官方文档,尝试修改图表的配置(如颜色、样式、图表类型),打造你自己的可视化效果。
    • 增加交互: 尝试为“实时事件流”中的每个事件添加点击事件,点击后在地图上标记位置(这需要引入地图API,是很好的进阶练习)。
    • 模拟实时更新: 使用 setInterval 定时向 eventStreamData 中添加新事件,并调用 renderEventStream() 重新渲染,模拟真实的数据流。 这个项目不仅是一份代码,更是一个生动的教具,它完美诠释了“微尘聚智”的核心理念:通过技术手段,将分散的数据转化为洞察力,最终服务于“平安护航”的伟大目标。