|
|
<!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">
|
|
|
<link rel="stylesheet" href="all.min.css">
|
|
|
<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>
|
|
|
<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;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* 设备详情内容样式 */
|
|
|
.device-details-content {
|
|
|
display: grid;
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
gap: 15px;
|
|
|
}
|
|
|
|
|
|
.device-details-content .detail-item {
|
|
|
margin-bottom: 15px;
|
|
|
}
|
|
|
|
|
|
.device-details-content .detail-label {
|
|
|
color: #4a6fa5;
|
|
|
font-size: 14px;
|
|
|
font-weight: 500;
|
|
|
margin-bottom: 5px;
|
|
|
}
|
|
|
|
|
|
.device-details-content .detail-value {
|
|
|
color: #1a4b8c;
|
|
|
font-size: 16px;
|
|
|
padding: 8px;
|
|
|
background: #f5f9ff;
|
|
|
border-radius: 6px;
|
|
|
border: 1px solid #e1e9f5;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* 地图控制按钮样式 */
|
|
|
.map-control-btn {
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
border: 1px solid #d1e0f5;
|
|
|
color: #4a6fa5;
|
|
|
border-radius: 4px;
|
|
|
padding: 6px 10px;
|
|
|
cursor: pointer;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 5px;
|
|
|
font-size: 12px;
|
|
|
transition: all 0.3s;
|
|
|
}
|
|
|
|
|
|
.map-control-btn:hover {
|
|
|
background: rgba(42, 125, 225, 0.1);
|
|
|
color: #1a4b8c;
|
|
|
}
|
|
|
|
|
|
.map-control-btn.active {
|
|
|
background: rgba(42, 125, 225, 0.8);
|
|
|
color: #fff;
|
|
|
border-color: #2a7de1;
|
|
|
}
|
|
|
|
|
|
.map-controls {
|
|
|
position: absolute;
|
|
|
top: 160px;
|
|
|
right: 40px;
|
|
|
z-index: 1000;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 5px;
|
|
|
}
|
|
|
|
|
|
.map-footer {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
gap: 10px;
|
|
|
margin-top: 15px;
|
|
|
padding-top: 15px;
|
|
|
border-top: 1px solid #e1e9f5;
|
|
|
}
|
|
|
|
|
|
/* 统一按钮样式 */
|
|
|
.btn {
|
|
|
padding: 8px 16px;
|
|
|
border: none;
|
|
|
border-radius: 6px;
|
|
|
cursor: pointer;
|
|
|
font-size: 14px;
|
|
|
transition: all 0.3s;
|
|
|
display: inline-flex;
|
|
|
align-items: center;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
|
|
|
/* 采购表单特定样式 */
|
|
|
#orderPartForm input[readonly] {
|
|
|
background-color: #f5f9ff;
|
|
|
color: #4a6fa5;
|
|
|
border: 1px solid #d1e0f5;
|
|
|
}
|
|
|
|
|
|
#orderPartForm input[type="number"] {
|
|
|
background-color: #ffffff;
|
|
|
color: #1a4b8c;
|
|
|
}
|
|
|
|
|
|
#orderTotalPrice {
|
|
|
font-weight: bold;
|
|
|
color: #1a4b8c;
|
|
|
}
|
|
|
|
|
|
/* 审批表单特定样式 */
|
|
|
#approveDisposalForm input[type="radio"] {
|
|
|
margin: 0;
|
|
|
width: 16px;
|
|
|
height: 16px;
|
|
|
}
|
|
|
|
|
|
#approveDisposalForm .detail-value select,
|
|
|
#approveDisposalForm .detail-value textarea {
|
|
|
background: #f5f9ff;
|
|
|
border: 1px solid #d1e0f5;
|
|
|
border-radius: 6px;
|
|
|
padding: 8px;
|
|
|
color: #1a4b8c;
|
|
|
font-size: 14px;
|
|
|
width: 100%;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
#approveDisposalForm .detail-value textarea {
|
|
|
min-height: 80px;
|
|
|
resize: vertical;
|
|
|
}
|
|
|
|
|
|
#approveDisposalForm input:disabled,
|
|
|
#approveDisposalForm select:disabled,
|
|
|
#approveDisposalForm textarea:disabled {
|
|
|
background-color: #f0f0f0;
|
|
|
color: #666;
|
|
|
}
|
|
|
|
|
|
/* 执行维护表单特定样式 */
|
|
|
#executeMaintenanceForm input[type="datetime-local"],
|
|
|
#executeMaintenanceForm select,
|
|
|
#executeMaintenanceForm textarea {
|
|
|
background: #f5f9ff;
|
|
|
border: 1px solid #d1e0f5;
|
|
|
border-radius: 6px;
|
|
|
padding: 8px;
|
|
|
color: #1a4b8c;
|
|
|
font-size: 14px;
|
|
|
width: 100%;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
#executeMaintenanceForm textarea {
|
|
|
min-height: 100px;
|
|
|
resize: vertical;
|
|
|
}
|
|
|
|
|
|
#usedPartsTable {
|
|
|
width: 100%;
|
|
|
border: 1px solid #e1e9f5;
|
|
|
border-radius: 6px;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
#usedPartsTable th {
|
|
|
background-color: #f5f9ff;
|
|
|
color: #1a4b8c;
|
|
|
font-weight: 500;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
|
|
|
#usedPartsTable td {
|
|
|
color: #4a6fa5;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
|
|
|
#photoPreview img {
|
|
|
border: 1px solid #e1e9f5;
|
|
|
transition: all 0.3s;
|
|
|
}
|
|
|
|
|
|
#photoPreview img:hover {
|
|
|
transform: scale(1.05);
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
/* 处理维修表单特定样式 */
|
|
|
#processRepairForm input[type="datetime-local"],
|
|
|
#processRepairForm select,
|
|
|
#processRepairForm textarea {
|
|
|
background: #f5f9ff;
|
|
|
border: 1px solid #d1e0f5;
|
|
|
border-radius: 6px;
|
|
|
padding: 8px;
|
|
|
color: #1a4b8c;
|
|
|
font-size: 14px;
|
|
|
width: 100%;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
#processRepairForm textarea {
|
|
|
min-height: 80px;
|
|
|
resize: vertical;
|
|
|
}
|
|
|
|
|
|
#repairActionsTable,
|
|
|
#repairPartsTable {
|
|
|
width: 100%;
|
|
|
border: 1px solid #e1e9f5;
|
|
|
border-radius: 6px;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
#repairActionsTable th,
|
|
|
#repairPartsTable th {
|
|
|
background-color: #f5f9ff;
|
|
|
color: #1a4b8c;
|
|
|
font-weight: 500;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
|
|
|
#repairActionsTable td,
|
|
|
#repairPartsTable td {
|
|
|
color: #4a6fa5;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
|
|
|
#repairPhotoPreview img {
|
|
|
border: 1px solid #e1e9f5;
|
|
|
transition: all 0.3s;
|
|
|
}
|
|
|
|
|
|
#repairPhotoPreview img:hover {
|
|
|
transform: scale(1.05);
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
</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="openDeviceModal()">📝 新增设备</button>
|
|
|
<button class="action-btn" onclick="openMaintenanceModal()">🔧 维护计划</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;margin-right: 5px;"></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>
|
|
|
</div>
|
|
|
|
|
|
<!-- 设备档案 -->
|
|
|
<div id="archive" class="tab-content active">
|
|
|
<div class="device-card">
|
|
|
<div class="card-title">
|
|
|
<i class="fas fa-archive"></i>
|
|
|
设备档案管理
|
|
|
</div>
|
|
|
<div class="form-grid">
|
|
|
<div class="form-group">
|
|
|
<label>设备类型</label>
|
|
|
<select id="deviceTypeFilter">
|
|
|
<option value="all">全部类型</option>
|
|
|
<option value="ship">打捞无人船</option>
|
|
|
<option value="drone">巡查无人机</option>
|
|
|
<option value="camera">智能摄像头</option>
|
|
|
<option value="truck">密闭转运车</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>设备状态</label>
|
|
|
<select id="deviceStatusFilter">
|
|
|
<option value="all">全部状态</option>
|
|
|
<option value="online">在线</option>
|
|
|
<option value="offline">离线</option>
|
|
|
<option value="maintenance">维护中</option>
|
|
|
<option value="fault">故障</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>所属区域</label>
|
|
|
<select id="deviceAreaFilter">
|
|
|
<option value="all">全部区域</option>
|
|
|
<option value="yuelu">岳麓区沿岸</option>
|
|
|
<option value="tianxin">天心区沿岸</option>
|
|
|
<option value="kaifu">开福区沿岸</option>
|
|
|
<option value="yuhua">雨花区沿岸</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>搜索设备</label>
|
|
|
<input type="text" id="deviceSearch" placeholder="输入设备编号或名称">
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="device-grid" id="deviceArchiveGrid">
|
|
|
<!-- 设备档案卡片将通过JS动态加载 -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 实时监控 -->
|
|
|
<div id="monitor" class="tab-content">
|
|
|
<div class="device-card">
|
|
|
<div class="card-title">
|
|
|
<i class="fas fa-desktop"></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="monitorTableBody">
|
|
|
<!-- 监控数据将通过JS动态加载 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<!-- 分页控件容器 -->
|
|
|
<div id="monitorPagination"
|
|
|
style="display: flex; justify-content: flex-end; align-items: center; gap: 8px; margin: 16px 0 0 0;">
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 维护保养 -->
|
|
|
<div id="maintenance" class="tab-content">
|
|
|
<div class="device-card">
|
|
|
<div class="card-title">
|
|
|
<i class="fas fa-tools"></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="maintenanceTableBody">
|
|
|
<!-- 维护数据将通过JS动态加载 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 故障维修 -->
|
|
|
<div id="repair" class="tab-content">
|
|
|
<div class="device-card">
|
|
|
<div class="card-title">
|
|
|
<i class="fas fa-wrench"></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="repairTableBody">
|
|
|
<!-- 维修数据将通过JS动态加载 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 备件管理 -->
|
|
|
<div id="parts" class="tab-content">
|
|
|
<div class="device-card">
|
|
|
<div class="card-title">
|
|
|
<i class="fas fa-boxes"></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="partsTableBody">
|
|
|
<!-- 备件数据将通过JS动态加载 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 报废处置 -->
|
|
|
<div id="disposal" class="tab-content">
|
|
|
<div class="device-card">
|
|
|
<div class="card-title">
|
|
|
<i class="fas fa-trash-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="disposalTableBody">
|
|
|
<!-- 报废数据将通过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">
|
|
|
<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">
|
|
|
<option value="type">按设备类型</option>
|
|
|
<option value="area">按区域分布</option>
|
|
|
<option value="usage">按使用频率</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="stats-grid">
|
|
|
<div class="stat-item">
|
|
|
<span class="stat-value" id="totalDevices">45</span>
|
|
|
<div class="stat-label">设备总数</div>
|
|
|
</div>
|
|
|
<div class="stat-item">
|
|
|
<span class="stat-value" id="onlineRate">87.2%</span>
|
|
|
<div class="stat-label">在线率</div>
|
|
|
</div>
|
|
|
<div class="stat-item">
|
|
|
<span class="stat-value" id="utilizationRate">73.5%</span>
|
|
|
<div class="stat-label">利用率</div>
|
|
|
</div>
|
|
|
<div class="stat-item">
|
|
|
<span class="stat-value" id="faultRate">2.3%</span>
|
|
|
<div class="stat-label">故障率</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="chart-container" style="height: 400px;">
|
|
|
<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">39</span>
|
|
|
<div class="stat-label">在线设备</div>
|
|
|
</div>
|
|
|
<div class="stat-item">
|
|
|
<span class="stat-value">6</span>
|
|
|
<div class="stat-label">离线设备</div>
|
|
|
</div>
|
|
|
<div class="stat-item">
|
|
|
<span class="stat-value">3</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="deviceAlerts">
|
|
|
<!-- 告警信息列表 -->
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="sidebar-section">
|
|
|
<div class="sidebar-title">
|
|
|
<i class="fas fa-chart-pie"></i>
|
|
|
设备分布
|
|
|
</div>
|
|
|
<div class="chart-container">
|
|
|
<canvas id="distributionChart"></canvas>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="sidebar-section">
|
|
|
<div class="sidebar-title">
|
|
|
<i class="fas fa-clock"></i>
|
|
|
维护提醒
|
|
|
</div>
|
|
|
<div class="alert-list" id="maintenanceReminders">
|
|
|
<!-- 维护提醒列表 -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 新增设备模态框 -->
|
|
|
<div id="deviceModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeDeviceModal()">×</span>
|
|
|
<h2>新增设备档案</h2>
|
|
|
<form id="deviceForm">
|
|
|
<div class="form-grid">
|
|
|
<div class="form-group">
|
|
|
<label>设备编号</label>
|
|
|
<input type="text" name="deviceId" placeholder="如:UC-2025001" required>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>设备名称</label>
|
|
|
<input type="text" name="deviceName" placeholder="如:垃圾打捞无人船A01" required>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>设备类型</label>
|
|
|
<select name="deviceType" required>
|
|
|
<option value="">请选择类型</option>
|
|
|
<option value="ship">打捞无人船</option>
|
|
|
<option value="drone">巡查无人机</option>
|
|
|
<option value="camera">智能摄像头</option>
|
|
|
<option value="truck">密闭转运车</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>型号规格</label>
|
|
|
<input type="text" name="model" placeholder="设备型号">
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>生产厂商</label>
|
|
|
<input type="text" name="manufacturer" placeholder="厂商名称">
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>采购日期</label>
|
|
|
<input type="date" name="purchaseDate" required>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>所属区域</label>
|
|
|
<select name="area" required>
|
|
|
<option value="">请选择区域</option>
|
|
|
<option value="yuelu">岳麓区沿岸</option>
|
|
|
<option value="tianxin">天心区沿岸</option>
|
|
|
<option value="kaifu">开福区沿岸</option>
|
|
|
<option value="yuhua">雨花区沿岸</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>管理责任人</label>
|
|
|
<select name="manager" required>
|
|
|
<option value="">请选择责任人</option>
|
|
|
<option value="zhang">张三</option>
|
|
|
<option value="li">李四</option>
|
|
|
<option value="wang">王五</option>
|
|
|
<option value="zhao">赵六</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>技术参数</label>
|
|
|
<textarea name="specifications" rows="4" placeholder="请输入设备的技术参数和规格"></textarea>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>备注信息</label>
|
|
|
<textarea name="remarks" rows="3" placeholder="其他说明信息"></textarea>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeDeviceModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">保存设备</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 维护计划模态框 -->
|
|
|
<div id="maintenanceModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeMaintenanceModal()">×</span>
|
|
|
<h2>制定维护计划</h2>
|
|
|
<form id="maintenanceForm">
|
|
|
<div class="form-grid">
|
|
|
<div class="form-group">
|
|
|
<label>选择设备</label>
|
|
|
<select name="deviceId" required>
|
|
|
<option value="">请选择设备</option>
|
|
|
<option value="UC-2025001">UC-2025001 - 垃圾打捞无人船A01</option>
|
|
|
<option value="UD-2025001">UD-2025001 - 巡查无人机B01</option>
|
|
|
<option value="IC-2025001">IC-2025001 - 智能摄像头C01</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>保养类型</label>
|
|
|
<select name="maintenanceType" required>
|
|
|
<option value="">请选择类型</option>
|
|
|
<option value="routine">例行保养</option>
|
|
|
<option value="periodic">周期保养</option>
|
|
|
<option value="condition">条件保养</option>
|
|
|
<option value="emergency">紧急保养</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>计划时间</label>
|
|
|
<input type="datetime-local" name="scheduledTime" required>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>负责人</label>
|
|
|
<select 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>
|
|
|
<div class="form-group">
|
|
|
<label>保养内容</label>
|
|
|
<textarea name="content" rows="4" placeholder="请描述具体的保养项目和要求"></textarea>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>所需备件</label>
|
|
|
<textarea name="parts" rows="3" placeholder="列出所需的备件和耗材"></textarea>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeMaintenanceModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">创建计划</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 查看设备模态框 -->
|
|
|
<div id="viewDeviceModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeViewDeviceModal()">×</span>
|
|
|
<h2>设备详情</h2>
|
|
|
<div id="deviceDetailsContent" class="device-details-content">
|
|
|
<!-- 内容将通过JS动态加载 -->
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeViewDeviceModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 编辑设备模态框 -->
|
|
|
<div id="editDeviceModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeEditDeviceModal()">×</span>
|
|
|
<h2>编辑设备</h2>
|
|
|
<form id="editDeviceForm">
|
|
|
<input type="hidden" id="editDeviceId" name="deviceId">
|
|
|
<div class="form-grid">
|
|
|
<div class="form-group">
|
|
|
<label>设备名称</label>
|
|
|
<input type="text" id="editDeviceName" name="deviceName" required>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>设备类型</label>
|
|
|
<select id="editDeviceType" name="deviceType" required>
|
|
|
<option value="ship">打捞无人船</option>
|
|
|
<option value="drone">巡查无人机</option>
|
|
|
<option value="camera">智能摄像头</option>
|
|
|
<option value="truck">密闭转运车</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>所属区域</label>
|
|
|
<select id="editDeviceArea" name="area" required>
|
|
|
<option value="yuelu">岳麓区沿岸</option>
|
|
|
<option value="tianxin">天心区沿岸</option>
|
|
|
<option value="kaifu">开福区沿岸</option>
|
|
|
<option value="yuhua">雨花区沿岸</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>管理责任人</label>
|
|
|
<select id="editDeviceManager" name="manager" required>
|
|
|
<option value="zhang">张三</option>
|
|
|
<option value="li">李四</option>
|
|
|
<option value="wang">王五</option>
|
|
|
<option value="zhao">赵六</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>设备状态</label>
|
|
|
<select id="editDeviceStatus" name="status" required>
|
|
|
<option value="online">在线</option>
|
|
|
<option value="offline">离线</option>
|
|
|
<option value="maintenance">维护中</option>
|
|
|
<option value="fault">故障</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeEditDeviceModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">保存更改</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 设备定位模态框 -->
|
|
|
<div id="locationModal" class="modal">
|
|
|
<div class="modal-content" style="width: 90%; max-width: 1000px;position: relative;">
|
|
|
<span class="close" onclick="closeLocationModal()">×</span>
|
|
|
<h2><i class="fas fa-map-marker-alt"></i> 设备实时位置</h2>
|
|
|
|
|
|
<div class="map-header">
|
|
|
<h4>设备位置:
|
|
|
<span id="deviceLocationTitle"></span>
|
|
|
<span style="float: right;">坐标: <span id="deviceCoordinates">112.955, 28.195</span>
|
|
|
</span>
|
|
|
</h4>
|
|
|
<div class="map-controls">
|
|
|
<button class="map-control-btn active" data-view="satellite"
|
|
|
onclick="switchDeviceMapType('satellite')">卫星图</button>
|
|
|
<button class="map-control-btn" data-view="street"
|
|
|
onclick="switchDeviceMapType('street')">街道图</button>
|
|
|
<button class="map-control-btn" data-view="terrain"
|
|
|
onclick="switchDeviceMapType('terrain')">地形图</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="deviceMap" class="tracking-map" style="height: 500px;"></div>
|
|
|
|
|
|
<div class="map-footer">
|
|
|
<!-- <div class="coordinates-display">
|
|
|
坐标: <span id="deviceCoordinates">112.955, 28.195</span>
|
|
|
</div> -->
|
|
|
<button class="btn" onclick="closeLocationModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 查看设备详情模态框 -->
|
|
|
<div id="viewDetailsModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeViewDetailsModal()">×</span>
|
|
|
<h2>设备详情</h2>
|
|
|
<div id="deviceDetailsContents" class="device-details-content">
|
|
|
<!-- 内容将通过JS动态加载 -->
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeViewDetailsModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- 查看维护计划详情模态框 -->
|
|
|
<div id="viewMaintenanceModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeViewMaintenanceModal()">×</span>
|
|
|
<h2>维护保养计划详情</h2>
|
|
|
<div id="maintenanceDetailsContent" class="device-details-content">
|
|
|
<!-- 内容将通过JS动态加载 -->
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeViewMaintenanceModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 执行维护计划模态框 -->
|
|
|
<div id="executeMaintenanceModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeExecuteMaintenanceModal()">×</span>
|
|
|
<h2>执行维护保养</h2>
|
|
|
<form id="executeMaintenanceForm">
|
|
|
<input type="hidden" id="executePlanId" name="planId">
|
|
|
<div class="device-details-content">
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">计划编号</div>
|
|
|
<div class="detail-value" id="executePlanNo"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value" id="executeDeviceName"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">保养类型</div>
|
|
|
<div class="detail-value" id="executeMaintenanceType"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">计划时间</div>
|
|
|
<div class="detail-value" id="executeScheduledTime"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">负责人</div>
|
|
|
<div class="detail-value" id="executeAssignee"></div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">实际执行时间</div>
|
|
|
<div class="detail-value">
|
|
|
<input type="datetime-local" id="executeActualTime" name="actualTime" required
|
|
|
style="width: 100%;">
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">执行人员</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="executeOperator" name="operator" required style="width: 100%;">
|
|
|
<option value="">请选择执行人员</option>
|
|
|
<option value="tech1">技术员张三</option>
|
|
|
<option value="tech2">技术员李四</option>
|
|
|
<option value="tech3">技术员王五</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">使用备件</div>
|
|
|
<div class="detail-value">
|
|
|
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
|
<select id="executePartSelect" style="flex: 1;">
|
|
|
<option value="">选择备件</option>
|
|
|
<option value="P001">P001 - 无人船专用电池</option>
|
|
|
<option value="P002">P002 - 无人机螺旋桨</option>
|
|
|
<option value="P003">P003 - 摄像头镜头</option>
|
|
|
<option value="P004">P004 - 转运车轮胎</option>
|
|
|
</select>
|
|
|
<input type="number" id="executePartQuantity" min="1" value="1" style="width: 80px;">
|
|
|
<button type="button" class="btn btn-primary" onclick="addUsedPart()"
|
|
|
style="width: 80px;">添加</button>
|
|
|
</div>
|
|
|
<table id="usedPartsTable"
|
|
|
style="width: 100%; border-collapse: collapse; margin-top: 10px;">
|
|
|
<thead>
|
|
|
<tr style="background-color: #f5f9ff;">
|
|
|
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #e1e9f5;">
|
|
|
备件编号</th>
|
|
|
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #e1e9f5;">
|
|
|
备件名称</th>
|
|
|
<th style="padding: 8px; text-align: right; border-bottom: 1px solid #e1e9f5;">
|
|
|
数量</th>
|
|
|
<th style="padding: 8px; text-align: center; border-bottom: 1px solid #e1e9f5;">
|
|
|
操作</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody id="usedPartsList">
|
|
|
<!-- 动态添加使用的备件 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">执行情况</div>
|
|
|
<div class="detail-value">
|
|
|
<textarea id="executeResult" name="result" rows="4" style="width: 100%;"
|
|
|
placeholder="请描述维护保养的具体执行情况和发现的问题" required></textarea>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">设备状态</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="executeDeviceStatus" name="deviceStatus" required style="width: 100%;">
|
|
|
<option value="">请选择维护后设备状态</option>
|
|
|
<option value="online">正常在线</option>
|
|
|
<option value="maintenance">需进一步维护</option>
|
|
|
<option value="fault">发现新故障</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">上传照片</div>
|
|
|
<div class="detail-value">
|
|
|
<input type="file" id="executePhotos" name="photos" accept="image/*" multiple
|
|
|
style="width: 100%;">
|
|
|
<div id="photoPreview" style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeExecuteMaintenanceModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">提交执行结果</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 查看故障维修详情模态框 -->
|
|
|
<div id="viewRepairModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeViewRepairModal()">×</span>
|
|
|
<h2>故障维修详情</h2>
|
|
|
<div id="repairDetailsContent" class="device-details-content">
|
|
|
<!-- 内容将通过JS动态加载 -->
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeViewRepairModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 处理故障维修模态框 -->
|
|
|
<div id="processRepairModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeProcessRepairModal()">×</span>
|
|
|
<h2>故障维修处理</h2>
|
|
|
<form id="processRepairForm">
|
|
|
<input type="hidden" id="processRepairId" name="repairId">
|
|
|
<div class="device-details-content">
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">维修单号</div>
|
|
|
<div class="detail-value" id="processRepairNo"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value" id="processDeviceName"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">故障描述</div>
|
|
|
<div class="detail-value" id="processFaultDesc"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">报修人</div>
|
|
|
<div class="detail-value" id="processReporter"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">紧急程度</div>
|
|
|
<div class="detail-value" id="processPriority"></div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">维修人员</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="repairTechnician" name="technician" required style="width: 100%;">
|
|
|
<option value="">请选择维修人员</option>
|
|
|
<option value="tech1">高级技师-张三</option>
|
|
|
<option value="tech2">技师-李四</option>
|
|
|
<option value="tech3">技术员-王五</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">开始时间</div>
|
|
|
<div class="detail-value">
|
|
|
<input type="datetime-local" id="repairStartTime" name="startTime" required
|
|
|
style="width: 100%;">
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">故障诊断</div>
|
|
|
<div class="detail-value">
|
|
|
<textarea id="repairDiagnosis" name="diagnosis" rows="3" style="width: 100%;"
|
|
|
placeholder="请描述故障诊断过程和发现的根本原因" required></textarea>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">维修措施</div>
|
|
|
<div class="detail-value">
|
|
|
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
|
<select id="repairActionSelect" style="flex: 1;">
|
|
|
<option value="">选择维修措施</option>
|
|
|
<option value="replace">更换部件</option>
|
|
|
<option value="repair">修复部件</option>
|
|
|
<option value="adjust">调整参数</option>
|
|
|
<option value="clean">清洁维护</option>
|
|
|
<option value="software">软件修复</option>
|
|
|
</select>
|
|
|
<button type="button" class="btn btn-primary" onclick="addRepairAction()"
|
|
|
style="width: 80px;">添加</button>
|
|
|
</div>
|
|
|
<table id="repairActionsTable"
|
|
|
style="width: 100%; border-collapse: collapse; margin-top: 10px;">
|
|
|
<thead>
|
|
|
<tr style="background-color: #f5f9ff;">
|
|
|
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #e1e9f5;">
|
|
|
措施类型</th>
|
|
|
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #e1e9f5;">
|
|
|
详细说明</th>
|
|
|
<th style="padding: 8px; text-align: center; border-bottom: 1px solid #e1e9f5;">
|
|
|
操作</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody id="repairActionsList">
|
|
|
<!-- 动态添加维修措施 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">使用备件</div>
|
|
|
<div class="detail-value">
|
|
|
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
|
<select id="repairPartSelect" style="flex: 1;">
|
|
|
<option value="">选择备件</option>
|
|
|
<option value="P001">P001 - 无人船专用电池</option>
|
|
|
<option value="P002">P002 - 无人机螺旋桨</option>
|
|
|
<option value="P003">P003 - 摄像头镜头</option>
|
|
|
<option value="P004">P004 - 转运车轮胎</option>
|
|
|
</select>
|
|
|
<input type="number" id="repairPartQuantity" min="1" value="1" style="width: 80px;">
|
|
|
<button type="button" class="btn btn-primary" onclick="addRepairPart()"
|
|
|
style="width: 80px;">添加</button>
|
|
|
</div>
|
|
|
<table id="repairPartsTable"
|
|
|
style="width: 100%; border-collapse: collapse; margin-top: 10px;">
|
|
|
<thead>
|
|
|
<tr style="background-color: #f5f9ff;">
|
|
|
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #e1e9f5;">
|
|
|
备件编号</th>
|
|
|
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #e1e9f5;">
|
|
|
备件名称</th>
|
|
|
<th style="padding: 8px; text-align: right; border-bottom: 1px solid #e1e9f5;">
|
|
|
数量</th>
|
|
|
<th style="padding: 8px; text-align: center; border-bottom: 1px solid #e1e9f5;">
|
|
|
操作</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody id="repairPartsList">
|
|
|
<!-- 动态添加使用的备件 -->
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">维修结果</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="repairResult" name="result" required style="width: 100%;">
|
|
|
<option value="">请选择维修结果</option>
|
|
|
<option value="fixed">已修复</option>
|
|
|
<option value="partial">部分修复</option>
|
|
|
<option value="unfixed">未修复</option>
|
|
|
<option value="unrepairable">无法修复</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">维修说明</div>
|
|
|
<div class="detail-value">
|
|
|
<textarea id="repairNotes" name="notes" rows="4" style="width: 100%;"
|
|
|
placeholder="请详细描述维修过程和最终结果" required></textarea>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">设备状态</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="repairDeviceStatus" name="deviceStatus" required style="width: 100%;">
|
|
|
<option value="">请选择维修后设备状态</option>
|
|
|
<option value="online">正常在线</option>
|
|
|
<option value="limited">功能受限</option>
|
|
|
<option value="maintenance">需进一步维护</option>
|
|
|
<option value="scrapped">建议报废</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">上传照片</div>
|
|
|
<div class="detail-value">
|
|
|
<input type="file" id="repairPhotos" name="photos" accept="image/*" multiple
|
|
|
style="width: 100%;">
|
|
|
<div id="repairPhotoPreview"
|
|
|
style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeProcessRepairModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">提交处理结果</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- 查看备件详情模态框 -->
|
|
|
<div id="viewPartModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeViewPartModal()">×</span>
|
|
|
<h2>备件详情</h2>
|
|
|
<div id="partDetailsContent" class="device-details-content">
|
|
|
<!-- 内容将通过JS动态加载 -->
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeViewPartModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- 采购备件模态框 -->
|
|
|
<div id="orderPartModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeOrderPartModal()">×</span>
|
|
|
<h2>备件采购</h2>
|
|
|
<form id="orderPartForm">
|
|
|
<input type="hidden" id="orderPartId" name="partId">
|
|
|
<div class="form-grid">
|
|
|
<div class="form-group">
|
|
|
<label>备件名称</label>
|
|
|
<input type="text" id="orderPartName" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>适用设备</label>
|
|
|
<input type="text" id="orderPartDevice" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>当前库存</label>
|
|
|
<input type="text" id="orderCurrentStock" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>最低库存</label>
|
|
|
<input type="text" id="orderMinStock" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>采购数量</label>
|
|
|
<input type="number" id="orderQuantity" name="quantity" min="1" value="1" required>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>单价</label>
|
|
|
<input type="text" id="orderUnitPrice" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>总价</label>
|
|
|
<input type="text" id="orderTotalPrice" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>供应商</label>
|
|
|
<input type="text" id="orderSupplier" readonly>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>采购人</label>
|
|
|
<select id="orderPurchaser" name="purchaser" required>
|
|
|
<option value="">请选择采购人</option>
|
|
|
<option value="zhang">张三</option>
|
|
|
<option value="li">李四</option>
|
|
|
<option value="wang">王五</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>预计到货时间</label>
|
|
|
<input type="date" id="orderExpectedDate" name="expectedDate" required>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="form-group">
|
|
|
<label>采购备注</label>
|
|
|
<textarea id="orderNotes" name="notes" rows="3" placeholder="请输入采购备注信息"></textarea>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeOrderPartModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">提交采购</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- 查看报废设备详情模态框 -->
|
|
|
<div id="viewDisposalModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeViewDisposalModal()">×</span>
|
|
|
<h2>报废处置详情</h2>
|
|
|
<div id="disposalDetailsContent" class="device-details-content">
|
|
|
<!-- 内容将通过JS动态加载 -->
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeViewDisposalModal()">关闭</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 审批报废设备模态框 -->
|
|
|
<div id="approveDisposalModal" class="modal">
|
|
|
<div class="modal-content">
|
|
|
<span class="close" onclick="closeApproveDisposalModal()">×</span>
|
|
|
<h2>报废处置审批</h2>
|
|
|
<form id="approveDisposalForm">
|
|
|
<input type="hidden" id="approveDisposalId" name="disposalId">
|
|
|
<div class="device-details-content">
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">申请编号</div>
|
|
|
<div class="detail-value" id="approveAppId"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value" id="approveDeviceName"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">报废原因</div>
|
|
|
<div class="detail-value" id="approveReason"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">申请人</div>
|
|
|
<div class="detail-value" id="approveApplicant"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">申请时间</div>
|
|
|
<div class="detail-value" id="approveApplyTime"></div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">技术评估</div>
|
|
|
<div class="detail-value" id="approveEvaluation"></div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">审批结果</div>
|
|
|
<div class="detail-value">
|
|
|
<div style="display: flex; gap: 15px;">
|
|
|
<label style="display: flex; align-items: center;">
|
|
|
<input type="radio" name="approvalResult" value="approved" required>
|
|
|
<span style="margin-left: 5px;">同意报废</span>
|
|
|
</label>
|
|
|
<label style="display: flex; align-items: center;">
|
|
|
<input type="radio" name="approvalResult" value="rejected" required>
|
|
|
<span style="margin-left: 5px;">驳回申请</span>
|
|
|
</label>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">处置方式</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="disposalMethod" name="disposalMethod" style="width: 100%;">
|
|
|
<option value="">请选择处置方式</option>
|
|
|
<option value="环保拆解回收">环保拆解回收</option>
|
|
|
<option value="部件拆解后分类处理">部件拆解后分类处理</option>
|
|
|
<option value="整机拍卖">整机拍卖</option>
|
|
|
<option value="捐赠教育机构">捐赠教育机构</option>
|
|
|
<option value="其他">其他</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">审批意见</div>
|
|
|
<div class="detail-value">
|
|
|
<textarea id="approvalComments" name="approvalComments" rows="3" style="width: 100%;"
|
|
|
placeholder="请输入审批意见" required></textarea>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">审批人</div>
|
|
|
<div class="detail-value">
|
|
|
<select id="approver" name="approver" required style="width: 100%;">
|
|
|
<option value="">请选择审批人</option>
|
|
|
<option value="manager1">张经理</option>
|
|
|
<option value="manager2">李总监</option>
|
|
|
<option value="manager3">王总</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="text-align: right; margin-top: 20px;">
|
|
|
<button type="button" class="btn" onclick="closeApproveDisposalModal()">取消</button>
|
|
|
<button type="submit" class="btn btn-primary">提交审批</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</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 'archive':
|
|
|
loadDeviceArchive();
|
|
|
break;
|
|
|
case 'monitor':
|
|
|
loadDeviceMonitor();
|
|
|
break;
|
|
|
case 'maintenance':
|
|
|
loadMaintenanceData();
|
|
|
break;
|
|
|
case 'repair':
|
|
|
loadRepairData();
|
|
|
break;
|
|
|
case 'parts':
|
|
|
loadPartsData();
|
|
|
break;
|
|
|
case 'disposal':
|
|
|
loadDisposalData();
|
|
|
break;
|
|
|
case 'analysis':
|
|
|
loadAnalysisData();
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 加载设备档案数据
|
|
|
function loadDeviceArchive() {
|
|
|
const deviceGrid = document.getElementById('deviceArchiveGrid');
|
|
|
if (!deviceGrid) return;
|
|
|
|
|
|
const statusMap = {
|
|
|
'online': { text: '在线', class: 'status-online' },
|
|
|
'offline': { text: '离线', class: 'status-offline' },
|
|
|
'maintenance': { text: '维护中', class: 'status-maintenance' },
|
|
|
'fault': { text: '故障', class: 'status-fault' }
|
|
|
};
|
|
|
|
|
|
deviceGrid.innerHTML = devices.map(device => `
|
|
|
<div class="device-item">
|
|
|
<div class="device-info">
|
|
|
<div class="device-icon">${device.icon}</div>
|
|
|
<div class="device-details">
|
|
|
<h4>${device.name}</h4>
|
|
|
<p>编号:${device.id}</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="device-params">
|
|
|
<div class="param-item">
|
|
|
<span class="param-value">${device.battery}</span>
|
|
|
<div class="param-label">电量/状态</div>
|
|
|
</div>
|
|
|
<div class="param-item">
|
|
|
<span class="param-value">${device.workTime}</span>
|
|
|
<div class="param-label">工作时长</div>
|
|
|
</div>
|
|
|
<div class="param-item">
|
|
|
<span class="param-value">${device.area}</span>
|
|
|
<div class="param-label">所属区域</div>
|
|
|
</div>
|
|
|
<div class="param-item">
|
|
|
<span class="param-value">${device.manager}</span>
|
|
|
<div class="param-label">负责人</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
|
<span class="status-badge ${statusMap[device.status].class}">${statusMap[device.status].text}</span>
|
|
|
<div>
|
|
|
<button class="btn btn-primary" onclick="viewDevice('${device.id}')">查看</button>
|
|
|
<button class="btn btn-success" onclick="editDevice('${device.id}')">编辑</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
`).join('');
|
|
|
}
|
|
|
|
|
|
// 分页相关变量
|
|
|
const MONITOR_PAGE_SIZE = 5;
|
|
|
let currentMonitorPage = 1;
|
|
|
let monitorData = [
|
|
|
{ id: 'UC-2025001', name: '垃圾打捞无人船A01', type: '打捞无人船', location: '橘子洲头', battery: '85%', status: 'online', lastUpdate: '2024-01-15 14:30' },
|
|
|
{ id: 'UD-2025001', name: '巡查无人机B01', type: '巡查无人机', location: '湘江大桥段', battery: '0%', status: 'offline', lastUpdate: '2024-01-15 12:15' },
|
|
|
{ id: 'IC-2025001', name: '智能摄像头C01', type: '智能摄像头', location: '银盆岭大桥', battery: '正常', status: 'online', lastUpdate: '2024-01-15 14:35' },
|
|
|
{ id: 'MT-2025001', name: '密闭转运车D01', type: '密闭转运车', location: '维修车间', battery: '正常', status: 'maintenance', lastUpdate: '2024-01-15 10:00' },
|
|
|
// 添加更多测试数据...
|
|
|
{ id: 'UC-2025002', name: '垃圾打捞无人船A02', type: '打捞无人船', location: '岳麓区沿岸', battery: '78%', status: 'online', lastUpdate: '2024-01-15 15:20' },
|
|
|
{ id: 'UD-2025002', name: '巡查无人机B02', type: '巡查无人机', location: '橘子洲尾', battery: '92%', status: 'online', lastUpdate: '2024-01-15 15:45' }
|
|
|
];
|
|
|
|
|
|
// // 加载设备监控数据
|
|
|
// function loadDeviceMonitor() {
|
|
|
// const monitorTableBody = document.getElementById('monitorTableBody');
|
|
|
// if (!monitorTableBody) return;
|
|
|
|
|
|
// const monitorData = [
|
|
|
// { id: 'UC-2025001', name: '垃圾打捞无人船A01', type: '打捞无人船', location: '橘子洲头', battery: '85%', status: 'online', lastUpdate: '2024-01-15 14:30' },
|
|
|
// { id: 'UD-2025001', name: '巡查无人机B01', type: '巡查无人机', location: '湘江大桥段', battery: '0%', status: 'offline', lastUpdate: '2024-01-15 12:15' },
|
|
|
// { id: 'IC-2025001', name: '智能摄像头C01', type: '智能摄像头', location: '银盆岭大桥', battery: '正常', status: 'online', lastUpdate: '2024-01-15 14:35' },
|
|
|
// { id: 'MT-2025001', name: '密闭转运车D01', type: '密闭转运车', location: '维修车间', battery: '正常', status: 'maintenance', lastUpdate: '2024-01-15 10:00' }
|
|
|
// ];
|
|
|
|
|
|
// const statusMap = {
|
|
|
// 'online': { text: '在线', class: 'status-online' },
|
|
|
// 'offline': { text: '离线', class: 'status-offline' },
|
|
|
// 'maintenance': { text: '维护中', class: 'status-maintenance' }
|
|
|
// };
|
|
|
|
|
|
// monitorTableBody.innerHTML = monitorData.map(device => `
|
|
|
// <tr>
|
|
|
// <td>${device.id}</td>
|
|
|
// <td>${device.name}</td>
|
|
|
// <td>${device.type}</td>
|
|
|
// <td>${device.location}</td>
|
|
|
// <td>${device.battery}</td>
|
|
|
// <td><span class="status-badge ${statusMap[device.status].class}">${statusMap[device.status].text}</span></td>
|
|
|
// <td>${device.lastUpdate}</td>
|
|
|
// <td>
|
|
|
// <button class="btn btn-primary" onclick="locateDevice('${device.id}')">定位</button>
|
|
|
// <button class="btn" onclick="viewDetails('${device.id}')">详情</button>
|
|
|
// </td>
|
|
|
// </tr>
|
|
|
// `).join('');
|
|
|
// }
|
|
|
// 加载监控数据并分页
|
|
|
function loadDeviceMonitor(page = 1) {
|
|
|
const monitorTableBody = document.getElementById('monitorTableBody');
|
|
|
if (!monitorTableBody) return;
|
|
|
const statusMap = {
|
|
|
'online': { text: '在线', class: 'status-online' },
|
|
|
'offline': { text: '离线', class: 'status-offline' },
|
|
|
'maintenance': { text: '维护中', class: 'status-maintenance' }
|
|
|
};
|
|
|
|
|
|
const start = (page - 1) * MONITOR_PAGE_SIZE;
|
|
|
const end = Math.min(start + MONITOR_PAGE_SIZE, monitorData.length);
|
|
|
monitorTableBody.innerHTML = '';
|
|
|
|
|
|
for (let i = start; i < end; i++) {
|
|
|
const device = monitorData[i];
|
|
|
monitorTableBody.innerHTML += `
|
|
|
<tr>
|
|
|
<td>${device.id}</td>
|
|
|
<td>${device.name}</td>
|
|
|
<td>${device.type}</td>
|
|
|
<td>${device.location}</td>
|
|
|
<td>${device.battery}</td>
|
|
|
<td><span class="status-badge ${statusMap[device.status].class}">${statusMap[device.status].text}</span></td>
|
|
|
<td>${device.lastUpdate}</td>
|
|
|
<td>
|
|
|
<button class="btn btn-primary" onclick="locateDevice('${device.id}')">定位</button>
|
|
|
<button class="btn" onclick="viewDetails('${device.id}')">详情</button>
|
|
|
</td>
|
|
|
</tr>
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
renderMonitorPagination(page);
|
|
|
}
|
|
|
|
|
|
// 渲染分页控件
|
|
|
function renderMonitorPagination(page) {
|
|
|
const total = monitorData.length;
|
|
|
const totalPages = Math.ceil(total / MONITOR_PAGE_SIZE);
|
|
|
const container = document.getElementById('monitorPagination');
|
|
|
container.innerHTML = '';
|
|
|
if (totalPages <= 1) return;
|
|
|
|
|
|
// 上一页
|
|
|
const prev = document.createElement('button');
|
|
|
prev.textContent = '上一页';
|
|
|
prev.className = 'btn';
|
|
|
prev.disabled = page === 1;
|
|
|
prev.onclick = () => gotoMonitorPage(page - 1);
|
|
|
container.appendChild(prev);
|
|
|
|
|
|
// 页码
|
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
|
if (i === 1 || i === totalPages || Math.abs(i - page) <= 1) {
|
|
|
const btn = document.createElement('button');
|
|
|
btn.textContent = i;
|
|
|
btn.className = 'btn' + (i === page ? ' btn-primary' : '');
|
|
|
btn.onclick = () => gotoMonitorPage(i);
|
|
|
container.appendChild(btn);
|
|
|
} else if (i === page - 2 || i === page + 2) {
|
|
|
const span = document.createElement('span');
|
|
|
span.textContent = '...';
|
|
|
span.style.margin = '0 4px';
|
|
|
container.appendChild(span);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 下一页
|
|
|
const next = document.createElement('button');
|
|
|
next.textContent = '下一页';
|
|
|
next.className = 'btn';
|
|
|
next.disabled = page === totalPages;
|
|
|
next.onclick = () => gotoMonitorPage(page + 1);
|
|
|
container.appendChild(next);
|
|
|
}
|
|
|
|
|
|
function gotoMonitorPage(page) {
|
|
|
currentMonitorPage = page;
|
|
|
loadDeviceMonitor(page);
|
|
|
}
|
|
|
const maintenanceData = [
|
|
|
{ planId: 'M001', deviceId: 'UC-2025001', deviceName: '垃圾打捞无人船A01', type: '例行保养', scheduledTime: '2024-01-16 09:00', assignee: '张三', status: 'pending' },
|
|
|
{ planId: 'M002', deviceId: 'UD-2025001', deviceName: '巡查无人机B01', type: '周期保养', scheduledTime: '2024-01-17 14:00', assignee: '李四', status: 'in_progress' },
|
|
|
{ planId: 'M003', deviceId: 'IC-2025001', deviceName: '智能摄像头C01', type: '条件保养', scheduledTime: '2024-01-15 10:00', assignee: '王五', status: 'completed' }
|
|
|
];
|
|
|
// 加载维护保养数据
|
|
|
function loadMaintenanceData() {
|
|
|
const maintenanceTableBody = document.getElementById('maintenanceTableBody');
|
|
|
if (!maintenanceTableBody) return;
|
|
|
|
|
|
|
|
|
|
|
|
const statusMap = {
|
|
|
'pending': { text: '待执行', class: 'status-maintenance' },
|
|
|
'in_progress': { text: '进行中', class: 'status-online' },
|
|
|
'completed': { text: '已完成', class: 'status-online' }
|
|
|
};
|
|
|
|
|
|
maintenanceTableBody.innerHTML = maintenanceData.map(plan => `
|
|
|
<tr>
|
|
|
<td>${plan.planId}</td>
|
|
|
<td>${plan.deviceId}</td>
|
|
|
<td>${plan.deviceName}</td>
|
|
|
<td>${plan.type}</td>
|
|
|
<td>${plan.scheduledTime}</td>
|
|
|
<td>${plan.assignee}</td>
|
|
|
<td><span class="status-badge ${statusMap[plan.status].class}">${statusMap[plan.status].text}</span></td>
|
|
|
<td>
|
|
|
<button class="btn btn-primary" onclick="viewMaintenance('${plan.planId}')">查看</button>
|
|
|
<button class="btn btn-success" onclick="executeMaintenance('${plan.planId}')">执行</button>
|
|
|
</td>
|
|
|
</tr>
|
|
|
`).join('');
|
|
|
}
|
|
|
|
|
|
const repairData = [
|
|
|
{ repairId: 'R001', deviceId: 'UC-2025001', fault: '推进器过载', reporter: '张三', repairer: '李四', priority: 'high', status: 'pending' },
|
|
|
{ repairId: 'R002', deviceId: 'UD-2025001', fault: '电池无法充电', reporter: '王五', repairer: '赵六', priority: 'medium', status: 'in_progress' },
|
|
|
{ repairId: 'R003', deviceId: 'IC-2025001', fault: '镜头模糊', reporter: '张三', repairer: '李四', priority: 'low', status: 'completed' }
|
|
|
];
|
|
|
// 加载故障维修数据
|
|
|
function loadRepairData() {
|
|
|
const repairTableBody = document.getElementById('repairTableBody');
|
|
|
if (!repairTableBody) return;
|
|
|
|
|
|
|
|
|
|
|
|
const statusMap = {
|
|
|
'pending': { text: '待维修', class: 'status-maintenance' },
|
|
|
'in_progress': { text: '维修中', class: 'status-online' },
|
|
|
'completed': { text: '已完成', class: 'status-online' }
|
|
|
};
|
|
|
|
|
|
const priorityMap = {
|
|
|
'high': { text: '紧急', class: 'status-fault' },
|
|
|
'medium': { text: '一般', class: 'status-maintenance' },
|
|
|
'low': { text: '低', class: 'status-online' }
|
|
|
};
|
|
|
|
|
|
repairTableBody.innerHTML = repairData.map(repair => `
|
|
|
<tr>
|
|
|
<td>${repair.repairId}</td>
|
|
|
<td>${repair.deviceId}</td>
|
|
|
<td>${repair.fault}</td>
|
|
|
<td>${repair.reporter}</td>
|
|
|
<td>${repair.repairer}</td>
|
|
|
<td><span class="status-badge ${priorityMap[repair.priority].class}">${priorityMap[repair.priority].text}</span></td>
|
|
|
<td><span class="status-badge ${statusMap[repair.status].class}">${statusMap[repair.status].text}</span></td>
|
|
|
<td>
|
|
|
<button class="btn btn-primary" onclick="viewRepair('${repair.repairId}')">查看</button>
|
|
|
<button class="btn btn-success" onclick="processRepair('${repair.repairId}')">处理</button>
|
|
|
</td>
|
|
|
</tr>
|
|
|
`).join('');
|
|
|
}
|
|
|
|
|
|
const partsData = [
|
|
|
{ partId: 'P001', name: '无人船专用电池', device: '打捞无人船', stock: 8, minStock: 5, price: '2000', supplier: '科技公司A' },
|
|
|
{ partId: 'P002', name: '无人机螺旋桨', device: '巡查无人机', stock: 3, minStock: 10, price: '150', supplier: '科技公司B' },
|
|
|
{ partId: 'P003', name: '摄像头镜头', device: '智能摄像头', stock: 15, minStock: 8, price: '800', supplier: '科技公司C' },
|
|
|
{ partId: 'P004', name: '转运车轮胎', device: '密闭转运车', stock: 6, minStock: 4, price: '500', supplier: '轮胎公司D' }
|
|
|
];
|
|
|
// 加载备件管理数据
|
|
|
function loadPartsData() {
|
|
|
const partsTableBody = document.getElementById('partsTableBody');
|
|
|
if (!partsTableBody) return;
|
|
|
|
|
|
partsTableBody.innerHTML = partsData.map(part => {
|
|
|
const isLowStock = part.stock < part.minStock;
|
|
|
const stockClass = isLowStock ? 'status-fault' : 'status-online';
|
|
|
|
|
|
return `
|
|
|
<tr>
|
|
|
<td>${part.partId}</td>
|
|
|
<td>${part.name}</td>
|
|
|
<td>${part.device}</td>
|
|
|
<td><span class="status-badge ${stockClass}">${part.stock}</span></td>
|
|
|
<td>${part.minStock}</td>
|
|
|
<td>¥${part.price}</td>
|
|
|
<td>${part.supplier}</td>
|
|
|
<td>
|
|
|
<button class="btn btn-primary" onclick="viewPart('${part.partId}')">查看</button>
|
|
|
<button class="btn btn-warning" onclick="orderPart('${part.partId}')">采购</button>
|
|
|
</td>
|
|
|
</tr>
|
|
|
`;
|
|
|
}).join('');
|
|
|
}
|
|
|
const disposalData = [
|
|
|
{ applicationId: 'D001', deviceId: 'UC-2024001', deviceName: '垃圾打捞无人船X01', reason: '使用年限到期', applicant: '张三', applyTime: '2024-01-10', status: 'pending' },
|
|
|
{ applicationId: 'D002', deviceId: 'UD-2023001', deviceName: '巡查无人机Y01', reason: '维修成本过高', applicant: '李四', applyTime: '2024-01-08', status: 'approved' },
|
|
|
{ applicationId: 'D003', deviceId: 'IC-2023002', deviceName: '智能摄像头Z01', reason: '技术落后', applicant: '王五', applyTime: '2024-01-05', status: 'rejected' }
|
|
|
];
|
|
|
// 加载报废处置数据
|
|
|
function loadDisposalData() {
|
|
|
const disposalTableBody = document.getElementById('disposalTableBody');
|
|
|
if (!disposalTableBody) return;
|
|
|
|
|
|
|
|
|
|
|
|
const statusMap = {
|
|
|
'pending': { text: '待审批', class: 'status-maintenance' },
|
|
|
'approved': { text: '已批准', class: 'status-online' },
|
|
|
'rejected': { text: '已驳回', class: 'status-fault' }
|
|
|
};
|
|
|
|
|
|
disposalTableBody.innerHTML = disposalData.map(disposal => `
|
|
|
<tr>
|
|
|
<td>${disposal.applicationId}</td>
|
|
|
<td>${disposal.deviceId}</td>
|
|
|
<td>${disposal.deviceName}</td>
|
|
|
<td>${disposal.reason}</td>
|
|
|
<td>${disposal.applicant}</td>
|
|
|
<td>${disposal.applyTime}</td>
|
|
|
<td><span class="status-badge ${statusMap[disposal.status].class}">${statusMap[disposal.status].text}</span></td>
|
|
|
<td>
|
|
|
<button class="btn btn-primary" onclick="viewDisposal('${disposal.applicationId}')">查看</button>
|
|
|
<button class="btn btn-success" onclick="approveDisposal('${disposal.applicationId}')">审批</button>
|
|
|
</td>
|
|
|
</tr>
|
|
|
`).join('');
|
|
|
}
|
|
|
|
|
|
// 加载统计分析数据
|
|
|
function loadAnalysisData() {
|
|
|
// 更新统计数据
|
|
|
document.getElementById('totalDevices').textContent = '45';
|
|
|
document.getElementById('onlineRate').textContent = '87.2%';
|
|
|
document.getElementById('utilizationRate').textContent = '73.5%';
|
|
|
document.getElementById('faultRate').textContent = '2.3%';
|
|
|
|
|
|
// 获取当前选择的统计周期和分析维度
|
|
|
const period = document.getElementById('analysisPeriod').value;
|
|
|
const dimension = document.getElementById('analysisDimension').value;
|
|
|
|
|
|
// 更新分析图表
|
|
|
updateAnalysisChart(period, dimension);
|
|
|
}
|
|
|
|
|
|
// 加载设备告警
|
|
|
function loadDeviceAlerts() {
|
|
|
const deviceAlerts = document.getElementById('deviceAlerts');
|
|
|
if (!deviceAlerts) return;
|
|
|
|
|
|
const alerts = [
|
|
|
{ id: 'UC-2025001', title: '无人船电量低', time: '14:30', level: '警告' },
|
|
|
{ id: 'UD-2025001', title: '无人机离线超时', time: '12:15', level: '严重' },
|
|
|
{ id: 'P002', title: '螺旋桨库存不足', time: '10:00', level: '提醒' }
|
|
|
];
|
|
|
|
|
|
deviceAlerts.innerHTML = alerts.map(alert => `
|
|
|
<div class="alert-item">
|
|
|
<div class="alert-title">${alert.title}</div>
|
|
|
<div class="alert-info">
|
|
|
<span>${alert.time}</span>
|
|
|
<span>${alert.level}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
`).join('');
|
|
|
}
|
|
|
|
|
|
// 加载维护提醒
|
|
|
function loadMaintenanceReminders() {
|
|
|
const maintenanceReminders = document.getElementById('maintenanceReminders');
|
|
|
if (!maintenanceReminders) return;
|
|
|
|
|
|
const reminders = [
|
|
|
{ id: 'UC-2025001', title: '无人船例行保养', time: '明天 09:00', type: '即将到期' },
|
|
|
{ id: 'IC-2025001', title: '摄像头清洁保养', time: '后天 14:00', type: '计划中' },
|
|
|
{ id: 'MT-2025001', title: '转运车全面检修', time: '本周五', type: '计划中' }
|
|
|
];
|
|
|
|
|
|
maintenanceReminders.innerHTML = reminders.map(reminder => `
|
|
|
<div class="alert-item">
|
|
|
<div class="alert-title">${reminder.title}</div>
|
|
|
<div class="alert-info">
|
|
|
<span>${reminder.time}</span>
|
|
|
<span>${reminder.type}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
`).join('');
|
|
|
}
|
|
|
|
|
|
// 模态框操作
|
|
|
function openDeviceModal() {
|
|
|
document.getElementById('deviceModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
function closeDeviceModal() {
|
|
|
document.getElementById('deviceModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
function openMaintenanceModal() {
|
|
|
document.getElementById('maintenanceModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
function closeMaintenanceModal() {
|
|
|
document.getElementById('maintenanceModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 表单提交
|
|
|
document.getElementById('deviceForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
|
|
|
// 获取表单数据
|
|
|
const formData = new FormData(this);
|
|
|
const deviceData = {
|
|
|
id: formData.get('deviceId'),
|
|
|
name: formData.get('deviceName'),
|
|
|
type: formData.get('deviceType'),
|
|
|
icon: getDeviceIcon(formData.get('deviceType')),
|
|
|
area: getAreaName(formData.get('area')),
|
|
|
manager: getManagerName(formData.get('manager')),
|
|
|
status: 'online', // 默认设置为在线状态
|
|
|
battery: '100%', // 默认电量
|
|
|
workTime: '0h', // 初始工作时长
|
|
|
lastMaintenance: new Date().toISOString().split('T')[0] // 当前日期
|
|
|
};
|
|
|
|
|
|
// 添加到设备列表
|
|
|
addDeviceToArchive(deviceData);
|
|
|
|
|
|
alert('设备档案创建成功!');
|
|
|
closeDeviceModal();
|
|
|
loadDeviceArchive(); // 重新加载设备档案
|
|
|
});
|
|
|
|
|
|
// 辅助函数:根据设备类型获取图标
|
|
|
function getDeviceIcon(type) {
|
|
|
const icons = {
|
|
|
'ship': '🚢',
|
|
|
'drone': '🚁',
|
|
|
'camera': '📹',
|
|
|
'truck': '🚛'
|
|
|
};
|
|
|
return icons[type] || '📱';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:根据manager值获取显示名称
|
|
|
function getAreaName(area) {
|
|
|
const areas = {
|
|
|
'yuelu': '岳麓区',
|
|
|
'tianxin': '天心区',
|
|
|
'kaifu': '开福区',
|
|
|
'yuhua': '雨花区'
|
|
|
};
|
|
|
return areas[area] || area;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:根据manager值获取显示名称
|
|
|
function getManagerName(manager) {
|
|
|
const managers = {
|
|
|
'zhang': '张三',
|
|
|
'li': '李四',
|
|
|
'wang': '王五',
|
|
|
'zhao': '赵六'
|
|
|
};
|
|
|
return managers[manager] || manager;
|
|
|
}
|
|
|
|
|
|
document.getElementById('maintenanceForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
alert('维护计划创建成功!');
|
|
|
closeMaintenanceModal();
|
|
|
loadMaintenanceData();
|
|
|
});
|
|
|
|
|
|
// 其他操作函数
|
|
|
// 查看设备详情
|
|
|
function viewDevice(id) {
|
|
|
const device = devices.find(d => d.id === id);
|
|
|
if (!device) return;
|
|
|
|
|
|
// 填充查看模态框内容
|
|
|
const detailsContent = document.getElementById('deviceDetailsContent');
|
|
|
detailsContent.innerHTML = `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备编号</div>
|
|
|
<div class="detail-value">${device.id}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value">${device.name}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备类型</div>
|
|
|
<div class="detail-value">${getDeviceTypeName(device.type)}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">所属区域</div>
|
|
|
<div class="detail-value">${device.area}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">管理责任人</div>
|
|
|
<div class="detail-value">${device.manager}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备状态</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${getStatusClass(device.status)}">${getStatusName(device.status)}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">当前电量</div>
|
|
|
<div class="detail-value">${device.battery}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">累计工作时长</div>
|
|
|
<div class="detail-value">${device.workTime}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">上次维护时间</div>
|
|
|
<div class="detail-value">${device.lastMaintenance}</div>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
// 显示查看模态框
|
|
|
document.getElementById('viewDeviceModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
|
|
|
// 编辑设备
|
|
|
function editDevice(id) {
|
|
|
const device = devices.find(d => d.id === id);
|
|
|
if (!device) return;
|
|
|
|
|
|
// 填充编辑表单
|
|
|
document.getElementById('editDeviceId').value = device.id;
|
|
|
document.getElementById('editDeviceName').value = device.name;
|
|
|
document.getElementById('editDeviceType').value = device.type;
|
|
|
|
|
|
// 修复区域选择问题 - 直接使用设备中的区域值
|
|
|
const areaValue = device.area.replace('沿岸', '').toLowerCase();
|
|
|
document.getElementById('editDeviceArea').value = areaValue;
|
|
|
|
|
|
document.getElementById('editDeviceManager').value = getManagerValue(device.manager);
|
|
|
document.getElementById('editDeviceStatus').value = device.status;
|
|
|
|
|
|
// 显示编辑模态框
|
|
|
document.getElementById('editDeviceModal').style.display = 'block';
|
|
|
}
|
|
|
// 关闭查看模态框
|
|
|
function closeViewDeviceModal() {
|
|
|
document.getElementById('viewDeviceModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 关闭编辑模态框
|
|
|
function closeEditDeviceModal() {
|
|
|
document.getElementById('editDeviceModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取设备类型名称
|
|
|
function getDeviceTypeName(type) {
|
|
|
const typeNames = {
|
|
|
'ship': '打捞无人船',
|
|
|
'drone': '巡查无人机',
|
|
|
'camera': '智能摄像头',
|
|
|
'truck': '密闭转运车'
|
|
|
};
|
|
|
return typeNames[type] || type;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取状态名称
|
|
|
function getStatusName(status) {
|
|
|
const statusNames = {
|
|
|
'online': '在线',
|
|
|
'offline': '离线',
|
|
|
'maintenance': '维护中',
|
|
|
'fault': '故障'
|
|
|
};
|
|
|
return statusNames[status] || status;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取状态类名
|
|
|
function getStatusClass(status) {
|
|
|
const statusClasses = {
|
|
|
'online': 'status-online',
|
|
|
'offline': 'status-offline',
|
|
|
'maintenance': 'status-maintenance',
|
|
|
'fault': 'status-fault'
|
|
|
};
|
|
|
return statusClasses[status] || '';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:根据显示名称获取manager值
|
|
|
function getManagerValue(managerName) {
|
|
|
const managers = {
|
|
|
'张三': 'zhang',
|
|
|
'李四': 'li',
|
|
|
'王五': 'wang',
|
|
|
'赵六': 'zhao'
|
|
|
};
|
|
|
return managers[managerName] || managerName;
|
|
|
}
|
|
|
// 处理编辑表单提交
|
|
|
document.getElementById('editDeviceForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const deviceId = document.getElementById('editDeviceId').value;
|
|
|
const device = devices.find(d => d.id === deviceId);
|
|
|
if (!device) return;
|
|
|
|
|
|
// 更新设备信息
|
|
|
device.name = document.getElementById('editDeviceName').value;
|
|
|
device.type = document.getElementById('editDeviceType').value;
|
|
|
device.area = document.getElementById('editDeviceArea').options[document.getElementById('editDeviceArea').selectedIndex].text;
|
|
|
device.manager = getManagerName(document.getElementById('editDeviceManager').value);
|
|
|
device.status = document.getElementById('editDeviceStatus').value;
|
|
|
|
|
|
// 重新加载设备档案
|
|
|
loadDeviceArchive();
|
|
|
closeEditDeviceModal();
|
|
|
alert('设备信息更新成功!');
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查看设备详情
|
|
|
function viewDetails(id) {
|
|
|
const device = monitorData.find(d => d.id === id);
|
|
|
if (!device) return;
|
|
|
|
|
|
// 填充查看模态框内容
|
|
|
const detailsContent = document.getElementById('deviceDetailsContents');
|
|
|
detailsContent.innerHTML = `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备编号</div>
|
|
|
<div class="detail-value">${device.id}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value">${device.name}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备类型</div>
|
|
|
<div class="detail-value">${device.type}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">当前位置</div>
|
|
|
<div class="detail-value">${device.location}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">电量/状态</div>
|
|
|
<div class="detail-value">${device.battery}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">在线状态</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${getStatusClass(device.status)}">${getStatusName(device.status)}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">最后更新时间</div>
|
|
|
<div class="detail-value">${device.lastUpdate}</div>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
// 显示查看模态框
|
|
|
document.getElementById('viewDetailsModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭查看详情模态框
|
|
|
function closeViewDetailsModal() {
|
|
|
document.getElementById('viewDetailsModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 全局变量存储使用的备件
|
|
|
let usedParts = [];
|
|
|
|
|
|
// 打开执行维护模态框
|
|
|
function executeMaintenance(id) {
|
|
|
const maintenance = maintenanceData.find(m => m.planId === id);
|
|
|
if (!maintenance) return;
|
|
|
|
|
|
// 填充执行表单
|
|
|
document.getElementById('executePlanId').value = maintenance.planId;
|
|
|
document.getElementById('executePlanNo').textContent = maintenance.planId;
|
|
|
document.getElementById('executeDeviceName').textContent = maintenance.deviceName;
|
|
|
document.getElementById('executeMaintenanceType').textContent = maintenance.type;
|
|
|
document.getElementById('executeScheduledTime').textContent = maintenance.scheduledTime;
|
|
|
document.getElementById('executeAssignee').textContent = maintenance.assignee;
|
|
|
|
|
|
// 设置默认实际执行时间为当前时间
|
|
|
const now = new Date();
|
|
|
const timezoneOffset = now.getTimezoneOffset() * 60000;
|
|
|
const localISOTime = new Date(now - timezoneOffset).toISOString().slice(0, 16);
|
|
|
document.getElementById('executeActualTime').value = localISOTime;
|
|
|
|
|
|
// 重置表单
|
|
|
document.getElementById('executeMaintenanceForm').reset();
|
|
|
usedParts = [];
|
|
|
document.getElementById('usedPartsList').innerHTML = '';
|
|
|
document.getElementById('photoPreview').innerHTML = '';
|
|
|
|
|
|
// 显示执行模态框
|
|
|
document.getElementById('executeMaintenanceModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭执行维护模态框
|
|
|
function closeExecuteMaintenanceModal() {
|
|
|
document.getElementById('executeMaintenanceModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 添加使用的备件
|
|
|
function addUsedPart() {
|
|
|
const partSelect = document.getElementById('executePartSelect');
|
|
|
const partQuantity = document.getElementById('executePartQuantity');
|
|
|
|
|
|
if (!partSelect.value || !partQuantity.value || parseInt(partQuantity.value) <= 0) {
|
|
|
alert('请选择备件并输入有效数量');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const partId = partSelect.value;
|
|
|
const partName = partSelect.options[partSelect.selectedIndex].text.split(' - ')[1];
|
|
|
const quantity = parseInt(partQuantity.value);
|
|
|
|
|
|
// 检查是否已添加过该备件
|
|
|
const existingPart = usedParts.find(p => p.partId === partId);
|
|
|
if (existingPart) {
|
|
|
existingPart.quantity += quantity;
|
|
|
} else {
|
|
|
usedParts.push({
|
|
|
partId: partId,
|
|
|
partName: partName,
|
|
|
quantity: quantity
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 更新备件列表显示
|
|
|
updateUsedPartsList();
|
|
|
|
|
|
// 重置选择
|
|
|
partSelect.value = '';
|
|
|
partQuantity.value = '1';
|
|
|
}
|
|
|
// 更新使用的备件列表显示
|
|
|
function updateUsedPartsList() {
|
|
|
const partsList = document.getElementById('usedPartsList');
|
|
|
partsList.innerHTML = '';
|
|
|
|
|
|
usedParts.forEach((part, index) => {
|
|
|
const row = document.createElement('tr');
|
|
|
row.innerHTML = `
|
|
|
<td style="padding: 8px; border-bottom: 1px solid #e1e9f5;">${part.partId}</td>
|
|
|
<td style="padding: 8px; border-bottom: 1px solid #e1e9f5;">${part.partName}</td>
|
|
|
<td style="padding: 8px; text-align: right; border-bottom: 1px solid #e1e9f5;">${part.quantity}</td>
|
|
|
<td style="padding: 8px; text-align: center; border-bottom: 1px solid #e1e9f5;">
|
|
|
<button class="btn btn-danger" onclick="removeUsedPart(${index})" style="padding: 2px 8px; font-size: 12px;">删除</button>
|
|
|
</td>
|
|
|
`;
|
|
|
partsList.appendChild(row);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 移除使用的备件
|
|
|
function removeUsedPart(index) {
|
|
|
usedParts.splice(index, 1);
|
|
|
updateUsedPartsList();
|
|
|
}
|
|
|
|
|
|
// 处理执行表单提交
|
|
|
document.getElementById('executeMaintenanceForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const planId = document.getElementById('executePlanId').value;
|
|
|
const actualTime = document.getElementById('executeActualTime').value;
|
|
|
const operator = document.getElementById('executeOperator').value;
|
|
|
const result = document.getElementById('executeResult').value;
|
|
|
const deviceStatus = document.getElementById('executeDeviceStatus').value;
|
|
|
const photos = document.getElementById('executePhotos').files;
|
|
|
|
|
|
// 更新维护计划状态
|
|
|
const maintenance = maintenanceData.find(m => m.planId === planId);
|
|
|
if (maintenance) {
|
|
|
maintenance.status = 'completed';
|
|
|
maintenance.actualTime = actualTime;
|
|
|
maintenance.operator = operator;
|
|
|
maintenance.result = result;
|
|
|
maintenance.deviceStatus = deviceStatus;
|
|
|
maintenance.usedParts = [...usedParts];
|
|
|
maintenance.completeTime = new Date().toISOString().split('T')[0];
|
|
|
|
|
|
// 这里可以添加实际业务逻辑,如更新库存、发送到后端等
|
|
|
|
|
|
// 重新加载维护计划数据
|
|
|
loadMaintenanceData();
|
|
|
|
|
|
alert('维护计划执行结果已提交!');
|
|
|
closeExecuteMaintenanceModal();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 照片预览功能
|
|
|
document.getElementById('executePhotos').addEventListener('change', function (e) {
|
|
|
const preview = document.getElementById('photoPreview');
|
|
|
preview.innerHTML = '';
|
|
|
|
|
|
Array.from(e.target.files).forEach(file => {
|
|
|
if (!file.type.match('image.*')) return;
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = function (e) {
|
|
|
const img = document.createElement('img');
|
|
|
img.src = e.target.result;
|
|
|
img.style.width = '100px';
|
|
|
img.style.height = '100px';
|
|
|
img.style.objectFit = 'cover';
|
|
|
img.style.borderRadius = '4px';
|
|
|
img.style.margin = '5px';
|
|
|
preview.appendChild(img);
|
|
|
};
|
|
|
reader.readAsDataURL(file);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
function viewRepair(id) {
|
|
|
alert('查看维修单: ' + id);
|
|
|
}
|
|
|
|
|
|
// 全局变量存储维修措施和使用的备件
|
|
|
let repairActions = [];
|
|
|
let repairParts = [];
|
|
|
|
|
|
// 打开处理维修模态框
|
|
|
function processRepair(id) {
|
|
|
const repair = repairData.find(r => r.repairId === id);
|
|
|
if (!repair) return;
|
|
|
|
|
|
// 填充处理表单
|
|
|
document.getElementById('processRepairId').value = repair.repairId;
|
|
|
document.getElementById('processRepairNo').textContent = repair.repairId;
|
|
|
document.getElementById('processDeviceName').textContent = getDeviceName(repair.deviceId);
|
|
|
document.getElementById('processFaultDesc').textContent = repair.fault;
|
|
|
document.getElementById('processReporter').textContent = repair.reporter;
|
|
|
|
|
|
// 设置紧急程度显示
|
|
|
const priorityText = {
|
|
|
'high': '<span class="status-badge status-fault">紧急</span>',
|
|
|
'medium': '<span class="status-badge status-maintenance">一般</span>',
|
|
|
'low': '<span class="status-badge status-online">低</span>'
|
|
|
};
|
|
|
document.getElementById('processPriority').innerHTML = priorityText[repair.priority] || repair.priority;
|
|
|
|
|
|
// 设置默认开始时间为当前时间
|
|
|
const now = new Date();
|
|
|
const timezoneOffset = now.getTimezoneOffset() * 60000;
|
|
|
const localISOTime = new Date(now - timezoneOffset).toISOString().slice(0, 16);
|
|
|
document.getElementById('repairStartTime').value = localISOTime;
|
|
|
|
|
|
// 如果是已处理的,禁用表单并显示结果
|
|
|
if (repair.status !== 'pending') {
|
|
|
document.querySelectorAll('#processRepairForm input, #processRepairForm select, #processRepairForm textarea')
|
|
|
.forEach(el => el.disabled = true);
|
|
|
document.getElementById('repairTechnician').value = repair.technician || '';
|
|
|
document.getElementById('repairStartTime').value = repair.startTime || '';
|
|
|
document.getElementById('repairDiagnosis').value = repair.diagnosis || '';
|
|
|
document.getElementById('repairResult').value = repair.result || '';
|
|
|
document.getElementById('repairNotes').value = repair.notes || '';
|
|
|
document.getElementById('repairDeviceStatus').value = repair.deviceStatus || '';
|
|
|
document.querySelector('#processRepairForm button[type="submit"]').style.display = 'none';
|
|
|
|
|
|
// 显示已有的维修措施和备件
|
|
|
if (repair.actions) {
|
|
|
repairActions = [...repair.actions];
|
|
|
updateRepairActionsList();
|
|
|
}
|
|
|
if (repair.usedParts) {
|
|
|
repairParts = [...repair.usedParts];
|
|
|
updateRepairPartsList();
|
|
|
}
|
|
|
} else {
|
|
|
// 重置表单
|
|
|
document.getElementById('processRepairForm').reset();
|
|
|
repairActions = [];
|
|
|
repairParts = [];
|
|
|
document.getElementById('repairActionsList').innerHTML = '';
|
|
|
document.getElementById('repairPartsList').innerHTML = '';
|
|
|
document.getElementById('repairPhotoPreview').innerHTML = '';
|
|
|
document.querySelectorAll('#processRepairForm input, #processRepairForm select, #processRepairForm textarea')
|
|
|
.forEach(el => el.disabled = false);
|
|
|
document.querySelector('#processRepairForm button[type="submit"]').style.display = 'inline-block';
|
|
|
}
|
|
|
|
|
|
// 显示处理模态框
|
|
|
document.getElementById('processRepairModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭处理维修模态框
|
|
|
function closeProcessRepairModal() {
|
|
|
document.getElementById('processRepairModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 添加维修措施
|
|
|
function addRepairAction() {
|
|
|
const actionSelect = document.getElementById('repairActionSelect');
|
|
|
|
|
|
if (!actionSelect.value) {
|
|
|
alert('请选择维修措施类型');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const actionType = actionSelect.value;
|
|
|
const actionText = actionSelect.options[actionSelect.selectedIndex].text.split('-')[1] || actionSelect.options[actionSelect.selectedIndex].text;
|
|
|
const actionDesc = prompt(`请输入"${actionText}"的详细说明`, "");
|
|
|
|
|
|
if (actionDesc === null || actionDesc.trim() === "") {
|
|
|
alert('必须输入维修措施详细说明');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
repairActions.push({
|
|
|
type: actionType,
|
|
|
description: actionDesc
|
|
|
});
|
|
|
|
|
|
// 更新维修措施列表显示
|
|
|
updateRepairActionsList();
|
|
|
|
|
|
// 重置选择
|
|
|
actionSelect.value = '';
|
|
|
}
|
|
|
|
|
|
// 更新维修措施列表显示
|
|
|
function updateRepairActionsList() {
|
|
|
const actionsList = document.getElementById('repairActionsList');
|
|
|
actionsList.innerHTML = '';
|
|
|
|
|
|
repairActions.forEach((action, index) => {
|
|
|
const typeText = {
|
|
|
'replace': '更换部件',
|
|
|
'repair': '修复部件',
|
|
|
'adjust': '调整参数',
|
|
|
'clean': '清洁维护',
|
|
|
'software': '软件修复'
|
|
|
};
|
|
|
|
|
|
const row = document.createElement('tr');
|
|
|
row.innerHTML = `
|
|
|
<td style="padding: 8px; border-bottom: 1px solid #e1e9f5;">${typeText[action.type] || action.type}</td>
|
|
|
<td style="padding: 8px; border-bottom: 1px solid #e1e9f5;">${action.description}</td>
|
|
|
<td style="padding: 8px; text-align: center; border-bottom: 1px solid #e1e9f5;">
|
|
|
<button class="btn btn-danger" onclick="removeRepairAction(${index})" style="padding: 2px 8px; font-size: 12px;">删除</button>
|
|
|
</td>
|
|
|
`;
|
|
|
actionsList.appendChild(row);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 移除维修措施
|
|
|
function removeRepairAction(index) {
|
|
|
repairActions.splice(index, 1);
|
|
|
updateRepairActionsList();
|
|
|
}
|
|
|
|
|
|
// 添加维修使用的备件
|
|
|
function addRepairPart() {
|
|
|
const partSelect = document.getElementById('repairPartSelect');
|
|
|
const partQuantity = document.getElementById('repairPartQuantity');
|
|
|
|
|
|
if (!partSelect.value || !partQuantity.value || parseInt(partQuantity.value) <= 0) {
|
|
|
alert('请选择备件并输入有效数量');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const partId = partSelect.value;
|
|
|
const partName = partSelect.options[partSelect.selectedIndex].text.split(' - ')[1];
|
|
|
const quantity = parseInt(partQuantity.value);
|
|
|
|
|
|
// 检查是否已添加过该备件
|
|
|
const existingPart = repairParts.find(p => p.partId === partId);
|
|
|
if (existingPart) {
|
|
|
existingPart.quantity += quantity;
|
|
|
} else {
|
|
|
repairParts.push({
|
|
|
partId: partId,
|
|
|
partName: partName,
|
|
|
quantity: quantity
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 更新备件列表显示
|
|
|
updateRepairPartsList();
|
|
|
|
|
|
// 重置选择
|
|
|
partSelect.value = '';
|
|
|
partQuantity.value = '1';
|
|
|
}
|
|
|
|
|
|
// 更新维修使用的备件列表显示
|
|
|
function updateRepairPartsList() {
|
|
|
const partsList = document.getElementById('repairPartsList');
|
|
|
partsList.innerHTML = '';
|
|
|
|
|
|
repairParts.forEach((part, index) => {
|
|
|
const row = document.createElement('tr');
|
|
|
row.innerHTML = `
|
|
|
<td style="padding: 8px; border-bottom: 1px solid #e1e9f5;">${part.partId}</td>
|
|
|
<td style="padding: 8px; border-bottom: 1px solid #e1e9f5;">${part.partName}</td>
|
|
|
<td style="padding: 8px; text-align: right; border-bottom: 1px solid #e1e9f5;">${part.quantity}</td>
|
|
|
<td style="padding: 8px; text-align: center; border-bottom: 1px solid #e1e9f5;">
|
|
|
<button class="btn btn-danger" onclick="removeRepairPart(${index})" style="padding: 2px 8px; font-size: 12px;">删除</button>
|
|
|
</td>
|
|
|
`;
|
|
|
partsList.appendChild(row);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 移除维修使用的备件
|
|
|
function removeRepairPart(index) {
|
|
|
repairParts.splice(index, 1);
|
|
|
updateRepairPartsList();
|
|
|
}
|
|
|
|
|
|
// 处理维修表单提交
|
|
|
document.getElementById('processRepairForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const repairId = document.getElementById('processRepairId').value;
|
|
|
const technician = document.getElementById('repairTechnician').value;
|
|
|
const startTime = document.getElementById('repairStartTime').value;
|
|
|
const diagnosis = document.getElementById('repairDiagnosis').value;
|
|
|
const result = document.getElementById('repairResult').value;
|
|
|
const notes = document.getElementById('repairNotes').value;
|
|
|
const deviceStatus = document.getElementById('repairDeviceStatus').value;
|
|
|
|
|
|
// 更新维修单状态
|
|
|
const repair = repairData.find(r => r.repairId === repairId);
|
|
|
if (repair) {
|
|
|
repair.status = 'completed';
|
|
|
repair.technician = technician;
|
|
|
repair.startTime = startTime;
|
|
|
repair.endTime = new Date().toISOString().split('T')[0];
|
|
|
repair.diagnosis = diagnosis;
|
|
|
repair.actions = [...repairActions];
|
|
|
repair.usedParts = [...repairParts];
|
|
|
repair.result = result;
|
|
|
repair.notes = notes;
|
|
|
repair.deviceStatus = deviceStatus;
|
|
|
repair.processor = getCurrentUser().name; // 只存储用户名
|
|
|
|
|
|
// 更新备件库存(模拟)
|
|
|
updatePartsInventory(repair.usedParts);
|
|
|
|
|
|
// 重新加载维修数据
|
|
|
loadRepairData();
|
|
|
|
|
|
alert('维修处理结果已提交!');
|
|
|
closeProcessRepairModal();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 更新备件库存(模拟函数)
|
|
|
function updatePartsInventory(usedParts) {
|
|
|
usedParts.forEach(usedPart => {
|
|
|
const part = partsData.find(p => p.partId === usedPart.partId);
|
|
|
if (part) {
|
|
|
part.stock = Math.max(0, part.stock - usedPart.quantity);
|
|
|
}
|
|
|
});
|
|
|
// 重新加载备件数据
|
|
|
loadPartsData();
|
|
|
}
|
|
|
|
|
|
// 维修照片预览功能
|
|
|
document.getElementById('repairPhotos').addEventListener('change', function (e) {
|
|
|
const preview = document.getElementById('repairPhotoPreview');
|
|
|
preview.innerHTML = '';
|
|
|
|
|
|
Array.from(e.target.files).forEach(file => {
|
|
|
if (!file.type.match('image.*')) return;
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = function (e) {
|
|
|
const img = document.createElement('img');
|
|
|
img.src = e.target.result;
|
|
|
img.style.width = '100px';
|
|
|
img.style.height = '100px';
|
|
|
img.style.objectFit = 'cover';
|
|
|
img.style.borderRadius = '4px';
|
|
|
img.style.margin = '5px';
|
|
|
preview.appendChild(img);
|
|
|
};
|
|
|
reader.readAsDataURL(file);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
// 获取当前用户(模拟函数,实际应用中应从登录信息获取)
|
|
|
function getCurrentUser() {
|
|
|
// 这里只是一个示例,实际应用中应该返回当前登录的用户信息
|
|
|
return {
|
|
|
id: "current_user",
|
|
|
name: "当前用户"
|
|
|
};
|
|
|
}
|
|
|
// 查看备件详情
|
|
|
function viewPart(id) {
|
|
|
const part = partsData.find(p => p.partId === id);
|
|
|
if (!part) return;
|
|
|
|
|
|
// 填充查看模态框内容
|
|
|
const detailsContent = document.getElementById('partDetailsContent');
|
|
|
detailsContent.innerHTML = `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">备件编号</div>
|
|
|
<div class="detail-value">${part.partId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">备件名称</div>
|
|
|
<div class="detail-value">${part.name}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">适用设备</div>
|
|
|
<div class="detail-value">${part.device}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">当前库存</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${part.stock < part.minStock ? 'status-fault' : 'status-online'}">
|
|
|
${part.stock}
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">最低库存</div>
|
|
|
<div class="detail-value">${part.minStock}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">单价</div>
|
|
|
<div class="detail-value">¥${part.price}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">供应商</div>
|
|
|
<div class="detail-value">${part.supplier}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">库存状态</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${part.stock < part.minStock ? 'status-fault' : 'status-online'}">
|
|
|
${part.stock < part.minStock ? '库存不足' : '库存正常'}
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">备件描述</div>
|
|
|
<div class="detail-value" style="min-height: 60px;">
|
|
|
${getPartDescription(part.partId)}
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
// 显示查看模态框
|
|
|
document.getElementById('viewPartModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭查看备件详情模态框
|
|
|
function closeViewPartModal() {
|
|
|
document.getElementById('viewPartModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取备件描述
|
|
|
function getPartDescription(partId) {
|
|
|
const descriptions = {
|
|
|
'P001': '无人船专用锂电池,容量5000mAh,电压24V,适用于UC系列无人船',
|
|
|
'P002': '无人机备用螺旋桨,材质为碳纤维,尺寸8英寸,适用于UD系列无人机',
|
|
|
'P003': '高清摄像头镜头,200万像素,防水防雾,适用于IC系列智能摄像头',
|
|
|
'P004': '转运车实心轮胎,尺寸18英寸,耐磨防滑,适用于MT系列转运车'
|
|
|
};
|
|
|
return descriptions[partId] || '暂无详细描述';
|
|
|
}
|
|
|
|
|
|
// 全局变量存储采购记录
|
|
|
let purchaseRecords = [];
|
|
|
|
|
|
// 打开采购模态框
|
|
|
function orderPart(id) {
|
|
|
const part = partsData.find(p => p.partId === id);
|
|
|
if (!part) return;
|
|
|
|
|
|
// 填充采购表单
|
|
|
document.getElementById('orderPartId').value = part.partId;
|
|
|
document.getElementById('orderPartName').value = part.name;
|
|
|
document.getElementById('orderPartDevice').value = part.device;
|
|
|
document.getElementById('orderCurrentStock').value = part.stock;
|
|
|
document.getElementById('orderMinStock').value = part.minStock;
|
|
|
document.getElementById('orderUnitPrice').value = `¥${part.price}`;
|
|
|
document.getElementById('orderSupplier').value = part.supplier;
|
|
|
|
|
|
// 设置默认预计到货时间为3天后
|
|
|
const threeDaysLater = new Date();
|
|
|
threeDaysLater.setDate(threeDaysLater.getDate() + 3);
|
|
|
document.getElementById('orderExpectedDate').valueAsDate = threeDaysLater;
|
|
|
|
|
|
// 计算总价
|
|
|
updateOrderTotalPrice();
|
|
|
|
|
|
// 显示采购模态框
|
|
|
document.getElementById('orderPartModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 更新采购总价
|
|
|
function updateOrderTotalPrice() {
|
|
|
const quantity = parseInt(document.getElementById('orderQuantity').value) || 0;
|
|
|
const priceText = document.getElementById('orderUnitPrice').value;
|
|
|
const price = parseInt(priceText.replace(/[^0-9]/g, '')) || 0;
|
|
|
const total = quantity * price;
|
|
|
|
|
|
document.getElementById('orderTotalPrice').value = `¥${total}`;
|
|
|
}
|
|
|
|
|
|
// 关闭采购模态框
|
|
|
function closeOrderPartModal() {
|
|
|
document.getElementById('orderPartModal').style.display = 'none';
|
|
|
document.getElementById('orderPartForm').reset();
|
|
|
}
|
|
|
|
|
|
// 处理采购表单提交
|
|
|
document.getElementById('orderPartForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const partId = document.getElementById('orderPartId').value;
|
|
|
const quantity = parseInt(document.getElementById('orderQuantity').value);
|
|
|
const purchaser = document.getElementById('orderPurchaser').value;
|
|
|
const expectedDate = document.getElementById('orderExpectedDate').value;
|
|
|
const notes = document.getElementById('orderNotes').value;
|
|
|
|
|
|
// 创建采购记录
|
|
|
const record = {
|
|
|
orderId: 'PO-' + new Date().getTime(),
|
|
|
partId: partId,
|
|
|
partName: document.getElementById('orderPartName').value,
|
|
|
quantity: quantity,
|
|
|
purchaser: purchaser,
|
|
|
orderDate: new Date().toISOString().split('T')[0],
|
|
|
expectedDate: expectedDate,
|
|
|
status: 'pending',
|
|
|
notes: notes
|
|
|
};
|
|
|
|
|
|
// 添加到采购记录
|
|
|
purchaseRecords.push(record);
|
|
|
|
|
|
alert(`采购单已提交!采购单号: ${record.orderId}`);
|
|
|
closeOrderPartModal();
|
|
|
|
|
|
// 这里可以添加实际业务逻辑,如发送到后端等
|
|
|
});
|
|
|
|
|
|
// 查看报废申请详情
|
|
|
function viewDisposal(id) {
|
|
|
const disposal = disposalData.find(d => d.applicationId === id);
|
|
|
if (!disposal) return;
|
|
|
|
|
|
// 填充查看模态框内容
|
|
|
const detailsContent = document.getElementById('disposalDetailsContent');
|
|
|
detailsContent.innerHTML = `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">申请编号</div>
|
|
|
<div class="detail-value">${disposal.applicationId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备编号</div>
|
|
|
<div class="detail-value">${disposal.deviceId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value">${disposal.deviceName}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">报废原因</div>
|
|
|
<div class="detail-value">${disposal.reason}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">申请人</div>
|
|
|
<div class="detail-value">${disposal.applicant}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">申请时间</div>
|
|
|
<div class="detail-value">${disposal.applyTime}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">审批状态</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${getDisposalStatusClass(disposal.status)}">
|
|
|
${getDisposalStatusName(disposal.status)}
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">技术评估</div>
|
|
|
<div class="detail-value">${disposal.evaluation}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">处置方式</div>
|
|
|
<div class="detail-value">${disposal.disposalMethod || '待确定'}</div>
|
|
|
</div>
|
|
|
${disposal.approvalComments ? `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">审批意见</div>
|
|
|
<div class="detail-value">${disposal.approvalComments}</div>
|
|
|
</div>
|
|
|
` : ''}
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">设备照片</div>
|
|
|
<div class="detail-value" style="min-height: 100px; background: #f5f9ff; display: flex; align-items: center; justify-content: center; border-radius: 6px;">
|
|
|
<i class="fas fa-camera" style="font-size: 24px; color: #a8c0e0;"></i>
|
|
|
<span style="margin-left: 10px; color: #4a6fa5;">设备照片预览区域</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
// 显示查看模态框
|
|
|
document.getElementById('viewDisposalModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭查看报废详情模态框
|
|
|
function closeViewDisposalModal() {
|
|
|
document.getElementById('viewDisposalModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取报废状态名称
|
|
|
function getDisposalStatusName(status) {
|
|
|
const statusNames = {
|
|
|
'pending': '待审批',
|
|
|
'approved': '已批准',
|
|
|
'rejected': '已驳回'
|
|
|
};
|
|
|
return statusNames[status] || status;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取报废状态类名
|
|
|
function getDisposalStatusClass(status) {
|
|
|
const statusClasses = {
|
|
|
'pending': 'status-maintenance',
|
|
|
'approved': 'status-online',
|
|
|
'rejected': 'status-fault'
|
|
|
};
|
|
|
return statusClasses[status] || '';
|
|
|
}
|
|
|
|
|
|
|
|
|
// 打开审批模态框
|
|
|
function approveDisposal(id) {
|
|
|
const disposal = disposalData.find(d => d.applicationId === id);
|
|
|
if (!disposal) return;
|
|
|
|
|
|
// 填充审批表单
|
|
|
document.getElementById('approveDisposalId').value = disposal.applicationId;
|
|
|
document.getElementById('approveAppId').textContent = disposal.applicationId;
|
|
|
document.getElementById('approveDeviceName').textContent = disposal.deviceName;
|
|
|
document.getElementById('approveReason').textContent = disposal.reason;
|
|
|
document.getElementById('approveApplicant').textContent = disposal.applicant;
|
|
|
document.getElementById('approveApplyTime').textContent = disposal.applyTime;
|
|
|
document.getElementById('approveEvaluation').textContent = disposal.evaluation;
|
|
|
|
|
|
// 重置表单
|
|
|
document.getElementById('approveDisposalForm').reset();
|
|
|
|
|
|
// 如果是已审批的,禁用表单并显示结果
|
|
|
if (disposal.status !== 'pending') {
|
|
|
document.querySelectorAll('#approveDisposalForm input, #approveDisposalForm select, #approveDisposalForm textarea')
|
|
|
.forEach(el => el.disabled = true);
|
|
|
document.querySelector(`input[name="approvalResult"][value="${disposal.status}"]`).checked = true;
|
|
|
document.getElementById('disposalMethod').value = disposal.disposalMethod || '';
|
|
|
document.getElementById('approvalComments').value = disposal.approvalComments || '';
|
|
|
document.getElementById('approver').value = disposal.approver || '';
|
|
|
document.querySelector('#approveDisposalForm button[type="submit"]').style.display = 'none';
|
|
|
} else {
|
|
|
document.querySelectorAll('#approveDisposalForm input, #approveDisposalForm select, #approveDisposalForm textarea')
|
|
|
.forEach(el => el.disabled = false);
|
|
|
document.querySelector('#approveDisposalForm button[type="submit"]').style.display = 'inline-block';
|
|
|
}
|
|
|
|
|
|
// 显示审批模态框
|
|
|
document.getElementById('approveDisposalModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭审批模态框
|
|
|
function closeApproveDisposalModal() {
|
|
|
document.getElementById('approveDisposalModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 处理审批表单提交
|
|
|
document.getElementById('approveDisposalForm').addEventListener('submit', function (e) {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const disposalId = document.getElementById('approveDisposalId').value;
|
|
|
const result = document.querySelector('input[name="approvalResult"]:checked').value;
|
|
|
const disposalMethod = document.getElementById('disposalMethod').value;
|
|
|
const comments = document.getElementById('approvalComments').value;
|
|
|
const approver = document.getElementById('approver').value;
|
|
|
|
|
|
// 更新报废申请状态
|
|
|
const disposal = disposalData.find(d => d.applicationId === disposalId);
|
|
|
if (disposal) {
|
|
|
disposal.status = result;
|
|
|
disposal.disposalMethod = disposalMethod;
|
|
|
disposal.approvalComments = comments;
|
|
|
disposal.approver = approver;
|
|
|
disposal.approveTime = new Date().toISOString().split('T')[0];
|
|
|
|
|
|
// 重新加载报废处置数据
|
|
|
loadDisposalData();
|
|
|
|
|
|
alert(`报废申请已${result === 'approved' ? '批准' : '驳回'}!`);
|
|
|
closeApproveDisposalModal();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
function exportReport() {
|
|
|
alert('导出设备报表');
|
|
|
}
|
|
|
|
|
|
// 页面加载完成后初始化
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
|
|
// 初始化设备数据
|
|
|
devices = [
|
|
|
{
|
|
|
id: 'UC-2025001',
|
|
|
name: '垃圾打捞无人船A01',
|
|
|
type: 'ship',
|
|
|
icon: '🚢',
|
|
|
area: '岳麓区沿岸',
|
|
|
manager: '张三',
|
|
|
status: 'online',
|
|
|
battery: '85%',
|
|
|
workTime: '120h',
|
|
|
lastMaintenance: '2024-01-10'
|
|
|
},
|
|
|
{
|
|
|
id: 'UD-2025001',
|
|
|
name: '巡查无人机B01',
|
|
|
type: 'drone',
|
|
|
icon: '🚁',
|
|
|
area: '天心区沿岸',
|
|
|
manager: '李四',
|
|
|
status: 'offline',
|
|
|
battery: '0%',
|
|
|
workTime: '45h',
|
|
|
lastMaintenance: '2024-01-08'
|
|
|
},
|
|
|
{
|
|
|
id: 'IC-2025001',
|
|
|
name: '智能摄像头C01',
|
|
|
type: 'camera',
|
|
|
icon: '📹',
|
|
|
area: '开福区沿岸',
|
|
|
manager: '王五',
|
|
|
status: 'online',
|
|
|
battery: '正常',
|
|
|
workTime: '720h',
|
|
|
lastMaintenance: '2024-01-05'
|
|
|
},
|
|
|
{
|
|
|
id: 'MT-2025001',
|
|
|
name: '密闭转运车D01',
|
|
|
type: 'truck',
|
|
|
icon: '🚛',
|
|
|
area: '雨花区沿岸',
|
|
|
manager: '赵六',
|
|
|
status: 'maintenance',
|
|
|
battery: '正常',
|
|
|
workTime: '200h',
|
|
|
lastMaintenance: '2024-01-12'
|
|
|
}
|
|
|
];
|
|
|
// 初始化加载设备档案数据
|
|
|
loadDeviceArchive();
|
|
|
|
|
|
// 加载侧边栏数据
|
|
|
loadDeviceAlerts();
|
|
|
loadMaintenanceReminders();
|
|
|
|
|
|
// 设置筛选器事件监听
|
|
|
const deviceTypeFilter = document.getElementById('deviceTypeFilter');
|
|
|
const deviceStatusFilter = document.getElementById('deviceStatusFilter');
|
|
|
const deviceAreaFilter = document.getElementById('deviceAreaFilter');
|
|
|
const deviceSearch = document.getElementById('deviceSearch');
|
|
|
|
|
|
if (deviceTypeFilter) deviceTypeFilter.addEventListener('change', filterDevices);
|
|
|
if (deviceStatusFilter) deviceStatusFilter.addEventListener('change', filterDevices);
|
|
|
if (deviceAreaFilter) deviceAreaFilter.addEventListener('change', filterDevices);
|
|
|
if (deviceSearch) deviceSearch.addEventListener('input', filterDevices);
|
|
|
|
|
|
initCharts();
|
|
|
|
|
|
// 为分析筛选器添加事件监听
|
|
|
document.getElementById('analysisPeriod').addEventListener('change', function () {
|
|
|
const period = this.value;
|
|
|
const dimension = document.getElementById('analysisDimension').value;
|
|
|
updateAnalysisChart(period, dimension);
|
|
|
});
|
|
|
|
|
|
document.getElementById('analysisDimension').addEventListener('change', function () {
|
|
|
const dimension = this.value;
|
|
|
const period = document.getElementById('analysisPeriod').value;
|
|
|
updateAnalysisChart(period, dimension);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
// 设备筛选功能
|
|
|
function filterDevices() {
|
|
|
const typeFilter = document.getElementById('deviceTypeFilter')?.value || 'all';
|
|
|
const statusFilter = document.getElementById('deviceStatusFilter')?.value || 'all';
|
|
|
const areaFilter = document.getElementById('deviceAreaFilter')?.value || 'all';
|
|
|
const searchText = document.getElementById('deviceSearch')?.value.toLowerCase() || '';
|
|
|
|
|
|
const deviceItems = document.querySelectorAll('.device-item');
|
|
|
|
|
|
deviceItems.forEach((item, index) => {
|
|
|
const device = devices[index];
|
|
|
if (!device) return;
|
|
|
|
|
|
const deviceName = device.name.toLowerCase();
|
|
|
const deviceId = device.id.toLowerCase();
|
|
|
const deviceStatus = device.status;
|
|
|
const deviceType = device.type;
|
|
|
const deviceArea = device.area;
|
|
|
|
|
|
let showItem = true;
|
|
|
|
|
|
// 搜索过滤
|
|
|
if (searchText && !deviceName.includes(searchText) && !deviceId.includes(searchText)) {
|
|
|
showItem = false;
|
|
|
}
|
|
|
|
|
|
// 类型过滤
|
|
|
if (typeFilter !== 'all' && deviceType !== typeFilter) {
|
|
|
showItem = false;
|
|
|
}
|
|
|
|
|
|
// 状态过滤
|
|
|
if (statusFilter !== 'all' && deviceStatus !== statusFilter) {
|
|
|
showItem = false;
|
|
|
}
|
|
|
|
|
|
// 区域过滤
|
|
|
if (areaFilter !== 'all' && deviceArea !== areaFilter) {
|
|
|
showItem = false;
|
|
|
}
|
|
|
|
|
|
item.style.display = showItem ? 'block' : 'none';
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 点击模态框外部关闭
|
|
|
window.onclick = function (event) {
|
|
|
const deviceModal = document.getElementById('deviceModal');
|
|
|
const maintenanceModal = document.getElementById('maintenanceModal');
|
|
|
|
|
|
if (event.target === deviceModal) {
|
|
|
closeDeviceModal();
|
|
|
}
|
|
|
if (event.target === maintenanceModal) {
|
|
|
closeMaintenanceModal();
|
|
|
}
|
|
|
|
|
|
if (event.target === document.getElementById('viewDeviceModal')) {
|
|
|
closeViewDeviceModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('editDeviceModal')) {
|
|
|
closeEditDeviceModal();
|
|
|
}
|
|
|
|
|
|
if (event.target === document.getElementById('viewDetailsModal')) {
|
|
|
closeViewDetailsModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('viewMaintenanceModal')) {
|
|
|
closeViewMaintenanceModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('viewRepairModal')) {
|
|
|
closeViewRepairModal();
|
|
|
}
|
|
|
|
|
|
if (event.target === document.getElementById('viewPartModal')) {
|
|
|
closeViewPartModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('orderPartModal')) {
|
|
|
closeOrderPartModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('viewDisposalModal')) {
|
|
|
closeViewDisposalModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('approveDisposalModal')) {
|
|
|
closeApproveDisposalModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('executeMaintenanceModal')) {
|
|
|
closeExecuteMaintenanceModal();
|
|
|
}
|
|
|
if (event.target === document.getElementById('processRepairModal')) {
|
|
|
closeProcessRepairModal();
|
|
|
}
|
|
|
}
|
|
|
// 为采购数量输入框添加事件监听
|
|
|
document.getElementById('orderQuantity').addEventListener('input', updateOrderTotalPrice);
|
|
|
|
|
|
// 全局变量存储图表实例
|
|
|
let distributionChart = null;
|
|
|
let analysisChart = null;
|
|
|
|
|
|
// 初始化图表
|
|
|
function initCharts() {
|
|
|
// 设备分布图表数据
|
|
|
const distributionData = {
|
|
|
labels: ['打捞无人船', '巡查无人机', '智能摄像头', '密闭转运车'],
|
|
|
datasets: [{
|
|
|
data: [15, 10, 12, 8],
|
|
|
backgroundColor: [
|
|
|
'#2a7de1',
|
|
|
'#1a4b8c',
|
|
|
'#4a6fa5',
|
|
|
'#a8c0e0'
|
|
|
],
|
|
|
borderColor: '#ffffff',
|
|
|
borderWidth: 1
|
|
|
}]
|
|
|
};
|
|
|
|
|
|
// 设备效能分析图表数据
|
|
|
const analysisData = {
|
|
|
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
|
|
datasets: [
|
|
|
{
|
|
|
label: '打捞无人船',
|
|
|
data: [85, 78, 90, 82, 88, 92, 87, 91, 89, 93, 90, 95],
|
|
|
borderColor: '#2a7de1',
|
|
|
backgroundColor: 'rgba(42, 125, 225, 0.1)',
|
|
|
tension: 0.3,
|
|
|
fill: true
|
|
|
},
|
|
|
{
|
|
|
label: '巡查无人机',
|
|
|
data: [78, 82, 85, 80, 83, 87, 85, 88, 86, 90, 92, 94],
|
|
|
borderColor: '#1a4b8c',
|
|
|
backgroundColor: 'rgba(26, 75, 140, 0.1)',
|
|
|
tension: 0.3,
|
|
|
fill: true
|
|
|
},
|
|
|
{
|
|
|
label: '智能摄像头',
|
|
|
data: [92, 90, 88, 91, 93, 95, 94, 96, 95, 97, 96, 98],
|
|
|
borderColor: '#4a6fa5',
|
|
|
backgroundColor: 'rgba(74, 111, 165, 0.1)',
|
|
|
tension: 0.3,
|
|
|
fill: true
|
|
|
},
|
|
|
{
|
|
|
label: '密闭转运车',
|
|
|
data: [80, 82, 85, 83, 86, 88, 87, 89, 90, 92, 93, 95],
|
|
|
borderColor: '#a8c0e0',
|
|
|
backgroundColor: 'rgba(168, 192, 224, 0.1)',
|
|
|
tension: 0.3,
|
|
|
fill: true
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
|
|
|
// 创建设备分布图表(饼图)
|
|
|
const distributionCtx = document.getElementById('distributionChart').getContext('2d');
|
|
|
distributionChart = new Chart(distributionCtx, {
|
|
|
type: 'pie',
|
|
|
data: distributionData,
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
maintainAspectRatio: false,
|
|
|
plugins: {
|
|
|
legend: {
|
|
|
position: 'bottom',
|
|
|
labels: {
|
|
|
color: '#1a4b8c',
|
|
|
font: {
|
|
|
size: 12
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
tooltip: {
|
|
|
callbacks: {
|
|
|
label: function (context) {
|
|
|
const label = context.label || '';
|
|
|
const value = context.raw || 0;
|
|
|
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
|
|
const percentage = Math.round((value / total) * 100);
|
|
|
return `${label}: ${value}台 (${percentage}%)`;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 创建设备效能分析图表(折线图)
|
|
|
// 初始化分析图表(空配置,实际数据在loadAnalysisData中加载)
|
|
|
const analysisCtx = document.getElementById('analysisChart').getContext('2d');
|
|
|
analysisChart = new Chart(analysisCtx, {
|
|
|
type: 'line',
|
|
|
data: { labels: [], datasets: [] },
|
|
|
options: {}
|
|
|
});
|
|
|
|
|
|
// 初始加载分析数据
|
|
|
loadAnalysisData();
|
|
|
}
|
|
|
|
|
|
// 更新分析图表数据 - 简洁线条风格
|
|
|
function updateAnalysisChart(period, dimension) {
|
|
|
// 简洁线条配色方案
|
|
|
const lineColors = [
|
|
|
'#36A2EB', // 亮蓝色
|
|
|
'#FF6384', // 粉色
|
|
|
'#4BC0C0', // 蓝绿色
|
|
|
'#FF9F40', // 橙色
|
|
|
'#9966FF', // 紫色
|
|
|
'#FFCD56' // 黄色
|
|
|
];
|
|
|
|
|
|
// 这里可以根据不同的统计周期和分析维度更新图表数据
|
|
|
let newData;
|
|
|
|
|
|
if (dimension === 'type') {
|
|
|
// 按设备类型分析 - 简洁线条风格
|
|
|
newData = {
|
|
|
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
|
|
datasets: [
|
|
|
{
|
|
|
label: '打捞无人船',
|
|
|
data: [85, 78, 90, 82, 88, 92, 87, 91, 89, 93, 90, 95],
|
|
|
borderColor: lineColors[0],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[0],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0.4,
|
|
|
fill: false
|
|
|
},
|
|
|
{
|
|
|
label: '巡查无人机',
|
|
|
data: [78, 82, 85, 80, 83, 87, 85, 88, 86, 90, 92, 94],
|
|
|
borderColor: lineColors[1],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[1],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0.4,
|
|
|
fill: false
|
|
|
},
|
|
|
{
|
|
|
label: '智能摄像头',
|
|
|
data: [92, 90, 88, 91, 93, 95, 94, 96, 95, 97, 96, 98],
|
|
|
borderColor: lineColors[2],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[2],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0.4,
|
|
|
fill: false
|
|
|
},
|
|
|
{
|
|
|
label: '密闭转运车',
|
|
|
data: [80, 82, 85, 83, 86, 88, 87, 89, 90, 92, 93, 95],
|
|
|
borderColor: lineColors[3],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[3],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0.4,
|
|
|
fill: false
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
} else if (dimension === 'area') {
|
|
|
// 按区域分布分析 - 简洁线条风格
|
|
|
newData = {
|
|
|
labels: ['岳麓区沿岸', '天心区沿岸', '开福区沿岸', '雨花区沿岸'],
|
|
|
datasets: [
|
|
|
{
|
|
|
label: '设备在线率',
|
|
|
data: [92, 85, 88, 90],
|
|
|
borderColor: lineColors[0],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[0],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0,
|
|
|
fill: false
|
|
|
},
|
|
|
{
|
|
|
label: '设备利用率',
|
|
|
data: [78, 82, 80, 85],
|
|
|
borderColor: lineColors[1],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[1],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0,
|
|
|
fill: false
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
} else if (dimension === 'usage') {
|
|
|
// 按使用频率分析 - 简洁线条风格
|
|
|
newData = {
|
|
|
labels: ['打捞无人船', '巡查无人机', '智能摄像头', '密闭转运车'],
|
|
|
datasets: [
|
|
|
{
|
|
|
label: '日均使用时长(小时)',
|
|
|
data: [8.5, 6.2, 24, 5.8],
|
|
|
borderColor: lineColors[4],
|
|
|
backgroundColor: 'transparent',
|
|
|
borderWidth: 3,
|
|
|
pointBackgroundColor: '#ffffff',
|
|
|
pointBorderColor: lineColors[4],
|
|
|
pointBorderWidth: 2,
|
|
|
pointRadius: 4,
|
|
|
pointHoverRadius: 6,
|
|
|
tension: 0,
|
|
|
fill: false
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
}
|
|
|
|
|
|
// 更新图表配置
|
|
|
const options = {
|
|
|
responsive: true,
|
|
|
maintainAspectRatio: false,
|
|
|
plugins: {
|
|
|
legend: {
|
|
|
position: 'bottom',
|
|
|
labels: {
|
|
|
color: '#555',
|
|
|
font: {
|
|
|
size: 12,
|
|
|
family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"
|
|
|
},
|
|
|
padding: 20,
|
|
|
usePointStyle: true,
|
|
|
pointStyle: 'circle'
|
|
|
}
|
|
|
},
|
|
|
tooltip: {
|
|
|
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
|
|
titleColor: '#333',
|
|
|
bodyColor: '#666',
|
|
|
borderColor: '#ddd',
|
|
|
borderWidth: 1,
|
|
|
padding: 12,
|
|
|
displayColors: true,
|
|
|
usePointStyle: true,
|
|
|
callbacks: {
|
|
|
label: function (context) {
|
|
|
let label = context.dataset.label || '';
|
|
|
if (label) {
|
|
|
label += ': ';
|
|
|
}
|
|
|
if (dimension === 'usage') {
|
|
|
label += context.raw + '小时';
|
|
|
} else {
|
|
|
label += context.raw + '%';
|
|
|
}
|
|
|
return label;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
scales: {
|
|
|
y: {
|
|
|
beginAtZero: dimension === 'usage' ? true : false,
|
|
|
min: dimension === 'usage' ? 0 : 70,
|
|
|
max: dimension === 'usage' ? 25 : 100,
|
|
|
ticks: {
|
|
|
color: '#888',
|
|
|
font: {
|
|
|
size: 11
|
|
|
},
|
|
|
callback: function (value) {
|
|
|
return dimension === 'usage' ? value + 'h' : value + '%';
|
|
|
}
|
|
|
},
|
|
|
grid: {
|
|
|
color: 'rgba(0, 0, 0, 0.05)',
|
|
|
drawBorder: false
|
|
|
}
|
|
|
},
|
|
|
x: {
|
|
|
ticks: {
|
|
|
color: '#888',
|
|
|
font: {
|
|
|
size: 11
|
|
|
}
|
|
|
},
|
|
|
grid: {
|
|
|
display: false,
|
|
|
drawBorder: false
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
elements: {
|
|
|
line: {
|
|
|
cubicInterpolationMode: 'monotone'
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 如果是区域和使用频率维度,使用折线图而非柱状图
|
|
|
analysisChart.config.type = 'line';
|
|
|
|
|
|
// 更新图表数据和配置
|
|
|
analysisChart.data = newData;
|
|
|
analysisChart.options = options;
|
|
|
analysisChart.update();
|
|
|
}
|
|
|
// 辅助函数:将十六进制颜色转换为RGBA
|
|
|
function hexToRgba(hex, alpha) {
|
|
|
const r = parseInt(hex.slice(1, 3), 16);
|
|
|
const g = parseInt(hex.slice(3, 5), 16);
|
|
|
const b = parseInt(hex.slice(5, 7), 16);
|
|
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
|
}
|
|
|
|
|
|
|
|
|
// 全局变量存储设备数据
|
|
|
let devices = [
|
|
|
{
|
|
|
id: 'UC-2025001',
|
|
|
name: '垃圾打捞无人船A01',
|
|
|
type: 'ship',
|
|
|
icon: '🚢',
|
|
|
area: '岳麓区沿岸',
|
|
|
manager: '张三',
|
|
|
status: 'online',
|
|
|
battery: '85%',
|
|
|
workTime: '120h',
|
|
|
lastMaintenance: '2024-01-10'
|
|
|
},
|
|
|
{
|
|
|
id: 'UD-2025001',
|
|
|
name: '巡查无人机B01',
|
|
|
type: 'drone',
|
|
|
icon: '🚁',
|
|
|
area: '天心区沿岸',
|
|
|
manager: '李四',
|
|
|
status: 'offline',
|
|
|
battery: '0%',
|
|
|
workTime: '45h',
|
|
|
lastMaintenance: '2024-01-08'
|
|
|
},
|
|
|
{
|
|
|
id: 'IC-2025001',
|
|
|
name: '智能摄像头C01',
|
|
|
type: 'camera',
|
|
|
icon: '📹',
|
|
|
area: '开福区沿岸',
|
|
|
manager: '王五',
|
|
|
status: 'online',
|
|
|
battery: '正常',
|
|
|
workTime: '720h',
|
|
|
lastMaintenance: '2024-01-05'
|
|
|
},
|
|
|
{
|
|
|
id: 'MT-2025001',
|
|
|
name: '密闭转运车D01',
|
|
|
type: 'truck',
|
|
|
icon: '🚛',
|
|
|
area: '雨花区沿岸',
|
|
|
manager: '赵六',
|
|
|
status: 'maintenance',
|
|
|
battery: '正常',
|
|
|
workTime: '200h',
|
|
|
lastMaintenance: '2024-01-12'
|
|
|
}
|
|
|
];
|
|
|
// 添加新设备到设备列表
|
|
|
function addDeviceToArchive(device) {
|
|
|
devices.push(device);
|
|
|
}
|
|
|
|
|
|
// 地图相关变量
|
|
|
let deviceMap;
|
|
|
let deviceVectorLayer;
|
|
|
// 在script标签中添加以下代码
|
|
|
var TDT_API_KEY = "a4f4b6000cb9d1bf148bb77452000f30";
|
|
|
// 初始化设备地图
|
|
|
function initDeviceMap(deviceId, location) {
|
|
|
const device = monitorData.find(d => d.id === deviceId);
|
|
|
if (!device) return;
|
|
|
|
|
|
document.getElementById('deviceLocationTitle').textContent = `${device.name} - ${device.location}`;
|
|
|
|
|
|
// 创建地图图层
|
|
|
const 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'
|
|
|
});
|
|
|
|
|
|
const 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'
|
|
|
});
|
|
|
|
|
|
const 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'
|
|
|
});
|
|
|
|
|
|
// 创建地图
|
|
|
deviceMap = new ol.Map({
|
|
|
layers: [satelliteLayer, streetLayer, terrainLayer],
|
|
|
target: 'deviceMap',
|
|
|
view: new ol.View({
|
|
|
projection: 'EPSG:4326',
|
|
|
center: [112.955, 28.195],
|
|
|
zoom: 14
|
|
|
})
|
|
|
});
|
|
|
|
|
|
// 添加设备位置标记
|
|
|
deviceVectorLayer = new ol.layer.Vector({
|
|
|
source: new ol.source.Vector(),
|
|
|
style: new ol.style.Style({
|
|
|
image: new ol.style.Circle({
|
|
|
radius: 8,
|
|
|
fill: new ol.style.Fill({ color: '#2a7de1' }),
|
|
|
stroke: new ol.style.Stroke({ color: 'white', width: 2 })
|
|
|
})
|
|
|
})
|
|
|
});
|
|
|
deviceMap.addLayer(deviceVectorLayer);
|
|
|
|
|
|
// 添加设备位置点
|
|
|
const devicePoint = new ol.Feature({
|
|
|
geometry: new ol.geom.Point([112.955, 28.195]),
|
|
|
name: device.name
|
|
|
});
|
|
|
deviceVectorLayer.getSource().addFeature(devicePoint);
|
|
|
|
|
|
// 调整视图以显示设备位置
|
|
|
deviceMap.getView().fit(devicePoint.getGeometry(), { padding: [50, 50, 50, 50], maxZoom: 16 });
|
|
|
}
|
|
|
|
|
|
// 切换地图类型
|
|
|
function switchDeviceMapType(type) {
|
|
|
document.querySelectorAll('#locationModal .map-controls .map-control-btn').forEach(btn => {
|
|
|
btn.classList.toggle('active', btn.dataset.view === type);
|
|
|
});
|
|
|
|
|
|
deviceMap.getLayers().forEach(layer => {
|
|
|
if (layer.get('name')) {
|
|
|
layer.setVisible(layer.get('name') === type);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 打开定位模态框
|
|
|
function locateDevice(id) {
|
|
|
const device = monitorData.find(d => d.id === id);
|
|
|
if (!device) return;
|
|
|
|
|
|
document.getElementById('locationModal').style.display = 'block';
|
|
|
//document.getElementById('deviceCoordinates').textContent = `经度: ${device.location[0]}, 纬度: ${device.location[1]}`;
|
|
|
document.getElementById('deviceLocationTitle').textContent = `${device.name}`;
|
|
|
|
|
|
setTimeout(() => {
|
|
|
if (!deviceMap) {
|
|
|
initDeviceMap(id, device.location);
|
|
|
} else {
|
|
|
deviceMap.setTarget('deviceMap');
|
|
|
deviceMap.updateSize();
|
|
|
}
|
|
|
}, 100);
|
|
|
}
|
|
|
|
|
|
// 关闭定位模态框
|
|
|
function closeLocationModal() {
|
|
|
document.getElementById('locationModal').style.display = 'none';
|
|
|
if (deviceMap) {
|
|
|
deviceMap.setTarget(undefined);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 查看维护计划详情
|
|
|
function viewMaintenance(id) {
|
|
|
const maintenance = maintenanceData.find(m => m.planId === id);
|
|
|
if (!maintenance) return;
|
|
|
|
|
|
// 填充查看模态框内容
|
|
|
const detailsContent = document.getElementById('maintenanceDetailsContent');
|
|
|
detailsContent.innerHTML = `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">计划编号</div>
|
|
|
<div class="detail-value">${maintenance.planId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备编号</div>
|
|
|
<div class="detail-value">${maintenance.deviceId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value">${maintenance.deviceName}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">保养类型</div>
|
|
|
<div class="detail-value">${maintenance.type}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">计划时间</div>
|
|
|
<div class="detail-value">${maintenance.scheduledTime}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">负责人</div>
|
|
|
<div class="detail-value">${maintenance.assignee}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">当前状态</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${getMaintenanceStatusClass(maintenance.status)}">
|
|
|
${getMaintenanceStatusName(maintenance.status)}
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">保养内容</div>
|
|
|
<div class="detail-value" style="min-height: 60px;">
|
|
|
${getMaintenanceContent(maintenance.type)}
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
// 显示查看模态框
|
|
|
document.getElementById('viewMaintenanceModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭查看维护计划详情模态框
|
|
|
function closeViewMaintenanceModal() {
|
|
|
document.getElementById('viewMaintenanceModal').style.display = 'none';
|
|
|
}
|
|
|
// 辅助函数:获取维护状态名称
|
|
|
function getMaintenanceStatusName(status) {
|
|
|
const statusNames = {
|
|
|
'pending': '待执行',
|
|
|
'in_progress': '进行中',
|
|
|
'completed': '已完成'
|
|
|
};
|
|
|
return statusNames[status] || status;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取维护状态类名
|
|
|
function getMaintenanceStatusClass(status) {
|
|
|
const statusClasses = {
|
|
|
'pending': 'status-maintenance',
|
|
|
'in_progress': 'status-online',
|
|
|
'completed': 'status-online'
|
|
|
};
|
|
|
return statusClasses[status] || '';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:根据保养类型获取保养内容
|
|
|
function getMaintenanceContent(type) {
|
|
|
const contents = {
|
|
|
'routine': '1. 设备外观清洁<br>2. 电池检查与充电<br>3. 机械部件润滑<br>4. 功能测试',
|
|
|
'periodic': '1. 全面设备检查<br>2. 关键部件更换<br>3. 系统软件更新<br>4. 性能校准',
|
|
|
'condition': '1. 根据设备使用情况定制保养<br>2. 针对性部件检查<br>3. 预防性维护',
|
|
|
'emergency': '1. 故障诊断<br>2. 紧急维修<br>3. 安全评估'
|
|
|
};
|
|
|
return contents[type] || '根据设备实际情况制定具体保养内容';
|
|
|
}
|
|
|
|
|
|
// 查看故障维修详情
|
|
|
function viewRepair(id) {
|
|
|
const repair = repairData.find(r => r.repairId === id);
|
|
|
if (!repair) return;
|
|
|
|
|
|
// 填充查看模态框内容
|
|
|
const detailsContent = document.getElementById('repairDetailsContent');
|
|
|
detailsContent.innerHTML = `
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">维修单号</div>
|
|
|
<div class="detail-value">${repair.repairId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备编号</div>
|
|
|
<div class="detail-value">${repair.deviceId}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">设备名称</div>
|
|
|
<div class="detail-value">${getDeviceName(repair.deviceId)}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">故障描述</div>
|
|
|
<div class="detail-value">${repair.fault}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">报修人</div>
|
|
|
<div class="detail-value">${repair.reporter}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">维修人员</div>
|
|
|
<div class="detail-value">${repair.repairer}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">报修时间</div>
|
|
|
<div class="detail-value">${getRepairTime(repair.repairId)}</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">紧急程度</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${getPriorityClass(repair.priority)}">
|
|
|
${getPriorityName(repair.priority)}
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<div class="detail-label">当前状态</div>
|
|
|
<div class="detail-value">
|
|
|
<span class="status-badge ${getRepairStatusClass(repair.status)}">
|
|
|
${getRepairStatusName(repair.status)}
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="detail-item" style="grid-column: 1 / -1;">
|
|
|
<div class="detail-label">维修进度</div>
|
|
|
<div class="detail-value" style="min-height: 60px;">
|
|
|
${getRepairProgress(repair.status)}
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
// 显示查看模态框
|
|
|
document.getElementById('viewRepairModal').style.display = 'block';
|
|
|
}
|
|
|
|
|
|
// 关闭查看故障维修详情模态框
|
|
|
function closeViewRepairModal() {
|
|
|
document.getElementById('viewRepairModal').style.display = 'none';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取设备名称
|
|
|
function getDeviceName(deviceId) {
|
|
|
const device = devices.find(d => d.id === deviceId) ||
|
|
|
monitorData.find(d => d.id === deviceId);
|
|
|
return device ? device.name : '未知设备';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取维修时间(模拟数据)
|
|
|
function getRepairTime(repairId) {
|
|
|
// 这里可以根据实际数据获取,现在使用模拟数据
|
|
|
const times = {
|
|
|
'R001': '2024-01-15 09:30',
|
|
|
'R002': '2024-01-14 14:15',
|
|
|
'R003': '2024-01-13 11:20'
|
|
|
};
|
|
|
return times[repairId] || new Date().toLocaleString();
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取紧急程度名称
|
|
|
function getPriorityName(priority) {
|
|
|
const priorityNames = {
|
|
|
'high': '紧急',
|
|
|
'medium': '一般',
|
|
|
'low': '低'
|
|
|
};
|
|
|
return priorityNames[priority] || priority;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取紧急程度类名
|
|
|
function getPriorityClass(priority) {
|
|
|
const priorityClasses = {
|
|
|
'high': 'status-fault',
|
|
|
'medium': 'status-maintenance',
|
|
|
'low': 'status-online'
|
|
|
};
|
|
|
return priorityClasses[priority] || '';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取维修状态名称
|
|
|
function getRepairStatusName(status) {
|
|
|
const statusNames = {
|
|
|
'pending': '待维修',
|
|
|
'in_progress': '维修中',
|
|
|
'completed': '已完成'
|
|
|
};
|
|
|
return statusNames[status] || status;
|
|
|
}
|
|
|
|
|
|
// 辅助函数:获取维修状态类名
|
|
|
function getRepairStatusClass(status) {
|
|
|
const statusClasses = {
|
|
|
'pending': 'status-maintenance',
|
|
|
'in_progress': 'status-online',
|
|
|
'completed': 'status-online'
|
|
|
};
|
|
|
return statusClasses[status] || '';
|
|
|
}
|
|
|
|
|
|
// 辅助函数:根据状态获取维修进度
|
|
|
function getRepairProgress(status) {
|
|
|
const progresses = {
|
|
|
'pending': '维修任务已创建,等待技术人员处理',
|
|
|
'in_progress': '技术人员已开始维修,正在进行故障诊断和修复',
|
|
|
'completed': '维修已完成,设备已恢复正常工作状态'
|
|
|
};
|
|
|
return progresses[status] || '维修状态未知';
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
</body>
|
|
|
|
|
|
</html> |