2 天以前 f7273277023f3497ac071bf98f556a569cb4dd25
yudao-server/src/main/resources/static/trace.html
@@ -229,10 +229,6 @@
  var CODE_INPUT = document.getElementById('codeInput');
  var SEARCH_BTN = document.getElementById('searchBtn');
  var BATCH_STATUS = { 10: ['草稿', 'gray'], 20: ['生产中', 'blue'], 30: ['已完成', 'green'], 40: ['已作废', 'red'] };
  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: '调拨入库',
@@ -327,12 +323,6 @@
    var html = '';
    html += buildVerifyCard(d);
    html += buildItemCard(d);
    html += buildBatchCard(d);
    html += buildQualityCard(d);
    html += buildWorkerCard(d);
    html += buildInboundCard(d);
    html += buildPalletCard(d);
    html += buildBagCard(d);
    html += buildOptionalCard(d);
    STATE.innerHTML = html || '<div class="empty-tip">暂无追溯数据</div>';
    window.scrollTo({ top: 0, behavior: 'smooth' });
@@ -394,117 +384,7 @@
    );
  }
  // 区块2:生产批次
  function buildBatchCard(d) {
    if (!d.batchCode && !d.tempCode) return '';
    var status = statusBadge(BATCH_STATUS, d.batchStatus);
    return card('生产批次', status,
      kv('批次号', text(d.batchCode)) +
      kv('状态', status) +
      kv('生产日期', fmtDate(d.produceDate)) +
      kv('产线', text(d.lineName ? (d.lineName + (d.lineCode ? ' / ' + d.lineCode : '')) : d.lineCode)) +
      kv('责任人', text(d.ownerUserName)) +
      kv('计划数量', fmtNum(d.planQuantity) === '-' ? '-' : fmtNum(d.planQuantity)) +
      kv('已生成袋码', fmtNum(d.bagQuantity)) +
      kv('已生成托盘', fmtNum(d.palletQuantity)) +
      kv('入库时间', fmtDate(d.inboundTime)) +
      (d.remark ? kv('备注', text(d.remark)) : ''),
      { collapsible: true, open: true }
    );
  }
  // 区块3:种子质检
  function buildQualityCard(d) {
    var q = d.quality;
    if (!q) return '';
    var result = QUALITY_RESULT[q.result];
    var badge = result
      ? '<span class="badge-big ' + result[1] + '">' + esc(result[0]) + '</span>'
      : '<span class="badge-big gray">未知</span>';
    var rows = '';
    if (q.indicators && q.indicators.length) {
      rows = '<table class="mini-table"><thead><tr><th>指标</th><th>检测值</th><th>标准范围</th><th>结果</th></tr></thead><tbody>';
      q.indicators.forEach(function (i) {
        var range = (i.minValue !== null && i.minValue !== undefined && i.maxValue !== null && i.maxValue !== undefined)
          ? esc(i.minValue) + ' ~ ' + esc(i.maxValue) : '-';
        var pass = (i.pass === true)
          ? '<span class="badge green">合格</span>'
          : (i.pass === false ? '<span class="badge red">不合格</span>' : '<span class="badge gray">--</span>');
        rows += '<tr><td>' + text(i.indicatorName) + '</td><td>' + text(i.value) + '</td><td>' + range + '</td><td>' + pass + '</td></tr>';
      });
      rows += '</tbody></table>';
    }
    return card('种子质检', badge,
      kv('检验结论', badge) +
      kv('检验员', text(q.inspectorName)) +
      kv('检验时间', fmtDate(q.inspectedAt)) +
      (q.reason ? kv('不合格原因', text(q.reason)) : '') +
      (rows ? '<div style="margin-top:8px">' + rows + '</div>' : ''),
      { collapsible: true, open: false }
    );
  }
  // 区块4:生产人员
  function buildWorkerCard(d) {
    if (isEmptyList(d.workers)) return '';
    var html = '<div class="pill-list">';
    d.workers.forEach(function (w) {
      var name = w.userName || '-';
      var type = w.workerTypeName || (WORKER_TYPE[w.workerType] || '');
      var color = { 10: 'var(--blue)', 20: 'var(--green)', 30: 'var(--primary-light)', 40: 'var(--red)', 50: 'var(--gray)' }[w.workerType] || 'var(--gray)';
      html += '<span class="pill"><span class="dot" style="background:' + color + '"></span>' +
        esc(type) + ':' + esc(name) + '</span>';
    });
    html += '</div>';
    return card('生产人员', null, html, { collapsible: true, open: false });
  }
  // 区块5:扫码入库事件
  function buildInboundCard(d) {
    var e = d.inbound;
    if (!e) return '';
    return card('扫码入库', null,
      kv('事件号', text(e.eventId)) +
      kv('入库数量', fmtNum(e.quantity)) +
      kv('设备编码', text(e.deviceCode)) +
      kv('产线', text(e.lineCode)) +
      kv('入库时间', fmtDate(e.eventTime)),
      { collapsible: true, open: false }
    );
  }
  // 区块6:托盘
  function buildPalletCard(d) {
    if (isEmptyList(d.pallets)) return '';
    var countHtml = '共 ' + esc(d.palletCount || d.pallets.length) + ' 个';
    var rows = '<table class="mini-table"><thead><tr><th>托盘码</th><th>序号</th><th>已装/可装</th></tr></thead><tbody>';
    d.pallets.forEach(function (p) {
      rows += '<tr><td>' + text(p.code) + '</td><td>' + fmtNum(p.palletNo) + '</td><td>' +
        fmtNum(p.bagCount) + ' / ' + fmtNum(p.capacity) + '</td></tr>';
    });
    rows += '</tbody></table>';
    return card('托盘信息', countHtml, rows, { collapsible: true, open: false });
  }
  // 区块7:袋码(一袋一码)
  function buildBagCard(d) {
    if (d.bagTotal === null || d.bagTotal === undefined || d.bagTotal === 0) return '';
    var countHtml = '共 ' + esc(d.bagTotal) + ' 个' + (d.bags && d.bags.length < d.bagTotal ? '(展示前 ' + esc(d.bags.length) + ' 个)' : '');
    var rows = '<table class="mini-table"><thead><tr><th>袋码</th><th>袋号</th><th>托盘</th><th>状态</th></tr></thead><tbody>';
    (d.bags || []).forEach(function (b) {
      rows += '<tr><td>' + text(b.code) + '</td><td>' + fmtNum(b.bagNo) + '</td><td>' + text(b.palletCode) + '</td><td>' +
        statusBadge(BAG_STATUS, b.status) + '</td></tr>';
    });
    rows += '</tbody></table>';
    return card('袋码信息', countHtml,
      '<div class="scroll-list">' + rows + '</div>',
      { collapsible: true, open: false }
    );
  }
  // 区块8:可选扩展(采购来源 / 种子流向信息(销售出库) / 加工流转),有数据才展示
  // 区块2:可选扩展(采购来源 / 种子流向信息(仅销售出库) / 加工流转),有数据才展示
  function buildOptionalCard(d) {
    var html = '';
    if (!isEmptyList(d.purchaseSources)) {
@@ -515,9 +395,11 @@
      rows += '</tbody></table>';
      html += card('采购来源', null, rows, { collapsible: true, open: false });
    }
    if (!isEmptyList(d.seedFlows)) {
    // 只展示销售出库(bizType=119)的流水,生产入库/产品产出等不展示
    var salesFlows = (d.seedFlows || []).filter(function (f) { return f.bizType === 119; });
    if (!isEmptyList(salesFlows)) {
      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) {
      salesFlows.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) +