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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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>