From c89f7dcf9344a35efde2a00edb10491b401ba000 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 16 九月 2026 09:19:00 +0800
Subject: [PATCH] feat(trace): 添加种子流向信息展示功能
---
yudao-server/src/main/resources/static/trace.html | 142 +++++++++++++++++++++++++++++++++++++++++------
1 files changed, 123 insertions(+), 19 deletions(-)
diff --git a/yudao-server/src/main/resources/static/trace.html b/yudao-server/src/main/resources/static/trace.html
index be5c7f5..ec75dff 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;
@@ -179,6 +207,14 @@
var BAG_STATUS = { 10: ['宸茬敓鎴�', 'gray'], 20: ['宸插鍑哄嵃鍒�', 'blue'], 30: ['宸蹭綔搴�', 'red'] };
var QUALITY_RESULT = { 0: ['澶勭悊涓�', 'gray'], 1: ['鍚堟牸', 'green'], 2: ['涓嶅悎鏍�', 'red'] };
var WORKER_TYPE = { 10: '缁勯暱', 20: '鎿嶄綔宸�', 30: '鍖呰宸�', 40: '璐ㄦ鍛�', 50: '鍏朵粬' };
+ var FLOW_DIRECTION = { 1: ['鍏ュ簱', 'green'], 2: ['鍑哄簱', 'blue'] };
+ var FLOW_BIZ_TYPE = {
+ 110: '閲囪喘鍏ュ簱', 111: '閲囪喘鍏ュ簱鍙栨秷', 112: '璋冩嫧鍑哄簱', 113: '璋冩嫧鍏ュ簱',
+ 114: '鍏朵粬鍑哄簱', 115: '鍏朵粬鍏ュ簱', 116: '鐢熶骇棰嗘枡', 117: '鐢熶骇閫�鏂�',
+ 118: '鐢熶骇鍏ュ簱', 119: '閿�鍞嚭搴�', 120: '閿�鍞��璐у叆搴�', 121: '澶栧崗鍙戞枡',
+ 122: '澶栧崗鍏ュ簱', 123: '鐢熶骇娑堣��', 124: '浜у搧浜у嚭', 125: '渚涘簲鍟嗛��璐у嚭搴�',
+ 126: '搴撳瓨璋冩暣鍏ュ簱', 127: '搴撳瓨璋冩暣鍑哄簱'
+ };
// ========== 宸ュ叿鍑芥暟 ==========
function esc(v) {
@@ -207,11 +243,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 +299,7 @@
return;
}
var html = '';
+ html += buildVerifyCard(d);
html += buildItemCard(d);
html += buildBatchCard(d);
html += buildQualityCard(d);
@@ -269,13 +312,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 +382,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 +415,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 +432,7 @@
esc(type) + '锛�' + esc(name) + '</span>';
});
html += '</div>';
- return card('鐢熶骇浜哄憳', null, html);
+ return card('鐢熶骇浜哄憳', null, html, { collapsible: true, open: false });
}
// 鍖哄潡5锛氭壂鐮佸叆搴撲簨浠�
@@ -353,7 +444,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 +459,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,11 +473,12 @@
});
rows += '</tbody></table>';
return card('琚嬬爜淇℃伅', countHtml,
- '<div class="scroll-list">' + rows + '</div>'
+ '<div class="scroll-list">' + rows + '</div>',
+ { collapsible: true, open: false }
);
}
- // 鍖哄潡8锛氬彲閫夋墿灞曪紙閲囪喘鏉ユ簮 / 閿�鍞幓鍚� / 鍔犲伐娴佽浆锛夛紝鏈夋暟鎹墠灞曠ず
+ // 鍖哄潡8锛氬彲閫夋墿灞曪紙閲囪喘鏉ユ簮 / 绉嶅瓙娴佸悜淇℃伅锛堥攢鍞嚭搴擄級 / 鍔犲伐娴佽浆锛夛紝鏈夋暟鎹墠灞曠ず
function buildOptionalCard(d) {
var html = '';
if (!isEmptyList(d.purchaseSources)) {
@@ -394,15 +487,26 @@
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>';
- d.salesTargets.forEach(function (s) {
- 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>';
+ if (!isEmptyList(d.seedFlows)) {
+ var rows2 = '<table class="mini-table"><thead><tr><th>鏂瑰悜</th><th>涓氬姟</th><th>鏉ユ簮鍗曞彿</th><th>瀹㈡埛</th><th>鏁伴噺</th><th>鏃堕棿</th><th>浠撳簱/搴撳尯</th></tr></thead><tbody>';
+ d.seedFlows.forEach(function (f) {
+ var qty = (f.quantity === null || f.quantity === undefined) ? '-' : fmtNum(Math.abs(Number(f.quantity)));
+ var wh = [f.warehouseName, f.locationName].filter(Boolean).join(' / ') || '-';
+ rows2 += '<tr><td>' + statusBadge(FLOW_DIRECTION, f.type) + '</td><td>' + text(FLOW_BIZ_TYPE[f.bizType] || f.bizType) +
+ '</td><td>' + text(f.bizCode) + '</td><td>' + text(f.clientName) + '</td><td>' + qty + '</td><td>' +
+ fmtDate(f.transactionTime) + '</td><td>' + text(wh) + '</td></tr>';
});
rows2 += '</tbody></table>';
- html += card('閿�鍞幓鍚�', null, rows2);
+ html += card('绉嶅瓙娴佸悜淇℃伅', null, rows2, { collapsible: true, open: false });
+ } else if (!isEmptyList(d.salesTargets)) {
+ var rows2b = '<table class="mini-table"><thead><tr><th>閿�鍞嚭搴撳崟</th><th>瀹㈡埛</th><th>鏁伴噺</th><th>鏃ユ湡</th></tr></thead><tbody>';
+ d.salesTargets.forEach(function (s) {
+ rows2b += '<tr><td>' + text(s.salesCode || s.salesOrderCode || '') + '</td><td>' + text(s.clientName) + '</td><td>' + fmtNum(s.quantity) + '</td><td>' + fmtDate(s.salesDate) + '</td></tr>';
+ });
+ rows2b += '</tbody></table>';
+ html += card('绉嶅瓙娴佸悜淇℃伅', null, rows2b, { 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 +514,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