You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4006 lines
165 KiB
HTML

1 month ago
<!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 rel="stylesheet" href="styles.css"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- 添加OpenLayers CSS和JS -->
<link rel="stylesheet" href="/common/plugin/map/ol/v6.12.0/css/ol.css" type="text/css" />
<script src="/common/plugin/map/ol/v6.12.0/build/ol.js"></script>
<link rel="stylesheet" href="all.min.css">
<style>
/* ==== 全局样式 ==== */
body {
margin: 0;
padding: 0;
font-family: 'Arial', sans-serif;
background-color: #f5f9ff;
color: #333;
}
/* ==== 主容器 ==== */
.main-container {
display: flex;
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
gap: 20px;
}
/* ==== 左侧内容区域 ==== */
.content-area {
flex: 1;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 75, 150, 0.1);
padding: 20px;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ==== 右侧侧边栏 ==== */
.sidebar-area {
width: 350px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 75, 150, 0.1);
padding: 20px;
display: flex;
flex-direction: column;
overflow-y: auto;
max-height: calc(100vh - 40px);
}
/* ==== 头部区域 ==== */
.device-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.device-title {
color: #1a4b8c;
font-size: 24px;
font-weight: 600;
}
.device-actions {
display: flex;
gap: 10px;
}
.action-btn {
background: linear-gradient(135deg, #2a7de1, #1a4b8c);
color: #fff;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
box-shadow: 0 2px 8px rgba(42, 125, 225, 0.2);
}
.action-btn:hover {
background: linear-gradient(135deg, #1a4b8c, #2a7de1);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(42, 125, 225, 0.3);
}
/* ==== 标签页 ==== */
.device-tabs {
display: flex;
background: #e1e9f5;
border-radius: 10px;
padding: 5px;
margin-bottom: 20px;
}
.tab-btn {
flex: 1;
padding: 12px 20px;
background: transparent;
color: #4a6fa5;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
font-weight: 500;
}
.tab-btn.active {
background: #ffffff;
color: #1a4b8c;
font-weight: 600;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.tab-content {
display: none;
flex: 1;
overflow-y: auto;
padding-right: 5px;
/* 为滚动条留空间 */
}
.tab-content.active {
display: block;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ==== 设备卡片 ==== */
.device-card {
background: #ffffff;
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e1e9f5;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
}
.card-title {
color: #1a4b8c;
font-size: 18px;
font-weight: 600;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.card-title i {
color: #2a7de1;
}
/* ==== 表单样式 ==== */
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-group label {
color: #4a6fa5;
font-size: 14px;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
background: #f5f9ff;
border: 1px solid #d1e0f5;
border-radius: 6px;
padding: 10px;
color: #1a4b8c;
font-size: 14px;
transition: all 0.3s ease;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
border-color: #2a7de1;
box-shadow: 0 0 0 3px rgba(42, 125, 225, 0.2);
outline: none;
}
.form-group input::placeholder,
.form-group textarea::placeholder {
color: #a8c0e0;
}
/* ==== 表格样式 ==== */
.device-table {
width: 100%;
background: #ffffff;
border-radius: 10px;
overflow: hidden;
border-collapse: separate;
border-spacing: 0;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
}
.device-table th,
.device-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #e1e9f5;
}
.device-table th {
background: #f5f9ff;
color: #1a4b8c;
font-weight: 600;
font-size: 14px;
}
.device-table td {
color: #4a6fa5;
font-size: 13px;
}
.device-table tr:last-child td {
border-bottom: none;
}
/* ==== 状态标签 ==== */
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
text-align: center;
display: inline-block;
min-width: 60px;
}
.status-online {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.status-offline {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border: 1px solid rgba(220, 53, 69, 0.3);
}
.status-maintenance {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
border: 1px solid rgba(255, 193, 7, 0.3);
}
.status-fault {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border: 1px solid rgba(220, 53, 69, 0.3);
}
/* ==== 侧边栏样式 ==== */
.sidebar-section {
background: #f5f9ff;
border-radius: 12px;
padding: 15px;
margin-bottom: 20px;
border: 1px solid #e1e9f5;
}
.sidebar-title {
color: #1a4b8c;
font-size: 16px;
font-weight: 600;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.sidebar-title i {
color: #2a7de1;
}
/* ==== 统计卡片 ==== */
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 15px;
}
.stat-item {
background: #ffffff;
padding: 15px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 75, 150, 0.05);
border: 1px solid #e1e9f5;
}
.stat-value {
color: #2a7de1;
font-size: 24px;
font-weight: 700;
display: block;
}
.stat-label {
color: #4a6fa5;
font-size: 12px;
margin-top: 5px;
}
/* ==== 图表容器 ==== */
.chart-container {
background: #ffffff;
border-radius: 8px;
padding: 15px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
color: #a8c0e0;
border: 1px solid #e1e9f5;
box-shadow: 0 2px 8px rgba(0, 75, 150, 0.05);
}
/* ==== 设备网格 ==== */
.device-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.device-item {
background: #ffffff;
border-radius: 12px;
padding: 20px;
border: 1px solid #e1e9f5;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 75, 150, 0.05);
}
.device-item:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 75, 150, 0.1);
}
.device-info {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 15px;
}
.device-icon {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #e1e9f5, #d1e0f5);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #2a7de1;
}
.device-details h4 {
color: #1a4b8c;
margin: 0 0 5px 0;
font-size: 16px;
}
.device-details p {
color: #4a6fa5;
margin: 0;
font-size: 14px;
}
.device-params {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 15px;
}
.param-item {
background: #f5f9ff;
padding: 8px;
border-radius: 6px;
text-align: center;
border: 1px solid #e1e9f5;
}
.param-value {
color: #2a7de1;
font-weight: 600;
display: block;
}
.param-label {
color: #4a6fa5;
font-size: 12px;
}
/* ==== 告警列表 ==== */
.alert-list {
max-height: 300px;
overflow-y: auto;
}
.alert-item {
background: #ffffff;
padding: 12px;
border-radius: 8px;
margin-bottom: 10px;
border-left: 4px solid #dc3545;
box-shadow: 0 2px 8px rgba(0, 75, 150, 0.05);
}
.alert-title {
color: #1a4b8c;
font-size: 14px;
font-weight: 600;
margin-bottom: 5px;
}
.alert-info {
color: #4a6fa5;
font-size: 12px;
display: flex;
justify-content: space-between;
}
/* ==== 按钮样式 ==== */
.btn {
background: #f5f9ff;
color: #2a7de1;
border: 1px solid #d1e0f5;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 12px;
margin-right: 5px;
}
.btn:hover {
background: #e1e9f5;
}
.btn-primary {
background: rgba(42, 125, 225, 0.1);
color: #2a7de1;
border-color: rgba(42, 125, 225, 0.3);
}
.btn-primary:hover {
background: rgba(42, 125, 225, 0.2);
}
.btn-success {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border-color: rgba(40, 167, 69, 0.3);
}
.btn-success:hover {
background: rgba(40, 167, 69, 0.2);
}
.btn-danger {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border-color: rgba(220, 53, 69, 0.3);
}
.btn-danger:hover {
background: rgba(220, 53, 69, 0.2);
}
.btn-warning {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
border-color: rgba(255, 193, 7, 0.3);
}
.btn-warning:hover {
background: rgba(255, 193, 7, 0.2);
}
/* ==== 模态框 ==== */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
backdrop-filter: blur(5px);
}
.modal-content {
background: #ffffff;
margin: 5% auto;
padding: 30px;
border-radius: 15px;
width: 80%;
max-width: 800px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 10px 30px rgba(0, 75, 150, 0.2);
}
.modal h2 {
color: #1a4b8c;
margin-bottom: 20px;
font-size: 20px;
border-bottom: 1px solid #e1e9f5;
padding-bottom: 10px;
}
.close {
color: #4a6fa5;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
margin-top: -10px;
transition: all 0.3s ease;
}
.close:hover {
color: #2a7de1;
}
/* ==== 滚动条样式 ==== */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f5f9ff;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #d1e0f5;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8c0e0;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
text-align: center;
display: inline-block;
min-width: 60px;
}
.status-pending {
background: rgba(255, 193, 7, 0.3);
color: #ffc107;
border: 1px solid #ffc107;
}
.status-executing {
background: rgba(0, 123, 255, 0.3);
color: #007bff;
border: 1px solid #007bff;
}
.status-completed {
background: rgba(40, 167, 69, 0.3);
color: #28a745;
border: 1px solid #28a745;
}
.status-overdue {
background: rgba(220, 53, 69, 0.3);
color: #dc3545;
border: 1px solid #dc3545;
}
.priority-high {
background: rgba(220, 53, 69, 0.3);
color: #dc3545;
border: 1px solid #dc3545;
}
.priority-medium {
background: rgba(255, 193, 7, 0.3);
color: #ffc107;
border: 1px solid #ffc107;
}
.priority-low {
background: rgba(40, 167, 69, 0.3);
color: #28a745;
border: 1px solid #28a745;
}
/* 添加任务详情样式 */
.task-detail-container {
padding: 10px;
}
.detail-section {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.detail-section h3 {
color: #1a4b8c;
font-size: 16px;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.detail-section h3 i {
color: #2a7de1;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.detail-item {
display: flex;
margin-bottom: 8px;
}
.detail-item.full-width {
grid-column: 1 / -1;
}
.detail-label {
color: #4a6fa5;
font-weight: 500;
min-width: 80px;
margin-right: 10px;
}
.detail-value {
color: #333;
flex: 1;
}
.checkpoint-list {
list-style-type: none;
padding: 0;
margin: 0;
}
.checkpoint-list li {
padding: 8px 0;
border-bottom: 1px dashed #e1e9f5;
display: flex;
align-items: center;
}
.checkpoint-list li:last-child {
border-bottom: none;
}
.checkpoint-list li::before {
content: "•";
color: #2a7de1;
font-weight: bold;
display: inline-block;
width: 1em;
margin-left: -1em;
}
/* 添加路线检查点样式 */
.checkpoint-container {
padding: 10px;
}
.checkpoint-item {
padding: 10px;
margin-bottom: 8px;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
border-left: 4px solid #e1e9f5;
transition: all 0.3s ease;
}
.checkpoint-item.completed {
border-left-color: #28a745;
background-color: rgba(40, 167, 69, 0.05);
}
.checkpoint-item.current {
border-left-color: #2a7de1;
background-color: rgba(42, 125, 225, 0.05);
}
.checkpoint-item h4 {
margin: 0 0 5px 0;
color: #1a4b8c;
font-size: 14px;
display: flex;
align-items: center;
}
.checkpoint-item h4 i {
margin-right: 8px;
color: #4a6fa5;
}
.checkpoint-item p {
margin: 0;
color: #4a6fa5;
font-size: 12px;
}
.checkpoint-status {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
margin-left: 8px;
}
.status-pending {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
}
.status-completed {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
}
/* 添加报告详情样式 */
.report-header {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.report-title {
font-size: 18px;
font-weight: bold;
color: #1a4b8c;
margin-bottom: 5px;
}
.report-meta {
display: flex;
gap: 15px;
font-size: 13px;
color: #4a6fa5;
}
.report-section {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.report-section h3 {
color: #1a4b8c;
font-size: 16px;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.report-section h3 i {
color: #2a7de1;
}
.report-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
}
.report-item {
display: flex;
flex-direction: column;
}
.report-item label {
color: #4a6fa5;
font-size: 13px;
margin-bottom: 3px;
}
.report-item span {
color: #333;
font-size: 14px;
}
.issues-list {
background: #ffffff;
border-radius: 8px;
border: 1px solid #e1e9f5;
}
.issue-item {
padding: 12px 15px;
border-bottom: 1px solid #e1e9f5;
}
.issue-item:last-child {
border-bottom: none;
}
.issue-title {
font-weight: bold;
color: #1a4b8c;
margin-bottom: 5px;
display: flex;
align-items: center;
}
.issue-title .severity {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
margin-left: 10px;
}
.severity-high {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
}
.severity-medium {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
}
.severity-low {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
}
.issue-desc {
color: #4a6fa5;
font-size: 13px;
margin-bottom: 5px;
}
.issue-action {
color: #6c757d;
font-size: 12px;
font-style: italic;
}
.photo-gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.photo-item {
height: 100px;
background: #f5f9ff;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
}
.photo-item:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.photo-item img {
max-width: 100%;
max-height: 100%;
}
.report-summary {
background: #f5f9ff;
padding: 15px;
border-radius: 8px;
color: #333;
line-height: 1.6;
}
/* 添加审核报告样式 */
.report-info {
background: #f5f9ff;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.info-item {
display: flex;
margin-bottom: 8px;
}
.info-item label {
color: #4a6fa5;
min-width: 80px;
font-weight: 500;
}
.info-item span {
color: #333;
line-height: 30px;
}
/* 添加下载报告样式 */
.report-download-info {
background: #f5f9ff;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.download-options {
padding: 10px;
}
.download-options h3 {
color: #1a4b8c;
font-size: 16px;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.download-options h3 i {
color: #2a7de1;
}
.checkbox-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
</style>
</head>
<body>
<div class="main-container">
<!-- 左侧内容区域 -->
<div class="content-area">
<div class="device-header">
<h1 class="device-title">巡检管理</h1>
<div class="device-actions">
<button class="action-btn" onclick="openPlanModal()">📝 新建计划</button>
<button class="action-btn" onclick="openTaskModal()">📋 派发任务</button>
<button class="action-btn" onclick="exportReport()">📊 导出报表</button>
<button class="action-btn" onclick="location.href='index.html'">
<i class="fas fa-home-alt" style="color: orange;"></i>
回到首页
</button>
</div>
</div>
<div class="device-tabs">
<!-- <button class="tab-btn active" onclick="switchTab('archive')">设备档案</button>
<button class="tab-btn" onclick="switchTab('monitor')">实时监控</button>
<button class="tab-btn" onclick="switchTab('maintenance')">维护保养</button>
<button class="tab-btn" onclick="switchTab('repair')">故障维修</button>
<button class="tab-btn" onclick="switchTab('parts')">备件管理</button>
<button class="tab-btn" onclick="switchTab('disposal')">报废处置</button>
<button class="tab-btn" onclick="switchTab('analysis')">统计分析</button> -->
<button class="tab-btn active" onclick="switchTab('plans')">巡检计划</button>
<button class="tab-btn" onclick="switchTab('tasks')">任务管理</button>
<button class="tab-btn" onclick="switchTab('tracking')">执行跟踪</button>
<button class="tab-btn" onclick="switchTab('reports')">巡检报告</button>
<button class="tab-btn" onclick="switchTab('analysis')">统计分析</button>
</div>
<!-- 设备档案 -->
<div id="plans" class="tab-content active">
<div class="device-card">
<div class="card-title">
<i class="fas fa-calendar-alt"></i>
巡检计划列表
</div>
<table class="device-table">
<thead>
<tr>
<th>计划编号</th>
<th>计划名称</th>
<th>巡检区域</th>
<th>执行周期</th>
<th>负责人</th>
<th>状态</th>
<th>下次执行</th>
<th>操作</th>
</tr>
</thead>
<tbody id="plansTableBody">
<!-- 计划数据将通过JS动态加载 -->
</tbody>
</table>
</div>
</div>
<!-- 实时监控 -->
<div id="tasks" class="tab-content">
<div class="device-card">
<div class="card-title">
<i class="fas fa-tasks"></i>
巡检任务列表
</div>
<table class="device-table">
<thead>
<tr>
<th>任务编号</th>
<th>关联计划</th>
<th>巡检区域</th>
<th>执行人员</th>
<th>计划时间</th>
<th>优先级</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tasksTableBody">
<!-- 任务数据将通过JS动态加载 -->
</tbody>
</table>
</div>
</div>
<!-- 维护保养 -->
<div id="tracking" class="tab-content">
<div class="device-card">
<div class="card-title">
<i class="fas fa-map-marked-alt"></i>
实时执行跟踪
</div>
<div class="form-grid">
<div class="form-group">
<label>选择日期</label>
<input type="date" id="trackingDate" value="2024-01-15">
</div>
<div class="form-group">
<label>巡检区域</label>
<select id="trackingArea">
<option value="all">全部区域</option>
<option value="area1">岳麓区沿岸</option>
<option value="area2">天心区沿岸</option>
<option value="area3">开福区沿岸</option>
</select>
</div>
</div>
<table class="device-table">
<thead>
<tr>
<th>任务编号</th>
<th>执行人员</th>
<th>当前位置</th>
<th>进度</th>
<th>开始时间</th>
<th>预计完成</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="trackingTableBody">
<!-- 跟踪数据将通过JS动态加载 -->
</tbody>
</table>
</div>
</div>
<!-- 故障维修 -->
<div id="reports" class="tab-content">
<div class="device-card">
<div class="card-title">
<i class="fas fa-file-alt"></i>
巡检报告管理
</div>
<table class="device-table">
<thead>
<tr>
<th>报告编号</th>
<th>关联任务</th>
<th>巡检区域</th>
<th>执行人员</th>
<th>完成时间</th>
<th>发现问题</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="reportsTableBody">
<!-- 报告数据将通过JS动态加载 -->
</tbody>
</table>
</div>
</div>
<!-- 备件管理 -->
<div id="analysis" class="tab-content">
<div class="device-card">
<div class="card-title">
<i class="fas fa-chart-bar"></i>
巡检效果分析
</div>
<div class="form-grid">
<div class="form-group">
<label>统计周期</label>
<select id="analysisPeriod" onchange="updateAnalysisChart()">
<option value="week">本周</option>
<option value="month">本月</option>
<option value="quarter">本季度</option>
<option value="year">本年度</option>
</select>
</div>
<div class="form-group">
<label>分析维度</label>
<select id="analysisDimension" onchange="updateAnalysisChart()">
<option value="area">按区域</option>
<option value="type">按问题类型</option>
<option value="personnel">按人员</option>
</select>
</div>
</div>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-value" id="totalPatrols">156</span>
<div class="stat-label">总巡检次数</div>
</div>
<div class="stat-item">
<span class="stat-value" id="completionRate">94.2%</span>
<div class="stat-label">完成率</div>
</div>
<div class="stat-item">
<span class="stat-value" id="issuesFound">23</span>
<div class="stat-label">发现问题</div>
</div>
<div class="stat-item">
<span class="stat-value" id="avgDuration">2.3h</span>
<div class="stat-label">平均时长</div>
</div>
</div>
<div class="chart-container" style="height: 420px;">
<canvas id="analysisChart"></canvas>
</div>
</div>
</div>
</div>
<!-- 右侧侧边栏 -->
<div class="sidebar-area">
<div class="sidebar-section">
<div class="sidebar-title">
<i class="fas fa-tachometer-alt"></i>
今日概览
</div>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-value">8</span>
<div class="stat-label">待执行任务</div>
</div>
<div class="stat-item">
<span class="stat-value">5</span>
<div class="stat-label">执行中任务</div>
</div>
<div class="stat-item">
<span class="stat-value">12</span>
<div class="stat-label">已完成任务</div>
</div>
<div class="stat-item">
<span class="stat-value">2</span>
<div class="stat-label">超时任务</div>
</div>
</div>
</div>
<div class="sidebar-section">
<div class="sidebar-title">
<i class="fas fa-exclamation-triangle"></i>
紧急任务
</div>
<div class="alert-list" id="urgentTasks">
<!-- 告警信息列表 -->
</div>
</div>
<div class="sidebar-section">
<div class="sidebar-title">
<i class="fas fa-chart-pie"></i>
区域分布
</div>
<div class="chart-container">
<canvas id="areaChart"></canvas>
</div>
</div>
<div class="sidebar-section">
<div class="sidebar-title">
<i class="fas fa-clock"></i>
执行效率
</div>
<div class="chart-container">
<canvas id="efficiencyChart"></canvas>
</div>
</div>
</div>
</div>
<!-- 新增设备模态框 -->
<div id="planModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closePlanModal()">&times;</span>
<h2><i class="fas fa-calendar-plus"></i>新建巡检计划</h2>
<form id="planForm">
<div class="form-grid">
<div class="form-group">
<label for="planName"><i class="fas fa-file-alt"></i>计划名称:</label>
<input type="text" id="planName" name="planName" placeholder="请输入巡检计划名称" required>
<small>建议使用描述性名称,如"春季河道安全巡检"</small>
</div>
<div class="form-group">
<label for="planCode"><i class="fas fa-barcode"></i>计划编号:</label>
<input type="text" id="planCode" name="planCode" placeholder="系统自动生成" readonly>
</div>
<div class="form-group">
<label for="planArea"><i class="fas fa-map-marker-alt"></i>巡检区域:</label>
<select id="planArea" name="area" required>
<option value="">请选择区域</option>
<option value="area1">河段A - 上游段桩号0-5km</option>
<option value="area2">河段B - 中游段桩号5-10km</option>
<option value="area3">河段C - 下游段桩号10-15km</option>
<option value="area4">河段D - 入海口段桩号15-20km</option>
<option value="bridge1">大桥周边区域</option>
<option value="dam1">水闸控制区域</option>
</select>
</div>
<div class="form-group">
<label for="planType"><i class="fas fa-tasks"></i>巡检类型:</label>
<select id="planType" name="planType" required>
<option value="">请选择类型</option>
<option value="routine">常规巡检 - 日常安全检查</option>
<option value="special">专项巡检 - 重点问题排查</option>
<option value="emergency">应急巡检 - 突发事件处置</option>
<option value="maintenance">维护巡检 - 设施设备检修</option>
<option value="environmental">环保巡检 - 水质环境监测</option>
</select>
</div>
<div class="form-group">
<label for="planFreq"><i class="fas fa-clock"></i>执行周期:</label>
<select id="planFreq" name="cycle" required>
<option value="">请选择周期</option>
<option value="daily">每日 - 每天执行一次</option>
<option value="weekly">每周 - 每周执行一次</option>
<option value="biweekly">双周 - 每两周执行一次</option>
<option value="monthly">每月 - 每月执行一次</option>
<option value="quarterly">季度 - 每季度执行一次</option>
<option value="custom">自定义 - 按需执行</option>
</select>
</div>
<div class="form-group">
<label for="planPriority"><i class="fas fa-exclamation-triangle"></i>优先级:</label>
<select id="planPriority" name="planPriority" required>
<option value="">请选择优先级</option>
<option value="high">高优先级 - 紧急重要</option>
<option value="medium">中优先级 - 正常处理</option>
<option value="low">低优先级 - 可延后处理</option>
</select>
</div>
<div class="form-group">
<label for="planStartDate"><i class="fas fa-calendar-alt"></i>开始日期:</label>
<input type="date" id="planStartDate" name="startDate" required>
</div>
<div class="form-group">
<label for="planEndDate"><i class="fas fa-calendar-check"></i>结束日期:</label>
<input type="date" id="planEndDate" name="endDate">
<small>留空表示长期有效</small>
</div>
<div class="form-group">
<label for="planExecutor"><i class="fas fa-user"></i>负责人</label>
<select id="planExecutor" name="assignee" required>
<option value="">请选择负责人</option>
<option value="zhang">张三 - 巡检员</option>
<option value="li">李四 - 高级巡检员</option>
<option value="wang">王五 - 技术专员</option>
<option value="zhao">赵六 - 安全员</option>
</select>
</div>
<div class="form-group">
<label for="planRoute"><i class="fas fa-route"></i>巡检路线:</label>
<textarea id="planRoute" name="planRoute" rows="2" placeholder="请描述具体的巡检路线和关键检查点"></textarea>
</div>
</div>
<div class="form-group">
<label for="planDesc"><i class="fas fa-file-text"></i>巡检内容:</label>
<textarea id="planDesc" name="content" rows="4" placeholder="请详细描述巡检的具体内容、要求和注意事项"></textarea>
</div>
<div class="form-group">
<label for="planCheckpoints"><i class="fas fa-map-pin"></i>检查要点:</label>
<textarea id="planCheckpoints" name="checkpoints" rows="3" placeholder="请列出需要重点检查的项目和标准"></textarea>
</div>
<div class="form-group">
<label for="planRemarks"><i class="fas fa-sticky-note"></i>备注:</label>
<textarea id="planRemarks" name="remarks" rows="3" placeholder="其他说明信息"></textarea>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closePlanModal()">取消</button>
<button type="submit" class="btn btn-primary">保存设备</button>
</div>
</form>
</div>
</div>
<!-- 维护计划模态框 -->
<div id="taskModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeTaskModal()">&times;</span>
<h2><i class="fas fa-tasks"></i>派发巡检任务</h2>
<form id="taskForm">
<div class="form-grid">
<div class="form-group">
<label for="taskPlan"><i class="fas fa-clipboard-list"></i>选择计划:</label>
<select id="taskPlan" name="taskPlan" required>
<option value="">请选择计划</option>
<option value="plan1">春季河道安全巡检 - 常规巡检</option>
<option value="plan2">汛期专项检查 - 专项巡检</option>
<option value="plan3">设施维护巡检 - 维护巡检</option>
<option value="plan4">水质环境监测 - 环保巡检</option>
<option value="plan5">应急响应巡检 - 应急巡检</option>
</select>
</div>
<div class="form-group">
<label for="taskCode"><i class="fas fa-barcode"></i>任务编号:</label>
<input type="text" id="taskCode" name="taskCode" placeholder="系统自动生成" readonly>
</div>
<div class="form-group">
<label for="taskExecutor"><i class="fas fa-user"></i>执行人员:</label>
<select id="taskExecutor" name="taskExecutor" required>
<option value="">请选择人员</option>
<option value="user1">张三 - 巡检员 (在岗)</option>
<option value="user2">李四 - 高级巡检员 (在岗)</option>
<option value="user3">王五 - 技术专员 (在岗)</option>
<option value="user4">赵六 - 安全员 (休假)</option>
</select>
</div>
<div class="form-group">
<label for="taskPriority"><i class="fas fa-exclamation-triangle"></i>任务优先级:</label>
<select id="taskPriority" name="taskPriority" required>
<option value="">请选择优先级</option>
<option value="urgent">紧急 - 立即执行</option>
<option value="high">高 - 优先处理</option>
<option value="medium">中 - 正常处理</option>
<option value="low">低 - 可延后处理</option>
</select>
</div>
<div class="form-group">
<label for="taskDate"><i class="fas fa-calendar-alt"></i>执行日期:</label>
<input type="date" id="taskDate" name="taskDate" required>
</div>
<div class="form-group">
<label for="taskTime"><i class="fas fa-clock"></i>执行时间:</label>
<input type="time" id="taskTime" name="taskTime" required>
</div>
<div class="form-group">
<label for="taskDuration"><i class="fas fa-hourglass-half"></i>预计时长:</label>
<select id="taskDuration" name="taskDuration">
<option value="">请选择时长</option>
<option value="30">30分钟</option>
<option value="60">1小时</option>
<option value="120">2小时</option>
<option value="240">4小时</option>
<option value="480">8小时</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="form-group">
<label for="taskArea"><i class="fas fa-map-marker-alt"></i>巡检区域:</label>
<select id="taskArea" name="taskArea" required>
<option value="">请选择区域</option>
<option value="area1">河段A - 上游段桩号0-5km</option>
<option value="area2">河段B - 中游段桩号5-10km</option>
<option value="area3">河段C - 下游段桩号10-15km</option>
<option value="area4">河段D - 入海口段桩号15-20km</option>
<option value="bridge1">大桥周边区域</option>
<option value="dam1">水闸控制区域</option>
</select>
</div>
<div class="form-group">
<label for="taskWeather"><i class="fas fa-cloud-sun"></i>天气条件:</label>
<select id="taskWeather" name="taskWeather">
<option value="">请选择天气</option>
<option value="sunny">晴天 - 适宜巡检</option>
<option value="cloudy">多云 - 适宜巡检</option>
<option value="rainy">雨天 - 注意安全</option>
<option value="windy">大风 - 谨慎执行</option>
<option value="extreme">恶劣天气 - 暂停巡检</option>
</select>
</div>
</div>
<div class="form-group">
<label for="taskNote"><i class="fas fa-file-text"></i>任务说明:</label>
<textarea id="taskNote" name="taskNote" rows="4" placeholder="请详细描述任务要求、注意事项和特殊说明"></textarea>
</div>
<div class="form-group">
<label for="taskCheckpoints"><i class="fas fa-map-pin"></i>检查要点:</label>
<textarea id="taskCheckpoints" name="taskCheckpoints" rows="3"
placeholder="请列出本次任务需要重点检查的项目"></textarea>
</div>
<div class="form-group">
<label for="taskEquipment"><i class="fas fa-tools"></i>所需设备:</label>
<div class="checkbox-group">
<label><input type="checkbox" name="equipment" value="camera"> 相机/摄像设备</label>
<label><input type="checkbox" name="equipment" value="gps"> GPS定位设备</label>
<label><input type="checkbox" name="equipment" value="measure"> 测量工具</label>
<label><input type="checkbox" name="equipment" value="safety"> 安全防护用品</label>
<label><input type="checkbox" name="equipment" value="communication"> 通讯设备</label>
<label><input type="checkbox" name="equipment" value="emergency"> 应急救援包</label>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closeTaskModal()">取消</button>
<button type="submit" class="btn btn-primary">创建计划</button>
</div>
</form>
</div>
</div>
<!-- 在HTML部分新增编辑计划的模态框 -->
<div id="editPlanModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeEditPlanModal()">&times;</span>
<h2><i class="fas fa-edit"></i>编辑巡检计划</h2>
<form id="editPlanForm">
<div class="form-grid">
<div class="form-group">
<label for="editPlanName"><i class="fas fa-file-alt"></i>计划名称:</label>
<input type="text" id="editPlanName" name="editPlanName" placeholder="请输入巡检计划名称" required>
</div>
<div class="form-group">
<label for="editPlanCode"><i class="fas fa-barcode"></i>计划编号:</label>
<input type="text" id="editPlanCode" name="editPlanCode" readonly>
</div>
<div class="form-group">
<label for="editPlanArea"><i class="fas fa-map-marker-alt"></i>巡检区域:</label>
<select id="editPlanArea" name="editPlanArea" required>
<option value="">请选择区域</option>
<option value="area1">河段A - 上游段桩号0-5km</option>
<option value="area2">河段B - 中游段桩号5-10km</option>
<option value="area3">河段C - 下游段桩号10-15km</option>
<option value="area4">河段D - 入海口段桩号15-20km</option>
<option value="bridge1">大桥周边区域</option>
<option value="dam1">水闸控制区域</option>
</select>
</div>
<div class="form-group">
<label for="editPlanType"><i class="fas fa-tasks"></i>巡检类型:</label>
<select id="editPlanType" name="editPlanType" required>
<option value="">请选择类型</option>
<option value="routine">常规巡检 - 日常安全检查</option>
<option value="special">专项巡检 - 重点问题排查</option>
<option value="emergency">应急巡检 - 突发事件处置</option>
<option value="maintenance">维护巡检 - 设施设备检修</option>
<option value="environmental">环保巡检 - 水质环境监测</option>
</select>
</div>
<div class="form-group">
<label for="editPlanFreq"><i class="fas fa-clock"></i>执行周期:</label>
<select id="editPlanFreq" name="editPlanFreq" required>
<option value="">请选择周期</option>
<option value="daily">每日 - 每天执行一次</option>
<option value="weekly">每周 - 每周执行一次</option>
<option value="biweekly">双周 - 每两周执行一次</option>
<option value="monthly">每月 - 每月执行一次</option>
<option value="quarterly">季度 - 每季度执行一次</option>
<option value="custom">自定义 - 按需执行</option>
</select>
</div>
<div class="form-group">
<label for="editPlanPriority"><i class="fas fa-exclamation-triangle"></i>优先级:</label>
<select id="editPlanPriority" name="editPlanPriority" required>
<option value="">请选择优先级</option>
<option value="high">高优先级 - 紧急重要</option>
<option value="medium">中优先级 - 正常处理</option>
<option value="low">低优先级 - 可延后处理</option>
</select>
</div>
<div class="form-group">
<label for="editPlanStartDate"><i class="fas fa-calendar-alt"></i>开始日期:</label>
<input type="date" id="editPlanStartDate" name="editPlanStartDate" required>
</div>
<div class="form-group">
<label for="editPlanEndDate"><i class="fas fa-calendar-check"></i>结束日期:</label>
<input type="date" id="editPlanEndDate" name="editPlanEndDate">
</div>
<div class="form-group">
<label for="editPlanExecutor"><i class="fas fa-user"></i>负责人</label>
<select id="editPlanExecutor" name="editPlanExecutor" required>
<option value="">请选择负责人</option>
<option value="zhang">张三 - 巡检员</option>
<option value="li">李四 - 高级巡检员</option>
<option value="wang">王五 - 技术专员</option>
<option value="zhao">赵六 - 安全员</option>
</select>
</div>
<div class="form-group">
<label for="editPlanRoute"><i class="fas fa-route"></i>巡检路线:</label>
<textarea id="editPlanRoute" name="editPlanRoute" rows="2"
placeholder="请描述具体的巡检路线和关键检查点"></textarea>
</div>
</div>
<div class="form-group">
<label for="editPlanDesc"><i class="fas fa-file-text"></i>巡检内容:</label>
<textarea id="editPlanDesc" name="editPlanDesc" rows="4"
placeholder="请详细描述巡检的具体内容、要求和注意事项"></textarea>
</div>
<div class="form-group">
<label for="editPlanCheckpoints"><i class="fas fa-map-pin"></i>检查要点:</label>
<textarea id="editPlanCheckpoints" name="editPlanCheckpoints" rows="3"
placeholder="请列出需要重点检查的项目和标准"></textarea>
</div>
<div class="form-group">
<label for="editPlanRemarks"><i class="fas fa-sticky-note"></i>备注:</label>
<textarea id="editPlanRemarks" name="editPlanRemarks" rows="3" placeholder="其他说明信息"></textarea>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closeEditPlanModal()">取消</button>
<button type="submit" class="btn btn-primary">保存修改</button>
</div>
</form>
</div>
</div>
<!-- 在HTML部分新增执行计划的模态框 -->
<div id="executePlanModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeExecutePlanModal()">&times;</span>
<h2><i class="fas fa-play-circle"></i>执行巡检计划</h2>
<form id="executePlanForm">
<div class="form-grid">
<div class="form-group">
<label for="executePlanName"><i class="fas fa-file-alt"></i>计划名称:</label>
<input type="text" id="executePlanName" name="executePlanName" readonly>
</div>
<div class="form-group">
<label for="executePlanCode"><i class="fas fa-barcode"></i>计划编号:</label>
<input type="text" id="executePlanCode" name="executePlanCode" readonly>
</div>
<div class="form-group">
<label for="executePlanArea"><i class="fas fa-map-marker-alt"></i>巡检区域:</label>
<input type="text" id="executePlanArea" name="executePlanArea" readonly>
</div>
<div class="form-group">
<label for="executePlanType"><i class="fas fa-tasks"></i>巡检类型:</label>
<input type="text" id="executePlanType" name="executePlanType" readonly>
</div>
<div class="form-group">
<label for="executePlanExecutor"><i class="fas fa-user"></i>负责人:</label>
<input type="text" id="executePlanExecutor" name="executePlanExecutor" readonly>
</div>
<div class="form-group">
<label for="executeTaskDate"><i class="fas fa-calendar-alt"></i>执行日期:</label>
<input type="date" id="executeTaskDate" name="executeTaskDate" required>
</div>
<div class="form-group">
<label for="executeTaskTime"><i class="fas fa-clock"></i>执行时间:</label>
<input type="time" id="executeTaskTime" name="executeTaskTime" required>
</div>
<div class="form-group">
<label for="executeTaskPriority"><i class="fas fa-exclamation-triangle"></i>任务优先级:</label>
<select id="executeTaskPriority" name="executeTaskPriority" required>
<option value="high">高 - 优先处理</option>
<option value="medium" selected>中 - 正常处理</option>
<option value="low">低 - 可延后处理</option>
</select>
</div>
<div class="form-group">
<label for="executeTaskWeather"><i class="fas fa-cloud-sun"></i>天气条件:</label>
<select id="executeTaskWeather" name="executeTaskWeather">
<option value="sunny">晴天 - 适宜巡检</option>
<option value="cloudy">多云 - 适宜巡检</option>
<option value="rainy">雨天 - 注意安全</option>
<option value="windy">大风 - 谨慎执行</option>
<option value="extreme">恶劣天气 - 暂停巡检</option>
</select>
</div>
</div>
<div class="form-group">
<label for="executeTaskNotes"><i class="fas fa-file-text"></i>执行说明:</label>
<textarea id="executeTaskNotes" name="executeTaskNotes" rows="3"
placeholder="请输入本次执行的特别说明或注意事项"></textarea>
</div>
<div class="form-group">
<label><i class="fas fa-tools"></i>所需设备:</label>
<div class="checkbox-group">
<label><input type="checkbox" name="executeEquipment" value="camera" checked> 相机/摄像设备</label>
<label><input type="checkbox" name="executeEquipment" value="gps" checked> GPS定位设备</label>
<label><input type="checkbox" name="executeEquipment" value="measure"> 测量工具</label>
<label><input type="checkbox" name="executeEquipment" value="safety" checked> 安全防护用品</label>
<label><input type="checkbox" name="executeEquipment" value="communication" checked>
通讯设备</label>
<label><input type="checkbox" name="executeEquipment" value="emergency"> 应急救援包</label>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closeExecutePlanModal()">取消</button>
<button type="submit" class="btn btn-success">开始执行</button>
</div>
</form>
</div>
</div>
<!-- 在HTML部分新增查看任务详情的模态框 -->
<div id="viewTaskModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeViewTaskModal()">&times;</span>
<h2><i class="fas fa-info-circle"></i>巡检任务详情</h2>
<div class="task-detail-container">
<div class="detail-section">
<h3><i class="fas fa-clipboard-list"></i> 基本信息</h3>
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">任务编号:</span>
<span class="detail-value" id="viewTaskId"></span>
</div>
<div class="detail-item">
<span class="detail-label">关联计划:</span>
<span class="detail-value" id="viewTaskPlan"></span>
</div>
<div class="detail-item">
<span class="detail-label">巡检区域:</span>
<span class="detail-value" id="viewTaskArea"></span>
</div>
<div class="detail-item">
<span class="detail-label">执行人员:</span>
<span class="detail-value" id="viewTaskExecutor"></span>
</div>
<div class="detail-item">
<span class="detail-label">计划时间:</span>
<span class="detail-value" id="viewTaskScheduledTime"></span>
</div>
<div class="detail-item">
<span class="detail-label">优先级:</span>
<span class="detail-value" id="viewTaskPriority"></span>
</div>
<div class="detail-item">
<span class="detail-label">状态:</span>
<span class="detail-value" id="viewTaskStatus"></span>
</div>
<div class="detail-item">
<span class="detail-label">创建时间:</span>
<span class="detail-value" id="viewTaskCreatedAt"></span>
</div>
</div>
</div>
<div class="detail-section">
<h3><i class="fas fa-map-marked-alt"></i> 巡检路线</h3>
<div class="chart-container" style="height: 200px; margin-bottom: 15px;">
<div id="taskRouteMap"
style="height: 100%; display: flex; align-items: center; justify-content: center; color: #a8c0e0;">
[巡检路线地图预览]
</div>
</div>
<div class="detail-item full-width">
<span class="detail-label">路线描述:</span>
<span class="detail-value" id="viewTaskRouteDesc"></span>
</div>
</div>
<div class="detail-section">
<h3><i class="fas fa-tasks"></i> 检查要点</h3>
<ul class="checkpoint-list" id="viewTaskCheckpoints">
<!-- 检查要点将通过JS动态加载 -->
</ul>
</div>
<div class="detail-section">
<h3><i class="fas fa-sticky-note"></i> 任务备注</h3>
<div class="detail-item full-width">
<span class="detail-value" id="viewTaskNotes"></span>
</div>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button class="btn btn-primary" onclick="printTaskDetail()"><i class="fas fa-print"></i> 打印</button>
<button class="btn" onclick="closeViewTaskModal()">关闭</button>
</div>
</div>
</div>
<!-- 在HTML部分新增开始任务的模态框 -->
<div id="startTaskModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeStartTaskModal()">&times;</span>
<h2><i class="fas fa-play"></i> 开始巡检任务</h2>
<form id="startTaskForm">
<div class="form-grid">
<div class="form-group">
<label for="startTaskId"><i class="fas fa-barcode"></i>任务编号:</label>
<input type="text" id="startTaskId" name="startTaskId" readonly>
</div>
<div class="form-group">
<label for="startTaskPlan"><i class="fas fa-clipboard-list"></i>关联计划:</label>
<input type="text" id="startTaskPlan" name="startTaskPlan" readonly>
</div>
<div class="form-group">
<label for="startTaskArea"><i class="fas fa-map-marker-alt"></i>巡检区域:</label>
<input type="text" id="startTaskArea" name="startTaskArea" readonly>
</div>
<div class="form-group">
<label for="startTaskExecutor"><i class="fas fa-user"></i>执行人员:</label>
<input type="text" id="startTaskExecutor" name="startTaskExecutor" readonly>
</div>
<div class="form-group">
<label for="startTaskActualTime"><i class="fas fa-clock"></i>实际开始时间:</label>
<input type="datetime-local" id="startTaskActualTime" name="startTaskActualTime" required>
</div>
<div class="form-group">
<label for="startTaskWeather"><i class="fas fa-cloud-sun"></i>当前天气:</label>
<select id="startTaskWeather" name="startTaskWeather" required>
<option value="sunny">晴天</option>
<option value="cloudy">多云</option>
<option value="rainy">雨天</option>
<option value="windy">大风</option>
<option value="foggy">雾天</option>
<option value="extreme">恶劣天气</option>
</select>
</div>
</div>
<div class="form-group">
<label><i class="fas fa-tools"></i>携带设备检查:</label>
<div class="checkbox-group"
style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
<label><input type="checkbox" name="equipmentCheck" value="camera" checked> 相机/摄像设备</label>
<label><input type="checkbox" name="equipmentCheck" value="gps" checked> GPS定位设备</label>
<label><input type="checkbox" name="equipmentCheck" value="measure"> 测量工具</label>
<label><input type="checkbox" name="equipmentCheck" value="safety" checked> 安全防护用品</label>
<label><input type="checkbox" name="equipmentCheck" value="communication" checked> 通讯设备</label>
<label><input type="checkbox" name="equipmentCheck" value="emergency"> 应急救援包</label>
</div>
</div>
<div class="form-group">
<label for="startTaskNotes"><i class="fas fa-sticky-note"></i>开始备注:</label>
<textarea id="startTaskNotes" name="startTaskNotes" rows="3"
placeholder="请输入任务开始前的备注信息,如特殊情况、注意事项等"></textarea>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closeStartTaskModal()">取消</button>
<button type="submit" class="btn btn-success">确认开始</button>
</div>
</form>
</div>
</div>
<!-- 在HTML部分新增定位模态框 -->
<div id="trackingModal" class="modal">
<div class="modal-content" style="max-width: 800px;">
<span class="close" onclick="closeTrackingModal()">&times;</span>
<h2><i class="fas fa-map-marker-alt"></i> 巡检人员实时定位</h2>
<div class="form-grid">
<div class="form-group">
<label for="trackingTaskId"><i class="fas fa-barcode"></i>任务编号:</label>
<input type="text" id="trackingTaskId" name="trackingTaskId" readonly>
</div>
<div class="form-group">
<label for="trackingExecutor"><i class="fas fa-user"></i>执行人员:</label>
<input type="text" id="trackingExecutor" name="trackingExecutor" readonly>
</div>
<div class="form-group">
<label for="trackingLocation"><i class="fas fa-location-dot"></i>当前位置:</label>
<input type="text" id="trackingLocation" name="trackingLocation" readonly>
</div>
<div class="form-group">
<label for="trackingTime"><i class="fas fa-clock"></i>更新时间:</label>
<input type="text" id="trackingTime" name="trackingTime" readonly>
</div>
</div>
<div class="chart-container" style="height: 400px; margin: 15px 0;">
<div id="trackingMap" style="height: 100%; width: 100%;"></div>
</div>
<div class="stats-grid" style="margin-bottom: 15px;">
<div class="stat-item">
<span class="stat-value" id="trackingSpeed">0</span>
<div class="stat-label">当前速度(km/h)</div>
</div>
<div class="stat-item">
<span class="stat-value" id="trackingDistance">0</span>
<div class="stat-label">已巡距离(km)</div>
</div>
<div class="stat-item">
<span class="stat-value" id="trackingDuration">0:00</span>
<div class="stat-label">持续时间</div>
</div>
<div class="stat-item">
<span class="stat-value" id="trackingProgress">0%</span>
<div class="stat-label">完成进度</div>
</div>
</div>
<div style="text-align: right;">
<button class="btn btn-primary" onclick="refreshTracking()"><i class="fas fa-sync-alt"></i>
刷新位置</button>
<button class="btn" onclick="closeTrackingModal()">关闭</button>
</div>
</div>
</div>
<!-- 在HTML部分新增路线模态框 -->
<div id="routeModal" class="modal">
<div class="modal-content" style="max-width: 900px;">
<span class="close" onclick="closeRouteModal()">&times;</span>
<h2><i class="fas fa-route"></i> 巡检任务路线规划</h2>
<div class="form-grid">
<div class="form-group">
<label for="routeTaskId"><i class="fas fa-barcode"></i>任务编号:</label>
<input type="text" id="routeTaskId" name="routeTaskId" readonly>
</div>
<div class="form-group">
<label for="routeExecutor"><i class="fas fa-user"></i>执行人员:</label>
<input type="text" id="routeExecutor" name="routeExecutor" readonly>
</div>
<div class="form-group">
<label for="routeArea"><i class="fas fa-map-marker-alt"></i>巡检区域:</label>
<input type="text" id="routeArea" name="routeArea" readonly>
</div>
<div class="form-group">
<label for="routeStatus"><i class="fas fa-info-circle"></i>当前状态:</label>
<input type="text" id="routeStatus" name="routeStatus" readonly>
</div>
</div>
<div style="display: flex; gap: 15px; margin: 15px 0;">
<div class="chart-container" style="flex: 2; height: 400px;">
<div id="routeMap"
style="height: 100%; display: flex; align-items: center; justify-content: center; background-color: #f5f9ff; border-radius: 8px;">
<div style="text-align: center; color: #4a6fa5;">
<i class="fas fa-map" style="font-size: 48px; margin-bottom: 10px;"></i>
<p>巡检路线图加载中...</p>
</div>
</div>
</div>
<div style="flex: 1;">
<div class="sidebar-section" style="height: 100%;">
<div class="sidebar-title">
<i class="fas fa-list-check"></i>
检查点列表
</div>
<div class="checkpoint-container" id="routeCheckpoints"
style="max-height: 350px; overflow-y: auto;">
<!-- 检查点列表将通过JS动态加载 -->
</div>
</div>
</div>
</div>
<div class="stats-grid" style="margin-bottom: 15px;">
<div class="stat-item">
<span class="stat-value" id="routeTotalDistance">0</span>
<div class="stat-label">总距离(km)</div>
</div>
<div class="stat-item">
<span class="stat-value" id="routeCheckpointsCount">0</span>
<div class="stat-label">检查点数量</div>
</div>
<div class="stat-item">
<span class="stat-value" id="routeCompletedPoints">0</span>
<div class="stat-label">已完成</div>
</div>
<div class="stat-item">
<span class="stat-value" id="routeRemainingTime">0:00</span>
<div class="stat-label">预计剩余时间</div>
</div>
</div>
<div style="text-align: right;">
<button class="btn btn-primary" onclick="printRoute()"><i class="fas fa-print"></i> 打印路线</button>
<button class="btn" onclick="closeRouteModal()">关闭</button>
</div>
</div>
</div>
<!-- 在HTML部分新增查看报告详情的模态框 -->
<div id="viewReportModal" class="modal">
<div class="modal-content" style="max-width: 800px;">
<span class="close" onclick="closeViewReportModal()">&times;</span>
<h2><i class="fas fa-file-alt"></i> 巡检报告详情</h2>
<div class="report-header">
<div class="report-title" id="reportTitle">巡检报告标题</div>
<div class="report-meta">
<span id="reportCode"></span>
<span id="reportDate"></span>
<span id="reportStatus" class="status-badge"></span>
</div>
</div>
<div class="report-section">
<h3><i class="fas fa-info-circle"></i> 基本信息</h3>
<div class="report-grid">
<div class="report-item">
<label>关联任务:</label>
<span id="reportTask"></span>
</div>
<div class="report-item">
<label>巡检区域:</label>
<span id="reportArea"></span>
</div>
<div class="report-item">
<label>执行人员:</label>
<span id="reportExecutor"></span>
</div>
<div class="report-item">
<label>开始时间:</label>
<span id="reportStartTime"></span>
</div>
<div class="report-item">
<label>结束时间:</label>
<span id="reportEndTime"></span>
</div>
<div class="report-item">
<label>持续时间:</label>
<span id="reportDuration"></span>
</div>
</div>
</div>
<div class="report-section">
<h3><i class="fas fa-check-circle"></i> 检查结果</h3>
<div class="stats-grid" style="margin-bottom: 15px;">
<div class="stat-item">
<span class="stat-value" id="totalCheckpoints">0</span>
<div class="stat-label">检查点总数</div>
</div>
<div class="stat-item">
<span class="stat-value" id="passedCheckpoints">0</span>
<div class="stat-label">通过</div>
</div>
<div class="stat-item">
<span class="stat-value" id="failedCheckpoints">0</span>
<div class="stat-label">未通过</div>
</div>
<div class="stat-item">
<span class="stat-value" id="completionRate">100%</span>
<div class="stat-label">完成率</div>
</div>
</div>
<div class="issues-list" id="reportIssues">
<!-- 问题列表将通过JS动态加载 -->
</div>
</div>
<div class="report-section">
<h3><i class="fas fa-image"></i> 现场照片</h3>
<div class="photo-gallery" id="reportPhotos">
<!-- 照片将通过JS动态加载 -->
</div>
</div>
<div class="report-section">
<h3><i class="fas fa-comment"></i> 报告总结</h3>
<div class="report-summary" id="reportSummary">
<!-- 总结内容将通过JS动态加载 -->
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button class="btn btn-primary" onclick="printReport()"><i class="fas fa-print"></i> 打印报告</button>
<button class="btn" onclick="closeViewReportModal()">关闭</button>
</div>
</div>
</div>
<!-- 在HTML部分新增审核报告的模态框 -->
<div id="approveReportModal" class="modal">
<div class="modal-content" style="max-width: 600px;">
<span class="close" onclick="closeApproveReportModal()">&times;</span>
<h2><i class="fas fa-clipboard-check"></i> 审核巡检报告</h2>
<div class="report-info">
<div class="info-item">
<label>报告编号:</label>
<span id="approveReportCode"></span>
</div>
<div class="info-item">
<label>关联任务:</label>
<span id="approveReportTask"></span>
</div>
<div class="info-item">
<label>巡检区域:</label>
<span id="approveReportArea"></span>
</div>
<div class="info-item">
<label>提交人员:</label>
<span id="approveReportSubmitter"></span>
</div>
<div class="info-item">
<label>提交时间:</label>
<span id="approveReportSubmitTime"></span>
</div>
</div>
<form id="approveReportForm">
<div class="form-group">
<label for="approveResult"><i class="fas fa-check-circle"></i> 审核结果:</label>
<select id="approveResult" name="approveResult" required>
<option value="">请选择审核结果</option>
<option value="approved">通过审核</option>
<option value="rejected">退回修改</option>
<option value="pending">暂不处理</option>
</select>
</div>
<div class="form-group">
<label for="approveComment"><i class="fas fa-comment-dots"></i> 审核意见:</label>
<textarea id="approveComment" name="approveComment" rows="4"
placeholder="请输入审核意见,如存在问题或改进建议"></textarea>
</div>
<div class="form-group">
<label for="approvePriority"><i class="fas fa-exclamation-triangle"></i> 处理优先级:</label>
<select id="approvePriority" name="approvePriority">
<option value="normal">普通 - 常规处理</option>
<option value="urgent">紧急 - 需优先处理</option>
<option value="critical">重大 - 立即处理</option>
</select>
</div>
<div class="form-group">
<label><i class="fas fa-bell"></i> 通知方式:</label>
<div class="checkbox-group" style="display: flex; gap: 15px;">
<label><input type="checkbox" name="notifyMethod" value="system" checked> 系统通知</label>
<label><input type="checkbox" name="notifyMethod" value="email"> 邮件通知</label>
<label><input type="checkbox" name="notifyMethod" value="sms"> 短信通知</label>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closeApproveReportModal()">取消</button>
<button type="submit" class="btn btn-success">提交审核</button>
</div>
</form>
</div>
</div>
<!-- 在HTML部分新增下载报告的模态框 -->
<div id="downloadReportModal" class="modal">
<div class="modal-content" style="max-width: 500px;">
<span class="close" onclick="closeDownloadReportModal()">&times;</span>
<h2><i class="fas fa-download"></i> 下载巡检报告</h2>
<div class="report-download-info">
<div class="info-item">
<label>报告编号:</label>
<span id="downloadReportCode"></span>
</div>
<div class="info-item">
<label>报告名称:</label>
<span id="downloadReportName"></span>
</div>
<div class="info-item">
<label>生成时间:</label>
<span id="downloadReportTime"></span>
</div>
</div>
<div class="download-options">
<h3><i class="fas fa-file-export"></i> 下载选项</h3>
<div class="form-group">
<label for="downloadFormat"><i class="fas fa-file"></i> 文件格式:</label>
<select id="downloadFormat" name="downloadFormat">
<option value="pdf">PDF文档 (.pdf)</option>
<option value="word">Word文档 (.docx)</option>
<option value="excel">Excel表格 (.xlsx)</option>
<option value="html">网页格式 (.html)</option>
</select>
</div>
<div class="form-group">
<label for="downloadContent"><i class="fas fa-layer-group"></i> 包含内容:</label>
<div class="checkbox-group">
<label><input type="checkbox" name="downloadContent" value="basic" checked> 基本信息</label>
<label><input type="checkbox" name="downloadContent" value="details" checked> 详细检查结果</label>
<label><input type="checkbox" name="downloadContent" value="photos"> 现场照片</label>
<label><input type="checkbox" name="downloadContent" value="attachments"> 相关附件</label>
</div>
</div>
<div class="form-group">
<label for="downloadQuality"><i class="fas fa-image"></i> 图片质量:</label>
<select id="downloadQuality" name="downloadQuality">
<option value="high">高质量 (文件较大)</option>
<option value="medium" selected>中等质量</option>
<option value="low">低质量 (文件较小)</option>
</select>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button type="button" class="btn" onclick="closeDownloadReportModal()">取消</button>
<button type="button" class="btn btn-primary" onclick="confirmDownloadReport()">
<i class="fas fa-download"></i> 开始下载
</button>
</div>
</div>
</div>
<script src="chart.js"></script>
<script>
// 标签页切换
function switchTab(tabName) {
// 隐藏所有标签页内容
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// 移除所有标签按钮的激活状态
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
});
// 显示选中的标签页内容
document.getElementById(tabName).classList.add('active');
// 激活选中的标签按钮
event.target.classList.add('active');
// 根据标签页加载对应数据
switch (tabName) {
case 'plans':
loadPlansData();
break;
case 'tasks':
loadTasksData();
break;
case 'tracking':
loadTrackingData();
break;
case 'reports':
loadReportsData();
break;
case 'analysis':
loadAnalysisData();
break;
}
}
// 全局变量保存计划数据
let plansData = [
{ id: 'P001', name: '岳麓区日常巡检', area: '岳麓区沿岸', cycle: '每日', assignee: '张三', status: 'active', nextExecution: '2024-01-16 09:00' },
{ id: 'P002', name: '天心区周检计划', area: '天心区沿岸', cycle: '每周', assignee: '李四', status: 'active', nextExecution: '2024-01-17 14:00' },
{ id: 'P003', name: '开福区月检计划', area: '开福区沿岸', cycle: '每月', assignee: '王五', status: 'pending', nextExecution: '2024-02-01 10:00' },
{ id: 'P004', name: '雨花区季度检查', area: '雨花区沿岸', cycle: '每季度', assignee: '赵六', status: 'draft', nextExecution: '2024-04-01 09:00' }
];
// 加载巡检计划数据
function loadPlansData() {
const plansTableBody = document.getElementById('plansTableBody');
if (!plansTableBody) return;
const statusMap = {
'active': { text: '执行中', class: 'status-executing' },
'pending': { text: '待执行', class: 'status-pending' },
'draft': { text: '草稿', class: 'status-overdue' }
};
plansTableBody.innerHTML = plansData.map(plan => `
<tr>
<td>${plan.id}</td>
<td>${plan.name}</td>
<td>${plan.area}</td>
<td>${plan.cycle}</td>
<td>${plan.assignee}</td>
<td><span class="status-badge ${statusMap[plan.status].class}">${statusMap[plan.status].text}</span></td>
<td>${plan.nextExecution}</td>
<td>
<button class="btn btn-primary" onclick="editPlan('${plan.id}')">编辑</button>
<button class="btn btn-success" onclick="executePlan('${plan.id}')">执行</button>
<button class="btn btn-danger" onclick="deletePlan('${plan.id}')">删除</button>
</td>
</tr>
`).join('');
}
// 删除计划
function deletePlan(id) {
if (confirm(`确定要删除计划 ${id} 吗?此操作不可恢复!`)) {
// 从数据中删除对应计划
plansData = plansData.filter(plan => plan.id !== id);
// 重新加载计划列表
loadPlansData();
// 显示删除成功提示
alert(`计划 ${id} 已成功删除`);
// 这里可以添加AJAX请求将删除操作同步到服务器
// fetch(`/api/plans/${id}`, { method: 'DELETE' })
// .then(response => {
// if (!response.ok) throw new Error('删除失败');
// loadPlansData();
// alert(`计划 ${id} 已成功删除`);
// })
// .catch(error => {
// console.error('删除失败:', error);
// alert('删除计划失败,请稍后重试');
// });
}
}
// 加载巡检任务数据
function loadTasksData() {
const tasksTableBody = document.getElementById('tasksTableBody');
if (!tasksTableBody) return;
const tasks = [
{ id: 'T001', planName: '岳麓区日常巡检', area: '岳麓区沿岸', executor: '张三', scheduledTime: '2024-01-15 09:00', priority: 'high', status: 'executing' },
{ id: 'T002', planName: '天心区周检计划', area: '天心区沿岸', executor: '李四', scheduledTime: '2024-01-15 14:00', priority: 'medium', status: 'pending' },
{ id: 'T003', planName: '开福区月检计划', area: '开福区沿岸', executor: '王五', scheduledTime: '2024-01-15 10:00', priority: 'low', status: 'completed' },
{ id: 'T004', planName: '雨花区季度检查', area: '雨花区沿岸', executor: '赵六', scheduledTime: '2024-01-14 16:00', priority: 'high', status: 'overdue' }
];
const statusMap = {
'pending': { text: '待执行', class: 'status-pending' },
'executing': { text: '执行中', class: 'status-executing' },
'completed': { text: '已完成', class: 'status-completed' },
'overdue': { text: '已超时', class: 'status-overdue' }
};
const priorityMap = {
'high': { text: '高', class: 'priority-high' },
'medium': { text: '中', class: 'priority-medium' },
'low': { text: '低', class: 'priority-low' }
};
tasksTableBody.innerHTML = tasks.map(task => `
<tr>
<td>${task.id}</td>
<td>${task.planName}</td>
<td>${task.area}</td>
<td>${task.executor}</td>
<td>${task.scheduledTime}</td>
<td><span class="status-badge ${priorityMap[task.priority].class}">${priorityMap[task.priority].text}</span></td>
<td><span class="status-badge ${statusMap[task.status].class}">${statusMap[task.status].text}</span></td>
<td>
<button class="btn btn-primary" onclick="viewTask('${task.id}')">查看</button>
<button class="btn btn-success" onclick="startTask('${task.id}')">开始</button>
<button class="btn btn-danger" onclick="cancelTask('${task.id}')">取消</button>
</td>
</tr>
`).join('');
}
// 加载执行跟踪数据
function loadTrackingData() {
const trackingTableBody = document.getElementById('trackingTableBody');
if (!trackingTableBody) return;
const tracking = [
{ id: 'T001', executor: '张三', location: '橘子洲头', progress: '60%', startTime: '09:00', estimatedEnd: '11:30', status: 'executing' },
{ id: 'T005', executor: '李四', location: '湘江大桥段', progress: '30%', startTime: '14:00', estimatedEnd: '16:00', status: 'executing' },
{ id: 'T006', executor: '王五', location: '银盆岭大桥', progress: '100%', startTime: '10:00', estimatedEnd: '12:00', status: 'completed' }
];
const statusMap = {
'executing': { text: '执行中', class: 'status-executing' },
'completed': { text: '已完成', class: 'status-completed' }
};
trackingTableBody.innerHTML = tracking.map(track => `
<tr>
<td>${track.id}</td>
<td>${track.executor}</td>
<td>${track.location}</td>
<td>${track.progress}</td>
<td>${track.startTime}</td>
<td>${track.estimatedEnd}</td>
<td><span class="status-badge ${statusMap[track.status].class}">${statusMap[track.status].text}</span></td>
<td>
<button class="btn btn-primary" onclick="trackLocation('${track.id}')">定位</button>
<button class="btn" onclick="viewRoute('${track.id}')">路线</button>
</td>
</tr>
`).join('');
}
// 加载巡检报告数据
function loadReportsData() {
const reportsTableBody = document.getElementById('reportsTableBody');
if (!reportsTableBody) return;
const reports = [
{ id: 'R001', taskId: 'T003', area: '开福区沿岸', executor: '王五', completedTime: '2024-01-15 12:00', issues: 2, status: 'submitted' },
{ id: 'R002', taskId: 'T007', area: '岳麓区沿岸', executor: '张三', completedTime: '2024-01-14 11:30', issues: 0, status: 'approved' },
{ id: 'R003', taskId: 'T008', area: '天心区沿岸', executor: '李四', completedTime: '2024-01-14 16:00', issues: 1, status: 'approved' }
];
const statusMap = {
'submitted': { text: '已提交', class: 'status-pending' },
'approved': { text: '已审核', class: 'status-completed' }
};
reportsTableBody.innerHTML = reports.map(report => `
<tr>
<td>${report.id}</td>
<td>${report.taskId}</td>
<td>${report.area}</td>
<td>${report.executor}</td>
<td>${report.completedTime}</td>
<td>${report.issues}</td>
<td><span class="status-badge ${statusMap[report.status].class}">${statusMap[report.status].text}</span></td>
<td>
<button class="btn btn-primary" onclick="viewReport('${report.id}')">查看</button>
<button class="btn btn-success" onclick="approveReport('${report.id}')">审核</button>
<button class="btn" onclick="downloadReport('${report.id}')">下载</button>
</td>
</tr>
`).join('');
}
// 加载统计分析数据
function loadAnalysisData() {
// 更新统计数据
document.getElementById('totalPatrols').textContent = '156';
document.getElementById('completionRate').textContent = '94.2%';
document.getElementById('issuesFound').textContent = '23';
document.getElementById('avgDuration').textContent = '2.3h';
}
// 加载紧急任务
function loadUrgentTasks() {
const urgentTasks = document.getElementById('urgentTasks');
if (!urgentTasks) return;
const tasks = [
{ id: 'T004', title: '雨花区紧急巡检', time: '16:00', status: '已超时' },
{ id: 'T009', title: '开福区设备检查', time: '18:00', status: '即将超时' },
{ id: 'T010', title: '岳麓区水质监测', time: '20:00', status: '待执行' }
];
urgentTasks.innerHTML = tasks.map(task => `
<div class="alert-item">
<div class="alert-title">${task.title}</div>
<div class="alert-info">
<span>${task.time}</span>
<span>${task.status}</span>
</div>
</div>
`).join('');
}
// 模态框操作
function openDeviceModal() {
document.getElementById('deviceModal').style.display = 'block';
}
// 模态框操作
function openPlanModal() {
document.getElementById('planModal').style.display = 'block';
}
function closePlanModal() {
document.getElementById('planModal').style.display = 'none';
}
function openTaskModal() {
document.getElementById('taskModal').style.display = 'block';
}
function closeTaskModal() {
document.getElementById('taskModal').style.display = 'none';
}
// 表单提交
document.getElementById('planForm').addEventListener('submit', function (e) {
e.preventDefault();
alert('巡检计划创建成功!');
closePlanModal();
loadPlansData();
});
document.getElementById('taskForm').addEventListener('submit', function (e) {
e.preventDefault();
alert('巡检任务派发成功!');
closeTaskModal();
loadTasksData();
});
// 打开编辑计划模态框并加载数据
function editPlan(id) {
// 根据ID获取计划数据 - 这里简化处理,实际应从数据源获取
const plan = {
id: id,
name: id === 'P001' ? '岳麓区日常巡检' :
id === 'P002' ? '天心区周检计划' :
id === 'P003' ? '开福区月检计划' : '雨花区季度检查',
area: id === 'P001' ? 'area1' :
id === 'P002' ? 'area2' :
id === 'P003' ? 'area3' : 'area4',
type: 'routine',
cycle: id === 'P001' ? 'daily' :
id === 'P002' ? 'weekly' :
id === 'P003' ? 'monthly' : 'quarterly',
priority: 'medium',
startDate: '2024-01-01',
endDate: id === 'P004' ? '2024-12-31' : '',
executor: id === 'P001' ? 'zhang' :
id === 'P002' ? 'li' :
id === 'P003' ? 'wang' : 'zhao',
route: '从起点开始,沿河岸检查堤防、水质和绿化情况',
desc: '常规巡检,检查河道安全、水质情况和周边环境',
checkpoints: '1. 堤防完整性\n2. 水质清澈度\n3. 周边绿化情况',
remarks: id === 'P001' ? '重点关注堤防安全' :
id === 'P002' ? '注意检查排水口' :
id === 'P003' ? '检查防汛设施' : '全面季度检查'
};
// 填充表单数据
document.getElementById('editPlanCode').value = plan.id;
document.getElementById('editPlanName').value = plan.name;
document.getElementById('editPlanArea').value = plan.area;
document.getElementById('editPlanType').value = plan.type;
document.getElementById('editPlanFreq').value = plan.cycle;
document.getElementById('editPlanPriority').value = plan.priority;
document.getElementById('editPlanStartDate').value = plan.startDate;
document.getElementById('editPlanEndDate').value = plan.endDate;
document.getElementById('editPlanExecutor').value = plan.executor;
document.getElementById('editPlanRoute').value = plan.route;
document.getElementById('editPlanDesc').value = plan.desc;
document.getElementById('editPlanCheckpoints').value = plan.checkpoints;
document.getElementById('editPlanRemarks').value = plan.remarks;
// 显示模态框
document.getElementById('editPlanModal').style.display = 'block';
}
// 关闭编辑计划模态框
function closeEditPlanModal() {
document.getElementById('editPlanModal').style.display = 'none';
}
// 提交编辑计划表单
document.getElementById('editPlanForm').addEventListener('submit', function (e) {
e.preventDefault();
alert('巡检计划修改成功!');
closeEditPlanModal();
loadPlansData(); // 刷新计划列表
});
// 打开执行计划模态框并加载数据
function executePlan(id) {
// 根据ID获取计划数据 - 这里简化处理,实际应从数据源获取
const plan = {
id: id,
name: id === 'P001' ? '岳麓区日常巡检' :
id === 'P002' ? '天心区周检计划' :
id === 'P003' ? '开福区月检计划' : '雨花区季度检查',
area: id === 'P001' ? '岳麓区沿岸' :
id === 'P002' ? '天心区沿岸' :
id === 'P003' ? '开福区沿岸' : '雨花区沿岸',
type: id === 'P001' ? '常规巡检 - 日常安全检查' :
id === 'P002' ? '专项巡检 - 重点问题排查' :
id === 'P003' ? '维护巡检 - 设施设备检修' : '环保巡检 - 水质环境监测',
executor: id === 'P001' ? '张三 - 巡检员' :
id === 'P002' ? '李四 - 高级巡检员' :
id === 'P003' ? '王五 - 技术专员' : '赵六 - 安全员'
};
// 填充表单数据
document.getElementById('executePlanCode').value = plan.id;
document.getElementById('executePlanName').value = plan.name;
document.getElementById('executePlanArea').value = plan.area;
document.getElementById('executePlanType').value = plan.type;
document.getElementById('executePlanExecutor').value = plan.executor;
// 设置默认执行时间为当前时间+30分钟
const now = new Date();
now.setMinutes(now.getMinutes() + 30);
document.getElementById('executeTaskDate').valueAsDate = now;
document.getElementById('executeTaskTime').value = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
// 显示模态框
document.getElementById('executePlanModal').style.display = 'block';
}
// 关闭执行计划模态框
function closeExecutePlanModal() {
document.getElementById('executePlanModal').style.display = 'none';
}
// 提交执行计划表单
document.getElementById('executePlanForm').addEventListener('submit', function (e) {
e.preventDefault();
// 获取表单数据
const planId = document.getElementById('executePlanCode').value;
const taskDate = document.getElementById('executeTaskDate').value;
const taskTime = document.getElementById('executeTaskTime').value;
const priority = document.getElementById('executeTaskPriority').value;
// 这里可以添加AJAX请求将数据发送到服务器
alert(`巡检计划 ${planId} 已开始执行\n执行时间: ${taskDate} ${taskTime}\n优先级: ${priority}`);
closeExecutePlanModal();
loadPlansData(); // 刷新计划列表
loadTasksData(); // 刷新任务列表
});
// 全局变量保存任务数据
let tasksData = [
{
id: 'T001',
planName: '岳麓区日常巡检',
area: '岳麓区沿岸',
executor: '张三',
scheduledTime: '2024-01-15 09:00',
priority: 'high',
status: 'executing',
createdAt: '2024-01-14 08:30',
routeDesc: '从橘子洲头开始,沿湘江右岸向北巡查,重点检查堤防和水质情况',
checkpoints: [
'检查堤防是否有裂缝或沉降',
'观察水质是否清澈,有无异味',
'检查沿岸绿化带是否完好',
'记录河道漂浮物情况'
],
notes: '近期降雨较多,需特别注意堤防安全'
},
{
id: 'T002',
planName: '天心区周检计划',
area: '天心区沿岸',
executor: '李四',
scheduledTime: '2024-01-15 14:00',
priority: 'medium',
status: 'pending',
createdAt: '2024-01-14 10:15',
routeDesc: '从南湖路闸口开始,向南巡查至黑石铺大桥',
checkpoints: [
'检查闸口设备运行情况',
'巡查沿岸排污口',
'检查防汛物资储备点'
],
notes: '需携带水质检测设备'
},
{
id: 'T003',
planName: '开福区月检计划',
area: '开福区沿岸',
executor: '王五',
scheduledTime: '2024-01-15 10:00',
priority: 'low',
status: 'completed',
createdAt: '2024-01-10 09:00',
routeDesc: '全面巡查开福区段河道及周边设施',
checkpoints: [
'检查所有堤防段',
'测试防汛设备',
'全面评估水质',
'检查沿岸设施标识'
],
notes: '本月需完成所有设施的二维码标签更新'
}
];
// 打开查看任务模态框
function viewTask(id) {
// 根据ID获取任务数据
const task = tasksData.find(t => t.id === id);
if (!task) {
alert('未找到该任务信息');
return;
}
// 填充任务详情数据
document.getElementById('viewTaskId').textContent = task.id;
document.getElementById('viewTaskPlan').textContent = task.planName;
document.getElementById('viewTaskArea').textContent = task.area;
document.getElementById('viewTaskExecutor').textContent = task.executor;
document.getElementById('viewTaskScheduledTime').textContent = task.scheduledTime;
document.getElementById('viewTaskCreatedAt').textContent = task.createdAt;
document.getElementById('viewTaskRouteDesc').textContent = task.routeDesc;
document.getElementById('viewTaskNotes').textContent = task.notes || '无';
// 设置优先级
const priorityMap = {
'high': { text: '高', class: 'priority-high' },
'medium': { text: '中', class: 'priority-medium' },
'low': { text: '低', class: 'priority-low' }
};
const priorityElement = document.getElementById('viewTaskPriority');
priorityElement.textContent = priorityMap[task.priority].text;
priorityElement.className = 'detail-value status-badge ' + priorityMap[task.priority].class;
// 设置状态
const statusMap = {
'pending': { text: '待执行', class: 'status-pending' },
'executing': { text: '执行中', class: 'status-executing' },
'completed': { text: '已完成', class: 'status-completed' }
};
const statusElement = document.getElementById('viewTaskStatus');
statusElement.textContent = statusMap[task.status].text;
statusElement.className = 'detail-value status-badge ' + statusMap[task.status].class;
// 加载检查要点
const checkpointsList = document.getElementById('viewTaskCheckpoints');
checkpointsList.innerHTML = task.checkpoints.map(item => `
<li>${item}</li>
`).join('');
// 显示模态框
document.getElementById('viewTaskModal').style.display = 'block';
}
// 关闭查看任务模态框
function closeViewTaskModal() {
document.getElementById('viewTaskModal').style.display = 'none';
}
// 打印任务详情
function printTaskDetail() {
alert('任务详情已发送到打印机');
// 实际应用中可以实现打印功能
// window.print();
}
// 打开开始任务模态框
function startTask(id) {
// 根据ID获取任务数据
const task = tasksData.find(t => t.id === id);
if (!task) {
alert('未找到该任务信息');
return;
}
// 填充任务数据
document.getElementById('startTaskId').value = task.id;
document.getElementById('startTaskPlan').value = task.planName;
document.getElementById('startTaskArea').value = task.area;
document.getElementById('startTaskExecutor').value = task.executor;
// 设置默认开始时间为当前时间
const now = new Date();
const timeString = now.toISOString().slice(0, 16);
document.getElementById('startTaskActualTime').value = timeString;
// 根据当前天气设置默认选项这里简化处理实际应用中可以从天气API获取
const hour = now.getHours();
let defaultWeather = 'sunny';
if (hour > 18 || hour < 6) defaultWeather = 'cloudy';
document.getElementById('startTaskWeather').value = defaultWeather;
// 显示模态框
document.getElementById('startTaskModal').style.display = 'block';
}
// 关闭开始任务模态框
function closeStartTaskModal() {
document.getElementById('startTaskModal').style.display = 'none';
}
// 提交开始任务表单
document.getElementById('startTaskForm').addEventListener('submit', function (e) {
e.preventDefault();
const taskId = document.getElementById('startTaskId').value;
const actualTime = document.getElementById('startTaskActualTime').value;
const weather = document.getElementById('startTaskWeather').value;
const notes = document.getElementById('startTaskNotes').value;
// 检查是否选择了所有必需设备
const requiredEquipment = ['camera', 'gps', 'safety', 'communication'];
const missingEquipment = requiredEquipment.filter(equip =>
!Array.from(document.querySelectorAll('input[name="equipmentCheck"]:checked'))
.some(cb => cb.value === equip)
);
if (missingEquipment.length > 0) {
alert(`请确认已携带以下必需设备: ${missingEquipment.join(', ')}`);
return;
}
// 更新任务状态
const taskIndex = tasksData.findIndex(t => t.id === taskId);
if (taskIndex !== -1) {
tasksData[taskIndex].status = 'executing';
tasksData[taskIndex].actualStartTime = actualTime;
tasksData[taskIndex].weather = weather;
tasksData[taskIndex].startNotes = notes;
// 这里可以添加AJAX请求将数据同步到服务器
// fetch(`/api/tasks/${taskId}/start`, {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({
// actualStartTime: actualTime,
// weather: weather,
// notes: notes
// })
// })
// .then(response => {
// if (!response.ok) throw new Error('开始任务失败');
// loadTasksData();
// alert(`任务 ${taskId} 已开始`);
// })
// .catch(error => {
// console.error('开始任务失败:', error);
// alert('开始任务失败,请稍后重试');
// });
loadTasksData(); // 刷新任务列表
alert(`任务 ${taskId} 已开始`);
}
closeStartTaskModal();
});
function cancelTask(id) {
if (confirm('确定要取消此任务吗?')) {
alert('任务已取消: ' + id);
loadTasksData();
}
}
// 全局变量保存地图和图层
let trackingMap = null;
let trackingVectorLayer = null;
let trackingMarker = null;
let trackingRouteLayer = null;
// 模拟的实时位置数据
const trackingData = {
'T001': {
executor: '张三',
locations: [
{ time: '09:00:00', place: '橘子洲头', coords: [112.9618, 28.1865], speed: 0, distance: 0 },
{ time: '09:15:30', place: '湘江一桥东', coords: [112.9632, 28.1923], speed: 3.2, distance: 0.8 },
{ time: '09:30:45', place: '五一广场段', coords: [112.9765, 28.1987], speed: 2.8, distance: 1.5 },
{ time: '09:45:15', place: '万达广场段', coords: [112.9812, 28.2054], speed: 3.5, distance: 2.3 }
],
progress: '60%',
currentIndex: 2 // 当前定位点索引
},
'T005': {
executor: '李四',
locations: [
{ time: '14:00:00', place: '湘江大桥段', coords: [112.9587, 28.1876], speed: 0, distance: 0 },
{ time: '14:20:15', place: '南湖路口', coords: [112.9534, 28.1823], speed: 2.5, distance: 0.7 },
{ time: '14:35:40', place: '书院路口', coords: [112.9489, 28.1765], speed: 3.0, distance: 1.2 }
],
progress: '30%',
currentIndex: 1
}
};
// 打开定位模态框
function trackLocation(id) {
const task = trackingData[id];
if (!task) {
alert('未找到该任务的定位信息');
return;
}
// 填充基本信息
document.getElementById('trackingTaskId').value = id;
document.getElementById('trackingExecutor').value = task.executor;
// 更新定位信息
updateTrackingInfo(id);
setTimeout(() => {
// 初始化地图
initTrackingMap(id);
}, 100);
// 显示模态框
document.getElementById('trackingModal').style.display = 'block';
//模拟实时更新位置
if (!window.trackingInterval) {
window.trackingInterval = setInterval(() => {
const currentId = document.getElementById('trackingTaskId').value;
if (currentId) updateTrackingInfo(currentId);
}, 5000); // 每5秒更新一次
}
}
var TDT_API_KEY = "a4f4b6000cb9d1bf148bb77452000f30";
// 初始化地图
function initTrackingMap(taskId) {
const task = trackingData[taskId];
if (!task || !task.locations || task.locations.length === 0) return;
// 如果地图已存在,则先移除
if (trackingMap) {
trackingMap.setTarget(undefined);
}
// 创建地图图层
var satelliteLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=" + TDT_API_KEY
}),
visible: true,
name: 'satellite'
});
// var streetLayer = new ol.layer.Tile({
// source: new ol.source.XYZ({
// url: "http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=" + TDT_API_KEY
// }),
// visible: false,
// name: 'street'
// });
// var terrainLayer = new ol.layer.Tile({
// source: new ol.source.XYZ({
// url: "http://t{0-7}.tianditu.com/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=" + TDT_API_KEY
// }),
// visible: false,
// name: 'terrain'
// });
// 创建地图
trackingMap = new ol.Map({
target: 'trackingMap',
layers: [satelliteLayer],
view: new ol.View({
projection: 'EPSG:4326',
center:task.locations[0].coords,
zoom: 12 // 增加初始缩放级别
})
});
// 创建路线图层
trackingRouteLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#2a7de1',
width: 3
})
})
});
trackingMap.addLayer(trackingRouteLayer);
// // 创建标记图层
// trackingVectorLayer = new ol.layer.Vector({
// source: new ol.source.Vector(),
// style: new ol.style.Style({
// image: new ol.style.Icon({
// anchor: [0.5, 1],
// src: '/images/marker-icon-green.png'
// })
// })
// });
// trackingMap.addLayer(trackingVectorLayer);
// 添加矢量图层用于显示标记点
trackingVectorLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
image: new ol.style.Icon({
src: '/images/marker-icon-green.png',
scale: 0.8
})
})
});
trackingMap.addLayer(trackingVectorLayer);
// 更新地图显示
updateMapDisplay(taskId);
}
// 更新定位信息
function updateTrackingInfo(id) {
const task = trackingData[id];
if (!task) return;
const currentLoc = task.locations[task.currentIndex];
// 更新基本信息
document.getElementById('trackingLocation').value = currentLoc.place;
document.getElementById('trackingTime').value = currentLoc.time;
// 更新统计信息
document.getElementById('trackingSpeed').textContent = currentLoc.speed.toFixed(1);
document.getElementById('trackingDistance').textContent = currentLoc.distance.toFixed(1);
document.getElementById('trackingDuration').textContent = calculateDuration(task.locations[0].time, currentLoc.time);
document.getElementById('trackingProgress').textContent = task.progress;
// 更新地图显示
updateMapDisplay(id);
// 模拟移动到下一个点
if (task.currentIndex < task.locations.length - 1) {
task.currentIndex++;
}
}
// 计算持续时间
function calculateDuration(startTime, endTime) {
const [startH, startM, startS] = startTime.split(':').map(Number);
const [endH, endM, endS] = endTime.split(':').map(Number);
let seconds = (endH - startH) * 3600 + (endM - startM) * 60 + (endS - startS);
const hours = Math.floor(seconds / 3600);
seconds %= 3600;
const minutes = Math.floor(seconds / 60);
return `${hours}:${minutes.toString().padStart(2, '0')}`;
}
// 更新地图显示
function updateMapDisplay(taskId) {
const task = trackingData[taskId];
if (!task || !trackingMap) return;
const currentLoc = task.locations[task.currentIndex];
const coordinates = currentLoc.coords;
// 清除之前的标记
trackingVectorLayer.getSource().clear();
// 添加当前位置标记
const marker = new ol.Feature({
geometry: new ol.geom.Point(coordinates)
});
trackingVectorLayer.getSource().addFeature(marker);
// 更新路线
const routeFeatures = [];
const routeCoords = task.locations
.slice(0, task.currentIndex + 1)
.map(loc => loc.coords);
if (routeCoords.length > 1) {
const routeFeature = new ol.Feature({
geometry: new ol.geom.LineString(routeCoords)
});
routeFeatures.push(routeFeature);
}
trackingRouteLayer.getSource().clear();
trackingRouteLayer.getSource().addFeatures(routeFeatures);
// 调整视图中心
trackingMap.getView().animate({
center: coordinates,
duration: 500
});
// 添加弹出窗口显示当前位置信息
const popup = new ol.Overlay({
element: document.createElement('div'),
positioning: 'bottom-center',
stopEvent: false,
offset: [0, -40]
});
popup.getElement().className = 'ol-popup';
popup.getElement().innerHTML = `
<div style="background: white; padding: 5px 10px; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">
<strong>${currentLoc.place}</strong><br>
时间: ${currentLoc.time}<br>
速度: ${currentLoc.speed.toFixed(1)} km/h
</div>
`;
trackingMap.addOverlay(popup);
popup.setPosition(coordinates);
}
// 刷新定位
function refreshTracking() {
const taskId = document.getElementById('trackingTaskId').value;
if (taskId) {
updateTrackingInfo(taskId);
alert('位置信息已刷新');
}
}
// 关闭定位模态框
function closeTrackingModal() {
// 清除定时器
if (window.trackingInterval) {
clearInterval(window.trackingInterval);
window.trackingInterval = null;
}
// 清除地图
if (trackingMap) {
trackingMap.setTarget(undefined);
trackingMap = null;
}
document.getElementById('trackingModal').style.display = 'none';
}
// 模拟的巡检路线数据
const routeData = {
'T001': {
executor: '张三',
area: '岳麓区沿岸',
status: '执行中',
checkpoints: [
{
id: 'CP001', name: '橘子洲头起点', coords: [28.1865, 112.9618], status: 'completed',
desc: '检查堤防起点标志物和初始段状况', completedTime: '09:05'
},
{
id: 'CP002', name: '湘江一桥东侧', coords: [28.1923, 112.9632], status: 'completed',
desc: '检查桥墩周围水流和堤防连接处', completedTime: '09:20'
},
{
id: 'CP003', name: '五一广场段', coords: [28.1987, 112.9765], status: 'current',
desc: '检查商业区段堤防和人流密集区域', completedTime: ''
},
{
id: 'CP004', name: '万达广场段', coords: [28.2054, 112.9812], status: 'pending',
desc: '检查新建堤防段工程质量', completedTime: ''
},
{
id: 'CP005', name: '北辰三角洲', coords: [28.2101, 112.9856], status: 'pending',
desc: '检查三叉河口特殊地形段', completedTime: ''
}
],
totalDistance: '3.5',
currentLocation: [28.1987, 112.9765]
},
'T005': {
executor: '李四',
area: '湘江大桥段',
status: '执行中',
checkpoints: [
{
id: 'CP011', name: '湘江大桥起点', coords: [28.1876, 112.9587], status: 'completed',
desc: '检查大桥南端堤防连接处', completedTime: '14:05'
},
{
id: 'CP012', name: '南湖路口', coords: [28.1823, 112.9534], status: 'current',
desc: '检查排水口和防洪闸门', completedTime: ''
},
{
id: 'CP013', name: '书院路口', coords: [28.1765, 112.9489], status: 'pending',
desc: '检查历史保护堤防段', completedTime: ''
},
{
id: 'CP014', name: '南郊公园段', coords: [28.1702, 112.9431], status: 'pending',
desc: '检查公园段景观堤防', completedTime: ''
}
],
totalDistance: '2.8',
currentLocation: [28.1823, 112.9534]
}
};
// 打开路线模态框
function viewRoute(id) {
const route = routeData[id];
if (!route) {
alert('未找到该任务的路线信息');
return;
}
// 填充基本信息
document.getElementById('routeTaskId').value = id;
document.getElementById('routeExecutor').value = route.executor;
document.getElementById('routeArea').value = route.area;
document.getElementById('routeStatus').value = route.status;
// 更新统计信息
document.getElementById('routeTotalDistance').textContent = route.totalDistance;
document.getElementById('routeCheckpointsCount').textContent = route.checkpoints.length;
const completedCount = route.checkpoints.filter(cp => cp.status === 'completed').length;
document.getElementById('routeCompletedPoints').textContent = completedCount;
document.getElementById('routeRemainingTime').textContent = calculateRemainingTime(route.checkpoints.length, completedCount);
// 加载检查点列表
loadCheckpointsList(id);
// 更新地图显示
updateRouteMap(id);
// 显示模态框
document.getElementById('routeModal').style.display = 'block';
}
// 加载检查点列表
function loadCheckpointsList(id) {
const route = routeData[id];
if (!route) return;
const container = document.getElementById('routeCheckpoints');
container.innerHTML = route.checkpoints.map(cp => `
<div class="checkpoint-item ${cp.status}">
<h4>
<i class="fas fa-${cp.status === 'completed' ? 'check-circle' : cp.status === 'current' ? 'location-dot' : 'map-pin'}"></i>
${cp.name}
<span class="checkpoint-status status-${cp.status}">
${cp.status === 'completed' ? '已完成' : cp.status === 'current' ? '进行中' : '待检查'}
</span>
</h4>
<p>${cp.desc}</p>
${cp.status === 'completed' ? `<p style="color:#28a745;font-size:11px;">完成时间: ${cp.completedTime}</p>` : ''}
</div>
`).join('');
}
// 更新路线地图显示
function updateRouteMap(id) {
const route = routeData[id];
if (!route) return;
const mapElement = document.getElementById('routeMap');
// 模拟地图显示 - 实际应用中应集成地图API
mapElement.innerHTML = `
<div style="width: 100%; height: 100%; background-color: #e1e9f5; border-radius: 8px; position: relative;">
<div style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); text-align: center;">
<i class="fas fa-route" style="font-size: 32px; color: #2a7de1;"></i>
<div style="margin-top: 10px; font-weight: bold;">${route.area}巡检路线</div>
<div style="font-size: 12px; color: #4a6fa5; margin-top: 5px;">
总距离: ${route.totalDistance}km | 检查点: ${route.checkpoints.length}个
</div>
</div>
${route.checkpoints.map((cp, index) => `
<div style="position: absolute;
left: ${20 + index * 15}%;
top: ${50 + Math.sin(index) * 30}%;
transform: translate(-50%, -50%);">
<div style="width: 16px; height: 16px; border-radius: 50%;
background: ${cp.status === 'completed' ? '#28a745' : cp.status === 'current' ? '#2a7de1' : '#ffc107'};
border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.2);"></div>
${cp.status === 'current' ? `
<div style="position: absolute; left: 50%; top: -20px; transform: translateX(-50%);
white-space: nowrap; font-size: 12px; font-weight: bold; color: #1a4b8c;">
${cp.name}
</div>` : ''}
</div>
`).join('')}
</div>
`;
}
// 计算预计剩余时间
function calculateRemainingTime(total, completed) {
const avgTimePerPoint = 15; // 假设每个检查点平均需要15分钟
const remaining = total - completed;
const minutes = remaining * avgTimePerPoint;
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return `${hours}:${mins.toString().padStart(2, '0')}`;
}
// 打印路线
function printRoute() {
alert('路线图已发送到打印机');
// 实际应用中可以实现打印功能
// window.print();
}
// 关闭路线模态框
function closeRouteModal() {
document.getElementById('routeModal').style.display = 'none';
}
// 模拟的巡检报告数据
const reportData = {
'R001': {
title: '岳麓区沿岸常规巡检报告',
code: 'R001-20240115',
date: '2024-01-15 12:30',
status: 'approved',
task: 'T003 - 开福区月检计划',
area: '开福区沿岸',
executor: '王五 - 技术专员',
startTime: '2024-01-15 10:00',
endTime: '2024-01-15 12:00',
duration: '2小时',
checkpoints: {
total: 15,
passed: 13,
failed: 2
},
issues: [
{
title: '堤防裂缝',
severity: 'high',
location: '五一广场段 (桩号K3+250)',
description: '发现长约1.2米的纵向裂缝宽度约2-3cm',
action: '已设置警示标志,需工程部紧急处理',
photo: 'laji1.png'
},
{
title: '排水口堵塞',
severity: 'medium',
location: '万达广场段 (桩号K4+100)',
description: '排水口被杂物部分堵塞,影响排水效率',
action: '已现场清理,建议增加巡查频次',
photo: 'laji1.png'
}
],
photos: [
'laji1.png',
'laji1.png',
'xiangjiang.jpg',
'shuiwei.png',
'shuiwei.png'
],
summary: '本次巡检共完成15个检查点发现2处问题。堤防整体状况良好但五一广场段发现一处需紧急处理的裂缝。建议工程部尽快安排维修并在下周进行复查。其他区域设施运行正常水质情况良好。'
},
'R002': {
title: '天心区沿岸专项检查报告',
code: 'R002-20240114',
date: '2024-01-14 16:45',
status: 'submitted',
task: 'T008 - 天心区沿岸专项检查',
area: '天心区沿岸',
executor: '李四 - 高级巡检员',
startTime: '2024-01-14 14:00',
endTime: '2024-01-14 16:30',
duration: '2.5小时',
checkpoints: {
total: 12,
passed: 12,
failed: 0
},
issues: [],
photos: [
'laji1.png',
'xiangjiang.jpg'
],
summary: '本次专项检查覆盖天心区沿岸全部12个检查点未发现重大问题。所有防汛设施运行正常排水系统畅通。建议继续保持当前维护标准。'
}
};
// 打开查看报告模态框
function viewReport(id) {
const report = reportData[id];
if (!report) {
alert('未找到该报告信息');
return;
}
// 填充报告头部信息
document.getElementById('reportTitle').textContent = report.title;
document.getElementById('reportCode').textContent = report.code;
document.getElementById('reportDate').textContent = report.date;
// 设置报告状态
const statusElement = document.getElementById('reportStatus');
statusElement.textContent = report.status === 'approved' ? '已审核' : '已提交';
statusElement.className = 'status-badge ' + (report.status === 'approved' ? 'status-completed' : 'status-pending');
// 填充基本信息
document.getElementById('reportTask').textContent = report.task;
document.getElementById('reportArea').textContent = report.area;
document.getElementById('reportExecutor').textContent = report.executor;
document.getElementById('reportStartTime').textContent = report.startTime;
document.getElementById('reportEndTime').textContent = report.endTime;
document.getElementById('reportDuration').textContent = report.duration;
// 填充检查结果
document.getElementById('totalCheckpoints').textContent = report.checkpoints.total;
document.getElementById('passedCheckpoints').textContent = report.checkpoints.passed;
document.getElementById('failedCheckpoints').textContent = report.checkpoints.failed;
document.getElementById('completionRate').textContent =
Math.round(report.checkpoints.passed / report.checkpoints.total * 100) + '%';
// 加载问题列表
loadReportIssues(id);
// 加载现场照片
loadReportPhotos(id);
// 加载报告总结
document.getElementById('reportSummary').textContent = report.summary;
// 显示模态框
document.getElementById('viewReportModal').style.display = 'block';
}
// 加载报告问题列表
function loadReportIssues(id) {
const report = reportData[id];
if (!report) return;
const container = document.getElementById('reportIssues');
if (report.issues.length === 0) {
container.innerHTML = `
<div style="padding: 20px; text-align: center; color: #4a6fa5;">
<i class="fas fa-check-circle" style="font-size: 24px; color: #28a745; margin-bottom: 10px;"></i>
<p>本次巡检未发现问题</p>
</div>
`;
return;
}
container.innerHTML = report.issues.map(issue => `
<div class="issue-item">
<div class="issue-title">
${issue.title}
<span class="severity severity-${issue.severity}">
${issue.severity === 'high' ? '严重' : issue.severity === 'medium' ? '中等' : '轻微'}
</span>
</div>
<div class="issue-desc">
<strong>位置:</strong> ${issue.location}<br>
${issue.description}
</div>
<div class="issue-action">
<strong>处理措施:</strong> ${issue.action}
</div>
</div>
`).join('');
}
// 加载报告照片
function loadReportPhotos(id) {
const report = reportData[id];
if (!report) return;
const container = document.getElementById('reportPhotos');
container.innerHTML = report.photos.map(photo => `
<div class="photo-item" onclick="viewPhoto('${photo}')">
<img src="images/${photo}" alt="巡检照片" onerror="this.src='data:image/svg+xml;charset=UTF-8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'100\' height=\'100\' viewBox=\'0 0 100 100\'%3E%3Crect width=\'100\' height=\'100\' fill=\'%23e1e9f5\'/%3E%3Ctext x=\'50%\' y=\'50%\' font-family=\'Arial\' font-size=\'12\' fill=\'%234a6fa5\' text-anchor=\'middle\' dominant-baseline=\'middle\'%3E照片加载失败%3C/text%3E%3C/svg%3E'">
</div>
`).join('');
}
// 查看照片大图
function viewPhoto(photo) {
alert(`查看照片: ${photo}`);
// 实际应用中可以实现照片放大查看功能
}
// 打印报告
function printReport() {
alert('报告已发送到打印机');
// 实际应用中可以实现打印功能
// window.print();
}
// 关闭查看报告模态框
function closeViewReportModal() {
document.getElementById('viewReportModal').style.display = 'none';
}
// 打开审核报告模态框
function approveReport(id) {
const report = reportData[id];
if (!report) {
alert('未找到该报告信息');
return;
}
// 填充报告信息
document.getElementById('approveReportCode').textContent = report.code;
document.getElementById('approveReportTask').textContent = report.task;
document.getElementById('approveReportArea').textContent = report.area;
document.getElementById('approveReportSubmitter').textContent = report.executor;
document.getElementById('approveReportSubmitTime').textContent = report.date;
// 重置表单
document.getElementById('approveResult').value = '';
document.getElementById('approveComment').value = '';
document.getElementById('approvePriority').value = 'normal';
document.querySelectorAll('input[name="notifyMethod"]').forEach(cb => {
cb.checked = cb.value === 'system';
});
// 显示模态框
document.getElementById('approveReportModal').style.display = 'block';
}
// 关闭审核报告模态框
function closeApproveReportModal() {
document.getElementById('approveReportModal').style.display = 'none';
}
// 提交审核报告表单
document.getElementById('approveReportForm').addEventListener('submit', function (e) {
e.preventDefault();
const reportId = document.getElementById('approveReportCode').textContent;
const result = document.getElementById('approveResult').value;
const comment = document.getElementById('approveComment').value;
const priority = document.getElementById('approvePriority').value;
if (!result) {
alert('请选择审核结果');
return;
}
// 更新报告状态
const report = reportData[reportId.substring(0, 4)]; // 提取报告ID前4位作为key
if (report) {
report.status = result;
// 这里可以添加AJAX请求将审核结果提交到服务器
// fetch(`/api/reports/${reportId}/approve`, {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({
// result: result,
// comment: comment,
// priority: priority
// })
// })
// .then(response => {
// if (!response.ok) throw new Error('提交审核失败');
// loadReportsData();
// alert(`报告 ${reportId} 审核结果已提交`);
// })
// .catch(error => {
// console.error('提交审核失败:', error);
// alert('提交审核失败,请稍后重试');
// });
loadReportsData(); // 刷新报告列表
alert(`报告 ${reportId} 审核结果已提交: ${result}`);
}
closeApproveReportModal();
});
// 打开下载报告模态框
function downloadReport(id) {
const report = reportData[id];
if (!report) {
alert('未找到该报告信息');
return;
}
// 填充报告信息
document.getElementById('downloadReportCode').textContent = report.code;
document.getElementById('downloadReportName').textContent = report.title;
document.getElementById('downloadReportTime').textContent = report.date;
// 重置表单默认值
document.getElementById('downloadFormat').value = 'pdf';
document.querySelectorAll('input[name="downloadContent"]').forEach(cb => {
cb.checked = cb.value === 'basic' || cb.value === 'details';
});
document.getElementById('downloadQuality').value = 'medium';
// 显示模态框
document.getElementById('downloadReportModal').style.display = 'block';
}
// 关闭下载报告模态框
function closeDownloadReportModal() {
document.getElementById('downloadReportModal').style.display = 'none';
}
// 确认下载报告
function confirmDownloadReport() {
const reportId = document.getElementById('downloadReportCode').textContent;
const format = document.getElementById('downloadFormat').value;
const quality = document.getElementById('downloadQuality').value;
// 获取选中的内容选项
const contentOptions = Array.from(document.querySelectorAll('input[name="downloadContent"]:checked'))
.map(cb => cb.value);
// 模拟下载过程
alert(`开始下载报告 ${reportId}\n格式: ${format}\n内容: ${contentOptions.join(', ')}\n质量: ${quality}`);
// 实际应用中这里应该是下载文件的代码
// 示例:
// const downloadUrl = `/api/reports/${reportId}/download?format=${format}&content=${contentOptions.join(',')}&quality=${quality}`;
// window.location.href = downloadUrl;
// 模拟延迟后关闭模态框
setTimeout(() => {
closeDownloadReportModal();
alert(`报告 ${reportId} 下载完成`);
}, 1500);
}
function exportReport() {
alert('导出巡检报表');
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function () {
loadPlansData();
loadUrgentTasks();
// 初始化区域分布图表
initAreaChart();
// 初始化执行效率图表
initEfficiencyChart();
// 初始化巡检效果分析图表
initAnalysisChart();
});
// 修改window.onclick函数以支持关闭编辑模态框
window.onclick = function (event) {
const modals = [
'planModal', 'taskModal', 'editPlanModal', 'executePlanModal',
'viewTaskModal', 'startTaskModal', 'trackingModal', 'routeModal',
'viewReportModal', 'approveReportModal', 'downloadReportModal'
];
modals.forEach(modalId => {
const modal = document.getElementById(modalId);
if (event.target === modal) {
if (modalId === 'trackingModal') closeTrackingModal();
else modal.style.display = 'none';
}
});
}
// 区域分布图表(饼图)
function initAreaChart() {
const ctx = document.getElementById('areaChart').getContext('2d');
new Chart(ctx, {
type: 'pie',
data: {
labels: ['岳麓区沿岸', '天心区沿岸', '开福区沿岸', '雨花区沿岸', '大桥周边', '水闸区域'],
datasets: [{
data: [35, 25, 20, 10, 5, 5],
backgroundColor: [
'#2a7de1',
'#28a745',
'#ffc107',
'#dc3545',
'#17a2b8',
'#6c757d'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
boxWidth: 12,
padding: 20,
font: {
size: 12
}
}
},
title: {
display: true,
text: '各区域巡检任务分布',
font: {
size: 14
},
padding: {
bottom: 10
}
}
}
}
});
}
// 执行效率图表(柱状图)
function initEfficiencyChart() {
const ctx = document.getElementById('efficiencyChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['张三', '李四', '王五', '赵六', '钱七'],
datasets: [{
label: '平均完成时间(小时)',
data: [2.1, 1.8, 2.5, 3.2, 2.9],
backgroundColor: '#2a7de1',
borderColor: '#1a4b8c',
borderWidth: 1
}, {
label: '平均延误时间(小时)',
data: [0.3, 0.1, 0.5, 1.2, 0.8],
backgroundColor: '#dc3545',
borderColor: '#a71d2a',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: '时间(小时)'
}
}
},
plugins: {
legend: {
position: 'bottom',
labels: {
boxWidth: 12,
padding: 20,
font: {
size: 12
}
}
},
title: {
display: true,
text: '巡检人员执行效率',
font: {
size: 14
},
padding: {
bottom: 10
}
}
}
}
});
}
// // 巡检效果分析图表(折线图+柱状图组合)
// function initAnalysisChart() {
// const ctx = document.getElementById('analysisChart').getContext('2d');
// new Chart(ctx, {
// type: 'line',
// data: {
// labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
// datasets: [{
// label: '发现问题数量',
// data: [12, 19, 15, 22, 18, 24, 27, 23, 19, 16, 21, 25],
// backgroundColor: 'rgba(220, 53, 69, 0.1)',
// borderColor: '#dc3545',
// borderWidth: 2,
// tension: 0.3,
// yAxisID: 'y'
// }, {
// label: '完成率(%)',
// data: [85, 88, 92, 90, 94, 96, 93, 95, 97, 98, 96, 95],
// backgroundColor: 'rgba(40, 167, 69, 0.1)',
// borderColor: '#28a745',
// borderWidth: 2,
// tension: 0.3,
// yAxisID: 'y1',
// type: 'line'
// }, {
// label: '巡检次数',
// data: [25, 28, 30, 32, 35, 38, 40, 42, 45, 48, 50, 52],
// backgroundColor: 'rgba(42, 125, 225, 0.5)',
// borderColor: '#2a7de1',
// borderWidth: 1,
// yAxisID: 'y',
// type: 'bar'
// }]
// },
// options: {
// responsive: true,
// maintainAspectRatio: false,
// interaction: {
// mode: 'index',
// intersect: false
// },
// scales: {
// y: {
// type: 'linear',
// display: true,
// position: 'left',
// title: {
// display: true,
// text: '数量/次数'
// }
// },
// y1: {
// type: 'linear',
// display: true,
// position: 'right',
// min: 80,
// max: 100,
// grid: {
// drawOnChartArea: false
// },
// title: {
// display: true,
// text: '完成率(%)'
// }
// }
// },
// plugins: {
// legend: {
// position: 'bottom',
// labels: {
// boxWidth: 12,
// padding: 20,
// font: {
// size: 12
// }
// }
// },
// title: {
// display: true,
// text: '年度巡检效果趋势分析',
// font: {
// size: 14
// },
// padding: {
// bottom: 10
// }
// },
// tooltip: {
// callbacks: {
// label: function (context) {
// let label = context.dataset.label || '';
// if (label) {
// label += ': ';
// }
// if (context.datasetIndex === 1) {
// label += context.raw + '%';
// } else {
// label += context.raw;
// }
// return label;
// }
// }
// }
// }
// }
// });
// }
// 全局变量保存图表实例
let analysisChart = null;
// 初始化分析图表
function initAnalysisChart() {
const ctx = document.getElementById('analysisChart').getContext('2d');
analysisChart = new Chart(ctx, getChartConfig('week', 'area'));
}
// 根据选择获取图表配置
function getChartConfig(period, dimension) {
// 这里根据不同的period和dimension返回不同的数据配置
// 以下是示例数据,实际应用中应该从后端获取真实数据
if (dimension === 'area') {
return getAreaChartConfig(period);
} else if (dimension === 'type') {
return getTypeChartConfig(period);
} else if (dimension === 'personnel') {
return getPersonnelChartConfig(period);
}
}
// 区域维度的图表配置
function getAreaChartConfig(period) {
let labels, data1, data2, data3;
// 根据周期设置不同的数据
switch (period) {
case 'week':
labels = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
data1 = [12, 19, 15, 22, 18, 10, 8]; // 发现问题
data2 = [85, 88, 92, 90, 94, 96, 98]; // 完成率
data3 = [25, 28, 30, 32, 35, 20, 15]; // 巡检次数
break;
case 'month':
labels = ['第1周', '第2周', '第3周', '第4周'];
data1 = [45, 52, 48, 55];
data2 = [85, 88, 90, 92];
data3 = [120, 135, 130, 140];
break;
case 'quarter':
labels = ['1月', '2月', '3月'];
data1 = [120, 135, 130];
data2 = [85, 88, 90];
data3 = [450, 480, 470];
break;
case 'year':
labels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
data1 = [12, 19, 15, 22, 18, 24, 27, 23, 19, 16, 21, 25];
data2 = [85, 88, 92, 90, 94, 96, 93, 95, 97, 98, 96, 95];
data3 = [25, 28, 30, 32, 35, 38, 40, 42, 45, 48, 50, 52];
break;
}
return {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '发现问题数量',
data: data1,
backgroundColor: 'rgba(220, 53, 69, 0.1)',
borderColor: '#dc3545',
borderWidth: 2,
tension: 0.3,
yAxisID: 'y'
}, {
label: '完成率(%)',
data: data2,
backgroundColor: 'rgba(40, 167, 69, 0.1)',
borderColor: '#28a745',
borderWidth: 2,
tension: 0.3,
yAxisID: 'y1',
type: 'line'
}, {
label: '巡检次数',
data: data3,
backgroundColor: 'rgba(42, 125, 225, 0.5)',
borderColor: '#2a7de1',
borderWidth: 1,
yAxisID: 'y',
type: 'bar'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: '数量/次数'
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
min: 80,
max: 100,
grid: {
drawOnChartArea: false
},
title: {
display: true,
text: '完成率(%)'
}
}
},
plugins: {
legend: {
position: 'bottom',
labels: {
boxWidth: 12,
padding: 20,
font: {
size: 12
}
}
},
title: {
display: true,
text: '按区域分析 - ' + getPeriodText(period),
font: {
size: 14
},
padding: {
bottom: 10
}
},
tooltip: {
callbacks: {
label: function (context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.datasetIndex === 1) {
label += context.raw + '%';
} else {
label += context.raw;
}
return label;
}
}
}
}
}
};
}
// 问题类型维度的图表配置
function getTypeChartConfig(period) {
// 类似getAreaChartConfig但按问题类型分组
// 这里简化为返回相同结构但不同标题的配置
const config = getAreaChartConfig(period);
config.options.plugins.title.text = '按问题类型分析 - ' + getPeriodText(period);
return config;
}
// 人员维度的图表配置
function getPersonnelChartConfig(period) {
// 类似getAreaChartConfig但按人员分组
// 这里简化为返回相同结构但不同标题的配置
const config = getAreaChartConfig(period);
config.options.plugins.title.text = '按人员分析 - ' + getPeriodText(period);
return config;
}
// 获取周期文本
function getPeriodText(period) {
const texts = {
'week': '本周',
'month': '本月',
'quarter': '本季度',
'year': '本年度'
};
return texts[period] || period;
}
// 更新图表函数
function updateAnalysisChart() {
const period = document.getElementById('analysisPeriod').value;
const dimension = document.getElementById('analysisDimension').value;
// 销毁旧图表
if (analysisChart) {
analysisChart.destroy();
}
// 创建新图表
const ctx = document.getElementById('analysisChart').getContext('2d');
analysisChart = new Chart(ctx, getChartConfig(period, dimension));
}
</script>
</body>
</html>