From d19811752fda1537ae9663235a8d858bb9706945 Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 10:50:26 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/css/style.css | 1612 ++++++++++++++++++ common/js/api.js | 200 +++ common/js/battlefield.js | 732 +++++++++ common/js/common.js | 169 ++ common/js/drones.js | 341 ++++ common/js/identification.js | 500 ++++++ common/js/signal.js | 261 +++ idx.html | 3102 +++++++++++++++++++++++++++++++++++ serve.py | 33 + 9 files changed, 6950 insertions(+) create mode 100644 common/css/style.css create mode 100644 common/js/api.js create mode 100644 common/js/battlefield.js create mode 100644 common/js/common.js create mode 100644 common/js/drones.js create mode 100644 common/js/identification.js create mode 100644 common/js/signal.js create mode 100644 idx.html create mode 100644 serve.py diff --git a/common/css/style.css b/common/css/style.css new file mode 100644 index 0000000..02f3d91 --- /dev/null +++ b/common/css/style.css @@ -0,0 +1,1612 @@ +/* 全局样式 */ +:root { + --primary-color: #1a2634; + --secondary-color: #2c3e50; + --accent-color: #00b4d8; + --text-color: #e9ecef; + --border-color: #2d4356; + --card-bg: rgba(26, 38, 52, 0.8); + --success-color: #2ecc71; + --warning-color: #f1c40f; + --danger-color: #e74c3c; + --info-color: #3498db; + --executing-color: #3498db; + --identification-color: #2ecc71; + --friendly-color: #27ae60; + --hostile-color: #e74c3c; +} + +body { + font-family: 'Noto Sans SC', sans-serif; + background-color: var(--primary-color); + color: var(--text-color); + min-height: 100vh; +} + +.battlefield-theme { + background: + linear-gradient(135deg, rgba(26, 38, 52, 0.95) 0%, rgba(44, 62, 80, 0.95) 100%), + url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%230a4b78' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"), + url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%230a4b78' fill-opacity='0.05'%3E%3Cpath d='M50 50l30-30M50 50l-30 30M50 50l30 30M50 50l-30-30'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-attachment: fixed; +} + +/* 动画效果 */ +@keyframes pulse { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes glow { + 0% { + box-shadow: 0 0 5px rgba(0, 180, 216, 0.2); + } + 50% { + box-shadow: 0 0 20px rgba(0, 180, 216, 0.4); + } + 100% { + box-shadow: 0 0 5px rgba(0, 180, 216, 0.2); + } +} + +/* 侧边栏样式 */ +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 250px; + background-color: var(--secondary-color); + border-right: 1px solid var(--border-color); + padding: 1rem; + z-index: 1000; +} + +.sidebar-header { + padding: 1rem 0; + border-bottom: 1px solid var(--border-color); +} + +.sidebar-header h4 { + color: var(--text-color); + margin: 0; + display: flex; + align-items: center; +} + +.logo-icon { + margin-right: 0.5rem; + color: var(--accent-color); +} + +.nav-link { + color: var(--text-color); + padding: 0.8rem 1rem; + margin: 0.2rem 0; + border-radius: 5px; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + background-color: var(--accent-color); + color: white; +} + +.nav-link i { + margin-right: 0.5rem; + width: 20px; + text-align: center; +} + +/* 主内容区域 */ +.main-content { + margin-left: 250px; + padding: 1rem; + min-height: 100vh; + max-height: 100vh; + overflow: hidden; +} + +/* 战场头部 */ +.battlefield-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + padding: 0.5rem 1rem; + background: var(--card-bg); + border-radius: 10px; + border: 1px solid var(--border-color); +} + +.header-left { + display: flex; + align-items: center; + gap: 1.5rem; +} + +.battlefield-time { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--accent-color); + font-size: 0.9rem; +} + +.operator-info { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--text-color); +} + +.status-badge { + padding: 0.25rem 0.5rem; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 500; +} + +.status-badge.online { + background: rgba(46, 204, 113, 0.2); + color: var(--success-color); +} + +.page-title { + color: var(--text-color); + margin: 0; + font-weight: 500; +} + +/* 状态卡片 */ +.status-cards { + margin-bottom: 1rem; +} + +.status-cards .col-md-3 { + padding: 0.4rem; + animation: fadeIn 0.5s ease-out forwards; +} + +.status-cards .col-md-3:nth-child(1) { animation-delay: 0s; } +.status-cards .col-md-3:nth-child(2) { animation-delay: 0.2s; } +.status-cards .col-md-3:nth-child(3) { animation-delay: 0.4s; } +.status-cards .col-md-3:nth-child(4) { animation-delay: 0.6s; } + +.status-card { + background: var(--card-bg); + border-radius: 8px; + padding: 12px; + margin-bottom: 0.8rem; + position: relative; + overflow: hidden; + min-height: 90px; + border: 1px solid rgba(255, 255, 255, 0.1); + transition: all 0.3s ease; +} + +.status-card.executing { + background: linear-gradient(135deg, var(--executing-color) 0%, rgba(52, 152, 219, 0.2) 100%); +} + +.status-card.identification { + background: linear-gradient(135deg, var(--identification-color) 0%, rgba(46, 204, 113, 0.2) 100%); +} + +.status-card.friendly { + background: linear-gradient(135deg, var(--friendly-color) 0%, rgba(39, 174, 96, 0.2) 100%); +} + +.status-card.hostile { + background: linear-gradient(135deg, var(--hostile-color) 0%, rgba(231, 76, 60, 0.2) 100%); +} + +.status-info { + position: relative; + z-index: 1; +} + +.status-info h3 { + font-size: 0.9rem; + margin: 0 0 8px 0; + color: rgba(255, 255, 255, 0.9); + font-weight: 500; +} + +.status-value { + font-size: 1.5rem; + font-weight: 600; + color: white; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.status-chart { + width: 60px; + height: 40px; + margin-left: 15px; + opacity: 0.5; +} + +/* 主雷达卡片 */ +.tactical-radar { + width: 100%; + height: 500px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + position: relative; + overflow: hidden; +} + +.main-radar-card { + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 10px; + margin-bottom: 1rem; +} + +.main-radar-card .card-body { + padding: 1rem; + height: 100%; +} + +.radar-controls { + display: flex; + gap: 0.5rem; +} + +.radar-controls .btn { + width: 32px; + height: 32px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.1); + border: none; + color: var(--text-color); + transition: all 0.3s ease; +} + +.radar-controls .btn:hover { + background: var(--accent-color); + color: white; +} + +.radar-stats { + display: flex; + gap: 2rem; + padding: 1rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-bottom: 1rem; + flex-wrap: wrap; +} + +.stat-item { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.stat-item .label { + font-size: 0.8rem; + color: rgba(255, 255, 255, 0.7); +} + +.stat-item .value { + font-size: 1.1rem; + font-weight: 500; + color: var(--accent-color); +} + +.radar-legend { + display: flex; + gap: 2rem; + padding: 1rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-top: 1rem; + justify-content: center; +} + +.legend-item { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.legend-item .dot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.dot.friendly { + background: var(--success-color); +} + +.dot.hostile { + background: var(--danger-color); +} + +.dot.unknown { + background: var(--warning-color); +} + +/* 全屏模式样式 */ +.tactical-radar.fullscreen { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 9999; + margin: 0; + border-radius: 0; +} + +/* 威胁等级指示器 */ +.threat-level { + padding: 1rem; + background: rgba(0, 180, 216, 0.1); + border-radius: 5px; + margin-bottom: 1rem; +} + +.level-label { + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.level-value { + font-size: 2rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +.level-value.low { + color: var(--success-color); +} + +.level-indicator { + height: 6px; + background: rgba(255, 255, 255, 0.1); + border-radius: 3px; + overflow: hidden; +} + +.level-indicator .indicator-bar { + height: 100%; + background: var(--success-color); + border-radius: 3px; + transition: width 0.3s ease; +} + +/* 卡片工具栏 */ +.card-tools { + display: flex; + gap: 0.5rem; +} + +.card-tools .btn { + width: 28px; + height: 28px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + color: var(--accent-color); + border-color: var(--accent-color); +} + +/* 新增的图表区域 */ +.mission-stats, +.area-coverage, +.system-status { + height: 200px; +} + +/* 响应式调整 */ +@media (max-width: 1400px) { + .status-card { + height: 90px; + } + + .status-info h3 { + font-size: 0.85rem; + } + + .status-value { + font-size: 1.3rem; + } + + .main-radar-card { + height: calc(100vh - 250px); + } + + .threat-analysis, + .battlefield-activity { + height: calc((100vh - 250px) / 2 - 1rem); + } +} + +@media (max-width: 992px) { + .battlefield-header { + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; + } + + .header-left { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } +} + +@media (max-width: 768px) { + .sidebar { + transform: translateX(-100%); + } + .main-content { + margin-left: 0; + } + .status-card { + margin-bottom: 1rem; + } +} + +/* 视频容器样式 */ +.video-container { + position: relative; + width: 100%; + height: 480px; + background: rgba(0, 0, 0, 0.8); + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.video-stream { + width: 100%; + height: 100%; + object-fit: cover; +} + +.video-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; +} + +.target-box { + position: absolute; + border: 2px solid #00ff00; + box-shadow: 0 0 10px rgba(0, 255, 0, 0.5); + animation: pulse 2s infinite; +} + +.target-marker { + position: absolute; + width: 20px; + height: 20px; + border: 2px solid #00ff00; + border-radius: 50%; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.target-info { + position: absolute; + bottom: -40px; + left: 50%; + transform: translateX(-50%); + background: rgba(0, 0, 0, 0.8); + padding: 4px 12px; + border-radius: 4px; + font-size: 12px; + color: #fff; + white-space: nowrap; +} + +.overlay-info { + position: absolute; + top: 20px; + right: 20px; + background: rgba(0, 0, 0, 0.7); + padding: 10px; + border-radius: 8px; + color: #fff; +} + +.info-item { + margin: 5px 0; + font-size: 14px; +} + +.info-item i { + margin-right: 8px; + color: #00ff00; +} + +.video-controls { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-top: 15px; +} + +.stream-settings .btn { + margin-left: 10px; + position: relative; + overflow: hidden; +} + +.stream-settings .btn::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 5px; + height: 5px; + background: rgba(255, 255, 255, 0.8); + border-radius: 50%; + transform: translate(-50%, -50%); + opacity: 0; + transition: all 0.3s; +} + +.stream-settings .btn.active::after { + opacity: 1; + width: 10px; + height: 10px; +} + +.target-details { + padding: 15px; +} + +.detail-group { + margin-bottom: 20px; + background: rgba(0, 0, 0, 0.2); + padding: 15px; + border-radius: 8px; + transition: all 0.3s; +} + +.detail-group:hover { + background: rgba(0, 0, 0, 0.3); + transform: translateY(-2px); +} + +.detail-item { + margin-bottom: 10px; +} + +.detail-item label { + display: block; + color: rgba(255, 255, 255, 0.7); + font-size: 12px; + margin-bottom: 5px; +} + +.detail-item .value { + font-size: 16px; + font-weight: 500; + color: #fff; +} + +.detail-item .progress { + height: 6px; + background: rgba(255, 255, 255, 0.1); +} + +.target-actions { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.target-actions .btn { + flex: 1; + min-width: 120px; + position: relative; + overflow: hidden; +} + +.target-actions .btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent); + transform: translateX(-100%); + transition: all 0.3s; +} + +.target-actions .btn:hover::before { + transform: translateX(100%); +} + +.recent-identifications { + max-height: 400px; + overflow-y: auto; + padding: 10px; +} + +.identification-item { + display: flex; + align-items: center; + padding: 12px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-bottom: 10px; + transition: all 0.3s; +} + +.identification-item:hover { + background: rgba(0, 0, 0, 0.3); + transform: translateX(5px); +} + +.item-time { + font-size: 12px; + color: rgba(255, 255, 255, 0.7); + width: 80px; +} + +.item-content { + flex: 1; + margin: 0 15px; +} + +.item-type { + font-weight: 500; + margin-bottom: 4px; +} + +.item-type.hostile { + color: #ff4757; +} + +.item-type.friendly { + color: #2ecc71; +} + +.item-type.unknown { + color: #f1c40f; +} + +.item-info { + font-size: 12px; + color: rgba(255, 255, 255, 0.7); +} + +.item-status { + font-size: 12px; + padding: 4px 8px; + border-radius: 4px; + background: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.7); +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.4); + } + 70% { + box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); + } +} + +/* 图表容器样式优化 */ +.analysis-chart { + height: 250px; + padding: 15px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-bottom: 15px; +} + +/* 响应式调整 */ +@media (max-width: 992px) { + .video-container { + height: 360px; + } + + .target-actions .btn { + min-width: 100px; + } + + .detail-group { + padding: 10px; + } +} + +@media (max-width: 768px) { + .video-container { + height: 280px; + } + + .overlay-info { + top: 10px; + right: 10px; + font-size: 12px; + } + + .target-actions { + flex-direction: column; + } + + .target-actions .btn { + width: 100%; + } +} + +/* 记录页面样式 */ +.records-page .card { + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin-bottom: 1.5rem; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.records-page .card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +.records-page .card-header { + background: rgba(44, 62, 80, 0.5); + border-bottom: 1px solid var(--border-color); + padding: 1rem 1.25rem; +} + +.records-page .card-title { + margin: 0; + font-size: 1.1rem; + font-weight: 500; + color: var(--accent-color); +} + +.records-page .form-label { + color: var(--text-color); + font-weight: 500; + margin-bottom: 0.5rem; +} + +.records-page .form-select { + background-color: rgba(26, 38, 52, 0.8); + border: 1px solid var(--border-color); + color: var(--text-color); + transition: border-color 0.3s ease, box-shadow 0.3s ease; +} + +.records-page .form-select:focus { + border-color: var(--accent-color); + box-shadow: 0 0 0 0.25rem rgba(0, 180, 216, 0.25); +} + +.records-page .btn-primary { + background-color: var(--accent-color); + border: none; + padding: 0.5rem 1.25rem; + transition: all 0.3s ease; +} + +.records-page .btn-primary:hover { + background-color: #0095b3; + transform: translateY(-1px); +} + +.records-page .btn-secondary { + background-color: #34495e; + border: none; + padding: 0.5rem 1.25rem; +} + +.records-page .table { + color: var(--text-color); + margin-bottom: 0; +} + +.records-page .table thead th { + background: rgba(44, 62, 80, 0.5); + border-bottom: 2px solid var(--border-color); + color: var(--accent-color); + font-weight: 500; + padding: 1rem; +} + +.records-page .table tbody tr { + transition: background-color 0.3s ease; +} + +.records-page .table tbody tr:hover { + background-color: rgba(44, 62, 80, 0.3); +} + +.records-page .table td { + padding: 1rem; + vertical-align: middle; + border-bottom: 1px solid var(--border-color); +} + +.records-page .badge { + padding: 0.5rem 0.75rem; + border-radius: 20px; + font-weight: 500; +} + +.records-page .badge-friendly { + background-color: rgba(46, 204, 113, 0.2); + color: var(--success-color); +} + +.records-page .badge-hostile { + background-color: rgba(231, 76, 60, 0.2); + color: var(--danger-color); +} + +.records-page .badge-unknown { + background-color: rgba(52, 152, 219, 0.2); + color: var(--info-color); +} + +.records-page .stats-card { + background: linear-gradient(135deg, rgba(26, 38, 52, 0.9) 0%, rgba(44, 62, 80, 0.9) 100%); + border-radius: 12px; + padding: 1.5rem; + height: 100%; + transition: transform 0.3s ease; +} + +.records-page .stats-card:hover { + transform: translateY(-2px); +} + +.records-page .stats-icon { + font-size: 2rem; + color: var(--accent-color); + margin-bottom: 1rem; +} + +.records-page .stats-value { + font-size: 1.8rem; + font-weight: 600; + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.records-page .stats-label { + color: #a0aec0; + font-size: 0.9rem; +} + +.records-page .pagination { + margin-top: 2rem; +} + +.records-page .page-link { + background-color: rgba(26, 38, 52, 0.8); + border: 1px solid var(--border-color); + color: var(--text-color); + padding: 0.5rem 1rem; + transition: all 0.3s ease; +} + +.records-page .page-link:hover { + background-color: var(--accent-color); + color: white; + border-color: var(--accent-color); +} + +.records-page .page-item.active .page-link { + background-color: var(--accent-color); + border-color: var(--accent-color); +} + +.records-page .modal-content { + background: var(--card-bg); + border: 1px solid var(--border-color); +} + +.records-page .modal-header { + border-bottom: 1px solid var(--border-color); + padding: 1.25rem; +} + +.records-page .modal-title { + color: var(--accent-color); +} + +.records-page .modal-body { + padding: 1.5rem; +} + +.records-page .table-sm td { + padding: 0.75rem; + color: var(--text-color); +} + +/* 动画效果 */ +.records-page .fade-in { + animation: fadeIn 0.5s ease-out forwards; +} + +.records-page .slide-in { + animation: slideIn 0.5s ease-out forwards; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 信号识别页面样式 */ +.signal-page .card { + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin-bottom: 1.5rem; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.signal-page .card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +.signal-page .card-header { + background: rgba(44, 62, 80, 0.5); + border-bottom: 1px solid var(--border-color); + padding: 1rem 1.25rem; +} + +.signal-page .card-title { + margin: 0; + font-size: 1.1rem; + font-weight: 500; + color: var(--accent-color); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.signal-page .signal-container { + position: relative; + background: rgba(26, 38, 52, 0.9); + border-radius: 8px; + padding: 1rem; +} + +.signal-page .signal-controls { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + flex-wrap: wrap; +} + +.signal-page .btn-group { + display: flex; + gap: 0.5rem; +} + +.signal-page .btn { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border-radius: 6px; + transition: all 0.3s ease; +} + +.signal-page .btn i { + font-size: 1rem; +} + +.signal-page .btn-primary { + background: var(--accent-color); + border: none; +} + +.signal-page .btn-primary:hover { + background: #0095b3; + transform: translateY(-1px); +} + +.signal-page .frequency-selector { + display: flex; + align-items: center; + gap: 1rem; +} + +.signal-page .form-select { + background-color: rgba(26, 38, 52, 0.8); + border: 1px solid var(--border-color); + color: var(--text-color); + min-width: 120px; +} + +.signal-page .signal-display { + margin: 1.5rem 0; + position: relative; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + overflow: hidden; + height: 300px; +} + +.signal-page .signal-display canvas { + width: 100%; + height: 100%; +} + +.signal-page .signal-info { + background: rgba(26, 38, 52, 0.6); + border-radius: 8px; + padding: 1rem; +} + +.signal-page .info-item { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.5rem; + border-radius: 6px; + background: rgba(0, 0, 0, 0.2); +} + +.signal-page .info-item label { + color: var(--accent-color); + margin: 0; + font-weight: 500; +} + +.signal-page .info-item span { + color: var(--text-color); + font-family: 'Roboto Mono', monospace; +} + +.signal-page .signal-features { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.signal-page .feature-item { + background: rgba(26, 38, 52, 0.6); + padding: 1rem; + border-radius: 8px; +} + +.signal-page .feature-item label { + color: var(--accent-color); + display: block; + margin-bottom: 0.5rem; + font-weight: 500; +} + +.signal-page .feature-item span { + color: var(--text-color); + font-family: 'Roboto Mono', monospace; +} + +.signal-page .progress { + height: 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + overflow: hidden; +} + +.signal-page .progress-bar { + transition: width 0.3s ease; +} + +.signal-page .threat-analysis { + background: rgba(26, 38, 52, 0.6); + border-radius: 8px; + padding: 1rem; +} + +.signal-page .threat-level { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; +} + +.signal-page .threat-level label { + color: var(--accent-color); + margin: 0; + font-weight: 500; +} + +.signal-page .threat-level .badge { + padding: 0.5rem 1rem; + font-size: 0.9rem; + border-radius: 20px; +} + +.signal-page .threat-details ul li { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 6px; + margin-bottom: 0.5rem; +} + +.signal-page .recommended-actions .btn { + text-align: left; + justify-content: flex-start; +} + +.signal-page .table { + color: var(--text-color); + margin: 0; +} + +.signal-page .table thead th { + background: rgba(44, 62, 80, 0.5); + border-bottom: 2px solid var(--border-color); + color: var(--accent-color); + font-weight: 500; + padding: 1rem; +} + +.signal-page .table tbody tr { + transition: background-color 0.3s ease; +} + +.signal-page .table tbody tr:hover { + background-color: rgba(44, 62, 80, 0.3); +} + +.signal-page .table td { + padding: 1rem; + vertical-align: middle; + border-bottom: 1px solid var(--border-color); +} + +/* 动画效果 */ +@keyframes signalPulse { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.02); + opacity: 0.8; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +@keyframes scanning { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +.signal-page .scanning { + animation: signalPulse 2s ease-in-out infinite; + background: linear-gradient(90deg, + rgba(0, 180, 216, 0.1) 0%, + rgba(0, 180, 216, 0.2) 50%, + rgba(0, 180, 216, 0.1) 100%); + background-size: 200% 100%; + animation: scanning 3s linear infinite; +} + +/* 信号页面额外样式 */ +.signal-page .frequency-controls { + display: flex; + align-items: center; + gap: 2rem; +} + +.signal-page .scan-speed { + display: flex; + align-items: center; + gap: 1rem; + min-width: 200px; +} + +.signal-page .form-range { + height: 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + cursor: pointer; +} + +.signal-page .form-range::-webkit-slider-thumb { + background: var(--accent-color); + width: 16px; + height: 16px; + border-radius: 50%; + cursor: pointer; + transition: transform 0.2s ease; +} + +.signal-page .form-range::-webkit-slider-thumb:hover { + transform: scale(1.2); +} + +.signal-page .signal-markers { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; +} + +.signal-page .frequency-marker { + position: absolute; + top: 0; + bottom: 0; +} + +.signal-page .marker-line { + position: absolute; + top: 0; + bottom: 0; + width: 1px; + background: rgba(255, 255, 255, 0.2); +} + +.signal-page .marker-label { + position: absolute; + bottom: 5px; + left: 50%; + transform: translateX(-50%); + background: rgba(0, 0, 0, 0.5); + padding: 2px 8px; + border-radius: 4px; + font-size: 0.8rem; + color: var(--accent-color); + white-space: nowrap; +} + +.signal-page .status-card { + background: linear-gradient(135deg, rgba(26, 38, 52, 0.9) 0%, rgba(44, 62, 80, 0.9) 100%); + border-radius: 12px; + padding: 1.5rem; + height: 100%; + transition: transform 0.3s ease; +} + +.signal-page .status-card:hover { + transform: translateY(-2px); +} + +.signal-page .status-icon { + font-size: 2rem; + color: var(--accent-color); + margin-bottom: 1rem; +} + +.signal-page .status-value { + font-size: 1.8rem; + font-weight: 600; + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.signal-page .status-label { + color: #a0aec0; + font-size: 0.9rem; +} + +/* Toast 消息样式 */ +.toast-message { + position: fixed; + top: 20px; + right: 20px; + background: rgba(0, 0, 0, 0.8); + color: #fff; + padding: 12px 24px; + border-radius: 4px; + z-index: 1000; + transform: translateX(120%); + transition: transform 0.3s ease-in-out; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); +} + +.toast-message.show { + transform: translateX(0); +} + +/* 全屏模式样式 */ +.signal-container:fullscreen { + background: var(--primary-color); + padding: 2rem; +} + +.signal-container:fullscreen .signal-display { + height: calc(100vh - 200px); +} + +/* 动画效果 */ +.signal-page .fade-in { + animation: fadeIn 0.5s ease-out forwards; +} + +/* 进度条动画 */ +.signal-page .progress-bar { + position: relative; + overflow: hidden; +} + +.signal-page .progress-bar::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.2) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: translateX(-100%); + animation: progressShine 2s linear infinite; +} + +@keyframes progressShine { + to { + transform: translateX(100%); + } +} + +/* 响应式调整 */ +@media (max-width: 1200px) { + .signal-page .frequency-controls { + flex-direction: column; + gap: 1rem; + } + + .signal-page .scan-speed { + width: 100%; + } +} + +@media (max-width: 768px) { + .signal-page .signal-info .row { + flex-direction: column; + } + + .signal-page .info-item { + margin-bottom: 0.5rem; + } + + .signal-page .status-card { + margin-bottom: 1rem; + } +} + +/* 截图闪光效果 */ +.capture-flash { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #fff; + opacity: 0; + animation: flash 0.2s ease-out; + pointer-events: none; +} + +@keyframes flash { + 0% { opacity: 0; } + 50% { opacity: 0.8; } + 100% { opacity: 0; } +} + +/* 按钮悬停效果 */ +.btn { + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn:active { + transform: translateY(0); +} + +.btn i { + margin-right: 6px; + transition: transform 0.3s ease; +} + +.btn:hover i { + transform: scale(1.2); +} + +/* 目标框动画效果 */ +.target-box { + transition: all 0.3s ease-out; +} + +.target-box:hover .target-info { + opacity: 1; + transform: translateY(0); +} + +.target-info { + opacity: 0; + transform: translateY(10px); + transition: all 0.3s ease-out; +} + +/* 图表容器悬停效果 */ +.analysis-chart { + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.analysis-chart:hover { + transform: translateY(-5px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); +} + +/* 状态标签动画 */ +.status-dot { + position: relative; +} + +.status-dot::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + background: inherit; + border-radius: 50%; + transform: translate(-50%, -50%) scale(1); + animation: pulse-dot 2s infinite; +} + +@keyframes pulse-dot { + 0% { + transform: translate(-50%, -50%) scale(1); + opacity: 0.5; + } + 70% { + transform: translate(-50%, -50%) scale(2); + opacity: 0; + } + 100% { + transform: translate(-50%, -50%) scale(1); + opacity: 0; + } +} + +/* 进度条动画 */ +.progress-bar { + transition: width 0.6s ease-in-out; +} + +/* 威胁等级标签动画 */ +.badge { + transition: all 0.3s ease; +} + +.badge:hover { + transform: scale(1.1); +} + +/* 最近识别列表动画 */ +.identification-item { + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 视频控制按钮组动画 */ +.video-controls .btn-group { + position: relative; + overflow: hidden; +} + +.video-controls .btn-group::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + animation: scanline 2s linear infinite; +} + +@keyframes scanline { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +/* 响应式调整 */ +@media (max-width: 768px) { + .toast-message { + top: auto; + bottom: 20px; + left: 20px; + right: 20px; + transform: translateY(120%); + } + + .toast-message.show { + transform: translateY(0); + } +} \ No newline at end of file diff --git a/common/js/api.js b/common/js/api.js new file mode 100644 index 0000000..7860156 --- /dev/null +++ b/common/js/api.js @@ -0,0 +1,200 @@ +// API 基础 URL +const API_BASE_URL = 'http://localhost:5000/api'; + +// API 调用函数 +const api = { + // 登录 + login: async (username, password) => { + try { + const response = await fetch(`${API_BASE_URL}/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '登录失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 获取无人机列表 + getDrones: async () => { + try { + const response = await fetch(`${API_BASE_URL}/drones`, { + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '获取无人机列表失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 添加无人机 + addDrone: async (droneData) => { + try { + const response = await fetch(`${API_BASE_URL}/drones`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: JSON.stringify(droneData) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '添加无人机失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 更新无人机 + updateDrone: async (droneId, droneData) => { + try { + const response = await fetch(`${API_BASE_URL}/drones/${droneId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: JSON.stringify(droneData) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '更新无人机失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 删除无人机 + deleteDrone: async (droneId) => { + try { + const response = await fetch(`${API_BASE_URL}/drones/${droneId}`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '删除无人机失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 获取识别记录 + getRecords: async (filters = {}) => { + try { + const queryParams = new URLSearchParams(filters).toString(); + const response = await fetch(`${API_BASE_URL}/records?${queryParams}`, { + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '获取识别记录失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 获取记录详情 + getRecordDetail: async (recordId) => { + try { + const response = await fetch(`${API_BASE_URL}/records/${recordId}`, { + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '获取记录详情失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 目标识别 + identify: async (formData) => { + try { + const response = await fetch(`${API_BASE_URL}/identify`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: formData + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '目标识别失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 信号识别 + identifySignal: async (signal) => { + try { + const response = await fetch(`${API_BASE_URL}/signal_identify`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: JSON.stringify({ signal }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '信号识别失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/common/js/battlefield.js b/common/js/battlefield.js new file mode 100644 index 0000000..22c40d7 --- /dev/null +++ b/common/js/battlefield.js @@ -0,0 +1,732 @@ +// 更新时间显示 +function updateTime() { + const now = new Date(); + const timeString = now.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }); + document.getElementById('current-time').textContent = timeString; +} + +// 状态卡片迷你图表 +class StatusMiniChart { + constructor(container) { + this.container = container; + this.canvas = document.createElement('canvas'); + this.container.appendChild(this.canvas); + this.ctx = this.canvas.getContext('2d'); + this.resize(); + this.generateData(); + this.draw(); + } + + resize() { + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + } + + generateData() { + this.data = Array.from({length: 20}, () => Math.random() * 0.8 + 0.2); + } + + draw() { + const ctx = this.ctx; + const width = this.canvas.width; + const height = this.canvas.height; + const step = width / (this.data.length - 1); + + ctx.clearRect(0, 0, width, height); + + // 绘制曲线 + ctx.beginPath(); + ctx.moveTo(0, height * (1 - this.data[0])); + + for (let i = 1; i < this.data.length; i++) { + const x = i * step; + const y = height * (1 - this.data[i]); + + if (i === 0) { + ctx.moveTo(x, y); + } else { + const xc = (x + (i - 1) * step) / 2; + const yc = (y + height * (1 - this.data[i - 1])) / 2; + ctx.quadraticCurveTo(xc, yc, x, y); + } + } + + ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)'; + ctx.lineWidth = 2; + ctx.stroke(); + + // 填充渐变 + const gradient = ctx.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, 'rgba(255, 255, 255, 0.2)'); + gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); + + ctx.lineTo(width, height); + ctx.lineTo(0, height); + ctx.fillStyle = gradient; + ctx.fill(); + } + + update() { + this.data.shift(); + this.data.push(Math.random() * 0.8 + 0.2); + this.draw(); + } +} + +// 雷达扫描效果 +class TacticalRadar { + constructor(containerId) { + this.container = document.getElementById(containerId); + this.canvas = document.createElement('canvas'); + this.ctx = this.canvas.getContext('2d'); + this.container.appendChild(this.canvas); + this.resize(); + this.setupRadar(); + window.addEventListener('resize', () => this.resize()); + } + + resize() { + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + this.centerX = this.canvas.width / 2; + this.centerY = this.canvas.height / 2; + this.radius = Math.min(this.centerX, this.centerY) * 0.85; + } + + setupRadar() { + this.angle = 0; + this.targets = this.generateRandomTargets(); + this.fadeOpacity = new Array(360).fill(0); + this.animate(); + } + + generateRandomTargets() { + const targets = []; + const numTargets = Math.floor(Math.random() * 5) + 3; + + for (let i = 0; i < numTargets; i++) { + const distance = Math.random() * 0.8 + 0.1; // 10% - 90% of radius + const angle = Math.random() * Math.PI * 2; + const type = Math.random() < 0.7 ? 'friendly' : 'hostile'; + const size = Math.random() * 3 + 2; + + targets.push({ + x: Math.cos(angle) * this.radius * distance, + y: Math.sin(angle) * this.radius * distance, + type, + size, + blip: 1.0, // 用于闪烁效果 + fadeStart: angle // 用于淡出效果 + }); + } + return targets; + } + + drawGrid() { + // 绘制背景 + const gradient = this.ctx.createRadialGradient( + this.centerX, this.centerY, 0, + this.centerX, this.centerY, this.radius + ); + gradient.addColorStop(0, 'rgba(0, 180, 216, 0.1)'); + gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); + + this.ctx.fillStyle = gradient; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + this.ctx.strokeStyle = 'rgba(0, 180, 216, 0.2)'; + this.ctx.lineWidth = 1; + + // 绘制同心圆 + for (let i = 1; i <= 4; i++) { + const radius = this.radius * (i/4); + this.ctx.beginPath(); + this.ctx.arc(this.centerX, this.centerY, radius, 0, Math.PI * 2); + this.ctx.stroke(); + + // 添加距离标记 + const distance = (i * 25).toString() + 'km'; + this.ctx.fillStyle = 'rgba(0, 180, 216, 0.5)'; + this.ctx.font = '12px Noto Sans SC'; + this.ctx.fillText(distance, this.centerX + radius + 5, this.centerY); + } + + // 绘制方向标记 + const directions = ['N', 'E', 'S', 'W']; + this.ctx.font = 'bold 14px Noto Sans SC'; + directions.forEach((dir, i) => { + const angle = (i * Math.PI / 2) - Math.PI / 2; + const x = this.centerX + Math.cos(angle) * (this.radius + 20); + const y = this.centerY + Math.sin(angle) * (this.radius + 20); + this.ctx.fillText(dir, x - 6, y + 6); + }); + + // 绘制刻度线 + for (let i = 0; i < 360; i += 30) { + const angle = (i * Math.PI) / 180; + const startRadius = this.radius - 10; + const endRadius = this.radius; + + this.ctx.beginPath(); + this.ctx.moveTo( + this.centerX + Math.cos(angle) * startRadius, + this.centerY + Math.sin(angle) * startRadius + ); + this.ctx.lineTo( + this.centerX + Math.cos(angle) * endRadius, + this.centerY + Math.sin(angle) * endRadius + ); + this.ctx.stroke(); + } + + // 绘制十字线 + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX - this.radius, this.centerY); + this.ctx.lineTo(this.centerX + this.radius, this.centerY); + this.ctx.moveTo(this.centerX, this.centerY - this.radius); + this.ctx.lineTo(this.centerX, this.centerY + this.radius); + this.ctx.stroke(); + } + + drawSweep() { + // 更新扫描线渐变效果 + const gradient = this.ctx.createLinearGradient( + this.centerX, + this.centerY, + this.centerX + Math.cos(this.angle) * this.radius, + this.centerY + Math.sin(this.angle) * this.radius + ); + gradient.addColorStop(0, 'rgba(0, 180, 216, 0.8)'); + gradient.addColorStop(0.5, 'rgba(0, 180, 216, 0.3)'); + gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); + + // 绘制扫描线 + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX, this.centerY); + this.ctx.lineTo( + this.centerX + Math.cos(this.angle) * this.radius, + this.centerY + Math.sin(this.angle) * this.radius + ); + this.ctx.strokeStyle = gradient; + this.ctx.lineWidth = 3; + this.ctx.stroke(); + + // 扫描扇形 + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX, this.centerY); + this.ctx.arc(this.centerX, this.centerY, this.radius, this.angle - 0.2, this.angle); + this.ctx.fillStyle = 'rgba(0, 180, 216, 0.1)'; + this.ctx.fill(); + + // 更新淡出效果数组 + const currentAngleDeg = (this.angle * 180 / Math.PI) % 360; + this.fadeOpacity[Math.floor(currentAngleDeg)] = 1; + + // 绘制历史扫描线 + for (let i = 0; i < 360; i++) { + if (this.fadeOpacity[i] > 0) { + const angle = (i * Math.PI) / 180; + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX, this.centerY); + this.ctx.lineTo( + this.centerX + Math.cos(angle) * this.radius, + this.centerY + Math.sin(angle) * this.radius + ); + this.ctx.strokeStyle = `rgba(0, 180, 216, ${this.fadeOpacity[i] * 0.1})`; + this.ctx.stroke(); + this.fadeOpacity[i] *= 0.95; + } + } + } + + drawTargets() { + this.targets.forEach(target => { + // 计算目标在画布上的位置 + const x = this.centerX + target.x; + const y = this.centerY + target.y; + + // 检查是否在扫描线范围内 + const targetAngle = Math.atan2(target.y, target.x); + const normalizedTargetAngle = targetAngle < 0 ? targetAngle + Math.PI * 2 : targetAngle; + const normalizedSweepAngle = this.angle % (Math.PI * 2); + + if (Math.abs(normalizedTargetAngle - normalizedSweepAngle) < 0.2) { + target.blip = 1.0; + } + + // 绘制目标 + this.ctx.beginPath(); + this.ctx.arc(x, y, target.size, 0, Math.PI * 2); + + const color = target.type === 'friendly' ? + `rgba(46, 204, 113, ${target.blip})` : + `rgba(231, 76, 60, ${target.blip})`; + + this.ctx.fillStyle = color; + this.ctx.fill(); + + // 绘制目标光晕 + this.ctx.beginPath(); + this.ctx.arc(x, y, target.size * 2, 0, Math.PI * 2); + this.ctx.fillStyle = color.replace('1)', '0.3)'); + this.ctx.fill(); + + // 淡出效果 + target.blip *= 0.95; + }); + } + + updateStats() { + const friendlyTargets = this.targets.filter(t => t.type === 'friendly').length; + const hostileTargets = this.targets.filter(t => t.type === 'hostile').length; + + document.querySelector('.stat-item:nth-child(2) .value').textContent = + `${friendlyTargets + hostileTargets}`; + } + + animate() { + this.ctx.fillStyle = 'rgba(26, 38, 52, 0.3)'; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + this.drawGrid(); + this.drawSweep(); + this.drawTargets(); + this.updateStats(); + + this.angle += 0.02; + if (this.angle >= Math.PI * 2) { + this.angle = 0; + if (Math.random() < 0.3) { + this.targets = this.generateRandomTargets(); + } + } + + requestAnimationFrame(() => this.animate()); + } +} + +// 状态卡片动画 +class StatusCards { + constructor() { + this.cards = document.querySelectorAll('.status-card'); + this.setupCards(); + } + + setupCards() { + this.cards.forEach(card => { + const value = card.querySelector('.status-value'); + const targetValue = parseInt(value.textContent); + value.textContent = '0'; + + // 添加数值增长动画 + this.animateValue(value, 0, targetValue, 2000); + + // 添加图标呼吸效果 + const icon = card.querySelector('.status-icon'); + this.addPulseEffect(icon); + }); + } + + animateValue(element, start, end, duration) { + const range = end - start; + const increment = range / (duration / 16); + let current = start; + + const animate = () => { + current += increment; + element.textContent = Math.floor(current); + + if (current < end) { + requestAnimationFrame(animate); + } else { + element.textContent = end; + } + }; + + animate(); + } + + addPulseEffect(element) { + element.style.animation = 'pulse 2s infinite'; + } +} + +// 威胁分析图表 +class ThreatAnalysis { + constructor(containerId) { + this.container = document.getElementById(containerId); + this.setupChart(); + } + + setupChart() { + // 这里可以使用Chart.js或其他图表库来实现威胁分析图表 + this.container.innerHTML = '
威胁分析图表加载中...
'; + } +} + +// 战场活动播报 +class BattlefieldActivity { + constructor(containerId) { + this.container = document.getElementById(containerId); + this.activities = []; + this.setupActivity(); + } + + setupActivity() { + this.container.innerHTML = ` +
+
+ 10:30 + 警告 + 检测到未知信号源,位于东北方向,距离2.5公里 +
+
+ 10:28 + 信息 + 友军编队完成例行巡逻任务 +
+
+ `; + } + + addActivity(activity) { + this.activities.unshift(activity); + this.updateDisplay(); + } + + updateDisplay() { + // 更新活动列表显示 + } +} + +// 任务执行统计图表 +class MissionStats { + constructor(containerId) { + const ctx = document.getElementById(containerId).getContext('2d'); + this.chart = new Chart(ctx, { + type: 'line', + data: { + labels: Array.from({length: 12}, (_, i) => `${i*2}:00`), + datasets: [{ + label: '任务完成率', + data: Array.from({length: 12}, () => Math.random() * 40 + 60), + borderColor: '#00b4d8', + backgroundColor: 'rgba(0, 180, 216, 0.1)', + fill: true, + tension: 0.4 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: '#e9ecef' + } + }, + x: { + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: '#e9ecef' + } + } + } + } + }); + } +} + +// 区域覆盖率图表 +class AreaCoverage { + constructor(containerId) { + const ctx = document.getElementById(containerId).getContext('2d'); + this.chart = new Chart(ctx, { + type: 'doughnut', + data: { + labels: ['已覆盖', '未覆盖'], + datasets: [{ + data: [75, 25], + backgroundColor: [ + 'rgba(0, 180, 216, 0.8)', + 'rgba(255, 255, 255, 0.1)' + ], + borderWidth: 0 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'bottom', + labels: { + color: '#e9ecef' + } + } + } + } + }); + } +} + +// 系统状态图表 +class SystemStatus { + constructor(containerId) { + const ctx = document.getElementById(containerId).getContext('2d'); + this.chart = new Chart(ctx, { + type: 'bar', + data: { + labels: ['CPU', '内存', '存储', '网络'], + datasets: [{ + label: '使用率', + data: [65, 45, 30, 80], + backgroundColor: 'rgba(0, 180, 216, 0.8)', + borderRadius: 5 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: '#e9ecef' + } + }, + x: { + grid: { + display: false + }, + ticks: { + color: '#e9ecef' + } + } + } + } + }); + } +} + +class Radar3D { + constructor(containerId) { + this.container = document.getElementById(containerId); + if (!this.container) { + console.error('Container not found:', containerId); + return; + } + + // 清空容器 + while (this.container.firstChild) { + this.container.removeChild(this.container.firstChild); + } + + this.scene = new THREE.Scene(); + this.camera = new THREE.PerspectiveCamera(75, this.container.clientWidth / this.container.clientHeight, 0.1, 1000); + + try { + this.renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true + }); + console.log('Renderer created successfully'); + } catch (error) { + console.error('Failed to create renderer:', error); + return; + } + + this.renderer.setSize(this.container.clientWidth, this.container.clientHeight); + this.renderer.setClearColor(0x000000, 0.1); + this.container.appendChild(this.renderer.domElement); + console.log('Renderer added to container'); + + // 设置相机位置 + this.camera.position.set(50, 30, 50); + this.camera.lookAt(0, 0, 0); + + // 创建场景内容 + this.createScene(); + + // 添加事件监听 + window.addEventListener('resize', () => this.onWindowResize()); + + // 开始动画循环 + this.animate(); + console.log('Animation started'); + } + + createScene() { + try { + // 添加环境光 + const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); + this.scene.add(ambientLight); + + // 添加点光源 + const pointLight = new THREE.PointLight(0x00b4d8, 1); + pointLight.position.set(10, 10, 10); + this.scene.add(pointLight); + + // 创建地面网格 + const gridHelper = new THREE.GridHelper(100, 20, 0x404040, 0x404040); + this.scene.add(gridHelper); + + // 创建坐标轴 + const axesHelper = new THREE.AxesHelper(50); + this.scene.add(axesHelper); + + // 创建雷达平面 + const radarGeometry = new THREE.CircleGeometry(40, 64); + const radarMaterial = new THREE.MeshPhongMaterial({ + color: 0x00b4d8, + transparent: true, + opacity: 0.3, + side: THREE.DoubleSide + }); + this.radarMesh = new THREE.Mesh(radarGeometry, radarMaterial); + this.radarMesh.rotation.x = -Math.PI / 2; + this.scene.add(this.radarMesh); + + // 创建扫描线 + const lineGeometry = new THREE.BufferGeometry().setFromPoints([ + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(40, 0, 0) + ]); + const lineMaterial = new THREE.LineBasicMaterial({ + color: 0x00ff00, + linewidth: 2 + }); + this.radarLine = new THREE.Line(lineGeometry, lineMaterial); + this.radarLine.rotation.x = -Math.PI / 2; + this.scene.add(this.radarLine); + + // 设置轨道控制器 + if (window.OrbitControls) { + this.controls = new window.OrbitControls(this.camera, this.renderer.domElement); + this.controls.enableDamping = true; + this.controls.dampingFactor = 0.05; + console.log('OrbitControls initialized'); + } else { + console.error('OrbitControls not available'); + } + + console.log('Scene created successfully'); + } catch (error) { + console.error('Error creating scene:', error); + } + } + + addTarget(position, type = 'unknown') { + const colors = { + friendly: 0x2ecc71, + hostile: 0xe74c3c, + unknown: 0xf1c40f + }; + + const geometry = new THREE.SphereGeometry(1, 16, 16); + const material = new THREE.MeshPhongMaterial({ + color: colors[type], + emissive: colors[type], + emissiveIntensity: 0.5 + }); + const target = new THREE.Mesh(geometry, material); + target.position.set(position.x, position.y, position.z); + this.scene.add(target); + } + + onWindowResize() { + if (!this.camera || !this.renderer || !this.container) return; + + const width = this.container.clientWidth; + const height = this.container.clientHeight; + + this.camera.aspect = width / height; + this.camera.updateProjectionMatrix(); + this.renderer.setSize(width, height); + } + + animate() { + if (!this.renderer || !this.scene || !this.camera) return; + + requestAnimationFrame(() => this.animate()); + + if (this.radarLine) { + this.radarLine.rotation.z += 0.02; + } + + if (this.controls) { + this.controls.update(); + } + + this.renderer.render(this.scene, this.camera); + } +} + +// 初始化 +document.addEventListener('DOMContentLoaded', () => { + // 更新时间 + updateTime(); + setInterval(updateTime, 1000); + + // 初始化雷达 + const radar = new Radar3D('tacticalRadar'); + + // 初始化状态卡片迷你图表 + const miniCharts = []; + document.querySelectorAll('.mini-chart').forEach(container => { + const chart = new StatusMiniChart(container); + miniCharts.push(chart); + }); + + // 更新迷你图表 + setInterval(() => { + miniCharts.forEach(chart => chart.update()); + }, 2000); + + // 初始化其他图表 + const missionStats = new MissionStats('missionStats'); + const areaCoverage = new AreaCoverage('areaCoverage'); + const systemStatus = new SystemStatus('systemStatus'); + + // 添加按钮事件监听 + document.querySelectorAll('.radar-controls .btn, .card-tools .btn').forEach(btn => { + btn.addEventListener('click', function() { + // 这里可以添加按钮的具体功能 + console.log('Button clicked:', this.querySelector('i').className); + }); + }); + + // 添加示例目标 + radar.addTarget({ x: 20, y: 0, z: 20 }, 'friendly'); + radar.addTarget({ x: -15, y: 5, z: -25 }, 'hostile'); + radar.addTarget({ x: 30, y: 2, z: -10 }, 'unknown'); +}); \ No newline at end of file diff --git a/common/js/common.js b/common/js/common.js new file mode 100644 index 0000000..4faf7a0 --- /dev/null +++ b/common/js/common.js @@ -0,0 +1,169 @@ +// 全局变量 +let currentUser = null; +let threatChart = null; +let radarTargets = []; // 存储雷达上的目标 +let radarInterval = null; // 存储雷达更新间隔 + +// 显示提示消息 +function showToast(message, type = 'info') { + // 创建toast容器(如果不存在) + let toastContainer = document.getElementById('toast-container'); + if (!toastContainer) { + toastContainer = document.createElement('div'); + toastContainer.id = 'toast-container'; + toastContainer.className = 'position-fixed top-0 end-0 p-3'; + toastContainer.style.zIndex = '1050'; + document.body.appendChild(toastContainer); + } + + // 创建toast元素 + const toastId = 'toast-' + Date.now(); + const toast = document.createElement('div'); + toast.className = `toast align-items-center text-white bg-${type} border-0`; + toast.id = toastId; + toast.setAttribute('role', 'alert'); + toast.setAttribute('aria-live', 'assertive'); + toast.setAttribute('aria-atomic', 'true'); + + toast.innerHTML = ` +
+
+ ${message} +
+ +
+ `; + + toastContainer.appendChild(toast); + + // 显示toast + const bsToast = new bootstrap.Toast(toast, { + animation: true, + autohide: true, + delay: 3000 + }); + bsToast.show(); + + // 自动删除toast元素 + toast.addEventListener('hidden.bs.toast', function() { + toast.remove(); + }); +} + +// 处理登录 +function handleLogin(event) { + event.preventDefault(); + + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + + // 硬编码的用户验证 + if (username === 'admin' && password === '123') { + // 保存登录状态 + currentUser = { + username: 'admin', + token: 'dummy-token' + }; + + // 保存到 localStorage + localStorage.setItem('currentUser', JSON.stringify(currentUser)); + + // 显示成功消息 + showToast('登录成功', 'success'); + + // 跳转到仪表盘页面 + setTimeout(() => { + window.location.href = 'dashboard.html'; + }, 1000); + + return false; + } else { + showToast('用户名或密码错误', 'danger'); + return false; + } +} + +// 退出登录 +function logout() { + currentUser = null; + localStorage.removeItem('currentUser'); + + // 清除雷达间隔 + if (radarInterval) { + clearInterval(radarInterval); + radarInterval = null; + } + + // 重定向到登录页面 + window.location.href = 'login.html'; +} + +// 初始化侧边栏 +function initializeSidebar() { + // 处理侧边栏链接点击 + document.querySelectorAll('.nav-link').forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + + // 处理退出登录 + if (this.id === 'logout-tab') { + logout(); + return; + } + + // 更新活动标签 + document.querySelectorAll('.nav-link').forEach(nav => nav.classList.remove('active')); + this.classList.add('active'); + + // 获取目标页面 + const targetPage = this.getAttribute('href'); + if (targetPage) { + window.location.href = targetPage; + } + }); + }); +} + +// 检查登录状态 +function checkAuth() { + // 从 localStorage 获取用户信息 + const savedUser = localStorage.getItem('currentUser'); + if (savedUser) { + currentUser = JSON.parse(savedUser); + return true; + } + + // 如果不在登录页面,则重定向到登录页面 + if (!window.location.pathname.includes('login.html')) { + window.location.href = 'login.html'; + } + return false; +} + +// 更新用户信息显示 +function updateUserInfo() { + if (currentUser) { + const userNameElement = document.getElementById('user-name'); + if (userNameElement) { + userNameElement.textContent = currentUser.username; + } + } +} + +// 初始化页面 +function initializePage() { + // 如果是登录页面,不需要检查认证 + if (window.location.pathname.includes('login.html')) { + return; + } + + if (!checkAuth()) return; + + initializeSidebar(); + updateUserInfo(); +} + +// 在页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + initializePage(); +}); \ No newline at end of file diff --git a/common/js/drones.js b/common/js/drones.js new file mode 100644 index 0000000..bbb3f0e --- /dev/null +++ b/common/js/drones.js @@ -0,0 +1,341 @@ +// 初始化地图 +let map; +let droneMarkers = {}; + +document.addEventListener('DOMContentLoaded', function() { + // 首先加载无人机列表和其他UI元素 + loadDroneData(); + + // 绑定事件处理器 + bindEventHandlers(); + + // 延迟加载地图和实时更新功能 + setTimeout(() => { + initMap(); + initRealTimeUpdates(); + }, 100); +}); + +function initMap() { + try { + // 使用百度地图 + map = new BMap.Map('map'); + + // 设置中心点和缩放级别 + const point = new BMap.Point(116.4074, 39.9042); + map.centerAndZoom(point, 13); + + // 开启鼠标滚轮缩放 + map.enableScrollWheelZoom(true); + + // 添加地图控件 + map.addControl(new BMap.NavigationControl()); // 添加平移缩放控件 + map.addControl(new BMap.ScaleControl()); // 添加比例尺控件 + map.addControl(new BMap.MapTypeControl()); // 添加地图类型控件 + + // 设置地图样式 - 暗色主题 + map.setMapStyle({ + style: 'midnight' // 使用暗色主题 + }); + + } catch (error) { + console.error('地图初始化失败:', error); + document.getElementById('map').innerHTML = '
地图加载中,请稍候...
'; + } +} + +function initRealTimeUpdates() { + // 降低更新频率,从5秒改为10秒 + setInterval(() => { + if (map && Object.keys(droneMarkers).length > 0) { + updateDronePositions(); + updateBatteryLevels(); + } + }, 10000); + + // 告警更新频率降低到30秒 + setInterval(() => { + updateAlerts(); + }, 30000); +} + +function bindEventHandlers() { + // 刷新状态按钮 + document.getElementById('refreshStatus').addEventListener('click', () => { + loadDroneData(); + }); + + // 任务分配表单提交 + document.getElementById('assignTaskForm').addEventListener('submit', (e) => { + e.preventDefault(); + assignTask(); + }); + + // 添加无人机表单保存 + document.getElementById('saveDrone').addEventListener('click', () => { + saveDrone(); + }); +} + +function loadDroneData() { + // 添加加载状态指示 + const tbody = document.getElementById('dronesList'); + tbody.innerHTML = '加载中...'; + + // 模拟从后端加载数据 + const drones = [ + { + id: 'UAV-001', + model: '侦察者-X1', + status: 'online', + battery: 85, + task: '巡逻任务', + position: { + lng: 116.4074, + lat: 39.9042 + } + }, + { + id: 'UAV-002', + model: '侦察者-X2', + status: 'mission', + battery: 65, + task: '侦察任务', + position: { + lng: 116.4174, + lat: 39.9142 + } + }, + { + id: 'UAV-003', + model: '追踪者-A1', + status: 'maintenance', + battery: 45, + task: '维护中', + position: { + lng: 116.4274, + lat: 39.9242 + } + } + ]; + + // 立即更新无人机列表 + updateDroneList(drones); + + // 如果地图已经初始化,则更新标记 + if (map) { + updateMapMarkers(drones); + } +} + +function updateDroneList(drones) { + const tbody = document.getElementById('dronesList'); + tbody.innerHTML = ''; + + drones.forEach(drone => { + const tr = document.createElement('tr'); + tr.innerHTML = ` + ${drone.id} + ${drone.model} + ${getStatusText(drone.status)} + +
+
+
+ + ${drone.task} + ${formatPosition(drone.position)} + +
+ + + +
+ + `; + tbody.appendChild(tr); + }); +} + +function updateMapMarkers(drones) { + // 清除现有标记 + map.clearOverlays(); + droneMarkers = {}; + + // 添加新标记 + drones.forEach(drone => { + // 创建标记点 + const point = new BMap.Point(drone.position.lng, drone.position.lat); + const marker = new BMap.Marker(point); + + // 创建信息窗口 + const content = ` +
+
${drone.id}
+

型号: ${drone.model}
+ 状态: ${getStatusText(drone.status)}
+ 电量: ${drone.battery}%
+ 任务: ${drone.task}

+
+ `; + + const infoWindow = new BMap.InfoWindow(content, { + width: 200, + height: 120, + title: `${drone.id} - ${drone.model}` + }); + + // 点击标记时显示信息窗口 + marker.addEventListener('click', function() { + map.openInfoWindow(infoWindow, point); + }); + + // 将标记添加到地图 + map.addOverlay(marker); + droneMarkers[drone.id] = { + marker: marker, + point: point + }; + }); +} + +function updateDronePositions() { + // 模拟无人机位置更新 + Object.keys(droneMarkers).forEach(droneId => { + const markerData = droneMarkers[droneId]; + if (markerData && markerData.marker) { + const currentPoint = markerData.point; + + // 随机移动位置 + const newLng = currentPoint.lng + (Math.random() - 0.5) * 0.01; + const newLat = currentPoint.lat + (Math.random() - 0.5) * 0.01; + const newPoint = new BMap.Point(newLng, newLat); + + // 创建动画移动效果 + const moveAnimation = new BMap.Animation.Move(markerData.marker, { + points: [currentPoint, newPoint], + duration: 1000 + }); + moveAnimation.start(); + + // 更新保存的位置 + markerData.point = newPoint; + } + }); +} + +function updateBatteryLevels() { + // 模拟电池电量更新 + document.querySelectorAll('.battery-level').forEach(battery => { + const currentWidth = parseInt(battery.style.width); + if (currentWidth > 0) { + battery.style.width = `${currentWidth - 1}%`; + } + }); +} + +function updateAlerts() { + // 模拟告警更新 + const alertPanel = document.querySelector('.alert-panel ul'); + const currentTime = new Date().toLocaleTimeString(); + + // 随机添加新告警 + if (Math.random() < 0.3) { + const alerts = [ + '通信信号弱,建议调整位置', + '检测到强风,建议降低飞行高度', + '接近禁飞区域边界', + '发现可疑目标,建议进一步确认' + ]; + + const newAlert = alerts[Math.floor(Math.random() * alerts.length)]; + const li = document.createElement('li'); + li.innerHTML = `• ${currentTime} - ${newAlert}`; + + alertPanel.insertBefore(li, alertPanel.firstChild); + + // 限制告警数量 + if (alertPanel.children.length > 5) { + alertPanel.removeChild(alertPanel.lastChild); + } + } +} + +function showDroneDetails(droneId) { + // 更新详情面板 + const detailPanel = document.getElementById('droneDetailPanel'); + detailPanel.querySelector('h5').textContent = `无人机详情 - ${droneId}`; + + // 模拟加载详细信息 + // 实际应用中应该从后端获取数据 +} + +function controlDrone(droneId) { + // 实现无人机控制逻辑 + console.log(`控制无人机: ${droneId}`); +} + +function maintainDrone(droneId) { + // 实现维护模式逻辑 + console.log(`维护无人机: ${droneId}`); +} + +function assignTask() { + const formData = { + droneId: document.getElementById('droneSelect').value, + taskType: document.getElementById('taskType').value, + taskArea: document.getElementById('taskArea').value, + priority: document.getElementById('taskPriority').value, + estimatedTime: document.getElementById('estimatedTime').value + }; + + // 实际应用中应该发送到后端 + console.log('分配任务:', formData); + + // 模拟成功响应 + alert('任务分配成功!'); +} + +function saveDrone() { + const formData = { + model: document.getElementById('droneModel').value, + serialNumber: document.getElementById('serialNumber').value, + maxFlightTime: document.getElementById('maxFlightTime').value, + maxPayload: document.getElementById('maxPayload').value, + communicationBand: document.getElementById('communicationBand').value, + hasCamera: document.getElementById('hasCamera').checked, + hasInfrared: document.getElementById('hasInfrared').checked, + hasGPS: document.getElementById('hasGPS').checked + }; + + // 实际应用中应该发送到后端 + console.log('添加无人机:', formData); + + // 模拟成功响应 + alert('无人机添加成功!'); + + // 关闭模态框 + const modal = bootstrap.Modal.getInstance(document.getElementById('addDroneModal')); + modal.hide(); +} + +// 辅助函数 +function getStatusText(status) { + const statusMap = { + 'online': '在线', + 'mission': '任务中', + 'maintenance': '维护中', + 'offline': '离线' + }; + return statusMap[status] || status; +} + +function formatPosition(position) { + return `N ${position.lat.toFixed(4)}° E ${position.lng.toFixed(4)}°`; +} \ No newline at end of file diff --git a/common/js/identification.js b/common/js/identification.js new file mode 100644 index 0000000..136dee7 --- /dev/null +++ b/common/js/identification.js @@ -0,0 +1,500 @@ +// 更新时间显示 +function updateTime() { + const now = new Date(); + document.getElementById('current-time').textContent = now.toLocaleString(); +} +setInterval(updateTime, 1000); + +// 初始化图表 +function initializeCharts() { + // 置信度历史图表 + const confidenceCtx = document.getElementById('confidenceChart').getContext('2d'); + const confidenceChart = new Chart(confidenceCtx, { + type: 'line', + data: { + labels: ['10:25', '10:26', '10:27', '10:28', '10:29', '10:30'], + datasets: [{ + label: '识别置信度', + data: [65, 75, 82, 78, 85, 88], + borderColor: '#00b4d8', + backgroundColor: 'rgba(0, 180, 216, 0.1)', + tension: 0.4, + fill: true + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + }, + tooltip: { + mode: 'index', + intersect: false, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#fff', + bodyColor: '#fff', + borderColor: 'rgba(255, 255, 255, 0.1)', + borderWidth: 1 + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.7)', + callback: function(value) { + return value + '%'; + } + } + }, + x: { + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.7)' + } + } + }, + interaction: { + intersect: false, + mode: 'index' + }, + animation: { + duration: 1000, + easing: 'easeInOutQuart' + } + } + }); + + // 目标类型分布图表 + const distributionCtx = document.getElementById('typeDistributionChart').getContext('2d'); + const distributionChart = new Chart(distributionCtx, { + type: 'doughnut', + data: { + labels: ['友方目标', '敌方目标', '未知目标'], + datasets: [{ + data: [4, 3, 2], + backgroundColor: [ + '#2ecc71', + '#e74c3c', + '#f1c40f' + ], + borderWidth: 0, + hoverOffset: 4 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'right', + labels: { + color: 'rgba(255, 255, 255, 0.7)', + font: { + size: 11 + }, + padding: 20, + usePointStyle: true, + pointStyle: 'circle' + } + }, + tooltip: { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#fff', + bodyColor: '#fff', + borderColor: 'rgba(255, 255, 255, 0.1)', + borderWidth: 1 + } + }, + animation: { + animateRotate: true, + animateScale: true + } + } + }); + + // 导出图表更新函数 + window.updateCharts = { + updateConfidence: function(newData) { + confidenceChart.data.datasets[0].data = newData; + confidenceChart.update('none'); + }, + updateDistribution: function(newData) { + distributionChart.data.datasets[0].data = newData; + distributionChart.update('none'); + } + }; +} + +// 模拟目标检测框 +function simulateTargetDetection() { + const videoContainer = document.querySelector('.video-container'); + const targetBox = document.querySelector('.target-box'); + let isTracking = false; + + function updateTargetPosition() { + if (!isTracking) return; + + const maxX = videoContainer.clientWidth - 100; + const maxY = videoContainer.clientHeight - 100; + const x = Math.random() * maxX; + const y = Math.random() * maxY; + + targetBox.style.display = 'block'; + targetBox.style.left = x + 'px'; + targetBox.style.top = y + 'px'; + + // 更新目标信息 + updateTargetInfo({ + type: ['未知目标', '友方目标', '敌方目标'][Math.floor(Math.random() * 3)], + distance: Math.floor(Math.random() * 5000 + 1000), + confidence: Math.floor(Math.random() * 30 + 70) + }); + } + + // 开始/停止跟踪 + window.toggleTracking = function(start) { + isTracking = start; + if (start) { + targetBox.style.display = 'block'; + updateTargetPosition(); + } else { + targetBox.style.display = 'none'; + } + }; + + setInterval(updateTargetPosition, 3000); +} + +// 更新目标信息 +function updateTargetInfo(data) { + const targetType = document.getElementById('targetType'); + const confidenceBar = document.querySelector('.progress-bar'); + const distance = document.getElementById('distance'); + const threatLevel = document.getElementById('threatLevel'); + + targetType.textContent = data.type; + confidenceBar.style.width = data.confidence + '%'; + confidenceBar.textContent = data.confidence + '%'; + distance.textContent = (data.distance / 1000).toFixed(1) + 'km'; + + // 更新威胁等级 + const threatLevels = ['低', '中', '高']; + const threatColors = ['success', 'warning', 'danger']; + const threatIndex = Math.floor(Math.random() * 3); + threatLevel.className = `badge bg-${threatColors[threatIndex]}`; + threatLevel.textContent = threatLevels[threatIndex]; +} + +// 绑定控制按钮事件 +function bindControlEvents() { + const startBtn = document.getElementById('startStream'); + const stopBtn = document.getElementById('stopStream'); + const captureBtn = document.getElementById('captureImage'); + const toggleARBtn = document.getElementById('toggleAR'); + const toggleNightVisionBtn = document.getElementById('toggleNightVision'); + const toggleThermalBtn = document.getElementById('toggleThermal'); + + let isStreaming = false; + + startBtn.addEventListener('click', () => { + isStreaming = true; + document.querySelector('.status-dot').classList.add('active'); + window.toggleTracking(true); + showToast('开始目标识别'); + }); + + stopBtn.addEventListener('click', () => { + isStreaming = false; + document.querySelector('.status-dot').classList.remove('active'); + window.toggleTracking(false); + showToast('停止目标识别'); + }); + + captureBtn.addEventListener('click', () => { + showToast('已保存截图'); + // 添加截图动画效果 + const flash = document.createElement('div'); + flash.className = 'capture-flash'; + document.querySelector('.video-container').appendChild(flash); + setTimeout(() => flash.remove(), 200); + }); + + const toggleButtons = [ + { btn: toggleARBtn, name: 'AR叠加' }, + { btn: toggleNightVisionBtn, name: '夜视模式' }, + { btn: toggleThermalBtn, name: '热成像' } + ]; + + toggleButtons.forEach(({ btn, name }) => { + btn.addEventListener('click', () => { + const isActive = btn.classList.toggle('active'); + showToast(`${name}已${isActive ? '开启' : '关闭'}`); + }); + }); +} + +// Toast提示 +function showToast(message) { + const toast = document.createElement('div'); + toast.className = 'toast-message'; + toast.textContent = message; + document.body.appendChild(toast); + + setTimeout(() => { + toast.classList.add('show'); + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 2000); + }, 100); +} + +// 页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + initializeCharts(); + simulateTargetDetection(); + bindControlEvents(); + updateTime(); + + // 添加键盘快捷键 + document.addEventListener('keydown', function(e) { + if (e.key === 'F' || e.key === 'f') { + document.getElementById('captureImage').click(); + } + }); +}); + +// 初始化筛选器 +function initializeFilters() { + const filterForm = document.getElementById('filterForm'); + filterForm.addEventListener('submit', function(e) { + e.preventDefault(); + loadRecords(); + }); + + // 重置按钮事件 + filterForm.addEventListener('reset', function(e) { + setTimeout(() => loadRecords(), 0); + }); + + // 时间范围选择器事件 + const timeRange = document.getElementById('timeRange'); + timeRange.addEventListener('change', function() { + if (this.value === 'custom') { + // TODO: 显示自定义日期选择器 + } + }); +} + +// 加载记录数据 +async function loadRecords() { + try { + const filters = getFilters(); + // TODO: 替换为实际的API调用 + const response = await fetch('/api/records?' + new URLSearchParams(filters)); + const data = await response.json(); + + updateRecordsList(data.records); + updatePagination(data.pagination); + updateStats(data.stats); + } catch (error) { + console.error('加载记录失败:', error); + showError('加载记录失败,请稍后重试'); + } +} + +// 获取筛选条件 +function getFilters() { + return { + timeRange: document.getElementById('timeRange').value, + targetType: document.getElementById('targetType').value, + identificationStatus: document.getElementById('identificationStatus').value, + threatLevel: document.getElementById('threatLevel').value, + page: currentPage + }; +} + +// 更新记录列表 +function updateRecordsList(records) { + const tbody = document.getElementById('recordsList'); + tbody.innerHTML = records.map(record => ` + + ${formatDateTime(record.time)} + ${formatType(record.type)} + ${record.result} + +
+
+
+
+ ${record.confidence}% + + ${formatThreatLevel(record.threatLevel)} + ${formatLocation(record.location)} + + + + + `).join(''); +} + +// 更新分页 +function updatePagination(pagination) { + const paginationElement = document.querySelector('.pagination'); + paginationElement.innerHTML = ` +
  • + 上一页 +
  • + ${generatePageNumbers(pagination)} +
  • + 下一页 +
  • + `; +} + +// 生成页码 +function generatePageNumbers(pagination) { + let pages = []; + const current = pagination.currentPage; + const total = pagination.totalPages; + + if (total <= 5) { + for (let i = 1; i <= total; i++) { + pages.push(i); + } + } else { + if (current <= 3) { + pages = [1, 2, 3, 4, '...', total]; + } else if (current >= total - 2) { + pages = [1, '...', total - 3, total - 2, total - 1, total]; + } else { + pages = [1, '...', current - 1, current, current + 1, '...', total]; + } + } + + return pages.map(page => { + if (page === '...') { + return '
  • ...
  • '; + } + return ` +
  • + ${page} +
  • + `; + }).join(''); +} + +// 显示记录详情 +function showRecordDetail(recordId) { + // TODO: 替换为实际的API调用 + fetch(`/api/records/${recordId}`) + .then(response => response.json()) + .then(record => { + document.getElementById('detailTime').textContent = formatDateTime(record.time); + document.getElementById('detailType').innerHTML = `${formatType(record.type)}`; + document.getElementById('detailResult').textContent = record.result; + document.getElementById('detailConfidence').innerHTML = ` +
    +
    +
    +
    + ${record.confidence}% + `; + document.getElementById('detailThreat').innerHTML = `${formatThreatLevel(record.threatLevel)}`; + document.getElementById('detailLocation').textContent = formatLocation(record.location); + document.getElementById('targetImage').src = record.imageUrl; + + const modal = new bootstrap.Modal(document.getElementById('recordDetailModal')); + modal.show(); + }) + .catch(error => { + console.error('加载记录详情失败:', error); + showError('加载记录详情失败,请稍后重试'); + }); +} + +// 格式化函数 +function formatDateTime(timestamp) { + return new Date(timestamp).toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); +} + +function formatType(type) { + const types = { + 'friendly': '友方', + 'hostile': '敌方', + 'unknown': '未知' + }; + return types[type.toLowerCase()] || type; +} + +function formatThreatLevel(level) { + const levels = { + 'high': '高', + 'medium': '中', + 'low': '低' + }; + return levels[level.toLowerCase()] || level; +} + +function formatLocation(location) { + if (typeof location === 'object') { + return `N ${location.latitude}° E ${location.longitude}°`; + } + return location; +} + +// 辅助函数 +function getConfidenceColor(confidence) { + if (confidence >= 90) return 'success'; + if (confidence >= 70) return 'warning'; + return 'danger'; +} + +function getThreatClass(level) { + const classes = { + 'high': 'danger', + 'medium': 'warning', + 'low': 'success' + }; + return classes[level.toLowerCase()] || 'secondary'; +} + +// 错误提示 +function showError(message) { + // TODO: 实现错误提示UI + console.error(message); +} + +// 导出功能 +document.querySelector('.btn-group .btn-outline-primary').addEventListener('click', function() { + const filters = getFilters(); + // TODO: 实现导出功能 + window.location.href = '/api/records/export?' + new URLSearchParams(filters); +}); + +// 打印功能 +document.querySelectorAll('.btn-group .btn-outline-primary')[1].addEventListener('click', function() { + window.print(); +}); \ No newline at end of file diff --git a/common/js/signal.js b/common/js/signal.js new file mode 100644 index 0000000..bc69b8e --- /dev/null +++ b/common/js/signal.js @@ -0,0 +1,261 @@ +// 页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + initializeSignalCanvas(); + initializeControls(); + setupEventListeners(); +}); + +let isScanning = false; +let animationFrameId = null; +let scanningData = []; +let currentFrequency = 2400; // MHz + +// 初始化信号画布 +function initializeSignalCanvas() { + const canvas = document.getElementById('signalCanvas'); + const ctx = canvas.getContext('2d'); + + // 设置画布大小 + function resizeCanvas() { + const container = canvas.parentElement; + canvas.width = container.clientWidth; + canvas.height = container.clientHeight; + } + + resizeCanvas(); + window.addEventListener('resize', resizeCanvas); + + // 初始化扫描数据 + for (let i = 0; i < 100; i++) { + scanningData.push(Math.random() * 0.5); + } + + // 开始动画循环 + function animate() { + if (!isScanning) return; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + drawSignalWaveform(ctx); + animationFrameId = requestAnimationFrame(animate); + } + + // 绘制信号波形 + function drawSignalWaveform(ctx) { + const width = canvas.width; + const height = canvas.height; + const points = 100; + + // 更新扫描数据 + scanningData.shift(); + scanningData.push(Math.random() * 0.5 + (isScanning ? Math.random() * 0.5 : 0)); + + // 绘制网格 + drawGrid(ctx, width, height); + + // 绘制波形 + ctx.beginPath(); + ctx.strokeStyle = '#00b4d8'; + ctx.lineWidth = 2; + + for (let i = 0; i < points; i++) { + const x = (i / points) * width; + const y = height - (scanningData[i] * height); + + if (i === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + } + + ctx.stroke(); + + // 绘制填充 + ctx.lineTo(width, height); + ctx.lineTo(0, height); + ctx.closePath(); + + const gradient = ctx.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, 'rgba(0, 180, 216, 0.2)'); + gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); + ctx.fillStyle = gradient; + ctx.fill(); + } + + // 绘制网格 + function drawGrid(ctx, width, height) { + const gridSize = 40; + ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; + ctx.lineWidth = 1; + + // 绘制垂直线 + for (let x = 0; x <= width; x += gridSize) { + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, height); + ctx.stroke(); + } + + // 绘制水平线 + for (let y = 0; y <= height; y += gridSize) { + ctx.beginPath(); + ctx.moveTo(0, y); + ctx.lineTo(width, y); + ctx.stroke(); + } + } + + // 导出动画控制函数 + window.startAnimation = function() { + if (!isScanning) { + isScanning = true; + animate(); + } + }; + + window.stopAnimation = function() { + isScanning = false; + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } + }; +} + +// 初始化控制器 +function initializeControls() { + const startButton = document.getElementById('startSignalScan'); + const stopButton = document.getElementById('stopSignalScan'); + const captureButton = document.getElementById('captureSignal'); + const frequencyRange = document.getElementById('frequencyRange'); + const scanSpeed = document.getElementById('scanSpeed'); + + // 开始扫描 + startButton.addEventListener('click', function() { + startButton.disabled = true; + stopButton.disabled = false; + window.startAnimation(); + updateSignalInfo(); + }); + + // 停止扫描 + stopButton.addEventListener('click', function() { + startButton.disabled = false; + stopButton.disabled = true; + window.stopAnimation(); + }); + + // 捕获信号 + captureButton.addEventListener('click', function() { + captureCurrentSignal(); + }); + + // 频率范围选择 + frequencyRange.addEventListener('change', function() { + updateFrequencyRange(this.value); + }); + + // 扫描速度调节 + scanSpeed.addEventListener('input', function() { + updateScanSpeed(this.value); + }); +} + +// 更新信号信息 +function updateSignalInfo() { + if (!isScanning) return; + + // 模拟信号数据更新 + const frequencyStep = Math.random() * 10 - 5; + currentFrequency = Math.max(300, Math.min(5000, currentFrequency + frequencyStep)); + + document.getElementById('currentFrequency').textContent = `${(currentFrequency / 1000).toFixed(2)} GHz`; + document.getElementById('signalStrength').textContent = `${-Math.floor(Math.random() * 20 + 60)} dBm`; + document.getElementById('bandwidth').textContent = `${Math.floor(Math.random() * 10 + 15)} MHz`; + document.getElementById('snr').textContent = `${Math.floor(Math.random() * 10 + 25)} dB`; + + setTimeout(() => { + if (isScanning) { + updateSignalInfo(); + } + }, 1000); +} + +// 捕获当前信号 +function captureCurrentSignal() { + const signalTypes = ['遥控信号', '数据链路', '图传信号', '干扰信号']; + const modulationTypes = ['QPSK', 'OFDM', '16QAM', 'FHSS']; + const encryptionTypes = ['AES-256', 'RSA-2048', 'ECC', '未加密']; + + // 更新信号特征 + document.getElementById('signalType').textContent = signalTypes[Math.floor(Math.random() * signalTypes.length)]; + document.getElementById('modulationType').textContent = modulationTypes[Math.floor(Math.random() * modulationTypes.length)]; + document.getElementById('encryptionType').textContent = encryptionTypes[Math.floor(Math.random() * encryptionTypes.length)]; + + // 显示捕获成功提示 + showToast('信号捕获成功'); +} + +// 更新频率范围 +function updateFrequencyRange(range) { + const ranges = { + 'low': { min: 300, max: 1000 }, + 'mid': { min: 1000, max: 2400 }, + 'high': { min: 2400, max: 5000 } + }; + + if (range !== 'all' && ranges[range]) { + currentFrequency = (ranges[range].min + ranges[range].max) / 2; + } +} + +// 更新扫描速度 +function updateScanSpeed(speed) { + // 实现扫描速度调节逻辑 +} + +// 设置事件监听器 +function setupEventListeners() { + // 全屏切换 + document.getElementById('toggleFullscreen').addEventListener('click', function() { + const signalContainer = document.querySelector('.signal-container'); + if (!document.fullscreenElement) { + signalContainer.requestFullscreen(); + this.innerHTML = ''; + } else { + document.exitFullscreen(); + this.innerHTML = ''; + } + }); + + // 导出记录 + document.querySelector('.btn-outline-primary').addEventListener('click', function() { + // 实现导出功能 + showToast('记录导出成功'); + }); + + // 清除记录 + document.querySelector('.btn-outline-danger').addEventListener('click', function() { + if (confirm('确定要清除所有记录吗?')) { + // 实现清除功能 + showToast('记录已清除'); + } + }); +} + +// 显示提示信息 +function showToast(message) { + // 创建提示元素 + const toast = document.createElement('div'); + toast.className = 'toast-message'; + toast.textContent = message; + document.body.appendChild(toast); + + // 添加动画类 + setTimeout(() => toast.classList.add('show'), 100); + + // 自动移除 + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 3000); +} \ No newline at end of file diff --git a/idx.html b/idx.html new file mode 100644 index 0000000..4bbfb8b --- /dev/null +++ b/idx.html @@ -0,0 +1,3102 @@ + + + + + + 无人机敌我识别系统 + + + + + + +
    +
    + + + + +
    + +
    +
    +
    +
    + +
    +

    无人机敌我识别系统

    +

    军事授权访问 - 请输入您的凭证

    +
    +
    +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    + +
    + 未经授权的访问将被记录并追踪 +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/serve.py b/serve.py new file mode 100644 index 0000000..6df68de --- /dev/null +++ b/serve.py @@ -0,0 +1,33 @@ +import http.server +import socketserver +import os + +# 设置端口 +PORT = 8080 + +# 自定义处理器添加CORS头 +class CORSHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): + def end_headers(self): + # 添加CORS头 + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') + super().end_headers() + + def do_OPTIONS(self): + # 处理OPTIONS请求 + self.send_response(200) + self.end_headers() + +# 打印启动信息 +print(f"启动前端服务器,端口: {PORT}") +print(f"请在浏览器中访问: http://localhost:{PORT}") +print("按Ctrl+C终止服务器") + +# 启动服务器 +with socketserver.TCPServer(("", PORT), CORSHTTPRequestHandler) as httpd: + print("前端服务器正在运行...") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\n服务器已停止") \ No newline at end of file -- 2.34.1 From 5f8b8fc95639f9cdcff6530e945863d3c5f4b12f Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 10:54:54 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard.html | 203 ++++++++++++++++++++ drones.html | 451 ++++++++++++++++++++++++++++++++++++++++++++ identification.html | 266 ++++++++++++++++++++++++++ 3 files changed, 920 insertions(+) create mode 100644 dashboard.html create mode 100644 drones.html create mode 100644 identification.html diff --git a/dashboard.html b/dashboard.html new file mode 100644 index 0000000..caa6286 --- /dev/null +++ b/dashboard.html @@ -0,0 +1,203 @@ + + + + + + 战场态势 - 无人机敌我识别系统 + + + + + + + + +
    +
    +
    +
    +

    战场态势

    +
    + + 2024-01-01 12:00:00 +
    +
    +
    + + 操作员:战术指挥官 + 在线 +
    +
    + +
    +
    +
    +
    +

    执行中无人机

    +
    0
    +
    +
    +
    +
    +
    +
    +

    今日识别数

    +
    0
    +
    +
    +
    +
    +
    +
    +

    友方目标数

    +
    0
    +
    +
    +
    +
    +
    +
    +

    敌方目标数

    +
    0
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + 实时战术态势 +
    +
    + + + +
    +
    +
    +
    +
    + 扫描范围 + 100km +
    +
    + 目标数量 + 12 +
    +
    + 信号强度 + 87% +
    +
    + 高度范围 + 10km +
    +
    +
    +
    +
    + + 友军目标 +
    +
    + + 敌军目标 +
    +
    + + 未识别目标 +
    +
    +
    +
    +
    +
    +
    +
    +
    + 威胁态势分析 +
    +
    + +
    +
    +
    +
    +
    当前威胁等级
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + 战场活动播报 +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/drones.html b/drones.html new file mode 100644 index 0000000..93cd5a5 --- /dev/null +++ b/drones.html @@ -0,0 +1,451 @@ + + + + + + 无人机管理 - 无人机敌我识别系统 + + + + + + + + + + +
    +
    +
    +
    +
    +

    无人机管理

    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    在线无人机
    +

    15

    +
    +
    +
    +
    +
    +
    +
    +
    执行任务中
    +

    9

    +
    +
    +
    +
    +
    +
    +
    +
    维护中
    +

    3

    +
    +
    +
    +
    +
    +
    +
    +
    离线
    +

    2

    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + 无人机位置 +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + 无人机列表 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    编号型号状态电量任务位置操作
    UAV-001侦察者-X1在线 +
    +
    +
    +
    巡逻任务N 39°54'25" E 116°23'50" +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    +
    无人机详情 - UAV-001
    +
    +
    基本信息
    +
    +
    + 型号 +

    侦察者-X1

    +
    +
    + 序列号 +

    SN20240101001

    +
    +
    + 续航时间 +

    4小时

    +
    +
    + 最大载重 +

    5kg

    +
    +
    +
    + +
    +
    实时状态
    +
    +
    + 飞行高度 +

    120m

    +
    +
    + 飞行速度 +

    15m/s

    +
    +
    + 电池状态 +
    +
    +
    + 预计剩余时间: 3小时15分钟 +
    +
    +
    + +
    +
    当前任务
    +
    +
    + 09:00 +

    开始巡逻任务

    +
    +
    + 09:15 +

    到达指定区域A

    +
    +
    + 09:30 +

    执行侦察任务

    +
    +
    +
    + + +
    +
    告警信息
    +
      +
    • + • 电池电量低于20%,建议及时返航 +
    • +
    • + • 天气条件转差,建议调整飞行计划 +
    • +
    +
    +
    + + +
    +
    +
    + 任务分配 +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/identification.html b/identification.html new file mode 100644 index 0000000..370d321 --- /dev/null +++ b/identification.html @@ -0,0 +1,266 @@ + + + + + + 目标识别 - 无人机敌我识别系统 + + + + + + + + +
    +
    +
    +
    +

    目标识别

    +
    + + 2024-01-01 12:00:00 +
    +
    +
    + + 操作员:战术指挥官 + 在线 +
    +
    + +
    +
    +
    +
    +
    + 实时视频流 +
    +
    + + 实时识别中 +
    +
    +
    +
    +
    + +
    +
    + +
    +
    + + 信号强度: 85% +
    +
    + + 方位: 235° +
    +
    + + 目标锁定: 已锁定 +
    +
    +
    +
    +
    +
    + + + +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    + 识别分析 +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 目标信息 +
    +
    +
    +
    +
    +
    + + 军用无人机 +
    +
    + +
    +
    + 85% +
    +
    +
    +
    +
    +
    + + 2.5km +
    +
    + + 120km/h +
    +
    +
    +
    + + 500m +
    +
    + + 235° +
    +
    +
    +
    + + +
    +
    + + 跟踪中 +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    + 最近识别 +
    + +
    +
    +
    +
    +
    10:30:15
    +
    +
    敌方无人机
    +
    距离: 3.2km / 速度: 150km/h
    +
    +
    已确认
    +
    +
    +
    10:28:45
    +
    +
    友方战机
    +
    距离: 5.1km / 速度: 280km/h
    +
    +
    已确认
    +
    +
    +
    10:25:30
    +
    +
    未知目标
    +
    距离: 8.7km / 速度: 90km/h
    +
    +
    分析中
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + \ No newline at end of file -- 2.34.1 From 4195d39530f325d9a0af01ae71873aa09f5fb5ce Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 10:56:36 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 14 +++ login.html | 75 +++++++++++++ records.html | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+) create mode 100644 index.html create mode 100644 login.html create mode 100644 records.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..8eab4c0 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + 无人机敌我识别系统 + + + + 正在跳转到登录页面... + + \ No newline at end of file diff --git a/login.html b/login.html new file mode 100644 index 0000000..16e30e3 --- /dev/null +++ b/login.html @@ -0,0 +1,75 @@ + + + + + + 登录 - 无人机敌我识别系统 + + + + + + + +
    +
    +
    +
    + +
    +
    + + + +
    +
    + + + + + + \ No newline at end of file diff --git a/records.html b/records.html new file mode 100644 index 0000000..a1bc535 --- /dev/null +++ b/records.html @@ -0,0 +1,310 @@ + + + + + + 识别记录 - 无人机敌我识别系统 + + + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    2,458
    +
    总识别次数
    +
    +
    +
    +
    +
    + +
    +
    1,245
    +
    友方目标
    +
    +
    +
    +
    +
    + +
    +
    892
    +
    敌方目标
    +
    +
    +
    +
    +
    + +
    +
    321
    +
    未知目标
    +
    +
    +
    + + +
    +
    +
    +
    +
    + 筛选条件 +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + 识别记录列表 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    时间目标类型识别结果置信度威胁等级位置操作
    2024-01-15 14:30:25友方侦察无人机98%N 39°54'25" E 116°23'50" + +
    2024-01-15 14:28:15敌方攻击无人机95%N 39°54'28" E 116°23'52" + +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + \ No newline at end of file -- 2.34.1 From 339f989f57b2eba49ff65daa683b07e9e9744175 Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 10:57:14 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- signal.html | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 signal.html diff --git a/signal.html b/signal.html new file mode 100644 index 0000000..e4381c4 --- /dev/null +++ b/signal.html @@ -0,0 +1,333 @@ + + + + + + 信号识别 - 无人机敌我识别系统 + + + + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    24
    +
    活跃信号源
    +
    +
    +
    +
    +
    + +
    +
    12
    +
    友方信号
    +
    +
    +
    +
    +
    + +
    +
    8
    +
    可疑信号
    +
    +
    +
    +
    +
    + +
    +
    4
    +
    干扰源
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 信号波形分析 +
    +
    + +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    1.2 GHz
    +
    +
    +
    +
    2.4 GHz
    +
    +
    +
    +
    +
    +
    +
    + + 2.4 GHz +
    +
    +
    +
    + + -65 dBm +
    +
    +
    +
    + + 20 MHz +
    +
    +
    +
    + + 32 dB +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 信号特征 +
    +
    +
    +
    +
    + + QPSK +
    +
    + + 遥控信号 +
    +
    + + AES-256 +
    +
    + +
    +
    + 85% +
    +
    +
    +
    + +
    +
    + 25% +
    +
    +
    +
    +
    +
    + +
    +
    +
    + 威胁分析 +
    +
    +
    +
    +
    + + 中等 +
    +
    +
    威胁详情
    +
      +
    • + + 检测到可疑的加密通信 +
    • +
    • + + 信号特征与已知敌方设备部分匹配 +
    • +
    • + + 通信协议已被识别 +
    • +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 信号记录 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    时间频率信号类型信号强度威胁等级状态操作
    2024-01-15 15:30:252.4 GHz遥控信号-65 dBm中等已识别 + +
    2024-01-15 15:28:151.8 GHz数据链路-72 dBm分析中 + +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file -- 2.34.1 From 4cbada21192ea6cadbf878f8482c66df4eb1a8ae Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 11:01:51 +0800 Subject: [PATCH 05/10] delete --- dashboard.html | 203 --- drones.html | 451 ------- identification.html | 266 ---- idx.html | 3102 ------------------------------------------- index.html | 14 - login.html | 75 -- records.html | 310 ----- signal.html | 333 ----- 8 files changed, 4754 deletions(-) delete mode 100644 dashboard.html delete mode 100644 drones.html delete mode 100644 identification.html delete mode 100644 idx.html delete mode 100644 index.html delete mode 100644 login.html delete mode 100644 records.html delete mode 100644 signal.html diff --git a/dashboard.html b/dashboard.html deleted file mode 100644 index caa6286..0000000 --- a/dashboard.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - 战场态势 - 无人机敌我识别系统 - - - - - - - - -
    -
    -
    -
    -

    战场态势

    -
    - - 2024-01-01 12:00:00 -
    -
    -
    - - 操作员:战术指挥官 - 在线 -
    -
    - -
    -
    -
    -
    -

    执行中无人机

    -
    0
    -
    -
    -
    -
    -
    -
    -

    今日识别数

    -
    0
    -
    -
    -
    -
    -
    -
    -

    友方目标数

    -
    0
    -
    -
    -
    -
    -
    -
    -

    敌方目标数

    -
    0
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - 实时战术态势 -
    -
    - - - -
    -
    -
    -
    -
    - 扫描范围 - 100km -
    -
    - 目标数量 - 12 -
    -
    - 信号强度 - 87% -
    -
    - 高度范围 - 10km -
    -
    -
    -
    -
    - - 友军目标 -
    -
    - - 敌军目标 -
    -
    - - 未识别目标 -
    -
    -
    -
    -
    -
    -
    -
    -
    - 威胁态势分析 -
    -
    - -
    -
    -
    -
    -
    当前威胁等级
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - 战场活动播报 -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - \ No newline at end of file diff --git a/drones.html b/drones.html deleted file mode 100644 index 93cd5a5..0000000 --- a/drones.html +++ /dev/null @@ -1,451 +0,0 @@ - - - - - - 无人机管理 - 无人机敌我识别系统 - - - - - - - - - - -
    -
    -
    -
    -
    -

    无人机管理

    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    在线无人机
    -

    15

    -
    -
    -
    -
    -
    -
    -
    -
    执行任务中
    -

    9

    -
    -
    -
    -
    -
    -
    -
    -
    维护中
    -

    3

    -
    -
    -
    -
    -
    -
    -
    -
    离线
    -

    2

    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - 无人机位置 -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - 无人机列表 -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    编号型号状态电量任务位置操作
    UAV-001侦察者-X1在线 -
    -
    -
    -
    巡逻任务N 39°54'25" E 116°23'50" -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    -
    无人机详情 - UAV-001
    -
    -
    基本信息
    -
    -
    - 型号 -

    侦察者-X1

    -
    -
    - 序列号 -

    SN20240101001

    -
    -
    - 续航时间 -

    4小时

    -
    -
    - 最大载重 -

    5kg

    -
    -
    -
    - -
    -
    实时状态
    -
    -
    - 飞行高度 -

    120m

    -
    -
    - 飞行速度 -

    15m/s

    -
    -
    - 电池状态 -
    -
    -
    - 预计剩余时间: 3小时15分钟 -
    -
    -
    - -
    -
    当前任务
    -
    -
    - 09:00 -

    开始巡逻任务

    -
    -
    - 09:15 -

    到达指定区域A

    -
    -
    - 09:30 -

    执行侦察任务

    -
    -
    -
    - - -
    -
    告警信息
    -
      -
    • - • 电池电量低于20%,建议及时返航 -
    • -
    • - • 天气条件转差,建议调整飞行计划 -
    • -
    -
    -
    - - -
    -
    -
    - 任务分配 -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - \ No newline at end of file diff --git a/identification.html b/identification.html deleted file mode 100644 index 370d321..0000000 --- a/identification.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - 目标识别 - 无人机敌我识别系统 - - - - - - - - -
    -
    -
    -
    -

    目标识别

    -
    - - 2024-01-01 12:00:00 -
    -
    -
    - - 操作员:战术指挥官 - 在线 -
    -
    - -
    -
    -
    -
    -
    - 实时视频流 -
    -
    - - 实时识别中 -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - - 信号强度: 85% -
    -
    - - 方位: 235° -
    -
    - - 目标锁定: 已锁定 -
    -
    -
    -
    -
    -
    - - - -
    -
    - - - -
    -
    -
    -
    - -
    -
    -
    - 识别分析 -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - 目标信息 -
    -
    -
    -
    -
    -
    - - 军用无人机 -
    -
    - -
    -
    - 85% -
    -
    -
    -
    -
    -
    - - 2.5km -
    -
    - - 120km/h -
    -
    -
    -
    - - 500m -
    -
    - - 235° -
    -
    -
    -
    - - -
    -
    - - 跟踪中 -
    -
    -
    - - - -
    -
    -
    -
    - -
    -
    -
    - 最近识别 -
    - -
    -
    -
    -
    -
    10:30:15
    -
    -
    敌方无人机
    -
    距离: 3.2km / 速度: 150km/h
    -
    -
    已确认
    -
    -
    -
    10:28:45
    -
    -
    友方战机
    -
    距离: 5.1km / 速度: 280km/h
    -
    -
    已确认
    -
    -
    -
    10:25:30
    -
    -
    未知目标
    -
    距离: 8.7km / 速度: 90km/h
    -
    -
    分析中
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - \ No newline at end of file diff --git a/idx.html b/idx.html deleted file mode 100644 index 4bbfb8b..0000000 --- a/idx.html +++ /dev/null @@ -1,3102 +0,0 @@ - - - - - - 无人机敌我识别系统 - - - - - - -
    -
    - - - - -
    - -
    -
    -
    -
    - -
    -

    无人机敌我识别系统

    -

    军事授权访问 - 请输入您的凭证

    -
    -
    -
    - -
    - - -
    -
    -
    - -
    - - -
    -
    - -
    - 未经授权的访问将被记录并追踪 -
    -
    -
    -
    - - - - - - - - - - - - - - - -
    -
    -
    - - - - - - - - - - - - - \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 8eab4c0..0000000 --- a/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - 无人机敌我识别系统 - - - - 正在跳转到登录页面... - - \ No newline at end of file diff --git a/login.html b/login.html deleted file mode 100644 index 16e30e3..0000000 --- a/login.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - 登录 - 无人机敌我识别系统 - - - - - - - -
    -
    -
    -
    - -
    -
    - - - -
    -
    - - - - - - \ No newline at end of file diff --git a/records.html b/records.html deleted file mode 100644 index a1bc535..0000000 --- a/records.html +++ /dev/null @@ -1,310 +0,0 @@ - - - - - - 识别记录 - 无人机敌我识别系统 - - - - - - - - -
    -
    - -
    -
    -
    -
    - -
    -
    2,458
    -
    总识别次数
    -
    -
    -
    -
    -
    - -
    -
    1,245
    -
    友方目标
    -
    -
    -
    -
    -
    - -
    -
    892
    -
    敌方目标
    -
    -
    -
    -
    -
    - -
    -
    321
    -
    未知目标
    -
    -
    -
    - - -
    -
    -
    -
    -
    - 筛选条件 -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - 识别记录列表 -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    时间目标类型识别结果置信度威胁等级位置操作
    2024-01-15 14:30:25友方侦察无人机98%N 39°54'25" E 116°23'50" - -
    2024-01-15 14:28:15敌方攻击无人机95%N 39°54'28" E 116°23'52" - -
    -
    - -
    -
    -
    -
    -
    -
    - - - - - - - - - \ No newline at end of file diff --git a/signal.html b/signal.html deleted file mode 100644 index e4381c4..0000000 --- a/signal.html +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - 信号识别 - 无人机敌我识别系统 - - - - - - - - - -
    -
    - -
    -
    -
    -
    - -
    -
    24
    -
    活跃信号源
    -
    -
    -
    -
    -
    - -
    -
    12
    -
    友方信号
    -
    -
    -
    -
    -
    - -
    -
    8
    -
    可疑信号
    -
    -
    -
    -
    -
    - -
    -
    4
    -
    干扰源
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - 信号波形分析 -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    - -
    -
    -
    -
    1.2 GHz
    -
    -
    -
    -
    2.4 GHz
    -
    -
    -
    -
    -
    -
    -
    - - 2.4 GHz -
    -
    -
    -
    - - -65 dBm -
    -
    -
    -
    - - 20 MHz -
    -
    -
    -
    - - 32 dB -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - 信号特征 -
    -
    -
    -
    -
    - - QPSK -
    -
    - - 遥控信号 -
    -
    - - AES-256 -
    -
    - -
    -
    - 85% -
    -
    -
    -
    - -
    -
    - 25% -
    -
    -
    -
    -
    -
    - -
    -
    -
    - 威胁分析 -
    -
    -
    -
    -
    - - 中等 -
    -
    -
    威胁详情
    -
      -
    • - - 检测到可疑的加密通信 -
    • -
    • - - 信号特征与已知敌方设备部分匹配 -
    • -
    • - - 通信协议已被识别 -
    • -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - 信号记录 -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    时间频率信号类型信号强度威胁等级状态操作
    2024-01-15 15:30:252.4 GHz遥控信号-65 dBm中等已识别 - -
    2024-01-15 15:28:151.8 GHz数据链路-72 dBm分析中 - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - \ No newline at end of file -- 2.34.1 From f5c7f7156f35d016d6963038cfee541f12266883 Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 11:02:59 +0800 Subject: [PATCH 06/10] 2.0 --- dashboard.html | 203 +++ drones.html | 451 +++++++ identification.html | 266 ++++ idx.html | 3102 +++++++++++++++++++++++++++++++++++++++++++ index.html | 14 + login.html | 75 ++ records.html | 310 +++++ signal.html | 333 +++++ 8 files changed, 4754 insertions(+) create mode 100644 dashboard.html create mode 100644 drones.html create mode 100644 identification.html create mode 100644 idx.html create mode 100644 index.html create mode 100644 login.html create mode 100644 records.html create mode 100644 signal.html diff --git a/dashboard.html b/dashboard.html new file mode 100644 index 0000000..caa6286 --- /dev/null +++ b/dashboard.html @@ -0,0 +1,203 @@ + + + + + + 战场态势 - 无人机敌我识别系统 + + + + + + + + +
    +
    +
    +
    +

    战场态势

    +
    + + 2024-01-01 12:00:00 +
    +
    +
    + + 操作员:战术指挥官 + 在线 +
    +
    + +
    +
    +
    +
    +

    执行中无人机

    +
    0
    +
    +
    +
    +
    +
    +
    +

    今日识别数

    +
    0
    +
    +
    +
    +
    +
    +
    +

    友方目标数

    +
    0
    +
    +
    +
    +
    +
    +
    +

    敌方目标数

    +
    0
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + 实时战术态势 +
    +
    + + + +
    +
    +
    +
    +
    + 扫描范围 + 100km +
    +
    + 目标数量 + 12 +
    +
    + 信号强度 + 87% +
    +
    + 高度范围 + 10km +
    +
    +
    +
    +
    + + 友军目标 +
    +
    + + 敌军目标 +
    +
    + + 未识别目标 +
    +
    +
    +
    +
    +
    +
    +
    +
    + 威胁态势分析 +
    +
    + +
    +
    +
    +
    +
    当前威胁等级
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + 战场活动播报 +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/drones.html b/drones.html new file mode 100644 index 0000000..93cd5a5 --- /dev/null +++ b/drones.html @@ -0,0 +1,451 @@ + + + + + + 无人机管理 - 无人机敌我识别系统 + + + + + + + + + + +
    +
    +
    +
    +
    +

    无人机管理

    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    在线无人机
    +

    15

    +
    +
    +
    +
    +
    +
    +
    +
    执行任务中
    +

    9

    +
    +
    +
    +
    +
    +
    +
    +
    维护中
    +

    3

    +
    +
    +
    +
    +
    +
    +
    +
    离线
    +

    2

    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + 无人机位置 +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + 无人机列表 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    编号型号状态电量任务位置操作
    UAV-001侦察者-X1在线 +
    +
    +
    +
    巡逻任务N 39°54'25" E 116°23'50" +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    +
    无人机详情 - UAV-001
    +
    +
    基本信息
    +
    +
    + 型号 +

    侦察者-X1

    +
    +
    + 序列号 +

    SN20240101001

    +
    +
    + 续航时间 +

    4小时

    +
    +
    + 最大载重 +

    5kg

    +
    +
    +
    + +
    +
    实时状态
    +
    +
    + 飞行高度 +

    120m

    +
    +
    + 飞行速度 +

    15m/s

    +
    +
    + 电池状态 +
    +
    +
    + 预计剩余时间: 3小时15分钟 +
    +
    +
    + +
    +
    当前任务
    +
    +
    + 09:00 +

    开始巡逻任务

    +
    +
    + 09:15 +

    到达指定区域A

    +
    +
    + 09:30 +

    执行侦察任务

    +
    +
    +
    + + +
    +
    告警信息
    +
      +
    • + • 电池电量低于20%,建议及时返航 +
    • +
    • + • 天气条件转差,建议调整飞行计划 +
    • +
    +
    +
    + + +
    +
    +
    + 任务分配 +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/identification.html b/identification.html new file mode 100644 index 0000000..370d321 --- /dev/null +++ b/identification.html @@ -0,0 +1,266 @@ + + + + + + 目标识别 - 无人机敌我识别系统 + + + + + + + + +
    +
    +
    +
    +

    目标识别

    +
    + + 2024-01-01 12:00:00 +
    +
    +
    + + 操作员:战术指挥官 + 在线 +
    +
    + +
    +
    +
    +
    +
    + 实时视频流 +
    +
    + + 实时识别中 +
    +
    +
    +
    +
    + +
    +
    + +
    +
    + + 信号强度: 85% +
    +
    + + 方位: 235° +
    +
    + + 目标锁定: 已锁定 +
    +
    +
    +
    +
    +
    + + + +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    + 识别分析 +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 目标信息 +
    +
    +
    +
    +
    +
    + + 军用无人机 +
    +
    + +
    +
    + 85% +
    +
    +
    +
    +
    +
    + + 2.5km +
    +
    + + 120km/h +
    +
    +
    +
    + + 500m +
    +
    + + 235° +
    +
    +
    +
    + + +
    +
    + + 跟踪中 +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    + 最近识别 +
    + +
    +
    +
    +
    +
    10:30:15
    +
    +
    敌方无人机
    +
    距离: 3.2km / 速度: 150km/h
    +
    +
    已确认
    +
    +
    +
    10:28:45
    +
    +
    友方战机
    +
    距离: 5.1km / 速度: 280km/h
    +
    +
    已确认
    +
    +
    +
    10:25:30
    +
    +
    未知目标
    +
    距离: 8.7km / 速度: 90km/h
    +
    +
    分析中
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/idx.html b/idx.html new file mode 100644 index 0000000..4bbfb8b --- /dev/null +++ b/idx.html @@ -0,0 +1,3102 @@ + + + + + + 无人机敌我识别系统 + + + + + + +
    +
    + + + + +
    + +
    +
    +
    +
    + +
    +

    无人机敌我识别系统

    +

    军事授权访问 - 请输入您的凭证

    +
    +
    +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    + +
    + 未经授权的访问将被记录并追踪 +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..8eab4c0 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + 无人机敌我识别系统 + + + + 正在跳转到登录页面... + + \ No newline at end of file diff --git a/login.html b/login.html new file mode 100644 index 0000000..16e30e3 --- /dev/null +++ b/login.html @@ -0,0 +1,75 @@ + + + + + + 登录 - 无人机敌我识别系统 + + + + + + + +
    +
    +
    +
    + +
    +
    + + + +
    +
    + + + + + + \ No newline at end of file diff --git a/records.html b/records.html new file mode 100644 index 0000000..a1bc535 --- /dev/null +++ b/records.html @@ -0,0 +1,310 @@ + + + + + + 识别记录 - 无人机敌我识别系统 + + + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    2,458
    +
    总识别次数
    +
    +
    +
    +
    +
    + +
    +
    1,245
    +
    友方目标
    +
    +
    +
    +
    +
    + +
    +
    892
    +
    敌方目标
    +
    +
    +
    +
    +
    + +
    +
    321
    +
    未知目标
    +
    +
    +
    + + +
    +
    +
    +
    +
    + 筛选条件 +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + 识别记录列表 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    时间目标类型识别结果置信度威胁等级位置操作
    2024-01-15 14:30:25友方侦察无人机98%N 39°54'25" E 116°23'50" + +
    2024-01-15 14:28:15敌方攻击无人机95%N 39°54'28" E 116°23'52" + +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + \ No newline at end of file diff --git a/signal.html b/signal.html new file mode 100644 index 0000000..e4381c4 --- /dev/null +++ b/signal.html @@ -0,0 +1,333 @@ + + + + + + 信号识别 - 无人机敌我识别系统 + + + + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    24
    +
    活跃信号源
    +
    +
    +
    +
    +
    + +
    +
    12
    +
    友方信号
    +
    +
    +
    +
    +
    + +
    +
    8
    +
    可疑信号
    +
    +
    +
    +
    +
    + +
    +
    4
    +
    干扰源
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 信号波形分析 +
    +
    + +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    1.2 GHz
    +
    +
    +
    +
    2.4 GHz
    +
    +
    +
    +
    +
    +
    +
    + + 2.4 GHz +
    +
    +
    +
    + + -65 dBm +
    +
    +
    +
    + + 20 MHz +
    +
    +
    +
    + + 32 dB +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 信号特征 +
    +
    +
    +
    +
    + + QPSK +
    +
    + + 遥控信号 +
    +
    + + AES-256 +
    +
    + +
    +
    + 85% +
    +
    +
    +
    + +
    +
    + 25% +
    +
    +
    +
    +
    +
    + +
    +
    +
    + 威胁分析 +
    +
    +
    +
    +
    + + 中等 +
    +
    +
    威胁详情
    +
      +
    • + + 检测到可疑的加密通信 +
    • +
    • + + 信号特征与已知敌方设备部分匹配 +
    • +
    • + + 通信协议已被识别 +
    • +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 信号记录 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    时间频率信号类型信号强度威胁等级状态操作
    2024-01-15 15:30:252.4 GHz遥控信号-65 dBm中等已识别 + +
    2024-01-15 15:28:151.8 GHz数据链路-72 dBm分析中 + +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file -- 2.34.1 From d255a833a7ceb79835a3034f22b384097f4988db Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 11:03:35 +0800 Subject: [PATCH 07/10] delete --- dashboard.html | 203 --- drones.html | 451 ------- identification.html | 266 ---- idx.html | 3102 ------------------------------------------- index.html | 14 - login.html | 75 -- records.html | 310 ----- signal.html | 333 ----- 8 files changed, 4754 deletions(-) delete mode 100644 dashboard.html delete mode 100644 drones.html delete mode 100644 identification.html delete mode 100644 idx.html delete mode 100644 index.html delete mode 100644 login.html delete mode 100644 records.html delete mode 100644 signal.html diff --git a/dashboard.html b/dashboard.html deleted file mode 100644 index caa6286..0000000 --- a/dashboard.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - 战场态势 - 无人机敌我识别系统 - - - - - - - - -
    -
    -
    -
    -

    战场态势

    -
    - - 2024-01-01 12:00:00 -
    -
    -
    - - 操作员:战术指挥官 - 在线 -
    -
    - -
    -
    -
    -
    -

    执行中无人机

    -
    0
    -
    -
    -
    -
    -
    -
    -

    今日识别数

    -
    0
    -
    -
    -
    -
    -
    -
    -

    友方目标数

    -
    0
    -
    -
    -
    -
    -
    -
    -

    敌方目标数

    -
    0
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - 实时战术态势 -
    -
    - - - -
    -
    -
    -
    -
    - 扫描范围 - 100km -
    -
    - 目标数量 - 12 -
    -
    - 信号强度 - 87% -
    -
    - 高度范围 - 10km -
    -
    -
    -
    -
    - - 友军目标 -
    -
    - - 敌军目标 -
    -
    - - 未识别目标 -
    -
    -
    -
    -
    -
    -
    -
    -
    - 威胁态势分析 -
    -
    - -
    -
    -
    -
    -
    当前威胁等级
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - 战场活动播报 -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - \ No newline at end of file diff --git a/drones.html b/drones.html deleted file mode 100644 index 93cd5a5..0000000 --- a/drones.html +++ /dev/null @@ -1,451 +0,0 @@ - - - - - - 无人机管理 - 无人机敌我识别系统 - - - - - - - - - - -
    -
    -
    -
    -
    -

    无人机管理

    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    在线无人机
    -

    15

    -
    -
    -
    -
    -
    -
    -
    -
    执行任务中
    -

    9

    -
    -
    -
    -
    -
    -
    -
    -
    维护中
    -

    3

    -
    -
    -
    -
    -
    -
    -
    -
    离线
    -

    2

    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - 无人机位置 -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - 无人机列表 -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    编号型号状态电量任务位置操作
    UAV-001侦察者-X1在线 -
    -
    -
    -
    巡逻任务N 39°54'25" E 116°23'50" -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    -
    无人机详情 - UAV-001
    -
    -
    基本信息
    -
    -
    - 型号 -

    侦察者-X1

    -
    -
    - 序列号 -

    SN20240101001

    -
    -
    - 续航时间 -

    4小时

    -
    -
    - 最大载重 -

    5kg

    -
    -
    -
    - -
    -
    实时状态
    -
    -
    - 飞行高度 -

    120m

    -
    -
    - 飞行速度 -

    15m/s

    -
    -
    - 电池状态 -
    -
    -
    - 预计剩余时间: 3小时15分钟 -
    -
    -
    - -
    -
    当前任务
    -
    -
    - 09:00 -

    开始巡逻任务

    -
    -
    - 09:15 -

    到达指定区域A

    -
    -
    - 09:30 -

    执行侦察任务

    -
    -
    -
    - - -
    -
    告警信息
    -
      -
    • - • 电池电量低于20%,建议及时返航 -
    • -
    • - • 天气条件转差,建议调整飞行计划 -
    • -
    -
    -
    - - -
    -
    -
    - 任务分配 -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - \ No newline at end of file diff --git a/identification.html b/identification.html deleted file mode 100644 index 370d321..0000000 --- a/identification.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - 目标识别 - 无人机敌我识别系统 - - - - - - - - -
    -
    -
    -
    -

    目标识别

    -
    - - 2024-01-01 12:00:00 -
    -
    -
    - - 操作员:战术指挥官 - 在线 -
    -
    - -
    -
    -
    -
    -
    - 实时视频流 -
    -
    - - 实时识别中 -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - - 信号强度: 85% -
    -
    - - 方位: 235° -
    -
    - - 目标锁定: 已锁定 -
    -
    -
    -
    -
    -
    - - - -
    -
    - - - -
    -
    -
    -
    - -
    -
    -
    - 识别分析 -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - 目标信息 -
    -
    -
    -
    -
    -
    - - 军用无人机 -
    -
    - -
    -
    - 85% -
    -
    -
    -
    -
    -
    - - 2.5km -
    -
    - - 120km/h -
    -
    -
    -
    - - 500m -
    -
    - - 235° -
    -
    -
    -
    - - -
    -
    - - 跟踪中 -
    -
    -
    - - - -
    -
    -
    -
    - -
    -
    -
    - 最近识别 -
    - -
    -
    -
    -
    -
    10:30:15
    -
    -
    敌方无人机
    -
    距离: 3.2km / 速度: 150km/h
    -
    -
    已确认
    -
    -
    -
    10:28:45
    -
    -
    友方战机
    -
    距离: 5.1km / 速度: 280km/h
    -
    -
    已确认
    -
    -
    -
    10:25:30
    -
    -
    未知目标
    -
    距离: 8.7km / 速度: 90km/h
    -
    -
    分析中
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - \ No newline at end of file diff --git a/idx.html b/idx.html deleted file mode 100644 index 4bbfb8b..0000000 --- a/idx.html +++ /dev/null @@ -1,3102 +0,0 @@ - - - - - - 无人机敌我识别系统 - - - - - - -
    -
    - - - - -
    - -
    -
    -
    -
    - -
    -

    无人机敌我识别系统

    -

    军事授权访问 - 请输入您的凭证

    -
    -
    -
    - -
    - - -
    -
    -
    - -
    - - -
    -
    - -
    - 未经授权的访问将被记录并追踪 -
    -
    -
    -
    - - - - - - - - - - - - - - - -
    -
    -
    - - - - - - - - - - - - - \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 8eab4c0..0000000 --- a/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - 无人机敌我识别系统 - - - - 正在跳转到登录页面... - - \ No newline at end of file diff --git a/login.html b/login.html deleted file mode 100644 index 16e30e3..0000000 --- a/login.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - 登录 - 无人机敌我识别系统 - - - - - - - -
    -
    -
    -
    - -
    -
    - - - -
    -
    - - - - - - \ No newline at end of file diff --git a/records.html b/records.html deleted file mode 100644 index a1bc535..0000000 --- a/records.html +++ /dev/null @@ -1,310 +0,0 @@ - - - - - - 识别记录 - 无人机敌我识别系统 - - - - - - - - -
    -
    - -
    -
    -
    -
    - -
    -
    2,458
    -
    总识别次数
    -
    -
    -
    -
    -
    - -
    -
    1,245
    -
    友方目标
    -
    -
    -
    -
    -
    - -
    -
    892
    -
    敌方目标
    -
    -
    -
    -
    -
    - -
    -
    321
    -
    未知目标
    -
    -
    -
    - - -
    -
    -
    -
    -
    - 筛选条件 -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - 识别记录列表 -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    时间目标类型识别结果置信度威胁等级位置操作
    2024-01-15 14:30:25友方侦察无人机98%N 39°54'25" E 116°23'50" - -
    2024-01-15 14:28:15敌方攻击无人机95%N 39°54'28" E 116°23'52" - -
    -
    - -
    -
    -
    -
    -
    -
    - - - - - - - - - \ No newline at end of file diff --git a/signal.html b/signal.html deleted file mode 100644 index e4381c4..0000000 --- a/signal.html +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - 信号识别 - 无人机敌我识别系统 - - - - - - - - - -
    -
    - -
    -
    -
    -
    - -
    -
    24
    -
    活跃信号源
    -
    -
    -
    -
    -
    - -
    -
    12
    -
    友方信号
    -
    -
    -
    -
    -
    - -
    -
    8
    -
    可疑信号
    -
    -
    -
    -
    -
    - -
    -
    4
    -
    干扰源
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - 信号波形分析 -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    - -
    -
    -
    -
    1.2 GHz
    -
    -
    -
    -
    2.4 GHz
    -
    -
    -
    -
    -
    -
    -
    - - 2.4 GHz -
    -
    -
    -
    - - -65 dBm -
    -
    -
    -
    - - 20 MHz -
    -
    -
    -
    - - 32 dB -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - 信号特征 -
    -
    -
    -
    -
    - - QPSK -
    -
    - - 遥控信号 -
    -
    - - AES-256 -
    -
    - -
    -
    - 85% -
    -
    -
    -
    - -
    -
    - 25% -
    -
    -
    -
    -
    -
    - -
    -
    -
    - 威胁分析 -
    -
    -
    -
    -
    - - 中等 -
    -
    -
    威胁详情
    -
      -
    • - - 检测到可疑的加密通信 -
    • -
    • - - 信号特征与已知敌方设备部分匹配 -
    • -
    • - - 通信协议已被识别 -
    • -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - 信号记录 -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    时间频率信号类型信号强度威胁等级状态操作
    2024-01-15 15:30:252.4 GHz遥控信号-65 dBm中等已识别 - -
    2024-01-15 15:28:151.8 GHz数据链路-72 dBm分析中 - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - \ No newline at end of file -- 2.34.1 From 95bbc2790290096d1dafd0ee87b99c5b300adace Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 11:04:16 +0800 Subject: [PATCH 08/10] delete --- common/css/style.css | 1612 ----------------------------------- common/js/api.js | 200 ----- common/js/battlefield.js | 732 ---------------- common/js/common.js | 169 ---- common/js/drones.js | 341 -------- common/js/identification.js | 500 ----------- common/js/signal.js | 261 ------ 7 files changed, 3815 deletions(-) delete mode 100644 common/css/style.css delete mode 100644 common/js/api.js delete mode 100644 common/js/battlefield.js delete mode 100644 common/js/common.js delete mode 100644 common/js/drones.js delete mode 100644 common/js/identification.js delete mode 100644 common/js/signal.js diff --git a/common/css/style.css b/common/css/style.css deleted file mode 100644 index 02f3d91..0000000 --- a/common/css/style.css +++ /dev/null @@ -1,1612 +0,0 @@ -/* 全局样式 */ -:root { - --primary-color: #1a2634; - --secondary-color: #2c3e50; - --accent-color: #00b4d8; - --text-color: #e9ecef; - --border-color: #2d4356; - --card-bg: rgba(26, 38, 52, 0.8); - --success-color: #2ecc71; - --warning-color: #f1c40f; - --danger-color: #e74c3c; - --info-color: #3498db; - --executing-color: #3498db; - --identification-color: #2ecc71; - --friendly-color: #27ae60; - --hostile-color: #e74c3c; -} - -body { - font-family: 'Noto Sans SC', sans-serif; - background-color: var(--primary-color); - color: var(--text-color); - min-height: 100vh; -} - -.battlefield-theme { - background: - linear-gradient(135deg, rgba(26, 38, 52, 0.95) 0%, rgba(44, 62, 80, 0.95) 100%), - url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%230a4b78' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"), - url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%230a4b78' fill-opacity='0.05'%3E%3Cpath d='M50 50l30-30M50 50l-30 30M50 50l30 30M50 50l-30-30'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); - background-attachment: fixed; -} - -/* 动画效果 */ -@keyframes pulse { - 0% { - transform: scale(1); - opacity: 1; - } - 50% { - transform: scale(1.1); - opacity: 0.8; - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes glow { - 0% { - box-shadow: 0 0 5px rgba(0, 180, 216, 0.2); - } - 50% { - box-shadow: 0 0 20px rgba(0, 180, 216, 0.4); - } - 100% { - box-shadow: 0 0 5px rgba(0, 180, 216, 0.2); - } -} - -/* 侧边栏样式 */ -.sidebar { - position: fixed; - left: 0; - top: 0; - bottom: 0; - width: 250px; - background-color: var(--secondary-color); - border-right: 1px solid var(--border-color); - padding: 1rem; - z-index: 1000; -} - -.sidebar-header { - padding: 1rem 0; - border-bottom: 1px solid var(--border-color); -} - -.sidebar-header h4 { - color: var(--text-color); - margin: 0; - display: flex; - align-items: center; -} - -.logo-icon { - margin-right: 0.5rem; - color: var(--accent-color); -} - -.nav-link { - color: var(--text-color); - padding: 0.8rem 1rem; - margin: 0.2rem 0; - border-radius: 5px; - transition: all 0.3s ease; -} - -.nav-link:hover, -.nav-link.active { - background-color: var(--accent-color); - color: white; -} - -.nav-link i { - margin-right: 0.5rem; - width: 20px; - text-align: center; -} - -/* 主内容区域 */ -.main-content { - margin-left: 250px; - padding: 1rem; - min-height: 100vh; - max-height: 100vh; - overflow: hidden; -} - -/* 战场头部 */ -.battlefield-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - padding: 0.5rem 1rem; - background: var(--card-bg); - border-radius: 10px; - border: 1px solid var(--border-color); -} - -.header-left { - display: flex; - align-items: center; - gap: 1.5rem; -} - -.battlefield-time { - display: flex; - align-items: center; - gap: 0.5rem; - color: var(--accent-color); - font-size: 0.9rem; -} - -.operator-info { - display: flex; - align-items: center; - gap: 0.5rem; - color: var(--text-color); -} - -.status-badge { - padding: 0.25rem 0.5rem; - border-radius: 20px; - font-size: 0.8rem; - font-weight: 500; -} - -.status-badge.online { - background: rgba(46, 204, 113, 0.2); - color: var(--success-color); -} - -.page-title { - color: var(--text-color); - margin: 0; - font-weight: 500; -} - -/* 状态卡片 */ -.status-cards { - margin-bottom: 1rem; -} - -.status-cards .col-md-3 { - padding: 0.4rem; - animation: fadeIn 0.5s ease-out forwards; -} - -.status-cards .col-md-3:nth-child(1) { animation-delay: 0s; } -.status-cards .col-md-3:nth-child(2) { animation-delay: 0.2s; } -.status-cards .col-md-3:nth-child(3) { animation-delay: 0.4s; } -.status-cards .col-md-3:nth-child(4) { animation-delay: 0.6s; } - -.status-card { - background: var(--card-bg); - border-radius: 8px; - padding: 12px; - margin-bottom: 0.8rem; - position: relative; - overflow: hidden; - min-height: 90px; - border: 1px solid rgba(255, 255, 255, 0.1); - transition: all 0.3s ease; -} - -.status-card.executing { - background: linear-gradient(135deg, var(--executing-color) 0%, rgba(52, 152, 219, 0.2) 100%); -} - -.status-card.identification { - background: linear-gradient(135deg, var(--identification-color) 0%, rgba(46, 204, 113, 0.2) 100%); -} - -.status-card.friendly { - background: linear-gradient(135deg, var(--friendly-color) 0%, rgba(39, 174, 96, 0.2) 100%); -} - -.status-card.hostile { - background: linear-gradient(135deg, var(--hostile-color) 0%, rgba(231, 76, 60, 0.2) 100%); -} - -.status-info { - position: relative; - z-index: 1; -} - -.status-info h3 { - font-size: 0.9rem; - margin: 0 0 8px 0; - color: rgba(255, 255, 255, 0.9); - font-weight: 500; -} - -.status-value { - font-size: 1.5rem; - font-weight: 600; - color: white; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); -} - -.status-chart { - width: 60px; - height: 40px; - margin-left: 15px; - opacity: 0.5; -} - -/* 主雷达卡片 */ -.tactical-radar { - width: 100%; - height: 500px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - position: relative; - overflow: hidden; -} - -.main-radar-card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 10px; - margin-bottom: 1rem; -} - -.main-radar-card .card-body { - padding: 1rem; - height: 100%; -} - -.radar-controls { - display: flex; - gap: 0.5rem; -} - -.radar-controls .btn { - width: 32px; - height: 32px; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba(255, 255, 255, 0.1); - border: none; - color: var(--text-color); - transition: all 0.3s ease; -} - -.radar-controls .btn:hover { - background: var(--accent-color); - color: white; -} - -.radar-stats { - display: flex; - gap: 2rem; - padding: 1rem; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - margin-bottom: 1rem; - flex-wrap: wrap; -} - -.stat-item { - display: flex; - flex-direction: column; - gap: 0.3rem; -} - -.stat-item .label { - font-size: 0.8rem; - color: rgba(255, 255, 255, 0.7); -} - -.stat-item .value { - font-size: 1.1rem; - font-weight: 500; - color: var(--accent-color); -} - -.radar-legend { - display: flex; - gap: 2rem; - padding: 1rem; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - margin-top: 1rem; - justify-content: center; -} - -.legend-item { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.legend-item .dot { - width: 8px; - height: 8px; - border-radius: 50%; -} - -.dot.friendly { - background: var(--success-color); -} - -.dot.hostile { - background: var(--danger-color); -} - -.dot.unknown { - background: var(--warning-color); -} - -/* 全屏模式样式 */ -.tactical-radar.fullscreen { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: 9999; - margin: 0; - border-radius: 0; -} - -/* 威胁等级指示器 */ -.threat-level { - padding: 1rem; - background: rgba(0, 180, 216, 0.1); - border-radius: 5px; - margin-bottom: 1rem; -} - -.level-label { - color: var(--text-color); - margin-bottom: 0.5rem; -} - -.level-value { - font-size: 2rem; - font-weight: bold; - margin-bottom: 0.5rem; -} - -.level-value.low { - color: var(--success-color); -} - -.level-indicator { - height: 6px; - background: rgba(255, 255, 255, 0.1); - border-radius: 3px; - overflow: hidden; -} - -.level-indicator .indicator-bar { - height: 100%; - background: var(--success-color); - border-radius: 3px; - transition: width 0.3s ease; -} - -/* 卡片工具栏 */ -.card-tools { - display: flex; - gap: 0.5rem; -} - -.card-tools .btn { - width: 28px; - height: 28px; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - color: var(--accent-color); - border-color: var(--accent-color); -} - -/* 新增的图表区域 */ -.mission-stats, -.area-coverage, -.system-status { - height: 200px; -} - -/* 响应式调整 */ -@media (max-width: 1400px) { - .status-card { - height: 90px; - } - - .status-info h3 { - font-size: 0.85rem; - } - - .status-value { - font-size: 1.3rem; - } - - .main-radar-card { - height: calc(100vh - 250px); - } - - .threat-analysis, - .battlefield-activity { - height: calc((100vh - 250px) / 2 - 1rem); - } -} - -@media (max-width: 992px) { - .battlefield-header { - flex-direction: column; - gap: 0.5rem; - padding: 0.5rem; - } - - .header-left { - flex-direction: column; - align-items: flex-start; - gap: 0.5rem; - } -} - -@media (max-width: 768px) { - .sidebar { - transform: translateX(-100%); - } - .main-content { - margin-left: 0; - } - .status-card { - margin-bottom: 1rem; - } -} - -/* 视频容器样式 */ -.video-container { - position: relative; - width: 100%; - height: 480px; - background: rgba(0, 0, 0, 0.8); - border-radius: 8px; - overflow: hidden; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); -} - -.video-stream { - width: 100%; - height: 100%; - object-fit: cover; -} - -.video-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: none; -} - -.target-box { - position: absolute; - border: 2px solid #00ff00; - box-shadow: 0 0 10px rgba(0, 255, 0, 0.5); - animation: pulse 2s infinite; -} - -.target-marker { - position: absolute; - width: 20px; - height: 20px; - border: 2px solid #00ff00; - border-radius: 50%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.target-info { - position: absolute; - bottom: -40px; - left: 50%; - transform: translateX(-50%); - background: rgba(0, 0, 0, 0.8); - padding: 4px 12px; - border-radius: 4px; - font-size: 12px; - color: #fff; - white-space: nowrap; -} - -.overlay-info { - position: absolute; - top: 20px; - right: 20px; - background: rgba(0, 0, 0, 0.7); - padding: 10px; - border-radius: 8px; - color: #fff; -} - -.info-item { - margin: 5px 0; - font-size: 14px; -} - -.info-item i { - margin-right: 8px; - color: #00ff00; -} - -.video-controls { - display: flex; - justify-content: space-between; - align-items: center; - padding: 15px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - margin-top: 15px; -} - -.stream-settings .btn { - margin-left: 10px; - position: relative; - overflow: hidden; -} - -.stream-settings .btn::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 5px; - height: 5px; - background: rgba(255, 255, 255, 0.8); - border-radius: 50%; - transform: translate(-50%, -50%); - opacity: 0; - transition: all 0.3s; -} - -.stream-settings .btn.active::after { - opacity: 1; - width: 10px; - height: 10px; -} - -.target-details { - padding: 15px; -} - -.detail-group { - margin-bottom: 20px; - background: rgba(0, 0, 0, 0.2); - padding: 15px; - border-radius: 8px; - transition: all 0.3s; -} - -.detail-group:hover { - background: rgba(0, 0, 0, 0.3); - transform: translateY(-2px); -} - -.detail-item { - margin-bottom: 10px; -} - -.detail-item label { - display: block; - color: rgba(255, 255, 255, 0.7); - font-size: 12px; - margin-bottom: 5px; -} - -.detail-item .value { - font-size: 16px; - font-weight: 500; - color: #fff; -} - -.detail-item .progress { - height: 6px; - background: rgba(255, 255, 255, 0.1); -} - -.target-actions { - display: flex; - gap: 10px; - flex-wrap: wrap; -} - -.target-actions .btn { - flex: 1; - min-width: 120px; - position: relative; - overflow: hidden; -} - -.target-actions .btn::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent); - transform: translateX(-100%); - transition: all 0.3s; -} - -.target-actions .btn:hover::before { - transform: translateX(100%); -} - -.recent-identifications { - max-height: 400px; - overflow-y: auto; - padding: 10px; -} - -.identification-item { - display: flex; - align-items: center; - padding: 12px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - margin-bottom: 10px; - transition: all 0.3s; -} - -.identification-item:hover { - background: rgba(0, 0, 0, 0.3); - transform: translateX(5px); -} - -.item-time { - font-size: 12px; - color: rgba(255, 255, 255, 0.7); - width: 80px; -} - -.item-content { - flex: 1; - margin: 0 15px; -} - -.item-type { - font-weight: 500; - margin-bottom: 4px; -} - -.item-type.hostile { - color: #ff4757; -} - -.item-type.friendly { - color: #2ecc71; -} - -.item-type.unknown { - color: #f1c40f; -} - -.item-info { - font-size: 12px; - color: rgba(255, 255, 255, 0.7); -} - -.item-status { - font-size: 12px; - padding: 4px 8px; - border-radius: 4px; - background: rgba(255, 255, 255, 0.1); - color: rgba(255, 255, 255, 0.7); -} - -@keyframes pulse { - 0% { - box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.4); - } - 70% { - box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); - } -} - -/* 图表容器样式优化 */ -.analysis-chart { - height: 250px; - padding: 15px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - margin-bottom: 15px; -} - -/* 响应式调整 */ -@media (max-width: 992px) { - .video-container { - height: 360px; - } - - .target-actions .btn { - min-width: 100px; - } - - .detail-group { - padding: 10px; - } -} - -@media (max-width: 768px) { - .video-container { - height: 280px; - } - - .overlay-info { - top: 10px; - right: 10px; - font-size: 12px; - } - - .target-actions { - flex-direction: column; - } - - .target-actions .btn { - width: 100%; - } -} - -/* 记录页面样式 */ -.records-page .card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - margin-bottom: 1.5rem; - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.records-page .card:hover { - transform: translateY(-2px); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); -} - -.records-page .card-header { - background: rgba(44, 62, 80, 0.5); - border-bottom: 1px solid var(--border-color); - padding: 1rem 1.25rem; -} - -.records-page .card-title { - margin: 0; - font-size: 1.1rem; - font-weight: 500; - color: var(--accent-color); -} - -.records-page .form-label { - color: var(--text-color); - font-weight: 500; - margin-bottom: 0.5rem; -} - -.records-page .form-select { - background-color: rgba(26, 38, 52, 0.8); - border: 1px solid var(--border-color); - color: var(--text-color); - transition: border-color 0.3s ease, box-shadow 0.3s ease; -} - -.records-page .form-select:focus { - border-color: var(--accent-color); - box-shadow: 0 0 0 0.25rem rgba(0, 180, 216, 0.25); -} - -.records-page .btn-primary { - background-color: var(--accent-color); - border: none; - padding: 0.5rem 1.25rem; - transition: all 0.3s ease; -} - -.records-page .btn-primary:hover { - background-color: #0095b3; - transform: translateY(-1px); -} - -.records-page .btn-secondary { - background-color: #34495e; - border: none; - padding: 0.5rem 1.25rem; -} - -.records-page .table { - color: var(--text-color); - margin-bottom: 0; -} - -.records-page .table thead th { - background: rgba(44, 62, 80, 0.5); - border-bottom: 2px solid var(--border-color); - color: var(--accent-color); - font-weight: 500; - padding: 1rem; -} - -.records-page .table tbody tr { - transition: background-color 0.3s ease; -} - -.records-page .table tbody tr:hover { - background-color: rgba(44, 62, 80, 0.3); -} - -.records-page .table td { - padding: 1rem; - vertical-align: middle; - border-bottom: 1px solid var(--border-color); -} - -.records-page .badge { - padding: 0.5rem 0.75rem; - border-radius: 20px; - font-weight: 500; -} - -.records-page .badge-friendly { - background-color: rgba(46, 204, 113, 0.2); - color: var(--success-color); -} - -.records-page .badge-hostile { - background-color: rgba(231, 76, 60, 0.2); - color: var(--danger-color); -} - -.records-page .badge-unknown { - background-color: rgba(52, 152, 219, 0.2); - color: var(--info-color); -} - -.records-page .stats-card { - background: linear-gradient(135deg, rgba(26, 38, 52, 0.9) 0%, rgba(44, 62, 80, 0.9) 100%); - border-radius: 12px; - padding: 1.5rem; - height: 100%; - transition: transform 0.3s ease; -} - -.records-page .stats-card:hover { - transform: translateY(-2px); -} - -.records-page .stats-icon { - font-size: 2rem; - color: var(--accent-color); - margin-bottom: 1rem; -} - -.records-page .stats-value { - font-size: 1.8rem; - font-weight: 600; - color: var(--text-color); - margin-bottom: 0.5rem; -} - -.records-page .stats-label { - color: #a0aec0; - font-size: 0.9rem; -} - -.records-page .pagination { - margin-top: 2rem; -} - -.records-page .page-link { - background-color: rgba(26, 38, 52, 0.8); - border: 1px solid var(--border-color); - color: var(--text-color); - padding: 0.5rem 1rem; - transition: all 0.3s ease; -} - -.records-page .page-link:hover { - background-color: var(--accent-color); - color: white; - border-color: var(--accent-color); -} - -.records-page .page-item.active .page-link { - background-color: var(--accent-color); - border-color: var(--accent-color); -} - -.records-page .modal-content { - background: var(--card-bg); - border: 1px solid var(--border-color); -} - -.records-page .modal-header { - border-bottom: 1px solid var(--border-color); - padding: 1.25rem; -} - -.records-page .modal-title { - color: var(--accent-color); -} - -.records-page .modal-body { - padding: 1.5rem; -} - -.records-page .table-sm td { - padding: 0.75rem; - color: var(--text-color); -} - -/* 动画效果 */ -.records-page .fade-in { - animation: fadeIn 0.5s ease-out forwards; -} - -.records-page .slide-in { - animation: slideIn 0.5s ease-out forwards; -} - -@keyframes slideIn { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -/* 信号识别页面样式 */ -.signal-page .card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - margin-bottom: 1.5rem; - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.signal-page .card:hover { - transform: translateY(-2px); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); -} - -.signal-page .card-header { - background: rgba(44, 62, 80, 0.5); - border-bottom: 1px solid var(--border-color); - padding: 1rem 1.25rem; -} - -.signal-page .card-title { - margin: 0; - font-size: 1.1rem; - font-weight: 500; - color: var(--accent-color); - display: flex; - align-items: center; - gap: 0.5rem; -} - -.signal-page .signal-container { - position: relative; - background: rgba(26, 38, 52, 0.9); - border-radius: 8px; - padding: 1rem; -} - -.signal-page .signal-controls { - display: flex; - justify-content: space-between; - align-items: center; - gap: 1rem; - flex-wrap: wrap; -} - -.signal-page .btn-group { - display: flex; - gap: 0.5rem; -} - -.signal-page .btn { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 1rem; - border-radius: 6px; - transition: all 0.3s ease; -} - -.signal-page .btn i { - font-size: 1rem; -} - -.signal-page .btn-primary { - background: var(--accent-color); - border: none; -} - -.signal-page .btn-primary:hover { - background: #0095b3; - transform: translateY(-1px); -} - -.signal-page .frequency-selector { - display: flex; - align-items: center; - gap: 1rem; -} - -.signal-page .form-select { - background-color: rgba(26, 38, 52, 0.8); - border: 1px solid var(--border-color); - color: var(--text-color); - min-width: 120px; -} - -.signal-page .signal-display { - margin: 1.5rem 0; - position: relative; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - overflow: hidden; - height: 300px; -} - -.signal-page .signal-display canvas { - width: 100%; - height: 100%; -} - -.signal-page .signal-info { - background: rgba(26, 38, 52, 0.6); - border-radius: 8px; - padding: 1rem; -} - -.signal-page .info-item { - display: flex; - align-items: center; - gap: 1rem; - padding: 0.5rem; - border-radius: 6px; - background: rgba(0, 0, 0, 0.2); -} - -.signal-page .info-item label { - color: var(--accent-color); - margin: 0; - font-weight: 500; -} - -.signal-page .info-item span { - color: var(--text-color); - font-family: 'Roboto Mono', monospace; -} - -.signal-page .signal-features { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.signal-page .feature-item { - background: rgba(26, 38, 52, 0.6); - padding: 1rem; - border-radius: 8px; -} - -.signal-page .feature-item label { - color: var(--accent-color); - display: block; - margin-bottom: 0.5rem; - font-weight: 500; -} - -.signal-page .feature-item span { - color: var(--text-color); - font-family: 'Roboto Mono', monospace; -} - -.signal-page .progress { - height: 6px; - background: rgba(0, 0, 0, 0.2); - border-radius: 3px; - overflow: hidden; -} - -.signal-page .progress-bar { - transition: width 0.3s ease; -} - -.signal-page .threat-analysis { - background: rgba(26, 38, 52, 0.6); - border-radius: 8px; - padding: 1rem; -} - -.signal-page .threat-level { - display: flex; - align-items: center; - gap: 1rem; - margin-bottom: 1rem; -} - -.signal-page .threat-level label { - color: var(--accent-color); - margin: 0; - font-weight: 500; -} - -.signal-page .threat-level .badge { - padding: 0.5rem 1rem; - font-size: 0.9rem; - border-radius: 20px; -} - -.signal-page .threat-details ul li { - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.5rem; - background: rgba(0, 0, 0, 0.2); - border-radius: 6px; - margin-bottom: 0.5rem; -} - -.signal-page .recommended-actions .btn { - text-align: left; - justify-content: flex-start; -} - -.signal-page .table { - color: var(--text-color); - margin: 0; -} - -.signal-page .table thead th { - background: rgba(44, 62, 80, 0.5); - border-bottom: 2px solid var(--border-color); - color: var(--accent-color); - font-weight: 500; - padding: 1rem; -} - -.signal-page .table tbody tr { - transition: background-color 0.3s ease; -} - -.signal-page .table tbody tr:hover { - background-color: rgba(44, 62, 80, 0.3); -} - -.signal-page .table td { - padding: 1rem; - vertical-align: middle; - border-bottom: 1px solid var(--border-color); -} - -/* 动画效果 */ -@keyframes signalPulse { - 0% { - transform: scale(1); - opacity: 1; - } - 50% { - transform: scale(1.02); - opacity: 0.8; - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -@keyframes scanning { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} - -.signal-page .scanning { - animation: signalPulse 2s ease-in-out infinite; - background: linear-gradient(90deg, - rgba(0, 180, 216, 0.1) 0%, - rgba(0, 180, 216, 0.2) 50%, - rgba(0, 180, 216, 0.1) 100%); - background-size: 200% 100%; - animation: scanning 3s linear infinite; -} - -/* 信号页面额外样式 */ -.signal-page .frequency-controls { - display: flex; - align-items: center; - gap: 2rem; -} - -.signal-page .scan-speed { - display: flex; - align-items: center; - gap: 1rem; - min-width: 200px; -} - -.signal-page .form-range { - height: 6px; - background: rgba(0, 0, 0, 0.2); - border-radius: 3px; - cursor: pointer; -} - -.signal-page .form-range::-webkit-slider-thumb { - background: var(--accent-color); - width: 16px; - height: 16px; - border-radius: 50%; - cursor: pointer; - transition: transform 0.2s ease; -} - -.signal-page .form-range::-webkit-slider-thumb:hover { - transform: scale(1.2); -} - -.signal-page .signal-markers { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: none; -} - -.signal-page .frequency-marker { - position: absolute; - top: 0; - bottom: 0; -} - -.signal-page .marker-line { - position: absolute; - top: 0; - bottom: 0; - width: 1px; - background: rgba(255, 255, 255, 0.2); -} - -.signal-page .marker-label { - position: absolute; - bottom: 5px; - left: 50%; - transform: translateX(-50%); - background: rgba(0, 0, 0, 0.5); - padding: 2px 8px; - border-radius: 4px; - font-size: 0.8rem; - color: var(--accent-color); - white-space: nowrap; -} - -.signal-page .status-card { - background: linear-gradient(135deg, rgba(26, 38, 52, 0.9) 0%, rgba(44, 62, 80, 0.9) 100%); - border-radius: 12px; - padding: 1.5rem; - height: 100%; - transition: transform 0.3s ease; -} - -.signal-page .status-card:hover { - transform: translateY(-2px); -} - -.signal-page .status-icon { - font-size: 2rem; - color: var(--accent-color); - margin-bottom: 1rem; -} - -.signal-page .status-value { - font-size: 1.8rem; - font-weight: 600; - color: var(--text-color); - margin-bottom: 0.5rem; -} - -.signal-page .status-label { - color: #a0aec0; - font-size: 0.9rem; -} - -/* Toast 消息样式 */ -.toast-message { - position: fixed; - top: 20px; - right: 20px; - background: rgba(0, 0, 0, 0.8); - color: #fff; - padding: 12px 24px; - border-radius: 4px; - z-index: 1000; - transform: translateX(120%); - transition: transform 0.3s ease-in-out; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); -} - -.toast-message.show { - transform: translateX(0); -} - -/* 全屏模式样式 */ -.signal-container:fullscreen { - background: var(--primary-color); - padding: 2rem; -} - -.signal-container:fullscreen .signal-display { - height: calc(100vh - 200px); -} - -/* 动画效果 */ -.signal-page .fade-in { - animation: fadeIn 0.5s ease-out forwards; -} - -/* 进度条动画 */ -.signal-page .progress-bar { - position: relative; - overflow: hidden; -} - -.signal-page .progress-bar::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.2) 50%, - rgba(255, 255, 255, 0) 100% - ); - transform: translateX(-100%); - animation: progressShine 2s linear infinite; -} - -@keyframes progressShine { - to { - transform: translateX(100%); - } -} - -/* 响应式调整 */ -@media (max-width: 1200px) { - .signal-page .frequency-controls { - flex-direction: column; - gap: 1rem; - } - - .signal-page .scan-speed { - width: 100%; - } -} - -@media (max-width: 768px) { - .signal-page .signal-info .row { - flex-direction: column; - } - - .signal-page .info-item { - margin-bottom: 0.5rem; - } - - .signal-page .status-card { - margin-bottom: 1rem; - } -} - -/* 截图闪光效果 */ -.capture-flash { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: #fff; - opacity: 0; - animation: flash 0.2s ease-out; - pointer-events: none; -} - -@keyframes flash { - 0% { opacity: 0; } - 50% { opacity: 0.8; } - 100% { opacity: 0; } -} - -/* 按钮悬停效果 */ -.btn { - position: relative; - overflow: hidden; - transition: all 0.3s ease; -} - -.btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); -} - -.btn:active { - transform: translateY(0); -} - -.btn i { - margin-right: 6px; - transition: transform 0.3s ease; -} - -.btn:hover i { - transform: scale(1.2); -} - -/* 目标框动画效果 */ -.target-box { - transition: all 0.3s ease-out; -} - -.target-box:hover .target-info { - opacity: 1; - transform: translateY(0); -} - -.target-info { - opacity: 0; - transform: translateY(10px); - transition: all 0.3s ease-out; -} - -/* 图表容器悬停效果 */ -.analysis-chart { - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.analysis-chart:hover { - transform: translateY(-5px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); -} - -/* 状态标签动画 */ -.status-dot { - position: relative; -} - -.status-dot::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 100%; - height: 100%; - background: inherit; - border-radius: 50%; - transform: translate(-50%, -50%) scale(1); - animation: pulse-dot 2s infinite; -} - -@keyframes pulse-dot { - 0% { - transform: translate(-50%, -50%) scale(1); - opacity: 0.5; - } - 70% { - transform: translate(-50%, -50%) scale(2); - opacity: 0; - } - 100% { - transform: translate(-50%, -50%) scale(1); - opacity: 0; - } -} - -/* 进度条动画 */ -.progress-bar { - transition: width 0.6s ease-in-out; -} - -/* 威胁等级标签动画 */ -.badge { - transition: all 0.3s ease; -} - -.badge:hover { - transform: scale(1.1); -} - -/* 最近识别列表动画 */ -.identification-item { - animation: slideIn 0.3s ease-out; -} - -@keyframes slideIn { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -/* 视频控制按钮组动画 */ -.video-controls .btn-group { - position: relative; - overflow: hidden; -} - -.video-controls .btn-group::after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 2px; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - animation: scanline 2s linear infinite; -} - -@keyframes scanline { - 0% { transform: translateX(-100%); } - 100% { transform: translateX(100%); } -} - -/* 响应式调整 */ -@media (max-width: 768px) { - .toast-message { - top: auto; - bottom: 20px; - left: 20px; - right: 20px; - transform: translateY(120%); - } - - .toast-message.show { - transform: translateY(0); - } -} \ No newline at end of file diff --git a/common/js/api.js b/common/js/api.js deleted file mode 100644 index 7860156..0000000 --- a/common/js/api.js +++ /dev/null @@ -1,200 +0,0 @@ -// API 基础 URL -const API_BASE_URL = 'http://localhost:5000/api'; - -// API 调用函数 -const api = { - // 登录 - login: async (username, password) => { - try { - const response = await fetch(`${API_BASE_URL}/login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username, password }) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '登录失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 获取无人机列表 - getDrones: async () => { - try { - const response = await fetch(`${API_BASE_URL}/drones`, { - headers: { - 'Authorization': `Bearer ${currentUser?.token}` - } - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '获取无人机列表失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 添加无人机 - addDrone: async (droneData) => { - try { - const response = await fetch(`${API_BASE_URL}/drones`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${currentUser?.token}` - }, - body: JSON.stringify(droneData) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '添加无人机失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 更新无人机 - updateDrone: async (droneId, droneData) => { - try { - const response = await fetch(`${API_BASE_URL}/drones/${droneId}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${currentUser?.token}` - }, - body: JSON.stringify(droneData) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '更新无人机失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 删除无人机 - deleteDrone: async (droneId) => { - try { - const response = await fetch(`${API_BASE_URL}/drones/${droneId}`, { - method: 'DELETE', - headers: { - 'Authorization': `Bearer ${currentUser?.token}` - } - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '删除无人机失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 获取识别记录 - getRecords: async (filters = {}) => { - try { - const queryParams = new URLSearchParams(filters).toString(); - const response = await fetch(`${API_BASE_URL}/records?${queryParams}`, { - headers: { - 'Authorization': `Bearer ${currentUser?.token}` - } - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '获取识别记录失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 获取记录详情 - getRecordDetail: async (recordId) => { - try { - const response = await fetch(`${API_BASE_URL}/records/${recordId}`, { - headers: { - 'Authorization': `Bearer ${currentUser?.token}` - } - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '获取记录详情失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 目标识别 - identify: async (formData) => { - try { - const response = await fetch(`${API_BASE_URL}/identify`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${currentUser?.token}` - }, - body: formData - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '目标识别失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - }, - - // 信号识别 - identifySignal: async (signal) => { - try { - const response = await fetch(`${API_BASE_URL}/signal_identify`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${currentUser?.token}` - }, - body: JSON.stringify({ signal }) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || '信号识别失败'); - } - - return await response.json(); - } catch (error) { - throw error; - } - } -}; \ No newline at end of file diff --git a/common/js/battlefield.js b/common/js/battlefield.js deleted file mode 100644 index 22c40d7..0000000 --- a/common/js/battlefield.js +++ /dev/null @@ -1,732 +0,0 @@ -// 更新时间显示 -function updateTime() { - const now = new Date(); - const timeString = now.toLocaleString('zh-CN', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false - }); - document.getElementById('current-time').textContent = timeString; -} - -// 状态卡片迷你图表 -class StatusMiniChart { - constructor(container) { - this.container = container; - this.canvas = document.createElement('canvas'); - this.container.appendChild(this.canvas); - this.ctx = this.canvas.getContext('2d'); - this.resize(); - this.generateData(); - this.draw(); - } - - resize() { - this.canvas.width = this.container.offsetWidth; - this.canvas.height = this.container.offsetHeight; - } - - generateData() { - this.data = Array.from({length: 20}, () => Math.random() * 0.8 + 0.2); - } - - draw() { - const ctx = this.ctx; - const width = this.canvas.width; - const height = this.canvas.height; - const step = width / (this.data.length - 1); - - ctx.clearRect(0, 0, width, height); - - // 绘制曲线 - ctx.beginPath(); - ctx.moveTo(0, height * (1 - this.data[0])); - - for (let i = 1; i < this.data.length; i++) { - const x = i * step; - const y = height * (1 - this.data[i]); - - if (i === 0) { - ctx.moveTo(x, y); - } else { - const xc = (x + (i - 1) * step) / 2; - const yc = (y + height * (1 - this.data[i - 1])) / 2; - ctx.quadraticCurveTo(xc, yc, x, y); - } - } - - ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)'; - ctx.lineWidth = 2; - ctx.stroke(); - - // 填充渐变 - const gradient = ctx.createLinearGradient(0, 0, 0, height); - gradient.addColorStop(0, 'rgba(255, 255, 255, 0.2)'); - gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); - - ctx.lineTo(width, height); - ctx.lineTo(0, height); - ctx.fillStyle = gradient; - ctx.fill(); - } - - update() { - this.data.shift(); - this.data.push(Math.random() * 0.8 + 0.2); - this.draw(); - } -} - -// 雷达扫描效果 -class TacticalRadar { - constructor(containerId) { - this.container = document.getElementById(containerId); - this.canvas = document.createElement('canvas'); - this.ctx = this.canvas.getContext('2d'); - this.container.appendChild(this.canvas); - this.resize(); - this.setupRadar(); - window.addEventListener('resize', () => this.resize()); - } - - resize() { - this.canvas.width = this.container.offsetWidth; - this.canvas.height = this.container.offsetHeight; - this.centerX = this.canvas.width / 2; - this.centerY = this.canvas.height / 2; - this.radius = Math.min(this.centerX, this.centerY) * 0.85; - } - - setupRadar() { - this.angle = 0; - this.targets = this.generateRandomTargets(); - this.fadeOpacity = new Array(360).fill(0); - this.animate(); - } - - generateRandomTargets() { - const targets = []; - const numTargets = Math.floor(Math.random() * 5) + 3; - - for (let i = 0; i < numTargets; i++) { - const distance = Math.random() * 0.8 + 0.1; // 10% - 90% of radius - const angle = Math.random() * Math.PI * 2; - const type = Math.random() < 0.7 ? 'friendly' : 'hostile'; - const size = Math.random() * 3 + 2; - - targets.push({ - x: Math.cos(angle) * this.radius * distance, - y: Math.sin(angle) * this.radius * distance, - type, - size, - blip: 1.0, // 用于闪烁效果 - fadeStart: angle // 用于淡出效果 - }); - } - return targets; - } - - drawGrid() { - // 绘制背景 - const gradient = this.ctx.createRadialGradient( - this.centerX, this.centerY, 0, - this.centerX, this.centerY, this.radius - ); - gradient.addColorStop(0, 'rgba(0, 180, 216, 0.1)'); - gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); - - this.ctx.fillStyle = gradient; - this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); - - this.ctx.strokeStyle = 'rgba(0, 180, 216, 0.2)'; - this.ctx.lineWidth = 1; - - // 绘制同心圆 - for (let i = 1; i <= 4; i++) { - const radius = this.radius * (i/4); - this.ctx.beginPath(); - this.ctx.arc(this.centerX, this.centerY, radius, 0, Math.PI * 2); - this.ctx.stroke(); - - // 添加距离标记 - const distance = (i * 25).toString() + 'km'; - this.ctx.fillStyle = 'rgba(0, 180, 216, 0.5)'; - this.ctx.font = '12px Noto Sans SC'; - this.ctx.fillText(distance, this.centerX + radius + 5, this.centerY); - } - - // 绘制方向标记 - const directions = ['N', 'E', 'S', 'W']; - this.ctx.font = 'bold 14px Noto Sans SC'; - directions.forEach((dir, i) => { - const angle = (i * Math.PI / 2) - Math.PI / 2; - const x = this.centerX + Math.cos(angle) * (this.radius + 20); - const y = this.centerY + Math.sin(angle) * (this.radius + 20); - this.ctx.fillText(dir, x - 6, y + 6); - }); - - // 绘制刻度线 - for (let i = 0; i < 360; i += 30) { - const angle = (i * Math.PI) / 180; - const startRadius = this.radius - 10; - const endRadius = this.radius; - - this.ctx.beginPath(); - this.ctx.moveTo( - this.centerX + Math.cos(angle) * startRadius, - this.centerY + Math.sin(angle) * startRadius - ); - this.ctx.lineTo( - this.centerX + Math.cos(angle) * endRadius, - this.centerY + Math.sin(angle) * endRadius - ); - this.ctx.stroke(); - } - - // 绘制十字线 - this.ctx.beginPath(); - this.ctx.moveTo(this.centerX - this.radius, this.centerY); - this.ctx.lineTo(this.centerX + this.radius, this.centerY); - this.ctx.moveTo(this.centerX, this.centerY - this.radius); - this.ctx.lineTo(this.centerX, this.centerY + this.radius); - this.ctx.stroke(); - } - - drawSweep() { - // 更新扫描线渐变效果 - const gradient = this.ctx.createLinearGradient( - this.centerX, - this.centerY, - this.centerX + Math.cos(this.angle) * this.radius, - this.centerY + Math.sin(this.angle) * this.radius - ); - gradient.addColorStop(0, 'rgba(0, 180, 216, 0.8)'); - gradient.addColorStop(0.5, 'rgba(0, 180, 216, 0.3)'); - gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); - - // 绘制扫描线 - this.ctx.beginPath(); - this.ctx.moveTo(this.centerX, this.centerY); - this.ctx.lineTo( - this.centerX + Math.cos(this.angle) * this.radius, - this.centerY + Math.sin(this.angle) * this.radius - ); - this.ctx.strokeStyle = gradient; - this.ctx.lineWidth = 3; - this.ctx.stroke(); - - // 扫描扇形 - this.ctx.beginPath(); - this.ctx.moveTo(this.centerX, this.centerY); - this.ctx.arc(this.centerX, this.centerY, this.radius, this.angle - 0.2, this.angle); - this.ctx.fillStyle = 'rgba(0, 180, 216, 0.1)'; - this.ctx.fill(); - - // 更新淡出效果数组 - const currentAngleDeg = (this.angle * 180 / Math.PI) % 360; - this.fadeOpacity[Math.floor(currentAngleDeg)] = 1; - - // 绘制历史扫描线 - for (let i = 0; i < 360; i++) { - if (this.fadeOpacity[i] > 0) { - const angle = (i * Math.PI) / 180; - this.ctx.beginPath(); - this.ctx.moveTo(this.centerX, this.centerY); - this.ctx.lineTo( - this.centerX + Math.cos(angle) * this.radius, - this.centerY + Math.sin(angle) * this.radius - ); - this.ctx.strokeStyle = `rgba(0, 180, 216, ${this.fadeOpacity[i] * 0.1})`; - this.ctx.stroke(); - this.fadeOpacity[i] *= 0.95; - } - } - } - - drawTargets() { - this.targets.forEach(target => { - // 计算目标在画布上的位置 - const x = this.centerX + target.x; - const y = this.centerY + target.y; - - // 检查是否在扫描线范围内 - const targetAngle = Math.atan2(target.y, target.x); - const normalizedTargetAngle = targetAngle < 0 ? targetAngle + Math.PI * 2 : targetAngle; - const normalizedSweepAngle = this.angle % (Math.PI * 2); - - if (Math.abs(normalizedTargetAngle - normalizedSweepAngle) < 0.2) { - target.blip = 1.0; - } - - // 绘制目标 - this.ctx.beginPath(); - this.ctx.arc(x, y, target.size, 0, Math.PI * 2); - - const color = target.type === 'friendly' ? - `rgba(46, 204, 113, ${target.blip})` : - `rgba(231, 76, 60, ${target.blip})`; - - this.ctx.fillStyle = color; - this.ctx.fill(); - - // 绘制目标光晕 - this.ctx.beginPath(); - this.ctx.arc(x, y, target.size * 2, 0, Math.PI * 2); - this.ctx.fillStyle = color.replace('1)', '0.3)'); - this.ctx.fill(); - - // 淡出效果 - target.blip *= 0.95; - }); - } - - updateStats() { - const friendlyTargets = this.targets.filter(t => t.type === 'friendly').length; - const hostileTargets = this.targets.filter(t => t.type === 'hostile').length; - - document.querySelector('.stat-item:nth-child(2) .value').textContent = - `${friendlyTargets + hostileTargets}`; - } - - animate() { - this.ctx.fillStyle = 'rgba(26, 38, 52, 0.3)'; - this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); - - this.drawGrid(); - this.drawSweep(); - this.drawTargets(); - this.updateStats(); - - this.angle += 0.02; - if (this.angle >= Math.PI * 2) { - this.angle = 0; - if (Math.random() < 0.3) { - this.targets = this.generateRandomTargets(); - } - } - - requestAnimationFrame(() => this.animate()); - } -} - -// 状态卡片动画 -class StatusCards { - constructor() { - this.cards = document.querySelectorAll('.status-card'); - this.setupCards(); - } - - setupCards() { - this.cards.forEach(card => { - const value = card.querySelector('.status-value'); - const targetValue = parseInt(value.textContent); - value.textContent = '0'; - - // 添加数值增长动画 - this.animateValue(value, 0, targetValue, 2000); - - // 添加图标呼吸效果 - const icon = card.querySelector('.status-icon'); - this.addPulseEffect(icon); - }); - } - - animateValue(element, start, end, duration) { - const range = end - start; - const increment = range / (duration / 16); - let current = start; - - const animate = () => { - current += increment; - element.textContent = Math.floor(current); - - if (current < end) { - requestAnimationFrame(animate); - } else { - element.textContent = end; - } - }; - - animate(); - } - - addPulseEffect(element) { - element.style.animation = 'pulse 2s infinite'; - } -} - -// 威胁分析图表 -class ThreatAnalysis { - constructor(containerId) { - this.container = document.getElementById(containerId); - this.setupChart(); - } - - setupChart() { - // 这里可以使用Chart.js或其他图表库来实现威胁分析图表 - this.container.innerHTML = '
    威胁分析图表加载中...
    '; - } -} - -// 战场活动播报 -class BattlefieldActivity { - constructor(containerId) { - this.container = document.getElementById(containerId); - this.activities = []; - this.setupActivity(); - } - - setupActivity() { - this.container.innerHTML = ` -
    -
    - 10:30 - 警告 - 检测到未知信号源,位于东北方向,距离2.5公里 -
    -
    - 10:28 - 信息 - 友军编队完成例行巡逻任务 -
    -
    - `; - } - - addActivity(activity) { - this.activities.unshift(activity); - this.updateDisplay(); - } - - updateDisplay() { - // 更新活动列表显示 - } -} - -// 任务执行统计图表 -class MissionStats { - constructor(containerId) { - const ctx = document.getElementById(containerId).getContext('2d'); - this.chart = new Chart(ctx, { - type: 'line', - data: { - labels: Array.from({length: 12}, (_, i) => `${i*2}:00`), - datasets: [{ - label: '任务完成率', - data: Array.from({length: 12}, () => Math.random() * 40 + 60), - borderColor: '#00b4d8', - backgroundColor: 'rgba(0, 180, 216, 0.1)', - fill: true, - tension: 0.4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - } - }, - scales: { - y: { - beginAtZero: true, - max: 100, - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: '#e9ecef' - } - }, - x: { - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: '#e9ecef' - } - } - } - } - }); - } -} - -// 区域覆盖率图表 -class AreaCoverage { - constructor(containerId) { - const ctx = document.getElementById(containerId).getContext('2d'); - this.chart = new Chart(ctx, { - type: 'doughnut', - data: { - labels: ['已覆盖', '未覆盖'], - datasets: [{ - data: [75, 25], - backgroundColor: [ - 'rgba(0, 180, 216, 0.8)', - 'rgba(255, 255, 255, 0.1)' - ], - borderWidth: 0 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'bottom', - labels: { - color: '#e9ecef' - } - } - } - } - }); - } -} - -// 系统状态图表 -class SystemStatus { - constructor(containerId) { - const ctx = document.getElementById(containerId).getContext('2d'); - this.chart = new Chart(ctx, { - type: 'bar', - data: { - labels: ['CPU', '内存', '存储', '网络'], - datasets: [{ - label: '使用率', - data: [65, 45, 30, 80], - backgroundColor: 'rgba(0, 180, 216, 0.8)', - borderRadius: 5 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - } - }, - scales: { - y: { - beginAtZero: true, - max: 100, - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: '#e9ecef' - } - }, - x: { - grid: { - display: false - }, - ticks: { - color: '#e9ecef' - } - } - } - } - }); - } -} - -class Radar3D { - constructor(containerId) { - this.container = document.getElementById(containerId); - if (!this.container) { - console.error('Container not found:', containerId); - return; - } - - // 清空容器 - while (this.container.firstChild) { - this.container.removeChild(this.container.firstChild); - } - - this.scene = new THREE.Scene(); - this.camera = new THREE.PerspectiveCamera(75, this.container.clientWidth / this.container.clientHeight, 0.1, 1000); - - try { - this.renderer = new THREE.WebGLRenderer({ - antialias: true, - alpha: true - }); - console.log('Renderer created successfully'); - } catch (error) { - console.error('Failed to create renderer:', error); - return; - } - - this.renderer.setSize(this.container.clientWidth, this.container.clientHeight); - this.renderer.setClearColor(0x000000, 0.1); - this.container.appendChild(this.renderer.domElement); - console.log('Renderer added to container'); - - // 设置相机位置 - this.camera.position.set(50, 30, 50); - this.camera.lookAt(0, 0, 0); - - // 创建场景内容 - this.createScene(); - - // 添加事件监听 - window.addEventListener('resize', () => this.onWindowResize()); - - // 开始动画循环 - this.animate(); - console.log('Animation started'); - } - - createScene() { - try { - // 添加环境光 - const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); - this.scene.add(ambientLight); - - // 添加点光源 - const pointLight = new THREE.PointLight(0x00b4d8, 1); - pointLight.position.set(10, 10, 10); - this.scene.add(pointLight); - - // 创建地面网格 - const gridHelper = new THREE.GridHelper(100, 20, 0x404040, 0x404040); - this.scene.add(gridHelper); - - // 创建坐标轴 - const axesHelper = new THREE.AxesHelper(50); - this.scene.add(axesHelper); - - // 创建雷达平面 - const radarGeometry = new THREE.CircleGeometry(40, 64); - const radarMaterial = new THREE.MeshPhongMaterial({ - color: 0x00b4d8, - transparent: true, - opacity: 0.3, - side: THREE.DoubleSide - }); - this.radarMesh = new THREE.Mesh(radarGeometry, radarMaterial); - this.radarMesh.rotation.x = -Math.PI / 2; - this.scene.add(this.radarMesh); - - // 创建扫描线 - const lineGeometry = new THREE.BufferGeometry().setFromPoints([ - new THREE.Vector3(0, 0, 0), - new THREE.Vector3(40, 0, 0) - ]); - const lineMaterial = new THREE.LineBasicMaterial({ - color: 0x00ff00, - linewidth: 2 - }); - this.radarLine = new THREE.Line(lineGeometry, lineMaterial); - this.radarLine.rotation.x = -Math.PI / 2; - this.scene.add(this.radarLine); - - // 设置轨道控制器 - if (window.OrbitControls) { - this.controls = new window.OrbitControls(this.camera, this.renderer.domElement); - this.controls.enableDamping = true; - this.controls.dampingFactor = 0.05; - console.log('OrbitControls initialized'); - } else { - console.error('OrbitControls not available'); - } - - console.log('Scene created successfully'); - } catch (error) { - console.error('Error creating scene:', error); - } - } - - addTarget(position, type = 'unknown') { - const colors = { - friendly: 0x2ecc71, - hostile: 0xe74c3c, - unknown: 0xf1c40f - }; - - const geometry = new THREE.SphereGeometry(1, 16, 16); - const material = new THREE.MeshPhongMaterial({ - color: colors[type], - emissive: colors[type], - emissiveIntensity: 0.5 - }); - const target = new THREE.Mesh(geometry, material); - target.position.set(position.x, position.y, position.z); - this.scene.add(target); - } - - onWindowResize() { - if (!this.camera || !this.renderer || !this.container) return; - - const width = this.container.clientWidth; - const height = this.container.clientHeight; - - this.camera.aspect = width / height; - this.camera.updateProjectionMatrix(); - this.renderer.setSize(width, height); - } - - animate() { - if (!this.renderer || !this.scene || !this.camera) return; - - requestAnimationFrame(() => this.animate()); - - if (this.radarLine) { - this.radarLine.rotation.z += 0.02; - } - - if (this.controls) { - this.controls.update(); - } - - this.renderer.render(this.scene, this.camera); - } -} - -// 初始化 -document.addEventListener('DOMContentLoaded', () => { - // 更新时间 - updateTime(); - setInterval(updateTime, 1000); - - // 初始化雷达 - const radar = new Radar3D('tacticalRadar'); - - // 初始化状态卡片迷你图表 - const miniCharts = []; - document.querySelectorAll('.mini-chart').forEach(container => { - const chart = new StatusMiniChart(container); - miniCharts.push(chart); - }); - - // 更新迷你图表 - setInterval(() => { - miniCharts.forEach(chart => chart.update()); - }, 2000); - - // 初始化其他图表 - const missionStats = new MissionStats('missionStats'); - const areaCoverage = new AreaCoverage('areaCoverage'); - const systemStatus = new SystemStatus('systemStatus'); - - // 添加按钮事件监听 - document.querySelectorAll('.radar-controls .btn, .card-tools .btn').forEach(btn => { - btn.addEventListener('click', function() { - // 这里可以添加按钮的具体功能 - console.log('Button clicked:', this.querySelector('i').className); - }); - }); - - // 添加示例目标 - radar.addTarget({ x: 20, y: 0, z: 20 }, 'friendly'); - radar.addTarget({ x: -15, y: 5, z: -25 }, 'hostile'); - radar.addTarget({ x: 30, y: 2, z: -10 }, 'unknown'); -}); \ No newline at end of file diff --git a/common/js/common.js b/common/js/common.js deleted file mode 100644 index 4faf7a0..0000000 --- a/common/js/common.js +++ /dev/null @@ -1,169 +0,0 @@ -// 全局变量 -let currentUser = null; -let threatChart = null; -let radarTargets = []; // 存储雷达上的目标 -let radarInterval = null; // 存储雷达更新间隔 - -// 显示提示消息 -function showToast(message, type = 'info') { - // 创建toast容器(如果不存在) - let toastContainer = document.getElementById('toast-container'); - if (!toastContainer) { - toastContainer = document.createElement('div'); - toastContainer.id = 'toast-container'; - toastContainer.className = 'position-fixed top-0 end-0 p-3'; - toastContainer.style.zIndex = '1050'; - document.body.appendChild(toastContainer); - } - - // 创建toast元素 - const toastId = 'toast-' + Date.now(); - const toast = document.createElement('div'); - toast.className = `toast align-items-center text-white bg-${type} border-0`; - toast.id = toastId; - toast.setAttribute('role', 'alert'); - toast.setAttribute('aria-live', 'assertive'); - toast.setAttribute('aria-atomic', 'true'); - - toast.innerHTML = ` -
    -
    - ${message} -
    - -
    - `; - - toastContainer.appendChild(toast); - - // 显示toast - const bsToast = new bootstrap.Toast(toast, { - animation: true, - autohide: true, - delay: 3000 - }); - bsToast.show(); - - // 自动删除toast元素 - toast.addEventListener('hidden.bs.toast', function() { - toast.remove(); - }); -} - -// 处理登录 -function handleLogin(event) { - event.preventDefault(); - - const username = document.getElementById('username').value; - const password = document.getElementById('password').value; - - // 硬编码的用户验证 - if (username === 'admin' && password === '123') { - // 保存登录状态 - currentUser = { - username: 'admin', - token: 'dummy-token' - }; - - // 保存到 localStorage - localStorage.setItem('currentUser', JSON.stringify(currentUser)); - - // 显示成功消息 - showToast('登录成功', 'success'); - - // 跳转到仪表盘页面 - setTimeout(() => { - window.location.href = 'dashboard.html'; - }, 1000); - - return false; - } else { - showToast('用户名或密码错误', 'danger'); - return false; - } -} - -// 退出登录 -function logout() { - currentUser = null; - localStorage.removeItem('currentUser'); - - // 清除雷达间隔 - if (radarInterval) { - clearInterval(radarInterval); - radarInterval = null; - } - - // 重定向到登录页面 - window.location.href = 'login.html'; -} - -// 初始化侧边栏 -function initializeSidebar() { - // 处理侧边栏链接点击 - document.querySelectorAll('.nav-link').forEach(link => { - link.addEventListener('click', function(e) { - e.preventDefault(); - - // 处理退出登录 - if (this.id === 'logout-tab') { - logout(); - return; - } - - // 更新活动标签 - document.querySelectorAll('.nav-link').forEach(nav => nav.classList.remove('active')); - this.classList.add('active'); - - // 获取目标页面 - const targetPage = this.getAttribute('href'); - if (targetPage) { - window.location.href = targetPage; - } - }); - }); -} - -// 检查登录状态 -function checkAuth() { - // 从 localStorage 获取用户信息 - const savedUser = localStorage.getItem('currentUser'); - if (savedUser) { - currentUser = JSON.parse(savedUser); - return true; - } - - // 如果不在登录页面,则重定向到登录页面 - if (!window.location.pathname.includes('login.html')) { - window.location.href = 'login.html'; - } - return false; -} - -// 更新用户信息显示 -function updateUserInfo() { - if (currentUser) { - const userNameElement = document.getElementById('user-name'); - if (userNameElement) { - userNameElement.textContent = currentUser.username; - } - } -} - -// 初始化页面 -function initializePage() { - // 如果是登录页面,不需要检查认证 - if (window.location.pathname.includes('login.html')) { - return; - } - - if (!checkAuth()) return; - - initializeSidebar(); - updateUserInfo(); -} - -// 在页面加载完成后初始化 -document.addEventListener('DOMContentLoaded', function() { - initializePage(); -}); \ No newline at end of file diff --git a/common/js/drones.js b/common/js/drones.js deleted file mode 100644 index bbb3f0e..0000000 --- a/common/js/drones.js +++ /dev/null @@ -1,341 +0,0 @@ -// 初始化地图 -let map; -let droneMarkers = {}; - -document.addEventListener('DOMContentLoaded', function() { - // 首先加载无人机列表和其他UI元素 - loadDroneData(); - - // 绑定事件处理器 - bindEventHandlers(); - - // 延迟加载地图和实时更新功能 - setTimeout(() => { - initMap(); - initRealTimeUpdates(); - }, 100); -}); - -function initMap() { - try { - // 使用百度地图 - map = new BMap.Map('map'); - - // 设置中心点和缩放级别 - const point = new BMap.Point(116.4074, 39.9042); - map.centerAndZoom(point, 13); - - // 开启鼠标滚轮缩放 - map.enableScrollWheelZoom(true); - - // 添加地图控件 - map.addControl(new BMap.NavigationControl()); // 添加平移缩放控件 - map.addControl(new BMap.ScaleControl()); // 添加比例尺控件 - map.addControl(new BMap.MapTypeControl()); // 添加地图类型控件 - - // 设置地图样式 - 暗色主题 - map.setMapStyle({ - style: 'midnight' // 使用暗色主题 - }); - - } catch (error) { - console.error('地图初始化失败:', error); - document.getElementById('map').innerHTML = '
    地图加载中,请稍候...
    '; - } -} - -function initRealTimeUpdates() { - // 降低更新频率,从5秒改为10秒 - setInterval(() => { - if (map && Object.keys(droneMarkers).length > 0) { - updateDronePositions(); - updateBatteryLevels(); - } - }, 10000); - - // 告警更新频率降低到30秒 - setInterval(() => { - updateAlerts(); - }, 30000); -} - -function bindEventHandlers() { - // 刷新状态按钮 - document.getElementById('refreshStatus').addEventListener('click', () => { - loadDroneData(); - }); - - // 任务分配表单提交 - document.getElementById('assignTaskForm').addEventListener('submit', (e) => { - e.preventDefault(); - assignTask(); - }); - - // 添加无人机表单保存 - document.getElementById('saveDrone').addEventListener('click', () => { - saveDrone(); - }); -} - -function loadDroneData() { - // 添加加载状态指示 - const tbody = document.getElementById('dronesList'); - tbody.innerHTML = '加载中...'; - - // 模拟从后端加载数据 - const drones = [ - { - id: 'UAV-001', - model: '侦察者-X1', - status: 'online', - battery: 85, - task: '巡逻任务', - position: { - lng: 116.4074, - lat: 39.9042 - } - }, - { - id: 'UAV-002', - model: '侦察者-X2', - status: 'mission', - battery: 65, - task: '侦察任务', - position: { - lng: 116.4174, - lat: 39.9142 - } - }, - { - id: 'UAV-003', - model: '追踪者-A1', - status: 'maintenance', - battery: 45, - task: '维护中', - position: { - lng: 116.4274, - lat: 39.9242 - } - } - ]; - - // 立即更新无人机列表 - updateDroneList(drones); - - // 如果地图已经初始化,则更新标记 - if (map) { - updateMapMarkers(drones); - } -} - -function updateDroneList(drones) { - const tbody = document.getElementById('dronesList'); - tbody.innerHTML = ''; - - drones.forEach(drone => { - const tr = document.createElement('tr'); - tr.innerHTML = ` - ${drone.id} - ${drone.model} - ${getStatusText(drone.status)} - -
    -
    -
    - - ${drone.task} - ${formatPosition(drone.position)} - -
    - - - -
    - - `; - tbody.appendChild(tr); - }); -} - -function updateMapMarkers(drones) { - // 清除现有标记 - map.clearOverlays(); - droneMarkers = {}; - - // 添加新标记 - drones.forEach(drone => { - // 创建标记点 - const point = new BMap.Point(drone.position.lng, drone.position.lat); - const marker = new BMap.Marker(point); - - // 创建信息窗口 - const content = ` -
    -
    ${drone.id}
    -

    型号: ${drone.model}
    - 状态: ${getStatusText(drone.status)}
    - 电量: ${drone.battery}%
    - 任务: ${drone.task}

    -
    - `; - - const infoWindow = new BMap.InfoWindow(content, { - width: 200, - height: 120, - title: `${drone.id} - ${drone.model}` - }); - - // 点击标记时显示信息窗口 - marker.addEventListener('click', function() { - map.openInfoWindow(infoWindow, point); - }); - - // 将标记添加到地图 - map.addOverlay(marker); - droneMarkers[drone.id] = { - marker: marker, - point: point - }; - }); -} - -function updateDronePositions() { - // 模拟无人机位置更新 - Object.keys(droneMarkers).forEach(droneId => { - const markerData = droneMarkers[droneId]; - if (markerData && markerData.marker) { - const currentPoint = markerData.point; - - // 随机移动位置 - const newLng = currentPoint.lng + (Math.random() - 0.5) * 0.01; - const newLat = currentPoint.lat + (Math.random() - 0.5) * 0.01; - const newPoint = new BMap.Point(newLng, newLat); - - // 创建动画移动效果 - const moveAnimation = new BMap.Animation.Move(markerData.marker, { - points: [currentPoint, newPoint], - duration: 1000 - }); - moveAnimation.start(); - - // 更新保存的位置 - markerData.point = newPoint; - } - }); -} - -function updateBatteryLevels() { - // 模拟电池电量更新 - document.querySelectorAll('.battery-level').forEach(battery => { - const currentWidth = parseInt(battery.style.width); - if (currentWidth > 0) { - battery.style.width = `${currentWidth - 1}%`; - } - }); -} - -function updateAlerts() { - // 模拟告警更新 - const alertPanel = document.querySelector('.alert-panel ul'); - const currentTime = new Date().toLocaleTimeString(); - - // 随机添加新告警 - if (Math.random() < 0.3) { - const alerts = [ - '通信信号弱,建议调整位置', - '检测到强风,建议降低飞行高度', - '接近禁飞区域边界', - '发现可疑目标,建议进一步确认' - ]; - - const newAlert = alerts[Math.floor(Math.random() * alerts.length)]; - const li = document.createElement('li'); - li.innerHTML = `• ${currentTime} - ${newAlert}`; - - alertPanel.insertBefore(li, alertPanel.firstChild); - - // 限制告警数量 - if (alertPanel.children.length > 5) { - alertPanel.removeChild(alertPanel.lastChild); - } - } -} - -function showDroneDetails(droneId) { - // 更新详情面板 - const detailPanel = document.getElementById('droneDetailPanel'); - detailPanel.querySelector('h5').textContent = `无人机详情 - ${droneId}`; - - // 模拟加载详细信息 - // 实际应用中应该从后端获取数据 -} - -function controlDrone(droneId) { - // 实现无人机控制逻辑 - console.log(`控制无人机: ${droneId}`); -} - -function maintainDrone(droneId) { - // 实现维护模式逻辑 - console.log(`维护无人机: ${droneId}`); -} - -function assignTask() { - const formData = { - droneId: document.getElementById('droneSelect').value, - taskType: document.getElementById('taskType').value, - taskArea: document.getElementById('taskArea').value, - priority: document.getElementById('taskPriority').value, - estimatedTime: document.getElementById('estimatedTime').value - }; - - // 实际应用中应该发送到后端 - console.log('分配任务:', formData); - - // 模拟成功响应 - alert('任务分配成功!'); -} - -function saveDrone() { - const formData = { - model: document.getElementById('droneModel').value, - serialNumber: document.getElementById('serialNumber').value, - maxFlightTime: document.getElementById('maxFlightTime').value, - maxPayload: document.getElementById('maxPayload').value, - communicationBand: document.getElementById('communicationBand').value, - hasCamera: document.getElementById('hasCamera').checked, - hasInfrared: document.getElementById('hasInfrared').checked, - hasGPS: document.getElementById('hasGPS').checked - }; - - // 实际应用中应该发送到后端 - console.log('添加无人机:', formData); - - // 模拟成功响应 - alert('无人机添加成功!'); - - // 关闭模态框 - const modal = bootstrap.Modal.getInstance(document.getElementById('addDroneModal')); - modal.hide(); -} - -// 辅助函数 -function getStatusText(status) { - const statusMap = { - 'online': '在线', - 'mission': '任务中', - 'maintenance': '维护中', - 'offline': '离线' - }; - return statusMap[status] || status; -} - -function formatPosition(position) { - return `N ${position.lat.toFixed(4)}° E ${position.lng.toFixed(4)}°`; -} \ No newline at end of file diff --git a/common/js/identification.js b/common/js/identification.js deleted file mode 100644 index 136dee7..0000000 --- a/common/js/identification.js +++ /dev/null @@ -1,500 +0,0 @@ -// 更新时间显示 -function updateTime() { - const now = new Date(); - document.getElementById('current-time').textContent = now.toLocaleString(); -} -setInterval(updateTime, 1000); - -// 初始化图表 -function initializeCharts() { - // 置信度历史图表 - const confidenceCtx = document.getElementById('confidenceChart').getContext('2d'); - const confidenceChart = new Chart(confidenceCtx, { - type: 'line', - data: { - labels: ['10:25', '10:26', '10:27', '10:28', '10:29', '10:30'], - datasets: [{ - label: '识别置信度', - data: [65, 75, 82, 78, 85, 88], - borderColor: '#00b4d8', - backgroundColor: 'rgba(0, 180, 216, 0.1)', - tension: 0.4, - fill: true - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - }, - tooltip: { - mode: 'index', - intersect: false, - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleColor: '#fff', - bodyColor: '#fff', - borderColor: 'rgba(255, 255, 255, 0.1)', - borderWidth: 1 - } - }, - scales: { - y: { - beginAtZero: true, - max: 100, - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: 'rgba(255, 255, 255, 0.7)', - callback: function(value) { - return value + '%'; - } - } - }, - x: { - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: 'rgba(255, 255, 255, 0.7)' - } - } - }, - interaction: { - intersect: false, - mode: 'index' - }, - animation: { - duration: 1000, - easing: 'easeInOutQuart' - } - } - }); - - // 目标类型分布图表 - const distributionCtx = document.getElementById('typeDistributionChart').getContext('2d'); - const distributionChart = new Chart(distributionCtx, { - type: 'doughnut', - data: { - labels: ['友方目标', '敌方目标', '未知目标'], - datasets: [{ - data: [4, 3, 2], - backgroundColor: [ - '#2ecc71', - '#e74c3c', - '#f1c40f' - ], - borderWidth: 0, - hoverOffset: 4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'right', - labels: { - color: 'rgba(255, 255, 255, 0.7)', - font: { - size: 11 - }, - padding: 20, - usePointStyle: true, - pointStyle: 'circle' - } - }, - tooltip: { - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleColor: '#fff', - bodyColor: '#fff', - borderColor: 'rgba(255, 255, 255, 0.1)', - borderWidth: 1 - } - }, - animation: { - animateRotate: true, - animateScale: true - } - } - }); - - // 导出图表更新函数 - window.updateCharts = { - updateConfidence: function(newData) { - confidenceChart.data.datasets[0].data = newData; - confidenceChart.update('none'); - }, - updateDistribution: function(newData) { - distributionChart.data.datasets[0].data = newData; - distributionChart.update('none'); - } - }; -} - -// 模拟目标检测框 -function simulateTargetDetection() { - const videoContainer = document.querySelector('.video-container'); - const targetBox = document.querySelector('.target-box'); - let isTracking = false; - - function updateTargetPosition() { - if (!isTracking) return; - - const maxX = videoContainer.clientWidth - 100; - const maxY = videoContainer.clientHeight - 100; - const x = Math.random() * maxX; - const y = Math.random() * maxY; - - targetBox.style.display = 'block'; - targetBox.style.left = x + 'px'; - targetBox.style.top = y + 'px'; - - // 更新目标信息 - updateTargetInfo({ - type: ['未知目标', '友方目标', '敌方目标'][Math.floor(Math.random() * 3)], - distance: Math.floor(Math.random() * 5000 + 1000), - confidence: Math.floor(Math.random() * 30 + 70) - }); - } - - // 开始/停止跟踪 - window.toggleTracking = function(start) { - isTracking = start; - if (start) { - targetBox.style.display = 'block'; - updateTargetPosition(); - } else { - targetBox.style.display = 'none'; - } - }; - - setInterval(updateTargetPosition, 3000); -} - -// 更新目标信息 -function updateTargetInfo(data) { - const targetType = document.getElementById('targetType'); - const confidenceBar = document.querySelector('.progress-bar'); - const distance = document.getElementById('distance'); - const threatLevel = document.getElementById('threatLevel'); - - targetType.textContent = data.type; - confidenceBar.style.width = data.confidence + '%'; - confidenceBar.textContent = data.confidence + '%'; - distance.textContent = (data.distance / 1000).toFixed(1) + 'km'; - - // 更新威胁等级 - const threatLevels = ['低', '中', '高']; - const threatColors = ['success', 'warning', 'danger']; - const threatIndex = Math.floor(Math.random() * 3); - threatLevel.className = `badge bg-${threatColors[threatIndex]}`; - threatLevel.textContent = threatLevels[threatIndex]; -} - -// 绑定控制按钮事件 -function bindControlEvents() { - const startBtn = document.getElementById('startStream'); - const stopBtn = document.getElementById('stopStream'); - const captureBtn = document.getElementById('captureImage'); - const toggleARBtn = document.getElementById('toggleAR'); - const toggleNightVisionBtn = document.getElementById('toggleNightVision'); - const toggleThermalBtn = document.getElementById('toggleThermal'); - - let isStreaming = false; - - startBtn.addEventListener('click', () => { - isStreaming = true; - document.querySelector('.status-dot').classList.add('active'); - window.toggleTracking(true); - showToast('开始目标识别'); - }); - - stopBtn.addEventListener('click', () => { - isStreaming = false; - document.querySelector('.status-dot').classList.remove('active'); - window.toggleTracking(false); - showToast('停止目标识别'); - }); - - captureBtn.addEventListener('click', () => { - showToast('已保存截图'); - // 添加截图动画效果 - const flash = document.createElement('div'); - flash.className = 'capture-flash'; - document.querySelector('.video-container').appendChild(flash); - setTimeout(() => flash.remove(), 200); - }); - - const toggleButtons = [ - { btn: toggleARBtn, name: 'AR叠加' }, - { btn: toggleNightVisionBtn, name: '夜视模式' }, - { btn: toggleThermalBtn, name: '热成像' } - ]; - - toggleButtons.forEach(({ btn, name }) => { - btn.addEventListener('click', () => { - const isActive = btn.classList.toggle('active'); - showToast(`${name}已${isActive ? '开启' : '关闭'}`); - }); - }); -} - -// Toast提示 -function showToast(message) { - const toast = document.createElement('div'); - toast.className = 'toast-message'; - toast.textContent = message; - document.body.appendChild(toast); - - setTimeout(() => { - toast.classList.add('show'); - setTimeout(() => { - toast.classList.remove('show'); - setTimeout(() => toast.remove(), 300); - }, 2000); - }, 100); -} - -// 页面加载完成后初始化 -document.addEventListener('DOMContentLoaded', function() { - initializeCharts(); - simulateTargetDetection(); - bindControlEvents(); - updateTime(); - - // 添加键盘快捷键 - document.addEventListener('keydown', function(e) { - if (e.key === 'F' || e.key === 'f') { - document.getElementById('captureImage').click(); - } - }); -}); - -// 初始化筛选器 -function initializeFilters() { - const filterForm = document.getElementById('filterForm'); - filterForm.addEventListener('submit', function(e) { - e.preventDefault(); - loadRecords(); - }); - - // 重置按钮事件 - filterForm.addEventListener('reset', function(e) { - setTimeout(() => loadRecords(), 0); - }); - - // 时间范围选择器事件 - const timeRange = document.getElementById('timeRange'); - timeRange.addEventListener('change', function() { - if (this.value === 'custom') { - // TODO: 显示自定义日期选择器 - } - }); -} - -// 加载记录数据 -async function loadRecords() { - try { - const filters = getFilters(); - // TODO: 替换为实际的API调用 - const response = await fetch('/api/records?' + new URLSearchParams(filters)); - const data = await response.json(); - - updateRecordsList(data.records); - updatePagination(data.pagination); - updateStats(data.stats); - } catch (error) { - console.error('加载记录失败:', error); - showError('加载记录失败,请稍后重试'); - } -} - -// 获取筛选条件 -function getFilters() { - return { - timeRange: document.getElementById('timeRange').value, - targetType: document.getElementById('targetType').value, - identificationStatus: document.getElementById('identificationStatus').value, - threatLevel: document.getElementById('threatLevel').value, - page: currentPage - }; -} - -// 更新记录列表 -function updateRecordsList(records) { - const tbody = document.getElementById('recordsList'); - tbody.innerHTML = records.map(record => ` - - ${formatDateTime(record.time)} - ${formatType(record.type)} - ${record.result} - -
    -
    -
    -
    - ${record.confidence}% - - ${formatThreatLevel(record.threatLevel)} - ${formatLocation(record.location)} - - - - - `).join(''); -} - -// 更新分页 -function updatePagination(pagination) { - const paginationElement = document.querySelector('.pagination'); - paginationElement.innerHTML = ` -
  • - 上一页 -
  • - ${generatePageNumbers(pagination)} -
  • - 下一页 -
  • - `; -} - -// 生成页码 -function generatePageNumbers(pagination) { - let pages = []; - const current = pagination.currentPage; - const total = pagination.totalPages; - - if (total <= 5) { - for (let i = 1; i <= total; i++) { - pages.push(i); - } - } else { - if (current <= 3) { - pages = [1, 2, 3, 4, '...', total]; - } else if (current >= total - 2) { - pages = [1, '...', total - 3, total - 2, total - 1, total]; - } else { - pages = [1, '...', current - 1, current, current + 1, '...', total]; - } - } - - return pages.map(page => { - if (page === '...') { - return '
  • ...
  • '; - } - return ` -
  • - ${page} -
  • - `; - }).join(''); -} - -// 显示记录详情 -function showRecordDetail(recordId) { - // TODO: 替换为实际的API调用 - fetch(`/api/records/${recordId}`) - .then(response => response.json()) - .then(record => { - document.getElementById('detailTime').textContent = formatDateTime(record.time); - document.getElementById('detailType').innerHTML = `${formatType(record.type)}`; - document.getElementById('detailResult').textContent = record.result; - document.getElementById('detailConfidence').innerHTML = ` -
    -
    -
    -
    - ${record.confidence}% - `; - document.getElementById('detailThreat').innerHTML = `${formatThreatLevel(record.threatLevel)}`; - document.getElementById('detailLocation').textContent = formatLocation(record.location); - document.getElementById('targetImage').src = record.imageUrl; - - const modal = new bootstrap.Modal(document.getElementById('recordDetailModal')); - modal.show(); - }) - .catch(error => { - console.error('加载记录详情失败:', error); - showError('加载记录详情失败,请稍后重试'); - }); -} - -// 格式化函数 -function formatDateTime(timestamp) { - return new Date(timestamp).toLocaleString('zh-CN', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }); -} - -function formatType(type) { - const types = { - 'friendly': '友方', - 'hostile': '敌方', - 'unknown': '未知' - }; - return types[type.toLowerCase()] || type; -} - -function formatThreatLevel(level) { - const levels = { - 'high': '高', - 'medium': '中', - 'low': '低' - }; - return levels[level.toLowerCase()] || level; -} - -function formatLocation(location) { - if (typeof location === 'object') { - return `N ${location.latitude}° E ${location.longitude}°`; - } - return location; -} - -// 辅助函数 -function getConfidenceColor(confidence) { - if (confidence >= 90) return 'success'; - if (confidence >= 70) return 'warning'; - return 'danger'; -} - -function getThreatClass(level) { - const classes = { - 'high': 'danger', - 'medium': 'warning', - 'low': 'success' - }; - return classes[level.toLowerCase()] || 'secondary'; -} - -// 错误提示 -function showError(message) { - // TODO: 实现错误提示UI - console.error(message); -} - -// 导出功能 -document.querySelector('.btn-group .btn-outline-primary').addEventListener('click', function() { - const filters = getFilters(); - // TODO: 实现导出功能 - window.location.href = '/api/records/export?' + new URLSearchParams(filters); -}); - -// 打印功能 -document.querySelectorAll('.btn-group .btn-outline-primary')[1].addEventListener('click', function() { - window.print(); -}); \ No newline at end of file diff --git a/common/js/signal.js b/common/js/signal.js deleted file mode 100644 index bc69b8e..0000000 --- a/common/js/signal.js +++ /dev/null @@ -1,261 +0,0 @@ -// 页面加载完成后初始化 -document.addEventListener('DOMContentLoaded', function() { - initializeSignalCanvas(); - initializeControls(); - setupEventListeners(); -}); - -let isScanning = false; -let animationFrameId = null; -let scanningData = []; -let currentFrequency = 2400; // MHz - -// 初始化信号画布 -function initializeSignalCanvas() { - const canvas = document.getElementById('signalCanvas'); - const ctx = canvas.getContext('2d'); - - // 设置画布大小 - function resizeCanvas() { - const container = canvas.parentElement; - canvas.width = container.clientWidth; - canvas.height = container.clientHeight; - } - - resizeCanvas(); - window.addEventListener('resize', resizeCanvas); - - // 初始化扫描数据 - for (let i = 0; i < 100; i++) { - scanningData.push(Math.random() * 0.5); - } - - // 开始动画循环 - function animate() { - if (!isScanning) return; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - drawSignalWaveform(ctx); - animationFrameId = requestAnimationFrame(animate); - } - - // 绘制信号波形 - function drawSignalWaveform(ctx) { - const width = canvas.width; - const height = canvas.height; - const points = 100; - - // 更新扫描数据 - scanningData.shift(); - scanningData.push(Math.random() * 0.5 + (isScanning ? Math.random() * 0.5 : 0)); - - // 绘制网格 - drawGrid(ctx, width, height); - - // 绘制波形 - ctx.beginPath(); - ctx.strokeStyle = '#00b4d8'; - ctx.lineWidth = 2; - - for (let i = 0; i < points; i++) { - const x = (i / points) * width; - const y = height - (scanningData[i] * height); - - if (i === 0) { - ctx.moveTo(x, y); - } else { - ctx.lineTo(x, y); - } - } - - ctx.stroke(); - - // 绘制填充 - ctx.lineTo(width, height); - ctx.lineTo(0, height); - ctx.closePath(); - - const gradient = ctx.createLinearGradient(0, 0, 0, height); - gradient.addColorStop(0, 'rgba(0, 180, 216, 0.2)'); - gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); - ctx.fillStyle = gradient; - ctx.fill(); - } - - // 绘制网格 - function drawGrid(ctx, width, height) { - const gridSize = 40; - ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; - ctx.lineWidth = 1; - - // 绘制垂直线 - for (let x = 0; x <= width; x += gridSize) { - ctx.beginPath(); - ctx.moveTo(x, 0); - ctx.lineTo(x, height); - ctx.stroke(); - } - - // 绘制水平线 - for (let y = 0; y <= height; y += gridSize) { - ctx.beginPath(); - ctx.moveTo(0, y); - ctx.lineTo(width, y); - ctx.stroke(); - } - } - - // 导出动画控制函数 - window.startAnimation = function() { - if (!isScanning) { - isScanning = true; - animate(); - } - }; - - window.stopAnimation = function() { - isScanning = false; - if (animationFrameId) { - cancelAnimationFrame(animationFrameId); - } - }; -} - -// 初始化控制器 -function initializeControls() { - const startButton = document.getElementById('startSignalScan'); - const stopButton = document.getElementById('stopSignalScan'); - const captureButton = document.getElementById('captureSignal'); - const frequencyRange = document.getElementById('frequencyRange'); - const scanSpeed = document.getElementById('scanSpeed'); - - // 开始扫描 - startButton.addEventListener('click', function() { - startButton.disabled = true; - stopButton.disabled = false; - window.startAnimation(); - updateSignalInfo(); - }); - - // 停止扫描 - stopButton.addEventListener('click', function() { - startButton.disabled = false; - stopButton.disabled = true; - window.stopAnimation(); - }); - - // 捕获信号 - captureButton.addEventListener('click', function() { - captureCurrentSignal(); - }); - - // 频率范围选择 - frequencyRange.addEventListener('change', function() { - updateFrequencyRange(this.value); - }); - - // 扫描速度调节 - scanSpeed.addEventListener('input', function() { - updateScanSpeed(this.value); - }); -} - -// 更新信号信息 -function updateSignalInfo() { - if (!isScanning) return; - - // 模拟信号数据更新 - const frequencyStep = Math.random() * 10 - 5; - currentFrequency = Math.max(300, Math.min(5000, currentFrequency + frequencyStep)); - - document.getElementById('currentFrequency').textContent = `${(currentFrequency / 1000).toFixed(2)} GHz`; - document.getElementById('signalStrength').textContent = `${-Math.floor(Math.random() * 20 + 60)} dBm`; - document.getElementById('bandwidth').textContent = `${Math.floor(Math.random() * 10 + 15)} MHz`; - document.getElementById('snr').textContent = `${Math.floor(Math.random() * 10 + 25)} dB`; - - setTimeout(() => { - if (isScanning) { - updateSignalInfo(); - } - }, 1000); -} - -// 捕获当前信号 -function captureCurrentSignal() { - const signalTypes = ['遥控信号', '数据链路', '图传信号', '干扰信号']; - const modulationTypes = ['QPSK', 'OFDM', '16QAM', 'FHSS']; - const encryptionTypes = ['AES-256', 'RSA-2048', 'ECC', '未加密']; - - // 更新信号特征 - document.getElementById('signalType').textContent = signalTypes[Math.floor(Math.random() * signalTypes.length)]; - document.getElementById('modulationType').textContent = modulationTypes[Math.floor(Math.random() * modulationTypes.length)]; - document.getElementById('encryptionType').textContent = encryptionTypes[Math.floor(Math.random() * encryptionTypes.length)]; - - // 显示捕获成功提示 - showToast('信号捕获成功'); -} - -// 更新频率范围 -function updateFrequencyRange(range) { - const ranges = { - 'low': { min: 300, max: 1000 }, - 'mid': { min: 1000, max: 2400 }, - 'high': { min: 2400, max: 5000 } - }; - - if (range !== 'all' && ranges[range]) { - currentFrequency = (ranges[range].min + ranges[range].max) / 2; - } -} - -// 更新扫描速度 -function updateScanSpeed(speed) { - // 实现扫描速度调节逻辑 -} - -// 设置事件监听器 -function setupEventListeners() { - // 全屏切换 - document.getElementById('toggleFullscreen').addEventListener('click', function() { - const signalContainer = document.querySelector('.signal-container'); - if (!document.fullscreenElement) { - signalContainer.requestFullscreen(); - this.innerHTML = ''; - } else { - document.exitFullscreen(); - this.innerHTML = ''; - } - }); - - // 导出记录 - document.querySelector('.btn-outline-primary').addEventListener('click', function() { - // 实现导出功能 - showToast('记录导出成功'); - }); - - // 清除记录 - document.querySelector('.btn-outline-danger').addEventListener('click', function() { - if (confirm('确定要清除所有记录吗?')) { - // 实现清除功能 - showToast('记录已清除'); - } - }); -} - -// 显示提示信息 -function showToast(message) { - // 创建提示元素 - const toast = document.createElement('div'); - toast.className = 'toast-message'; - toast.textContent = message; - document.body.appendChild(toast); - - // 添加动画类 - setTimeout(() => toast.classList.add('show'), 100); - - // 自动移除 - setTimeout(() => { - toast.classList.remove('show'); - setTimeout(() => toast.remove(), 300); - }, 3000); -} \ No newline at end of file -- 2.34.1 From 075c317354ef540f537ceb70bad0eed9ad0b0c7a Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 11:04:40 +0800 Subject: [PATCH 09/10] delete --- serve.py | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 serve.py diff --git a/serve.py b/serve.py deleted file mode 100644 index 6df68de..0000000 --- a/serve.py +++ /dev/null @@ -1,33 +0,0 @@ -import http.server -import socketserver -import os - -# 设置端口 -PORT = 8080 - -# 自定义处理器添加CORS头 -class CORSHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): - def end_headers(self): - # 添加CORS头 - self.send_header('Access-Control-Allow-Origin', '*') - self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') - self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') - super().end_headers() - - def do_OPTIONS(self): - # 处理OPTIONS请求 - self.send_response(200) - self.end_headers() - -# 打印启动信息 -print(f"启动前端服务器,端口: {PORT}") -print(f"请在浏览器中访问: http://localhost:{PORT}") -print("按Ctrl+C终止服务器") - -# 启动服务器 -with socketserver.TCPServer(("", PORT), CORSHTTPRequestHandler) as httpd: - print("前端服务器正在运行...") - try: - httpd.serve_forever() - except KeyboardInterrupt: - print("\n服务器已停止") \ No newline at end of file -- 2.34.1 From 3090f7e1a253607cd84d8431186b2433ea2bb8d9 Mon Sep 17 00:00:00 2001 From: xuyuanpeng22 <2088213375@qq.com> Date: Fri, 11 Jul 2025 11:06:23 +0800 Subject: [PATCH 10/10] 3.0 --- frontend/common/css/style.css | 1612 +++++++++++++ frontend/common/js/api.js | 200 ++ frontend/common/js/battlefield.js | 732 ++++++ frontend/common/js/common.js | 169 ++ frontend/common/js/drones.js | 341 +++ frontend/common/js/identification.js | 500 +++++ frontend/common/js/signal.js | 261 +++ frontend/dashboard.html | 203 ++ frontend/drones.html | 451 ++++ frontend/identification.html | 266 +++ frontend/idx.html | 3102 ++++++++++++++++++++++++++ frontend/index.html | 14 + frontend/login.html | 75 + frontend/records.html | 310 +++ frontend/serve.py | 33 + frontend/signal.html | 333 +++ 16 files changed, 8602 insertions(+) create mode 100644 frontend/common/css/style.css create mode 100644 frontend/common/js/api.js create mode 100644 frontend/common/js/battlefield.js create mode 100644 frontend/common/js/common.js create mode 100644 frontend/common/js/drones.js create mode 100644 frontend/common/js/identification.js create mode 100644 frontend/common/js/signal.js create mode 100644 frontend/dashboard.html create mode 100644 frontend/drones.html create mode 100644 frontend/identification.html create mode 100644 frontend/idx.html create mode 100644 frontend/index.html create mode 100644 frontend/login.html create mode 100644 frontend/records.html create mode 100644 frontend/serve.py create mode 100644 frontend/signal.html diff --git a/frontend/common/css/style.css b/frontend/common/css/style.css new file mode 100644 index 0000000..02f3d91 --- /dev/null +++ b/frontend/common/css/style.css @@ -0,0 +1,1612 @@ +/* 全局样式 */ +:root { + --primary-color: #1a2634; + --secondary-color: #2c3e50; + --accent-color: #00b4d8; + --text-color: #e9ecef; + --border-color: #2d4356; + --card-bg: rgba(26, 38, 52, 0.8); + --success-color: #2ecc71; + --warning-color: #f1c40f; + --danger-color: #e74c3c; + --info-color: #3498db; + --executing-color: #3498db; + --identification-color: #2ecc71; + --friendly-color: #27ae60; + --hostile-color: #e74c3c; +} + +body { + font-family: 'Noto Sans SC', sans-serif; + background-color: var(--primary-color); + color: var(--text-color); + min-height: 100vh; +} + +.battlefield-theme { + background: + linear-gradient(135deg, rgba(26, 38, 52, 0.95) 0%, rgba(44, 62, 80, 0.95) 100%), + url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%230a4b78' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"), + url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%230a4b78' fill-opacity='0.05'%3E%3Cpath d='M50 50l30-30M50 50l-30 30M50 50l30 30M50 50l-30-30'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-attachment: fixed; +} + +/* 动画效果 */ +@keyframes pulse { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes glow { + 0% { + box-shadow: 0 0 5px rgba(0, 180, 216, 0.2); + } + 50% { + box-shadow: 0 0 20px rgba(0, 180, 216, 0.4); + } + 100% { + box-shadow: 0 0 5px rgba(0, 180, 216, 0.2); + } +} + +/* 侧边栏样式 */ +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 250px; + background-color: var(--secondary-color); + border-right: 1px solid var(--border-color); + padding: 1rem; + z-index: 1000; +} + +.sidebar-header { + padding: 1rem 0; + border-bottom: 1px solid var(--border-color); +} + +.sidebar-header h4 { + color: var(--text-color); + margin: 0; + display: flex; + align-items: center; +} + +.logo-icon { + margin-right: 0.5rem; + color: var(--accent-color); +} + +.nav-link { + color: var(--text-color); + padding: 0.8rem 1rem; + margin: 0.2rem 0; + border-radius: 5px; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + background-color: var(--accent-color); + color: white; +} + +.nav-link i { + margin-right: 0.5rem; + width: 20px; + text-align: center; +} + +/* 主内容区域 */ +.main-content { + margin-left: 250px; + padding: 1rem; + min-height: 100vh; + max-height: 100vh; + overflow: hidden; +} + +/* 战场头部 */ +.battlefield-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + padding: 0.5rem 1rem; + background: var(--card-bg); + border-radius: 10px; + border: 1px solid var(--border-color); +} + +.header-left { + display: flex; + align-items: center; + gap: 1.5rem; +} + +.battlefield-time { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--accent-color); + font-size: 0.9rem; +} + +.operator-info { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--text-color); +} + +.status-badge { + padding: 0.25rem 0.5rem; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 500; +} + +.status-badge.online { + background: rgba(46, 204, 113, 0.2); + color: var(--success-color); +} + +.page-title { + color: var(--text-color); + margin: 0; + font-weight: 500; +} + +/* 状态卡片 */ +.status-cards { + margin-bottom: 1rem; +} + +.status-cards .col-md-3 { + padding: 0.4rem; + animation: fadeIn 0.5s ease-out forwards; +} + +.status-cards .col-md-3:nth-child(1) { animation-delay: 0s; } +.status-cards .col-md-3:nth-child(2) { animation-delay: 0.2s; } +.status-cards .col-md-3:nth-child(3) { animation-delay: 0.4s; } +.status-cards .col-md-3:nth-child(4) { animation-delay: 0.6s; } + +.status-card { + background: var(--card-bg); + border-radius: 8px; + padding: 12px; + margin-bottom: 0.8rem; + position: relative; + overflow: hidden; + min-height: 90px; + border: 1px solid rgba(255, 255, 255, 0.1); + transition: all 0.3s ease; +} + +.status-card.executing { + background: linear-gradient(135deg, var(--executing-color) 0%, rgba(52, 152, 219, 0.2) 100%); +} + +.status-card.identification { + background: linear-gradient(135deg, var(--identification-color) 0%, rgba(46, 204, 113, 0.2) 100%); +} + +.status-card.friendly { + background: linear-gradient(135deg, var(--friendly-color) 0%, rgba(39, 174, 96, 0.2) 100%); +} + +.status-card.hostile { + background: linear-gradient(135deg, var(--hostile-color) 0%, rgba(231, 76, 60, 0.2) 100%); +} + +.status-info { + position: relative; + z-index: 1; +} + +.status-info h3 { + font-size: 0.9rem; + margin: 0 0 8px 0; + color: rgba(255, 255, 255, 0.9); + font-weight: 500; +} + +.status-value { + font-size: 1.5rem; + font-weight: 600; + color: white; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.status-chart { + width: 60px; + height: 40px; + margin-left: 15px; + opacity: 0.5; +} + +/* 主雷达卡片 */ +.tactical-radar { + width: 100%; + height: 500px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + position: relative; + overflow: hidden; +} + +.main-radar-card { + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 10px; + margin-bottom: 1rem; +} + +.main-radar-card .card-body { + padding: 1rem; + height: 100%; +} + +.radar-controls { + display: flex; + gap: 0.5rem; +} + +.radar-controls .btn { + width: 32px; + height: 32px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.1); + border: none; + color: var(--text-color); + transition: all 0.3s ease; +} + +.radar-controls .btn:hover { + background: var(--accent-color); + color: white; +} + +.radar-stats { + display: flex; + gap: 2rem; + padding: 1rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-bottom: 1rem; + flex-wrap: wrap; +} + +.stat-item { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.stat-item .label { + font-size: 0.8rem; + color: rgba(255, 255, 255, 0.7); +} + +.stat-item .value { + font-size: 1.1rem; + font-weight: 500; + color: var(--accent-color); +} + +.radar-legend { + display: flex; + gap: 2rem; + padding: 1rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-top: 1rem; + justify-content: center; +} + +.legend-item { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.legend-item .dot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.dot.friendly { + background: var(--success-color); +} + +.dot.hostile { + background: var(--danger-color); +} + +.dot.unknown { + background: var(--warning-color); +} + +/* 全屏模式样式 */ +.tactical-radar.fullscreen { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 9999; + margin: 0; + border-radius: 0; +} + +/* 威胁等级指示器 */ +.threat-level { + padding: 1rem; + background: rgba(0, 180, 216, 0.1); + border-radius: 5px; + margin-bottom: 1rem; +} + +.level-label { + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.level-value { + font-size: 2rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +.level-value.low { + color: var(--success-color); +} + +.level-indicator { + height: 6px; + background: rgba(255, 255, 255, 0.1); + border-radius: 3px; + overflow: hidden; +} + +.level-indicator .indicator-bar { + height: 100%; + background: var(--success-color); + border-radius: 3px; + transition: width 0.3s ease; +} + +/* 卡片工具栏 */ +.card-tools { + display: flex; + gap: 0.5rem; +} + +.card-tools .btn { + width: 28px; + height: 28px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + color: var(--accent-color); + border-color: var(--accent-color); +} + +/* 新增的图表区域 */ +.mission-stats, +.area-coverage, +.system-status { + height: 200px; +} + +/* 响应式调整 */ +@media (max-width: 1400px) { + .status-card { + height: 90px; + } + + .status-info h3 { + font-size: 0.85rem; + } + + .status-value { + font-size: 1.3rem; + } + + .main-radar-card { + height: calc(100vh - 250px); + } + + .threat-analysis, + .battlefield-activity { + height: calc((100vh - 250px) / 2 - 1rem); + } +} + +@media (max-width: 992px) { + .battlefield-header { + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; + } + + .header-left { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } +} + +@media (max-width: 768px) { + .sidebar { + transform: translateX(-100%); + } + .main-content { + margin-left: 0; + } + .status-card { + margin-bottom: 1rem; + } +} + +/* 视频容器样式 */ +.video-container { + position: relative; + width: 100%; + height: 480px; + background: rgba(0, 0, 0, 0.8); + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.video-stream { + width: 100%; + height: 100%; + object-fit: cover; +} + +.video-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; +} + +.target-box { + position: absolute; + border: 2px solid #00ff00; + box-shadow: 0 0 10px rgba(0, 255, 0, 0.5); + animation: pulse 2s infinite; +} + +.target-marker { + position: absolute; + width: 20px; + height: 20px; + border: 2px solid #00ff00; + border-radius: 50%; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.target-info { + position: absolute; + bottom: -40px; + left: 50%; + transform: translateX(-50%); + background: rgba(0, 0, 0, 0.8); + padding: 4px 12px; + border-radius: 4px; + font-size: 12px; + color: #fff; + white-space: nowrap; +} + +.overlay-info { + position: absolute; + top: 20px; + right: 20px; + background: rgba(0, 0, 0, 0.7); + padding: 10px; + border-radius: 8px; + color: #fff; +} + +.info-item { + margin: 5px 0; + font-size: 14px; +} + +.info-item i { + margin-right: 8px; + color: #00ff00; +} + +.video-controls { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-top: 15px; +} + +.stream-settings .btn { + margin-left: 10px; + position: relative; + overflow: hidden; +} + +.stream-settings .btn::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 5px; + height: 5px; + background: rgba(255, 255, 255, 0.8); + border-radius: 50%; + transform: translate(-50%, -50%); + opacity: 0; + transition: all 0.3s; +} + +.stream-settings .btn.active::after { + opacity: 1; + width: 10px; + height: 10px; +} + +.target-details { + padding: 15px; +} + +.detail-group { + margin-bottom: 20px; + background: rgba(0, 0, 0, 0.2); + padding: 15px; + border-radius: 8px; + transition: all 0.3s; +} + +.detail-group:hover { + background: rgba(0, 0, 0, 0.3); + transform: translateY(-2px); +} + +.detail-item { + margin-bottom: 10px; +} + +.detail-item label { + display: block; + color: rgba(255, 255, 255, 0.7); + font-size: 12px; + margin-bottom: 5px; +} + +.detail-item .value { + font-size: 16px; + font-weight: 500; + color: #fff; +} + +.detail-item .progress { + height: 6px; + background: rgba(255, 255, 255, 0.1); +} + +.target-actions { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.target-actions .btn { + flex: 1; + min-width: 120px; + position: relative; + overflow: hidden; +} + +.target-actions .btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent); + transform: translateX(-100%); + transition: all 0.3s; +} + +.target-actions .btn:hover::before { + transform: translateX(100%); +} + +.recent-identifications { + max-height: 400px; + overflow-y: auto; + padding: 10px; +} + +.identification-item { + display: flex; + align-items: center; + padding: 12px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-bottom: 10px; + transition: all 0.3s; +} + +.identification-item:hover { + background: rgba(0, 0, 0, 0.3); + transform: translateX(5px); +} + +.item-time { + font-size: 12px; + color: rgba(255, 255, 255, 0.7); + width: 80px; +} + +.item-content { + flex: 1; + margin: 0 15px; +} + +.item-type { + font-weight: 500; + margin-bottom: 4px; +} + +.item-type.hostile { + color: #ff4757; +} + +.item-type.friendly { + color: #2ecc71; +} + +.item-type.unknown { + color: #f1c40f; +} + +.item-info { + font-size: 12px; + color: rgba(255, 255, 255, 0.7); +} + +.item-status { + font-size: 12px; + padding: 4px 8px; + border-radius: 4px; + background: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.7); +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.4); + } + 70% { + box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); + } +} + +/* 图表容器样式优化 */ +.analysis-chart { + height: 250px; + padding: 15px; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + margin-bottom: 15px; +} + +/* 响应式调整 */ +@media (max-width: 992px) { + .video-container { + height: 360px; + } + + .target-actions .btn { + min-width: 100px; + } + + .detail-group { + padding: 10px; + } +} + +@media (max-width: 768px) { + .video-container { + height: 280px; + } + + .overlay-info { + top: 10px; + right: 10px; + font-size: 12px; + } + + .target-actions { + flex-direction: column; + } + + .target-actions .btn { + width: 100%; + } +} + +/* 记录页面样式 */ +.records-page .card { + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin-bottom: 1.5rem; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.records-page .card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +.records-page .card-header { + background: rgba(44, 62, 80, 0.5); + border-bottom: 1px solid var(--border-color); + padding: 1rem 1.25rem; +} + +.records-page .card-title { + margin: 0; + font-size: 1.1rem; + font-weight: 500; + color: var(--accent-color); +} + +.records-page .form-label { + color: var(--text-color); + font-weight: 500; + margin-bottom: 0.5rem; +} + +.records-page .form-select { + background-color: rgba(26, 38, 52, 0.8); + border: 1px solid var(--border-color); + color: var(--text-color); + transition: border-color 0.3s ease, box-shadow 0.3s ease; +} + +.records-page .form-select:focus { + border-color: var(--accent-color); + box-shadow: 0 0 0 0.25rem rgba(0, 180, 216, 0.25); +} + +.records-page .btn-primary { + background-color: var(--accent-color); + border: none; + padding: 0.5rem 1.25rem; + transition: all 0.3s ease; +} + +.records-page .btn-primary:hover { + background-color: #0095b3; + transform: translateY(-1px); +} + +.records-page .btn-secondary { + background-color: #34495e; + border: none; + padding: 0.5rem 1.25rem; +} + +.records-page .table { + color: var(--text-color); + margin-bottom: 0; +} + +.records-page .table thead th { + background: rgba(44, 62, 80, 0.5); + border-bottom: 2px solid var(--border-color); + color: var(--accent-color); + font-weight: 500; + padding: 1rem; +} + +.records-page .table tbody tr { + transition: background-color 0.3s ease; +} + +.records-page .table tbody tr:hover { + background-color: rgba(44, 62, 80, 0.3); +} + +.records-page .table td { + padding: 1rem; + vertical-align: middle; + border-bottom: 1px solid var(--border-color); +} + +.records-page .badge { + padding: 0.5rem 0.75rem; + border-radius: 20px; + font-weight: 500; +} + +.records-page .badge-friendly { + background-color: rgba(46, 204, 113, 0.2); + color: var(--success-color); +} + +.records-page .badge-hostile { + background-color: rgba(231, 76, 60, 0.2); + color: var(--danger-color); +} + +.records-page .badge-unknown { + background-color: rgba(52, 152, 219, 0.2); + color: var(--info-color); +} + +.records-page .stats-card { + background: linear-gradient(135deg, rgba(26, 38, 52, 0.9) 0%, rgba(44, 62, 80, 0.9) 100%); + border-radius: 12px; + padding: 1.5rem; + height: 100%; + transition: transform 0.3s ease; +} + +.records-page .stats-card:hover { + transform: translateY(-2px); +} + +.records-page .stats-icon { + font-size: 2rem; + color: var(--accent-color); + margin-bottom: 1rem; +} + +.records-page .stats-value { + font-size: 1.8rem; + font-weight: 600; + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.records-page .stats-label { + color: #a0aec0; + font-size: 0.9rem; +} + +.records-page .pagination { + margin-top: 2rem; +} + +.records-page .page-link { + background-color: rgba(26, 38, 52, 0.8); + border: 1px solid var(--border-color); + color: var(--text-color); + padding: 0.5rem 1rem; + transition: all 0.3s ease; +} + +.records-page .page-link:hover { + background-color: var(--accent-color); + color: white; + border-color: var(--accent-color); +} + +.records-page .page-item.active .page-link { + background-color: var(--accent-color); + border-color: var(--accent-color); +} + +.records-page .modal-content { + background: var(--card-bg); + border: 1px solid var(--border-color); +} + +.records-page .modal-header { + border-bottom: 1px solid var(--border-color); + padding: 1.25rem; +} + +.records-page .modal-title { + color: var(--accent-color); +} + +.records-page .modal-body { + padding: 1.5rem; +} + +.records-page .table-sm td { + padding: 0.75rem; + color: var(--text-color); +} + +/* 动画效果 */ +.records-page .fade-in { + animation: fadeIn 0.5s ease-out forwards; +} + +.records-page .slide-in { + animation: slideIn 0.5s ease-out forwards; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 信号识别页面样式 */ +.signal-page .card { + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin-bottom: 1.5rem; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.signal-page .card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +.signal-page .card-header { + background: rgba(44, 62, 80, 0.5); + border-bottom: 1px solid var(--border-color); + padding: 1rem 1.25rem; +} + +.signal-page .card-title { + margin: 0; + font-size: 1.1rem; + font-weight: 500; + color: var(--accent-color); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.signal-page .signal-container { + position: relative; + background: rgba(26, 38, 52, 0.9); + border-radius: 8px; + padding: 1rem; +} + +.signal-page .signal-controls { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + flex-wrap: wrap; +} + +.signal-page .btn-group { + display: flex; + gap: 0.5rem; +} + +.signal-page .btn { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border-radius: 6px; + transition: all 0.3s ease; +} + +.signal-page .btn i { + font-size: 1rem; +} + +.signal-page .btn-primary { + background: var(--accent-color); + border: none; +} + +.signal-page .btn-primary:hover { + background: #0095b3; + transform: translateY(-1px); +} + +.signal-page .frequency-selector { + display: flex; + align-items: center; + gap: 1rem; +} + +.signal-page .form-select { + background-color: rgba(26, 38, 52, 0.8); + border: 1px solid var(--border-color); + color: var(--text-color); + min-width: 120px; +} + +.signal-page .signal-display { + margin: 1.5rem 0; + position: relative; + background: rgba(0, 0, 0, 0.2); + border-radius: 8px; + overflow: hidden; + height: 300px; +} + +.signal-page .signal-display canvas { + width: 100%; + height: 100%; +} + +.signal-page .signal-info { + background: rgba(26, 38, 52, 0.6); + border-radius: 8px; + padding: 1rem; +} + +.signal-page .info-item { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.5rem; + border-radius: 6px; + background: rgba(0, 0, 0, 0.2); +} + +.signal-page .info-item label { + color: var(--accent-color); + margin: 0; + font-weight: 500; +} + +.signal-page .info-item span { + color: var(--text-color); + font-family: 'Roboto Mono', monospace; +} + +.signal-page .signal-features { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.signal-page .feature-item { + background: rgba(26, 38, 52, 0.6); + padding: 1rem; + border-radius: 8px; +} + +.signal-page .feature-item label { + color: var(--accent-color); + display: block; + margin-bottom: 0.5rem; + font-weight: 500; +} + +.signal-page .feature-item span { + color: var(--text-color); + font-family: 'Roboto Mono', monospace; +} + +.signal-page .progress { + height: 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + overflow: hidden; +} + +.signal-page .progress-bar { + transition: width 0.3s ease; +} + +.signal-page .threat-analysis { + background: rgba(26, 38, 52, 0.6); + border-radius: 8px; + padding: 1rem; +} + +.signal-page .threat-level { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; +} + +.signal-page .threat-level label { + color: var(--accent-color); + margin: 0; + font-weight: 500; +} + +.signal-page .threat-level .badge { + padding: 0.5rem 1rem; + font-size: 0.9rem; + border-radius: 20px; +} + +.signal-page .threat-details ul li { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 6px; + margin-bottom: 0.5rem; +} + +.signal-page .recommended-actions .btn { + text-align: left; + justify-content: flex-start; +} + +.signal-page .table { + color: var(--text-color); + margin: 0; +} + +.signal-page .table thead th { + background: rgba(44, 62, 80, 0.5); + border-bottom: 2px solid var(--border-color); + color: var(--accent-color); + font-weight: 500; + padding: 1rem; +} + +.signal-page .table tbody tr { + transition: background-color 0.3s ease; +} + +.signal-page .table tbody tr:hover { + background-color: rgba(44, 62, 80, 0.3); +} + +.signal-page .table td { + padding: 1rem; + vertical-align: middle; + border-bottom: 1px solid var(--border-color); +} + +/* 动画效果 */ +@keyframes signalPulse { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.02); + opacity: 0.8; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +@keyframes scanning { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +.signal-page .scanning { + animation: signalPulse 2s ease-in-out infinite; + background: linear-gradient(90deg, + rgba(0, 180, 216, 0.1) 0%, + rgba(0, 180, 216, 0.2) 50%, + rgba(0, 180, 216, 0.1) 100%); + background-size: 200% 100%; + animation: scanning 3s linear infinite; +} + +/* 信号页面额外样式 */ +.signal-page .frequency-controls { + display: flex; + align-items: center; + gap: 2rem; +} + +.signal-page .scan-speed { + display: flex; + align-items: center; + gap: 1rem; + min-width: 200px; +} + +.signal-page .form-range { + height: 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + cursor: pointer; +} + +.signal-page .form-range::-webkit-slider-thumb { + background: var(--accent-color); + width: 16px; + height: 16px; + border-radius: 50%; + cursor: pointer; + transition: transform 0.2s ease; +} + +.signal-page .form-range::-webkit-slider-thumb:hover { + transform: scale(1.2); +} + +.signal-page .signal-markers { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; +} + +.signal-page .frequency-marker { + position: absolute; + top: 0; + bottom: 0; +} + +.signal-page .marker-line { + position: absolute; + top: 0; + bottom: 0; + width: 1px; + background: rgba(255, 255, 255, 0.2); +} + +.signal-page .marker-label { + position: absolute; + bottom: 5px; + left: 50%; + transform: translateX(-50%); + background: rgba(0, 0, 0, 0.5); + padding: 2px 8px; + border-radius: 4px; + font-size: 0.8rem; + color: var(--accent-color); + white-space: nowrap; +} + +.signal-page .status-card { + background: linear-gradient(135deg, rgba(26, 38, 52, 0.9) 0%, rgba(44, 62, 80, 0.9) 100%); + border-radius: 12px; + padding: 1.5rem; + height: 100%; + transition: transform 0.3s ease; +} + +.signal-page .status-card:hover { + transform: translateY(-2px); +} + +.signal-page .status-icon { + font-size: 2rem; + color: var(--accent-color); + margin-bottom: 1rem; +} + +.signal-page .status-value { + font-size: 1.8rem; + font-weight: 600; + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.signal-page .status-label { + color: #a0aec0; + font-size: 0.9rem; +} + +/* Toast 消息样式 */ +.toast-message { + position: fixed; + top: 20px; + right: 20px; + background: rgba(0, 0, 0, 0.8); + color: #fff; + padding: 12px 24px; + border-radius: 4px; + z-index: 1000; + transform: translateX(120%); + transition: transform 0.3s ease-in-out; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); +} + +.toast-message.show { + transform: translateX(0); +} + +/* 全屏模式样式 */ +.signal-container:fullscreen { + background: var(--primary-color); + padding: 2rem; +} + +.signal-container:fullscreen .signal-display { + height: calc(100vh - 200px); +} + +/* 动画效果 */ +.signal-page .fade-in { + animation: fadeIn 0.5s ease-out forwards; +} + +/* 进度条动画 */ +.signal-page .progress-bar { + position: relative; + overflow: hidden; +} + +.signal-page .progress-bar::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.2) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: translateX(-100%); + animation: progressShine 2s linear infinite; +} + +@keyframes progressShine { + to { + transform: translateX(100%); + } +} + +/* 响应式调整 */ +@media (max-width: 1200px) { + .signal-page .frequency-controls { + flex-direction: column; + gap: 1rem; + } + + .signal-page .scan-speed { + width: 100%; + } +} + +@media (max-width: 768px) { + .signal-page .signal-info .row { + flex-direction: column; + } + + .signal-page .info-item { + margin-bottom: 0.5rem; + } + + .signal-page .status-card { + margin-bottom: 1rem; + } +} + +/* 截图闪光效果 */ +.capture-flash { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #fff; + opacity: 0; + animation: flash 0.2s ease-out; + pointer-events: none; +} + +@keyframes flash { + 0% { opacity: 0; } + 50% { opacity: 0.8; } + 100% { opacity: 0; } +} + +/* 按钮悬停效果 */ +.btn { + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn:active { + transform: translateY(0); +} + +.btn i { + margin-right: 6px; + transition: transform 0.3s ease; +} + +.btn:hover i { + transform: scale(1.2); +} + +/* 目标框动画效果 */ +.target-box { + transition: all 0.3s ease-out; +} + +.target-box:hover .target-info { + opacity: 1; + transform: translateY(0); +} + +.target-info { + opacity: 0; + transform: translateY(10px); + transition: all 0.3s ease-out; +} + +/* 图表容器悬停效果 */ +.analysis-chart { + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.analysis-chart:hover { + transform: translateY(-5px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); +} + +/* 状态标签动画 */ +.status-dot { + position: relative; +} + +.status-dot::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + background: inherit; + border-radius: 50%; + transform: translate(-50%, -50%) scale(1); + animation: pulse-dot 2s infinite; +} + +@keyframes pulse-dot { + 0% { + transform: translate(-50%, -50%) scale(1); + opacity: 0.5; + } + 70% { + transform: translate(-50%, -50%) scale(2); + opacity: 0; + } + 100% { + transform: translate(-50%, -50%) scale(1); + opacity: 0; + } +} + +/* 进度条动画 */ +.progress-bar { + transition: width 0.6s ease-in-out; +} + +/* 威胁等级标签动画 */ +.badge { + transition: all 0.3s ease; +} + +.badge:hover { + transform: scale(1.1); +} + +/* 最近识别列表动画 */ +.identification-item { + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 视频控制按钮组动画 */ +.video-controls .btn-group { + position: relative; + overflow: hidden; +} + +.video-controls .btn-group::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + animation: scanline 2s linear infinite; +} + +@keyframes scanline { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +/* 响应式调整 */ +@media (max-width: 768px) { + .toast-message { + top: auto; + bottom: 20px; + left: 20px; + right: 20px; + transform: translateY(120%); + } + + .toast-message.show { + transform: translateY(0); + } +} \ No newline at end of file diff --git a/frontend/common/js/api.js b/frontend/common/js/api.js new file mode 100644 index 0000000..7860156 --- /dev/null +++ b/frontend/common/js/api.js @@ -0,0 +1,200 @@ +// API 基础 URL +const API_BASE_URL = 'http://localhost:5000/api'; + +// API 调用函数 +const api = { + // 登录 + login: async (username, password) => { + try { + const response = await fetch(`${API_BASE_URL}/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '登录失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 获取无人机列表 + getDrones: async () => { + try { + const response = await fetch(`${API_BASE_URL}/drones`, { + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '获取无人机列表失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 添加无人机 + addDrone: async (droneData) => { + try { + const response = await fetch(`${API_BASE_URL}/drones`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: JSON.stringify(droneData) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '添加无人机失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 更新无人机 + updateDrone: async (droneId, droneData) => { + try { + const response = await fetch(`${API_BASE_URL}/drones/${droneId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: JSON.stringify(droneData) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '更新无人机失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 删除无人机 + deleteDrone: async (droneId) => { + try { + const response = await fetch(`${API_BASE_URL}/drones/${droneId}`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '删除无人机失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 获取识别记录 + getRecords: async (filters = {}) => { + try { + const queryParams = new URLSearchParams(filters).toString(); + const response = await fetch(`${API_BASE_URL}/records?${queryParams}`, { + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '获取识别记录失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 获取记录详情 + getRecordDetail: async (recordId) => { + try { + const response = await fetch(`${API_BASE_URL}/records/${recordId}`, { + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '获取记录详情失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 目标识别 + identify: async (formData) => { + try { + const response = await fetch(`${API_BASE_URL}/identify`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: formData + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '目标识别失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + }, + + // 信号识别 + identifySignal: async (signal) => { + try { + const response = await fetch(`${API_BASE_URL}/signal_identify`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${currentUser?.token}` + }, + body: JSON.stringify({ signal }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || '信号识别失败'); + } + + return await response.json(); + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/frontend/common/js/battlefield.js b/frontend/common/js/battlefield.js new file mode 100644 index 0000000..22c40d7 --- /dev/null +++ b/frontend/common/js/battlefield.js @@ -0,0 +1,732 @@ +// 更新时间显示 +function updateTime() { + const now = new Date(); + const timeString = now.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }); + document.getElementById('current-time').textContent = timeString; +} + +// 状态卡片迷你图表 +class StatusMiniChart { + constructor(container) { + this.container = container; + this.canvas = document.createElement('canvas'); + this.container.appendChild(this.canvas); + this.ctx = this.canvas.getContext('2d'); + this.resize(); + this.generateData(); + this.draw(); + } + + resize() { + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + } + + generateData() { + this.data = Array.from({length: 20}, () => Math.random() * 0.8 + 0.2); + } + + draw() { + const ctx = this.ctx; + const width = this.canvas.width; + const height = this.canvas.height; + const step = width / (this.data.length - 1); + + ctx.clearRect(0, 0, width, height); + + // 绘制曲线 + ctx.beginPath(); + ctx.moveTo(0, height * (1 - this.data[0])); + + for (let i = 1; i < this.data.length; i++) { + const x = i * step; + const y = height * (1 - this.data[i]); + + if (i === 0) { + ctx.moveTo(x, y); + } else { + const xc = (x + (i - 1) * step) / 2; + const yc = (y + height * (1 - this.data[i - 1])) / 2; + ctx.quadraticCurveTo(xc, yc, x, y); + } + } + + ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)'; + ctx.lineWidth = 2; + ctx.stroke(); + + // 填充渐变 + const gradient = ctx.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, 'rgba(255, 255, 255, 0.2)'); + gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); + + ctx.lineTo(width, height); + ctx.lineTo(0, height); + ctx.fillStyle = gradient; + ctx.fill(); + } + + update() { + this.data.shift(); + this.data.push(Math.random() * 0.8 + 0.2); + this.draw(); + } +} + +// 雷达扫描效果 +class TacticalRadar { + constructor(containerId) { + this.container = document.getElementById(containerId); + this.canvas = document.createElement('canvas'); + this.ctx = this.canvas.getContext('2d'); + this.container.appendChild(this.canvas); + this.resize(); + this.setupRadar(); + window.addEventListener('resize', () => this.resize()); + } + + resize() { + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + this.centerX = this.canvas.width / 2; + this.centerY = this.canvas.height / 2; + this.radius = Math.min(this.centerX, this.centerY) * 0.85; + } + + setupRadar() { + this.angle = 0; + this.targets = this.generateRandomTargets(); + this.fadeOpacity = new Array(360).fill(0); + this.animate(); + } + + generateRandomTargets() { + const targets = []; + const numTargets = Math.floor(Math.random() * 5) + 3; + + for (let i = 0; i < numTargets; i++) { + const distance = Math.random() * 0.8 + 0.1; // 10% - 90% of radius + const angle = Math.random() * Math.PI * 2; + const type = Math.random() < 0.7 ? 'friendly' : 'hostile'; + const size = Math.random() * 3 + 2; + + targets.push({ + x: Math.cos(angle) * this.radius * distance, + y: Math.sin(angle) * this.radius * distance, + type, + size, + blip: 1.0, // 用于闪烁效果 + fadeStart: angle // 用于淡出效果 + }); + } + return targets; + } + + drawGrid() { + // 绘制背景 + const gradient = this.ctx.createRadialGradient( + this.centerX, this.centerY, 0, + this.centerX, this.centerY, this.radius + ); + gradient.addColorStop(0, 'rgba(0, 180, 216, 0.1)'); + gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); + + this.ctx.fillStyle = gradient; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + this.ctx.strokeStyle = 'rgba(0, 180, 216, 0.2)'; + this.ctx.lineWidth = 1; + + // 绘制同心圆 + for (let i = 1; i <= 4; i++) { + const radius = this.radius * (i/4); + this.ctx.beginPath(); + this.ctx.arc(this.centerX, this.centerY, radius, 0, Math.PI * 2); + this.ctx.stroke(); + + // 添加距离标记 + const distance = (i * 25).toString() + 'km'; + this.ctx.fillStyle = 'rgba(0, 180, 216, 0.5)'; + this.ctx.font = '12px Noto Sans SC'; + this.ctx.fillText(distance, this.centerX + radius + 5, this.centerY); + } + + // 绘制方向标记 + const directions = ['N', 'E', 'S', 'W']; + this.ctx.font = 'bold 14px Noto Sans SC'; + directions.forEach((dir, i) => { + const angle = (i * Math.PI / 2) - Math.PI / 2; + const x = this.centerX + Math.cos(angle) * (this.radius + 20); + const y = this.centerY + Math.sin(angle) * (this.radius + 20); + this.ctx.fillText(dir, x - 6, y + 6); + }); + + // 绘制刻度线 + for (let i = 0; i < 360; i += 30) { + const angle = (i * Math.PI) / 180; + const startRadius = this.radius - 10; + const endRadius = this.radius; + + this.ctx.beginPath(); + this.ctx.moveTo( + this.centerX + Math.cos(angle) * startRadius, + this.centerY + Math.sin(angle) * startRadius + ); + this.ctx.lineTo( + this.centerX + Math.cos(angle) * endRadius, + this.centerY + Math.sin(angle) * endRadius + ); + this.ctx.stroke(); + } + + // 绘制十字线 + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX - this.radius, this.centerY); + this.ctx.lineTo(this.centerX + this.radius, this.centerY); + this.ctx.moveTo(this.centerX, this.centerY - this.radius); + this.ctx.lineTo(this.centerX, this.centerY + this.radius); + this.ctx.stroke(); + } + + drawSweep() { + // 更新扫描线渐变效果 + const gradient = this.ctx.createLinearGradient( + this.centerX, + this.centerY, + this.centerX + Math.cos(this.angle) * this.radius, + this.centerY + Math.sin(this.angle) * this.radius + ); + gradient.addColorStop(0, 'rgba(0, 180, 216, 0.8)'); + gradient.addColorStop(0.5, 'rgba(0, 180, 216, 0.3)'); + gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); + + // 绘制扫描线 + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX, this.centerY); + this.ctx.lineTo( + this.centerX + Math.cos(this.angle) * this.radius, + this.centerY + Math.sin(this.angle) * this.radius + ); + this.ctx.strokeStyle = gradient; + this.ctx.lineWidth = 3; + this.ctx.stroke(); + + // 扫描扇形 + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX, this.centerY); + this.ctx.arc(this.centerX, this.centerY, this.radius, this.angle - 0.2, this.angle); + this.ctx.fillStyle = 'rgba(0, 180, 216, 0.1)'; + this.ctx.fill(); + + // 更新淡出效果数组 + const currentAngleDeg = (this.angle * 180 / Math.PI) % 360; + this.fadeOpacity[Math.floor(currentAngleDeg)] = 1; + + // 绘制历史扫描线 + for (let i = 0; i < 360; i++) { + if (this.fadeOpacity[i] > 0) { + const angle = (i * Math.PI) / 180; + this.ctx.beginPath(); + this.ctx.moveTo(this.centerX, this.centerY); + this.ctx.lineTo( + this.centerX + Math.cos(angle) * this.radius, + this.centerY + Math.sin(angle) * this.radius + ); + this.ctx.strokeStyle = `rgba(0, 180, 216, ${this.fadeOpacity[i] * 0.1})`; + this.ctx.stroke(); + this.fadeOpacity[i] *= 0.95; + } + } + } + + drawTargets() { + this.targets.forEach(target => { + // 计算目标在画布上的位置 + const x = this.centerX + target.x; + const y = this.centerY + target.y; + + // 检查是否在扫描线范围内 + const targetAngle = Math.atan2(target.y, target.x); + const normalizedTargetAngle = targetAngle < 0 ? targetAngle + Math.PI * 2 : targetAngle; + const normalizedSweepAngle = this.angle % (Math.PI * 2); + + if (Math.abs(normalizedTargetAngle - normalizedSweepAngle) < 0.2) { + target.blip = 1.0; + } + + // 绘制目标 + this.ctx.beginPath(); + this.ctx.arc(x, y, target.size, 0, Math.PI * 2); + + const color = target.type === 'friendly' ? + `rgba(46, 204, 113, ${target.blip})` : + `rgba(231, 76, 60, ${target.blip})`; + + this.ctx.fillStyle = color; + this.ctx.fill(); + + // 绘制目标光晕 + this.ctx.beginPath(); + this.ctx.arc(x, y, target.size * 2, 0, Math.PI * 2); + this.ctx.fillStyle = color.replace('1)', '0.3)'); + this.ctx.fill(); + + // 淡出效果 + target.blip *= 0.95; + }); + } + + updateStats() { + const friendlyTargets = this.targets.filter(t => t.type === 'friendly').length; + const hostileTargets = this.targets.filter(t => t.type === 'hostile').length; + + document.querySelector('.stat-item:nth-child(2) .value').textContent = + `${friendlyTargets + hostileTargets}`; + } + + animate() { + this.ctx.fillStyle = 'rgba(26, 38, 52, 0.3)'; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + this.drawGrid(); + this.drawSweep(); + this.drawTargets(); + this.updateStats(); + + this.angle += 0.02; + if (this.angle >= Math.PI * 2) { + this.angle = 0; + if (Math.random() < 0.3) { + this.targets = this.generateRandomTargets(); + } + } + + requestAnimationFrame(() => this.animate()); + } +} + +// 状态卡片动画 +class StatusCards { + constructor() { + this.cards = document.querySelectorAll('.status-card'); + this.setupCards(); + } + + setupCards() { + this.cards.forEach(card => { + const value = card.querySelector('.status-value'); + const targetValue = parseInt(value.textContent); + value.textContent = '0'; + + // 添加数值增长动画 + this.animateValue(value, 0, targetValue, 2000); + + // 添加图标呼吸效果 + const icon = card.querySelector('.status-icon'); + this.addPulseEffect(icon); + }); + } + + animateValue(element, start, end, duration) { + const range = end - start; + const increment = range / (duration / 16); + let current = start; + + const animate = () => { + current += increment; + element.textContent = Math.floor(current); + + if (current < end) { + requestAnimationFrame(animate); + } else { + element.textContent = end; + } + }; + + animate(); + } + + addPulseEffect(element) { + element.style.animation = 'pulse 2s infinite'; + } +} + +// 威胁分析图表 +class ThreatAnalysis { + constructor(containerId) { + this.container = document.getElementById(containerId); + this.setupChart(); + } + + setupChart() { + // 这里可以使用Chart.js或其他图表库来实现威胁分析图表 + this.container.innerHTML = '
    威胁分析图表加载中...
    '; + } +} + +// 战场活动播报 +class BattlefieldActivity { + constructor(containerId) { + this.container = document.getElementById(containerId); + this.activities = []; + this.setupActivity(); + } + + setupActivity() { + this.container.innerHTML = ` +
    +
    + 10:30 + 警告 + 检测到未知信号源,位于东北方向,距离2.5公里 +
    +
    + 10:28 + 信息 + 友军编队完成例行巡逻任务 +
    +
    + `; + } + + addActivity(activity) { + this.activities.unshift(activity); + this.updateDisplay(); + } + + updateDisplay() { + // 更新活动列表显示 + } +} + +// 任务执行统计图表 +class MissionStats { + constructor(containerId) { + const ctx = document.getElementById(containerId).getContext('2d'); + this.chart = new Chart(ctx, { + type: 'line', + data: { + labels: Array.from({length: 12}, (_, i) => `${i*2}:00`), + datasets: [{ + label: '任务完成率', + data: Array.from({length: 12}, () => Math.random() * 40 + 60), + borderColor: '#00b4d8', + backgroundColor: 'rgba(0, 180, 216, 0.1)', + fill: true, + tension: 0.4 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: '#e9ecef' + } + }, + x: { + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: '#e9ecef' + } + } + } + } + }); + } +} + +// 区域覆盖率图表 +class AreaCoverage { + constructor(containerId) { + const ctx = document.getElementById(containerId).getContext('2d'); + this.chart = new Chart(ctx, { + type: 'doughnut', + data: { + labels: ['已覆盖', '未覆盖'], + datasets: [{ + data: [75, 25], + backgroundColor: [ + 'rgba(0, 180, 216, 0.8)', + 'rgba(255, 255, 255, 0.1)' + ], + borderWidth: 0 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'bottom', + labels: { + color: '#e9ecef' + } + } + } + } + }); + } +} + +// 系统状态图表 +class SystemStatus { + constructor(containerId) { + const ctx = document.getElementById(containerId).getContext('2d'); + this.chart = new Chart(ctx, { + type: 'bar', + data: { + labels: ['CPU', '内存', '存储', '网络'], + datasets: [{ + label: '使用率', + data: [65, 45, 30, 80], + backgroundColor: 'rgba(0, 180, 216, 0.8)', + borderRadius: 5 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: '#e9ecef' + } + }, + x: { + grid: { + display: false + }, + ticks: { + color: '#e9ecef' + } + } + } + } + }); + } +} + +class Radar3D { + constructor(containerId) { + this.container = document.getElementById(containerId); + if (!this.container) { + console.error('Container not found:', containerId); + return; + } + + // 清空容器 + while (this.container.firstChild) { + this.container.removeChild(this.container.firstChild); + } + + this.scene = new THREE.Scene(); + this.camera = new THREE.PerspectiveCamera(75, this.container.clientWidth / this.container.clientHeight, 0.1, 1000); + + try { + this.renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true + }); + console.log('Renderer created successfully'); + } catch (error) { + console.error('Failed to create renderer:', error); + return; + } + + this.renderer.setSize(this.container.clientWidth, this.container.clientHeight); + this.renderer.setClearColor(0x000000, 0.1); + this.container.appendChild(this.renderer.domElement); + console.log('Renderer added to container'); + + // 设置相机位置 + this.camera.position.set(50, 30, 50); + this.camera.lookAt(0, 0, 0); + + // 创建场景内容 + this.createScene(); + + // 添加事件监听 + window.addEventListener('resize', () => this.onWindowResize()); + + // 开始动画循环 + this.animate(); + console.log('Animation started'); + } + + createScene() { + try { + // 添加环境光 + const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); + this.scene.add(ambientLight); + + // 添加点光源 + const pointLight = new THREE.PointLight(0x00b4d8, 1); + pointLight.position.set(10, 10, 10); + this.scene.add(pointLight); + + // 创建地面网格 + const gridHelper = new THREE.GridHelper(100, 20, 0x404040, 0x404040); + this.scene.add(gridHelper); + + // 创建坐标轴 + const axesHelper = new THREE.AxesHelper(50); + this.scene.add(axesHelper); + + // 创建雷达平面 + const radarGeometry = new THREE.CircleGeometry(40, 64); + const radarMaterial = new THREE.MeshPhongMaterial({ + color: 0x00b4d8, + transparent: true, + opacity: 0.3, + side: THREE.DoubleSide + }); + this.radarMesh = new THREE.Mesh(radarGeometry, radarMaterial); + this.radarMesh.rotation.x = -Math.PI / 2; + this.scene.add(this.radarMesh); + + // 创建扫描线 + const lineGeometry = new THREE.BufferGeometry().setFromPoints([ + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(40, 0, 0) + ]); + const lineMaterial = new THREE.LineBasicMaterial({ + color: 0x00ff00, + linewidth: 2 + }); + this.radarLine = new THREE.Line(lineGeometry, lineMaterial); + this.radarLine.rotation.x = -Math.PI / 2; + this.scene.add(this.radarLine); + + // 设置轨道控制器 + if (window.OrbitControls) { + this.controls = new window.OrbitControls(this.camera, this.renderer.domElement); + this.controls.enableDamping = true; + this.controls.dampingFactor = 0.05; + console.log('OrbitControls initialized'); + } else { + console.error('OrbitControls not available'); + } + + console.log('Scene created successfully'); + } catch (error) { + console.error('Error creating scene:', error); + } + } + + addTarget(position, type = 'unknown') { + const colors = { + friendly: 0x2ecc71, + hostile: 0xe74c3c, + unknown: 0xf1c40f + }; + + const geometry = new THREE.SphereGeometry(1, 16, 16); + const material = new THREE.MeshPhongMaterial({ + color: colors[type], + emissive: colors[type], + emissiveIntensity: 0.5 + }); + const target = new THREE.Mesh(geometry, material); + target.position.set(position.x, position.y, position.z); + this.scene.add(target); + } + + onWindowResize() { + if (!this.camera || !this.renderer || !this.container) return; + + const width = this.container.clientWidth; + const height = this.container.clientHeight; + + this.camera.aspect = width / height; + this.camera.updateProjectionMatrix(); + this.renderer.setSize(width, height); + } + + animate() { + if (!this.renderer || !this.scene || !this.camera) return; + + requestAnimationFrame(() => this.animate()); + + if (this.radarLine) { + this.radarLine.rotation.z += 0.02; + } + + if (this.controls) { + this.controls.update(); + } + + this.renderer.render(this.scene, this.camera); + } +} + +// 初始化 +document.addEventListener('DOMContentLoaded', () => { + // 更新时间 + updateTime(); + setInterval(updateTime, 1000); + + // 初始化雷达 + const radar = new Radar3D('tacticalRadar'); + + // 初始化状态卡片迷你图表 + const miniCharts = []; + document.querySelectorAll('.mini-chart').forEach(container => { + const chart = new StatusMiniChart(container); + miniCharts.push(chart); + }); + + // 更新迷你图表 + setInterval(() => { + miniCharts.forEach(chart => chart.update()); + }, 2000); + + // 初始化其他图表 + const missionStats = new MissionStats('missionStats'); + const areaCoverage = new AreaCoverage('areaCoverage'); + const systemStatus = new SystemStatus('systemStatus'); + + // 添加按钮事件监听 + document.querySelectorAll('.radar-controls .btn, .card-tools .btn').forEach(btn => { + btn.addEventListener('click', function() { + // 这里可以添加按钮的具体功能 + console.log('Button clicked:', this.querySelector('i').className); + }); + }); + + // 添加示例目标 + radar.addTarget({ x: 20, y: 0, z: 20 }, 'friendly'); + radar.addTarget({ x: -15, y: 5, z: -25 }, 'hostile'); + radar.addTarget({ x: 30, y: 2, z: -10 }, 'unknown'); +}); \ No newline at end of file diff --git a/frontend/common/js/common.js b/frontend/common/js/common.js new file mode 100644 index 0000000..4faf7a0 --- /dev/null +++ b/frontend/common/js/common.js @@ -0,0 +1,169 @@ +// 全局变量 +let currentUser = null; +let threatChart = null; +let radarTargets = []; // 存储雷达上的目标 +let radarInterval = null; // 存储雷达更新间隔 + +// 显示提示消息 +function showToast(message, type = 'info') { + // 创建toast容器(如果不存在) + let toastContainer = document.getElementById('toast-container'); + if (!toastContainer) { + toastContainer = document.createElement('div'); + toastContainer.id = 'toast-container'; + toastContainer.className = 'position-fixed top-0 end-0 p-3'; + toastContainer.style.zIndex = '1050'; + document.body.appendChild(toastContainer); + } + + // 创建toast元素 + const toastId = 'toast-' + Date.now(); + const toast = document.createElement('div'); + toast.className = `toast align-items-center text-white bg-${type} border-0`; + toast.id = toastId; + toast.setAttribute('role', 'alert'); + toast.setAttribute('aria-live', 'assertive'); + toast.setAttribute('aria-atomic', 'true'); + + toast.innerHTML = ` +
    +
    + ${message} +
    + +
    + `; + + toastContainer.appendChild(toast); + + // 显示toast + const bsToast = new bootstrap.Toast(toast, { + animation: true, + autohide: true, + delay: 3000 + }); + bsToast.show(); + + // 自动删除toast元素 + toast.addEventListener('hidden.bs.toast', function() { + toast.remove(); + }); +} + +// 处理登录 +function handleLogin(event) { + event.preventDefault(); + + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + + // 硬编码的用户验证 + if (username === 'admin' && password === '123') { + // 保存登录状态 + currentUser = { + username: 'admin', + token: 'dummy-token' + }; + + // 保存到 localStorage + localStorage.setItem('currentUser', JSON.stringify(currentUser)); + + // 显示成功消息 + showToast('登录成功', 'success'); + + // 跳转到仪表盘页面 + setTimeout(() => { + window.location.href = 'dashboard.html'; + }, 1000); + + return false; + } else { + showToast('用户名或密码错误', 'danger'); + return false; + } +} + +// 退出登录 +function logout() { + currentUser = null; + localStorage.removeItem('currentUser'); + + // 清除雷达间隔 + if (radarInterval) { + clearInterval(radarInterval); + radarInterval = null; + } + + // 重定向到登录页面 + window.location.href = 'login.html'; +} + +// 初始化侧边栏 +function initializeSidebar() { + // 处理侧边栏链接点击 + document.querySelectorAll('.nav-link').forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + + // 处理退出登录 + if (this.id === 'logout-tab') { + logout(); + return; + } + + // 更新活动标签 + document.querySelectorAll('.nav-link').forEach(nav => nav.classList.remove('active')); + this.classList.add('active'); + + // 获取目标页面 + const targetPage = this.getAttribute('href'); + if (targetPage) { + window.location.href = targetPage; + } + }); + }); +} + +// 检查登录状态 +function checkAuth() { + // 从 localStorage 获取用户信息 + const savedUser = localStorage.getItem('currentUser'); + if (savedUser) { + currentUser = JSON.parse(savedUser); + return true; + } + + // 如果不在登录页面,则重定向到登录页面 + if (!window.location.pathname.includes('login.html')) { + window.location.href = 'login.html'; + } + return false; +} + +// 更新用户信息显示 +function updateUserInfo() { + if (currentUser) { + const userNameElement = document.getElementById('user-name'); + if (userNameElement) { + userNameElement.textContent = currentUser.username; + } + } +} + +// 初始化页面 +function initializePage() { + // 如果是登录页面,不需要检查认证 + if (window.location.pathname.includes('login.html')) { + return; + } + + if (!checkAuth()) return; + + initializeSidebar(); + updateUserInfo(); +} + +// 在页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + initializePage(); +}); \ No newline at end of file diff --git a/frontend/common/js/drones.js b/frontend/common/js/drones.js new file mode 100644 index 0000000..bbb3f0e --- /dev/null +++ b/frontend/common/js/drones.js @@ -0,0 +1,341 @@ +// 初始化地图 +let map; +let droneMarkers = {}; + +document.addEventListener('DOMContentLoaded', function() { + // 首先加载无人机列表和其他UI元素 + loadDroneData(); + + // 绑定事件处理器 + bindEventHandlers(); + + // 延迟加载地图和实时更新功能 + setTimeout(() => { + initMap(); + initRealTimeUpdates(); + }, 100); +}); + +function initMap() { + try { + // 使用百度地图 + map = new BMap.Map('map'); + + // 设置中心点和缩放级别 + const point = new BMap.Point(116.4074, 39.9042); + map.centerAndZoom(point, 13); + + // 开启鼠标滚轮缩放 + map.enableScrollWheelZoom(true); + + // 添加地图控件 + map.addControl(new BMap.NavigationControl()); // 添加平移缩放控件 + map.addControl(new BMap.ScaleControl()); // 添加比例尺控件 + map.addControl(new BMap.MapTypeControl()); // 添加地图类型控件 + + // 设置地图样式 - 暗色主题 + map.setMapStyle({ + style: 'midnight' // 使用暗色主题 + }); + + } catch (error) { + console.error('地图初始化失败:', error); + document.getElementById('map').innerHTML = '
    地图加载中,请稍候...
    '; + } +} + +function initRealTimeUpdates() { + // 降低更新频率,从5秒改为10秒 + setInterval(() => { + if (map && Object.keys(droneMarkers).length > 0) { + updateDronePositions(); + updateBatteryLevels(); + } + }, 10000); + + // 告警更新频率降低到30秒 + setInterval(() => { + updateAlerts(); + }, 30000); +} + +function bindEventHandlers() { + // 刷新状态按钮 + document.getElementById('refreshStatus').addEventListener('click', () => { + loadDroneData(); + }); + + // 任务分配表单提交 + document.getElementById('assignTaskForm').addEventListener('submit', (e) => { + e.preventDefault(); + assignTask(); + }); + + // 添加无人机表单保存 + document.getElementById('saveDrone').addEventListener('click', () => { + saveDrone(); + }); +} + +function loadDroneData() { + // 添加加载状态指示 + const tbody = document.getElementById('dronesList'); + tbody.innerHTML = '加载中...'; + + // 模拟从后端加载数据 + const drones = [ + { + id: 'UAV-001', + model: '侦察者-X1', + status: 'online', + battery: 85, + task: '巡逻任务', + position: { + lng: 116.4074, + lat: 39.9042 + } + }, + { + id: 'UAV-002', + model: '侦察者-X2', + status: 'mission', + battery: 65, + task: '侦察任务', + position: { + lng: 116.4174, + lat: 39.9142 + } + }, + { + id: 'UAV-003', + model: '追踪者-A1', + status: 'maintenance', + battery: 45, + task: '维护中', + position: { + lng: 116.4274, + lat: 39.9242 + } + } + ]; + + // 立即更新无人机列表 + updateDroneList(drones); + + // 如果地图已经初始化,则更新标记 + if (map) { + updateMapMarkers(drones); + } +} + +function updateDroneList(drones) { + const tbody = document.getElementById('dronesList'); + tbody.innerHTML = ''; + + drones.forEach(drone => { + const tr = document.createElement('tr'); + tr.innerHTML = ` + ${drone.id} + ${drone.model} + ${getStatusText(drone.status)} + +
    +
    +
    + + ${drone.task} + ${formatPosition(drone.position)} + +
    + + + +
    + + `; + tbody.appendChild(tr); + }); +} + +function updateMapMarkers(drones) { + // 清除现有标记 + map.clearOverlays(); + droneMarkers = {}; + + // 添加新标记 + drones.forEach(drone => { + // 创建标记点 + const point = new BMap.Point(drone.position.lng, drone.position.lat); + const marker = new BMap.Marker(point); + + // 创建信息窗口 + const content = ` +
    +
    ${drone.id}
    +

    型号: ${drone.model}
    + 状态: ${getStatusText(drone.status)}
    + 电量: ${drone.battery}%
    + 任务: ${drone.task}

    +
    + `; + + const infoWindow = new BMap.InfoWindow(content, { + width: 200, + height: 120, + title: `${drone.id} - ${drone.model}` + }); + + // 点击标记时显示信息窗口 + marker.addEventListener('click', function() { + map.openInfoWindow(infoWindow, point); + }); + + // 将标记添加到地图 + map.addOverlay(marker); + droneMarkers[drone.id] = { + marker: marker, + point: point + }; + }); +} + +function updateDronePositions() { + // 模拟无人机位置更新 + Object.keys(droneMarkers).forEach(droneId => { + const markerData = droneMarkers[droneId]; + if (markerData && markerData.marker) { + const currentPoint = markerData.point; + + // 随机移动位置 + const newLng = currentPoint.lng + (Math.random() - 0.5) * 0.01; + const newLat = currentPoint.lat + (Math.random() - 0.5) * 0.01; + const newPoint = new BMap.Point(newLng, newLat); + + // 创建动画移动效果 + const moveAnimation = new BMap.Animation.Move(markerData.marker, { + points: [currentPoint, newPoint], + duration: 1000 + }); + moveAnimation.start(); + + // 更新保存的位置 + markerData.point = newPoint; + } + }); +} + +function updateBatteryLevels() { + // 模拟电池电量更新 + document.querySelectorAll('.battery-level').forEach(battery => { + const currentWidth = parseInt(battery.style.width); + if (currentWidth > 0) { + battery.style.width = `${currentWidth - 1}%`; + } + }); +} + +function updateAlerts() { + // 模拟告警更新 + const alertPanel = document.querySelector('.alert-panel ul'); + const currentTime = new Date().toLocaleTimeString(); + + // 随机添加新告警 + if (Math.random() < 0.3) { + const alerts = [ + '通信信号弱,建议调整位置', + '检测到强风,建议降低飞行高度', + '接近禁飞区域边界', + '发现可疑目标,建议进一步确认' + ]; + + const newAlert = alerts[Math.floor(Math.random() * alerts.length)]; + const li = document.createElement('li'); + li.innerHTML = `• ${currentTime} - ${newAlert}`; + + alertPanel.insertBefore(li, alertPanel.firstChild); + + // 限制告警数量 + if (alertPanel.children.length > 5) { + alertPanel.removeChild(alertPanel.lastChild); + } + } +} + +function showDroneDetails(droneId) { + // 更新详情面板 + const detailPanel = document.getElementById('droneDetailPanel'); + detailPanel.querySelector('h5').textContent = `无人机详情 - ${droneId}`; + + // 模拟加载详细信息 + // 实际应用中应该从后端获取数据 +} + +function controlDrone(droneId) { + // 实现无人机控制逻辑 + console.log(`控制无人机: ${droneId}`); +} + +function maintainDrone(droneId) { + // 实现维护模式逻辑 + console.log(`维护无人机: ${droneId}`); +} + +function assignTask() { + const formData = { + droneId: document.getElementById('droneSelect').value, + taskType: document.getElementById('taskType').value, + taskArea: document.getElementById('taskArea').value, + priority: document.getElementById('taskPriority').value, + estimatedTime: document.getElementById('estimatedTime').value + }; + + // 实际应用中应该发送到后端 + console.log('分配任务:', formData); + + // 模拟成功响应 + alert('任务分配成功!'); +} + +function saveDrone() { + const formData = { + model: document.getElementById('droneModel').value, + serialNumber: document.getElementById('serialNumber').value, + maxFlightTime: document.getElementById('maxFlightTime').value, + maxPayload: document.getElementById('maxPayload').value, + communicationBand: document.getElementById('communicationBand').value, + hasCamera: document.getElementById('hasCamera').checked, + hasInfrared: document.getElementById('hasInfrared').checked, + hasGPS: document.getElementById('hasGPS').checked + }; + + // 实际应用中应该发送到后端 + console.log('添加无人机:', formData); + + // 模拟成功响应 + alert('无人机添加成功!'); + + // 关闭模态框 + const modal = bootstrap.Modal.getInstance(document.getElementById('addDroneModal')); + modal.hide(); +} + +// 辅助函数 +function getStatusText(status) { + const statusMap = { + 'online': '在线', + 'mission': '任务中', + 'maintenance': '维护中', + 'offline': '离线' + }; + return statusMap[status] || status; +} + +function formatPosition(position) { + return `N ${position.lat.toFixed(4)}° E ${position.lng.toFixed(4)}°`; +} \ No newline at end of file diff --git a/frontend/common/js/identification.js b/frontend/common/js/identification.js new file mode 100644 index 0000000..136dee7 --- /dev/null +++ b/frontend/common/js/identification.js @@ -0,0 +1,500 @@ +// 更新时间显示 +function updateTime() { + const now = new Date(); + document.getElementById('current-time').textContent = now.toLocaleString(); +} +setInterval(updateTime, 1000); + +// 初始化图表 +function initializeCharts() { + // 置信度历史图表 + const confidenceCtx = document.getElementById('confidenceChart').getContext('2d'); + const confidenceChart = new Chart(confidenceCtx, { + type: 'line', + data: { + labels: ['10:25', '10:26', '10:27', '10:28', '10:29', '10:30'], + datasets: [{ + label: '识别置信度', + data: [65, 75, 82, 78, 85, 88], + borderColor: '#00b4d8', + backgroundColor: 'rgba(0, 180, 216, 0.1)', + tension: 0.4, + fill: true + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + }, + tooltip: { + mode: 'index', + intersect: false, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#fff', + bodyColor: '#fff', + borderColor: 'rgba(255, 255, 255, 0.1)', + borderWidth: 1 + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.7)', + callback: function(value) { + return value + '%'; + } + } + }, + x: { + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.7)' + } + } + }, + interaction: { + intersect: false, + mode: 'index' + }, + animation: { + duration: 1000, + easing: 'easeInOutQuart' + } + } + }); + + // 目标类型分布图表 + const distributionCtx = document.getElementById('typeDistributionChart').getContext('2d'); + const distributionChart = new Chart(distributionCtx, { + type: 'doughnut', + data: { + labels: ['友方目标', '敌方目标', '未知目标'], + datasets: [{ + data: [4, 3, 2], + backgroundColor: [ + '#2ecc71', + '#e74c3c', + '#f1c40f' + ], + borderWidth: 0, + hoverOffset: 4 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'right', + labels: { + color: 'rgba(255, 255, 255, 0.7)', + font: { + size: 11 + }, + padding: 20, + usePointStyle: true, + pointStyle: 'circle' + } + }, + tooltip: { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#fff', + bodyColor: '#fff', + borderColor: 'rgba(255, 255, 255, 0.1)', + borderWidth: 1 + } + }, + animation: { + animateRotate: true, + animateScale: true + } + } + }); + + // 导出图表更新函数 + window.updateCharts = { + updateConfidence: function(newData) { + confidenceChart.data.datasets[0].data = newData; + confidenceChart.update('none'); + }, + updateDistribution: function(newData) { + distributionChart.data.datasets[0].data = newData; + distributionChart.update('none'); + } + }; +} + +// 模拟目标检测框 +function simulateTargetDetection() { + const videoContainer = document.querySelector('.video-container'); + const targetBox = document.querySelector('.target-box'); + let isTracking = false; + + function updateTargetPosition() { + if (!isTracking) return; + + const maxX = videoContainer.clientWidth - 100; + const maxY = videoContainer.clientHeight - 100; + const x = Math.random() * maxX; + const y = Math.random() * maxY; + + targetBox.style.display = 'block'; + targetBox.style.left = x + 'px'; + targetBox.style.top = y + 'px'; + + // 更新目标信息 + updateTargetInfo({ + type: ['未知目标', '友方目标', '敌方目标'][Math.floor(Math.random() * 3)], + distance: Math.floor(Math.random() * 5000 + 1000), + confidence: Math.floor(Math.random() * 30 + 70) + }); + } + + // 开始/停止跟踪 + window.toggleTracking = function(start) { + isTracking = start; + if (start) { + targetBox.style.display = 'block'; + updateTargetPosition(); + } else { + targetBox.style.display = 'none'; + } + }; + + setInterval(updateTargetPosition, 3000); +} + +// 更新目标信息 +function updateTargetInfo(data) { + const targetType = document.getElementById('targetType'); + const confidenceBar = document.querySelector('.progress-bar'); + const distance = document.getElementById('distance'); + const threatLevel = document.getElementById('threatLevel'); + + targetType.textContent = data.type; + confidenceBar.style.width = data.confidence + '%'; + confidenceBar.textContent = data.confidence + '%'; + distance.textContent = (data.distance / 1000).toFixed(1) + 'km'; + + // 更新威胁等级 + const threatLevels = ['低', '中', '高']; + const threatColors = ['success', 'warning', 'danger']; + const threatIndex = Math.floor(Math.random() * 3); + threatLevel.className = `badge bg-${threatColors[threatIndex]}`; + threatLevel.textContent = threatLevels[threatIndex]; +} + +// 绑定控制按钮事件 +function bindControlEvents() { + const startBtn = document.getElementById('startStream'); + const stopBtn = document.getElementById('stopStream'); + const captureBtn = document.getElementById('captureImage'); + const toggleARBtn = document.getElementById('toggleAR'); + const toggleNightVisionBtn = document.getElementById('toggleNightVision'); + const toggleThermalBtn = document.getElementById('toggleThermal'); + + let isStreaming = false; + + startBtn.addEventListener('click', () => { + isStreaming = true; + document.querySelector('.status-dot').classList.add('active'); + window.toggleTracking(true); + showToast('开始目标识别'); + }); + + stopBtn.addEventListener('click', () => { + isStreaming = false; + document.querySelector('.status-dot').classList.remove('active'); + window.toggleTracking(false); + showToast('停止目标识别'); + }); + + captureBtn.addEventListener('click', () => { + showToast('已保存截图'); + // 添加截图动画效果 + const flash = document.createElement('div'); + flash.className = 'capture-flash'; + document.querySelector('.video-container').appendChild(flash); + setTimeout(() => flash.remove(), 200); + }); + + const toggleButtons = [ + { btn: toggleARBtn, name: 'AR叠加' }, + { btn: toggleNightVisionBtn, name: '夜视模式' }, + { btn: toggleThermalBtn, name: '热成像' } + ]; + + toggleButtons.forEach(({ btn, name }) => { + btn.addEventListener('click', () => { + const isActive = btn.classList.toggle('active'); + showToast(`${name}已${isActive ? '开启' : '关闭'}`); + }); + }); +} + +// Toast提示 +function showToast(message) { + const toast = document.createElement('div'); + toast.className = 'toast-message'; + toast.textContent = message; + document.body.appendChild(toast); + + setTimeout(() => { + toast.classList.add('show'); + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 2000); + }, 100); +} + +// 页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + initializeCharts(); + simulateTargetDetection(); + bindControlEvents(); + updateTime(); + + // 添加键盘快捷键 + document.addEventListener('keydown', function(e) { + if (e.key === 'F' || e.key === 'f') { + document.getElementById('captureImage').click(); + } + }); +}); + +// 初始化筛选器 +function initializeFilters() { + const filterForm = document.getElementById('filterForm'); + filterForm.addEventListener('submit', function(e) { + e.preventDefault(); + loadRecords(); + }); + + // 重置按钮事件 + filterForm.addEventListener('reset', function(e) { + setTimeout(() => loadRecords(), 0); + }); + + // 时间范围选择器事件 + const timeRange = document.getElementById('timeRange'); + timeRange.addEventListener('change', function() { + if (this.value === 'custom') { + // TODO: 显示自定义日期选择器 + } + }); +} + +// 加载记录数据 +async function loadRecords() { + try { + const filters = getFilters(); + // TODO: 替换为实际的API调用 + const response = await fetch('/api/records?' + new URLSearchParams(filters)); + const data = await response.json(); + + updateRecordsList(data.records); + updatePagination(data.pagination); + updateStats(data.stats); + } catch (error) { + console.error('加载记录失败:', error); + showError('加载记录失败,请稍后重试'); + } +} + +// 获取筛选条件 +function getFilters() { + return { + timeRange: document.getElementById('timeRange').value, + targetType: document.getElementById('targetType').value, + identificationStatus: document.getElementById('identificationStatus').value, + threatLevel: document.getElementById('threatLevel').value, + page: currentPage + }; +} + +// 更新记录列表 +function updateRecordsList(records) { + const tbody = document.getElementById('recordsList'); + tbody.innerHTML = records.map(record => ` + + ${formatDateTime(record.time)} + ${formatType(record.type)} + ${record.result} + +
    +
    +
    +
    + ${record.confidence}% + + ${formatThreatLevel(record.threatLevel)} + ${formatLocation(record.location)} + + + + + `).join(''); +} + +// 更新分页 +function updatePagination(pagination) { + const paginationElement = document.querySelector('.pagination'); + paginationElement.innerHTML = ` +
  • + 上一页 +
  • + ${generatePageNumbers(pagination)} +
  • + 下一页 +
  • + `; +} + +// 生成页码 +function generatePageNumbers(pagination) { + let pages = []; + const current = pagination.currentPage; + const total = pagination.totalPages; + + if (total <= 5) { + for (let i = 1; i <= total; i++) { + pages.push(i); + } + } else { + if (current <= 3) { + pages = [1, 2, 3, 4, '...', total]; + } else if (current >= total - 2) { + pages = [1, '...', total - 3, total - 2, total - 1, total]; + } else { + pages = [1, '...', current - 1, current, current + 1, '...', total]; + } + } + + return pages.map(page => { + if (page === '...') { + return '
  • ...
  • '; + } + return ` +
  • + ${page} +
  • + `; + }).join(''); +} + +// 显示记录详情 +function showRecordDetail(recordId) { + // TODO: 替换为实际的API调用 + fetch(`/api/records/${recordId}`) + .then(response => response.json()) + .then(record => { + document.getElementById('detailTime').textContent = formatDateTime(record.time); + document.getElementById('detailType').innerHTML = `${formatType(record.type)}`; + document.getElementById('detailResult').textContent = record.result; + document.getElementById('detailConfidence').innerHTML = ` +
    +
    +
    +
    + ${record.confidence}% + `; + document.getElementById('detailThreat').innerHTML = `${formatThreatLevel(record.threatLevel)}`; + document.getElementById('detailLocation').textContent = formatLocation(record.location); + document.getElementById('targetImage').src = record.imageUrl; + + const modal = new bootstrap.Modal(document.getElementById('recordDetailModal')); + modal.show(); + }) + .catch(error => { + console.error('加载记录详情失败:', error); + showError('加载记录详情失败,请稍后重试'); + }); +} + +// 格式化函数 +function formatDateTime(timestamp) { + return new Date(timestamp).toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); +} + +function formatType(type) { + const types = { + 'friendly': '友方', + 'hostile': '敌方', + 'unknown': '未知' + }; + return types[type.toLowerCase()] || type; +} + +function formatThreatLevel(level) { + const levels = { + 'high': '高', + 'medium': '中', + 'low': '低' + }; + return levels[level.toLowerCase()] || level; +} + +function formatLocation(location) { + if (typeof location === 'object') { + return `N ${location.latitude}° E ${location.longitude}°`; + } + return location; +} + +// 辅助函数 +function getConfidenceColor(confidence) { + if (confidence >= 90) return 'success'; + if (confidence >= 70) return 'warning'; + return 'danger'; +} + +function getThreatClass(level) { + const classes = { + 'high': 'danger', + 'medium': 'warning', + 'low': 'success' + }; + return classes[level.toLowerCase()] || 'secondary'; +} + +// 错误提示 +function showError(message) { + // TODO: 实现错误提示UI + console.error(message); +} + +// 导出功能 +document.querySelector('.btn-group .btn-outline-primary').addEventListener('click', function() { + const filters = getFilters(); + // TODO: 实现导出功能 + window.location.href = '/api/records/export?' + new URLSearchParams(filters); +}); + +// 打印功能 +document.querySelectorAll('.btn-group .btn-outline-primary')[1].addEventListener('click', function() { + window.print(); +}); \ No newline at end of file diff --git a/frontend/common/js/signal.js b/frontend/common/js/signal.js new file mode 100644 index 0000000..bc69b8e --- /dev/null +++ b/frontend/common/js/signal.js @@ -0,0 +1,261 @@ +// 页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + initializeSignalCanvas(); + initializeControls(); + setupEventListeners(); +}); + +let isScanning = false; +let animationFrameId = null; +let scanningData = []; +let currentFrequency = 2400; // MHz + +// 初始化信号画布 +function initializeSignalCanvas() { + const canvas = document.getElementById('signalCanvas'); + const ctx = canvas.getContext('2d'); + + // 设置画布大小 + function resizeCanvas() { + const container = canvas.parentElement; + canvas.width = container.clientWidth; + canvas.height = container.clientHeight; + } + + resizeCanvas(); + window.addEventListener('resize', resizeCanvas); + + // 初始化扫描数据 + for (let i = 0; i < 100; i++) { + scanningData.push(Math.random() * 0.5); + } + + // 开始动画循环 + function animate() { + if (!isScanning) return; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + drawSignalWaveform(ctx); + animationFrameId = requestAnimationFrame(animate); + } + + // 绘制信号波形 + function drawSignalWaveform(ctx) { + const width = canvas.width; + const height = canvas.height; + const points = 100; + + // 更新扫描数据 + scanningData.shift(); + scanningData.push(Math.random() * 0.5 + (isScanning ? Math.random() * 0.5 : 0)); + + // 绘制网格 + drawGrid(ctx, width, height); + + // 绘制波形 + ctx.beginPath(); + ctx.strokeStyle = '#00b4d8'; + ctx.lineWidth = 2; + + for (let i = 0; i < points; i++) { + const x = (i / points) * width; + const y = height - (scanningData[i] * height); + + if (i === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + } + + ctx.stroke(); + + // 绘制填充 + ctx.lineTo(width, height); + ctx.lineTo(0, height); + ctx.closePath(); + + const gradient = ctx.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, 'rgba(0, 180, 216, 0.2)'); + gradient.addColorStop(1, 'rgba(0, 180, 216, 0)'); + ctx.fillStyle = gradient; + ctx.fill(); + } + + // 绘制网格 + function drawGrid(ctx, width, height) { + const gridSize = 40; + ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; + ctx.lineWidth = 1; + + // 绘制垂直线 + for (let x = 0; x <= width; x += gridSize) { + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, height); + ctx.stroke(); + } + + // 绘制水平线 + for (let y = 0; y <= height; y += gridSize) { + ctx.beginPath(); + ctx.moveTo(0, y); + ctx.lineTo(width, y); + ctx.stroke(); + } + } + + // 导出动画控制函数 + window.startAnimation = function() { + if (!isScanning) { + isScanning = true; + animate(); + } + }; + + window.stopAnimation = function() { + isScanning = false; + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } + }; +} + +// 初始化控制器 +function initializeControls() { + const startButton = document.getElementById('startSignalScan'); + const stopButton = document.getElementById('stopSignalScan'); + const captureButton = document.getElementById('captureSignal'); + const frequencyRange = document.getElementById('frequencyRange'); + const scanSpeed = document.getElementById('scanSpeed'); + + // 开始扫描 + startButton.addEventListener('click', function() { + startButton.disabled = true; + stopButton.disabled = false; + window.startAnimation(); + updateSignalInfo(); + }); + + // 停止扫描 + stopButton.addEventListener('click', function() { + startButton.disabled = false; + stopButton.disabled = true; + window.stopAnimation(); + }); + + // 捕获信号 + captureButton.addEventListener('click', function() { + captureCurrentSignal(); + }); + + // 频率范围选择 + frequencyRange.addEventListener('change', function() { + updateFrequencyRange(this.value); + }); + + // 扫描速度调节 + scanSpeed.addEventListener('input', function() { + updateScanSpeed(this.value); + }); +} + +// 更新信号信息 +function updateSignalInfo() { + if (!isScanning) return; + + // 模拟信号数据更新 + const frequencyStep = Math.random() * 10 - 5; + currentFrequency = Math.max(300, Math.min(5000, currentFrequency + frequencyStep)); + + document.getElementById('currentFrequency').textContent = `${(currentFrequency / 1000).toFixed(2)} GHz`; + document.getElementById('signalStrength').textContent = `${-Math.floor(Math.random() * 20 + 60)} dBm`; + document.getElementById('bandwidth').textContent = `${Math.floor(Math.random() * 10 + 15)} MHz`; + document.getElementById('snr').textContent = `${Math.floor(Math.random() * 10 + 25)} dB`; + + setTimeout(() => { + if (isScanning) { + updateSignalInfo(); + } + }, 1000); +} + +// 捕获当前信号 +function captureCurrentSignal() { + const signalTypes = ['遥控信号', '数据链路', '图传信号', '干扰信号']; + const modulationTypes = ['QPSK', 'OFDM', '16QAM', 'FHSS']; + const encryptionTypes = ['AES-256', 'RSA-2048', 'ECC', '未加密']; + + // 更新信号特征 + document.getElementById('signalType').textContent = signalTypes[Math.floor(Math.random() * signalTypes.length)]; + document.getElementById('modulationType').textContent = modulationTypes[Math.floor(Math.random() * modulationTypes.length)]; + document.getElementById('encryptionType').textContent = encryptionTypes[Math.floor(Math.random() * encryptionTypes.length)]; + + // 显示捕获成功提示 + showToast('信号捕获成功'); +} + +// 更新频率范围 +function updateFrequencyRange(range) { + const ranges = { + 'low': { min: 300, max: 1000 }, + 'mid': { min: 1000, max: 2400 }, + 'high': { min: 2400, max: 5000 } + }; + + if (range !== 'all' && ranges[range]) { + currentFrequency = (ranges[range].min + ranges[range].max) / 2; + } +} + +// 更新扫描速度 +function updateScanSpeed(speed) { + // 实现扫描速度调节逻辑 +} + +// 设置事件监听器 +function setupEventListeners() { + // 全屏切换 + document.getElementById('toggleFullscreen').addEventListener('click', function() { + const signalContainer = document.querySelector('.signal-container'); + if (!document.fullscreenElement) { + signalContainer.requestFullscreen(); + this.innerHTML = ''; + } else { + document.exitFullscreen(); + this.innerHTML = ''; + } + }); + + // 导出记录 + document.querySelector('.btn-outline-primary').addEventListener('click', function() { + // 实现导出功能 + showToast('记录导出成功'); + }); + + // 清除记录 + document.querySelector('.btn-outline-danger').addEventListener('click', function() { + if (confirm('确定要清除所有记录吗?')) { + // 实现清除功能 + showToast('记录已清除'); + } + }); +} + +// 显示提示信息 +function showToast(message) { + // 创建提示元素 + const toast = document.createElement('div'); + toast.className = 'toast-message'; + toast.textContent = message; + document.body.appendChild(toast); + + // 添加动画类 + setTimeout(() => toast.classList.add('show'), 100); + + // 自动移除 + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 3000); +} \ No newline at end of file diff --git a/frontend/dashboard.html b/frontend/dashboard.html new file mode 100644 index 0000000..caa6286 --- /dev/null +++ b/frontend/dashboard.html @@ -0,0 +1,203 @@ + + + + + + 战场态势 - 无人机敌我识别系统 + + + + + + + + +
    +
    +
    +
    +

    战场态势

    +
    + + 2024-01-01 12:00:00 +
    +
    +
    + + 操作员:战术指挥官 + 在线 +
    +
    + +
    +
    +
    +
    +

    执行中无人机

    +
    0
    +
    +
    +
    +
    +
    +
    +

    今日识别数

    +
    0
    +
    +
    +
    +
    +
    +
    +

    友方目标数

    +
    0
    +
    +
    +
    +
    +
    +
    +

    敌方目标数

    +
    0
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + 实时战术态势 +
    +
    + + + +
    +
    +
    +
    +
    + 扫描范围 + 100km +
    +
    + 目标数量 + 12 +
    +
    + 信号强度 + 87% +
    +
    + 高度范围 + 10km +
    +
    +
    +
    +
    + + 友军目标 +
    +
    + + 敌军目标 +
    +
    + + 未识别目标 +
    +
    +
    +
    +
    +
    +
    +
    +
    + 威胁态势分析 +
    +
    + +
    +
    +
    +
    +
    当前威胁等级
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + 战场活动播报 +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/frontend/drones.html b/frontend/drones.html new file mode 100644 index 0000000..93cd5a5 --- /dev/null +++ b/frontend/drones.html @@ -0,0 +1,451 @@ + + + + + + 无人机管理 - 无人机敌我识别系统 + + + + + + + + + + +
    +
    +
    +
    +
    +

    无人机管理

    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    在线无人机
    +

    15

    +
    +
    +
    +
    +
    +
    +
    +
    执行任务中
    +

    9

    +
    +
    +
    +
    +
    +
    +
    +
    维护中
    +

    3

    +
    +
    +
    +
    +
    +
    +
    +
    离线
    +

    2

    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + 无人机位置 +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + 无人机列表 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    编号型号状态电量任务位置操作
    UAV-001侦察者-X1在线 +
    +
    +
    +
    巡逻任务N 39°54'25" E 116°23'50" +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    +
    无人机详情 - UAV-001
    +
    +
    基本信息
    +
    +
    + 型号 +

    侦察者-X1

    +
    +
    + 序列号 +

    SN20240101001

    +
    +
    + 续航时间 +

    4小时

    +
    +
    + 最大载重 +

    5kg

    +
    +
    +
    + +
    +
    实时状态
    +
    +
    + 飞行高度 +

    120m

    +
    +
    + 飞行速度 +

    15m/s

    +
    +
    + 电池状态 +
    +
    +
    + 预计剩余时间: 3小时15分钟 +
    +
    +
    + +
    +
    当前任务
    +
    +
    + 09:00 +

    开始巡逻任务

    +
    +
    + 09:15 +

    到达指定区域A

    +
    +
    + 09:30 +

    执行侦察任务

    +
    +
    +
    + + +
    +
    告警信息
    +
      +
    • + • 电池电量低于20%,建议及时返航 +
    • +
    • + • 天气条件转差,建议调整飞行计划 +
    • +
    +
    +
    + + +
    +
    +
    + 任务分配 +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/frontend/identification.html b/frontend/identification.html new file mode 100644 index 0000000..370d321 --- /dev/null +++ b/frontend/identification.html @@ -0,0 +1,266 @@ + + + + + + 目标识别 - 无人机敌我识别系统 + + + + + + + + +
    +
    +
    +
    +

    目标识别

    +
    + + 2024-01-01 12:00:00 +
    +
    +
    + + 操作员:战术指挥官 + 在线 +
    +
    + +
    +
    +
    +
    +
    + 实时视频流 +
    +
    + + 实时识别中 +
    +
    +
    +
    +
    + +
    +
    + +
    +
    + + 信号强度: 85% +
    +
    + + 方位: 235° +
    +
    + + 目标锁定: 已锁定 +
    +
    +
    +
    +
    +
    + + + +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    + 识别分析 +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 目标信息 +
    +
    +
    +
    +
    +
    + + 军用无人机 +
    +
    + +
    +
    + 85% +
    +
    +
    +
    +
    +
    + + 2.5km +
    +
    + + 120km/h +
    +
    +
    +
    + + 500m +
    +
    + + 235° +
    +
    +
    +
    + + +
    +
    + + 跟踪中 +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    + 最近识别 +
    + +
    +
    +
    +
    +
    10:30:15
    +
    +
    敌方无人机
    +
    距离: 3.2km / 速度: 150km/h
    +
    +
    已确认
    +
    +
    +
    10:28:45
    +
    +
    友方战机
    +
    距离: 5.1km / 速度: 280km/h
    +
    +
    已确认
    +
    +
    +
    10:25:30
    +
    +
    未知目标
    +
    距离: 8.7km / 速度: 90km/h
    +
    +
    分析中
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/frontend/idx.html b/frontend/idx.html new file mode 100644 index 0000000..4bbfb8b --- /dev/null +++ b/frontend/idx.html @@ -0,0 +1,3102 @@ + + + + + + 无人机敌我识别系统 + + + + + + +
    +
    + + + + +
    + +
    +
    +
    +
    + +
    +

    无人机敌我识别系统

    +

    军事授权访问 - 请输入您的凭证

    +
    +
    +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    + +
    + 未经授权的访问将被记录并追踪 +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..8eab4c0 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + 无人机敌我识别系统 + + + + 正在跳转到登录页面... + + \ No newline at end of file diff --git a/frontend/login.html b/frontend/login.html new file mode 100644 index 0000000..16e30e3 --- /dev/null +++ b/frontend/login.html @@ -0,0 +1,75 @@ + + + + + + 登录 - 无人机敌我识别系统 + + + + + + + +
    +
    +
    +
    + +
    +
    + + + +
    +
    + + + + + + \ No newline at end of file diff --git a/frontend/records.html b/frontend/records.html new file mode 100644 index 0000000..a1bc535 --- /dev/null +++ b/frontend/records.html @@ -0,0 +1,310 @@ + + + + + + 识别记录 - 无人机敌我识别系统 + + + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    2,458
    +
    总识别次数
    +
    +
    +
    +
    +
    + +
    +
    1,245
    +
    友方目标
    +
    +
    +
    +
    +
    + +
    +
    892
    +
    敌方目标
    +
    +
    +
    +
    +
    + +
    +
    321
    +
    未知目标
    +
    +
    +
    + + +
    +
    +
    +
    +
    + 筛选条件 +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + 识别记录列表 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    时间目标类型识别结果置信度威胁等级位置操作
    2024-01-15 14:30:25友方侦察无人机98%N 39°54'25" E 116°23'50" + +
    2024-01-15 14:28:15敌方攻击无人机95%N 39°54'28" E 116°23'52" + +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + \ No newline at end of file diff --git a/frontend/serve.py b/frontend/serve.py new file mode 100644 index 0000000..6df68de --- /dev/null +++ b/frontend/serve.py @@ -0,0 +1,33 @@ +import http.server +import socketserver +import os + +# 设置端口 +PORT = 8080 + +# 自定义处理器添加CORS头 +class CORSHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): + def end_headers(self): + # 添加CORS头 + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') + super().end_headers() + + def do_OPTIONS(self): + # 处理OPTIONS请求 + self.send_response(200) + self.end_headers() + +# 打印启动信息 +print(f"启动前端服务器,端口: {PORT}") +print(f"请在浏览器中访问: http://localhost:{PORT}") +print("按Ctrl+C终止服务器") + +# 启动服务器 +with socketserver.TCPServer(("", PORT), CORSHTTPRequestHandler) as httpd: + print("前端服务器正在运行...") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\n服务器已停止") \ No newline at end of file diff --git a/frontend/signal.html b/frontend/signal.html new file mode 100644 index 0000000..e4381c4 --- /dev/null +++ b/frontend/signal.html @@ -0,0 +1,333 @@ + + + + + + 信号识别 - 无人机敌我识别系统 + + + + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    24
    +
    活跃信号源
    +
    +
    +
    +
    +
    + +
    +
    12
    +
    友方信号
    +
    +
    +
    +
    +
    + +
    +
    8
    +
    可疑信号
    +
    +
    +
    +
    +
    + +
    +
    4
    +
    干扰源
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 信号波形分析 +
    +
    + +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    1.2 GHz
    +
    +
    +
    +
    2.4 GHz
    +
    +
    +
    +
    +
    +
    +
    + + 2.4 GHz +
    +
    +
    +
    + + -65 dBm +
    +
    +
    +
    + + 20 MHz +
    +
    +
    +
    + + 32 dB +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 信号特征 +
    +
    +
    +
    +
    + + QPSK +
    +
    + + 遥控信号 +
    +
    + + AES-256 +
    +
    + +
    +
    + 85% +
    +
    +
    +
    + +
    +
    + 25% +
    +
    +
    +
    +
    +
    + +
    +
    +
    + 威胁分析 +
    +
    +
    +
    +
    + + 中等 +
    +
    +
    威胁详情
    +
      +
    • + + 检测到可疑的加密通信 +
    • +
    • + + 信号特征与已知敌方设备部分匹配 +
    • +
    • + + 通信协议已被识别 +
    • +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 信号记录 +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    时间频率信号类型信号强度威胁等级状态操作
    2024-01-15 15:30:252.4 GHz遥控信号-65 dBm中等已识别 + +
    2024-01-15 15:28:151.8 GHz数据链路-72 dBm分析中 + +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file -- 2.34.1