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.

5022 lines
186 KiB
HTML

This file contains ambiguous Unicode characters!

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

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>工单管理 - 智慧河道管理平台</title>
<!-- <link rel="stylesheet" href="styles.css"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
<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>
<script src="chart.js"></script>
<style>
/* 统一按钮样式 */
.action-btn,
.stats-controls .btn-primary,
.settings-controls .btn-primary,
.settings-controls .btn-secondary,
.strategy-controls .btn-primary,
.strategy-controls .btn-secondary {
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);
display: flex;
align-items: center;
gap: 8px;
margin: 0 5px;
}
.action-btn:hover,
.stats-controls .btn-primary:hover,
.settings-controls .btn-primary:hover,
.settings-controls .btn-secondary:hover,
.strategy-controls .btn-primary:hover,
.strategy-controls .btn-secondary:hover {
background: linear-gradient(135deg, #1a4b8c, #2a7de1);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(42, 125, 225, 0.3);
}
/* 图表控制区域样式调整 */
.stats-controls {
display: flex;
gap: 10px;
align-items: center;
justify-content: flex-end;
/* 将内容右对齐 */
margin-bottom: 15px;
}
/* 图表类型切换按钮样式 */
.chart-type-controls {
display: flex;
gap: 5px;
margin-left: auto;
/* 将按钮组推到右侧 */
}
.chart-type-btn {
background: rgba(42, 125, 225, 0.1);
border: 1px solid #d1e0f5;
color: #2a7de1;
padding: 8px 12px;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
font-size: 12px;
}
.chart-type-btn:hover,
.chart-type-btn.active {
background: rgba(42, 125, 225, 0.2);
color: #1a4b8c;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #f5f9ff;
color: #333;
min-height: 100vh;
padding: 20px;
font-family: 'Noto Sans SC', sans-serif;
}
.dashboard {
display: grid;
grid-template-columns: 1fr 350px;
gap: 20px;
margin: 0 auto;
}
.main-content {
margin-top: 0 !important;
background: #ffffff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 75, 150, 0.1);
}
.sidebar {
background: #ffffff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 75, 150, 0.1);
display: flex;
flex-direction: column;
overflow-y: auto;
/* max-height: calc(100vh + 140px); */
/* max-height: calc(100vh - 40px); */
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.header h1 {
color: #1a4b8c;
font-size: 24px;
font-weight: 600;
}
/* 统计分析概览卡片样式 */
.stats-overview {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background: #f5f9ff;
border: 1px solid #d1e0f5;
border-radius: 10px;
padding: 15px;
text-align: center;
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(42, 125, 225, 0.15);
}
.stat-number {
font-size: 20px;
font-weight: bold;
color: #2a7de1;
margin-bottom: 5px;
}
.stat-label {
font-size: 12px;
color: #4a6fa5;
}
.stat-change {
font-size: 12px;
margin-top: 5px;
}
.change-up {
color: #28a745;
}
.change-down {
color: #dc3545;
}
/* 统计分析头部样式 */
.stats-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.stats-controls {
display: flex;
gap: 10px;
align-items: center;
}
/* 图表容器样式 */
.chart-container {
background: #ffffff;
border: 1px solid #e1e9f5;
border-radius: 10px;
padding: 15px;
box-shadow: 0 2px 8px rgba(0, 75, 150, 0.05);
margin-bottom: 20px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.chart-canvas {
height: 300px;
}
/* 关键指标布局样式 */
.kpi-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
.kpi-item {
background: #f5f9ff;
border-radius: 8px;
padding: 15px;
text-align: center;
border: 1px solid #e1e9f5;
}
.kpi-icon {
font-size: 20px;
color: #2a7de1;
margin-bottom: 5px;
}
.kpi-value {
font-size: 18px;
font-weight: bold;
color: #2a7de1;
}
.kpi-label {
font-size: 12px;
color: #4a6fa5;
margin-top: 5px;
}
/* 进度条样式 */
.progress-bar {
width: 100%;
height: 8px;
background: #e1e9f5;
border-radius: 4px;
margin: 5px 0;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #2a7de1, #1a4b8c);
border-radius: 4px;
transition: width 0.3s;
}
/* 排名列表样式 */
.ranking-list {
list-style: none;
}
.ranking-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
border-bottom: 1px solid #e1e9f5;
}
.rank-number {
font-weight: bold;
color: #1a4b8c;
min-width: 20px;
}
.rank-name {
flex: 1;
}
.rank-score {
min-width: 40px;
text-align: right;
}
/* 人员效率样式 */
.efficiency-stats {
list-style: none;
}
.efficiency-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
border-bottom: 1px solid #e1e9f5;
}
.worker-name {
flex: 1;
}
.efficiency-rate {
min-width: 40px;
text-align: right;
}
/* 筛选按钮样式 */
.filter-btn {
background: rgba(42, 125, 225, 0.1);
border: 1px solid #d1e0f5;
color: #2a7de1;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
font-size: 12px;
display: inline-flex;
align-items: center;
gap: 5px;
}
/* .filter-btn {
background: rgba(42, 125, 225, 0.1);
border: 1px solid #d1e0f5;
color: #2a7de1;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
font-size: 12px;
} */
.filter-btn:hover,
.filter-btn.active {
background: rgba(42, 125, 225, 0.2);
color: #1a4b8c;
}
/* 侧边栏样式 */
.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;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.stats-overview {
grid-template-columns: repeat(3, 1fr);
}
.kpi-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.stats-overview {
grid-template-columns: repeat(2, 1fr);
}
.kpi-grid {
grid-template-columns: 1fr;
}
.dashboard {
grid-template-columns: 1fr;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #f5f9ff;
color: #333;
min-height: 100vh;
padding: 20px;
font-family: 'Noto Sans SC', sans-serif;
}
.dashboard {
display: grid;
grid-template-columns: 1fr 350px;
gap: 20px;
margin: 0 auto;
}
.main-content {
margin-top: 0 !important;
background: #ffffff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 75, 150, 0.1);
}
.sidebar {
background: #ffffff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 75, 150, 0.1);
display: flex;
flex-direction: column;
overflow-y: auto;
/* max-height: calc(100vh - 40px); */
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e1e9f5;
}
.header h1 {
color: #1a4b8c;
font-size: 24px;
font-weight: 600;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 15px;
margin-bottom: 20px;
overflow-x: auto;
white-space: nowrap;
}
.stat-card {
min-width: 150px !important;
background: #f5f9ff !important;
border: 1px solid #d1e0f5 !important;
border-radius: 10px !important;
padding: 15px !important;
text-align: center !important;
box-shadow: 0 2px 8px rgba(0, 75, 150, 0.05);
}
.stat-number {
font-size: 24px !important;
font-weight: bold !important;
color: #2a7de1 !important;
margin-bottom: 5px !important;
}
.stat-label {
font-size: 12px;
color: #4a6fa5;
}
/* 标签页样式 */
.tab-container {
margin-bottom: 20px;
}
.tab-nav {
display: flex;
gap: 2px;
margin-bottom: 20px;
background: #e1e9f5;
border-radius: 10px;
padding: 5px;
}
.tab-btn {
flex: 1;
background: transparent;
color: #4a6fa5;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
font-weight: 500;
}
.tab-btn:hover {
background: rgba(42, 125, 225, 0.1);
}
.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;
animation: fadeIn 0.5s ease;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.filter-section {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.filter-btn {
background: rgba(42, 125, 225, 0.1);
border: 1px solid #d1e0f5;
color: #2a7de1;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
font-size: 12px;
}
.filter-btn:hover,
.filter-btn.active {
background: rgba(42, 125, 225, 0.2);
color: #1a4b8c;
}
.search-box {
background: #f5f9ff;
border: 1px solid #d1e0f5;
border-radius: 8px;
padding: 8px 12px;
color: #1a4b8c;
width: 200px;
}
.table-container {
overflow-x: auto;
margin: 20px 0;
border-radius: 10px;
background: #ffffff;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
}
.workorder-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
background: #ffffff;
border-radius: 10px;
overflow: hidden;
}
.workorder-table th,
.workorder-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #e1e9f5;
}
.workorder-table th {
background: #f5f9ff;
color: #1a4b8c;
font-weight: 600;
font-size: 14px;
}
.workorder-table td {
color: #4a6fa5;
font-size: 13px;
}
.workorder-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;
}
.priority-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
text-align: center;
display: inline-block;
min-width: 40px;
}
.priority-high {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border: 1px solid rgba(220, 53, 69, 0.3);
}
.priority-medium {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
border: 1px solid rgba(255, 193, 7, 0.3);
}
.priority-low {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.status-created {
background: rgba(108, 117, 125, 0.1);
color: #6c757d;
border: 1px solid rgba(108, 117, 125, 0.3);
}
.status-assigned {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
border: 1px solid rgba(255, 193, 7, 0.3);
}
.status-dispatched {
background: rgba(0, 123, 255, 0.1);
color: #007bff;
border: 1px solid rgba(0, 123, 255, 0.3);
}
.status-departed {
background: rgba(102, 16, 242, 0.1);
color: #6610f2;
border: 1px solid rgba(102, 16, 242, 0.3);
}
.status-arrived {
background: rgba(255, 165, 0, 0.1);
color: #ffa500;
border: 1px solid rgba(255, 165, 0, 0.3);
}
.status-processed {
background: rgba(32, 201, 151, 0.1);
color: #20c997;
border: 1px solid rgba(32, 201, 151, 0.3);
}
.status-transported {
background: rgba(111, 66, 193, 0.1);
color: #6f42c1;
border: 1px solid rgba(111, 66, 193, 0.3);
}
.status-completed {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
/* 统一按钮样式 */
.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;
}
.btn-primary {
background: linear-gradient(135deg, #2a7de1, #1a4b8c);
color: white;
}
.btn-secondary {
background: linear-gradient(135deg, #6c757d, #495057);
color: white;
}
.btn-success {
background: linear-gradient(135deg, #28a745, #1e7e34);
color: white;
}
.btn-info {
background: linear-gradient(135deg, #17a2b8, #117a8b);
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
/* .btn {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin: 0 2px;
transition: all 0.3s;
}
.btn-primary {
background: rgba(42, 125, 225, 0.1);
color: #2a7de1;
border: 1px solid rgba(42, 125, 225, 0.3);
}
.btn-success {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
} */
/* 侧边栏样式 */
.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;
}
.chart-container {
height: 200px;
background: #ffffff;
border-radius: 8px;
padding: 15px;
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);
}
.priority-list {
list-style: none;
max-height: 200px;
overflow-y: auto;
}
.priority-item {
padding: 8px;
margin-bottom: 5px;
background: rgba(220, 53, 69, 0.1);
border-left: 3px solid #dc3545;
border-radius: 4px;
font-size: 12px;
}
.efficiency-stats {
display: flex;
flex-direction: column;
gap: 10px;
}
.efficiency-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #e1e9f5;
}
.efficiency-item:last-child {
border-bottom: none;
}
/* 模态框样式 */
.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;
}
/* 模态框头部样式 */
.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);
}
/* 大型模态框 */
.large-modal {
width: 90%;
max-width: 1000px;
}
/* 工单详情样式 */
.order-details {
max-height: 70vh;
overflow-y: auto;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.detail-section {
background: #f5f9ff;
border: 1px solid #e1e9f5;
border-radius: 8px;
padding: 15px;
}
.detail-section.full-width {
grid-column: 1 / -1;
}
.detail-section h3 {
color: #1a4b8c;
font-size: 16px;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.detail-row {
display: flex;
margin-bottom: 10px;
align-items: center;
}
.detail-label {
color: #4a6fa5;
font-size: 14px;
min-width: 80px;
margin-right: 10px;
}
.detail-value {
color: #1a4b8c;
font-size: 14px;
font-weight: 500;
}
.detail-content {
color: #4a6fa5;
line-height: 1.6;
padding: 10px;
background: #f5f9ff;
border-radius: 6px;
border: 1px solid #e1e9f5;
}
/* 图片画廊 */
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 15px;
}
.image-item {
text-align: center;
}
.image-item img {
width: 100%;
height: 80px;
object-fit: cover;
border-radius: 6px;
border: 1px solid #d1e0f5;
cursor: pointer;
transition: transform 0.3s;
}
.image-item img:hover {
transform: scale(1.05);
}
.image-label {
display: block;
margin-top: 5px;
font-size: 12px;
color: #4a6fa5;
}
/* 时间线样式 */
.timeline {
position: relative;
padding-left: 20px;
}
.timeline::before {
content: '';
position: absolute;
left: 8px;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(to bottom, #2a7de1, rgba(42, 125, 225, 0.3));
}
.timeline-item {
position: relative;
margin-bottom: 20px;
padding-left: 25px;
}
.timeline-item::before {
content: '';
position: absolute;
left: -4px;
top: 5px;
width: 8px;
height: 8px;
background: #2a7de1;
border-radius: 50%;
box-shadow: 0 0 10px rgba(42, 125, 225, 0.5);
}
.timeline-time {
color: #2a7de1;
font-size: 12px;
font-weight: 500;
margin-bottom: 5px;
}
.timeline-content {
color: #4a6fa5;
font-size: 14px;
line-height: 1.5;
}
/* 选中工单标签 */
.selected-orders {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 10px;
background: #f5f9ff;
border-radius: 6px;
min-height: 40px;
border: 1px solid #e1e9f5;
}
.order-tag {
background: rgba(42, 125, 225, 0.1);
color: #2a7de1;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
display: flex;
align-items: center;
gap: 5px;
border: 1px solid rgba(42, 125, 225, 0.3);
}
.order-tag i {
cursor: pointer;
opacity: 0.7;
}
.order-tag i:hover {
opacity: 1;
}
/* 跟踪模态框样式 */
.track-content {
display: flex;
gap: 20px;
margin-top: 20px;
}
.track-main {
flex: 2;
display: flex;
flex-direction: column;
gap: 20px;
}
.track-sidebar {
flex: 1;
min-width: 300px;
display: flex;
flex-direction: column;
gap: 20px;
}
.track-map-container,
.track-timeline,
.track-info-card,
.track-stats-card,
.track-actions-card,
.track-photos-card {
background: #ffffff;
border: 1px solid #e1e9f5;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
}
/* 地图容器样式 */
.map-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.map-header h4 {
margin: 0;
color: #1a4b8c;
font-size: 16px;
}
.map-controls {
display: flex;
gap: 5px;
}
.map-btn {
padding: 6px 12px;
background: rgba(42, 125, 225, 0.1);
border: 1px solid #d1e0f5;
color: #2a7de1;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
}
.map-btn.active,
.map-btn:hover {
background: rgba(42, 125, 225, 0.2);
color: #1a4b8c;
}
.tracking-map {
height: 300px;
background: #f5f9ff;
border: 1px solid #e1e9f5;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.map-placeholder {
text-align: center;
color: #a8c0e0;
}
.map-placeholder i {
font-size: 48px;
margin-bottom: 10px;
color: #2a7de1;
}
/* 统计分析页面样式 */
.stats-content {
display: flex;
gap: 20px;
margin-top: 20px;
}
.stats-main {
flex: 2;
display: flex;
flex-direction: column;
gap: 20px;
}
.stats-sidebar {
flex: 1;
min-width: 300px;
display: flex;
flex-direction: column;
gap: 20px;
}
/* .stats-overview {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-bottom: 20px;
} */
.overview-card {
background: #f5f9ff;
border: 1px solid #e1e9f5;
border-radius: 10px;
padding: 15px;
text-align: center;
min-height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
}
.stats-charts,
.stats-analysis,
.stats-card {
background: #ffffff;
border: 1px solid #e1e9f5;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
}
.analysis-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
/* margin-top: 20px; */
}
/* 系统设置页面样式 */
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #e1e9f5;
}
.settings-header h3 {
margin: 0;
color: #1a4b8c;
font-size: 24px;
}
.settings-controls {
display: flex;
gap: 10px;
}
.settings-content {
display: flex;
gap: 30px;
}
.settings-main {
flex: 2;
}
.settings-sidebar {
flex: 1;
min-width: 300px;
}
.settings-section {
background: #ffffff;
border-radius: 8px;
padding: 25px;
margin-bottom: 25px;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
border: 1px solid #e1e9f5;
}
.settings-section h4 {
margin: 0 0 20px 0;
color: #1a4b8c;
font-size: 18px;
border-bottom: 1px solid #e1e9f5;
padding-bottom: 10px;
}
/* 滚动条样式 */
::-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;
}
/* 头部信息样式 */
.header-info {
display: flex;
gap: 30px;
align-items: center;
}
.weather-info {
display: flex;
align-items: center;
gap: 10px;
color: #4a6fa5;
}
#datetime {
color: #4a6fa5;
font-size: 14px;
}
.back-button {
cursor: pointer;
font-size: 20px;
margin-left: 20px;
transition: transform 0.3s;
color: #4a6fa5;
}
.back-button:hover {
transform: scale(1.1);
color: #2a7de1;
}
.header-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);
display: flex;
align-items: center;
gap: 8px;
}
.action-btn:hover {
background: linear-gradient(135deg, #1a4b8c, #2a7de1);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(42, 125, 225, 0.3);
}
/* 新增样式 */
.settings-section {
background: #ffffff;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0, 75, 150, 0.05);
border: 1px solid #e1e9f5;
}
.settings-section h4 {
color: #1a4b8c;
font-size: 18px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e1e9f5;
display: flex;
align-items: center;
gap: 8px;
}
.setting-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.setting-item {
margin-bottom: 15px;
}
.setting-label {
display: block;
margin-bottom: 8px;
color: #1a4b8c;
font-size: 14px;
font-weight: 500;
}
.setting-input,
.setting-select {
width: 100%;
padding: 10px;
border: 1px solid #d1e0f5;
border-radius: 6px;
background: #f5f9ff;
color: #1a4b8c;
font-size: 14px;
}
.setting-desc {
display: block;
margin-top: 5px;
font-size: 12px;
color: #4a6fa5;
}
.strategy-list {
display: grid;
gap: 15px;
}
.strategy-item {
background: #f5f9ff;
border: 1px solid #e1e9f5;
border-radius: 8px;
padding: 15px;
}
.strategy-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.strategy-header h5 {
margin: 0;
color: #1a4b8c;
font-size: 16px;
}
.strategy-status {
display: flex;
align-items: center;
gap: 10px;
}
.status-enabled {
color: #28a745;
font-size: 14px;
}
.status-disabled {
color: #6c757d;
font-size: 14px;
}
.strategy-actions {
display: flex;
gap: 5px;
}
.strategy-desc {
color: #4a6fa5;
font-size: 14px;
margin-bottom: 10px;
}
.strategy-rules {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.rule-item {
font-size: 13px;
color: #4a6fa5;
}
.rule-label {
font-weight: 500;
color: #1a4b8c;
}
.dispatch-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
}
.stat-item {
background: #f5f9ff;
border: 1px solid #e1e9f5;
border-radius: 8px;
padding: 15px;
text-align: center;
}
.stat-label {
font-size: 12px;
color: #4a6fa5;
margin-top: 5px;
}
.stat-value {
font-size: 18px;
font-weight: bold;
color: #2a7de1;
}
.config-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
}
.config-item {
margin-bottom: 15px;
}
.config-label {
display: block;
margin-bottom: 8px;
color: #1a4b8c;
font-size: 14px;
font-weight: 500;
}
.config-select,
.config-input {
width: 100%;
padding: 10px;
border: 1px solid #d1e0f5;
border-radius: 6px;
background: #f5f9ff;
color: #1a4b8c;
font-size: 14px;
}
/* 策略控制按钮样式 */
.strategy-controls {
display: flex;
gap: 10px;
}
/* 确保标题和按钮在同一行对齐 */
.settings-section h4 {
margin: 0;
/* 移除默认的margin */
display: flex;
align-items: center;
gap: 8px;
}
/* 统一操作按钮样式 */
.btn-sm {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin: 0 2px;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 5px;
}
.btn-sm:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-edit {
background: rgba(42, 125, 225, 0.1);
color: #2a7de1;
border: 1px solid rgba(42, 125, 225, 0.3);
}
.btn-disable {
background: rgba(108, 117, 125, 0.1);
color: #6c757d;
border: 1px solid rgba(108, 117, 125, 0.3);
}
.btn-enable {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
/* 策略项状态样式 */
.strategy-item {
border-left: 4px solid #e1e9f5;
transition: all 0.3s ease;
}
.strategy-item.active {
border-left-color: #2a7de1;
background-color: rgba(42, 125, 225, 0.05);
}
/* 策略状态文本样式 */
.status-enabled {
color: #28a745;
font-size: 14px;
padding: 2px 8px;
background: rgba(40, 167, 69, 0.1);
border-radius: 12px;
}
.status-disabled {
color: #6c757d;
font-size: 14px;
padding: 2px 8px;
background: rgba(108, 117, 125, 0.1);
border-radius: 12px;
}
/* 策略操作按钮容器 */
.strategy-actions {
display: flex;
gap: 8px;
margin-left: auto;
}
/* 统一图表容器高度 */
.stats-card .chart-canvas {
height: 380px;
/* 统一高度 */
min-height: 260px;
/* 确保最小高度 */
}
/* 调整设置部分的标题下划线间距 */
.settings-section h4 {
margin: 0 0 15px 0;
/* 增加下边距 */
padding-bottom: 10px;
border-bottom: 1px solid #e1e9f5;
}
/* 派单统计卡片调整 */
.dispatch-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-top: 15px;
/* 增加上边距 */
}
/* 策略配置部分调整 */
.strategy-config {
margin-top: 15px;
/* 增加上边距 */
}
/* 工单配置部分调整 */
.setting-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-top: 15px;
/* 增加上边距 */
}
/* 统计分析控件样式调整 */
.stats-controls {
display: flex;
gap: 10px;
align-items: center;
}
.stats-select {
height: 36px;
/* 与按钮高度一致 */
padding: 6px 12px;
border: 1px solid #d1e0f5;
border-radius: 6px;
background: #f5f9ff;
color: #1a4b8c;
font-size: 14px;
min-width: 150px;
/* 增加最小宽度 */
transition: all 0.3s;
}
.stats-select:hover {
border-color: #a8c0e0;
}
.stats-date {
height: 36px;
/* 与按钮高度一致 */
padding: 6px 12px;
border: 1px solid #d1e0f5;
border-radius: 6px;
background: #f5f9ff;
color: #1a4b8c;
font-size: 14px;
width: 150px;
/* 固定宽度 */
}
/* 确保按钮高度一致 */
.btn-primary {
height: 36px;
padding: 0 16px;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* 添加到现有的style部分 */
.track-modal-content {
max-height: 85vh;
overflow-y: auto;
}
.track-content {
display: flex;
gap: 20px;
margin-top: 20px;
flex-wrap: wrap;
/* 允许在小屏幕上换行 */
}
.track-main,
.track-sidebar {
min-width: 300px;
flex: 1;
}
.tracking-map {
height: 300px;
max-height: 50vh;
/* 限制最大高度 */
}
/* 确保模态框内容不会超出屏幕 */
.modal-content.large-modal {
max-width: 90vw;
width: auto;
margin: 2% auto;
}
/* 确保时间线可以滚动 */
.timeline-container {
max-height: 320px;
overflow-y: auto;
}
/* 跟踪模态框样式 */
.track-container {
display: flex;
gap: 20px;
margin-top: 20px;
height: calc(100% - 60px);
}
.track-left {
flex: 2;
display: flex;
flex-direction: column;
gap: 20px;
}
.track-right {
flex: 1;
min-width: 300px;
display: flex;
flex-direction: column;
gap: 20px;
}
.track-controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
align-items: center;
}
.track-select {
flex: 1;
padding: 8px 12px;
border: 1px solid #d1e0f5;
border-radius: 6px;
background: #f5f9ff;
color: #1a4b8c;
}
.tracking-map {
height: 400px;
background: #f5f9ff;
border: 1px solid #e1e9f5;
border-radius: 8px;
position: relative;
overflow: hidden;
}
.timeline-container {
max-height: 320px;
overflow-y: auto;
padding: 10px;
background: #f5f9ff;
border-radius: 8px;
border: 1px solid #e1e9f5;
}
.info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #e1e9f5;
}
.info-label {
color: #4a6fa5;
font-size: 14px;
}
.info-value {
color: #1a4b8c;
font-weight: 500;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.action-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.photos-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.photo-item {
text-align: center;
}
.photo-item img {
width: 100%;
height: 80px;
object-fit: cover;
border-radius: 6px;
border: 1px solid #d1e0f5;
}
.add-photo {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 80px;
background: #f5f9ff;
border: 1px dashed #d1e0f5;
border-radius: 6px;
cursor: pointer;
color: #4a6fa5;
}
.add-photo:hover {
background: rgba(42, 125, 225, 0.1);
}
.photo-time {
display: block;
font-size: 12px;
color: #4a6fa5;
margin-top: 5px;
}
.image-preview-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.image-preview-item {
position: relative;
width: 100px;
height: 100px;
border-radius: 4px;
overflow: hidden;
}
.image-preview-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.image-preview-item .delete-btn {
position: absolute;
top: 5px;
right: 5px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 14px;
}
.image-preview-item .delete-btn:hover {
background-color: rgba(255, 0, 0, 0.7);
}
.order-modal-content {
max-height: 60vh;
overflow-y: auto;
padding-right: 10px;
}
/* 地图模态框样式 开始*/
/* 地图模态框样式 */
.map-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);
}
.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;
}
/* 地图模态框样式 结束*/
#editLocationMap {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="dashboard">
<div class="main-content">
<div class="header">
<h1>🗂️ 工单管理系统</h1>
<div class="header-actions">
<button class="action-btn" onclick="location.href='index.html'">
<i class="fas fa-home-alt" style="color: orange;"></i> 回到首页
</button>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-number" id="totalOrders">156</div>
<div class="stat-label">总工单数</div>
</div>
<div class="stat-card">
<div class="stat-number" id="pendingOrders">23</div>
<div class="stat-label">待处理</div>
</div>
<div class="stat-card">
<div class="stat-number" id="processingOrders">45</div>
<div class="stat-label">处理中</div>
</div>
<div class="stat-card">
<div class="stat-number" id="completedOrders">88</div>
<div class="stat-label">已完成</div>
</div>
<div class="stat-card">
<div class="stat-number" id="aiOrders">12</div>
<div class="stat-label">AI识别</div>
</div>
<div class="stat-card">
<div class="stat-number" id="publicOrders">8</div>
<div class="stat-label">公众反馈</div>
</div>
</div>
<div class="tab-container">
<div class="tab-nav">
<button class="tab-btn active" onclick="switchTab('workorder-list')">工单列表</button>
<button class="tab-btn" onclick="switchTab('workorder-analysis')">统计分析</button>
<button class="tab-btn" onclick="switchTab('workorder-settings')">系统设置</button>
</div>
<div id="workorder-list" class="tab-content active">
<div class="filter-section">
<button class="filter-btn active" data-status="all">全部工单</button>
<button class="filter-btn" data-status="created">新建</button>
<button class="filter-btn" data-status="assigned">待分配</button>
<button class="filter-btn" data-status="dispatched">待出发</button>
<button class="filter-btn" data-status="departed">已出发</button>
<button class="filter-btn" data-status="arrived">已抵达</button>
<button class="filter-btn" data-status="processed">已处理</button>
<button class="filter-btn" data-status="transported">已转运</button>
<button class="filter-btn" data-status="completed">已完成</button>
<button class="filter-btn" data-priority="high">高优先级</button>
<select class="filter-btn" id="sourceFilter">
<option value="all">全部来源</option>
<option value="ai">AI识别</option>
<option value="patrol">巡查上报</option>
<option value="public">公众反馈</option>
</select>
<input type="text" class="search-box" placeholder="搜索工单..." id="searchInput">
<button class="filter-btn" onclick="openNewOrderModal()">📝 新建工单</button>
<button class="filter-btn" onclick="exportReport()">📊 导出报表</button>
<button class="filter-btn" onclick="openBatchModal()">📋 批量派单</button>
</div>
<div class="table-container">
<table class="workorder-table">
<thead>
<tr>
<th><input type="checkbox" id="selectAll"></th>
<th>工单编号</th>
<th>来源</th>
<th>类型</th>
<th>位置</th>
<th>优先级</th>
<th>状态</th>
<th>负责人</th>
<th>创建时间</th>
<th>响应时限</th>
<th>操作</th>
</tr>
</thead>
<tbody id="workorderTableBody">
<!-- 工单数据将通过JS动态加载 -->
</tbody>
</table>
<!-- 分页控件容器 -->
<div id="pagination"
style="display: flex; justify-content: flex-end; align-items: center; gap: 8px; margin: 16px 0 0 0;">
</div>
</div>
</div>
<div id="workorder-analysis" class="tab-content">
<!-- 统计分析部分 -->
<div class="stats-header">
<h3><i class="fas fa-chart-bar"></i>统计分析</h3>
<div class="stats-controls">
<select id="statsTimeRange" class="stats-select">
<option value="today">今日</option>
<option value="week" selected>本周</option>
<option value="month">本月</option>
<option value="quarter">本季度</option>
<option value="year">本年</option>
<option value="custom">自定义</option>
</select>
<input type="date" id="statsStartDate" class="stats-date" style="display:none;">
<input type="date" id="statsEndDate" class="stats-date" style="display:none;">
<button class="btn-primary" onclick="refreshStats()">
<i class="fas fa-sync-alt"></i>刷新
</button>
<button class="btn-primary" onclick="exportStatsReport()">
<i class="fas fa-file-excel"></i>导出报表
</button>
</div>
</div>
<!-- 统计分析概览 -->
<div class="stats-overview">
<div class="stat-card">
<div class="stat-number">156</div>
<div class="stat-label">总工单数</div>
<div class="stat-change change-up">↗ +12%</div>
</div>
<div class="stat-card">
<div class="stat-number">132</div>
<div class="stat-label">已完成</div>
<div class="stat-change change-up">↗ +8%</div>
</div>
<div class="stat-card">
<div class="stat-number">18</div>
<div class="stat-label">处理中</div>
<div class="stat-change change-down">↘ -3%</div>
</div>
<div class="stat-card">
<div class="stat-number">6</div>
<div class="stat-label">超时工单</div>
<div class="stat-change change-down">↘ +2</div>
</div>
<div class="stat-card">
<div class="stat-number">84.6%</div>
<div class="stat-label">完成率</div>
<div class="stat-change change-up">↗ +2.1%</div>
</div>
<div class="stat-card">
<div class="stat-number">2.3h</div>
<div class="stat-label">平均处理时间</div>
<div class="stat-change change-up">↗ -0.2h</div>
</div>
</div>
<div class="stats-content">
<div class="stats-main">
<!-- 工单趋势分析部分 -->
<div class="stats-card">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4><i class="fas fa-calendar-alt"></i>工单趋势分析</h4>
<div class="chart-type-controls">
<button class="chart-type-btn active" data-chart="line">趋势图</button>
<button class="chart-type-btn" data-chart="bar">柱状图</button>
<button class="chart-type-btn" data-chart="pie">饼图</button>
</div>
</div>
<div class="time-distribution">
<div class="chart-canvas">
<canvas id="trendChart"></canvas>
</div>
</div>
</div>
<div class="analysis-grid">
<div class="stats-card">
<h4><i class="fas fa-map-marked-alt"></i>区域分布</h4>
<div class="chart-canvas">
<canvas id="regionChart"></canvas>
</div>
</div>
<div class="stats-card">
<h4><i class="fas fa-users"></i>人员效率</h4>
<div class="chart-canvas">
<canvas id="efficiencyChart"></canvas>
</div>
</div>
</div>
</div>
<div class="stats-sidebar">
<!-- <div class="stats-card">
<h4><i class="fas fa-calendar-alt"></i>时间分布</h4>
<div class="time-distribution">
<div class="chart-canvas">
<canvas id="timeDistributionChart"></canvas>
</div>
</div>
</div> -->
<div class="stats-card">
<h4 style="height: 35px;line-height: 35px;"><i class="fas fa-trophy"></i>本周排行</h4>
<div class="chart-canvas">
<canvas id="rankingChart"></canvas>
</div>
</div>
<div class="stats-card">
<h4><i class="fas fa-exclamation-circle"></i>问题类型</h4>
<div class="chart-canvas">
<canvas id="problemTypeChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<div id="workorder-settings" class="tab-content">
<!-- 系统设置部分 -->
<div class="settings-header">
<h3><i class="fas fa-cog"></i>系统设置</h3>
<div class="settings-controls">
<button class="btn-primary" onclick="saveAllSettings()"><i
class="fas fa-save"></i>保存设置</button>
<button class="btn-secondary" onclick="resetSettings()"><i
class="fas fa-undo"></i>重置</button>
<button class="btn-primary" onclick="exportSettings()"><i
class="fas fa-download"></i>导出配置</button>
</div>
</div>
<div class="settings-content">
<div class="settings-main">
<div class="settings-section">
<h4><i class="fas fa-clipboard-list"></i>工单配置</h4>
<div class="setting-group">
<div class="setting-item">
<label class="setting-label">工单编号前缀</label>
<input type="text" class="setting-input" value="WO" placeholder="如WO">
<span class="setting-desc">工单编号的前缀字符</span>
</div>
<div class="setting-item">
<label class="setting-label">编号位数</label>
<select class="setting-select">
<option value="6">6位</option>
<option value="8" selected>8位</option>
<option value="10">10位</option>
</select>
<span class="setting-desc">工单编号的数字位数</span>
</div>
<div class="setting-item">
<label class="setting-label">默认优先级</label>
<select class="setting-select">
<option value="low"></option>
<option value="medium" selected></option>
<option value="high"></option>
<option value="urgent">紧急</option>
</select>
<span class="setting-desc">新建工单的默认优先级</span>
</div>
<div class="setting-item">
<label class="setting-label">自动分配</label>
<div class="setting-toggle">
<input type="checkbox" id="autoAssign" checked>
<label for="autoAssign" class="toggle-label"></label>
</div>
<span class="setting-desc">是否启用工单自动分配功能</span>
</div>
<div class="setting-item">
<label class="setting-label">超时提醒(小时)</label>
<input type="number" class="setting-input" value="24" min="1" max="168">
<span class="setting-desc">工单超时提醒时间</span>
</div>
</div>
</div>
<div class="settings-section">
<div
style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<h4><i class="fas fa-route"></i> 派单策略</h4>
<div class="strategy-controls" style="display: flex; gap: 10px;">
<button class="btn-primary" onclick="addStrategy()"><i
class="fas fa-plus"></i>新增策略</button>
<button class="btn-secondary" onclick="testStrategy()"><i
class="fas fa-play"></i>测试策略</button>
<button class="btn-primary" onclick="importStrategy()"><i
class="fas fa-upload"></i>导入策略</button>
</div>
</div>
<div class="strategy-list">
<div class="strategy-item active">
<div class="strategy-header">
<h5>智能就近派单</h5>
<div class="strategy-status">
<span class="status-enabled">已启用</span>
<div class="strategy-actions">
<button class="btn-sm btn-edit" onclick="editStrategy('nearby')">
<i class="fas fa-edit"></i> 编辑
</button>
<button class="btn-sm btn-disable"
onclick="toggleStrategy('nearby')">
<i class="fas fa-ban"></i> 禁用
</button>
</div>
</div>
</div>
<div class="strategy-content">
<p class="strategy-desc">根据处理人员位置和工单地点,自动分配最近的可用人员</p>
<div class="strategy-rules">
<div class="rule-item">
<span class="rule-label">优先级:</span>
<span class="rule-value"></span>
</div>
<div class="rule-item">
<span class="rule-label">最大距离:</span>
<span class="rule-value">5公里</span>
</div>
<div class="rule-item">
<span class="rule-label">适用类型:</span>
<span class="rule-value">清洁、维修</span>
</div>
</div>
</div>
</div>
<div class="strategy-item">
<div class="strategy-header">
<h5>技能匹配派单</h5>
<div class="strategy-status">
<span class="status-enabled">已启用</span>
<div class="strategy-actions">
<button class="btn-sm btn-edit" onclick="editStrategy('skill')">
<i class="fas fa-edit"></i> 编辑
</button>
<button class="btn-sm btn-disable"
onclick="toggleStrategy('skill')">
<i class="fas fa-ban"></i> 禁用
</button>
</div>
</div>
</div>
<div class="strategy-content">
<p class="strategy-desc">根据工单类型和处理人员技能标签进行智能匹配</p>
<div class="strategy-rules">
<div class="rule-item">
<span class="rule-label">优先级:</span>
<span class="rule-value"></span>
</div>
<div class="rule-item">
<span class="rule-label">匹配度:</span>
<span class="rule-value">≥80%</span>
</div>
<div class="rule-item">
<span class="rule-label">适用类型:</span>
<span class="rule-value">全部</span>
</div>
</div>
</div>
</div>
<div class="strategy-item">
<div class="strategy-header">
<h5>负载均衡派单</h5>
<div class="strategy-status">
<span class="status-disabled">已禁用</span>
<div class="strategy-actions">
<button class="btn-sm btn-edit" onclick="editStrategy('balance')">
<i class="fas fa-edit"></i> 编辑
</button>
<button class="btn-sm btn-enable"
onclick="toggleStrategy('balance')">
<i class="fas fa-check"></i> 启用
</button>
</div>
</div>
</div>
<div class="strategy-content">
<p class="strategy-desc">平衡各处理人员的工作负载,避免任务过度集中</p>
<div class="strategy-rules">
<div class="rule-item">
<span class="rule-label">优先级:</span>
<span class="rule-value"></span>
</div>
<div class="rule-item">
<span class="rule-label">最大任务数:</span>
<span class="rule-value">5个</span>
</div>
<div class="rule-item">
<span class="rule-label">适用类型:</span>
<span class="rule-value">全部</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="settings-sidebar">
<div class="settings-card">
<div class="settings-section">
<h4><i class="fas fa-chart-line"></i>派单统计</h4>
<div class="dispatch-stats">
<div class="stat-item">
<div class="stat-value">156</div>
<div class="stat-label">今日派单</div>
</div>
<div class="stat-item">
<div class="stat-value">87.5%</div>
<div class="stat-label">自动派单率</div>
</div>
<div class="stat-item">
<div class="stat-value">2.3分钟</div>
<div class="stat-label">平均响应时间</div>
</div>
<div class="stat-item">
<div class="stat-value">92.1%</div>
<div class="stat-label">策略命中率</div>
</div>
</div>
</div>
</div>
<div class="settings-card">
<div class="settings-section">
<h4><i class="fas fa-cogs"></i>策略配置</h4>
<div class="strategy-config">
<div class="config-item">
<span class="config-label">执行顺序</span>
<select class="config-select">
<option value="priority">按优先级</option>
<option value="time">按时间</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="config-item">
<span class="config-label">失败重试</span>
<div class="setting-toggle">
<input type="checkbox" id="retryEnabled" checked>
<label for="retryEnabled" class="toggle-label"></label>
</div>
</div>
<div class="config-item">
<span class="config-label">重试次数</span>
<input type="number" class="config-input" value="3" min="1" max="10">
</div>
<div class="config-item">
<span class="config-label">超时时间(秒)</span>
<input type="number" class="config-input" value="30" min="10" max="300">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sidebar">
<div class="sidebar-section">
<div class="sidebar-title">工单状态分布</div>
<div class="chart-canvas">
<canvas id="statusChart"></canvas>
</div>
</div>
<div class="sidebar-section">
<div class="sidebar-title">本周工单趋势</div>
<div class="chart-canvas">
<canvas id="weeklyTrendChart"></canvas>
</div>
</div>
<div class="sidebar-section">
<div class="sidebar-title">事件来源分析</div>
<div class="chart-canvas">
<canvas id="sourceChart"></canvas>
</div>
</div>
<!-- 高优先级工单列表 -->
<!-- <div class="sidebar-section">
<div class="sidebar-title">高优先级工单</div>
<ul class="priority-list" id="priorityList">
</ul>
</div> -->
<div class="sidebar-section">
<div class="sidebar-title">处理效率统计</div>
<div class="efficiency-stats">
<div class="efficiency-item">
<span>平均响应时间</span>
<span id="avgResponseTime">2.3小时</span>
</div>
<div class="efficiency-item">
<span>验收通过率</span>
<span id="passRate">95.2%</span>
</div>
<div class="efficiency-item">
<span>按时完成率</span>
<span id="onTimeRate">87.5%</span>
</div>
</div>
</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">
<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 class="coordinates-display" id="coordinatesDisplay">坐标:未选择</div> -->
</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="button" class="btn btn-primary" onclick="submitNewOrder()">
<i class="fas fa-check"></i> 创建工单
</button>
<!-- <button type="submit" class="btn btn-primary">
<i class="fas fa-check"></i> 创建工单
</button> -->
</div>
</div>
</div>
<!-- 查看工单详情模态框 -->
<div id="viewOrderModal" class="modal">
<div class="modal-content large-modal">
<div class="modal-header">
<h2><i class="fas fa-eye"></i> 工单详情</h2>
<span class="close" onclick="closeViewOrderModal()">&times;</span>
</div>
<div class="order-details">
<div class="detail-grid">
<div class="detail-section">
<h3><i class="fas fa-info-circle"></i> 基本信息</h3>
<div class="detail-row">
<span class="detail-label">工单编号:</span>
<span class="detail-value" id="detailOrderId">WO-2025001</span>
</div>
<div class="detail-row">
<span class="detail-label">工单类型:</span>
<span class="detail-value" id="detailOrderType">垃圾清理</span>
</div>
<div class="detail-row">
<span class="detail-label">优先级:</span>
<span class="detail-value" id="detailOrderPriority"></span>
</div>
<div class="detail-row">
<span class="detail-label">当前状态:</span>
<span class="detail-value" id="detailOrderStatus">新建</span>
</div>
</div>
<div class="detail-section">
<h3><i class="fas fa-map-marker-alt"></i> 位置信息</h3>
<div class="detail-row">
<span class="detail-label">事件位置:</span>
<span class="detail-value" id="detailOrderLocation">湘江橘子洲段</span>
</div>
<div class="detail-row">
<span class="detail-label">GPS坐标</span>
<span class="detail-value" id="detailOrderGPS">28.1987, 112.9734</span>
</div>
</div>
<div class="detail-section">
<h3><i class="fas fa-user"></i> 人员信息</h3>
<div class="detail-row">
<span class="detail-label">上报人:</span>
<span class="detail-value" id="detailOrderReporter">张三</span>
</div>
<div class="detail-row">
<span class="detail-label">负责人:</span>
<span class="detail-value" id="detailOrderAssignee">李四</span>
</div>
<div class="detail-row">
<span class="detail-label">联系方式:</span>
<span class="detail-value" id="detailOrderContact">138****5678</span>
</div>
</div>
<div class="detail-section">
<h3><i class="fas fa-clock"></i> 时间信息</h3>
<div class="detail-row">
<span class="detail-label">创建时间:</span>
<span class="detail-value" id="detailCreateTime">2025-01-03 09:30</span>
</div>
<div class="detail-row">
<span class="detail-label">响应时限:</span>
<span class="detail-value" id="detailResponseTime">2小时内</span>
</div>
<div class="detail-row">
<span class="detail-label">预计完成:</span>
<span class="detail-value" id="detailEstimatedTime">2025-01-03 15:30</span>
</div>
</div>
</div>
<div class="detail-section full-width">
<h3><i class="fas fa-edit"></i> 问题描述</h3>
<div class="detail-content" id="detailDescription">
在湘江橘子洲段发现大量生活垃圾堆积,包括塑料袋、饮料瓶等,影响河道环境美观,需要及时清理。
</div>
</div>
<div class="detail-section full-width">
<h3><i class="fas fa-camera"></i> 现场照片</h3>
<div class="image-gallery" id="detailImages">
<div class="image-item">
<img src="/images/laji1.png" alt="现场照片1" style="width: 100px; height: 100px;">
<span class="image-label">垃圾堆积现场</span>
</div>
<div class="image-item">
<img src="/images/shuiwei.png" alt="现场照片2" style="width: 100px; height: 100px;">
<span class="image-label">周边环境</span>
</div>
</div>
</div>
<div class="detail-section full-width">
<h3><i class="fas fa-history"></i> 处理记录</h3>
<div class="timeline" id="detailTimeline">
<div class="timeline-item">
<div class="timeline-time">2025-01-03 09:30</div>
<div class="timeline-content">
<strong>工单创建</strong><br>
张三创建工单,上报垃圾清理问题
</div>
</div>
<div class="timeline-item">
<div class="timeline-time">2025-01-03 09:45</div>
<div class="timeline-content">
<strong>工单分配</strong><br>
系统自动分配给李四处理
</div>
</div>
</div>
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="closeViewOrderModal()">
<i class="fas fa-times"></i> 关闭
</button>
<button type="button" class="btn btn-primary" onclick="editWorkorderFromView()">
<i class="fas fa-edit"></i> 编辑工单
</button>
<button type="button" class="btn btn-success" onclick="assignWorkorder()">
<i class="fas fa-user-plus"></i> 分配处理
</button>
</div>
</div>
</div>
<!-- 批量派单模态框 -->
<div id="batchModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2><i class="fas fa-tasks"></i> 批量派单</h2>
<span class="close" onclick="closeBatchModal()">&times;</span>
</div>
<form id="batchForm">
<div class="form-group">
<label for="batchOrderSelect"><i class="fas fa-list"></i> 选择工单</label>
<select id="batchOrderSelect" multiple class="filter-btn" style="width: 100%; height: auto; min-height: 180px;">
<!-- 工单选项将通过JS动态加载 -->
</select>
</div>
<div class="form-grid">
<div class="form-group">
<label for="batchAssignee"><i class="fas fa-user"></i> 指派给</label>
<select id="batchAssignee" required>
<option value="">请选择处理人员</option>
<option value="zhang">张三 - 垃圾清理专员</option>
<option value="li">李四 - 设施维护专员</option>
<option value="wang">王五 - 水质监测专员</option>
<option value="zhao">赵六 - 应急处理专员</option>
</select>
</div>
<div class="form-group">
<label for="batchDeadline"><i class="fas fa-calendar-alt"></i> 完成期限</label>
<input type="datetime-local" id="batchDeadline" required>
</div>
</div>
<div class="form-group">
<label for="batchInstructions"><i class="fas fa-clipboard-list"></i> 处理说明</label>
<textarea id="batchInstructions" rows="3" placeholder="请输入统一的处理要求和注意事项"></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="closeBatchModal()">
<i class="fas fa-times"></i> 取消
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-check"></i> 批量派单
</button>
</div>
</form>
</div>
</div>
<!-- 工单跟踪模态框 -->
<div id="trackModal" class="modal">
<div class="modal-content large-modal" style="width: 90%; max-width: 1400px;">
<div class="modal-header">
<h2><i class="fas fa-route"></i> 工单跟踪</h2>
<span class="close" onclick="closeTrackModal()">&times;</span>
</div>
<div class="track-controls">
<select id="trackOrderSelect" class="track-select">
<option value="">选择要跟踪的工单</option>
<option value="WO202501001">WO202501001 - 河道垃圾清理</option>
<option value="WO202501002">WO202501002 - 水质异常处理</option>
<option value="WO202501003">WO202501003 - 设备故障维修</option>
</select>
<button class="action-btn" onclick="refreshTracking()"><i class="fas fa-sync-alt"></i>刷新</button>
<button class="action-btn" onclick="exportTrackReport()"><i class="fas fa-download"></i>导出轨迹</button>
</div>
<div class="track-container" style="max-height: 76vh; overflow-y: auto;">
<!-- 左侧地图和时间线区域 -->
<div class="track-left">
<div class="track-map-container">
<div class="map-header">
<h4><i class="fas fa-map-marked-alt"></i>实时位置跟踪</h4>
<div class="map-controls">
<button class="map-btn active" data-view="satellite"
onclick="switchMapType('satellite')">卫星图</button>
<button class="map-btn" data-view="street"
onclick="switchMapType('street')">街道图</button>
<button class="map-btn" data-view="terrain"
onclick="switchMapType('terrain')">地形图</button>
</div>
</div>
<!-- 修改后的地图容器 -->
<div id="trackingMap" class="tracking-map" style="position: relative; height: 400px;">
<div id="map_track" style="width: 100%; height: 100%;"></div>
<!-- 地图控制按钮 -->
<div class="map-overlay-controls"
style="position: absolute; top: 10px; right: 10px; z-index: 1000;">
<div style="display: flex; flex-direction: column; gap: 5px;">
<button class="map-btn" onclick="showStartPoint()"
style="background: rgba(255,255,255,0.9); padding: 5px 10px; border-radius: 4px; border: 1px solid #d1e0f5; cursor: pointer;">
<i class="fas fa-map-marker" style="color: #28a745;"></i> 起始位置
</button>
<button class="map-btn" onclick="showCurrentPosition()"
style="background: rgba(255,255,255,0.9); padding: 5px 10px; border-radius: 4px; border: 1px solid #d1e0f5; cursor: pointer;">
<i class="fas fa-location-arrow" style="color: #dc3545;"></i> 当前位置
</button>
<button class="map-btn" onclick="showTargetPosition()"
style="background: rgba(255,255,255,0.9); padding: 5px 10px; border-radius: 4px; border: 1px solid #d1e0f5; cursor: pointer;">
<i class="fas fa-bullseye" style="color: #2a7de1;"></i> 目标位置
</button>
<button class="map-btn" onclick="showRoute()"
style="background: rgba(255,255,255,0.9); padding: 5px 10px; border-radius: 4px; border: 1px solid #d1e0f5; cursor: pointer;">
<i class="fas fa-route" style="color: #6610f2;"></i> 行进轨迹
</button>
</div>
</div>
</div>
</div>
<div class="track-timeline">
<h4><i class="fas fa-clock"></i>处理时间线</h4>
<div class="timeline-container" style="margin-top: 10px;">
<div class="timeline-item completed">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">2025-01-10 09:00</div>
<div class="timeline-title">工单创建</div>
<div class="timeline-desc">系统自动创建工单,等待分配</div>
<div class="timeline-user">系统</div>
</div>
</div>
<div class="timeline-item completed">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">2025-01-10 09:15</div>
<div class="timeline-title">工单分配</div>
<div class="timeline-desc">已分配给张三处理</div>
<div class="timeline-user">调度员李四</div>
</div>
</div>
<div class="timeline-item completed">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">2025-01-10 09:30</div>
<div class="timeline-title">开始出发</div>
<div class="timeline-desc">处理人员已出发前往现场</div>
<div class="timeline-user">张三</div>
</div>
</div>
<div class="timeline-item current">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">2025-01-10 10:15</div>
<div class="timeline-title">抵达现场</div>
<div class="timeline-desc">已抵达现场,开始处理</div>
<div class="timeline-user">张三</div>
</div>
</div>
<div class="timeline-item pending">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">预计 11:00</div>
<div class="timeline-title">处理完成</div>
<div class="timeline-desc">预计处理完成时间</div>
<div class="timeline-user">-</div>
</div>
</div>
</div>
</div>
</div>
<!-- 右侧信息区域 -->
<div class="track-right">
<div class="track-info-card">
<h4><i class="fas fa-info-circle"></i>工单信息</h4>
<div class="info-grid" style="margin-top: 10px;">
<div class="info-item">
<span class="info-label">工单编号:</span>
<span class="info-value">WO202501001</span>
</div>
<div class="info-item">
<span class="info-label">工单类型:</span>
<span class="info-value">垃圾清理</span>
</div>
<div class="info-item">
<span class="info-label">优先级:</span>
<span class="info-value priority-high" style="padding: 0 10px;"></span>
</div>
<div class="info-item">
<span class="info-label">当前状态:</span>
<span class="info-value status-arrived" style="padding: 0 10px;">已抵达</span>
</div>
<div class="info-item">
<span class="info-label">处理人员:</span>
<span class="info-value">张三</span>
</div>
<div class="info-item">
<span class="info-label">联系电话:</span>
<span class="info-value">138****1234</span>
</div>
<div class="info-item">
<span class="info-label">预计完成:</span>
<span class="info-value">11:00</span>
</div>
</div>
</div>
<div class="track-stats-card">
<h4><i class="fas fa-chart-line"></i>处理统计</h4>
<div class="stats-grid" style="margin-top: 10px;margin-bottom: 0px;">
<div class="stat-item">
<div class="stat-value">2.5小时</div>
<div class="stat-label">已用时间</div>
</div>
<div class="stat-item">
<div class="stat-value">0.5小时</div>
<div class="stat-label">预计剩余</div>
</div>
<div class="stat-item">
<div class="stat-value">3.2km</div>
<div class="stat-label">行进距离</div>
</div>
<div class="stat-item">
<div class="stat-value">85%</div>
<div class="stat-label">完成进度</div>
</div>
</div>
</div>
<div class="track-actions-card">
<h4><i class="fas fa-tools"></i>快速操作</h4>
<div class="action-buttons" style="margin-top: 10px;">
<button class="action-btn" onclick="callWorker()"><i class="fas fa-phone"></i>联系处理人</button>
<button class="action-btn" onclick="sendMessage()"><i
class="fas fa-comment"></i>发送消息</button>
<button class="action-btn" onclick="requestUpdate()"><i
class="fas fa-sync"></i>请求更新</button>
<button class="action-btn" onclick="escalateOrder()"><i
class="fas fa-exclamation-triangle"></i>升级处理</button>
</div>
</div>
<div class="track-photos-card">
<h4><i class="fas fa-camera"></i>现场照片</h4>
<div class="photos-grid" style="margin-top: 10px;">
<div class="photo-item">
<img src="images/demo.png" alt="现场照片1">
<span class="photo-time">10:15</span>
</div>
<div class="photo-item">
<img src="images/demo.png" alt="现场照片2">
<span class="photo-time">10:20</span>
</div>
<div class="photo-item add-photo" onclick="requestPhoto()">
<i class="fas fa-plus"></i>
<span>请求照片</span>
</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="editOrderModal" class="modal">
<div class="modal-content" style="max-width:1000px;">
<div class="modal-header">
<h2><i class="fas fa-edit"></i> 编辑工单</h2>
<span class="close" onclick="closeEditOrderModal()">&times;</span>
</div>
<form id="editOrderForm" class="order-modal-content">
<div class="form-grid">
<div class="form-group">
<label for="editOrderId"><i class="fas fa-id-card"></i> 工单编号</label>
<input type="text" id="editOrderId" readonly>
</div>
<div class="form-group">
<label for="editOrderType"><i class="fas fa-tags"></i> 工单类型</label>
<select id="editOrderType" 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="editOrderPriority"><i class="fas fa-exclamation-triangle"></i> 优先级</label>
<select id="editOrderPriority" required>
<option value="">请选择优先级</option>
<option value="low">🟢 低优先级</option>
<option value="medium">🟡 中优先级</option>
<option value="high">🔴 高优先级</option>
</select>
</div>
<div class="form-group">
<label for="editOrderStatus"><i class="fas fa-info-circle"></i> 状态</label>
<select id="editOrderStatus" required>
<option value="created">新建</option>
<option value="assigned">待分配</option>
<option value="dispatched">待出发</option>
<option value="departed">已出发</option>
<option value="arrived">已抵达</option>
<option value="processed">已处理</option>
<option value="transported">已转运</option>
<option value="completed">已完成</option>
</select>
</div>
<div class="form-group">
<label for="editOrderGPS"><i class="fas fa-map-marker-alt"></i> GPS坐标</label>
<div class="location-input-group">
<input type="text" id="editOrderGPS" placeholder="请点击地图图标选择坐标" required readonly>
<i class="fas fa-map-marked-alt map-icon" onclick="openEditLocationMap()"></i>
</div>
</div>
<div class="form-group">
<label for="editOrderLocation"><i class="fas fa-location-dot"></i> 位置描述</label>
<input type="text" id="editOrderLocation" placeholder="请输入位置描述" required>
</div>
<div class="form-group">
<label for="editOrderReporter"><i class="fas fa-user"></i> 上报人</label>
<input type="text" id="editOrderReporter" placeholder="请输入上报人姓名" required>
</div>
<div class="form-group">
<label for="editOrderAssignee"><i class="fas fa-user-tie"></i> 负责人</label>
<input type="text" id="editOrderAssignee" placeholder="请输入负责人姓名">
</div>
<div class="form-group">
<label for="editOrderContact"><i class="fas fa-phone"></i> 联系方式</label>
<input type="tel" id="editOrderContact" placeholder="请输入联系电话">
</div>
<div class="form-group">
<label for="editOrderSource"><i class="fas fa-source"></i> 工单来源</label>
<select id="editOrderSource" 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="editOrderDescription"><i class="fas fa-edit"></i> 问题描述</label>
<textarea id="editOrderDescription" rows="4" placeholder="请详细描述发现的问题,包括具体情况、影响范围等"
required></textarea>
</div>
<div class="form-group">
<label for="editOrderRemarks"><i class="fas fa-sticky-note"></i> 备注信息</label>
<textarea id="editOrderRemarks" rows="2" placeholder="其他需要说明的信息(选填)"></textarea>
</div>
</form>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="closeEditOrderModal()">
<i class="fas fa-times"></i> 取消
</button>
<button type="button" class="btn btn-primary" onclick="saveEditedWorkorder()">
<i class="fas fa-save"></i> 保存修改
</button>
</div>
</div>
</div>
<!-- 编辑位置地图模态框 -->
<div id="editLocationMapModal" 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="closeEditLocationMap()">&times;</span>
</div>
<div class="map-container">
<div id="editLocationMap"></div>
<div class="map-controls">
<button class="map-control-btn active" data-map-type="satellite"
onclick="switchEditMapType('satellite', this)">
<i class="fas fa-satellite"></i> 卫星图
</button>
<button class="map-control-btn" data-map-type="street" onclick="switchEditMapType('street', this)">
<i class="fas fa-road"></i> 街道图
</button>
<button class="map-control-btn" data-map-type="terrain"
onclick="switchEditMapType('terrain', this)">
<i class="fas fa-mountain"></i> 地形图
</button>
</div>
</div>
<div class="map-footer">
<button class="btn btn-secondary" onclick="closeEditLocationMap()">
<i class="fas fa-times"></i> 取消
</button>
<button class="btn btn-primary" onclick="confirmEditLocation()">
<i class="fas fa-check"></i> 确定
</button>
</div>
</div>
</div>
<!-- 分配处理模态框 -->
<div id="assignModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2><i class="fas fa-user-plus"></i> 分配处理</h2>
<span class="close" onclick="closeAssignModal()">&times;</span>
</div>
<form id="assignForm">
<div class="form-group">
<label for="assignOrderId"><i class="fas fa-id-card"></i> 工单编号</label>
<input type="text" id="assignOrderId" readonly>
</div>
<div class="form-group">
<label for="assignOrderType"><i class="fas fa-tags"></i> 工单类型</label>
<input type="text" id="assignOrderType" readonly>
</div>
<div class="form-group">
<label for="assignOrderPriority"><i class="fas fa-exclamation-triangle"></i> 优先级</label>
<input type="text" id="assignOrderPriority" readonly>
</div>
<div class="form-group">
<label for="assignOrderLocation"><i class="fas fa-map-marker-alt"></i> 位置</label>
<input type="text" id="assignOrderLocation" readonly>
</div>
<div class="form-grid">
<div class="form-group">
<label for="assignWorker"><i class="fas fa-user-tie"></i> 分配处理人员</label>
<select id="assignWorker" required>
<option value="">请选择处理人员</option>
<option value="zhangsan">张三 - 垃圾清理专员</option>
<option value="lisi">李四 - 水质监测专员</option>
<option value="wangwu">王五 - 设施维护专员</option>
<option value="zhaoliu">赵六 - 应急处理专员</option>
</select>
</div>
<div class="form-group">
<label for="assignDeadline"><i class="fas fa-clock"></i> 完成期限</label>
<input type="datetime-local" id="assignDeadline" required>
</div>
</div>
<div class="form-group">
<label for="assignInstructions"><i class="fas fa-clipboard-list"></i> 处理说明</label>
<textarea id="assignInstructions" rows="3" placeholder="请输入处理要求和注意事项"></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="closeAssignModal()">
<i class="fas fa-times"></i> 取消
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-check"></i> 确认分配
</button>
</div>
</form>
</div>
</div>
<script>
// 标签页切换功能
function switchTab(tabId) {
// 隐藏所有标签页内容
const tabContents = document.querySelectorAll('.tab-content');
tabContents.forEach(content => {
content.classList.remove('active');
});
// 移除所有标签按钮的激活状态
const tabBtns = document.querySelectorAll('.tab-btn');
tabBtns.forEach(btn => {
btn.classList.remove('active');
});
// 显示选中的标签页内容
document.getElementById(tabId).classList.add('active');
// 激活对应的标签按钮
event.target.classList.add('active');
}
// 工单数据
const workorders = [
{
id: 'WO-2025001',
source: 'AI识别',
type: '垃圾清理',
location: '湘江橘子洲段',
priority: 'high',
status: 'created',
assignee: '张三',
createTime: '2025-01-03 09:30',
responseTime: '2小时内'
},
{
id: 'WO-2025002',
source: '巡查上报',
type: '水质污染',
location: '湘江猴子石段',
priority: 'medium',
status: 'assigned',
assignee: '李四',
createTime: '2025-01-03 10:15',
responseTime: '4小时内'
},
{
id: 'WO-2025003',
source: '公众反馈',
type: '设施维护',
location: '湘江风光带',
priority: 'low',
status: 'completed',
assignee: '王五',
createTime: '2025-01-02 14:20',
responseTime: '24小时内'
}, {
id: 'WO-2025004',
source: 'AI识别',
type: '垃圾清理',
location: '湘江橘子洲段',
priority: 'high',
status: 'created',
assignee: '张三',
createTime: '2025-01-03 09:30',
responseTime: '2小时内'
},
{
id: 'WO-2025005',
source: '巡查上报',
type: '水质污染',
location: '湘江猴子石段',
priority: 'medium',
status: 'assigned',
assignee: '李四',
createTime: '2025-01-03 10:15',
responseTime: '4小时内'
},
{
id: 'WO-2025006',
source: '公众反馈',
type: '设施维护',
location: '湘江风光带',
priority: 'low',
status: 'completed',
assignee: '王五',
createTime: '2025-01-02 14:20',
responseTime: '24小时内'
}, {
id: 'WO-2025007',
source: 'AI识别',
type: '垃圾清理',
location: '湘江橘子洲段',
priority: 'high',
status: 'created',
assignee: '张三',
createTime: '2025-01-03 09:30',
responseTime: '2小时内'
},
{
id: 'WO-2025008',
source: '巡查上报',
type: '水质污染',
location: '湘江猴子石段',
priority: 'medium',
status: 'assigned',
assignee: '李四',
createTime: '2025-01-03 10:15',
responseTime: '4小时内'
},
{
id: 'WO-2025009',
source: '公众反馈',
type: '设施维护',
location: '湘江风光带',
priority: 'low',
status: 'completed',
assignee: '王五',
createTime: '2025-01-02 14:20',
responseTime: '24小时内'
}, {
id: 'WO-2025010',
source: 'AI识别',
type: '垃圾清理',
location: '湘江橘子洲段',
priority: 'high',
status: 'created',
assignee: '张三',
createTime: '2025-01-03 09:30',
responseTime: '2小时内'
},
{
id: 'WO-2025011',
source: '巡查上报',
type: '水质污染',
location: '湘江猴子石段',
priority: 'medium',
status: 'assigned',
assignee: '李四',
createTime: '2025-01-03 10:15',
responseTime: '4小时内'
},
{
id: 'WO-2025012',
source: '公众反馈',
type: '设施维护',
location: '湘江风光带',
priority: 'low',
status: 'completed',
assignee: '王五',
createTime: '2025-01-02 14:20',
responseTime: '24小时内'
}, {
id: 'WO-2025013',
source: 'AI识别',
type: '垃圾清理',
location: '湘江橘子洲段',
priority: 'high',
status: 'created',
assignee: '张三',
createTime: '2025-01-03 09:30',
responseTime: '2小时内'
},
{
id: 'WO-2025014',
source: '巡查上报',
type: '水质污染',
location: '湘江猴子石段',
priority: 'medium',
status: 'assigned',
assignee: '李四',
createTime: '2025-01-03 10:15',
responseTime: '4小时内'
},
{
id: 'WO-2025015',
source: '公众反馈',
type: '设施维护',
location: '湘江风光带',
priority: 'low',
status: 'completed',
assignee: '王五',
createTime: '2025-01-02 14:20',
responseTime: '24小时内'
}
];
// 状态映射
const statusMap = {
'created': { text: '新建', class: 'status-created' },
'assigned': { text: '待分配', class: 'status-assigned' },
'dispatched': { text: '待出发', class: 'status-dispatched' },
'departed': { text: '已出发', class: 'status-departed' },
'arrived': { text: '已抵达', class: 'status-arrived' },
'processed': { text: '已处理', class: 'status-processed' },
'transported': { text: '已转运', class: 'status-transported' },
'completed': { text: '已完成', class: 'status-completed' }
};
// 优先级映射
const priorityMap = {
'high': { text: '高', class: 'priority-high' },
'medium': { text: '中', class: 'priority-medium' },
'low': { text: '低', class: 'priority-low' }
};
// 加载工单数据
function loadWorkorders() {
const tableBody = document.getElementById('workorderTableBody');
tableBody.innerHTML = workorders.map(wo => `
<tr>
<td><input type="checkbox" value="${wo.id}"></td>
<td>${wo.id}</td>
<td>${wo.source}</td>
<td>${wo.type}</td>
<td>${wo.location}</td>
<td><span class="priority-badge ${priorityMap[wo.priority].class}">${priorityMap[wo.priority].text}</span></td>
<td><span class="status-badge ${statusMap[wo.status].class}">${statusMap[wo.status].text}</span></td>
<td>${wo.assignee}</td>
<td>${wo.createTime}</td>
<td>${wo.responseTime}</td>
<td>
<button class="btn btn-primary" onclick="viewWorkorder('${wo.id}')">查看</button>
<button class="btn btn-success" onclick="editWorkorder('${wo.id}')">编辑</button>
<button class="btn btn-info" onclick="trackWorkorder('${wo.id}')">跟踪</button>
</td>
</tr>
`).join('');
}
// 新建工单模态框操作
function openNewOrderModal() {
document.getElementById('newOrderModal').style.display = 'block';
}
function closeNewOrderModal() {
document.getElementById('newOrderModal').style.display = 'none';
}
function viewWorkorder(id) {
// 查找对应的工单数据
const workorder = workorders.find(wo => wo.id === id);
if (!workorder) return;
// 填充模态框内容
document.getElementById('detailOrderId').textContent = workorder.id;
document.getElementById('detailOrderType').textContent = workorder.type;
document.getElementById('detailOrderPriority').textContent = priorityMap[workorder.priority].text;
document.getElementById('detailOrderStatus').textContent = statusMap[workorder.status].text;
document.getElementById('detailOrderLocation').textContent = workorder.location;
document.getElementById('detailOrderReporter').textContent = workorder.assignee || "系统";
document.getElementById('detailOrderContact').textContent = "138****5678"; // 模拟数据
document.getElementById('detailCreateTime').textContent = workorder.createTime;
document.getElementById('detailResponseTime').textContent = workorder.responseTime;
document.getElementById('detailEstimatedTime').textContent = "2025-01-03 15:30"; // 模拟数据
// 显示模态框
document.getElementById('viewOrderModal').style.display = 'block';
}
function closeViewOrderModal() {
document.getElementById('viewOrderModal').style.display = 'none';
}
// function editWorkorder(id) {
// alert('编辑工单: ' + id);
// }
function closeTrackModal() {
document.getElementById('trackModal').style.display = 'none';
}
// 派单策略相关函数
function addStrategy() {
alert('新增派单策略功能');
}
function editStrategy(strategyId) {
alert('编辑策略: ' + strategyId);
}
function toggleStrategy(strategyId) {
const strategyItem = event.target.closest('.strategy-item');
const statusSpan = strategyItem.querySelector('.strategy-status span');
const toggleBtn = strategyItem.querySelector('.strategy-actions button:last-child');
// 切换状态
if (statusSpan.classList.contains('status-enabled')) {
// 从启用切换到禁用
statusSpan.classList.replace('status-enabled', 'status-disabled');
statusSpan.textContent = '已禁用';
toggleBtn.classList.replace('btn-disable', 'btn-enable');
toggleBtn.innerHTML = '<i class="fas fa-check"></i> 启用';
strategyItem.classList.remove('active');
} else {
// 从禁用切换到启用
statusSpan.classList.replace('status-disabled', 'status-enabled');
statusSpan.textContent = '已启用';
toggleBtn.classList.replace('btn-enable', 'btn-disable');
toggleBtn.innerHTML = '<i class="fas fa-ban"></i> 禁用';
strategyItem.classList.add('active');
}
// 这里可以添加实际的启用/禁用逻辑
console.log(`策略 ${strategyId} 状态已变更: ${statusSpan.textContent}`);
}
function editStrategy(strategyId) {
// 统一的编辑函数
console.log(`编辑策略: ${strategyId}`);
// 这里可以打开编辑模态框等操作
}
function testStrategy() {
alert('测试派单策略功能');
}
function importStrategy() {
alert('导入派单策略功能');
}
function saveAllSettings() {
alert('保存所有设置');
}
function resetSettings() {
if (confirm('确定要重置所有设置吗?')) {
alert('设置已重置');
}
}
function exportSettings() {
alert('导出配置文件');
}
function exportReport() {
alert('导出工单报表');
}
// 刷新跟踪数据
function refreshTracking() {
alert('刷新跟踪数据');
}
// 导出轨迹报告
function exportTrackReport() {
alert('导出轨迹报告功能开发中...');
}
// 切换图例显示
function toggleLegend() {
const legend = document.getElementById('mapLegend');
const toggle = document.querySelector('.legend-toggle');
if (legend && toggle) {
if (legend.classList.contains('show')) {
legend.classList.remove('show');
toggle.classList.remove('active');
} else {
legend.classList.add('show');
toggle.classList.add('active');
}
}
}
function openBatchModal() {
alert('批量派单功能');
}
// 更新时间
function updateDateTime() {
const now = new Date();
const dateTimeStr = now.toLocaleString('zh-CN');
document.getElementById('datetime').textContent = dateTimeStr;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function () {
initializeCharts();
renderWorkorderTable(currentPage);
//initializeTrackMap();
//loadWorkorders();
// updateDateTime();
// setInterval(updateDateTime, 1000);
// 绑定图表类型切换事件
document.querySelectorAll('[data-chart]').forEach(btn => {
btn.addEventListener('click', function () {
switchChartType(this.dataset.chart);
});
});
// 点击地图外部区域关闭图例
document.addEventListener('click', function (event) {
const legend = document.getElementById('mapLegend');
const toggle = document.querySelector('.legend-toggle');
const mapContainer = document.getElementById('trackingMap');
if (legend && toggle && mapContainer) {
// 如果点击的不是地图容器内的元素,则关闭图例
if (!mapContainer.contains(event.target)) {
legend.classList.remove('show');
toggle.classList.remove('active');
}
}
});
// 阻止图例内部点击事件冒泡
const legend = document.getElementById('mapLegend');
if (legend) {
legend.addEventListener('click', function (event) {
event.stopPropagation();
});
}
});
// 点击模态框外部关闭
window.onclick = function (event) {
const newOrderModal = document.getElementById('newOrderModal');
const trackModal = document.getElementById('trackModal');
const viewOrderModal = document.getElementById('viewOrderModal');
const editOrderModal = document.getElementById('editOrderModal');
const editLocationMapModal = document.getElementById('editLocationMapModal');
const assignModal = document.getElementById('assignModal');
if (event.target === newOrderModal) {
closeNewOrderModal();
}
if (event.target === trackModal) {
closeTrackModal();
}
if (event.target === viewOrderModal) {
closeViewOrderModal();
}
if (event.target === editOrderModal) {
closeEditOrderModal();
}
if (event.target === editLocationMapModal) {
closeEditLocationMap();
}
if (event.target === assignModal) {
closeAssignModal();
}
}
// 初始化所有图表
function initializeCharts() {
// 工单趋势分析图表
const trendCtx = document.getElementById('trendChart').getContext('2d');
new Chart(trendCtx, {
type: 'line',
data: {
labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
datasets: [{
label: '工单数量',
data: [120, 135, 148, 156, 142, 130],
borderColor: '#2a7de1',
backgroundColor: 'rgba(42, 125, 225, 0.1)',
tension: 0.4,
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
// 工单状态分布图表
const statusCtx = document.getElementById('statusChart').getContext('2d');
new Chart(statusCtx, {
type: 'doughnut',
data: {
labels: ['新建', '待分配', '处理中', '已完成', '超时'],
datasets: [{
data: [15, 8, 18, 132, 6],
backgroundColor: ['#6c757d', '#ffc107', '#007bff', '#28a745', '#dc3545'],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
// 本周工单趋势图表
const weeklyCtx = document.getElementById('weeklyTrendChart').getContext('2d');
new Chart(weeklyCtx, {
type: 'line',
data: {
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
datasets: [{
label: '工单数量',
data: [25, 32, 28, 35, 30, 20, 15],
borderColor: '#2a7de1',
backgroundColor: 'rgba(42, 125, 225, 0.1)',
tension: 0.4,
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
// 事件来源分析图表
const sourceCtx = document.getElementById('sourceChart').getContext('2d');
new Chart(sourceCtx, {
type: 'pie',
data: {
labels: ['AI识别', '巡查上报', '公众反馈', '手动创建'],
datasets: [{
data: [45, 35, 15, 5],
backgroundColor: ['#2a7de1', '#1a4b8c', '#28a745', '#ffc107'],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
// // 时间分布图表
// const timeCtx = document.getElementById('timeDistributionChart').getContext('2d');
// new Chart(timeCtx, {
// type: 'pie',
// data: {
// labels: ['08:00-10:00', '10:00-12:00', '12:00-14:00', '14:00-16:00', '16:00-18:00'],
// datasets: [{
// data: [32, 26, 18, 28, 22],
// backgroundColor: ['#2a7de1', '#1a4b8c', '#28a745', '#ffc107', '#6c757d'],
// borderWidth: 1
// }]
// },
// options: {
// responsive: true,
// maintainAspectRatio: false
// }
// });
// 区域分布图表
const regionCtx = document.getElementById('regionChart').getContext('2d');
new Chart(regionCtx, {
type: 'bar',
data: {
labels: ['河道区域', '公园绿地', '道路清洁', '设施维护', '其他区域'],
datasets: [{
label: '工单数量',
data: [42, 28, 22, 16, 8],
backgroundColor: '#2a7de1'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
}
});
// 人员效率图表
const efficiencyCtx = document.getElementById('efficiencyChart').getContext('2d');
new Chart(efficiencyCtx, {
type: 'radar',
data: {
labels: ['张三', '李四', '王五', '赵六'],
datasets: [{
label: '完成率',
data: [96, 91, 87, 75],
backgroundColor: 'rgba(42, 125, 225, 0.2)',
borderColor: '#2a7de1',
pointBackgroundColor: '#2a7de1'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
r: {
angleLines: {
display: true
},
suggestedMin: 50,
suggestedMax: 100
}
}
}
});
// 本周排行图表
const rankingCtx = document.getElementById('rankingChart').getContext('2d');
new Chart(rankingCtx, {
type: 'bar',
data: {
labels: ['张三', '李四', '王五', '赵六', '钱七'],
datasets: [{
label: '得分',
data: [96, 91, 87, 75, 68],
backgroundColor: '#2a7de1'
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false
}
});
// 问题类型图表
const problemTypeCtx = document.getElementById('problemTypeChart').getContext('2d');
new Chart(problemTypeCtx, {
type: 'pie',
data: {
labels: ['垃圾清理', '水质问题', '设备故障', '绿化维护', '道路维护'],
datasets: [{
data: [45, 28, 23, 19, 15],
backgroundColor: ['#2a7de1', '#1a4b8c', '#28a745', '#ffc107', '#6c757d']
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
}
// 图表类型切换功能
function switchChartType(type) {
// 更新按钮状态
document.querySelectorAll('.chart-type-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.chart === type);
});
// 原有的图表切换逻辑保持不变
const allowedTypes = ['line', 'bar', 'pie'];
if (!allowedTypes.includes(type)) return;
const trendCtx = document.getElementById('trendChart').getContext('2d');
const chart = Chart.getChart(trendCtx);
if (chart) chart.destroy();
const data = {
labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
datasets: [{
label: '工单数量',
data: [120, 135, 148, 156, 142, 130],
backgroundColor: type === 'pie' ?
['#2a7de1', '#1a4b8c', '#28a745', '#ffc107', '#6c757d', '#6610f2'] :
'rgba(42, 125, 225, 0.1)',
borderColor: '#2a7de1',
borderWidth: 2
}]
};
new Chart(trendCtx, {
type: type,
data: data,
options: {
responsive: true,
maintainAspectRatio: false
}
});
}
// 分页逻辑集成到真实工单数据
const PAGE_SIZE = 10;
let currentPage = 1;
let filteredWorkorders = workorders;
// 在渲染工单表格时处理位置显示
function renderWorkorderTable(page = 1) {
const tbody = document.getElementById('workorderTableBody');
tbody.innerHTML = '';
const start = (page - 1) * PAGE_SIZE;
const end = Math.min(start + PAGE_SIZE, filteredWorkorders.length);
for (let i = start; i < end; i++) {
const wo = filteredWorkorders[i];
// 处理位置显示 - 如果有GPS坐标则只显示描述部分
let displayLocation = wo.location;
if (wo.location.includes('|')) {
displayLocation = wo.location.split('|')[1];
}
tbody.innerHTML += `
<tr>
<td><input type="checkbox" value="${wo.id}"></td>
<td>${wo.id}</td>
<td>${wo.source}</td>
<td>${wo.type}</td>
<td>${displayLocation}</td>
<td><span class="priority-badge ${priorityMap[wo.priority].class}">${priorityMap[wo.priority].text}</span></td>
<td><span class="status-badge ${statusMap[wo.status].class}">${statusMap[wo.status].text}</span></td>
<td>${wo.assignee}</td>
<td>${wo.createTime}</td>
<td>${wo.responseTime}</td>
<td>
<button class="btn btn-primary" onclick="viewWorkorder('${wo.id}')">查看</button>
<button class="btn btn-success" onclick="editWorkorder('${wo.id}')">编辑</button>
<button class="btn btn-info" onclick="trackWorkorder('${wo.id}')">跟踪</button>
</td>
</tr>
`;
}
renderPagination(page);
}
function renderPagination(page) {
const total = filteredWorkorders.length;
const totalPages = Math.ceil(total / PAGE_SIZE);
const container = document.getElementById('pagination');
container.innerHTML = '';
if (totalPages <= 1) return;
// 上一页
const prev = document.createElement('button');
prev.textContent = '上一页';
prev.className = 'filter-btn';
prev.disabled = page === 1;
prev.onclick = () => gotoPage(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 = 'filter-btn' + (i === page ? ' active' : '');
btn.onclick = () => gotoPage(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 = 'filter-btn';
next.disabled = page === totalPages;
next.onclick = () => gotoPage(page + 1);
container.appendChild(next);
}
function gotoPage(page) {
currentPage = page;
renderWorkorderTable(page);
}
// // 初始化渲染
// document.addEventListener('DOMContentLoaded', function () {
// renderWorkorderTable(currentPage);
// });
// 在script标签中添加以下代码
var TDT_API_KEY = "a4f4b6000cb9d1bf148bb77452000f30";
var trackMap;
var vectorLayer;
function initializeTrackMap() {
// 创建地图图层
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'
});
// 创建地图
trackMap = new ol.Map({
layers: [satelliteLayer, streetLayer, terrainLayer],
target: 'map_track',
view: new ol.View({
projection: 'EPSG:4326',
center: [112.955, 28.195],
zoom: 12 // 增加初始缩放级别
})
});
// 添加矢量图层用于显示轨迹和标记点
vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({ color: 'red' }),
stroke: new ol.style.Stroke({ color: 'white', width: 2 })
}),
stroke: new ol.style.Stroke({
color: 'blue',
width: 3
})
}),
name: 'vector' // 为矢量图层添加名称
});
trackMap.addLayer(vectorLayer);
// 添加示例轨迹和标记点
addSampleTrack();
}
function addSampleTrack() {
// 清空现有要素
vectorLayer.getSource().clear();
// 添加起始点
var startPoint = new ol.Feature({
geometry: new ol.geom.Point([112.952, 28.20]),
name: '起始位置'
});
startPoint.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: '/images/marker-icon-green.png',
scale: 0.8,
color: '#28a745'
})
}));
vectorLayer.getSource().addFeature(startPoint);
// 添加当前位置
var currentPoint = new ol.Feature({
geometry: new ol.geom.Point([112.955, 28.195]),
name: '当前位置'
});
currentPoint.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: '/images/marker-icon-red.png',
scale: 0.8,
color: '#dc3545'
})
}));
vectorLayer.getSource().addFeature(currentPoint);
// 添加目标位置
var targetPoint = new ol.Feature({
geometry: new ol.geom.Point([112.96, 28.19]),
name: '目标位置'
});
targetPoint.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: '/images/marker-icon-blue.png',
scale: 0.8,
color: '#2a7de1'
})
}));
vectorLayer.getSource().addFeature(targetPoint);
// 添加行进轨迹
var route = new ol.Feature({
geometry: new ol.geom.LineString([
[112.952, 28.20],
[112.953, 28.198],
[112.955, 28.195]
]),
name: '行进轨迹'
});
route.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#6610f2',
width: 3
})
}));
vectorLayer.getSource().addFeature(route);
// 调整视图以显示所有要素
var extent = vectorLayer.getSource().getExtent();
trackMap.getView().fit(extent, { padding: [50, 50, 50, 50], maxZoom: 14 });
}
function switchMapType(type) {
// 更新按钮状态
document.querySelectorAll('.map-controls .map-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.view === type);
});
// 切换地图类型,保持矢量图层始终可见
trackMap.getLayers().forEach(layer => {
if (layer.get('name') === 'vector') {
layer.setVisible(true); // 确保矢量图层始终可见
} else {
layer.setVisible(layer.get('name') === type);
}
});
}
function showStartPoint() {
var source = vectorLayer.getSource();
var feature = source.getFeatures().find(f => f.get('name') === '起始位置');
if (feature) {
trackMap.getView().animate({
center: feature.getGeometry().getCoordinates(),
zoom: 16,
duration: 1000
});
}
}
function showCurrentPosition() {
var source = vectorLayer.getSource();
var feature = source.getFeatures().find(f => f.get('name') === '当前位置');
if (feature) {
trackMap.getView().animate({
center: feature.getGeometry().getCoordinates(),
zoom: 16,
duration: 1000
});
}
}
function showTargetPosition() {
var source = vectorLayer.getSource();
var feature = source.getFeatures().find(f => f.get('name') === '目标位置');
if (feature) {
trackMap.getView().animate({
center: feature.getGeometry().getCoordinates(),
zoom: 16,
duration: 1000
});
}
}
function showRoute() {
var source = vectorLayer.getSource();
var feature = source.getFeatures().find(f => f.get('name') === '行进轨迹');
if (feature) {
trackMap.getView().fit(feature.getGeometry(), {
padding: [50, 50, 50, 50],
maxZoom: 14,
duration: 1000
});
}
}
// 确保在模态框显示时初始化地图
function trackWorkorder(id) {
document.getElementById('trackModal').style.display = 'block';
setTimeout(() => {
if (!trackMap) {
initializeTrackMap();
} else {
trackMap.updateSize(); // 确保地图正确渲染
addSampleTrack(); // 重新添加轨迹和标记点
}
}, 100);
}
// 预览上传的图片
function previewImages(input) {
const container = document.getElementById('imagePreviewContainer');
container.innerHTML = ''; // 清空容器
if (input.files && input.files.length > 0) {
for (let i = 0; i < input.files.length; i++) {
const file = input.files[i];
const reader = new FileReader();
reader.onload = function (e) {
const previewDiv = document.createElement('div');
previewDiv.className = 'image-preview-item';
previewDiv.style.position = 'relative';
previewDiv.style.width = '100px';
previewDiv.style.height = '100px';
const img = document.createElement('img');
img.src = e.target.result;
img.style.width = '100%';
img.style.height = '100%';
img.style.objectFit = 'cover';
img.style.borderRadius = '4px';
const deleteBtn = document.createElement('span');
deleteBtn.innerHTML = '&times;';
deleteBtn.style.position = 'absolute';
deleteBtn.style.top = '0';
deleteBtn.style.right = '0';
deleteBtn.style.backgroundColor = 'rgba(0,0,0,0.5)';
deleteBtn.style.color = 'white';
deleteBtn.style.width = '20px';
deleteBtn.style.height = '20px';
deleteBtn.style.borderRadius = '50%';
deleteBtn.style.display = 'flex';
deleteBtn.style.justifyContent = 'center';
deleteBtn.style.alignItems = 'center';
deleteBtn.style.cursor = 'pointer';
deleteBtn.onclick = function () {
removeImage(input, i);
};
previewDiv.appendChild(img);
previewDiv.appendChild(deleteBtn);
container.appendChild(previewDiv);
};
reader.readAsDataURL(file);
}
}
}
// 删除图片
function removeImage(input, index) {
// 从文件列表中删除
const files = Array.from(input.files);
files.splice(index, 1);
// 创建一个新的DataTransfer对象来更新文件输入
const dataTransfer = new DataTransfer();
files.forEach(file => dataTransfer.items.add(file));
input.files = dataTransfer.files;
// 重新渲染预览
previewImages(input);
}
// 地图相关变量
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) {
// 将经纬度坐标显示在输入框中,格式:经度,纬度
document.getElementById('orderGps').value =
`${selectedLocation.coords[0].toFixed(6)}, ${selectedLocation.coords[1].toFixed(6)}`;
// document.getElementById('coordinatesDisplay').textContent =
// `坐标:${selectedLocation.coords[0].toFixed(6)}, ${selectedLocation.coords[1].toFixed(6)}`;
closeLocationMap();
} else {
alert('请先在地图上选择位置');
}
}
// 初始化位置选择地图
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);
});
// 如果已有坐标值,尝试定位
var currentValue = document.getElementById('orderLocation').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 openBatchModal() {
document.getElementById('batchModal').style.display = 'block';
// 填充工单选择下拉框
const select = document.getElementById('batchOrderSelect');
select.innerHTML = '';
// 只显示未完成的工单
const uncompletedWorkorders = workorders.filter(wo =>
wo.status !== 'completed' && wo.status !== 'transported'
);
uncompletedWorkorders.forEach(wo => {
const option = document.createElement('option');
option.value = wo.id;
option.textContent = `${wo.id} - ${wo.type} (${statusMap[wo.status].text})`;
select.appendChild(option);
});
}
// 关闭批量派单模态框
function closeBatchModal() {
document.getElementById('batchModal').style.display = 'none';
}
// 批量派单表单提交
document.getElementById('batchForm').addEventListener('submit', function(e) {
e.preventDefault();
const selectedOptions = Array.from(document.getElementById('batchOrderSelect').selectedOptions);
const worker = document.getElementById('batchAssignee').value;
const deadline = document.getElementById('batchDeadline').value;
const instructions = document.getElementById('batchInstructions').value;
if (selectedOptions.length === 0) {
alert('请至少选择一个工单');
return;
}
if (!worker) {
alert('请选择处理人员');
return;
}
// 获取选中的工单ID
const selectedOrderIds = selectedOptions.map(option => option.value);
// 更新选中的工单
selectedOrderIds.forEach(orderId => {
const workorderIndex = workorders.findIndex(wo => wo.id === orderId);
if (workorderIndex !== -1) {
workorders[workorderIndex].status = 'assigned';
workorders[workorderIndex].assignee = document.getElementById('batchAssignee')
.options[document.getElementById('batchAssignee').selectedIndex].text.split(' - ')[0];
}
});
// 刷新表格显示
renderWorkorderTable(currentPage);
// 关闭模态框
closeBatchModal();
alert(`已成功分配 ${selectedOrderIds.length} 个工单`);
});
// 全局变量用于存储当前编辑的工单ID
let currentEditWorkorderId = null;
let editLocationMap = null;
let editSelectedLocation = null;
// 打开编辑工单模态框
// 打开编辑工单模态框
function editWorkorder(id) {
currentEditWorkorderId = id;
const workorder = workorders.find(wo => wo.id === id);
if (!workorder) return;
// 填充表单数据
document.getElementById('editOrderId').value = workorder.id;
document.getElementById('editOrderType').value = getTypeValue(workorder.type);
document.getElementById('editOrderPriority').value = workorder.priority;
document.getElementById('editOrderStatus').value = workorder.status;
// 分离GPS坐标和位置描述
const locationParts = workorder.location.split('|');
const gpsCoords = locationParts.length > 1 ? locationParts[0] : '';
const locationDesc = locationParts.length > 1 ? locationParts[1] : workorder.location;
document.getElementById('editOrderGPS').value = gpsCoords;
document.getElementById('editOrderLocation').value = locationDesc;
document.getElementById('editOrderReporter').value = workorder.assignee || '';
document.getElementById('editOrderAssignee').value = workorder.assignee || '';
document.getElementById('editOrderContact').value = "138****5678"; // 模拟数据
document.getElementById('editOrderSource').value = getSourceValue(workorder.source);
document.getElementById('editOrderDescription').value = "在湘江橘子洲段发现大量生活垃圾堆积,包括塑料袋、饮料瓶等,影响河道环境美观,需要及时清理。"; // 模拟数据
document.getElementById('editOrderRemarks').value = ""; // 模拟数据
document.getElementById('editOrderModal').style.display = 'block';
}
// 从查看模态框打开编辑
function editWorkorderFromView() {
const id = document.getElementById('detailOrderId').textContent;
editWorkorder(id);
closeViewOrderModal();
}
// 关闭编辑工单模态框
function closeEditOrderModal() {
document.getElementById('editOrderModal').style.display = 'none';
currentEditWorkorderId = null;
}
// 保存编辑后的工单
function saveEditedWorkorder() {
if (!currentEditWorkorderId) return;
const workorderIndex = workorders.findIndex(wo => wo.id === currentEditWorkorderId);
if (workorderIndex === -1) return;
// 获取表单数据
const type = document.getElementById('editOrderType').options[document.getElementById('editOrderType').selectedIndex].text.replace(/[^\u4e00-\u9fa5]/g, '');
const priority = document.getElementById('editOrderPriority').value;
const status = document.getElementById('editOrderStatus').value;
// 合并GPS坐标和位置描述
const gpsCoords = document.getElementById('editOrderGPS').value;
const locationDesc = document.getElementById('editOrderLocation').value;
const location = gpsCoords ? `${gpsCoords}|${locationDesc}` : locationDesc;
const assignee = document.getElementById('editOrderAssignee').value;
const source = document.getElementById('editOrderSource').options[document.getElementById('editOrderSource').selectedIndex].text.replace(/[^\u4e00-\u9fa5]/g, '');
// 更新工单数据
workorders[workorderIndex].type = type;
workorders[workorderIndex].priority = priority;
workorders[workorderIndex].status = status;
workorders[workorderIndex].location = location;
workorders[workorderIndex].assignee = assignee;
workorders[workorderIndex].source = source;
// 更新表格显示
renderWorkorderTable(currentPage);
// 关闭模态框
closeEditOrderModal();
alert('工单修改已保存');
}
// 辅助函数:获取类型值
function getTypeValue(typeText) {
const typeMap = {
'垃圾清理': 'garbage',
'水质污染': 'pollution',
'设施维护': 'facility',
'应急处理': 'emergency',
'植被管理': 'vegetation',
'安全隐患': 'safety'
};
return typeMap[typeText] || '';
}
// 辅助函数:获取来源值
function getSourceValue(sourceText) {
const sourceMap = {
'AI识别': 'ai',
'巡查上报': 'patrol',
'公众反馈': 'public',
'手动创建': 'manual'
};
return sourceMap[sourceText] || '';
}
// 打开编辑位置地图
function openEditLocationMap() {
document.getElementById('editLocationMapModal').style.display = 'block';
setTimeout(initializeEditLocationMap, 100);
setTimeout(() => {
if (!editLocationMap) {
initializeEditLocationMap();
} else {
editLocationMap.updateSize(); // 确保地图正确渲染
}
}, 100);
}
// 关闭编辑位置地图
function closeEditLocationMap() {
document.getElementById('editLocationMapModal').style.display = 'none';
}
// 确认编辑位置
function confirmEditLocation() {
if (editSelectedLocation) {
document.getElementById('editOrderGPS').value =
`${editSelectedLocation.coords[0].toFixed(6)}, ${editSelectedLocation.coords[1].toFixed(6)}`;
closeEditLocationMap();
} else {
alert('请先在地图上选择位置');
}
}
// 初始化编辑位置地图
function initializeEditLocationMap() {
if (editLocationMap) {
editLocationMap.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'
});
// 创建地图
editLocationMap = new ol.Map({
layers: [satelliteLayer, streetLayer, terrainLayer],
target: 'editLocationMap',
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
})
})
});
editLocationMap.addLayer(vectorLayer);
// 添加点击事件
editLocationMap.on('click', function (evt) {
var coords = evt.coordinate;
updateEditSelectedLocation(coords);
});
// 如果已有坐标值,尝试定位
var currentValue = document.getElementById('editOrderLocation').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)) {
updateEditSelectedLocation([lon, lat]);
editLocationMap.getView().setCenter([lon, lat]);
editLocationMap.getView().setZoom(16);
}
}
}
// 更新编辑选择的位置
function updateEditSelectedLocation(coords) {
editSelectedLocation = {
coords: coords
};
// 添加标记
var vectorSource = editLocationMap.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);
// 移动视图到选择的位置
editLocationMap.getView().animate({
center: coords,
zoom: 16,
duration: 500
});
}
// 切换编辑地图类型
function switchEditMapType(type, button) {
if (!editLocationMap) return;
// 更新按钮样式
document.querySelectorAll('#editLocationMapModal .map-control-btn').forEach(btn => {
btn.classList.remove('active');
});
button.classList.add('active');
// 切换地图图层
editLocationMap.getLayers().forEach(layer => {
if (layer.get('name')) {
layer.setVisible(layer.get('name') === type);
}
});
}
// 点击模态框外部关闭
window.onclick = function (event) {
const newOrderModal = document.getElementById('newOrderModal');
const trackModal = document.getElementById('trackModal');
const viewOrderModal = document.getElementById('viewOrderModal');
const editOrderModal = document.getElementById('editOrderModal');
const editLocationMapModal = document.getElementById('editLocationMapModal');
if (event.target === newOrderModal) {
closeNewOrderModal();
}
if (event.target === trackModal) {
closeTrackModal();
}
if (event.target === viewOrderModal) {
closeViewOrderModal();
}
if (event.target === editOrderModal) {
closeEditOrderModal();
}
if (event.target === editLocationMapModal) {
closeEditLocationMap();
}
}
// 打开分配处理模态框
function assignWorkorder() {
// 获取当前工单详情
const orderId = document.getElementById('detailOrderId').textContent;
const orderType = document.getElementById('detailOrderType').textContent;
const orderPriority = document.getElementById('detailOrderPriority').textContent;
const orderLocation = document.getElementById('detailOrderLocation').textContent;
// 填充表单数据
document.getElementById('assignOrderId').value = orderId;
document.getElementById('assignOrderType').value = orderType;
document.getElementById('assignOrderPriority').value = orderPriority;
document.getElementById('assignOrderLocation').value = orderLocation;
// 设置默认完成期限为当前时间+24小时
const now = new Date();
now.setHours(now.getHours() + 24);
document.getElementById('assignDeadline').value = now.toISOString().slice(0, 16);
// 关闭详情模态框,打开分配模态框
closeViewOrderModal();
document.getElementById('assignModal').style.display = 'block';
}
// 关闭分配处理模态框
function closeAssignModal() {
document.getElementById('assignModal').style.display = 'none';
}
// 提交分配表单
document.getElementById('assignForm').addEventListener('submit', function (e) {
e.preventDefault();
const orderId = document.getElementById('assignOrderId').value;
const worker = document.getElementById('assignWorker').value;
const deadline = document.getElementById('assignDeadline').value;
const instructions = document.getElementById('assignInstructions').value;
if (!worker) {
alert('请选择处理人员');
return;
}
// 更新工单状态
const workorderIndex = workorders.findIndex(wo => wo.id === orderId);
if (workorderIndex !== -1) {
workorders[workorderIndex].status = 'assigned';
workorders[workorderIndex].assignee = document.getElementById('assignWorker').options[document.getElementById('assignWorker').selectedIndex].text.split(' - ')[0];
// 更新表格显示
renderWorkorderTable(currentPage);
// 关闭模态框
closeAssignModal();
alert('工单已成功分配');
} else {
alert('工单不存在');
}
});
function submitNewOrder() {
document.getElementById('newOrderForm').dispatchEvent(new Event('submit'));
}
// 新建工单表单提交处理
document.getElementById('newOrderForm').addEventListener('submit', function (e) {
e.preventDefault(); // 阻止表单默认提交
// 收集表单数据
const type = document.getElementById('orderType').options[document.getElementById('orderType').selectedIndex].text.replace(/[^\u4e00-\u9fa5]/g, '');
const priority = document.getElementById('orderPriority').value;
const location = document.getElementById('orderGps').value + "|" + document.getElementById('orderLocation').value;
const reporter = document.getElementById('orderReporter').value;//上报人
const source = document.getElementById('orderSource').options[document.getElementById('orderSource').selectedIndex].text.replace(/[^\u4e00-\u9fa5]/g, '');
// 创建新工单ID (简单示例实际应用中应该使用更复杂的ID生成逻辑)
if (!Array.isArray(workorders)) {
window.workorders = [];
}
const newId = 'WO-' + (new Date().getFullYear()) + (workorders.length + 1).toString().padStart(4, '0');
// 创建新工单对象
const newWorkorder = {
id: newId,
source: source,
type: type,
location: location,
priority: priority,
status: 'created',
assignee: reporter,
createTime: new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false
}).replace(/\//g, '-'),
responseTime: priority === 'high' ? '2小时内' :
priority === 'medium' ? '4小时内' : '24小时内'
};
// 添加到工单数组
workorders.unshift(newWorkorder);
// 更新统计数字
updateStats();
// 刷新表格显示
renderWorkorderTable(1); // 回到第一页显示新添加的工单
// 显示成功提示
alert('工单创建成功: ' + newId);
// 关闭模态框
closeNewOrderModal();
// 重置表单
this.reset();
document.getElementById('imagePreviewContainer').innerHTML = '';
});
// 更新统计数字的函数
function updateStats() {
document.getElementById('totalOrders').textContent = workorders.length;
document.getElementById('pendingOrders').textContent = workorders.filter(wo => wo.status === 'created' || wo.status === 'assigned').length;
document.getElementById('processingOrders').textContent = workorders.filter(wo => wo.status === 'dispatched' || wo.status === 'departed' || wo.status === 'arrived').length;
document.getElementById('completedOrders').textContent = workorders.filter(wo => wo.status === 'completed').length;
document.getElementById('aiOrders').textContent = workorders.filter(wo => wo.source === 'AI识别').length;
document.getElementById('publicOrders').textContent = workorders.filter(wo => wo.source === '公众反馈').length;
}
</script>
</body>
</html>