云
7 小时以前 66958ca7242928b76795d98124e1d012665e8782
yudao-server/src/main/resources/static/trace.html
@@ -372,14 +372,6 @@
  var CODE_INPUT = document.getElementById('codeInput');
  var SEARCH_BTN = document.getElementById('searchBtn');
  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: '库存调整出库'
  };
  var SOURCE_TYPE = { BAG: '袋码', PALLET: '托盘码', BATCH: '批次码' };
  // ========== 工具函数 ==========
@@ -411,12 +403,6 @@
  // 表格窄列(数量 / 日期)不换行,避免数字被拆行
  function nw(html) {
    return '<span style="white-space:nowrap">' + html + '</span>';
  }
  function statusBadge(map, key) {
    var item = map[key];
    if (!item) return '<span class="badge gray">' + (key === null || key === undefined ? '' : esc(key)) + '</span>';
    return '<span class="badge ' + item[1] + '">' + esc(item[0]) + '</span>';
  }
  function kv(k, v, dim) {
@@ -499,14 +485,89 @@
      return;
    }
    var html = '';
    if (new URLSearchParams(location.search).get('view') === 'location') {
      html += locationSections(d);
      html += sec('产品追溯标识', null, tracePanel(d), null);
      STATE.innerHTML = html;
      window.scrollTo({ top: 0, behavior: 'smooth' });
      return;
    }
    html += sec('防伪查询', null, fakePanel(d), null);
    html += sec('追溯查询', null, tracePanel(d), null);
    // 生产明细折叠区:对外展示种子流向、加工流转,生产批次信息不展示。
    // 生产明细折叠区:对外展示加工流转,生产批次信息不展示。
    // 质检/人员/托盘/袋码/采购来源等内部明细不对外展示,
    // 如需一并展示,改为 detailSections(d, true) 即可。
    html += detailSections(d, false);
    STATE.innerHTML = html || '<div class="empty-tip">暂无追溯数据</div>';
    window.scrollTo({ top: 0, behavior: 'smooth' });
  }
  // 定位授权入口沿用本页的品牌背景与区块样式,按生产、仓储、物流组织已有真实数据。
  function groupOutboundOrders(rows, codeOf, dateField) {
    var grouped = [];
    var byCode = new Map();
    rows.forEach(function (item) {
      var code = String(codeOf(item) || '').trim();
      var quantity = Number(item.quantity);
      var validQuantity = item.quantity !== null && item.quantity !== undefined && Number.isFinite(quantity);
      var existing = code ? byCode.get(code) : null;
      if (!existing) {
        var order = Object.assign({}, item, { quantity: validQuantity ? Math.abs(quantity) : null });
        grouped.push(order);
        if (code) byCode.set(code, order);
      } else {
        if (validQuantity) existing.quantity = (existing.quantity || 0) + Math.abs(quantity);
        if (!existing.contactName && item.contactName) existing.contactName = item.contactName;
        if (item[dateField] && (!existing[dateField] || item[dateField] > existing[dateField])) {
          existing[dateField] = item[dateField];
        }
      }
    });
    return grouped;
  }
  function locationSections(d) {
    var production = kv('产品名称', text(d.itemName)) +
      kv('规格', text(d.itemSpecification || d.itemUnitMeasureName)) +
      kv('加工批次', text(d.batchCode || d.tempCode));
    if (d.produceDate) production = kv('生产日期', fmtDay(d.produceDate)) + production;
    var html = sec('生产信息', null, production, null);
    var salesFlows = groupOutboundOrders((d.seedFlows || []).filter(function (item) { return item.bizType === 119; }),
      function (item) { return item.bizCode; }, 'transactionTime');
    var salesTargets = groupOutboundOrders(d.salesTargets || [],
      function (item) { return item.salesCode || item.salesOrderCode; }, 'salesDate');
    var flow = (d.seedFlows || []).find(function (item) { return item.warehouseName; });
    var storage = '';
    if (d.inboundTime) storage += kv('入库日期', fmtDay(d.inboundTime));
    var outboundDates = salesFlows.map(function (item) { return item.transactionTime; })
      .concat(salesTargets.map(function (item) { return item.salesDate; })).filter(Boolean).sort();
    if (outboundDates.length) storage += kv('最近出库日期', fmtDay(outboundDates[outboundDates.length - 1]));
    if (flow) storage += kv('仓库', text(flow.warehouseName));
    html += sec('仓储信息', null, storage || '<div class="empty-tip">暂无仓储信息</div>', null);
    var logistics = salesFlows.map(function (item) {
      var route = [item.warehouseName, item.clientName].filter(Boolean).join(' → ');
      var description = '该批次' + (item.transactionTime ? '于 ' + String(item.transactionTime).slice(0, 10) + ' ' : '') +
        (item.warehouseName ? '从' + item.warehouseName + '出库' : '完成销售出库') +
        (item.clientName ? ',发往' + item.clientName : '') + '。';
      return li('<span class="li-ttl">' + text(route || '销售出库') + '</span>', fmtDay(item.transactionTime),
        kv('出库单号', text(item.bizCode)) + kv('数量', fmtNum(item.quantity)) +
        kv('流转说明', text(description)) +
        (item.contactName ? kv('联系人', text(item.contactName)) : ''));
    }).join('');
    if (!logistics && !isEmptyList(salesTargets)) {
      logistics = salesTargets.map(function (item) {
        var description = '该批次' + (item.salesDate ? '于 ' + String(item.salesDate).slice(0, 10) + ' ' : '') +
          '完成销售出库' + (item.clientName ? ',去向' + item.clientName : '') + '。';
        return li('<span class="li-ttl">' + text(item.clientName || '销售去向') + '</span>', fmtDay(item.salesDate),
          kv('出库单号', text(item.salesCode || item.salesOrderCode)) + kv('数量', fmtNum(item.quantity)) +
          kv('流转说明', text(description)) +
          (item.contactName ? kv('联系人', text(item.contactName)) : ''));
      }).join('');
    }
    html += sec('物流信息', null, logistics || '<div class="empty-tip">暂无物流信息</div>', null);
    return html;
  }
  function stripUrl(v) {
@@ -583,7 +644,7 @@
      kv(operatorLabel, text(operatorName)) +
      kv(codeLabel, text(code || '-')) +
      kv('追溯网址', traceUrl
        ? '<a class="link" href="' + esc(traceUrl) + '">' + esc(traceUrl) + '</a>'
        ? '<a class="link" target="_top" href="' + esc(location.pathname.replace(/trace\.html$/, 'trace-location.html') + '?code=' + encodeURIComponent(code || '')) + '">' + esc(traceUrl) + '</a>'
        : '-') +
      kv('计量单位', text(d.itemUnitMeasureName)) +
      kv('追溯类型', esc(SOURCE_TYPE[d.sourceType] || '追溯码')) +
@@ -662,27 +723,6 @@
        return [text(s.receiptCode), text(s.vendorName), nw(fmtNum(s.receivedQuantity)), nw(fmtDay(s.receiptDate))];
      });
      html += sec('采购来源', null, table(['采购入库单', '供应商', '数量', '日期'], psRows), { collapsible: true, open: false });
    }
    // 种子流向信息(仅销售出库 bizType=119)
    var salesFlows = (d.seedFlows || []).filter(function (f) { return f.bizType === 119; });
    if (!isEmptyList(salesFlows)) {
      var sfBody = salesFlows.map(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(' / ') || '-';
        return li(statusBadge(FLOW_DIRECTION, f.type) + '<span class="li-ttl">' + esc(FLOW_BIZ_TYPE[f.bizType] || '') + '</span>',
          fmtDate(f.transactionTime),
          kv('来源单号', text(f.bizCode)) +
          kv('客户', text(f.clientName)) +
          kv('数量', qty) +
          kv('仓库/库区', text(wh)));
      }).join('');
      html += sec('种子流向信息', null, '<div class="scroll-list">' + sfBody + '</div>', { collapsible: true, open: false });
    } else if (!isEmptyList(d.salesTargets)) {
      var stRows = d.salesTargets.map(function (s) {
        return [text(s.salesCode || s.salesOrderCode || ''), text(s.clientName), nw(fmtNum(s.quantity)), nw(fmtDay(s.salesDate))];
      });
      html += sec('种子流向信息', null, table(['销售出库单', '客户', '数量', '日期'], stRows), { collapsible: true, open: false });
    }
    // 加工流转