零碳园区草稿edit icon

作者:
Fadinghaze
Fork(复制)
下载
嵌入
BUG反馈
index.html
style.css
index.js
现在支持上传本地图片了!
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>零碳园区GIS地图展示原型 (V4.1 - 已修复)</title>

    <!-- 字体与图标库 -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">

    <!-- Mapbox GL JS 库 -->
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js"></script>

    <style>
        /* 1. 整体设计规范 */
        :root {
            --bg-color: #0A1931;
            --primary-color: #27E1C1;
            --secondary-color: #00FFAB;
            --accent-color: #409EFF;
            --text-color: #E0E6F1;
            --text-muted-color: #A9C1D9;
            --warn-color: #FFC107;
            --danger-color: #F44336;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Noto Sans SC', sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            overflow: hidden;
        }

        /* 2. 界面总体布局 */
        .main-container {
            position: relative;
            width: 1920px;
            height: 1080px;
            overflow: hidden;
        }

        #map {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
        
        /* 3.1 中央地图视区元素 */
        .info-label {
            position: absolute;
            width: 160px;
            height: 70px;
            background-color: rgba(10, 25, 49, 0.8);
            border-left: 3px solid var(--primary-color);
            padding: 8px 12px;
            cursor: pointer;
            backdrop-filter: blur(5px);
            transition: all 0.3s ease;
        }
        .info-label:hover {
            transform: scale(1.05);
            background-color: rgba(10, 25, 49, 1);
        }
        .info-label .title {
            font-size: 14px;
            font-weight: 500;
            color: var(--text-color);
        }
        .info-label .data {
            font-size: 22px;
            font-weight: 700;
            color: var(--secondary-color);
        }
        .info-label .unit {
            font-size: 12px;
            color: var(--text-muted-color);
            margin-left: 4px;
        }
        .custom-marker {
            width: 20px;
            height: 20px;
            background-color: var(--primary-color);
            border-radius: 50%;
            border: 2px solid white;
            box-shadow: 0 0 10px var(--primary-color);
            cursor: pointer;
        }

        /* 3.2 & 3.3 左右侧图标入口区 */
        .icon-bar {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            display: flex;
            flex-direction: column;
            gap: 20px;
            z-index: 10;
        }
        .left-bar { left: 20px; }
        .right-bar { right: 20px; }

        .icon-btn {
            position: relative;
            width: 56px;
            height: 56px;
            background-color: rgba(10, 30, 60, 0.7);
            border: 1px solid var(--accent-color);
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 24px;
            color: var(--text-muted-color);
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .icon-btn:hover {
            background-color: var(--accent-color);
            color: white;
            transform: scale(1.1);
        }
        
        .layer-toggle {
            position: absolute;
            top: 2px;
            right: 2px;
            width: 18px;
            height: 18px;
            background-color: rgba(0,0,0,0.5);
            color: var(--text-muted-color);
            border-radius: 50%;
            font-size: 10px;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .layer-toggle.active {
            color: var(--secondary-color);
        }

        /* 3.4 弹出式卡片 */
        .side-card {
            position: absolute;
            top: 60px; /* 预留顶部空间 */
            width: 450px;
            height: 960px; /* 1080 - 60*2 */
            background: linear-gradient(to bottom, rgba(10, 25, 49, 0.95), rgba(10, 25, 49, 0.85));
            backdrop-filter: blur(10px);
            border: 1px solid var(--accent-color);
            border-radius: 8px;
            box-shadow: 0 0 20px rgba(0,0,0,0.5);
            z-index: 20;
            display: flex;
            flex-direction: column;
            transition: transform 0.5s ease-in-out, opacity 0.5s ease-in-out;
            opacity: 0;
            pointer-events: none;
        }
        
        .side-card.left {
            left: 86px; /* 20px margin + 64px icon bar */
            transform: translateX(-110%);
        }
        .side-card.right {
            right: 86px;
            transform: translateX(110%);
        }
        .side-card.show {
            transform: translateX(0);
            opacity: 1;
            pointer-events: auto;
        }

        .card-header {
            padding: 15px 20px;
            border-bottom: 1px solid rgba(64, 158, 255, 0.3);
            display: flex;
            align-items: center;
            gap: 15px;
            flex-shrink: 0;
        }
        .card-header h3 {
            font-size: 20px;
            color: white;
            flex-grow: 1;
        }
        .card-close-btn {
            font-size: 20px;
            color: var(--text-muted-color);
            cursor: pointer;
        }
        .card-close-btn:hover {
            color: white;
        }

        .card-body {
            padding: 20px;
            flex-grow: 1;
            overflow-y: auto;
        }
         /* 自定义滚动条 */
        .card-body::-webkit-scrollbar { width: 6px; }
        .card-body::-webkit-scrollbar-track { background: transparent; }
        .card-body::-webkit-scrollbar-thumb { background: var(--accent-color); border-radius: 3px; }

        /* 列表卡片内容 */
        .search-stats {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        .search-stats input {
            background-color: rgba(0,0,0,0.3);
            border: 1px solid var(--accent-color);
            border-radius: 4px;
            color: white;
            padding: 8px 12px;
            width: 60%;
        }
        .search-stats .stats {
            font-size: 14px;
            color: var(--text-muted-color);
        }

        .item-list .list-item {
            background-color: rgba(64, 158, 255, 0.1);
            border-radius: 4px;
            padding: 15px;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 15px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .item-list .list-item:hover {
            background-color: rgba(64, 158, 255, 0.2);
            border-left: 3px solid var(--primary-color);
        }
        .list-item .status-dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background-color: var(--secondary-color);
        }
        .list-item .item-info {
            flex-grow: 1;
        }
        .item-info .item-title { font-size: 16px; font-weight: 500; color: white; }
        .item-info .item-subtitle { font-size: 12px; color: var(--text-muted-color); }
        .list-item .item-data { font-size: 18px; font-weight: 700; color: var(--accent-color); }

        /* 详情卡片内容 */
        .detail-tabs {
            display: flex;
            gap: 1px;
            background-color: rgba(64, 158, 255, 0.3);
            margin-bottom: 20px;
        }
        .detail-tabs .tab {
            flex-grow: 1;
            padding: 12px;
            text-align: center;
            background-color: rgba(10, 25, 49, 0.8);
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .detail-tabs .tab.active, .detail-tabs .tab:hover {
            background-color: var(--accent-color);
            color: white;
        }

        .tab-content { display: none; }
        .tab-content.active { display: block; }
        
        .key-metrics {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-bottom: 20px;
        }
        .metric-card {
            background-color: rgba(64, 158, 255, 0.1);
            padding: 20px;
            border-radius: 4px;
            text-align: center;
        }
        .metric-card .label { font-size: 14px; color: var(--text-muted-color); margin-bottom: 10px; }
        .metric-card .value { font-size: 32px; font-weight: 700; color: var(--primary-color); }
        .metric-card .unit { font-size: 14px; margin-left: 5px; }

        .param-list .param-item {
            display: flex;
            justify-content: space-between;
            padding: 12px 0;
            border-bottom: 1px solid rgba(64, 158, 255, 0.2);
        }
        .param-list .param-label { color: var(--text-muted-color); }
        .param-list .param-value { color: white; font-weight: 500; }
        
    </style>
</head>
<body>

    <div class="main-container">
        <!-- 3.1 中央地图视区 -->
        <div id="map"></div>
        
        <!-- 3.1.2 常驻信息标签 (示例) -->
        <div id="info-label-1" class="info-label">
            <div class="title">华虹半导体</div>
            <div class="data">1,234 <span class="unit">kW</span></div>
        </div>
        <div id="info-label-2" class="info-label">
            <div class="title">园区1号储能站</div>
            <div class="data">-5.2 <span class="unit">MW</span></div>
        </div>

        <!-- 3.2 左侧功能入口区 -->
        <div class="icon-bar left-bar">
            <div class="icon-btn" title="碳排分析"><i class="fa-solid fa-smog"></i></div>
            <div class="icon-btn" title="能耗分析"><i class="fa-solid fa-chart-line"></i></div>
            <div class="icon-btn" title="指标评价"><i class="fa-solid fa-bullseye"></i></div>
            <div class="icon-btn" title="告警中心"><i class="fa-solid fa-bell"></i></div>
        </div>

        <!-- 3.3 右侧图层控制区 -->
        <div class="icon-bar right-bar">
            <div class="icon-btn" id="btn-enterprise-list" title="企业列表">
                <i class="fa-solid fa-building"></i>
                <div class="layer-toggle active" id="toggle-enterprise-layer"><i class="fa-solid fa-eye"></i></div>
            </div>
            <div class="icon-btn" id="btn-pv-list" title="光伏列表">
                <i class="fa-solid fa-solar-panel"></i>
                <div class="layer-toggle active" id="toggle-pv-layer"><i class="fa-solid fa-eye"></i></div>
            </div>
            <div class="icon-btn" id="btn-storage-list" title="储能列表">
                <i class="fa-solid fa-car-battery"></i>
                <div class="layer-toggle active" id="toggle-storage-layer"><i class="fa-solid fa-eye"></i></div>
            </div>
        </div>

        <!-- 3.2.2 详细信息卡片 (左侧) -->
        <div id="detail-card" class="side-card left">
            <div class="card-header">
                <i class="fa-solid fa-car-battery fa-lg"></i>
                <h3 id="detail-title">园区1号储能站</h3>
                <i class="fa-solid fa-xmark card-close-btn" onclick="closeCard('detail-card')"></i>
            </div>
            <div class="card-body">
                <div class="key-metrics">
                    <div class="metric-card">
                        <div class="label">荷电状态(SOC)</div>
                        <div class="value">88.5<span class="unit">%</span></div>
                    </div>
                    <div class="metric-card">
                        <div class="label">实时功率</div>
                        <div class="value">-5.2<span class="unit">MW</span></div>
                    </div>
                </div>
                <div class="detail-tabs">
                    <div class="tab active">实时状态</div>
                    <div class="tab">基础档案</div>
                    <div class="tab">历史数据</div>
                </div>
                <div class="tab-content active">
                    <div class="param-list">
                        <div class="param-item"><span class="param-label">健康状态(SOH)</span><span class="param-value">98.2 %</span></div>
                        <div class="param-item"><span class="param-label">电池簇平均温度</span><span class="param-value">28.5 ℃</span></div>
                        <div class="param-item"><span class="param-label">总电压 / 总电流</span><span class="param-value">750 V / -693 A</span></div>
                        <div class="param-item"><span class="param-label">今日累计充电量</span><span class="param-value">12.5 MWh</span></div>
                        <div class="param-item"><span class="param-label">今日累计放电量</span><span class="param-value">8.2 MWh</span></div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 3.3.2 分类设施列表卡片 (右侧) -->
        <div id="list-card" class="side-card right">
            <div class="card-header">
                <i class="fa-solid fa-car-battery fa-lg"></i>
                <h3 id="list-title">储能设施列表</h3>
                <i class="fa-solid fa-xmark card-close-btn" onclick="closeCard('list-card')"></i>
            </div>
            <div class="card-body">
                <div class="search-stats">
                    <input type="text" placeholder="请输入设施名称">
                    <div class="stats">共 5 座,总容量 30MWh</div>
                </div>
                <div class="item-list">
                    <div class="list-item" onclick="openDetailCard()">
                        <div class="status-dot"></div>
                        <div class="item-info">
                            <div class="item-title">园区1号储能站</div>
                            <div class="item-subtitle">状态: 充电中 | SOC: 88.5%</div>
                        </div>
                        <div class="item-data">-5.2 MW</div>
                    </div>
                     <div class="list-item">
                        <div class="status-dot" style="background-color: var(--primary-color);"></div>
                        <div class="item-info">
                            <div class="item-title">华虹半导体用户侧储能</div>
                            <div class="item-subtitle">状态: 放电中 | SOC: 65.1%</div>
                        </div>
                        <div class="item-data">+2.1 MW</div>
                    </div>
                    <!-- 更多列表项... -->
                </div>
            </div>
        </div>
    </div>


    <script>
        // --- 初始化地图 ---
        
        // ===================================================================
        // VITAL: 请将 'YOUR_MAPBOX_ACCESS_TOKEN' 替换为您自己的Mapbox访问令牌
        // ===================================================================
        mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; 

        const map = new mapboxgl.Map({
            container: 'map',
            style: 'mapbox://styles/mapbox/dark-v11', // 深色科技风格
            center: [120.73, 31.27], // 模拟苏州工业园区位置
            zoom: 13,
            pitch: 45,
            bearing: -17.6,
        });

        map.on('load', () => {
            // 在地图上添加模拟的点位和常驻标签
            const marker1 = new mapboxgl.Marker({ element: document.createElement('div'), className: 'custom-marker' })
                .setLngLat([120.74, 31.28])
                .addTo(map);

            const label1 = document.getElementById('info-label-1');
            const markerLabel1 = new mapboxgl.Marker({ element: label1, anchor: 'bottom-left', offset: [15, 0] })
                .setLngLat([120.74, 31.28])
                .addTo(map);
                
            const label2 = document.getElementById('info-label-2');
            const markerLabel2 = new mapboxgl.Marker({ element: label2, anchor: 'bottom-left', offset: [15, 0] })
                .setLngLat([120.72, 31.26])
                .addTo(map);
            
            // 点击地图点位打开详情卡片
            marker1.getElement().addEventListener('click', () => {
                openDetailCard('华虹半导体详情');
            });
        });

        // --- 交互逻辑 ---
        
        // 打开/关闭卡片
        function openCard(cardId) {
            // 关闭所有可能打开的卡片,确保每次只显示一个
            document.querySelectorAll('.side-card').forEach(card => card.classList.remove('show'));
            // 打开指定的卡片
            document.getElementById(cardId).classList.add('show');
        }
        function closeCard(cardId) {
            document.getElementById(cardId).classList.remove('show');
        }
        function openDetailCard(title = '园区1号储能站') {
            document.getElementById('detail-title').innerText = title;
            openCard('detail-card');
        }

        // 右侧图层控制图标交互
        document.getElementById('btn-storage-list').addEventListener('click', (event) => {
            // 点击眼睛角标
            if (event.target.closest('.layer-toggle')) {
                event.stopPropagation(); // 阻止事件冒泡到父元素
                const toggle = event.target.closest('.layer-toggle');
                toggle.classList.toggle('active');
                const icon = toggle.querySelector('i');
                if (toggle.classList.contains('active')) {
                    icon.className = 'fa-solid fa-eye';
                    console.log('储能图层已显示');
                } else {
                    icon.className = 'fa-solid fa-eye-slash';
                    console.log('储能图层已隐藏');
                }
            } else { // 点击图标本身
                document.getElementById('list-title').innerText = "储能设施列表";
                openCard('list-card');
            }
        });

        // 模拟其他右侧按钮
        document.getElementById('btn-enterprise-list').addEventListener('click', (e) => {
             if (e.target.closest('.layer-toggle')) { e.stopPropagation(); return; }
             document.getElementById('list-title').innerText = "企业列表";
             openCard('list-card');
        });
        document.getElementById('btn-pv-list').addEventListener('click', (e) => {
            if (e.target.closest('.layer-toggle')) { e.stopPropagation(); return; }
            document.getElementById('list-title').innerText = "光伏设施列表";
            openCard('list-card');
        });

    </script>

</body>
</html>
        
编辑器加载中
预览
控制台