File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -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 => `
|
||||
<tr class="fade-in">
|
||||
<td>${formatDateTime(record.time)}</td>
|
||||
<td><span class="badge badge-${record.type.toLowerCase()}">${formatType(record.type)}</span></td>
|
||||
<td>${record.result}</td>
|
||||
<td>
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-${getConfidenceColor(record.confidence)}"
|
||||
role="progressbar"
|
||||
style="width: ${record.confidence}%">
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">${record.confidence}%</small>
|
||||
</td>
|
||||
<td><span class="badge badge-${getThreatClass(record.threatLevel)}">${formatThreatLevel(record.threatLevel)}</span></td>
|
||||
<td>${formatLocation(record.location)}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" onclick="showRecordDetail(${record.id})">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 更新分页
|
||||
function updatePagination(pagination) {
|
||||
const paginationElement = document.querySelector('.pagination');
|
||||
paginationElement.innerHTML = `
|
||||
<li class="page-item ${pagination.currentPage === 1 ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="changePage(${pagination.currentPage - 1})">上一页</a>
|
||||
</li>
|
||||
${generatePageNumbers(pagination)}
|
||||
<li class="page-item ${pagination.currentPage === pagination.totalPages ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="changePage(${pagination.currentPage + 1})">下一页</a>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
|
||||
// 生成页码
|
||||
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 '<li class="page-item disabled"><span class="page-link">...</span></li>';
|
||||
}
|
||||
return `
|
||||
<li class="page-item ${page === current ? 'active' : ''}">
|
||||
<a class="page-link" href="#" onclick="changePage(${page})">${page}</a>
|
||||
</li>
|
||||
`;
|
||||
}).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 = `<span class="badge badge-${record.type.toLowerCase()}">${formatType(record.type)}</span>`;
|
||||
document.getElementById('detailResult').textContent = record.result;
|
||||
document.getElementById('detailConfidence').innerHTML = `
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-${getConfidenceColor(record.confidence)}"
|
||||
role="progressbar"
|
||||
style="width: ${record.confidence}%">
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">${record.confidence}%</small>
|
||||
`;
|
||||
document.getElementById('detailThreat').innerHTML = `<span class="badge badge-${getThreatClass(record.threatLevel)}">${formatThreatLevel(record.threatLevel)}</span>`;
|
||||
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();
|
||||
});
|
||||
@ -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 = '<i class="fas fa-compress"></i>';
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
this.innerHTML = '<i class="fas fa-expand"></i>';
|
||||
}
|
||||
});
|
||||
|
||||
// 导出记录
|
||||
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);
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>战场态势 - 无人机敌我识别系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<link href="common/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="battlefield-theme">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h4>
|
||||
<div class="logo-icon">
|
||||
<i class="fas fa-drone"></i>
|
||||
</div>
|
||||
无人机敌我识别系统
|
||||
</h4>
|
||||
</div>
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link active" href="dashboard.html">
|
||||
<i class="fas fa-chart-line"></i>战场态势
|
||||
</a>
|
||||
<a class="nav-link" href="identification.html">
|
||||
<i class="fas fa-crosshairs"></i>目标识别
|
||||
</a>
|
||||
<a class="nav-link" href="drones.html">
|
||||
<i class="fas fa-drone"></i>无人机管理
|
||||
</a>
|
||||
<a class="nav-link" href="records.html">
|
||||
<i class="fas fa-history"></i>识别记录
|
||||
</a>
|
||||
<a class="nav-link" href="signal.html">
|
||||
<i class="fas fa-broadcast-tower"></i>信号识别
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="container-fluid">
|
||||
<div class="battlefield-header">
|
||||
<div class="header-left">
|
||||
<h2 class="page-title">战场态势</h2>
|
||||
<div class="battlefield-time">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span id="current-time">2024-01-01 12:00:00</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operator-info">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
操作员:战术指挥官
|
||||
<span class="status-badge online">在线</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row status-cards">
|
||||
<div class="col-md-3">
|
||||
<div class="status-card executing">
|
||||
<div class="status-info">
|
||||
<h3>执行中无人机</h3>
|
||||
<div class="status-value">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="status-card identification">
|
||||
<div class="status-info">
|
||||
<h3>今日识别数</h3>
|
||||
<div class="status-value">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="status-card friendly">
|
||||
<div class="status-info">
|
||||
<h3>友方目标数</h3>
|
||||
<div class="status-value">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="status-card hostile">
|
||||
<div class="status-info">
|
||||
<h3>敌方目标数</h3>
|
||||
<div class="status-value">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card battlefield-card main-radar-card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-radar"></i>实时战术态势
|
||||
</h5>
|
||||
<div class="radar-controls">
|
||||
<button class="btn btn-sm btn-outline-info" id="toggleFullscreen">
|
||||
<i class="fas fa-expand"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info" id="resetCamera">
|
||||
<i class="fas fa-sync"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info" id="toggleGrid">
|
||||
<i class="fas fa-th"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="radar-stats">
|
||||
<div class="stat-item">
|
||||
<span class="label">扫描范围</span>
|
||||
<span class="value">100km</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="label">目标数量</span>
|
||||
<span class="value">12</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="label">信号强度</span>
|
||||
<span class="value">87%</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="label">高度范围</span>
|
||||
<span class="value">10km</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tacticalRadar" class="tactical-radar"></div>
|
||||
<div class="radar-legend">
|
||||
<div class="legend-item">
|
||||
<span class="dot friendly"></span>
|
||||
<span>友军目标</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="dot hostile"></span>
|
||||
<span>敌军目标</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="dot unknown"></span>
|
||||
<span>未识别目标</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card battlefield-card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-shield-alt"></i>威胁态势分析
|
||||
</h5>
|
||||
<div class="card-tools">
|
||||
<button class="btn btn-sm btn-outline-info">
|
||||
<i class="fas fa-sync"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="threat-level">
|
||||
<div class="level-label">当前威胁等级</div>
|
||||
<div class="level-value low">低</div>
|
||||
<div class="level-indicator">
|
||||
<div class="indicator-bar" style="width: 20%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="threatAnalysis" class="threat-analysis"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card battlefield-card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-bell"></i>战场活动播报
|
||||
</h5>
|
||||
<div class="card-tools">
|
||||
<button class="btn btn-sm btn-outline-info">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="battlefieldActivity" class="battlefield-activity"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.158.0/three.min.js"></script>
|
||||
<script type="module">
|
||||
import { OrbitControls } from 'https://unpkg.com/three@0.158.0/examples/jsm/controls/OrbitControls.js';
|
||||
window.OrbitControls = OrbitControls;
|
||||
</script>
|
||||
<script src="common/js/api.js"></script>
|
||||
<script src="common/js/common.js"></script>
|
||||
<script src="common/js/battlefield.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,266 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>目标识别 - 无人机敌我识别系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<link href="common/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="battlefield-theme">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h4>
|
||||
<div class="logo-icon">
|
||||
<i class="fas fa-drone"></i>
|
||||
</div>
|
||||
无人机敌我识别系统
|
||||
</h4>
|
||||
</div>
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link" href="dashboard.html">
|
||||
<i class="fas fa-chart-line"></i>战场态势
|
||||
</a>
|
||||
<a class="nav-link active" href="identification.html">
|
||||
<i class="fas fa-crosshairs"></i>目标识别
|
||||
</a>
|
||||
<a class="nav-link" href="drones.html">
|
||||
<i class="fas fa-drone"></i>无人机管理
|
||||
</a>
|
||||
<a class="nav-link" href="records.html">
|
||||
<i class="fas fa-history"></i>识别记录
|
||||
</a>
|
||||
<a class="nav-link" href="signal.html">
|
||||
<i class="fas fa-broadcast-tower"></i>信号识别
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="container-fluid">
|
||||
<div class="battlefield-header">
|
||||
<div class="header-left">
|
||||
<h2 class="page-title">目标识别</h2>
|
||||
<div class="battlefield-time">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span id="current-time">2024-01-01 12:00:00</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operator-info">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
操作员:战术指挥官
|
||||
<span class="status-badge online">在线</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card battlefield-card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-camera"></i>实时视频流
|
||||
</h5>
|
||||
<div class="stream-status">
|
||||
<span class="status-dot active"></span>
|
||||
实时识别中
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="video-container">
|
||||
<div id="videoStream" class="video-stream">
|
||||
<!-- 视频流将通过 JavaScript 加载 -->
|
||||
</div>
|
||||
<div class="video-overlay">
|
||||
<div class="target-box" style="display: none;">
|
||||
<div class="target-marker"></div>
|
||||
<div class="target-info">
|
||||
<span class="target-type">未知目标</span>
|
||||
<span class="target-distance">距离: 0m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay-info">
|
||||
<div class="info-item">
|
||||
<i class="fas fa-signal"></i>
|
||||
信号强度: 85%
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="fas fa-compass"></i>
|
||||
方位: 235°
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="fas fa-arrows-alt"></i>
|
||||
目标锁定: 已锁定
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-controls mt-3">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" id="startStream">
|
||||
<i class="fas fa-play"></i>开始识别
|
||||
</button>
|
||||
<button class="btn btn-danger" id="stopStream">
|
||||
<i class="fas fa-stop"></i>停止识别
|
||||
</button>
|
||||
<button class="btn btn-success" id="captureImage">
|
||||
<i class="fas fa-camera"></i>截图
|
||||
</button>
|
||||
</div>
|
||||
<div class="stream-settings">
|
||||
<button class="btn btn-outline-info" id="toggleAR">
|
||||
<i class="fas fa-vr-cardboard"></i>AR叠加
|
||||
</button>
|
||||
<button class="btn btn-outline-info" id="toggleNightVision">
|
||||
<i class="fas fa-moon"></i>夜视模式
|
||||
</button>
|
||||
<button class="btn btn-outline-info" id="toggleThermal">
|
||||
<i class="fas fa-temperature-high"></i>热成像
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card battlefield-card mt-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-chart-bar"></i>识别分析
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="analysis-chart">
|
||||
<canvas id="confidenceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="analysis-chart">
|
||||
<canvas id="typeDistributionChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card battlefield-card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-info-circle"></i>目标信息
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="target-details">
|
||||
<div class="detail-group">
|
||||
<div class="detail-item">
|
||||
<label>目标类型</label>
|
||||
<span id="targetType" class="value">军用无人机</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>置信度</label>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: 85%">
|
||||
85%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-group">
|
||||
<div class="detail-item">
|
||||
<label>距离</label>
|
||||
<span id="distance" class="value">2.5km</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>速度</label>
|
||||
<span id="speed" class="value">120km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-group">
|
||||
<div class="detail-item">
|
||||
<label>高度</label>
|
||||
<span id="altitude" class="value">500m</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>航向</label>
|
||||
<span id="heading" class="value">235°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-group">
|
||||
<div class="detail-item">
|
||||
<label>威胁等级</label>
|
||||
<span id="threatLevel" class="badge bg-danger">高</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>目标状态</label>
|
||||
<span id="targetStatus" class="badge bg-warning">跟踪中</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="target-actions mt-3">
|
||||
<button class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-exclamation-triangle"></i>发送警告
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm">
|
||||
<i class="fas fa-radiation"></i>标记威胁
|
||||
</button>
|
||||
<button class="btn btn-info btn-sm">
|
||||
<i class="fas fa-share-alt"></i>共享信息
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card battlefield-card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-history"></i>最近识别
|
||||
</h5>
|
||||
<button class="btn btn-sm btn-outline-info">
|
||||
<i class="fas fa-sync"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="recent-identifications">
|
||||
<div class="identification-item">
|
||||
<div class="item-time">10:30:15</div>
|
||||
<div class="item-content">
|
||||
<div class="item-type hostile">敌方无人机</div>
|
||||
<div class="item-info">距离: 3.2km / 速度: 150km/h</div>
|
||||
</div>
|
||||
<div class="item-status">已确认</div>
|
||||
</div>
|
||||
<div class="identification-item">
|
||||
<div class="item-time">10:28:45</div>
|
||||
<div class="item-content">
|
||||
<div class="item-type friendly">友方战机</div>
|
||||
<div class="item-info">距离: 5.1km / 速度: 280km/h</div>
|
||||
</div>
|
||||
<div class="item-status">已确认</div>
|
||||
</div>
|
||||
<div class="identification-item">
|
||||
<div class="item-time">10:25:30</div>
|
||||
<div class="item-content">
|
||||
<div class="item-type unknown">未知目标</div>
|
||||
<div class="item-info">距离: 8.7km / 速度: 90km/h</div>
|
||||
</div>
|
||||
<div class="item-status">分析中</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="common/js/api.js"></script>
|
||||
<script src="common/js/common.js"></script>
|
||||
<script src="common/js/identification.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>无人机敌我识别系统</title>
|
||||
<script>
|
||||
window.location.href = 'login.html';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
正在跳转到登录页面...
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>登录 - 无人机敌我识别系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<link href="common/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<!-- 雷达动画背景 -->
|
||||
<div class="radar-animation">
|
||||
<div class="radar-spinner"></div>
|
||||
<div class="radar-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<div class="login-header">
|
||||
<div class="logo-icon">
|
||||
<div class="military-star">★</div>
|
||||
</div>
|
||||
<h2>无人机敌我识别系统</h2>
|
||||
<p class="text-muted">军用级安全认证</p>
|
||||
</div>
|
||||
<div class="login-form">
|
||||
<form id="loginForm" onsubmit="return handleLogin(event)">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</span>
|
||||
<input type="text" class="form-control" id="username" placeholder="用户名" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-key"></i>
|
||||
</span>
|
||||
<input type="password" class="form-control" id="password" placeholder="密码" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="rememberMe">
|
||||
<label class="form-check-label" for="rememberMe">记住登录</label>
|
||||
</div>
|
||||
<a href="#" class="forgot-password">重置密码</a>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-sign-in-alt me-2"></i>安全登录
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="login-footer">
|
||||
<p class="text-center mb-0">
|
||||
<small>
|
||||
<i class="fas fa-shield-alt me-1"></i>
|
||||
军用级安全加密传输
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="common/js/api.js"></script>
|
||||
<script src="common/js/common.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,310 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>识别记录 - 无人机敌我识别系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<link href="common/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="battlefield-theme">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h4>
|
||||
<div class="logo-icon">
|
||||
<i class="fas fa-drone"></i>
|
||||
</div>
|
||||
无人机敌我识别系统
|
||||
</h4>
|
||||
</div>
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link" href="dashboard.html">
|
||||
<i class="fas fa-chart-line"></i>战场态势
|
||||
</a>
|
||||
<a class="nav-link" href="identification.html">
|
||||
<i class="fas fa-crosshairs"></i>目标识别
|
||||
</a>
|
||||
<a class="nav-link" href="drones.html">
|
||||
<i class="fas fa-drone"></i>无人机管理
|
||||
</a>
|
||||
<a class="nav-link active" href="records.html">
|
||||
<i class="fas fa-history"></i>识别记录
|
||||
</a>
|
||||
<a class="nav-link" href="signal.html">
|
||||
<i class="fas fa-broadcast-tower"></i>信号识别
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content records-page">
|
||||
<div class="container-fluid">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="stats-card fade-in">
|
||||
<div class="stats-icon">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
</div>
|
||||
<div class="stats-value">2,458</div>
|
||||
<div class="stats-label">总识别次数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stats-card fade-in">
|
||||
<div class="stats-icon">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<div class="stats-value">1,245</div>
|
||||
<div class="stats-label">友方目标</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stats-card fade-in">
|
||||
<div class="stats-icon">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<div class="stats-value">892</div>
|
||||
<div class="stats-label">敌方目标</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stats-card fade-in">
|
||||
<div class="stats-icon">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</div>
|
||||
<div class="stats-value">321</div>
|
||||
<div class="stats-label">未知目标</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选条件卡片 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card slide-in">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-filter"></i> 筛选条件
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="filterForm" class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">时间范围</label>
|
||||
<select class="form-select" id="timeRange">
|
||||
<option value="today">今天</option>
|
||||
<option value="week">本周</option>
|
||||
<option value="month">本月</option>
|
||||
<option value="custom">自定义</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">目标类型</label>
|
||||
<select class="form-select" id="targetType">
|
||||
<option value="">全部</option>
|
||||
<option value="friendly">友方</option>
|
||||
<option value="hostile">敌方</option>
|
||||
<option value="unknown">未知</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">识别状态</label>
|
||||
<select class="form-select" id="identificationStatus">
|
||||
<option value="">全部</option>
|
||||
<option value="success">识别成功</option>
|
||||
<option value="failed">识别失败</option>
|
||||
<option value="pending">待确认</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">威胁等级</label>
|
||||
<select class="form-select" id="threatLevel">
|
||||
<option value="">全部</option>
|
||||
<option value="high">高</option>
|
||||
<option value="medium">中</option>
|
||||
<option value="low">低</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-search"></i> 搜索
|
||||
</button>
|
||||
<button type="reset" class="btn btn-secondary ms-2">
|
||||
<i class="fas fa-redo"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 记录列表卡片 -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card slide-in">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-list"></i> 识别记录列表
|
||||
</h5>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-outline-primary">
|
||||
<i class="fas fa-download"></i> 导出
|
||||
</button>
|
||||
<button class="btn btn-outline-primary ms-2">
|
||||
<i class="fas fa-print"></i> 打印
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>目标类型</th>
|
||||
<th>识别结果</th>
|
||||
<th>置信度</th>
|
||||
<th>威胁等级</th>
|
||||
<th>位置</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recordsList">
|
||||
<!-- 示例数据 -->
|
||||
<tr>
|
||||
<td>2024-01-15 14:30:25</td>
|
||||
<td><span class="badge badge-friendly">友方</span></td>
|
||||
<td>侦察无人机</td>
|
||||
<td>98%</td>
|
||||
<td><span class="badge badge-success">低</span></td>
|
||||
<td>N 39°54'25" E 116°23'50"</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#recordDetailModal">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2024-01-15 14:28:15</td>
|
||||
<td><span class="badge badge-hostile">敌方</span></td>
|
||||
<td>攻击无人机</td>
|
||||
<td>95%</td>
|
||||
<td><span class="badge badge-danger">高</span></td>
|
||||
<td>N 39°54'28" E 116°23'52"</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#recordDetailModal">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav aria-label="Page navigation" class="mt-4">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" tabindex="-1">上一页</a>
|
||||
</li>
|
||||
<li class="page-item active"><a class="page-link" href="#">1</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">2</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="#">下一页</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 记录详情模态框 -->
|
||||
<div class="modal fade" id="recordDetailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">识别记录详情</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<img src="" class="img-fluid mb-3" id="targetImage" alt="目标图像">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-accent mb-3">基本信息</h6>
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="35%">识别时间:</td>
|
||||
<td id="detailTime">2024-01-15 14:30:25</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>目标类型:</td>
|
||||
<td id="detailType"><span class="badge badge-friendly">友方</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>识别结果:</td>
|
||||
<td id="detailResult">侦察无人机</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>置信度:</td>
|
||||
<td id="detailConfidence">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: 98%"></div>
|
||||
</div>
|
||||
<small class="text-muted">98%</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>威胁等级:</td>
|
||||
<td id="detailThreat"><span class="badge badge-success">低</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>位置坐标:</td>
|
||||
<td id="detailLocation">N 39°54'25" E 116°23'50"</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h6 class="text-accent mb-3">技术参数</h6>
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="35%">飞行高度:</td>
|
||||
<td>1,200米</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>飞行速度:</td>
|
||||
<td>180km/h</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>航向角度:</td>
|
||||
<td>45°</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
<button type="button" class="btn btn-primary">
|
||||
<i class="fas fa-download"></i> 导出详情
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="common/js/identification.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -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服务器已停止")
|
||||
@ -0,0 +1,333 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>信号识别 - 无人机敌我识别系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link href="common/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="battlefield-theme">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h4>
|
||||
<div class="logo-icon">
|
||||
<i class="fas fa-drone"></i>
|
||||
</div>
|
||||
无人机敌我识别系统
|
||||
</h4>
|
||||
</div>
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link" href="dashboard.html">
|
||||
<i class="fas fa-chart-line"></i>战场态势
|
||||
</a>
|
||||
<a class="nav-link" href="identification.html">
|
||||
<i class="fas fa-crosshairs"></i>目标识别
|
||||
</a>
|
||||
<a class="nav-link" href="drones.html">
|
||||
<i class="fas fa-drone"></i>无人机管理
|
||||
</a>
|
||||
<a class="nav-link" href="records.html">
|
||||
<i class="fas fa-history"></i>识别记录
|
||||
</a>
|
||||
<a class="nav-link active" href="signal.html">
|
||||
<i class="fas fa-broadcast-tower"></i>信号识别
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content signal-page">
|
||||
<div class="container-fluid">
|
||||
<!-- 状态卡片 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="status-card fade-in">
|
||||
<div class="status-icon">
|
||||
<i class="fas fa-satellite-dish"></i>
|
||||
</div>
|
||||
<div class="status-value">24</div>
|
||||
<div class="status-label">活跃信号源</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="status-card fade-in">
|
||||
<div class="status-icon">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<div class="status-value">12</div>
|
||||
<div class="status-label">友方信号</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="status-card fade-in">
|
||||
<div class="status-icon">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<div class="status-value">8</div>
|
||||
<div class="status-label">可疑信号</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="status-card fade-in">
|
||||
<div class="status-icon">
|
||||
<i class="fas fa-radiation"></i>
|
||||
</div>
|
||||
<div class="status-value">4</div>
|
||||
<div class="status-label">干扰源</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-wave-square"></i>信号波形分析
|
||||
</h5>
|
||||
<div class="card-tools">
|
||||
<button class="btn btn-sm btn-outline-primary" id="toggleFullscreen">
|
||||
<i class="fas fa-expand"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="signal-container">
|
||||
<div class="signal-controls mb-3">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" id="startSignalScan">
|
||||
<i class="fas fa-play"></i>开始扫描
|
||||
</button>
|
||||
<button class="btn btn-danger" id="stopSignalScan" disabled>
|
||||
<i class="fas fa-stop"></i>停止扫描
|
||||
</button>
|
||||
<button class="btn btn-success" id="captureSignal">
|
||||
<i class="fas fa-camera"></i>捕获信号
|
||||
</button>
|
||||
</div>
|
||||
<div class="frequency-controls">
|
||||
<div class="frequency-selector">
|
||||
<label>频率范围:</label>
|
||||
<select class="form-select" id="frequencyRange">
|
||||
<option value="all">全频段</option>
|
||||
<option value="low">低频段 (300MHz-1GHz)</option>
|
||||
<option value="mid">中频段 (1GHz-2.4GHz)</option>
|
||||
<option value="high">高频段 (2.4GHz+)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="scan-speed">
|
||||
<label>扫描速度:</label>
|
||||
<input type="range" class="form-range" id="scanSpeed" min="1" max="5" value="3">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signal-display scanning">
|
||||
<canvas id="signalCanvas"></canvas>
|
||||
<div class="signal-markers">
|
||||
<div class="frequency-marker" style="left: 25%">
|
||||
<div class="marker-line"></div>
|
||||
<div class="marker-label">1.2 GHz</div>
|
||||
</div>
|
||||
<div class="frequency-marker" style="left: 75%">
|
||||
<div class="marker-line"></div>
|
||||
<div class="marker-label">2.4 GHz</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signal-info mt-3">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="info-item">
|
||||
<label>当前频率</label>
|
||||
<span id="currentFrequency">2.4 GHz</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="info-item">
|
||||
<label>信号强度</label>
|
||||
<span id="signalStrength">-65 dBm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="info-item">
|
||||
<label>带宽</label>
|
||||
<span id="bandwidth">20 MHz</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="info-item">
|
||||
<label>信噪比</label>
|
||||
<span id="snr">32 dB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-fingerprint"></i>信号特征
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="signal-features">
|
||||
<div class="feature-item">
|
||||
<label>调制方式</label>
|
||||
<span id="modulationType">QPSK</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<label>信号类型</label>
|
||||
<span id="signalType">遥控信号</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<label>加密类型</label>
|
||||
<span id="encryptionType">AES-256</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<label>信号质量</label>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: 85%">
|
||||
<span class="progress-text">85%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<label>干扰程度</label>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-warning" role="progressbar" style="width: 25%">
|
||||
<span class="progress-text">25%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-shield-alt"></i>威胁分析
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="threat-analysis">
|
||||
<div class="threat-level">
|
||||
<label>威胁等级</label>
|
||||
<span class="badge bg-warning">中等</span>
|
||||
</div>
|
||||
<div class="threat-details mt-3">
|
||||
<h6 class="text-accent">威胁详情</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<i class="fas fa-exclamation-triangle text-warning"></i>
|
||||
检测到可疑的加密通信
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-info-circle text-info"></i>
|
||||
信号特征与已知敌方设备部分匹配
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-shield-alt text-success"></i>
|
||||
通信协议已被识别
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="recommended-actions mt-3">
|
||||
<h6 class="text-accent">建议操作</h6>
|
||||
<button class="btn btn-warning btn-sm mb-2 w-100">
|
||||
<i class="fas fa-radar"></i>持续监控
|
||||
</button>
|
||||
<button class="btn btn-info btn-sm w-100">
|
||||
<i class="fas fa-file-export"></i>导出分析报告
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-history"></i>信号记录
|
||||
</h5>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline-primary">
|
||||
<i class="fas fa-download"></i>导出记录
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger">
|
||||
<i class="fas fa-trash"></i>清除记录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>频率</th>
|
||||
<th>信号类型</th>
|
||||
<th>信号强度</th>
|
||||
<th>威胁等级</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>2024-01-15 15:30:25</td>
|
||||
<td>2.4 GHz</td>
|
||||
<td>遥控信号</td>
|
||||
<td>-65 dBm</td>
|
||||
<td><span class="badge bg-warning">中等</span></td>
|
||||
<td><span class="badge bg-success">已识别</span></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2024-01-15 15:28:15</td>
|
||||
<td>1.8 GHz</td>
|
||||
<td>数据链路</td>
|
||||
<td>-72 dBm</td>
|
||||
<td><span class="badge bg-danger">高</span></td>
|
||||
<td><span class="badge bg-warning">分析中</span></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="common/js/signal.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in new issue