|
|
|
|
@ -43,9 +43,9 @@
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.summary-counts {
|
|
|
|
|
min-width: 360px;
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, minmax(130px, 1fr));
|
|
|
|
|
grid-template-columns: minmax(130px, 1fr);
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -136,6 +136,135 @@
|
|
|
|
|
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: 66px;
|
|
|
|
|
min-height: 126px;
|
|
|
|
|
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;
|
|
|
|
|
@ -277,7 +406,8 @@
|
|
|
|
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
.summary-counts,
|
|
|
|
|
.life-detail-grid {
|
|
|
|
|
.life-detail-grid,
|
|
|
|
|
.maintenance-compare {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -317,14 +447,21 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="col-lg-8 col-md-7 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>
|
|
|
|
|
<span class="title clickable-title" onclick="goMaintenanceList()">保养信息</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>
|
|
|
|
|
@ -335,9 +472,11 @@
|
|
|
|
|
<script th:inline="javascript">
|
|
|
|
|
var prefix = ctx + "tyre/car";
|
|
|
|
|
var carNo = $("#carNo").val();
|
|
|
|
|
var POSITION_ORDER = ["左前轮", "右前轮", "左外轮", "左内轮", "右内轮", "右外轮"];
|
|
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
|
loadLifecycle();
|
|
|
|
|
loadLatestMaintenance();
|
|
|
|
|
// 兜底关闭父窗口 loading,避免 iframe 加载时序导致遮罩残留。
|
|
|
|
|
if (window.parent && window.parent !== window && window.parent.$ && window.parent.$.modal) {
|
|
|
|
|
window.parent.$.modal.closeLoading();
|
|
|
|
|
@ -354,26 +493,33 @@
|
|
|
|
|
renderSummary(data);
|
|
|
|
|
renderVehicleInfo(data.car || {});
|
|
|
|
|
renderMountedTyres(data.mountedTyres || []);
|
|
|
|
|
renderTimeline(data.recentEvents || []);
|
|
|
|
|
// 维保历史已合并到生命周期聚合接口,页面不再额外调用独立列表接口,避免同一车牌重复查数。
|
|
|
|
|
renderTimeline(data.maintenanceList || []);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadLatestMaintenance() {
|
|
|
|
|
$.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;
|
|
|
|
|
}
|
|
|
|
|
renderLatestMaintenance(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),
|
|
|
|
|
// "当前里程:" + safeText(car.inputMileage)
|
|
|
|
|
"当前里程:" + latestMileage
|
|
|
|
|
].join("<br/>"));
|
|
|
|
|
|
|
|
|
|
$("#summaryCounts").html([
|
|
|
|
|
countItem("装卸记录", data.installCount),
|
|
|
|
|
countItem("维保工单", data.maintenanceCount),
|
|
|
|
|
countItem("质检记录", data.checkCount),
|
|
|
|
|
countItem("里程记录", data.mileageCount)
|
|
|
|
|
].join(""));
|
|
|
|
|
$("#summaryCounts").html(countItem("维保工单", data.maintenanceCount));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderVehicleInfo(car) {
|
|
|
|
|
@ -382,6 +528,7 @@
|
|
|
|
|
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)
|
|
|
|
|
@ -409,17 +556,18 @@
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderTimeline(rows) {
|
|
|
|
|
if (!rows.length) {
|
|
|
|
|
var maintenanceRows = rows || [];
|
|
|
|
|
if (!maintenanceRows.length) {
|
|
|
|
|
$("#timelineList").empty();
|
|
|
|
|
$("#emptyTimeline").show();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$("#emptyTimeline").hide();
|
|
|
|
|
var html = [];
|
|
|
|
|
$.each(rows, function (index, item) {
|
|
|
|
|
$.each(maintenanceRows, function (index, item) {
|
|
|
|
|
html.push('<li class="life-item">');
|
|
|
|
|
html.push('<span class="life-dot ' + eventClass(item.eventType) + '">' + eventIcon(item.eventType) + '</span>');
|
|
|
|
|
html.push('<div class="life-time">' + safeText(item.eventTime) + '</div>');
|
|
|
|
|
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>');
|
|
|
|
|
@ -427,6 +575,92 @@
|
|
|
|
|
$("#timelineList").html(html.join(""));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderLatestMaintenance(data) {
|
|
|
|
|
var order = data.order;
|
|
|
|
|
if (!order) {
|
|
|
|
|
$("#latestMaintenance").html('<div class="text-center text-muted-dash" style="padding: 48px 0;">暂无维保记录</div>');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var html = [];
|
|
|
|
|
html.push('<div class="maintenance-summary">');
|
|
|
|
|
html.push(metaCell("工单号", order.orderNo));
|
|
|
|
|
html.push(metaCell("维保类型", orderTypeFormatter(order.typeCode)));
|
|
|
|
|
html.push(metaCell("状态", statusFormatter(order.status)));
|
|
|
|
|
html.push(metaCell("维修站点", order.factoryName));
|
|
|
|
|
html.push(metaCell("保养日期", order.maintainDate));
|
|
|
|
|
html.push(metaCell("录入里程", order.inputMileage == null ? "-" : order.inputMileage + " km"));
|
|
|
|
|
html.push(metaCell("上次里程", order.lastMileage == null ? "-" : order.lastMileage + " km"));
|
|
|
|
|
if (order.description) {
|
|
|
|
|
html.push('<div>补充说明:' + safeText(order.description) + '</div>');
|
|
|
|
|
}
|
|
|
|
|
html.push('</div>');
|
|
|
|
|
|
|
|
|
|
html.push('<div class="maintenance-compare">');
|
|
|
|
|
html.push('<div class="maintenance-side"><div class="maintenance-side-title">维保前</div>');
|
|
|
|
|
html.push(renderCompareTyres(data.tireDetailsBefore || []));
|
|
|
|
|
html.push('</div>');
|
|
|
|
|
html.push('<div class="maintenance-side"><div class="maintenance-side-title">维保后</div>');
|
|
|
|
|
html.push(renderCompareTyres(data.tireDetailsAfter || []));
|
|
|
|
|
html.push('</div>');
|
|
|
|
|
html.push('</div>');
|
|
|
|
|
$("#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)
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
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>',
|
|
|
|
|
'</div>'
|
|
|
|
|
].join("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function eventDetails(item) {
|
|
|
|
|
var details = [];
|
|
|
|
|
if (item.eventType == "INSTALL") {
|
|
|
|
|
@ -439,14 +673,14 @@
|
|
|
|
|
details.push(detail("里程", item.mileage));
|
|
|
|
|
details.push(detail("花纹深度", item.patternDepth));
|
|
|
|
|
details.push(detail("动作", installTypeFormatter(item.status)));
|
|
|
|
|
} else if (item.eventType == "MAINTENANCE") {
|
|
|
|
|
} 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));
|
|
|
|
|
details.push(detail("上次里程", item.lastMileage));
|
|
|
|
|
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") {
|
|
|
|
|
// 质检节点通过曾装车轮胎间接归属车辆,页面上明确展示轮胎身份,避免误以为质检表直接有车牌。
|
|
|
|
|
@ -483,6 +717,10 @@
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@ -522,6 +760,10 @@
|
|
|
|
|
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>';
|
|
|
|
|
}
|
|
|
|
|
@ -589,6 +831,11 @@
|
|
|
|
|
}
|
|
|
|
|
return displayValue(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function goMaintenanceList() {
|
|
|
|
|
// ctx 由 include.html 注入且自带尾斜杠,按项目既有写法直接拼接相对路径。
|
|
|
|
|
window.open(ctx + "tyre/order", "_blank");
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
|