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