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

4 months ago
<!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>