765 lines
34 KiB
HTML
765 lines
34 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>任务管理 - 多任务YOLO检测系统</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
|
<link href="css/style.css" rel="stylesheet">
|
|
</head>
|
|
<body>
|
|
<!-- 导航栏 -->
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
|
<div class="container-fluid">
|
|
<a class="navbar-brand" href="index.html">
|
|
<i class="bi bi-cpu-fill me-2"></i>多任务YOLO检测系统
|
|
</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="navbarNav">
|
|
<ul class="navbar-nav ms-auto">
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="index.html">
|
|
<i class="bi bi-house-door me-1"></i>首页
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link active" href="task-management.html">
|
|
<i class="bi bi-list-task me-1"></i>任务管理
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="monitoring.html">
|
|
<i class="bi bi-graph-up me-1"></i>系统监控
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="video-player.html">
|
|
<i class="bi bi-camera-video me-1"></i>视频播放
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- 主内容区 -->
|
|
<div class="container-fluid mt-4">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2><i class="bi bi-list-task text-primary me-2"></i>任务管理</h2>
|
|
<div>
|
|
<button class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#createTaskModal">
|
|
<i class="bi bi-plus-circle me-1"></i>创建新任务
|
|
</button>
|
|
<button class="btn btn-outline-secondary" id="refreshTasks">
|
|
<i class="bi bi-arrow-clockwise me-1"></i>刷新
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 任务列表 -->
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-white">
|
|
<h5 class="mb-0">运行中的任务</h5>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>任务ID</th>
|
|
<th>任务名称</th>
|
|
<th>RTMP地址</th>
|
|
<th>模型数量</th>
|
|
<th>状态</th>
|
|
<th>FPS</th>
|
|
<th>创建时间</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="taskTableBody">
|
|
<!-- 任务数据将通过JavaScript动态生成 -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer text-muted">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>共 <span id="totalTasks">0</span> 个任务,<span id="runningTasks">0</span> 个运行中</div>
|
|
<div class="small">最后更新: <span id="lastUpdateTime">--:--:--</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 批量操作 -->
|
|
<div class="card shadow-sm border-0 mt-4">
|
|
<div class="card-header bg-white">
|
|
<h5 class="mb-0">批量操作</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<button class="btn btn-success" id="startAllTasks">
|
|
<i class="bi bi-play-circle me-1"></i>启动所有任务
|
|
</button>
|
|
<button class="btn btn-warning" id="stopAllTasks">
|
|
<i class="bi bi-pause-circle me-1"></i>停止所有任务
|
|
</button>
|
|
<button class="btn btn-danger" id="cleanupStoppedTasks">
|
|
<i class="bi bi-trash me-1"></i>清理已停止任务
|
|
</button>
|
|
<button class="btn btn-info" id="cleanupAllTasks">
|
|
<i class="bi bi-eraser me-1"></i>清理所有任务
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 模型库 -->
|
|
<div class="card shadow-sm border-0 mt-4">
|
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">加密模型库</h5>
|
|
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#uploadModelModal">
|
|
<i class="bi bi-upload me-1"></i>上传模型
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row" id="modelLibrary">
|
|
<!-- 模型卡片将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 创建任务模态框 -->
|
|
<div class="modal fade" id="createTaskModal" 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">
|
|
<form id="createTaskForm">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="taskName" class="form-label">任务名称</label>
|
|
<input type="text" class="form-control" id="taskName" placeholder="输入任务名称" required>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="rtmpUrl" class="form-label">RTMP视频流地址</label>
|
|
<input type="text" class="form-control" id="rtmpUrl" placeholder="rtmp://example.com/live/stream" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="pushUrl" class="form-label">推流地址 (可选)</label>
|
|
<input type="text" class="form-control" id="pushUrl" placeholder="rtmp://example.com/live/output">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">选择模型</label>
|
|
<div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;">
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="model1" checked>
|
|
<label class="form-check-label" for="model1">
|
|
yolov8n.pt (行人检测)
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="model2" checked>
|
|
<label class="form-check-label" for="model2">
|
|
yolov8s.pt (车辆检测)
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="model3">
|
|
<label class="form-check-label" for="model3">
|
|
yolov8m.pt (交通标志识别)
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="model4">
|
|
<label class="form-check-label" for="model4">
|
|
custom.pt (自定义模型)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="encryptionKey" class="form-label">加密密钥</label>
|
|
<input type="password" class="form-control" id="encryptionKey" placeholder="输入模型加密密钥" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="enablePush" checked>
|
|
<label class="form-check-label" for="enablePush">
|
|
启用结果推流
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" id="submitTaskForm">创建任务</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 上传模型模态框 -->
|
|
<div class="modal fade" id="uploadModelModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<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">
|
|
<form id="uploadModelForm">
|
|
<div class="mb-3">
|
|
<label for="modelFile" class="form-label">选择模型文件 (.pt, .pth)</label>
|
|
<input class="form-control" type="file" id="modelFile" accept=".pt,.pth,.enc">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="modelName" class="form-label">模型名称</label>
|
|
<input type="text" class="form-control" id="modelName" placeholder="输入模型名称">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="modelDescription" class="form-label">模型描述</label>
|
|
<textarea class="form-control" id="modelDescription" rows="3" placeholder="描述模型的用途和特点"></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="modelEncryptionKey" class="form-label">加密密钥</label>
|
|
<input type="password" class="form-control" id="modelEncryptionKey" placeholder="输入加密密钥" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="confirmEncryptionKey" class="form-label">确认加密密钥</label>
|
|
<input type="password" class="form-control" id="confirmEncryptionKey" placeholder="再次输入加密密钥" required>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" id="submitUploadModel">上传模型</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 任务详情模态框 -->
|
|
<div class="modal fade" id="taskDetailModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="detailModalTitle">任务详情</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="taskDetailContent">
|
|
<!-- 任务详情将通过JavaScript动态生成 -->
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 页脚 -->
|
|
<footer class="bg-dark text-white mt-5 py-4">
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h5>任务管理系统</h5>
|
|
<p class="text-light">多任务YOLO检测系统的任务管理界面</p>
|
|
</div>
|
|
<div class="col-md-6 text-md-end">
|
|
<p class="mb-0">© 2023 多任务YOLO检测系统 | 演示版本 v1.0.0</p>
|
|
<p class="text-light small">此页面为演示版本,使用模拟数据</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// 模拟任务数据
|
|
const mockTasks = [
|
|
{
|
|
id: "task_001",
|
|
name: "交通监控-路口A",
|
|
rtmpUrl: "rtmp://localhost:1935/live/camera1",
|
|
pushUrl: "rtmp://localhost:1935/live/output1",
|
|
models: 3,
|
|
status: "running",
|
|
fps: 24.5,
|
|
createdAt: "2023-10-15 09:30:00",
|
|
detectionCount: 156,
|
|
avgProcessTime: "0.045s",
|
|
encryptionStatus: "已验证"
|
|
},
|
|
{
|
|
id: "task_002",
|
|
name: "安全监控-入口",
|
|
rtmpUrl: "rtmp://localhost:1935/live/camera2",
|
|
pushUrl: "rtmp://localhost:1935/live/output2",
|
|
models: 2,
|
|
status: "running",
|
|
fps: 28.1,
|
|
createdAt: "2023-10-15 10:15:00",
|
|
detectionCount: 89,
|
|
avgProcessTime: "0.038s",
|
|
encryptionStatus: "已验证"
|
|
},
|
|
{
|
|
id: "task_003",
|
|
name: "停车场监控",
|
|
rtmpUrl: "rtmp://localhost:1935/live/camera3",
|
|
pushUrl: "",
|
|
models: 1,
|
|
status: "stopped",
|
|
fps: 0,
|
|
createdAt: "2023-10-14 14:20:00",
|
|
detectionCount: 324,
|
|
avgProcessTime: "0.042s",
|
|
encryptionStatus: "已验证"
|
|
},
|
|
{
|
|
id: "task_004",
|
|
name: "生产线检测",
|
|
rtmpUrl: "rtmp://192.168.1.100:1935/live/production",
|
|
pushUrl: "rtmp://192.168.1.100:1935/live/production_out",
|
|
models: 2,
|
|
status: "error",
|
|
fps: 15.3,
|
|
createdAt: "2023-10-15 08:45:00",
|
|
detectionCount: 0,
|
|
avgProcessTime: "0.062s",
|
|
encryptionStatus: "已验证"
|
|
},
|
|
{
|
|
id: "task_005",
|
|
name: "测试任务",
|
|
rtmpUrl: "rtmp://test.com:1935/live/test",
|
|
pushUrl: "",
|
|
models: 1,
|
|
status: "creating",
|
|
fps: 0,
|
|
createdAt: "2023-10-15 11:05:00",
|
|
detectionCount: 0,
|
|
avgProcessTime: "0s",
|
|
encryptionStatus: "验证中"
|
|
}
|
|
];
|
|
|
|
// 模拟模型数据
|
|
const mockModels = [
|
|
{ id: 1, name: "yolov8n.pt", size: "6.2 MB", type: "行人检测", encrypted: true, uploaded: "2023-10-10" },
|
|
{ id: 2, name: "yolov8s.pt", size: "22.4 MB", type: "车辆检测", encrypted: true, uploaded: "2023-10-11" },
|
|
{ id: 3, name: "yolov8m.pt", size: "50.1 MB", type: "交通标志", encrypted: true, uploaded: "2023-10-12" },
|
|
{ id: 4, name: "custom.pt", size: "15.7 MB", type: "自定义模型", encrypted: true, uploaded: "2023-10-13" },
|
|
{ id: 5, name: "yolov5s.pt", size: "14.5 MB", type: "通用检测", encrypted: true, uploaded: "2023-10-14" }
|
|
];
|
|
|
|
// 状态颜色映射
|
|
const statusColors = {
|
|
running: "success",
|
|
stopped: "secondary",
|
|
error: "danger",
|
|
creating: "info"
|
|
};
|
|
|
|
// 状态文本映射
|
|
const statusText = {
|
|
running: "运行中",
|
|
stopped: "已停止",
|
|
error: "错误",
|
|
creating: "创建中"
|
|
};
|
|
|
|
// 渲染任务表格
|
|
function renderTaskTable() {
|
|
const tbody = document.getElementById('taskTableBody');
|
|
tbody.innerHTML = '';
|
|
|
|
let runningCount = 0;
|
|
|
|
mockTasks.forEach(task => {
|
|
const row = document.createElement('tr');
|
|
const statusColor = statusColors[task.status] || 'secondary';
|
|
const statusTextValue = statusText[task.status] || task.status;
|
|
|
|
if (task.status === 'running') runningCount++;
|
|
|
|
row.innerHTML = `
|
|
<td><code>${task.id}</code></td>
|
|
<td><strong>${task.name}</strong></td>
|
|
<td><small>${task.rtmpUrl}</small></td>
|
|
<td>${task.models}</td>
|
|
<td><span class="badge bg-${statusColor}">${statusTextValue}</span></td>
|
|
<td>${task.fps > 0 ? task.fps.toFixed(1) : '0'}</td>
|
|
<td>${task.createdAt}</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary me-1 view-task" data-task-id="${task.id}">
|
|
<i class="bi bi-eye"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-success me-1 start-task" data-task-id="${task.id}" ${task.status === 'running' ? 'disabled' : ''}>
|
|
<i class="bi bi-play"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-warning me-1 stop-task" data-task-id="${task.id}" ${task.status !== 'running' ? 'disabled' : ''}>
|
|
<i class="bi bi-stop"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger delete-task" data-task-id="${task.id}">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</td>
|
|
`;
|
|
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
// 更新统计信息
|
|
document.getElementById('totalTasks').textContent = mockTasks.length;
|
|
document.getElementById('runningTasks').textContent = runningCount;
|
|
document.getElementById('lastUpdateTime').textContent = new Date().toLocaleTimeString();
|
|
|
|
// 添加事件监听器
|
|
addTaskEventListeners();
|
|
}
|
|
|
|
// 渲染模型库
|
|
function renderModelLibrary() {
|
|
const container = document.getElementById('modelLibrary');
|
|
container.innerHTML = '';
|
|
|
|
mockModels.forEach(model => {
|
|
const col = document.createElement('div');
|
|
col.className = 'col-md-4 mb-3';
|
|
|
|
col.innerHTML = `
|
|
<div class="card h-100">
|
|
<div class="card-body">
|
|
<h6 class="card-title">${model.name}</h6>
|
|
<p class="card-text small text-muted">
|
|
<i class="bi bi-info-circle me-1"></i>${model.type}<br>
|
|
<i class="bi bi-database me-1"></i>${model.size}<br>
|
|
<i class="bi bi-calendar me-1"></i>${model.uploaded}
|
|
</p>
|
|
<div class="d-flex justify-content-between">
|
|
<span class="badge bg-success"><i class="bi bi-shield-check me-1"></i>已加密</span>
|
|
<div>
|
|
<button class="btn btn-sm btn-outline-primary me-1" title="查看详情">
|
|
<i class="bi bi-eye"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger" title="删除模型">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
container.appendChild(col);
|
|
});
|
|
}
|
|
|
|
// 添加任务事件监听器
|
|
function addTaskEventListeners() {
|
|
// 查看任务详情
|
|
document.querySelectorAll('.view-task').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const taskId = this.getAttribute('data-task-id');
|
|
const task = mockTasks.find(t => t.id === taskId);
|
|
showTaskDetail(task);
|
|
});
|
|
});
|
|
|
|
// 启动任务
|
|
document.querySelectorAll('.start-task').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const taskId = this.getAttribute('data-task-id');
|
|
startTask(taskId);
|
|
});
|
|
});
|
|
|
|
// 停止任务
|
|
document.querySelectorAll('.stop-task').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const taskId = this.getAttribute('data-task-id');
|
|
stopTask(taskId);
|
|
});
|
|
});
|
|
|
|
// 删除任务
|
|
document.querySelectorAll('.delete-task').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const taskId = this.getAttribute('data-task-id');
|
|
deleteTask(taskId);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 显示任务详情
|
|
function showTaskDetail(task) {
|
|
const modalTitle = document.getElementById('detailModalTitle');
|
|
const modalContent = document.getElementById('taskDetailContent');
|
|
|
|
modalTitle.textContent = `任务详情: ${task.name}`;
|
|
|
|
const statusColor = statusColors[task.status] || 'secondary';
|
|
const statusTextValue = statusText[task.status] || task.status;
|
|
|
|
modalContent.innerHTML = `
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>基本信息</h6>
|
|
<table class="table table-sm">
|
|
<tr><td><strong>任务ID:</strong></td><td>${task.id}</td></tr>
|
|
<tr><td><strong>任务名称:</strong></td><td>${task.name}</td></tr>
|
|
<tr><td><strong>状态:</strong></td><td><span class="badge bg-${statusColor}">${statusTextValue}</span></td></tr>
|
|
<tr><td><strong>创建时间:</strong></td><td>${task.createdAt}</td></tr>
|
|
<tr><td><strong>加密状态:</strong></td><td><span class="badge bg-success">${task.encryptionStatus}</span></td></tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>性能统计</h6>
|
|
<table class="table table-sm">
|
|
<tr><td><strong>模型数量:</strong></td><td>${task.models}</td></tr>
|
|
<tr><td><strong>当前FPS:</strong></td><td>${task.fps > 0 ? task.fps.toFixed(1) : '0'}</td></tr>
|
|
<tr><td><strong>平均处理时间:</strong></td><td>${task.avgProcessTime}</td></tr>
|
|
<tr><td><strong>检测目标数:</strong></td><td>${task.detectionCount}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<h6>流地址</h6>
|
|
<div class="mb-2">
|
|
<strong>RTMP输入:</strong> <code>${task.rtmpUrl}</code>
|
|
</div>
|
|
${task.pushUrl ? `<div><strong>推流输出:</strong> <code>${task.pushUrl}</code></div>` : ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('taskDetailModal'));
|
|
modal.show();
|
|
}
|
|
|
|
// 启动任务
|
|
function startTask(taskId) {
|
|
const task = mockTasks.find(t => t.id === taskId);
|
|
if (task) {
|
|
task.status = 'running';
|
|
task.fps = 24.5;
|
|
renderTaskTable();
|
|
showNotification('任务启动成功', `任务 ${task.name} 已成功启动`, 'success');
|
|
}
|
|
}
|
|
|
|
// 停止任务
|
|
function stopTask(taskId) {
|
|
const task = mockTasks.find(t => t.id === taskId);
|
|
if (task) {
|
|
task.status = 'stopped';
|
|
task.fps = 0;
|
|
renderTaskTable();
|
|
showNotification('任务停止成功', `任务 ${task.name} 已停止`, 'warning');
|
|
}
|
|
}
|
|
|
|
// 删除任务
|
|
function deleteTask(taskId) {
|
|
if (confirm('确定要删除这个任务吗?')) {
|
|
const index = mockTasks.findIndex(t => t.id === taskId);
|
|
if (index !== -1) {
|
|
mockTasks.splice(index, 1);
|
|
renderTaskTable();
|
|
showNotification('任务删除成功', '任务已成功删除', 'danger');
|
|
}
|
|
}
|
|
}
|
|
|
|
// 批量操作
|
|
document.getElementById('startAllTasks').addEventListener('click', function() {
|
|
mockTasks.forEach(task => {
|
|
task.status = 'running';
|
|
task.fps = task.fps > 0 ? task.fps : 24.5;
|
|
});
|
|
renderTaskTable();
|
|
showNotification('批量启动成功', '所有任务已启动', 'success');
|
|
});
|
|
|
|
document.getElementById('stopAllTasks').addEventListener('click', function() {
|
|
mockTasks.forEach(task => {
|
|
task.status = 'stopped';
|
|
task.fps = 0;
|
|
});
|
|
renderTaskTable();
|
|
showNotification('批量停止成功', '所有任务已停止', 'warning');
|
|
});
|
|
|
|
document.getElementById('cleanupStoppedTasks').addEventListener('click', function() {
|
|
const stoppedTasks = mockTasks.filter(task => task.status === 'stopped');
|
|
const stoppedCount = stoppedTasks.length;
|
|
|
|
stoppedTasks.forEach(task => {
|
|
const index = mockTasks.findIndex(t => t.id === task.id);
|
|
if (index !== -1) {
|
|
mockTasks.splice(index, 1);
|
|
}
|
|
});
|
|
|
|
renderTaskTable();
|
|
showNotification('清理完成', `已清理 ${stoppedCount} 个已停止的任务`, 'info');
|
|
});
|
|
|
|
document.getElementById('cleanupAllTasks').addEventListener('click', function() {
|
|
if (confirm('确定要清理所有任务吗?这将删除所有任务记录。')) {
|
|
mockTasks.length = 0;
|
|
renderTaskTable();
|
|
showNotification('清理完成', '所有任务已清理', 'info');
|
|
}
|
|
});
|
|
|
|
// 创建任务表单提交
|
|
document.getElementById('submitTaskForm').addEventListener('click', function() {
|
|
const taskName = document.getElementById('taskName').value;
|
|
const rtmpUrl = document.getElementById('rtmpUrl').value;
|
|
const encryptionKey = document.getElementById('encryptionKey').value;
|
|
|
|
if (!taskName || !rtmpUrl || !encryptionKey) {
|
|
showNotification('表单验证失败', '请填写所有必填字段', 'danger');
|
|
return;
|
|
}
|
|
|
|
// 生成新任务
|
|
const newTask = {
|
|
id: `task_${String(mockTasks.length + 1).padStart(3, '0')}`,
|
|
name: taskName,
|
|
rtmpUrl: rtmpUrl,
|
|
pushUrl: document.getElementById('pushUrl').value,
|
|
models: document.querySelectorAll('input[type="checkbox"]:checked').length,
|
|
status: 'creating',
|
|
fps: 0,
|
|
createdAt: new Date().toLocaleString(),
|
|
detectionCount: 0,
|
|
avgProcessTime: "0s",
|
|
encryptionStatus: "验证中"
|
|
};
|
|
|
|
mockTasks.push(newTask);
|
|
renderTaskTable();
|
|
|
|
// 关闭模态框
|
|
bootstrap.Modal.getInstance(document.getElementById('createTaskModal')).hide();
|
|
|
|
// 重置表单
|
|
document.getElementById('createTaskForm').reset();
|
|
|
|
// 模拟任务创建过程
|
|
setTimeout(() => {
|
|
const taskIndex = mockTasks.findIndex(t => t.id === newTask.id);
|
|
if (taskIndex !== -1) {
|
|
mockTasks[taskIndex].status = 'running';
|
|
mockTasks[taskIndex].fps = 24.5;
|
|
mockTasks[taskIndex].encryptionStatus = '已验证';
|
|
renderTaskTable();
|
|
}
|
|
}, 1500);
|
|
|
|
showNotification('任务创建成功', `任务 ${taskName} 已创建并启动`, 'success');
|
|
});
|
|
|
|
// 上传模型表单提交
|
|
document.getElementById('submitUploadModel').addEventListener('click', function() {
|
|
const modelName = document.getElementById('modelName').value;
|
|
const encryptionKey = document.getElementById('modelEncryptionKey').value;
|
|
const confirmKey = document.getElementById('confirmEncryptionKey').value;
|
|
|
|
if (!modelName || !encryptionKey || !confirmKey) {
|
|
showNotification('表单验证失败', '请填写所有必填字段', 'danger');
|
|
return;
|
|
}
|
|
|
|
if (encryptionKey !== confirmKey) {
|
|
showNotification('密钥不匹配', '两次输入的加密密钥不一致', 'danger');
|
|
return;
|
|
}
|
|
|
|
// 添加新模型
|
|
const newModel = {
|
|
id: mockModels.length + 1,
|
|
name: modelName || '未命名模型.pt',
|
|
size: `${(Math.random() * 50 + 5).toFixed(1)} MB`,
|
|
type: document.getElementById('modelDescription').value || '自定义模型',
|
|
encrypted: true,
|
|
uploaded: new Date().toLocaleDateString()
|
|
};
|
|
|
|
mockModels.push(newModel);
|
|
renderModelLibrary();
|
|
|
|
// 关闭模态框
|
|
bootstrap.Modal.getInstance(document.getElementById('uploadModelModal')).hide();
|
|
|
|
// 重置表单
|
|
document.getElementById('uploadModelForm').reset();
|
|
|
|
showNotification('模型上传成功', `模型 ${newModel.name} 已上传并加密`, 'success');
|
|
});
|
|
|
|
// 刷新任务列表
|
|
document.getElementById('refreshTasks').addEventListener('click', function() {
|
|
// 模拟数据更新
|
|
mockTasks.forEach(task => {
|
|
if (task.status === 'running') {
|
|
task.fps = (Math.random() * 10 + 20).toFixed(1);
|
|
task.detectionCount += Math.floor(Math.random() * 10);
|
|
}
|
|
});
|
|
|
|
renderTaskTable();
|
|
showNotification('数据已刷新', '任务列表已更新', 'info');
|
|
});
|
|
|
|
// 显示通知
|
|
function showNotification(title, message, type) {
|
|
// 创建通知元素
|
|
const notification = document.createElement('div');
|
|
notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
|
|
notification.style.cssText = 'top: 20px; right: 20px; z-index: 1050; min-width: 300px;';
|
|
notification.innerHTML = `
|
|
<strong>${title}</strong> ${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// 3秒后自动移除
|
|
setTimeout(() => {
|
|
if (notification.parentNode) {
|
|
notification.remove();
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
// 初始化
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
renderTaskTable();
|
|
renderModelLibrary();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |