feat(轮胎详情): 重构轮胎详情页为抽屉式布局并优化生命周期展示

重构轮胎详情页为右侧抽屉式布局,提升用户体验和操作流畅性:
- 复用pdaQueryTyreTimeLine,直接让tyreDetil2接口使用 baseTyreService.pdaQueryTyreTimeLine(baseTyre),接收BaseTyre返回Map resultMap。
- 重构前端调用接口接收数据
主要变更包括:
1. 将详情页改为抽屉式设计,保持列表上下文不丢失
2. 优化生命周期记录展示,区分出入库和装卸车记录
3. 新增标准气压字段并展示在基础信息中
4. 简化控制器逻辑,复用PDA查询接口
5. 增强移动端适配和交互体验
master
zch 1 week ago
parent 615601866d
commit b57f010af2

@ -404,100 +404,19 @@ public class BaseTyreController extends BaseController
return getDataTable(list);
}
@GetMapping("/detail2/{tyreId}")
public String tyreDetil2(@PathVariable("tyreId") Long tyreId, Model model)
@GetMapping("/detail2")
public String tyreDetil2(BaseTyre baseTyre, Model model)
{
// 1. 根据 ID 查询轮胎数据
BaseTyre tyre = baseTyreService.selectBaseTyreById(tyreId);
if (tyre == null) {
// 处理未找到轮胎的情况
if (baseTyre == null || (baseTyre.getTyreId() == null && StringUtils.isBlank(baseTyre.getKeyParam()))) {
return "error/404";
}
model.addAttribute("tyre", tyre);
// 用最少规则给详情页一个当前状态展示,避免页面出现完全空白的核心业务信息。
model.addAttribute("tyreStatusText", StringUtils.isNotBlank(tyre.getCarNo()) ? "在车" : "在库");
// 2. 查询并整合所有记录
List<TimelineItem> timelineItems = new ArrayList<>();
// --- 2.1 查询并转换入库记录 ---
RecordWarehousing warehousingQuery = new RecordWarehousing();
warehousingQuery.setTyreRfid(tyre.getTyreEpc());
List<RecordWarehousing> warehousingList = recordWarehousingService.selectRecordWarehousingList(warehousingQuery);
if (warehousingList != null) {
for (RecordWarehousing item : warehousingList) {
TimelineItem timelineItem = new TimelineItem();
timelineItem.setId(item.getId());
// 核心修改:根据 type 字段判断是入库还是出库
if (item.getType() != null && item.getType().equals("1")) {
// 1 代表 出库
timelineItem.setTitle("轮胎出库:"+item.getCreateBy());
timelineItem.setIcon("fa-sign-out"); // 或者是 fa-truck (卡车图标)
timelineItem.setColor("warning"); // 橙色/黄色,代表离开
timelineItem.setTime(item.getCreateTime());
} else {
// 0 代表 入库 (默认情况)
timelineItem.setTitle("轮胎入库:"+item.getCreateBy());
timelineItem.setIcon("fa-sign-in"); // 或者是 fa-inbox (盒子图标)
timelineItem.setColor("primary"); // 蓝色,代表进入
timelineItem.setTime(item.getCreateTime());
// 与 PDA 时间线接口保持同一个 service 返回结构;列表页传 tyreIdPDA 仍可传 keyParam。
Map resultMap = baseTyreService.pdaQueryTyreTimeLine(baseTyre);
if (resultMap == null || resultMap.isEmpty()) {
return "error/404";
}
timelineItem.setType("warehousing");
timelineItem.setDetail("仓库:" + "轮胎仓库"); // 如果有仓库名称字段,可以替换 "轮胎仓库"
timelineItems.add(timelineItem);
}
}
// --- 2.2 查询并转换安装记录 ---
RecordTyreInstall installQuery = new RecordTyreInstall();
installQuery.setTyreRfid(tyre.getTyreEpc());
List<RecordTyreInstall> installList = recordTyreInstallService.selectRecordTyreInstallList(installQuery);
if (installList != null) {
for (RecordTyreInstall item : installList) {
TimelineItem timelineItem = new TimelineItem();
timelineItem.setId(item.getId());
timelineItem.setTitle("轮胎安装:"+item.getCreateBy());
timelineItem.setType("install");
timelineItem.setDetail("车辆:" + item.getCarNo() + " | 轮位:" + item.getWheelPostion());
timelineItem.setTime(item.getCreateTime());
timelineItem.setIcon("fa-car");
timelineItem.setColor("success");
timelineItems.add(timelineItem);
}
}
// --- 2.3 查询并转换检查记录 ---
RecordCheck checkQuery = new RecordCheck();
checkQuery.setTyreRfid(tyre.getTyreEpc());
List<RecordCheck> checkList = recordCheckService.selectRecordCheckList(checkQuery);
if (checkList != null) {
for (RecordCheck item : checkList) {
TimelineItem timelineItem = new TimelineItem();
timelineItem.setId(item.getId());
timelineItem.setTitle("轮胎检查"+item.getCreateBy());
timelineItem.setType("check");
timelineItem.setDetail(buildCheckDetail(item));
// timelineItem.setDetail("深度:" + "17.8mm");
timelineItem.setTime(item.getCreateTime());
timelineItem.setIcon("fa-check-circle");
timelineItem.setColor("warning");
timelineItems.add(timelineItem);
}
}
// --- 2.4 按时间排序 (最新在最上面) ---
timelineItems = timelineItems.stream()
.sorted(Comparator.comparing(TimelineItem::getTime, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList());
// 3. 传给前端
model.addAttribute("timelineItems", timelineItems);
// 库存盘点链路统一切到新版详情模板,保留旧模板文件给其他场景继续观察或回退。
model.addAttribute("resultMap", resultMap);
return prefix + "/typreDetill2";
}

@ -200,7 +200,8 @@
}
function openTyreDetail(tyreId, tyreNo) {
var url = prefix + "/detail2/" + tyreId;
// tyreDetil2 直接接收 BaseTyre库存盘点页按 tyreId 参数绑定,避免再走旧路径。
var url = prefix + "/detail2?tyreId=" + encodeURIComponent(tyreId);
// 先在当前 iframe 上下文创建页签,确保 createMenuItem 能拿到 frameElement
// 然后再关闭弹窗,避免出现“只关窗不跳转”的问题。
$.modal.openTab("轮胎详情-" + tyreNo, url);

@ -3,365 +3,397 @@
<head>
<th:block th:include="include :: header('轮胎详情')" />
<style>
body {
background: #f3f3f4;
}
.detail-shell {
padding: 15px;
padding: 14px;
}
.summary-card,
.info-card,
.timeline-card {
.summary-panel,
.info-panel,
.life-panel {
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
margin-bottom: 16px;
border: 1px solid #e7eaec;
border-radius: 6px;
margin-bottom: 14px;
}
.summary-card {
padding: 20px 24px;
.summary-panel {
padding: 18px 20px;
}
.summary-title {
.summary-main {
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;
font-size: 26px;
font-weight: 700;
line-height: 1.25;
word-break: break-all;
}
.summary-meta {
color: #666;
font-size: 14px;
line-height: 1.9;
.summary-sub {
margin-top: 8px;
color: #676a6c;
line-height: 1.8;
}
.summary-link {
.summary-status {
min-width: 118px;
text-align: right;
}
.status-pill {
display: inline-block;
margin-top: 14px;
padding: 6px 12px;
border-radius: 16px;
color: #1c84c6;
border-bottom: 2px solid #1c84c6;
padding-bottom: 4px;
}
.info-card {
padding: 18px 20px 10px;
background: #e8f4fb;
font-weight: 600;
}
.section-title {
font-size: 16px;
font-weight: 600;
padding: 14px 16px;
border-bottom: 1px solid #edf1f2;
color: #2f4050;
margin-bottom: 14px;
font-weight: 600;
}
.section-title i {
margin-right: 6px;
color: #666;
color: #1c84c6;
}
.info-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
margin-bottom: 16px;
.info-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
border-top: 0;
}
.info-table th,
.info-table td {
border: 1px solid #edf1f2;
padding: 12px 14px;
.info-cell {
min-height: 68px;
padding: 10px 12px;
border-right: 1px solid #edf1f2;
border-bottom: 1px solid #edf1f2;
}
.info-cell:nth-child(4n) {
border-right: 0;
}
.info-label {
margin-bottom: 6px;
color: #8a9199;
font-size: 12px;
}
.info-value {
color: #2f4050;
font-size: 14px;
line-height: 1.5;
word-break: break-all;
}
.info-table th {
width: 20%;
color: #7a8590;
font-weight: 500;
background: #fafbfc;
.life-panel {
padding-bottom: 12px;
}
.info-table td {
color: #2f4050;
background: #fff;
}
.timeline-card {
padding: 18px 18px 12px;
min-height: 320px;
}
.timeline-header {
.life-summary {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 18px;
gap: 10px;
padding: 12px 16px 4px;
color: #676a6c;
}
.timeline-header .title {
font-size: 18px;
font-weight: 600;
color: #2f4050;
}
.mini-tip {
.life-count {
padding: 5px 10px;
border-radius: 4px;
background: #f8fafb;
border: 1px solid #edf1f2;
font-size: 12px;
color: #999;
}
.life-section {
padding: 8px 16px 0;
}
.life-section-title {
margin: 8px 0 12px;
color: #2f4050;
font-weight: 600;
}
.life-list {
position: relative;
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
.life-list:before {
content: "";
position: absolute;
left: 22px;
top: 8px;
bottom: 8px;
left: 13px;
width: 2px;
background: #e4e7ea;
background: #e7eaec;
}
.life-item {
position: relative;
padding-left: 54px;
margin-bottom: 22px;
min-height: 58px;
padding: 0 0 16px 42px;
}
.life-dot {
position: absolute;
left: 14px;
top: 4px;
top: 3px;
left: 5px;
width: 18px;
height: 18px;
border: 3px solid #d7ebf8;
border-radius: 50%;
background: #1c84c6;
border: 3px solid #d7ebf8;
}
.life-time {
color: #909399;
font-size: 12px;
margin-bottom: 4px;
.life-item.is-out .life-dot {
border-color: #f8e7c6;
background: #f8ac59;
}
.life-item.is-install .life-dot {
border-color: #d8f0df;
background: #1ab394;
}
.life-item.is-uninstall .life-dot {
border-color: #f7d8dd;
background: #ed5565;
}
.life-title-row {
display: flex;
justify-content: space-between;
gap: 12px;
margin-bottom: 6px;
}
.life-title {
color: #2f4050;
font-weight: 600;
margin-bottom: 6px;
}
.life-body {
color: #666;
line-height: 1.7;
font-size: 13px;
}
.text-muted-dash {
.life-time {
color: #999;
font-size: 12px;
white-space: nowrap;
}
@media (max-width: 991px) {
.summary-title {
.life-meta {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px 14px;
color: #676a6c;
font-size: 13px;
line-height: 1.6;
}
.life-remark {
grid-column: 1 / -1;
color: #8a6d3b;
}
.empty-life {
padding: 48px 16px;
color: #999;
text-align: center;
}
@media (max-width: 900px) {
.summary-main,
.life-title-row {
flex-direction: column;
}
.summary-status {
text-align: left;
}
.info-grid,
.life-meta {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.info-cell:nth-child(4n) {
border-right: 1px solid #edf1f2;
}
.info-cell:nth-child(2n) {
border-right: 0;
}
}
</style>
</head>
<body class="gray-bg">
<div class="wrapper wrapper-content detail-shell">
<div class="summary-card">
<div class="summary-title">
<body>
<div class="detail-shell"
th:with="resultBase=${resultMap['resultBase']},
recordWarehousingList=${resultMap['recordWarehousingList']},
recordTyreInstallList=${resultMap['recordTyreInstallList']}">
<div class="summary-panel">
<div class="summary-main">
<div>
<div class="summary-name" th:text="'胎号:' + ${tyre.tyreNo}">胎号:-</div>
<div class="summary-meta">
<div th:text="'所在车辆:' + ${#strings.isEmpty(tyre.carNo) ? '-' : tyre.carNo}">所在车辆:-</div>
<div th:text="'所属公司:' + ${#strings.isEmpty(tyre.company) ? '-' : tyre.company}">所属公司:-</div>
<div th:text="'所属修理厂:' + ${#strings.isEmpty(tyre.team) ? '-' : tyre.team}">所属修理厂:-</div>
<div th:text="'所属车队:' + ${#strings.isEmpty(tyre.carTeam) ? '-' : tyre.carTeam}">所属车队:-</div>
<div class="summary-name" th:text="${#strings.isEmpty(resultBase.tyreNo) ? '胎号:-' : '胎号:' + resultBase.tyreNo}">胎号:-</div>
<div class="summary-sub">
<span th:text="${#strings.isEmpty(resultBase.tyreEpc) ? 'RFID-' : 'RFID' + resultBase.tyreEpc}">RFID-</span>
<span> / </span>
<span th:text="${#strings.isEmpty(resultBase.selfNo) ? '自编号:-' : '自编号:' + resultBase.selfNo}">自编号:-</span>
</div>
<span class="summary-link" th:text="${tyre.tyreNo}">-</span>
</div>
<!-- <div>-->
<!-- <a class="btn btn-white btn-sm" href="javascript:void(0)" onclick="editTyre()" th:if="${@permission.hasPermi('tyre:tyre:edit')}">编辑</a>-->
<!-- </div>-->
<div class="summary-status">
<span class="status-pill" th:text="${#strings.isEmpty(resultBase.carNo) ? '在库/待入库' : '在车'}">在库/待入库</span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-9 col-md-8 col-sm-12">
<div class="info-card">
<div class="section-title"><i class="fa fa-bookmark"></i>基本信息</div>
<table class="info-table">
<tr>
<th>胎号</th>
<td th:text="${#strings.isEmpty(tyre.tyreNo) ? '-' : tyre.tyreNo}">-</td>
<th>轮胎自编号</th>
<td th:text="${#strings.isEmpty(tyre.selfNo) ? '-' : tyre.selfNo}">-</td>
</tr>
<tr>
<!-- <th>轮胎自编号二</th>-->
<!-- <td>-</td>-->
<th>RFID标签</th>
<td th:text="${#strings.isEmpty(tyre.tyreEpc) ? '-' : tyre.tyreEpc}">-</td>
<th>轮胎品牌</th>
<td th:text="${#strings.isEmpty(tyre.tyreBrand) ? '-' : tyre.tyreBrand}">-</td>
</tr>
<!-- <tr>-->
<!-- <th>供应商</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<tr>
<th>轮胎花纹</th>
<td th:text="${#strings.isEmpty(tyre.tyrePattern) ? '-' : tyre.tyrePattern}">-</td>
<th>轮胎层级</th>
<td th:text="${#strings.isEmpty(tyre.tyreLevel) ? '-' : tyre.tyreLevel}">-</td>
</tr>
<tr>
<!-- <th>DOT</th>-->
<!-- <td>-</td>-->
<th>轮胎规格</th>
<td th:text="${#strings.isEmpty(tyre.tyreModel) ? '-' : tyre.tyreModel}">-</td>
</tr>
</table>
<div class="info-panel">
<div class="section-title"><i class="fa fa-bookmark"></i>基础档案</div>
<div class="info-grid">
<div class="info-cell">
<div class="info-label">轮胎品牌</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.tyreBrand) ? '-' : resultBase.tyreBrand}">-</div>
</div>
<div class="info-card">
<div class="section-title"><i class="fa fa-bookmark"></i>状态信息</div>
<table class="info-table">
<!-- <tr>-->
<!-- <th>轮胎状态</th>-->
<!-- <td th:text="${tyreStatusText}">在库</td>-->
<!-- </tr>-->
<tr>
<th>轮胎类别</th>
<td th:text="${#strings.isEmpty(tyre.tyreType) ? '-' : @dict.getLabel('tyre_type', tyre.tyreType)}">-</td>
<th>初始花纹深度(mm)</th>
<td th:text="${#strings.isEmpty(tyre.patternDepth) ? '-' : tyre.patternDepth}">-</td>
<!-- <th>剩余花纹深度(mm)</th>-->
<!-- <td>-</td>-->
</tr>
<!-- <tr>-->
<!-- <th>行驶时长(h)</th>-->
<!-- <td>-</td>-->
<!-- <th>花纹磨损(h/mm)</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <th>GPS里程(km)</th>-->
<!-- <td>-</td>-->
<!-- <th>轮胎里程(km)</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <th>预计时长(h)</th>-->
<!-- <td>-</td>-->
<!-- <th>单耗里程(km/mm)</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <th>是否试验胎</th>-->
<!-- <td>-</td>-->
<!-- <th>保行里程(km)</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<tr>
<th>当前车辆</th>
<td th:text="${#strings.isEmpty(tyre.carNo) ? '-' : tyre.carNo}">-</td>
<th>当前轮位</th>
<td th:text="${#strings.isEmpty(tyre.wheelPostion) ? '-' : tyre.wheelPostion}">-</td>
</tr>
</table>
<div class="info-cell">
<div class="info-label">轮胎规格</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.tyreModel) ? '-' : resultBase.tyreModel}">-</div>
</div>
<div class="info-cell">
<div class="info-label">轮胎层级</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.tyreLevel) ? '-' : resultBase.tyreLevel}">-</div>
</div>
<div class="info-cell">
<div class="info-label">轮胎花纹</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.tyrePattern) ? '-' : resultBase.tyrePattern}">-</div>
</div>
<div class="info-cell">
<div class="info-label">轮胎类别</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.tyreType) ? '-' : @dict.getLabel('tyre_type', resultBase.tyreType)}">-</div>
</div>
<div class="info-cell">
<div class="info-label">初始花纹深度</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.patternDepth) ? '-' : resultBase.patternDepth + ' mm'}">-</div>
</div>
<div class="info-cell">
<div class="info-label">标准气压</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.pressure) ? '-' : resultBase.pressure}">-</div>
</div>
<div class="info-cell">
<div class="info-label">建档时间</div>
<div class="info-value" th:text="${resultBase.createTime == null ? '-' : #dates.format(resultBase.createTime, 'yyyy-MM-dd HH:mm:ss')}">-</div>
</div>
<div class="info-card">
<div class="section-title"><i class="fa fa-bookmark"></i>其他</div>
<table class="info-table">
<!-- <tr>-->
<!-- <th>来源</th>-->
<!-- <td>-</td>-->
<!-- <th>订单编号</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<tr>
<th>RFID</th>
<td th:text="${#strings.isEmpty(tyre.tyreEpc) ? '-' : tyre.tyreEpc}">-</td>
<th>入库时间</th>
<td th:text="${tyre.createTime == null ? '-' : #dates.format(tyre.createTime, 'yyyy-MM-dd HH:mm:ss')}">-</td>
<!-- <th>TKPH</th>-->
<!-- <td>-</td>-->
</tr>
<!-- <tr>-->
<!-- <th>建议公里</th>-->
<!-- <td>-</td>-->
<!-- <th>补气次数</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <th>修补次数</th>-->
<!-- <td>-</td>-->
<!-- <th>翻新次数</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <th>SKU编码</th>-->
<!-- <td>-</td>-->
<!-- </tr>-->
<tr>
<th>备注</th>
<td colspan="3" th:text="${#strings.isEmpty(tyre.remark) ? '-' : tyre.remark}">-</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-12">
<div class="timeline-card">
<div class="timeline-header">
<span class="title">生命周期</span>
<span class="mini-tip">按时间倒序展示</span>
<div class="info-panel">
<div class="section-title"><i class="fa fa-truck"></i>当前位置</div>
<div class="info-grid">
<div class="info-cell">
<div class="info-label">所属公司</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.company) ? '-' : resultBase.company}">-</div>
</div>
<ul class="life-list" th:if="${!#lists.isEmpty(timelineItems)}">
<li class="life-item" th:each="item : ${timelineItems}">
<div class="info-cell">
<div class="info-label">修理厂</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.team) ? '-' : resultBase.team}">-</div>
</div>
<div class="info-cell">
<div class="info-label">车队</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.carTeam) ? '-' : resultBase.carTeam}">-</div>
</div>
<div class="info-cell">
<div class="info-label">当前车辆</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.carNo) ? '-' : resultBase.carNo}">-</div>
</div>
<div class="info-cell">
<div class="info-label">当前轮位</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.wheelPostion) ? '-' : resultBase.wheelPostion}">-</div>
</div>
<div class="info-cell">
<div class="info-label">创建人</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.createBy) ? '-' : resultBase.createBy}">-</div>
</div>
<div class="info-cell" style="grid-column: span 2;">
<div class="info-label">备注</div>
<div class="info-value" th:text="${#strings.isEmpty(resultBase.remark) ? '-' : resultBase.remark}">-</div>
</div>
</div>
</div>
<div class="life-panel">
<div class="section-title"><i class="fa fa-history"></i>生命周期</div>
<div class="life-summary">
<span class="life-count" th:text="${'出入库记录:' + (#lists.isEmpty(recordWarehousingList) ? 0 : #lists.size(recordWarehousingList))}">出入库记录0</span>
<span class="life-count" th:text="${'装卸车记录:' + (#lists.isEmpty(recordTyreInstallList) ? 0 : #lists.size(recordTyreInstallList))}">装卸车记录0</span>
</div>
<div class="life-section" th:if="${!#lists.isEmpty(recordWarehousingList)}">
<div class="life-section-title">出入库流转</div>
<ul class="life-list">
<li class="life-item" th:each="item : ${recordWarehousingList}" th:classappend="${item.type == '1'} ? ' is-out' : ''">
<span class="life-dot"></span>
<div class="life-time" th:text="${item.time == null ? '-' : #dates.format(item.time, 'yyyy-MM-dd HH:mm:ss')}">-</div>
<div class="life-title" th:text="${item.title}">轮胎入库</div>
<div class="life-body" th:text="${#strings.isEmpty(item.detail) ? '-' : item.detail}">-</div>
<div class="life-title-row">
<div class="life-title" th:text="${item.type == '1' ? '轮胎出库' : '轮胎入库'}">轮胎入库</div>
<div class="life-time" th:text="${item.createTime == null ? '-' : #dates.format(item.createTime, 'yyyy-MM-dd HH:mm:ss')}">-</div>
</div>
<div class="life-meta">
<div th:text="${#strings.isEmpty(item.createBy) ? '操作人:-' : '操作人:' + item.createBy}">操作人:-</div>
<div th:text="${#strings.isEmpty(item.tyreRfid) ? 'RFID-' : 'RFID' + item.tyreRfid}">RFID-</div>
<div th:text="${#strings.isEmpty(item.tyreBrand) ? '品牌:-' : '品牌:' + item.tyreBrand}">品牌:-</div>
<div th:text="${#strings.isEmpty(item.tyreModel) ? '规格:-' : '规格:' + item.tyreModel}">规格:-</div>
<div class="life-remark" th:if="${!#strings.isEmpty(item.remark)}" th:text="${'备注:' + item.remark}">备注:-</div>
</div>
</li>
</ul>
<div class="text-center text-muted-dash" style="padding: 60px 0;" th:if="${#lists.isEmpty(timelineItems)}">
</div>
<div class="life-section" th:if="${!#lists.isEmpty(recordTyreInstallList)}">
<div class="life-section-title">装卸车流转</div>
<ul class="life-list">
<li class="life-item"
th:each="item : ${recordTyreInstallList}"
th:classappend="${item.type == '1'} ? ' is-uninstall' : ' is-install'">
<span class="life-dot"></span>
<div class="life-title-row">
<div class="life-title" th:text="${item.type == '1' ? '轮胎卸下' : '轮胎安装'}">轮胎安装</div>
<div class="life-time" th:text="${item.createTime == null ? '-' : #dates.format(item.createTime, 'yyyy-MM-dd HH:mm:ss')}">-</div>
</div>
<div class="life-meta">
<div th:text="${#strings.isEmpty(item.createBy) ? '操作人:-' : '操作人:' + item.createBy}">操作人:-</div>
<div th:text="${#strings.isEmpty(item.carNo) ? '车辆:-' : '车辆:' + item.carNo}">车辆:-</div>
<div th:text="${#strings.isEmpty(item.wheelPostion) ? '轮位:-' : '轮位:' + item.wheelPostion}">轮位:-</div>
<div th:text="${item.mileage == null ? '里程:-' : '里程:' + item.mileage + ' km'}">里程:-</div>
<div th:text="${#strings.isEmpty(item.patternDepth) ? '花纹深度:-' : '花纹深度:' + item.patternDepth + ' mm'}">花纹深度:-</div>
<div th:text="${#strings.isEmpty(item.carTeam) ? '车队:-' : '车队:' + item.carTeam}">车队:-</div>
<div class="life-remark" th:if="${!#strings.isEmpty(item.remark)}" th:text="${'备注:' + item.remark}">备注:-</div>
</div>
</li>
</ul>
</div>
<div class="empty-life" th:if="${#lists.isEmpty(recordWarehousingList) and #lists.isEmpty(recordTyreInstallList)}">
暂无生命周期记录
</div>
</div>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "tyre/tyre";
var tyreId = [[${tyre.tyreId}]];
function editTyre() {
var url = prefix + "/edit/" + tyreId;
$.modal.open("编辑轮胎", url, "900", "700");
}
<script type="text/javascript">
$(function() {
// 兜底:关闭父窗口 tab 的 loading 遮罩
// 若 iframe 在 index.js 绑定 load 事件前就已加载完毕,遮罩不会自动关闭
if (window.parent && window.parent !== window && window.parent.$ && window.parent.$.modal) {
window.parent.$.modal.closeLoading();
}

@ -2,6 +2,77 @@
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('轮胎基础信息列表')" />
<style>
body.tyre-drawer-open {
overflow: hidden;
}
.tyre-detail-mask {
display: none;
position: fixed;
z-index: 2040;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.28);
}
.tyre-detail-drawer {
position: fixed;
z-index: 2050;
top: 0;
right: 0;
width: 92vw;
max-width: 980px;
height: 100vh;
background: #f3f3f4;
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.18);
transform: translateX(100%);
transition: transform 0.22s ease;
}
.tyre-detail-drawer.is-open {
transform: translateX(0);
}
.tyre-detail-drawer__header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 16px;
color: #2f4050;
background: #fff;
border-bottom: 1px solid #e7eaec;
}
.tyre-detail-drawer__title {
font-size: 15px;
font-weight: 600;
}
.tyre-detail-drawer__close {
border: 0;
background: transparent;
color: #676a6c;
font-size: 18px;
line-height: 1;
padding: 8px;
}
.tyre-detail-drawer__close:hover {
color: #ed5565;
}
.tyre-detail-drawer iframe {
display: block;
width: 100%;
height: calc(100vh - 50px);
border: 0;
background: #f3f3f4;
}
</style>
</head>
<body class="gray-bg">
<div class="container-div">
@ -89,6 +160,16 @@
</div>
</div>
</div>
<div id="tyreDetailDrawerMask" class="tyre-detail-mask" onclick="closeTyreDetailDrawer()"></div>
<div id="tyreDetailDrawer" class="tyre-detail-drawer" aria-hidden="true">
<div class="tyre-detail-drawer__header">
<span class="tyre-detail-drawer__title"><i class="fa fa-list-alt"></i> 轮胎详情</span>
<button type="button" class="tyre-detail-drawer__close" onclick="closeTyreDetailDrawer()" title="关闭">
<i class="fa fa-times"></i>
</button>
</div>
<iframe id="tyreDetailFrame" name="tyreDetailFrame"></iframe>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var editFlag = [[${@permission.hasPermi('tyre:tyre:edit')}]];
@ -98,13 +179,47 @@
var inventoryStatusDict = [[${@dict.getType('inventory_status')}]];
// 1. 定义点击后的处理函数
function showDetail(tyreId) {
// 这里使用了若依框架常用的 $.modal.open 方法
// 如果你的框架没有这个方法,可以使用 Bootstrap 的 Modal 手动填充数据
$.modal.open("轮胎详情", prefix + "/detail/" + tyreId);
// 或者如果你是弹出一个包含详细信息的 Alert
// $.modal.alertSuccess("你点击的轮胎ID是: " + tyreId);
if (!tyreId) {
$.modal.alertWarning("当前轮胎缺少主键,无法查询生命周期");
return;
}
// 详情页固定在当前列表右侧,避免弹层尺寸压缩生命周期信息,同时保持列表上下文不丢失。
$("#tyreDetailFrame").attr("src", prefix + "/detail2?tyreId=" + encodeURIComponent(tyreId));
$("body").addClass("tyre-drawer-open");
$("#tyreDetailDrawerMask").fadeIn(120);
$("#tyreDetailDrawer").addClass("is-open").attr("aria-hidden", "false");
}
function closeTyreDetailDrawer() {
$("body").removeClass("tyre-drawer-open");
$("#tyreDetailDrawerMask").fadeOut(120);
$("#tyreDetailDrawer").removeClass("is-open").attr("aria-hidden", "true");
window.setTimeout(function () {
if (!$("#tyreDetailDrawer").hasClass("is-open")) {
$("#tyreDetailFrame").attr("src", "about:blank");
}
}, 220);
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
$(function() {
$(document).on("click", ".tyre-detail-link", function() {
showDetail($(this).data("tyre-id"));
});
$(document).on("keydown", function(event) {
if (event.key === "Escape") {
closeTyreDetailDrawer();
}
});
var options = {
url: prefix + "/list",
createUrl: prefix + "/add",
@ -126,8 +241,7 @@
field : 'tyreNo',
title : '胎号',
formatter: function(value, row, index) {
return '<a href="javascript:void(0)" onclick="showDetail(\'' + row.tyreId + '\')">' + value + '</a>';
return '<a href="javascript:void(0)" class="tyre-detail-link" data-tyre-id="' + escapeHtml(row.tyreId) + '">' + escapeHtml(value) + '</a>';
}
},

@ -78,6 +78,9 @@ public class BaseTyre extends BaseEntity
@Excel(name = "所在轮位")
private String wheelPostion;
private String pressure;
/**
* inventory_status
*

@ -8,10 +8,13 @@ import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.BaseCar;
import com.ruoyi.system.domain.BaseTyre;
import com.ruoyi.system.domain.RecordTyreInstall;
import com.ruoyi.system.domain.RecordWarehousing;
import com.ruoyi.system.domain.vo.BaseTyreVo;
import com.ruoyi.system.mapper.BaseTyreMapper;
import com.ruoyi.system.mapper.RecordTyreInstallMapper;
import com.ruoyi.system.mapper.RecordWarehousingMapper;
import com.ruoyi.system.service.IBaseTyreService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ -172,8 +172,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LEFT JOIN sys_dept sd ON bc.dept_id = sd.dept_id
LEFT JOIN sys_dept sds ON sd.parent_id = sds.dept_id
LEFT JOIN sys_dept sdss ON sds.parent_id = sdss.dept_id
where tyre_id is not null
<if test="keyParam != null and keyParam != ''">and d.tyre_epc = #{keyParam} OR d.tyre_no = #{keyParam} OR d.self_no = #{keyParam}</if>
<where>
<choose>
<when test="tyreId != null">d.tyre_id = #{tyreId}</when>
<when test="keyParam != null and keyParam != ''">
<!-- tyreId 用于后台详情抽屉keyParam 保留给 PDA 按 EPC/胎号/自编号查询,避免两端走出不同生命周期口径。 -->
(d.tyre_epc = #{keyParam} OR d.tyre_no = #{keyParam} OR d.self_no = #{keyParam})
</when>
<otherwise>
1 = 0
</otherwise>
</choose>
</where>
</select>

Loading…
Cancel
Save