LAE-log/index.html
2026-05-16 11:50:33 +08:00

1291 lines
46 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #1a73e8;
--primary-dark: #1557b0;
--success: #34a853;
--warning: #fbbc04;
--danger: #ea4335;
--info: #4285f4;
--dark: #1f2937;
--gray: #6b7280;
--light: #f3f4f6;
--white: #ffffff;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
body {
font-family: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
min-height: 100vh;
color: var(--white);
}
.app-container {
display: flex;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
width: 260px;
background: rgba(15, 23, 42, 0.95);
border-right: 1px solid rgba(255, 255, 255, 0.1);
padding: 20px 0;
display: flex;
flex-direction: column;
}
.logo {
padding: 0 24px 30px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 20px;
}
.logo h1 {
font-size: 20px;
font-weight: 700;
background: linear-gradient(90deg, #60a5fa, #a78bfa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 10px;
}
.logo-icon {
width: 36px;
height: 36px;
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.nav-menu {
flex: 1;
padding: 0 12px;
}
.nav-item {
display: flex;
align-items: center;
padding: 14px 16px;
margin: 4px 0;
border-radius: 10px;
cursor: pointer;
transition: all 0.2s;
color: #94a3b8;
font-size: 14px;
gap: 12px;
}
.nav-item:hover {
background: rgba(255, 255, 255, 0.05);
color: var(--white);
}
.nav-item.active {
background: linear-gradient(90deg, rgba(59, 130, 246, 0.3), rgba(139, 92, 246, 0.3));
color: var(--white);
border: 1px solid rgba(139, 92, 246, 0.3);
}
.nav-icon {
width: 20px;
text-align: center;
font-size: 16px;
}
.nav-badge {
margin-left: auto;
background: var(--danger);
color: white;
font-size: 11px;
padding: 2px 8px;
border-radius: 10px;
}
/* Main Content */
.main-content {
flex: 1;
overflow-y: auto;
}
.header {
background: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(10px);
padding: 16px 30px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
.header-title {
font-size: 18px;
font-weight: 500;
}
.header-actions {
display: flex;
align-items: center;
gap: 16px;
}
.header-btn {
padding: 8px 16px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: transparent;
color: var(--white);
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.header-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
.header-btn.primary {
background: linear-gradient(135deg, #3b82f6, #6366f1);
border: none;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: linear-gradient(135deg, #10b981, #3b82f6);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
}
/* Page Content */
.page {
display: none;
padding: 24px 30px;
}
.page.active {
display: block;
}
/* Dashboard */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 24px;
}
.stat-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 20px;
transition: transform 0.2s, box-shadow 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.stat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.stat-label {
color: #94a3b8;
font-size: 13px;
}
.stat-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.stat-value {
font-size: 32px;
font-weight: 700;
margin-bottom: 8px;
}
.stat-trend {
font-size: 12px;
display: flex;
align-items: center;
gap: 4px;
}
.stat-trend.up { color: var(--success); }
.stat-trend.down { color: var(--danger); }
/* Dashboard Grid */
.dashboard-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
margin-bottom: 24px;
}
.panel {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
overflow: hidden;
}
.panel-header {
padding: 16px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-title {
font-size: 15px;
font-weight: 500;
}
.panel-body {
padding: 16px 20px;
}
/* Alert List */
.alert-item {
display: flex;
align-items: center;
padding: 12px;
border-radius: 10px;
margin-bottom: 10px;
background: rgba(255, 255, 255, 0.03);
gap: 12px;
cursor: pointer;
transition: background 0.2s;
}
.alert-item:hover {
background: rgba(255, 255, 255, 0.08);
}
.alert-icon {
width: 36px;
height: 36px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.alert-icon.warning { background: rgba(251, 188, 4, 0.2); color: var(--warning); }
.alert-icon.danger { background: rgba(234, 67, 53, 0.2); color: var(--danger); }
.alert-icon.info { background: rgba(66, 133, 244, 0.2); color: var(--info); }
.alert-content {
flex: 1;
}
.alert-title {
font-size: 13px;
margin-bottom: 2px;
}
.alert-time {
font-size: 11px;
color: #64748b;
}
/* Drone Status */
.drone-list {
max-height: 300px;
overflow-y: auto;
}
.drone-item {
display: flex;
align-items: center;
padding: 12px;
border-radius: 10px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.03);
gap: 12px;
}
.drone-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
display: flex;
align-items: center;
justify-content: center;
}
.drone-info {
flex: 1;
}
.drone-name {
font-size: 13px;
font-weight: 500;
margin-bottom: 2px;
}
.drone-location {
font-size: 11px;
color: #64748b;
}
.drone-status {
padding: 4px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: 500;
}
.drone-status.online { background: rgba(52, 168, 83, 0.2); color: var(--success); }
.drone-status.flying { background: rgba(66, 133, 244, 0.2); color: var(--info); }
.drone-status.charging { background: rgba(251, 188, 4, 0.2); color: var(--warning); }
.drone-status.offline { background: rgba(107, 114, 128, 0.2); color: var(--gray); }
/* Tasks Page */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 12px;
}
.search-box {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 0 14px;
width: 280px;
}
.search-box input {
background: transparent;
border: none;
padding: 10px;
color: var(--white);
width: 100%;
font-size: 14px;
}
.search-box input::placeholder {
color: #64748b;
}
.search-box input:focus {
outline: none;
}
.filter-btns {
display: flex;
gap: 8px;
}
.filter-btn {
padding: 8px 16px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: transparent;
color: #94a3b8;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.filter-btn:hover {
background: rgba(255, 255, 255, 0.05);
color: var(--white);
}
.filter-btn.active {
background: var(--primary);
border-color: var(--primary);
color: var(--white);
}
/* Task Table */
.task-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
}
.task-table th {
text-align: left;
padding: 14px 16px;
background: rgba(255, 255, 255, 0.03);
color: #64748b;
font-weight: 500;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.task-table td {
padding: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 13px;
}
.task-table tr:hover td {
background: rgba(255, 255, 255, 0.02);
}
.task-id {
font-family: monospace;
color: #60a5fa;
}
.task-route {
display: flex;
align-items: center;
gap: 8px;
}
.route-arrow {
color: #64748b;
}
.task-status {
display: inline-flex;
align-items: center;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.task-status.pending { background: rgba(107, 114, 128, 0.2); color: #94a3b8; }
.task-status.running { background: rgba(66, 133, 244, 0.2); color: #60a5fa; }
.task-status.completed { background: rgba(52, 168, 83, 0.2); color: var(--success); }
.task-status.cancelled { background: rgba(234, 67, 53, 0.2); color: var(--danger); }
.task-status.exception { background: rgba(251, 188, 4, 0.2); color: var(--warning); }
.task-priority {
display: inline-flex;
align-items: center;
gap: 4px;
}
.priority-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.priority-dot.high { background: var(--danger); }
.priority-dot.medium { background: var(--warning); }
.priority-dot.low { background: var(--success); }
.task-actions {
display: flex;
gap: 8px;
}
.action-btn {
padding: 6px 12px;
border-radius: 6px;
border: none;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.action-btn.view {
background: rgba(66, 133, 244, 0.2);
color: #60a5fa;
}
.action-btn.view:hover {
background: rgba(66, 133, 244, 0.3);
}
/* Map Page */
.map-container {
height: calc(100vh - 180px);
border-radius: 16px;
overflow: hidden;
position: relative;
}
#map {
height: 100%;
width: 100%;
}
.map-overlay {
position: absolute;
top: 20px;
left: 20px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 12px;
}
.map-toolbar {
background: rgba(15, 23, 42, 0.95);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.toolbar-title {
font-size: 13px;
color: #94a3b8;
margin-bottom: 4px;
}
.toolbar-btns {
display: flex;
gap: 8px;
}
.toolbar-btn {
padding: 8px 14px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: transparent;
color: #94a3b8;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.toolbar-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: var(--white);
}
.toolbar-btn.active {
background: var(--primary);
border-color: var(--primary);
color: var(--white);
}
.warehouse-legend {
background: rgba(15, 23, 42, 0.95);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 14px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
font-size: 12px;
}
.legend-item:last-child {
margin-bottom: 0;
}
.legend-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.legend-dot.center { background: #3b82f6; }
.legend-dot.station { background: #10b981; }
.legend-dot.landing { background: #f59e0b; }
/* Warehouse Info Panel */
.warehouse-panel {
position: absolute;
bottom: 20px;
right: 20px;
width: 320px;
background: rgba(15, 23, 42, 0.98);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
z-index: 1000;
display: none;
}
.warehouse-panel.show {
display: block;
}
.panel-close {
position: absolute;
top: 12px;
right: 12px;
width: 28px;
height: 28px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
border: none;
color: #94a3b8;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.warehouse-header {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.warehouse-name {
font-size: 16px;
font-weight: 600;
margin-bottom: 4px;
}
.warehouse-type {
display: inline-block;
padding: 4px 10px;
border-radius: 20px;
font-size: 11px;
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
}
.warehouse-body {
padding: 16px 20px;
}
.warehouse-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.ws-stat {
background: rgba(255, 255, 255, 0.03);
border-radius: 10px;
padding: 12px;
}
.ws-label {
font-size: 11px;
color: #64748b;
margin-bottom: 4px;
}
.ws-value {
font-size: 18px;
font-weight: 600;
}
.warehouse-info-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 13px;
}
.warehouse-info-row:last-child {
border-bottom: none;
}
.wi-label {
color: #64748b;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.page.active {
animation: fadeIn 0.3s ease-out;
}
.stat-card, .panel, .task-table {
animation: fadeIn 0.4s ease-out;
}
</style>
</head>
<body>
<div class="app-container">
<!-- Sidebar -->
<div class="sidebar">
<div class="logo">
<h1>
<span class="logo-icon">🚁</span>
无人机物流控制台
</h1>
</div>
<div class="nav-menu">
<div class="nav-item active" data-page="dashboard">
<span class="nav-icon">📊</span>
<span>物流看板</span>
</div>
<div class="nav-item" data-page="tasks">
<span class="nav-icon">📋</span>
<span>任务管理</span>
<span class="nav-badge">12</span>
</div>
<div class="nav-item" data-page="warehouse">
<span class="nav-icon">🗺️</span>
<span>仓库GIS</span>
</div>
</div>
</div>
<!-- Main Content -->
<div class="main-content">
<!-- Header -->
<div class="header">
<div class="header-title">低空无人机物流控制平台</div>
<div class="header-actions">
<button class="header-btn">🔔 通知</button>
<button class="header-btn primary">+ 新建任务</button>
<div class="user-avatar">A</div>
</div>
</div>
<!-- Dashboard Page -->
<div class="page active" id="page-dashboard">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-header">
<span class="stat-label">今日任务</span>
<div class="stat-icon" style="background: rgba(59, 130, 246, 0.2);">📦</div>
</div>
<div class="stat-value">156</div>
<div class="stat-trend up">↑ 12% 较昨日</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-label">进行中</span>
<div class="stat-icon" style="background: rgba(66, 133, 244, 0.2);">🚁</div>
</div>
<div class="stat-value">23</div>
<div class="stat-trend up">↑ 5% 较昨日</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-label">配送完成率</span>
<div class="stat-icon" style="background: rgba(52, 168, 83, 0.2);"></div>
</div>
<div class="stat-value">94.2%</div>
<div class="stat-trend up">↑ 2.1% 较昨日</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-label">在线无人机</span>
<div class="stat-icon" style="background: rgba(139, 92, 246, 0.2);"></div>
</div>
<div class="stat-value">48/56</div>
<div class="stat-trend">正常运行</div>
</div>
</div>
<div class="dashboard-grid">
<div class="panel">
<div class="panel-header">
<span class="panel-title">实时预警</span>
<span style="color: #64748b; font-size: 12px;">查看全部 →</span>
</div>
<div class="panel-body">
<div class="alert-item">
<div class="alert-icon danger">⚠️</div>
<div class="alert-content">
<div class="alert-title">设备异常: Drone-023 电池温度过高</div>
<div class="alert-time">2026-05-16 10:32:15</div>
</div>
</div>
<div class="alert-item">
<div class="alert-icon warning">🌧️</div>
<div class="alert-content">
<div class="alert-title">天气预警:朝阳区预计有雷暴天气</div>
<div class="alert-time">2026-05-16 10:15:00</div>
</div>
</div>
<div class="alert-item">
<div class="alert-icon info"></div>
<div class="alert-content">
<div class="alert-title">任务超时TASK-20260516-089 等待超2小时</div>
<div class="alert-time">2026-05-16 09:45:22</div>
</div>
</div>
<div class="alert-item">
<div class="alert-icon warning">📍</div>
<div class="alert-content">
<div class="alert-title">空域限制:亦庄区域暂时禁飞</div>
<div class="alert-time">2026-05-16 09:20:00</div>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">
<span class="panel-title">无人机状态</span>
<span style="color: #64748b; font-size: 12px;">在线 48 架</span>
</div>
<div class="panel-body">
<div class="drone-list">
<div class="drone-item">
<div class="drone-avatar">🚁</div>
<div class="drone-info">
<div class="drone-name">Drone-001</div>
<div class="drone-location">望京配送站 → 798园区</div>
</div>
<span class="drone-status flying">执飞中</span>
</div>
<div class="drone-item">
<div class="drone-avatar">🚁</div>
<div class="drone-info">
<div class="drone-name">Drone-012</div>
<div class="drone-location">亦庄中心仓</div>
</div>
<span class="drone-status charging">充电中</span>
</div>
<div class="drone-item">
<div class="drone-avatar">🚁</div>
<div class="drone-info">
<div class="drone-name">Drone-008</div>
<div class="drone-location">通州配送站</div>
</div>
<span class="drone-status online">待命</span>
</div>
<div class="drone-item">
<div class="drone-avatar">🚁</div>
<div class="drone-info">
<div class="drone-name">Drone-035</div>
<div class="drone-location">顺义起降点</div>
</div>
<span class="drone-status offline">离线</span>
</div>
</div>
</div>
</div>
</div>
<div class="panel" style="margin-top: 20px;">
<div class="panel-header">
<span class="panel-title">近期任务趋势</span>
</div>
<div class="panel-body" style="height: 200px; display: flex; align-items: center; justify-content: center; color: #64748b;">
[趋势图表区域 - 可集成 ECharts]
</div>
</div>
</div>
<!-- Tasks Page -->
<div class="page" id="page-tasks">
<div class="toolbar">
<div class="search-box">
<span>🔍</span>
<input type="text" placeholder="搜索任务ID、目的地...">
</div>
<div class="filter-btns">
<button class="filter-btn active">全部</button>
<button class="filter-btn">待执行</button>
<button class="filter-btn">进行中</button>
<button class="filter-btn">已完成</button>
<button class="filter-btn">异常</button>
</div>
</div>
<div class="panel">
<table class="task-table">
<thead>
<tr>
<th>任务ID</th>
<th>航线</th>
<th>货物信息</th>
<th>优先级</th>
<th>状态</th>
<th>执行无人机</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="task-id">TASK-0892</span></td>
<td>
<div class="task-route">
<span>望京站</span>
<span class="route-arrow"></span>
<span>798园区</span>
</div>
</td>
<td>文件 / 2.3kg</td>
<td><span class="task-priority"><span class="priority-dot high"></span></span></td>
<td><span class="task-status running">进行中</span></td>
<td>Drone-001</td>
<td>10:32</td>
<td>
<div class="task-actions">
<button class="action-btn view">详情</button>
</div>
</td>
</tr>
<tr>
<td><span class="task-id">TASK-0891</span></td>
<td>
<div class="task-route">
<span>亦庄仓</span>
<span class="route-arrow"></span>
<span>通州站</span>
</div>
</td>
<td>生鲜 / 5.8kg</td>
<td><span class="task-priority"><span class="priority-dot high"></span></span></td>
<td><span class="task-status pending">待执行</span></td>
<td>Drone-012</td>
<td>10:28</td>
<td>
<div class="task-actions">
<button class="action-btn view">详情</button>
</div>
</td>
</tr>
<tr>
<td><span class="task-id">TASK-0890</span></td>
<td>
<div class="task-route">
<span>顺义起降点</span>
<span class="route-arrow"></span>
<span>机场物流园</span>
</div>
</td>
<td>药品 / 1.2kg</td>
<td><span class="task-priority"><span class="priority-dot medium"></span></span></td>
<td><span class="task-status completed">已完成</span></td>
<td>Drone-008</td>
<td>10:15</td>
<td>
<div class="task-actions">
<button class="action-btn view">详情</button>
</div>
</td>
</tr>
<tr>
<td><span class="task-id">TASK-0889</span></td>
<td>
<div class="task-route">
<span>朝阳站</span>
<span class="route-arrow"></span>
<span>国贸商圈</span>
</div>
</td>
<td>外卖 / 3.5kg</td>
<td><span class="task-priority"><span class="priority-dot low"></span></span></td>
<td><span class="task-status exception">异常</span></td>
<td>Drone-023</td>
<td>10:05</td>
<td>
<div class="task-actions">
<button class="action-btn view">详情</button>
</div>
</td>
</tr>
<tr>
<td><span class="task-id">TASK-0888</span></td>
<td>
<div class="task-route">
<span>海淀中心</span>
<span class="route-arrow"></span>
<span>中关村</span>
</div>
</td>
<td>电子产品 / 4.2kg</td>
<td><span class="task-priority"><span class="priority-dot medium"></span></span></td>
<td><span class="task-status completed">已完成</span></td>
<td>Drone-015</td>
<td>09:48</td>
<td>
<div class="task-actions">
<button class="action-btn view">详情</button>
</div>
</td>
</tr>
<tr>
<td><span class="task-id">TASK-0887</span></td>
<td>
<div class="task-route">
<span>丰台站</span>
<span class="route-arrow"></span>
<span>西局小区</span>
</div>
</td>
<td>包裹 / 2.0kg</td>
<td><span class="task-priority"><span class="priority-dot low"></span></span></td>
<td><span class="task-status completed">已完成</span></td>
<td>Drone-022</td>
<td>09:32</td>
<td>
<div class="task-actions">
<button class="action-btn view">详情</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Warehouse GIS Page -->
<div class="page" id="page-warehouse">
<div class="map-container">
<div id="map"></div>
<div class="map-overlay">
<div class="map-toolbar">
<div class="toolbar-title">视图控制</div>
<div class="toolbar-btns">
<button class="toolbar-btn active" id="btn-all">全部</button>
<button class="toolbar-btn" id="btn-center">中心仓</button>
<button class="toolbar-btn" id="btn-station">配送站</button>
<button class="toolbar-btn" id="btn-landing">起降点</button>
</div>
</div>
<div class="warehouse-legend">
<div class="legend-item">
<span class="legend-dot center"></span>
<span>中心仓 (3)</span>
</div>
<div class="legend-item">
<span class="legend-dot station"></span>
<span>配送站 (8)</span>
</div>
<div class="legend-item">
<span class="legend-dot landing"></span>
<span>起降点 (12)</span>
</div>
</div>
</div>
<div class="warehouse-panel" id="warehouse-panel">
<button class="panel-close" onclick="closeWarehousePanel()">×</button>
<div class="warehouse-header">
<div class="warehouse-name" id="wh-name">亦庄物流中心</div>
<span class="warehouse-type" id="wh-type">中心仓</span>
</div>
<div class="warehouse-body">
<div class="warehouse-stats">
<div class="ws-stat">
<div class="ws-label">在仓包裹</div>
<div class="ws-value">1,284</div>
</div>
<div class="ws-stat">
<div class="ws-label">今日出库</div>
<div class="ws-value">856</div>
</div>
<div class="ws-stat">
<div class="ws-label">无人机</div>
<div class="ws-value">12</div>
</div>
<div class="ws-stat">
<div class="ws-label">员工</div>
<div class="ws-value">28</div>
</div>
</div>
<div class="warehouse-info-row">
<span class="wi-label">地址</span>
<span>北京经济技术开发区荣华路15号</span>
</div>
<div class="warehouse-info-row">
<span class="wi-label">负责人</span>
<span>张建国</span>
</div>
<div class="warehouse-info-row">
<span class="wi-label">联系方式</span>
<span>138****8888</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// Navigation
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', () => {
const page = item.dataset.page;
document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.getElementById('page-' + page).classList.add('active');
if (page === 'warehouse') {
setTimeout(initMap, 100);
}
});
});
// Map
let map;
const warehouseData = [
{ id: 1, name: '亦庄物流中心', type: 'center', lat: 39.795, lng: 116.528, address: '北京经济技术开发区荣华路15号', packages: 1284, drones: 12, staff: 28, leader: '张建国', phone: '138****8888' },
{ id: 2, name: '望京配送站', type: 'station', lat: 39.985, lng: 116.470, address: '朝阳区望京SOHO', packages: 456, drones: 6, staff: 12, leader: '李明', phone: '139****6666' },
{ id: 3, name: '通州配送站', type: 'station', lat: 39.907, lng: 116.656, address: '通州区新华大街', packages: 328, drones: 5, staff: 10, leader: '王芳', phone: '137****5555' },
{ id: 4, name: '顺义起降点', type: 'landing', lat: 40.130, lng: 116.635, address: '顺义区南法信镇', packages: 120, drones: 3, staff: 5, leader: '赵强', phone: '136****4444' },
{ id: 5, name: '朝阳站', type: 'station', lat: 39.928, lng: 116.457, address: '朝阳区建国路', packages: 512, drones: 7, staff: 15, leader: '刘洋', phone: '135****3333' },
{ id: 6, name: '海淀中心', type: 'center', lat: 39.983, lng: 116.312, address: '海淀区中关村', packages: 980, drones: 10, staff: 22, leader: '陈静', phone: '134****2222' },
{ id: 7, name: '丰台站', type: 'station', lat: 39.858, lng: 116.286, address: '丰台区西局', packages: 284, drones: 4, staff: 8, leader: '周磊', phone: '133****1111' },
{ id: 8, name: '亦庄起降点', type: 'landing', lat: 39.812, lng: 116.545, address: '亦庄经济区', packages: 86, drones: 2, staff: 4, leader: '吴涛', phone: '132****0000' },
];
const typeColors = {
center: '#3b82f6',
station: '#10b981',
landing: '#f59e0b'
};
const typeIcons = {
center: '🏭',
station: '📦',
landing: '🛫'
};
function initMap() {
if (map) {
map.remove();
}
map = L.map('map').setView([39.904, 116.407], 11);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
attribution: '© OpenStreetMap, © CARTO',
subdomains: 'abcd',
maxZoom: 19
}).addTo(map);
warehouseData.forEach(wh => {
const marker = L.circleMarker([wh.lat, wh.lng], {
radius: 12,
fillColor: typeColors[wh.type],
color: '#fff',
weight: 2,
opacity: 1,
fillOpacity: 0.9
}).addTo(map);
marker.bindTooltip(`${typeIcons[wh.type]} ${wh.name}`, {
permanent: false,
direction: 'top',
className: 'custom-tooltip'
});
marker.on('click', () => showWarehousePanel(wh));
});
const routes = [
[[39.795, 116.528], [39.985, 116.470]],
[[39.795, 116.528], [39.907, 116.656]],
[[39.983, 116.312], [39.928, 116.457]],
];
routes.forEach(route => {
L.polyline(route, {
color: 'rgba(139, 92, 246, 0.6)',
weight: 2,
dashArray: '5, 10'
}).addTo(map);
});
}
function showWarehousePanel(wh) {
document.getElementById('wh-name').textContent = wh.name;
document.getElementById('wh-type').textContent = wh.type === 'center' ? '中心仓' : wh.type === 'station' ? '配送站' : '起降点';
document.querySelector('.ws-stat:nth-child(1) .ws-value').textContent = wh.packages.toLocaleString();
document.querySelector('.ws-stat:nth-child(2) .ws-value').textContent = Math.floor(wh.packages * 0.67).toLocaleString();
document.querySelector('.ws-stat:nth-child(3) .ws-value').textContent = wh.drones;
document.querySelector('.ws-stat:nth-child(4) .ws-value').textContent = wh.staff;
document.querySelector('.warehouse-info-row:nth-child(1) span:last-child').textContent = wh.address;
document.querySelector('.warehouse-info-row:nth-child(2) span:last-child').textContent = wh.leader;
document.querySelector('.warehouse-info-row:nth-child(3) span:last-child').textContent = wh.phone;
document.getElementById('warehouse-panel').classList.add('show');
}
function closeWarehousePanel() {
document.getElementById('warehouse-panel').classList.remove('show');
}
// Filter buttons
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
});
});
document.querySelectorAll('.toolbar-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.toolbar-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
});
});
</script>
</body>
</html>