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

1573 lines
51 KiB
HTML

This file contains ambiguous Unicode characters!

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

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智慧河道管理平台</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/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>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
scrollbar-color: rgba(42, 125, 225, 0.5) rgba(209, 224, 245, 0.3);
scrollbar-width: thin;
}
body {
font-family: 'Microsoft YaHei', 'PingFang SC', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f5f7fa;
min-height: 100vh;
overflow-x: hidden;
color: #333;
}
.dashboard-container {
max-width: 1400px;
margin: 0 auto;
padding: 30px;
min-height: 100vh;
box-sizing: border-box;
}
/* 新增的全局阴影效果 */
.dashboard-container>* {
animation: fadeIn 0.6s ease-out forwards;
opacity: 0;
filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.05));
}
.dashboard-container>*:nth-child(1) {
animation-delay: 0.1s;
}
.dashboard-container>*:nth-child(2) {
animation-delay: 0.3s;
}
/* 头部区域 */
.header-section {
background: linear-gradient(135deg, #1e88e5 0%, #0d47a1 100%);
border-radius: 12px;
padding: 30px;
color: white;
margin-bottom: 30px;
box-shadow: 0 10px 30px rgba(13, 71, 161, 0.2);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.header-left h1 {
margin: 0;
font-size: 2.3em;
font-weight: 600;
color: white;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header-left p {
margin: 12px 0 0 0;
color: rgba(255, 255, 255, 0.8);
font-size: 1.1em;
font-weight: 400;
}
.header-right {
text-align: right;
}
.datetime {
font-size: 1.1em;
margin-bottom: 12px;
color: white;
font-weight: 600;
}
.weather-info {
display: flex;
align-items: center;
gap: 10px;
justify-content: flex-end;
color: rgba(255, 255, 255, 0.9);
font-size: 0.95em;
}
/* 主要内容区域 */
.main-content {
display: grid;
grid-template-columns: 3fr 2fr;
gap: 30px;
margin-bottom: 30px;
}
.left-panel {
display: flex;
flex-direction: column;
gap: 20px;
}
/* 统计概览 */
.stats-overview,
.nav-section,
.quick-actions,
.recent-alerts {
background: #ffffff;
border-radius: 12px;
padding: 30px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: 1px solid rgba(0, 0, 0, 0.03);
}
.stats-overview:hover,
.nav-section:hover,
.quick-actions:hover,
.recent-alerts:hover {
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.12);
transform: translateY(-3px);
}
.stats-overview h2 {
margin: 0 0 25px 0;
color: #2c3e50;
font-size: 1.4em;
font-weight: 600;
text-align: left;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-top: 20px;
width: 100%;
}
.stat-card {
background: #ffffff;
border-radius: 12px;
padding: 25px 20px;
text-align: center;
position: relative;
overflow: hidden;
min-height: 140px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.03);
}
.stat-card:hover {
transform: translateY(-8px);
box-shadow: 0 15px 30px rgba(30, 136, 229, 0.15);
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #3498db, #2ecc71);
}
.stat-icon {
font-size: 2.2em;
background: linear-gradient(135deg, #3498db, #2ecc71);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 12px;
display: block;
}
.stat-number {
font-size: 2.2em;
font-weight: 700;
background: linear-gradient(135deg, #2c3e50, #3498db);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 8px;
line-height: 1;
display: block;
}
.stat-label {
color: #7f8c8d;
font-size: 0.9em;
margin: 0;
display: block;
white-space: nowrap;
font-weight: 500;
}
/* 功能导航 */
.nav-section {
background: #ffffff;
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.nav-section h2 {
margin: 0 0 25px 0;
color: #2c3e50;
font-size: 1.4em;
font-weight: 600;
text-align: left;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.nav-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
margin-top: 20px;
}
.nav-card {
background: #ffffff;
border-radius: 12px;
padding: 25px 20px;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
min-height: 160px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.03);
}
.nav-card:hover {
animation: float 2s ease-in-out infinite;
box-shadow: 0 15px 30px rgba(30, 136, 229, 0.15);
}
.nav-card i {
font-size: 2.2em;
background: linear-gradient(135deg, #3498db, #2ecc71);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 12px;
}
.nav-card h3 {
margin: 8px 0;
color: #2c3e50;
font-size: 1.1em;
font-weight: 600;
}
.nav-card p {
color: #7f8c8d;
font-size: 0.85em;
margin: 0;
line-height: 1.4;
}
/* 右侧面板 */
.right-panel {
display: flex;
flex-direction: column;
gap: 20px;
}
/* 快速操作 */
.quick-actions {
background: #ffffff;
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.quick-actions h3 {
margin: 0 0 25px 0;
color: #2c3e50;
font-size: 1.4em;
font-weight: 600;
text-align: left;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.action-btn {
display: block;
width: 100%;
padding: 15px;
margin-bottom: 12px;
background: linear-gradient(135deg, #1e88e5, #0d47a1);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
text-align: center;
font-weight: 500;
box-shadow: 0 5px 15px rgba(30, 136, 229, 0.3);
position: relative;
overflow: hidden;
}
.action-btn:hover {
background: linear-gradient(135deg, #1565c0, #0a3a7a);
box-shadow: 0 8px 20px rgba(30, 136, 229, 0.4);
transform: translateY(-3px);
}
/* 最近预警 */
.recent-alerts {
background: #ffffff;
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
height: 400px;
}
.recent-alerts h3 {
margin: 0 0 25px 0;
color: #2c3e50;
font-size: 1.4em;
font-weight: 600;
text-align: left;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.alert-item {
display: flex;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.alert-item:hover {
transform: translateX(8px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
background: #f8f9fa;
}
.alert-item:last-child {
border-bottom: none;
}
.alert-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-size: 1.2em;
}
.alert-high {
background: #ffebee;
color: #c62828;
}
.alert-medium {
background: #fff3e0;
color: #ef6c00;
}
.alert-low {
background: #e8f5e8;
color: #2e7d32;
}
.alert-content {
flex: 1;
}
.alert-title {
font-weight: bold;
margin-bottom: 5px;
font-size: 0.9em;
}
.alert-time {
color: #666;
font-size: 0.8em;
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
@keyframes ripple {
to {
transform: scale(4);
opacity: 0;
}
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
/* 响应式设计 */
@media (max-width: 1200px) {
.main-content {
grid-template-columns: 1fr;
gap: 20px;
}
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 15px;
}
.nav-cards {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
}
@media (max-width: 768px) {
.dashboard-container {
padding: 20px;
}
.header-section {
background: linear-gradient(90deg, #3498db 0%, #2ecc71 100%);
padding: 20px;
margin-bottom: 20px;
}
.header-content {
flex-direction: column;
text-align: center;
gap: 15px;
}
.header-left h1 {
font-size: 2em;
}
.header-right {
text-align: center;
}
.stats-grid,
.nav-cards {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.stat-card,
.nav-card {
padding: 15px;
min-height: 120px;
}
}
@media (max-width: 480px) {
.dashboard-container {
padding: 15px;
}
.stats-grid,
.nav-cards {
grid-template-columns: 1fr;
gap: 8px;
}
.nav-card {
padding: 12px;
}
.nav-card i {
font-size: 2em;
margin-bottom: 10px;
}
.nav-card h3 {
font-size: 1em;
margin: 8px 0;
}
.nav-card p {
font-size: 0.8em;
}
.header-left h1 {
font-size: 1.8em;
}
.weather-info {
flex-direction: column;
gap: 5px;
}
}
/* 模态框头部样式 */
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.modal-header h2 {
color: #1a4b8c;
font-size: 20px;
margin: 0;
}
/* 表单网格布局 */
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
/* 表单操作按钮 */
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #e1e9f5;
}
.btn-secondary {
background: rgba(108, 117, 125, 0.1);
color: #6c757d;
border: 1px solid rgba(108, 117, 125, 0.3);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.modal-content {
background: #ffffff;
margin: 5% auto;
padding: 30px;
border-radius: 15px;
width: 80%;
max-width: 600px;
box-shadow: 0 10px 30px rgba(0, 75, 150, 0.2);
}
.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;
}
.form-group {
margin-bottom: 15px;
}
.btn-primary {
background: linear-gradient(135deg, #2a7de1, #1a4b8c);
color: white;
}
.btn-secondary {
background: linear-gradient(135deg, #6c757d, #495057);
color: white;
}
.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;
}
/* 地图模态框样式 开始*/
/* 地图模态框样式 */
.map-modal {
display: none;
position: fixed;
z-index: 2000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.map-modal-content {
background: #ffffff;
margin: 5% auto;
padding: 20px;
border-radius: 15px;
width: 80%;
max-width: 900px;
box-shadow: 0 10px 30px rgba(0, 75, 150, 0.2);
display: flex;
flex-direction: column;
height: 80vh;
}
.map-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.map-container {
flex: 1;
height: 100%;
position: relative;
border: 1px solid #e1e9f5;
border-radius: 8px;
overflow: hidden;
}
#locationMap {
width: 100%;
height: 100%;
}
.map-controls {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 5px;
}
/* 地图控制按钮样式 */
.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-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #e1e9f5;
}
.location-input-group {
display: flex;
align-items: center;
}
.location-input-group input {
flex: 1;
}
.location-input-group .map-icon {
margin-left: 8px;
cursor: pointer;
color: #2a7de1;
font-size: 18px;
transition: all 0.3s;
}
.location-input-group .map-icon:hover {
color: #1a4b8c;
transform: scale(1.1);
}
.coordinates-display {
margin-top: 10px;
font-size: 12px;
color: #4a6fa5;
}
.map-instruction {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.9);
padding: 8px 12px;
border-radius: 4px;
border: 1px solid #d1e0f5;
font-size: 12px;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.modal-content {
background: #ffffff;
margin: 5% auto;
padding: 30px;
border-radius: 15px;
width: 80%;
max-width: 600px;
box-shadow: 0 10px 30px rgba(0, 75, 150, 0.2);
}
.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;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #1a4b8c;
font-size: 14px;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #d1e0f5;
border-radius: 6px;
background: #f5f9ff;
color: #1a4b8c;
font-size: 14px;
}
.form-group textarea {
height: 80px;
resize: vertical;
}
.form-actions {
text-align: right;
margin-top: 20px;
}
.image-preview-container img {
max-width: 80px;
max-height: 80px;
border-radius: 6px;
margin-right: 8px;
}
.image-preview-container {
max-height: 120px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
scrollbar-color: rgba(42, 125, 225, 0.5) rgba(209, 224, 245, 0.3);
scrollbar-width: thin;
}
.form-grid-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px 24px;
margin-bottom: 18px;
}
@media (max-width: 900px) {
.form-grid-3 {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 600px) {
.form-grid-3 {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="dashboard-container">
<!-- 头部区域 -->
<div class="header-section">
<div class="header-content">
<div class="header-left">
<h1>🌊 智慧河道管理平台</h1>
<p>实时监控 · 智能预警 · 高效管理</p>
</div>
<div class="header-right">
<div class="datetime" id="datetime"></div>
<div class="weather-info">
<i class="fas fa-sun"></i>
<span>晴朗 25°C</span>
<span>|</span>
<span>湿度 65%</span>
</div>
</div>
</div>
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<!-- 左侧面板 -->
<div class="left-panel">
<!-- 统计概览 -->
<div class="stats-overview">
<h2><i class="fas fa-chart-line"></i> 实时统计概览</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-exclamation-triangle"></i></div>
<div class="stat-number" id="alertCount">12</div>
<div class="stat-label">活跃预警</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-clipboard-list"></i></div>
<div class="stat-number" id="workorderCount">8</div>
<div class="stat-label">待处理工单</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-robot"></i></div>
<div class="stat-number" id="deviceCount">15</div>
<div class="stat-label">在线设备</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-users"></i></div>
<div class="stat-number" id="personnelCount">23</div>
<div class="stat-label">在岗人员</div>
</div>
</div>
</div>
<!-- 功能导航 -->
<div class="nav-section">
<h2><i class="fas fa-th-large"></i> 功能模块</h2>
<div class="nav-cards">
<div class="nav-card" onclick="location.href='monitor.html'">
<i class="fas fa-tv"></i>
<h3>一张图</h3>
<p>实时监控河道情况</p>
</div>
<div class="nav-card" onclick="location.href='workorder.html'">
<i class="fas fa-clipboard-list"></i>
<h3>工单管理</h3>
<p>处理河道清理工单</p>
</div>
<div class="nav-card" onclick="location.href='device.html'">
<i class="fas fa-robot"></i>
<h3>设备管理</h3>
<p>管理清洁设备</p>
</div>
<div class="nav-card" onclick="location.href='patrol.html'">
<i class="fas fa-walking"></i>
<h3>巡检管理</h3>
<p>河道巡检计划与执行</p>
</div>
<div class="nav-card" onclick="location.href='warning.html'">
<i class="fas fa-exclamation-triangle"></i>
<h3>预警管理</h3>
<p>查看河道预警信息</p>
</div>
<div class="nav-card" onclick="location.href='personnel.html'">
<i class="fas fa-users"></i>
<h3>人员管理</h3>
<p>网格化精细管理与绩效考核</p>
</div>
<div class="nav-card" onclick="location.href='transfer.html'">
<i class="fas fa-truck"></i>
<h3>垃圾转运管理</h3>
<p>垃圾转运计划与执行</p>
</div>
<div class="nav-card" onclick="location.href='statistics.html'">
<i class="fas fa-chart-bar"></i>
<h3>统计分析</h3>
<p>数据统计与分析报表</p>
</div>
</div>
</div>
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<!-- 快速操作 -->
<div class="quick-actions">
<h3><i class="fas fa-bolt"></i> 快速操作</h3>
<!-- <a href="workorder.html" class="action-btn">
<i class="fas fa-plus"></i> 新建工单
</a> -->
<a type="button" class="action-btn" onclick="openNewOrderModal()">
<i class="fas fa-plus"></i> 新建工单
</a>
<a href="warning.html" class="action-btn">
<i class="fas fa-eye"></i> 查看预警
</a>
<a href="device.html" class="action-btn">
<i class="fas fa-cog"></i> 设备控制
</a>
<a href="statistics.html" class="action-btn">
<i class="fas fa-download"></i> 导出报表
</a>
</div>
<!-- 最近预警 -->
<div class="recent-alerts">
<h3><i class="fas fa-bell"></i> 最近预警</h3>
<div class="alert-item">
<div class="alert-icon alert-high">
<i class="fas fa-exclamation"></i>
</div>
<div class="alert-content">
<div class="alert-title">水质异常</div>
<div class="alert-time">5分钟前</div>
</div>
</div>
<div class="alert-item">
<div class="alert-icon alert-medium">
<i class="fas fa-wrench"></i>
</div>
<div class="alert-content">
<div class="alert-title">设备故障</div>
<div class="alert-time">15分钟前</div>
</div>
</div>
<div class="alert-item">
<div class="alert-icon alert-low">
<i class="fas fa-info"></i>
</div>
<div class="alert-content">
<div class="alert-title">巡检提醒</div>
<div class="alert-time">30分钟前</div>
</div>
</div>
<div class="alert-item">
<div class="alert-icon alert-medium">
<i class="fas fa-trash"></i>
</div>
<div class="alert-content">
<div class="alert-title">垃圾堆积</div>
<div class="alert-time">1小时前</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 添加地图选择模态框 -->
<div id="locationMapModal" class="map-modal">
<div class="map-modal-content">
<div class="map-header">
<h3><i class="fas fa-map-marker-alt"></i> 选择位置</h3>
<span class="close" onclick="closeLocationMap()">&times;</span>
</div>
<div class="map-container">
<div id="locationMap"></div>
<!-- <div class="map-instruction">请在地图上点击选择位置</div> -->
<div class="map-controls">
<button class="map-control-btn active" data-map-type="satellite"
onclick="switchMapType2('satellite', this)">
<i class="fas fa-satellite"></i> 卫星图
</button>
<button class="map-control-btn" data-map-type="street" onclick="switchMapType2('street', this)">
<i class="fas fa-road"></i> 街道图
</button>
<button class="map-control-btn" data-map-type="terrain" onclick="switchMapType2('terrain', this)">
<i class="fas fa-mountain"></i> 地形图
</button>
</div>
</div>
<div class="map-footer">
<button class="btn btn-secondary" onclick="closeLocationMap()">
<i class="fas fa-times"></i> 取消
</button>
<button class="btn btn-primary" onclick="confirmLocation()">
<i class="fas fa-check"></i> 确定
</button>
</div>
</div>
</div>
<!-- 新建工单模态框 -->
<div id="newOrderModal" class="modal">
<div class="modal-content" style="max-width:1000px;">
<div class="modal-header">
<h2><i class="fas fa-plus-circle"></i> 新建工单</h2>
<span class="close" onclick="closeNewOrderModal()">&times;</span>
</div>
<form id="newOrderForm" class="order-modal-content">
<div class="form-grid-3">
<div class="form-group">
<label for="orderType"><i class="fas fa-tags"></i> 工单类型</label>
<select id="orderType" required>
<option value="">请选择类型</option>
<option value="garbage">🗑️ 垃圾清理</option>
<option value="pollution">💧 水质污染</option>
<option value="facility">🔧 设施维护</option>
<option value="emergency">🚨 应急处理</option>
<option value="vegetation">🌿 植被管理</option>
<option value="safety">⚠️ 安全隐患</option>
</select>
</div>
<div class="form-group">
<label for="orderPriority"><i class="fas fa-exclamation-triangle"></i> 优先级</label>
<select id="orderPriority" required>
<option value="">请选择优先级</option>
<option value="low">🟢 低优先级</option>
<option value="medium">🟡 中优先级</option>
<option value="high">🔴 高优先级</option>
</select>
</div>
<div class="form-group">
<label for="orderLocation"><i class="fas fa-map-marker-alt"></i> 事件位置</label>
<input type="text" id="orderLocation" placeholder="如:河道名称或地标" required>
</div>
<div class="form-group">
<label for="orderGps"><i class="fas fa-crosshairs"></i> GPS坐标</label>
<div class="location-input-group">
<input type="text" id="orderGps" placeholder="经度,纬度" required readonly>
<i class="fas fa-map-marked-alt map-icon" onclick="openLocationMap()"></i>
</div>
</div>
<div class="form-group">
<label for="orderReporter"><i class="fas fa-user"></i> 上报人</label>
<input type="text" id="orderReporter" placeholder="请输入上报人姓名" required>
</div>
<div class="form-group">
<label for="orderContact"><i class="fas fa-phone"></i> 联系方式</label>
<input type="tel" id="orderContact" placeholder="请输入联系电话">
</div>
<div class="form-group">
<label for="orderSource"><i class="fas fa-source"></i> 工单来源</label>
<select id="orderSource" required>
<option value="">请选择来源</option>
<option value="ai">🤖 AI智能识别</option>
<option value="patrol">👮 巡查上报</option>
<option value="public">📱 公众反馈</option>
<option value="manual">✋ 手动创建</option>
</select>
</div>
</div>
<div class="form-group">
<label for="orderDescription"><i class="fas fa-edit"></i> 问题描述</label>
<textarea id="orderDescription" rows="4" placeholder="请详细描述发现的问题,包括具体情况、影响范围等" required></textarea>
</div>
<div class="form-group">
<label for="orderImages"><i class="fas fa-camera"></i> 现场照片</label>
<input type="file" id="orderImages" multiple accept="image/*" onchange="previewImages(this)">
<small>支持上传多张现场照片格式JPG、PNG</small>
<div class="image-preview-container" id="imagePreviewContainer"
style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;"></div>
</div>
<div class="form-group">
<label for="orderRemarks"><i class="fas fa-sticky-note"></i> 备注信息</label>
<textarea id="orderRemarks" rows="2" placeholder="其他需要说明的信息(选填)"></textarea>
</div>
</form>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="closeNewOrderModal()">
<i class="fas fa-times"></i> 取消
</button>
<button type="submit" class="btn btn-primary" onclick="allNewOrderModal()">
<i class="fas fa-check"></i> 创建工单
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// 更新时间显示
updateTime();
setInterval(updateTime, 1000);
// 初始化仪表板
initDashboard();
// 定期更新数据
setInterval(updateDashboardData, 30000); // 每30秒更新一次
// 添加卡片点击效果
document.querySelectorAll('.nav-card').forEach(card => {
card.addEventListener('click', function () {
this.style.transform = 'scale(0.95)';
setTimeout(() => {
this.style.transform = '';
}, 150);
});
});
// 添加快速操作按钮点击效果
document.querySelectorAll('.action-btn').forEach(btn => {
btn.addEventListener('click', function (e) {
const ripple = document.createElement('span');
const rect = this.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
ripple.style.cssText = `
position: absolute;
width: ${size}px;
height: ${size}px;
left: ${x}px;
top: ${y}px;
background: rgba(255,255,255,0.3);
border-radius: 50%;
transform: scale(0);
animation: ripple 0.6s linear;
pointer-events: none;
`;
this.appendChild(ripple);
setTimeout(() => {
ripple.remove();
}, 600);
});
});
});
// 更新时间
function updateTime() {
const now = new Date();
const options = {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
};
document.getElementById('datetime').textContent = now.toLocaleDateString('zh-CN', options);
}
// 初始化仪表板
function initDashboard() {
updateDashboardData();
animateCounters();
}
// 更新仪表板数据
function updateDashboardData() {
// 模拟实时数据更新
const stats = {
alerts: Math.floor(Math.random() * 20) + 5,
workorders: Math.floor(Math.random() * 15) + 3,
devices: Math.floor(Math.random() * 3) + 14,
personnel: Math.floor(Math.random() * 5) + 20
};
// 更新统计数字
document.getElementById('alertCount').textContent = stats.alerts;
document.getElementById('workorderCount').textContent = stats.workorders;
document.getElementById('deviceCount').textContent = stats.devices;
document.getElementById('personnelCount').textContent = stats.personnel;
// 随机更新一个预警
if (Math.random() > 0.7) {
updateRecentAlerts();
}
}
// 数字动画效果
function animateCounters() {
const counters = document.querySelectorAll('.stat-number');
counters.forEach(counter => {
const target = parseInt(counter.textContent);
let current = 0;
const increment = target / 20;
const timer = setInterval(() => {
current += increment;
if (current >= target) {
counter.textContent = target;
clearInterval(timer);
} else {
counter.textContent = Math.floor(current);
}
}, 50);
});
}
// 模拟实时预警更新
function updateRecentAlerts() {
const alertTypes = [
{ title: '水质异常', icon: 'exclamation', level: 'high' },
{ title: '设备故障', icon: 'wrench', level: 'medium' },
{ title: '巡检提醒', icon: 'info', level: 'low' },
{ title: '垃圾堆积', icon: 'trash', level: 'medium' },
{ title: '水位预警', icon: 'tint', level: 'high' },
{ title: '网络异常', icon: 'wifi', level: 'warning' }
];
const randomAlert = alertTypes[Math.floor(Math.random() * alertTypes.length)];
const alertsContainer = document.querySelector('.recent-alerts');
const alertItems = alertsContainer.querySelectorAll('.alert-item');
// 移除最后一个预警
if (alertItems.length > 3) {
alertItems[alertItems.length - 1].remove();
}
// 添加新预警到顶部
const newAlert = document.createElement('div');
newAlert.className = 'alert-item';
newAlert.innerHTML = `
<div class="alert-icon alert-${randomAlert.level}">
<i class="fas fa-${randomAlert.icon}"></i>
</div>
<div class="alert-content">
<div class="alert-title">${randomAlert.title}</div>
<div class="alert-time">刚刚</div>
</div>
`;
// 插入到第一个预警项之后(标题之后)
const firstAlert = alertsContainer.querySelector('.alert-item');
if (firstAlert) {
alertsContainer.insertBefore(newAlert, firstAlert);
} else {
alertsContainer.appendChild(newAlert);
}
// 添加新增动画
newAlert.style.opacity = '0';
newAlert.style.transform = 'translateX(-20px)';
setTimeout(() => {
newAlert.style.transition = 'all 0.5s ease';
newAlert.style.opacity = '1';
newAlert.style.transform = 'translateX(0)';
}, 100);
}
// 地图相关变量
var locationMap;
var selectedLocation = null;
var selectedMarker = null;
// 打开地图选择模态框
function openLocationMap() {
document.getElementById('locationMapModal').style.display = 'block';
setTimeout(initializeLocationMap, 100);
setTimeout(() => {
if (!locationMap) {
initializeLocationMap();
} else {
locationMap.updateSize(); // 确保地图正确渲染
}
}, 100);
}
// 关闭地图选择模态框
function closeLocationMap() {
document.getElementById('locationMapModal').style.display = 'none';
}
// 确认选择的位置
function confirmLocation() {
if (selectedLocation) {
// 将经纬度坐标显示在GPS文本框中格式经度,纬度
document.getElementById('orderGps').value =
`${selectedLocation.coords[0].toFixed(6)}, ${selectedLocation.coords[1].toFixed(6)}`;
closeLocationMap();
} else {
alert('请先在地图上选择位置');
}
}
var TDT_API_KEY = "a4f4b6000cb9d1bf148bb77452000f30";
// 初始化位置选择地图
function initializeLocationMap() {
if (locationMap) {
locationMap.updateSize();
return;
}
// 创建地图图层
var satelliteLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=" + TDT_API_KEY
}),
visible: true,
name: 'satellite'
});
var streetLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=" + TDT_API_KEY
}),
visible: false,
name: 'street'
});
var terrainLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=" + TDT_API_KEY
}),
visible: false,
name: 'terrain'
});
// 创建地图
locationMap = new ol.Map({
layers: [satelliteLayer, streetLayer, terrainLayer],
target: 'locationMap',
view: new ol.View({
projection: 'EPSG:4326',
center: [112.955, 28.195],
zoom: 12
})
});
// 添加矢量图层用于显示标记点
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
image: new ol.style.Icon({
src: '/images/marker-icon-green.png',
scale: 0.8
})
})
});
locationMap.addLayer(vectorLayer);
// 添加点击事件
locationMap.on('click', function (evt) {
var coords = evt.coordinate;
updateSelectedLocation(coords);
});
// 如果已有GPS坐标值尝试定位
var currentValue = document.getElementById('orderGps').value;
if (currentValue && currentValue.includes(',')) {
var parts = currentValue.split(',');
var lon = parseFloat(parts[0].trim());
var lat = parseFloat(parts[1].trim());
if (!isNaN(lon) && !isNaN(lat)) {
updateSelectedLocation([lon, lat]);
locationMap.getView().setCenter([lon, lat]);
locationMap.getView().setZoom(16);
}
}
}
// 更新选择的位置
function updateSelectedLocation(coords) {
selectedLocation = {
coords: coords
};
// 添加标记
var vectorSource = locationMap.getLayers().getArray()
.find(layer => layer.getSource() instanceof ol.source.Vector).getSource();
vectorSource.clear();
var marker = new ol.Feature({
geometry: new ol.geom.Point(coords)
});
vectorSource.addFeature(marker);
// 移动视图到选择的位置
locationMap.getView().animate({
center: coords,
zoom: 16,
duration: 500
});
}
// 切换地图类型
function switchMapType2(type, button) {
if (!locationMap) return;
// 更新按钮样式
document.querySelectorAll('.map-control-btn').forEach(btn => {
btn.classList.remove('active');
});
button.classList.add('active');
// 切换地图图层
locationMap.getLayers().forEach(layer => {
if (layer.get('name')) {
layer.setVisible(layer.get('name') === type);
}
});
}
function openNewOrderModal() {
document.getElementById('newOrderModal').style.display = 'block';
}
function closeNewOrderModal() {
document.getElementById('newOrderModal').style.display = 'none';
}
// 图片预览及删除
let selectedImageFiles = [];
function previewImages(input) {
var container = document.getElementById('imagePreviewContainer');
container.innerHTML = '';
selectedImageFiles = Array.from(input.files);
selectedImageFiles.forEach(function (file, idx) {
var reader = new FileReader();
reader.onload = function (e) {
var imgWrapper = document.createElement('div');
imgWrapper.style.position = 'relative';
imgWrapper.style.display = 'inline-block';
imgWrapper.style.marginRight = '8px';
imgWrapper.style.marginBottom = '8px';
var img = document.createElement('img');
img.src = e.target.result;
imgWrapper.appendChild(img);
var delIcon = document.createElement('span');
delIcon.innerHTML = '<i class="fas fa-times-circle"></i>';
delIcon.style.position = 'absolute';
delIcon.style.top = '2px';
delIcon.style.right = '2px';
delIcon.style.color = '#e74c3c';
delIcon.style.background = 'rgba(255,255,255,0.7)';
delIcon.style.borderRadius = '50%';
delIcon.style.cursor = 'pointer';
delIcon.style.fontSize = '18px';
delIcon.title = '删除图片';
delIcon.onclick = function () {
removeImage(idx);
};
imgWrapper.appendChild(delIcon);
container.appendChild(imgWrapper);
};
reader.readAsDataURL(file);
});
}
function removeImage(idx) {
selectedImageFiles.splice(idx, 1);
// 重新生成 FileList
var dt = new DataTransfer();
selectedImageFiles.forEach(f => dt.items.add(f));
var input = document.getElementById('orderImages');
input.files = dt.files;
previewImages(input);
}
// 创建工单按钮提交逻辑
function allNewOrderModal() {
closeNewOrderModal();
alert('创建成功');
}
</script>
</body>
</html>