| | |
| | | .scroll-list::-webkit-scrollbar { width: 4px; } |
| | | .scroll-list::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 2px; } |
| | | |
| | | /* 正品验证卡(顶部,扫码优先展示) */ |
| | | .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; |
| | |
| | | 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) { |
| | |
| | | return; |
| | | } |
| | | var html = ''; |
| | | html += buildVerifyCard(d); |
| | | html += buildItemCard(d); |
| | | html += buildBatchCard(d); |
| | | html += buildQualityCard(d); |
| | |
| | | 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">正品验证</div>' + |
| | | '<div class="verify-badge"><span class="badge-big green">✅ 正品验证通过</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 } |
| | | ); |
| | | } |
| | | |
| | |
| | | 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 } |
| | | ); |
| | | } |
| | | |
| | |
| | | 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 } |
| | | ); |
| | | } |
| | | |
| | |
| | | esc(type) + ':' + esc(name) + '</span>'; |
| | | }); |
| | | html += '</div>'; |
| | | return card('生产人员', null, html); |
| | | return card('生产人员', null, html, { collapsible: true, open: false }); |
| | | } |
| | | |
| | | // 区块5:扫码入库事件 |
| | |
| | | kv('入库数量', fmtNum(e.quantity)) + |
| | | kv('设备编码', text(e.deviceCode)) + |
| | | kv('产线', text(e.lineCode)) + |
| | | kv('入库时间', fmtDate(e.eventTime)) |
| | | kv('入库时间', fmtDate(e.eventTime)), |
| | | { collapsible: true, open: false } |
| | | ); |
| | | } |
| | | |
| | |
| | | fmtNum(p.bagCount) + ' / ' + fmtNum(p.capacity) + '</td></tr>'; |
| | | }); |
| | | rows += '</tbody></table>'; |
| | | return card('托盘信息', countHtml, rows); |
| | | return card('托盘信息', countHtml, rows, { collapsible: true, open: false }); |
| | | } |
| | | |
| | | // 区块7:袋码(一袋一码) |
| | |
| | | }); |
| | | rows += '</tbody></table>'; |
| | | return card('袋码信息', countHtml, |
| | | '<div class="scroll-list">' + rows + '</div>' |
| | | '<div class="scroll-list">' + rows + '</div>', |
| | | { collapsible: true, open: false } |
| | | ); |
| | | } |
| | | |
| | |
| | | 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>'; |
| | |
| | | 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>'; |
| | |
| | | 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; |
| | | } |