From b29d86d9ed34fd890eb14cb7a75f6ae4f7148865 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 01 九月 2026 13:58:03 +0800
Subject: [PATCH] feat(mes): 新增防伪防窜货功能模块
---
yudao-server/src/main/resources/static/trace.html | 113 +++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 99 insertions(+), 14 deletions(-)
diff --git a/yudao-server/src/main/resources/static/trace.html b/yudao-server/src/main/resources/static/trace.html
index be5c7f5..afafce6 100644
--- a/yudao-server/src/main/resources/static/trace.html
+++ b/yudao-server/src/main/resources/static/trace.html
@@ -141,6 +141,34 @@
.scroll-list::-webkit-scrollbar { width: 4px; }
.scroll-list::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 2px; }
+ /* 姝e搧楠岃瘉鍗★紙椤堕儴锛屾壂鐮佷紭鍏堝睍绀猴級 */
+ .verify-card {
+ border: 1px solid var(--primary-light);
+ background: linear-gradient(180deg, #eef8ee, #fff);
+ }
+ .verify-card .card-bd { padding: 14px 16px; }
+ .verify-title {
+ display: flex; align-items: center; gap: 8px;
+ font-size: 16px; font-weight: 700; color: var(--primary);
+ margin-bottom: 4px;
+ }
+ .verify-title::before { content: ""; width: 5px; height: 16px; border-radius: 2px; background: var(--primary); }
+ .verify-sub { font-size: 12px; color: var(--text-sub); margin-bottom: 10px; }
+ .verify-badge { margin-bottom: 10px; }
+ .verify-stats { margin-top: 10px; border-top: 1px dashed var(--border); padding-top: 4px; }
+
+ /* 鍙姌鍙犲崱鐗� */
+ .card.collapsible .card-hd { cursor: pointer; position: relative; padding-right: 30px; }
+ .card.collapsible .card-hd::after {
+ content: "鈻�"; position: absolute; right: 12px; top: 50%;
+ transform: translateY(-50%); color: var(--text-sub); font-size: 12px;
+ transition: transform .2s;
+ }
+ .card.collapsible:not(.open) .card-hd::after { transform: translateY(-50%) rotate(-90deg); }
+ .card.collapsible .card-hd .count { margin-right: 16px; }
+ .card.collapsible .card-bd { display: none; }
+ .card.collapsible.open .card-bd { display: block; }
+
footer {
max-width: 480px; margin: 0 auto;
padding: 20px 16px 28px; text-align: center;
@@ -207,11 +235,17 @@
return '<span class="badge ' + item[1] + '">' + esc(item[0]) + '</span>';
}
- function card(title, countHtml, bodyHtml) {
- return '<div class="card">' +
- '<div class="card-hd"><span class="bar"></span>' + esc(title) +
+ function card(title, countHtml, bodyHtml, opts) {
+ opts = opts || {};
+ var collapsible = !!opts.collapsible;
+ var open = collapsible && !!opts.open;
+ var hd = '<div class="card-hd"' +
+ (collapsible ? ' onclick="this.parentNode.classList.toggle(\'open\')"' : '') +
+ '><span class="bar"></span>' + esc(title) +
(countHtml ? '<span class="count">' + countHtml + '</span>' : '') +
- '</div>' + '<div class="card-bd">' + bodyHtml + '</div>' + '</div>';
+ '</div>';
+ return '<div class="card' + (collapsible ? ' collapsible' : '') + (open ? ' open' : '') + '">' +
+ hd + '<div class="card-bd">' + bodyHtml + '</div>' + '</div>';
}
function kv(k, v, dim) {
@@ -257,6 +291,7 @@
return;
}
var html = '';
+ html += buildVerifyCard(d);
html += buildItemCard(d);
html += buildBatchCard(d);
html += buildQualityCard(d);
@@ -269,13 +304,59 @@
window.scrollTo({ top: 0, behavior: 'smooth' });
}
+ // 鍖哄潡0锛氭鍝侀獙璇侊紙椤堕儴锛屾壂鐮佷紭鍏堝睍绀洪獙璇佺粨璁轰笌鏌ヨ缁熻锛�
+ function stripUrl(v) {
+ if (!v) return v;
+ if (/^https?:\/\//i.test(v)) {
+ var m = v.match(/[?&]code=([^&]*)/);
+ return m ? decodeURIComponent(m[1]) : '';
+ }
+ return v;
+ }
+
+ function buildVerifyCard(d) {
+ var typeLabel = { BAG: '琚嬬爜', PALLET: '鎵樼洏鐮�', BATCH: '鎵规鐮�' }[d.sourceType] || '杩芥函鐮�';
+ var code = stripUrl(d.requestCode) || d.batchCode || d.tempCode || '-';
+ var bagNo = '-';
+ if (d.sourceType === 'BAG' && d.bags) {
+ for (var i = 0; i < d.bags.length; i++) {
+ if (d.bags[i].code === code) {
+ bagNo = d.bags[i].bagNo;
+ break;
+ }
+ }
+ }
+ var statsHtml = '<div class="verify-stats">';
+ if (d.antiFake && d.antiFake.scanCount > 0) {
+ statsHtml += kv('绱鏌ヨ', esc(d.antiFake.scanCount) + ' 娆�') +
+ kv('棣栨鏌ヨ', fmtDate(d.antiFake.firstScanTime)) +
+ kv('鏈�杩戞煡璇�', fmtDate(d.antiFake.lastScanTime));
+ } else {
+ statsHtml += kv('绱鏌ヨ', '鏆傛棤鏌ヨ璁板綍');
+ }
+ statsHtml += '</div>';
+ return '<div class="card verify-card">' +
+ '<div class="card-bd">' +
+ '<div class="verify-title">姝e搧楠岃瘉</div>' +
+ '<div class="verify-badge"><span class="badge-big green">鉁� 姝e搧楠岃瘉閫氳繃</span></div>' +
+ '<div class="verify-sub">璇ヨ拷婧爜涓烘瑙勬笭閬撲骇鍝侊紝婧簮淇℃伅鐪熷疄鏈夋晥</div>' +
+ kv('杩芥函绫诲瀷', typeLabel) +
+ kv(typeLabel, text(code)) +
+ (d.sourceType === 'BAG' ? kv('琚嬪彿', bagNo) : '') +
+ kv('鎵规鍙�', text(d.batchCode)) +
+ kv('鍝佺', text(d.itemName)) +
+ statsHtml +
+ '</div></div>';
+ }
+
// 鍖哄潡1锛氬搧绉嶄俊鎭�
function buildItemCard(d) {
if (!d.itemCode && !d.itemName && !d.itemSpecification) return '';
return card('鍝佺淇℃伅', null,
kv('鍝佺鍚嶇О', text(d.itemName)) +
kv('瑙勬牸', text(d.itemSpecification)) +
- kv('鍝佺缂栫爜', text(d.itemCode))
+ kv('鍝佺缂栫爜', text(d.itemCode)),
+ { collapsible: true, open: true }
);
}
@@ -293,7 +374,8 @@
kv('宸茬敓鎴愯鐮�', fmtNum(d.bagQuantity)) +
kv('宸茬敓鎴愭墭鐩�', fmtNum(d.palletQuantity)) +
kv('鍏ュ簱鏃堕棿', fmtDate(d.inboundTime)) +
- (d.remark ? kv('澶囨敞', text(d.remark)) : '')
+ (d.remark ? kv('澶囨敞', text(d.remark)) : ''),
+ { collapsible: true, open: true }
);
}
@@ -325,7 +407,8 @@
kv('妫�楠屽憳', text(q.inspectorName)) +
kv('妫�楠屾椂闂�', fmtDate(q.inspectedAt)) +
(q.reason ? kv('涓嶅悎鏍煎師鍥�', text(q.reason)) : '') +
- (rows ? '<div style="margin-top:8px">' + rows + '</div>' : '')
+ (rows ? '<div style="margin-top:8px">' + rows + '</div>' : ''),
+ { collapsible: true, open: false }
);
}
@@ -341,7 +424,7 @@
esc(type) + '锛�' + esc(name) + '</span>';
});
html += '</div>';
- return card('鐢熶骇浜哄憳', null, html);
+ return card('鐢熶骇浜哄憳', null, html, { collapsible: true, open: false });
}
// 鍖哄潡5锛氭壂鐮佸叆搴撲簨浠�
@@ -353,7 +436,8 @@
kv('鍏ュ簱鏁伴噺', fmtNum(e.quantity)) +
kv('璁惧缂栫爜', text(e.deviceCode)) +
kv('浜х嚎', text(e.lineCode)) +
- kv('鍏ュ簱鏃堕棿', fmtDate(e.eventTime))
+ kv('鍏ュ簱鏃堕棿', fmtDate(e.eventTime)),
+ { collapsible: true, open: false }
);
}
@@ -367,7 +451,7 @@
fmtNum(p.bagCount) + ' / ' + fmtNum(p.capacity) + '</td></tr>';
});
rows += '</tbody></table>';
- return card('鎵樼洏淇℃伅', countHtml, rows);
+ return card('鎵樼洏淇℃伅', countHtml, rows, { collapsible: true, open: false });
}
// 鍖哄潡7锛氳鐮侊紙涓�琚嬩竴鐮侊級
@@ -381,7 +465,8 @@
});
rows += '</tbody></table>';
return card('琚嬬爜淇℃伅', countHtml,
- '<div class="scroll-list">' + rows + '</div>'
+ '<div class="scroll-list">' + rows + '</div>',
+ { collapsible: true, open: false }
);
}
@@ -394,7 +479,7 @@
rows += '<tr><td>' + text(s.receiptCode) + '</td><td>' + text(s.vendorName) + '</td><td>' + fmtNum(s.receivedQuantity) + '</td><td>' + fmtDate(s.receiptDate) + '</td></tr>';
});
rows += '</tbody></table>';
- html += card('閲囪喘鏉ユ簮', null, rows);
+ html += card('閲囪喘鏉ユ簮', null, rows, { collapsible: true, open: false });
}
if (!isEmptyList(d.salesTargets)) {
var rows2 = '<table class="mini-table"><thead><tr><th>閿�鍞嚭搴撳崟</th><th>瀹㈡埛</th><th>鏁伴噺</th><th>鏃ユ湡</th></tr></thead><tbody>';
@@ -402,7 +487,7 @@
rows2 += '<tr><td>' + text(s.outCode || s.salesOrderCode || '') + '</td><td>' + text(s.customerName) + '</td><td>' + fmtNum(s.quantity) + '</td><td>' + fmtDate(s.outTime) + '</td></tr>';
});
rows2 += '</tbody></table>';
- html += card('閿�鍞幓鍚�', null, rows2);
+ html += card('閿�鍞幓鍚�', null, rows2, { collapsible: true, open: false });
}
if (!isEmptyList(d.processTraces)) {
var rows3 = '<table class="mini-table"><thead><tr><th>鑺傜偣</th><th>璇存槑</th><th>鏃堕棿</th></tr></thead><tbody>';
@@ -410,7 +495,7 @@
rows3 += '<tr><td>' + text(t.nodeName) + '</td><td>' + text(t.description) + '</td><td>' + fmtDate(t.processTime) + '</td></tr>';
});
rows3 += '</tbody></table>';
- html += card('鍔犲伐娴佽浆', null, rows3);
+ html += card('鍔犲伐娴佽浆', null, rows3, { collapsible: true, open: false });
}
return html;
}
--
Gitblit v1.9.3