|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<th:block th:include="include :: header('车辆详情')" />
|
|
|
|
|
|
<style>
|
|
|
|
|
|
.detail-shell {
|
|
|
|
|
|
padding: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-card,
|
|
|
|
|
|
.info-card,
|
|
|
|
|
|
.timeline-card {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-card {
|
|
|
|
|
|
padding: 20px 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-name {
|
|
|
|
|
|
font-size: 34px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-meta {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-counts {
|
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: minmax(130px, 1fr);
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.count-item {
|
|
|
|
|
|
border: 1px solid #edf1f2;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
background: #fafbfc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.count-value {
|
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #1c84c6;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.count-label {
|
|
|
|
|
|
color: #7a8590;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-card {
|
|
|
|
|
|
padding: 18px 20px 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
margin-bottom: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title i {
|
|
|
|
|
|
margin-right: 6px;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-table {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
|
table-layout: fixed;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-table th,
|
|
|
|
|
|
.info-table td {
|
|
|
|
|
|
border: 1px solid #edf1f2;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-table th {
|
|
|
|
|
|
width: 28%;
|
|
|
|
|
|
color: #7a8590;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
background: #fafbfc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-table td {
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tyre-block {
|
|
|
|
|
|
border: 1px solid #edf1f2;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tyre-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tyre-meta {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
line-height: 1.8;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.maintenance-summary {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
line-height: 1.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.maintenance-compare {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.maintenance-side {
|
|
|
|
|
|
border: 1px solid #edf1f2;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.maintenance-side-title {
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.axle-row {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.axle-label {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
padding: 2px 7px;
|
|
|
|
|
|
border: 1px solid #e7eaec;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 90px;
|
|
|
|
|
|
min-height: 174px;
|
|
|
|
|
|
padding: 24px 6px 8px;
|
|
|
|
|
|
border-radius: 8px 8px 16px 16px;
|
|
|
|
|
|
background: #2f4050;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card.empty {
|
|
|
|
|
|
border: 1px dashed #c9cfd6;
|
|
|
|
|
|
background: #eef1f4;
|
|
|
|
|
|
color: #8a929a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card.installed {
|
|
|
|
|
|
border: 2px solid #f8ac59;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card.removed {
|
|
|
|
|
|
border: 2px solid #1ab394;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card:before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
opacity: 0.16;
|
|
|
|
|
|
background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, #fff 8px, #fff 14px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card.empty:before {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-pos {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
|
border-radius: 0 0 8px 0;
|
|
|
|
|
|
background: #8a929a;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card.installed .compare-pos {
|
|
|
|
|
|
background: #f8ac59;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-card.removed .compare-pos {
|
|
|
|
|
|
background: #1ab394;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-tyre-info {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.compare-depth {
|
|
|
|
|
|
margin-top: 3px;
|
|
|
|
|
|
color: #1ab394;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.clickable-title {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.clickable-title:hover {
|
|
|
|
|
|
color: #1c84c6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.timeline-card {
|
|
|
|
|
|
padding: 18px 18px 12px;
|
|
|
|
|
|
min-height: 520px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.timeline-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.timeline-header .title {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mini-tip {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-list {
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-list:before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 22px;
|
|
|
|
|
|
top: 8px;
|
|
|
|
|
|
bottom: 8px;
|
|
|
|
|
|
width: 2px;
|
|
|
|
|
|
background: #e4e7ea;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-item {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding-left: 58px;
|
|
|
|
|
|
margin-bottom: 22px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-dot {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 12px;
|
|
|
|
|
|
top: 4px;
|
|
|
|
|
|
width: 22px;
|
|
|
|
|
|
height: 22px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
border: 3px solid #d7ebf8;
|
|
|
|
|
|
background: #1c84c6;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
line-height: 16px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-dot.install {
|
|
|
|
|
|
background: #1c84c6;
|
|
|
|
|
|
border-color: #d7ebf8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-dot.maintenance {
|
|
|
|
|
|
background: #f8ac59;
|
|
|
|
|
|
border-color: #fde6c8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-dot.check {
|
|
|
|
|
|
background: #1ab394;
|
|
|
|
|
|
border-color: #d7f0eb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-dot.mileage {
|
|
|
|
|
|
background: #23c6c8;
|
|
|
|
|
|
border-color: #d7f4f4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-time {
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-title {
|
|
|
|
|
|
color: #2f4050;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-body {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
line-height: 1.7;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-detail-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
|
gap: 6px 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-detail {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-detail .label-title {
|
|
|
|
|
|
color: #8a929a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.life-description {
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
|
border-left: 3px solid #e4e7ea;
|
|
|
|
|
|
background: #fafbfc;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.text-muted-dash {
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 991px) {
|
|
|
|
|
|
.summary-title {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-counts {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
|
.summary-counts,
|
|
|
|
|
|
.life-detail-grid,
|
|
|
|
|
|
.maintenance-compare {
|
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.summary-name {
|
|
|
|
|
|
font-size: 26px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body class="gray-bg">
|
|
|
|
|
|
<div class="wrapper wrapper-content detail-shell">
|
|
|
|
|
|
<input id="carNo" type="hidden" th:value="${carNo}"/>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="summary-card">
|
|
|
|
|
|
<div class="summary-title">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="summary-name" id="summaryName">车辆:-</div>
|
|
|
|
|
|
<div class="summary-meta" id="summaryMeta">正在加载...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="summary-counts" id="summaryCounts"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
|
<!-- 暂时隐藏基础信息与当前装车轮胎,避免车辆详情对话框出现左右分栏导致主视图宽度不足。 -->
|
|
|
|
|
|
<!--<div class="col-lg-4 col-md-5 col-sm-12">
|
|
|
|
|
|
<div class="info-card">
|
|
|
|
|
|
<div class="section-title"><i class="fa fa-bookmark"></i>基本信息</div>
|
|
|
|
|
|
<table class="info-table" id="vehicleInfo"></table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-card">
|
|
|
|
|
|
<div class="section-title"><i class="fa fa-truck"></i>当前装车轮胎</div>
|
|
|
|
|
|
<div id="mountedTyres">
|
|
|
|
|
|
<div class="text-center text-muted-dash" style="padding: 40px 0;">正在加载...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>-->
|
|
|
|
|
|
|
|
|
|
|
|
<div class="col-lg-12 col-md-12 col-sm-12">
|
|
|
|
|
|
<div class="info-card">
|
|
|
|
|
|
<div class="section-title"><i class="fa fa-wrench"></i>轮位视图</div>
|
|
|
|
|
|
<div id="latestMaintenance">
|
|
|
|
|
|
<div class="text-center text-muted-dash" style="padding: 48px 0;">正在加载...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="timeline-card">
|
|
|
|
|
|
<div class="timeline-header">
|
|
|
|
|
|
<span class="title">生命周期</span>
|
|
|
|
|
|
<span class="mini-tip">按时间倒序展示维保工单,点击记录打开维保工单详情页签</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<ul class="life-list" id="timelineList"></ul>
|
|
|
|
|
|
<div class="text-center text-muted-dash" id="emptyTimeline" style="display: none; padding: 80px 0;">
|
|
|
|
|
|
暂无生命周期
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<th:block th:include="include :: footer" />
|
|
|
|
|
|
<script th:inline="javascript">
|
|
|
|
|
|
var prefix = ctx + "tyre/car";
|
|
|
|
|
|
var carNo = $("#carNo").val();
|
|
|
|
|
|
var POSITION_ORDER = ["左前轮", "右前轮", "右内轮", "右外轮", "左内轮", "左外轮"];
|
|
|
|
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
|
|
loadLifecycle();
|
|
|
|
|
|
loadWheelPositionView();
|
|
|
|
|
|
// 兜底关闭父窗口 loading,避免 iframe 加载时序导致遮罩残留。
|
|
|
|
|
|
if (window.parent && window.parent !== window && window.parent.$ && window.parent.$.modal) {
|
|
|
|
|
|
window.parent.$.modal.closeLoading();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function loadLifecycle() {
|
|
|
|
|
|
$.get(prefix + "/lifecycle/" + encodeURIComponent(carNo), function (result) {
|
|
|
|
|
|
if (result.code != web_status.SUCCESS) {
|
|
|
|
|
|
$.modal.alertError(result.msg);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var data = result.data || {};
|
|
|
|
|
|
renderSummary(data);
|
|
|
|
|
|
// 基本信息与当前装车轮胎已从页面隐藏,保留汇总和生命周期即可让主内容与对话框等宽。
|
|
|
|
|
|
// renderVehicleInfo(data.car || {});
|
|
|
|
|
|
// renderMountedTyres(data.mountedTyres || []);
|
|
|
|
|
|
// 维保历史已合并到生命周期聚合接口,页面不再额外调用独立列表接口,避免同一车牌重复查数。
|
|
|
|
|
|
renderTimeline(data.maintenanceList || []);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function loadWheelPositionView() {
|
|
|
|
|
|
$.get(prefix + "/lifecycle/" + encodeURIComponent(carNo) + "/latest-maintenance", function (result) {
|
|
|
|
|
|
if (result.code != web_status.SUCCESS) {
|
|
|
|
|
|
$("#latestMaintenance").html('<div class="text-center text-muted-dash" style="padding: 48px 0;">轮位视图加载失败</div>');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
renderWheelPositionView(result.data || []);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderSummary(data) {
|
|
|
|
|
|
var car = data.car || {};
|
|
|
|
|
|
var latestMileage = car.inputMileage == null ? "-" : safeText(car.inputMileage) + " km";
|
|
|
|
|
|
$("#summaryName").html("车辆:" + safeText(car.carNo));
|
|
|
|
|
|
$("#summaryMeta").html([
|
|
|
|
|
|
"所属车队:" + safeText(car.team),
|
|
|
|
|
|
"线路:" + safeText(car.line),
|
|
|
|
|
|
"车型:" + safeText(car.type),
|
|
|
|
|
|
"当前里程:" + latestMileage
|
|
|
|
|
|
].join("<br/>"));
|
|
|
|
|
|
|
|
|
|
|
|
$("#summaryCounts").html(countItem("维保工单", data.maintenanceCount));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderVehicleInfo(car) {
|
|
|
|
|
|
$("#vehicleInfo").html([
|
|
|
|
|
|
tableRow("车牌", car.carNo),
|
|
|
|
|
|
tableRow("车队", car.team),
|
|
|
|
|
|
tableRow("线路", car.line),
|
|
|
|
|
|
tableRow("车型", car.type),
|
|
|
|
|
|
tableRow("车辆最新里程", car.inputMileage == null ? "-" : safeText(car.inputMileage) + " km")
|
|
|
|
|
|
// tableRow("车辆主键", car.carId),
|
|
|
|
|
|
// tableRow("部门ID", car.deptId),
|
|
|
|
|
|
// tableRow("最近工单里程", car.inputMileage)
|
|
|
|
|
|
].join(""));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderMountedTyres(rows) {
|
|
|
|
|
|
if (!rows.length) {
|
|
|
|
|
|
$("#mountedTyres").html('<div class="text-center text-muted-dash" style="padding: 40px 0;">暂无当前装车轮胎</div>');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var html = [];
|
|
|
|
|
|
$.each(rows, function (index, item) {
|
|
|
|
|
|
// 当前装车轮胎作为车辆当前状态展示,便于和时间轴历史装卸节点互相核对。
|
|
|
|
|
|
html.push('<div class="tyre-block">');
|
|
|
|
|
|
html.push('<div class="tyre-title"><span>' + safeText(item.tyreNo || item.selfNo || item.tyreEpc) + '</span><span>' + safeText(item.wheelPostion) + '</span></div>');
|
|
|
|
|
|
html.push('<div class="tyre-meta">');
|
|
|
|
|
|
html.push('自编号:' + safeText(item.selfNo) + '<br/>');
|
|
|
|
|
|
html.push('RFID:' + safeText(item.tyreEpc) + '<br/>');
|
|
|
|
|
|
html.push('品牌/规格:' + safeText(joinText(item.tyreBrand, item.tyreModel)) + '<br/>');
|
|
|
|
|
|
html.push('花纹深度:' + safeText(item.patternDepth));
|
|
|
|
|
|
html.push('</div></div>');
|
|
|
|
|
|
});
|
|
|
|
|
|
$("#mountedTyres").html(html.join(""));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderTimeline(rows) {
|
|
|
|
|
|
var maintenanceRows = rows || [];
|
|
|
|
|
|
if (!maintenanceRows.length) {
|
|
|
|
|
|
$("#timelineList").empty();
|
|
|
|
|
|
$("#emptyTimeline").show();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$("#emptyTimeline").hide();
|
|
|
|
|
|
var html = [];
|
|
|
|
|
|
$.each(maintenanceRows, function (index, item) {
|
|
|
|
|
|
var orderId = item.orderId == null ? item.id : item.orderId;
|
|
|
|
|
|
// 生命周期页嵌在车辆抽屉 iframe 中,只把业务主键交给父页,由父页接管主框架页签与抽屉关闭。
|
|
|
|
|
|
html.push('<li class="life-item clickable-title" data-order-id="'
|
|
|
|
|
|
+ encodeURIComponent(displayValue(orderId)) + '" data-order-no="'
|
|
|
|
|
|
+ encodeURIComponent(displayValue(item.orderNo))
|
|
|
|
|
|
+ '" onclick="openMaintenanceOrderDetail(this)" title="打开维保工单详情页签">');
|
|
|
|
|
|
html.push('<span class="life-dot maintenance">维</span>');
|
|
|
|
|
|
html.push('<div class="life-time">' + safeText(item.maintainDate) + '</div>');
|
|
|
|
|
|
html.push('<div class="life-title">' + safeText(eventTitle(item)) + '</div>');
|
|
|
|
|
|
html.push('<div class="life-body">' + eventDetails(item) + '</div>');
|
|
|
|
|
|
html.push('</li>');
|
|
|
|
|
|
});
|
|
|
|
|
|
$("#timelineList").html(html.join(""));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderWheelPositionView(rows) {
|
|
|
|
|
|
if (!rows || !rows.length) {
|
|
|
|
|
|
$("#latestMaintenance").html('<div class="text-center text-muted-dash" style="padding: 48px 0;">暂无轮位维护数据</div>');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var html = [];
|
|
|
|
|
|
html.push(renderCompareTyres(rows));
|
|
|
|
|
|
$("#latestMaintenance").html(html.join(""));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderCompareTyres(rows) {
|
|
|
|
|
|
var data = transformTyreData(rows);
|
|
|
|
|
|
var html = [];
|
|
|
|
|
|
html.push('<div class="axle-row"><div class="axle-label">1</div>');
|
|
|
|
|
|
html.push(createTyreCard(data[0], "左前轮"));
|
|
|
|
|
|
html.push(createTyreCard(data[1], "右前轮"));
|
|
|
|
|
|
html.push('</div>');
|
|
|
|
|
|
html.push('<div class="axle-row"><div class="axle-label">2</div>');
|
|
|
|
|
|
html.push(createTyreCard(data[2], "右内轮"));
|
|
|
|
|
|
html.push(createTyreCard(data[3], "右外轮"));
|
|
|
|
|
|
html.push(createTyreCard(data[4], "左内轮"));
|
|
|
|
|
|
html.push(createTyreCard(data[5], "左外轮"));
|
|
|
|
|
|
html.push('</div>');
|
|
|
|
|
|
return html.join("");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function transformTyreData(rows) {
|
|
|
|
|
|
var result = new Array(6).fill(null);
|
|
|
|
|
|
$.each(rows || [], function (index, item) {
|
|
|
|
|
|
var position = displayValue(item.position || item.pos);
|
|
|
|
|
|
var posIndex = POSITION_ORDER.indexOf(position);
|
|
|
|
|
|
if (posIndex === -1) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
result[posIndex] = {
|
|
|
|
|
|
pos: position,
|
|
|
|
|
|
brand: displayValue(item.brand),
|
|
|
|
|
|
spec: displayValue(item.spec),
|
|
|
|
|
|
dot: displayValue(item.dot),
|
|
|
|
|
|
depth: item.depth == null || item.depth === "" ? "-" : item.depth + " mm",
|
|
|
|
|
|
status: displayValue(item.status),
|
|
|
|
|
|
orderNo: displayValue(item.orderNo),
|
|
|
|
|
|
maintainDate: displayValue(item.maintainDate),
|
|
|
|
|
|
inputMileage: item.inputMileage == null ? "-" : item.inputMileage + " km"
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function createTyreCard(tyre, fallbackPos) {
|
|
|
|
|
|
if (!tyre) {
|
|
|
|
|
|
return '<div class="compare-tyre-card empty"><div class="compare-pos">' + safeText(fallbackPos) + '</div></div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
var statusClass = tyre.status === "installed" ? " installed" : (tyre.status === "removed" ? " removed" : "");
|
|
|
|
|
|
return [
|
|
|
|
|
|
'<div class="compare-tyre-card' + statusClass + '">',
|
|
|
|
|
|
'<div class="compare-pos">' + safeText(tyre.pos) + '</div>',
|
|
|
|
|
|
'<div class="compare-tyre-info">',
|
|
|
|
|
|
'<div>' + safeText(tyre.brand) + '</div>',
|
|
|
|
|
|
'<div>' + safeText(tyre.spec) + '</div>',
|
|
|
|
|
|
'<div>' + safeText(tyre.dot) + '</div>',
|
|
|
|
|
|
'<div class="compare-depth">' + safeText(tyre.depth) + '</div>',
|
|
|
|
|
|
'<div>工单:' + safeText(tyre.orderNo) + '</div>',
|
|
|
|
|
|
'<div>日期:' + safeText(tyre.maintainDate) + '</div>',
|
|
|
|
|
|
'<div>里程:' + safeText(tyre.inputMileage) + '</div>',
|
|
|
|
|
|
'</div>',
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
].join("");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function eventDetails(item) {
|
|
|
|
|
|
var details = [];
|
|
|
|
|
|
if (item.eventType == "INSTALL") {
|
|
|
|
|
|
// 装卸节点保留轮胎、轮位、里程和花纹深度,方便追溯“何时装到哪个轮位、卸下时状态如何”。
|
|
|
|
|
|
details.push(detail("胎号", item.tyreNo));
|
|
|
|
|
|
details.push(detail("自编号", item.selfNo));
|
|
|
|
|
|
details.push(detail("RFID", item.tyreRfid));
|
|
|
|
|
|
details.push(detail("品牌/规格", joinText(item.tyreBrand, item.tyreModel)));
|
|
|
|
|
|
details.push(detail("轮位", item.wheelPostion));
|
|
|
|
|
|
details.push(detail("里程", item.mileage));
|
|
|
|
|
|
details.push(detail("花纹深度", item.patternDepth));
|
|
|
|
|
|
details.push(detail("动作", installTypeFormatter(item.status)));
|
|
|
|
|
|
} else if (item.eventType == "MAINTENANCE" || item.orderNo) {
|
|
|
|
|
|
// 工单节点展示工单号和站点,排查车辆维保历史时不用再跳转列表反查。
|
|
|
|
|
|
details.push(detail("工单号", item.orderNo));
|
|
|
|
|
|
details.push(detail("维保类型", orderTypeFormatter(item.typeCode)));
|
|
|
|
|
|
details.push(detail("状态", statusFormatter(item.status)));
|
|
|
|
|
|
details.push(detail("维修站点", item.factoryName));
|
|
|
|
|
|
details.push(detail("录入里程", item.inputMileage == null ? "-" : item.inputMileage + " km"));
|
|
|
|
|
|
details.push(detail("上次里程", item.lastMileage == null ? "-" : item.lastMileage + " km"));
|
|
|
|
|
|
details.push(detail("维保日期", item.maintainDate));
|
|
|
|
|
|
} else if (item.eventType == "CHECK") {
|
|
|
|
|
|
// 质检节点通过曾装车轮胎间接归属车辆,页面上明确展示轮胎身份,避免误以为质检表直接有车牌。
|
|
|
|
|
|
details.push(detail("胎号", item.tyreNo));
|
|
|
|
|
|
details.push(detail("自编号", item.selfNo));
|
|
|
|
|
|
details.push(detail("RFID", item.tyreRfid));
|
|
|
|
|
|
details.push(detail("品牌/规格", joinText(item.tyreBrand, item.tyreModel)));
|
|
|
|
|
|
details.push(detail("保养类型", item.maintenanceType));
|
|
|
|
|
|
details.push(detail("车辆里程", item.mileage));
|
|
|
|
|
|
details.push(detail("花纹深度", item.patternDepth));
|
|
|
|
|
|
details.push(detail("处理意见", item.result));
|
|
|
|
|
|
} else if (item.eventType == "MILEAGE") {
|
|
|
|
|
|
// 里程节点展示安装/卸下时间窗,提醒用户该记录是按轮胎 RFID 间接关联车辆。
|
|
|
|
|
|
details.push(detail("胎号", item.tyreNo));
|
|
|
|
|
|
details.push(detail("自编号", item.selfNo));
|
|
|
|
|
|
details.push(detail("RFID", item.tyreRfid));
|
|
|
|
|
|
details.push(detail("品牌/规格", joinText(item.tyreBrand, item.tyreModel)));
|
|
|
|
|
|
details.push(detail("安装时间", item.startTime));
|
|
|
|
|
|
details.push(detail("卸下时间", item.endTime));
|
|
|
|
|
|
details.push(detail("记录里程", item.mileage));
|
|
|
|
|
|
details.push(detail("花纹深度", item.patternDepth));
|
|
|
|
|
|
}
|
|
|
|
|
|
var html = '<div class="life-detail-grid">' + details.join("") + '</div>';
|
|
|
|
|
|
if (item.description) {
|
|
|
|
|
|
html += '<div class="life-description">补充说明:' + safeText(item.description) + '</div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
return html;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function eventTitle(item) {
|
|
|
|
|
|
if (item.eventType == "INSTALL") {
|
|
|
|
|
|
return installTypeFormatter(item.status) + "轮胎:" + displayValue(item.tyreNo || item.selfNo || item.tyreRfid);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.eventType == "MAINTENANCE") {
|
|
|
|
|
|
return orderTypeFormatter(item.typeCode) + ":" + displayValue(item.orderNo);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.orderNo) {
|
|
|
|
|
|
// 合并接口后时间线直接接收维保工单DTO,通过工单号识别维保节点。
|
|
|
|
|
|
return orderTypeFormatter(item.typeCode) + ":" + displayValue(item.orderNo);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.eventType == "CHECK") {
|
|
|
|
|
|
return "轮胎质检:" + displayValue(item.tyreNo || item.selfNo || item.tyreRfid);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.eventType == "MILEAGE") {
|
|
|
|
|
|
return "轮胎里程:" + displayValue(item.tyreNo || item.selfNo || item.tyreRfid);
|
|
|
|
|
|
}
|
|
|
|
|
|
return displayValue(item.eventType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function eventClass(type) {
|
|
|
|
|
|
if (type == "MAINTENANCE") {
|
|
|
|
|
|
return "maintenance";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (type == "CHECK") {
|
|
|
|
|
|
return "check";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (type == "MILEAGE") {
|
|
|
|
|
|
return "mileage";
|
|
|
|
|
|
}
|
|
|
|
|
|
return "install";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function eventIcon(type) {
|
|
|
|
|
|
if (type == "MAINTENANCE") {
|
|
|
|
|
|
return "维";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (type == "CHECK") {
|
|
|
|
|
|
return "检";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (type == "MILEAGE") {
|
|
|
|
|
|
return "里";
|
|
|
|
|
|
}
|
|
|
|
|
|
return "装";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function detail(name, value) {
|
|
|
|
|
|
return '<div class="life-detail"><span class="label-title">' + safeText(name) + ':</span>' + safeText(value) + '</div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function metaCell(name, value) {
|
|
|
|
|
|
return '<div>' + safeText(name) + ':' + safeText(value) + '</div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function tableRow(name, value) {
|
|
|
|
|
|
return '<tr><th>' + safeText(name) + '</th><td>' + safeText(value) + '</td></tr>';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function countItem(name, value) {
|
|
|
|
|
|
return '<div class="count-item"><div class="count-value">' + safeText(value) + '</div><div class="count-label">' + safeText(name) + '</div></div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function joinText(left, right) {
|
|
|
|
|
|
var first = displayValue(left);
|
|
|
|
|
|
var second = displayValue(right);
|
|
|
|
|
|
if (first == "-" && second == "-") {
|
|
|
|
|
|
return "-";
|
|
|
|
|
|
}
|
|
|
|
|
|
return (first == "-" ? "" : first) + (second == "-" ? "" : " / " + second);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function displayValue(value) {
|
|
|
|
|
|
var text = $.common.nullToStr(value);
|
|
|
|
|
|
return text === "" ? "-" : text;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function safeText(value) {
|
|
|
|
|
|
return escapeHtml(displayValue(value));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function escapeHtml(value) {
|
|
|
|
|
|
return String(value)
|
|
|
|
|
|
.replace(/&/g, "&")
|
|
|
|
|
|
.replace(/</g, "<")
|
|
|
|
|
|
.replace(/>/g, ">")
|
|
|
|
|
|
.replace(/"/g, """)
|
|
|
|
|
|
.replace(/'/g, "'");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function installTypeFormatter(value) {
|
|
|
|
|
|
if (value == "0") {
|
|
|
|
|
|
return "安装";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value == "1") {
|
|
|
|
|
|
return "卸下";
|
|
|
|
|
|
}
|
|
|
|
|
|
return displayValue(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function orderTypeFormatter(value) {
|
|
|
|
|
|
if (value == "1") {
|
|
|
|
|
|
return "二级保养";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value == "4") {
|
|
|
|
|
|
return "月检";
|
|
|
|
|
|
}
|
|
|
|
|
|
return displayValue(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function statusFormatter(value) {
|
|
|
|
|
|
if (value == "UNSTARTED") {
|
|
|
|
|
|
return "未开始";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value == "PROCESSING") {
|
|
|
|
|
|
return "执行中";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value == "COMPLETED") {
|
|
|
|
|
|
return "已完成";
|
|
|
|
|
|
}
|
|
|
|
|
|
return displayValue(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openMaintenanceOrderDetail(target) {
|
|
|
|
|
|
var orderId = decodeURIComponent($(target).attr("data-order-id") || "");
|
|
|
|
|
|
var orderNo = decodeURIComponent($(target).attr("data-order-no") || "");
|
|
|
|
|
|
if (!/^\d+$/.test(orderId)) {
|
|
|
|
|
|
// 没有有效工单主键时禁止拼接详情 URL,避免无意义请求或路径污染。
|
|
|
|
|
|
$.modal.alertWarning("该维保记录缺少有效工单ID,无法打开详情");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 生命周期页嵌在车辆列表抽屉 iframe 中,必须交给父页打开主框架页签并关闭抽屉。
|
|
|
|
|
|
if (window.parent && typeof window.parent.openMaintenanceOrderDetailTabFromLifecycle === "function") {
|
|
|
|
|
|
window.parent.openMaintenanceOrderDetailTabFromLifecycle(orderId, orderNo);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 兜底:若未来生命周期页被独立打开,仍按 RuoYi 页签方式进入对应工单详情。
|
|
|
|
|
|
$.modal.openTab(buildMaintenanceOrderDetailTitle(orderNo), ctx + "tyre/order/edit/" + encodeURIComponent(orderId));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildMaintenanceOrderDetailTitle(orderNo) {
|
|
|
|
|
|
var text = displayValue(orderNo);
|
|
|
|
|
|
return text == "-" ? "维保工单详情" : "维保工单详情 - " + text;
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|