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