|
|
<!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()">×</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()">×</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()">×</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()">×</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()">×</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()">×</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()">×</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()">×</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()">×</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()">×</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()">×</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> |