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 = '
型号: ${drone.model}
+ 状态: ${getStatusText(drone.status)}
+ 电量: ${drone.battery}%
+ 任务: ${drone.task}
| 编号 | +型号 | +状态 | +电量 | +任务 | +位置 | +操作 | +
|---|---|---|---|---|---|---|
| UAV-001 | +侦察者-X1 | +在线 | +
+
+
+
+ |
+ 巡逻任务 | +N 39°54'25" E 116°23'50" | +
+
+
+
+
+
+ |
+
侦察者-X1
+SN20240101001
+4小时
+5kg
+120m
+15m/s
+开始巡逻任务
+到达指定区域A
+执行侦察任务
+军事授权访问 - 请输入您的凭证
+军用级安全认证
+| 时间 | +频率 | +信号类型 | +信号强度 | +威胁等级 | +状态 | +操作 | +
|---|---|---|---|---|---|---|
| 2024-01-15 15:30:25 | +2.4 GHz | +遥控信号 | +-65 dBm | +中等 | +已识别 | ++ + | +
| 2024-01-15 15:28:15 | +1.8 GHz | +数据链路 | +-72 dBm | +高 | +分析中 | ++ + | +