Compare commits
3 Commits
yanhui-wst
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 54ad6aad89 | |||
| 555ac6b116 | |||
| b955e78ba2 |
@ -245,8 +245,26 @@ function buildNotifyText(data = {}, options = {}) {
|
|||||||
const typeLine = buildTypeLine(data);
|
const typeLine = buildTypeLine(data);
|
||||||
if (typeLine) head.push(`类型:${typeLine}`);
|
if (typeLine) head.push(`类型:${typeLine}`);
|
||||||
|
|
||||||
|
// 🔥 2026-11-02 修:0 单有两种完全不同的含义,不能都写「全部正常」——
|
||||||
|
// ① 数据源没同步(镜像表窗口 0 行)→ 0 单是假的,写「全部正常」等于给群里发假绿;
|
||||||
|
// ② 当天确实没发货 → 0 单是真的,那也不叫「正常」,叫「无发货可核」。
|
||||||
|
const freshness = data.freshness || {};
|
||||||
if (totalPackages === 0) {
|
if (totalPackages === 0) {
|
||||||
head.push('本批无转运异常,全部正常。');
|
if (freshness.emptyWindow && freshness.sourceStale) {
|
||||||
|
const hours = Number(freshness.sourceAgeMinutes) > 0
|
||||||
|
? `(已 ${Math.round((Number(freshness.sourceAgeMinutes) / 60) * 10) / 10} 小时未同步)`
|
||||||
|
: '';
|
||||||
|
head.push(`⚠️ 物流数据未同步:镜像表最后更新 ${freshness.sourceLastUpdate || '未知'}${hours},`
|
||||||
|
+ `${batch.shipDate || '该日'} 的发货记录一条都没进库 —— 上面 0 单不代表当天没发货,请先到聚水潭核对。`);
|
||||||
|
} else if (freshness.emptyWindow) {
|
||||||
|
head.push(`${batch.shipDate || '该日'} 在物流镜像表里 0 条发货记录`
|
||||||
|
+ `(表最后更新 ${freshness.sourceLastUpdate || '未知'},数据源本身正常)——`
|
||||||
|
+ ' 若当天确有发货,说明上游漏同步了这一天,请到聚水潭核对。');
|
||||||
|
} else if (shipped === 0) {
|
||||||
|
head.push('本批发货 0 单,无异常可核。');
|
||||||
|
} else {
|
||||||
|
head.push('本批无转运异常,全部正常。');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const excludedCount = (Number(exclusions.carrierPackages) || 0)
|
const excludedCount = (Number(exclusions.carrierPackages) || 0)
|
||||||
|
|||||||
@ -38,6 +38,10 @@ const EXPORT_DETAIL_LIMIT = 5000;
|
|||||||
|
|
||||||
// 快照超过这么久没刷新 → 页面黄告警(这张表正常每小时刷一轮)
|
// 快照超过这么久没刷新 → 页面黄告警(这张表正常每小时刷一轮)
|
||||||
const SNAPSHOT_STALE_MINUTES = 120;
|
const SNAPSHOT_STALE_MINUTES = 120;
|
||||||
|
// 数据源(整张镜像表)多久没刷新算停更。与上面同值,但语义不同:
|
||||||
|
// SNAPSHOT_STALE_MINUTES 判的是「本批窗口内行的 query_time」,
|
||||||
|
// SOURCE_STALE_MINUTES 判的是「整张表最后被灌库的时刻」—— 窗口内 0 行时只能靠后者。
|
||||||
|
const SOURCE_STALE_MINUTES = Number(process.env.CANGKU_WLE_SOURCE_STALE_MINUTES) || 120;
|
||||||
|
|
||||||
const CACHE_TTL_MS = Number(process.env.CANGKU_WLE_CACHE_TTL_MS) || 5 * 60 * 1000;
|
const CACHE_TTL_MS = Number(process.env.CANGKU_WLE_CACHE_TTL_MS) || 5 * 60 * 1000;
|
||||||
// key = `${verifyDate}|${limit}`。用 Map 而不是单槽缓存:
|
// key = `${verifyDate}|${limit}`。用 Map 而不是单槽缓存:
|
||||||
@ -148,6 +152,36 @@ async function loadShippedPackages(batch) {
|
|||||||
return rows || [];
|
return rows || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 整张镜像表最后被灌库的时刻(表级新鲜度)。
|
||||||
|
*
|
||||||
|
* 🔥 2026-11-02 新增,起因:11-02 09:00 那批推出「已发货 0 单 / 0 个包裹,异常 0 个包裹」,
|
||||||
|
* 业务问「怎么可能 0 单」。查出来镜像表 **11-01 07:55 之后就没再灌过**,
|
||||||
|
* 而 v1 的 freshness 只用「本批窗口内行的 query_time」算 —— 窗口一行都没有时
|
||||||
|
* `snapshotAt` 是空串 → `stale=false`、`hint=''`,页面和钉钉于是把它当成
|
||||||
|
* 「本批确实没有发货 / 全部正常」。这是最危险的一种假绿。
|
||||||
|
*
|
||||||
|
* 成本:全表 `MAX(query_time)` 在一个 402 万行的视图上实测 **13.5s**(谓词下推差),
|
||||||
|
* 所以**只在窗口 0 行时才调**(正常批次一行都不用付这笔钱),结果随主结果进 5 分钟缓存。
|
||||||
|
*
|
||||||
|
* @returns {Promise<string>} 'YYYY-MM-DD HH:mm:ss',取不到返回空串
|
||||||
|
*/
|
||||||
|
async function loadSourceLastUpdate() {
|
||||||
|
try {
|
||||||
|
const rows = await cloudDatabase.query(
|
||||||
|
`SELECT MAX(e.query_time) AS max_q
|
||||||
|
FROM dbo.jst_order_express_details e WITH (NOLOCK)`,
|
||||||
|
{},
|
||||||
|
{ timeoutMs: 240000 }
|
||||||
|
);
|
||||||
|
return formatDateTime(rows?.[0]?.max_q);
|
||||||
|
} catch (err) {
|
||||||
|
// 探测失败不该让整页挂掉:退回「不知道」,hint 里会说明取不到表级时间
|
||||||
|
console.warn(`[WLE] 数据源新鲜度探测失败:${err.message}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 售后拦截判定:一次 IN 多少单 + 查询超时。
|
// 售后拦截判定:一次 IN 多少单 + 查询超时。
|
||||||
// 这两张售后表在 rpt 副本里**都是堆表(零索引)**,`o_id IN (...)` 只能全表扫,
|
// 这两张售后表在 rpt 副本里**都是堆表(零索引)**,`o_id IN (...)` 只能全表扫,
|
||||||
// 实测 125 万行的 returnAndRefund 4.5s、176 万行的 qmcust 8.5s —— 所以:
|
// 实测 125 万行的 returnAndRefund 4.5s、176 万行的 qmcust 8.5s —— 所以:
|
||||||
@ -315,7 +349,17 @@ async function getLogisticsExceptions(options = {}) {
|
|||||||
orders: [],
|
orders: [],
|
||||||
detailLimit: limit,
|
detailLimit: limit,
|
||||||
truncated: false,
|
truncated: false,
|
||||||
freshness: { snapshotAt: '', ageMinutes: null, stale: false, coversDeadline: null, hint: '' },
|
freshness: {
|
||||||
|
snapshotAt: '',
|
||||||
|
ageMinutes: null,
|
||||||
|
stale: false,
|
||||||
|
coversDeadline: null,
|
||||||
|
emptyWindow: false,
|
||||||
|
sourceLastUpdate: '',
|
||||||
|
sourceAgeMinutes: null,
|
||||||
|
sourceStale: false,
|
||||||
|
hint: '',
|
||||||
|
},
|
||||||
generatedAt,
|
generatedAt,
|
||||||
cached: false,
|
cached: false,
|
||||||
cacheAgeSec: 0,
|
cacheAgeSec: 0,
|
||||||
@ -510,11 +554,33 @@ async function getLogisticsExceptions(options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── 新鲜度 ──────────────────────────────────────────────────────────
|
// ── 新鲜度 ──────────────────────────────────────────────────────────
|
||||||
|
// 🔥 emptyWindow = 本批窗口里**一行都没取到**(12 仓范围)。
|
||||||
|
// 这时 snapshotAt 必然是空串,v1 的判据(coversDeadline=null、stale=false、hint='')
|
||||||
|
// 会让「上游没同步」和「当天真的没发货」长得一模一样,页面/钉钉都显示成 0 单全绿。
|
||||||
|
// 所以窗口为空时补查一次**表级**最后灌库时刻,把两种情形分开。
|
||||||
|
const emptyWindow = rows.length === 0;
|
||||||
|
const sourceLastUpdate = emptyWindow ? await loadSourceLastUpdate() : '';
|
||||||
const ageMinutes = snapshotAt ? diffMinutes(snapshotAt, generatedAt) : null;
|
const ageMinutes = snapshotAt ? diffMinutes(snapshotAt, generatedAt) : null;
|
||||||
const stale = ageMinutes !== null && ageMinutes > SNAPSHOT_STALE_MINUTES;
|
const stale = ageMinutes !== null && ageMinutes > SNAPSHOT_STALE_MINUTES;
|
||||||
|
const sourceAgeMinutes = sourceLastUpdate ? diffMinutes(sourceLastUpdate, generatedAt) : null;
|
||||||
|
const sourceStale = sourceAgeMinutes !== null && sourceAgeMinutes > SOURCE_STALE_MINUTES;
|
||||||
const coversDeadline = snapshotAt ? snapshotAt >= batch.deadline : null;
|
const coversDeadline = snapshotAt ? snapshotAt >= batch.deadline : null;
|
||||||
let hint = '';
|
let hint = '';
|
||||||
if (coversDeadline === false) {
|
if (emptyWindow) {
|
||||||
|
const lastText = sourceLastUpdate || '';
|
||||||
|
const hours = sourceAgeMinutes === null ? 0 : Math.round((sourceAgeMinutes / 60) * 10) / 10;
|
||||||
|
if (sourceStale) {
|
||||||
|
hint = `${batch.shipDate} 的发货记录在物流镜像表里一条都没有 —— 该表最后更新 ${lastText}(已 ${hours} 小时未同步)。`
|
||||||
|
+ ' 上面的「0 单 / 0 个包裹」是数据没进库,不代表当天没发货。'
|
||||||
|
+ ' 请先到聚水潭核对当天发货量,等上游同步后再刷新本页。';
|
||||||
|
} else if (lastText) {
|
||||||
|
hint = `${batch.shipDate} 在物流镜像表里 0 条发货记录(该表最后更新 ${lastText},数据源本身正常)。`
|
||||||
|
+ ' 若当天确实有发货,说明上游漏同步了这一天,请到聚水潭核对;若当天本就停发,这个 0 才是真的。';
|
||||||
|
} else {
|
||||||
|
hint = `${batch.shipDate} 在物流镜像表里 0 条发货记录,且取不到该表最后更新时间 ——`
|
||||||
|
+ ' 分不清是「当天没发货」还是「数据没同步」,请到聚水潭核对后再看本页。';
|
||||||
|
}
|
||||||
|
} else if (coversDeadline === false) {
|
||||||
hint = `物流快照最新只到 ${snapshotAt.slice(11, 16)},没覆盖本轮 ${batch.deadline.slice(11, 16)} 核验时刻 ——`
|
hint = `物流快照最新只到 ${snapshotAt.slice(11, 16)},没覆盖本轮 ${batch.deadline.slice(11, 16)} 核验时刻 ——`
|
||||||
+ ' 之后才揽收/更新的轨迹没进来,异常量会偏大,请以上面「数据更新于」为准,或等上游同步后刷新。';
|
+ ' 之后才揽收/更新的轨迹没进来,异常量会偏大,请以上面「数据更新于」为准,或等上游同步后刷新。';
|
||||||
} else if (stale) {
|
} else if (stale) {
|
||||||
@ -561,6 +627,12 @@ async function getLogisticsExceptions(options = {}) {
|
|||||||
ageMinutes,
|
ageMinutes,
|
||||||
stale,
|
stale,
|
||||||
coversDeadline,
|
coversDeadline,
|
||||||
|
// 本批窗口 0 行:0 单含义不明,必须配合下面两个字段读
|
||||||
|
emptyWindow,
|
||||||
|
// 表级最后灌库时刻(只有 emptyWindow 时才探测,否则为空串)
|
||||||
|
sourceLastUpdate,
|
||||||
|
sourceAgeMinutes,
|
||||||
|
sourceStale,
|
||||||
hint,
|
hint,
|
||||||
},
|
},
|
||||||
generatedAt,
|
generatedAt,
|
||||||
|
|||||||
@ -154,6 +154,10 @@ function stubDb(rows, options = {}) {
|
|||||||
if (/jst_returnAndRefundInfo|jst_qmcust_refundlist/u.test(String(sql))) {
|
if (/jst_returnAndRefundInfo|jst_qmcust_refundlist/u.test(String(sql))) {
|
||||||
return options.interceptRows || [];
|
return options.interceptRows || [];
|
||||||
}
|
}
|
||||||
|
// 窗口 0 行时的「表级最后灌库时刻」探测
|
||||||
|
if (/MAX\(\s*e\.query_time\s*\)/iu.test(String(sql))) {
|
||||||
|
return options.sourceMaxQueryTime === undefined ? [] : [{ max_q: options.sourceMaxQueryTime }];
|
||||||
|
}
|
||||||
return rows;
|
return rows;
|
||||||
};
|
};
|
||||||
require.cache[dbPath] = {
|
require.cache[dbPath] = {
|
||||||
@ -672,3 +676,42 @@ test('售后表查询失败时降级:不剔单、不炸页面', async () => {
|
|||||||
delete require.cache[require.resolve('../src/services/warehouseLogisticsExceptionRegistry')];
|
delete require.cache[require.resolve('../src/services/warehouseLogisticsExceptionRegistry')];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── ⑧ 窗口 0 行:0 单不能被当成「当天没发货 / 全部正常」(2026-11-02)──────
|
||||||
|
// 起因:11-02 09:00 那批推出「已发货 0 单 / 0 个包裹,异常 0 个包裹」,
|
||||||
|
// 实况是镜像表 11-01 07:55 之后就没再灌库,11-01 的 12 仓发货记录一条都没进库。
|
||||||
|
test('窗口 0 行 + 数据源停更 → emptyWindow/sourceStale,hint 明说「数据没进库」', async () => {
|
||||||
|
const nov2 = new Date(2026, 10, 2, 10, 0, 0); // 2026-11-02 10:00(本机时区)
|
||||||
|
await withStub(async (svc, calls) => {
|
||||||
|
const data = await svc.getLogisticsExceptions({ date: '2026-11-02', now: nov2, limit: 100 });
|
||||||
|
assert.equal(data.summary.shippedOrders, 0);
|
||||||
|
assert.equal(data.freshness.emptyWindow, true);
|
||||||
|
assert.equal(data.freshness.sourceLastUpdate, '2026-11-01 07:55:04');
|
||||||
|
assert.ok(data.freshness.sourceAgeMinutes > 120, `应算出停更小时数,实际 ${data.freshness.sourceAgeMinutes}`);
|
||||||
|
assert.equal(data.freshness.sourceStale, true);
|
||||||
|
assert.match(data.freshness.hint, /一条都没有/);
|
||||||
|
assert.match(data.freshness.hint, /不代表当天没发货/);
|
||||||
|
// 只在空窗口才付那次全表 MAX(query_time)(实测 13.5s)
|
||||||
|
assert.equal(calls.sql.filter((s) => /MAX\(\s*e\.query_time\s*\)/iu.test(s)).length, 1);
|
||||||
|
}, [], { sourceMaxQueryTime: '2026-11-01 07:55:04' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('窗口 0 行但数据源是新的 → 仍然提示「0 条记录」,不能静默全绿', async () => {
|
||||||
|
const nov2 = new Date(2026, 10, 2, 10, 0, 0);
|
||||||
|
await withStub(async (svc) => {
|
||||||
|
const data = await svc.getLogisticsExceptions({ date: '2026-11-02', now: nov2, limit: 100 });
|
||||||
|
assert.equal(data.freshness.emptyWindow, true);
|
||||||
|
assert.equal(data.freshness.sourceStale, false, '表是新的,不该报停更');
|
||||||
|
assert.match(data.freshness.hint, /0 条发货记录/, '真 0 单也要说清是「库里没有记录」');
|
||||||
|
assert.match(data.freshness.hint, /数据源本身正常/);
|
||||||
|
}, [], { sourceMaxQueryTime: '2026-11-02 09:58:00' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('窗口有行时不做全表探测(省掉 13.5s 的表级 MAX)', async () => {
|
||||||
|
await withStub(async (svc, calls) => {
|
||||||
|
const data = await svc.getLogisticsExceptions({ date: '2026-10-21', now: at(21, 10, 0), limit: 100 });
|
||||||
|
assert.equal(data.freshness.emptyWindow, false);
|
||||||
|
assert.equal(data.freshness.sourceLastUpdate, '');
|
||||||
|
assert.equal(calls.sql.filter((s) => /MAX\(\s*e\.query_time\s*\)/iu.test(s)).length, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -495,3 +495,50 @@ test('发文本:超长正文自动截断后仍会发出去(不是直接抛
|
|||||||
stub.restore();
|
stub.restore();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── ⑤ 0 单不能用「全部正常」糊过去(2026-11-02)──────────────────────────
|
||||||
|
// 实况:11-02 09:00 那批推的是「0 单 / 0 个包裹,异常 0 个包裹」,业务以为真没发货,
|
||||||
|
// 其实是物流镜像表 11-01 07:55 后就没同步。群里绝不能出现「全部正常」这种假绿。
|
||||||
|
test('数据源停更导致 0 单 → 正文首部醒目告警,且不出现「全部正常」', () => {
|
||||||
|
const data = sampleData({
|
||||||
|
rows: [],
|
||||||
|
summary: {
|
||||||
|
shippedOrders: 0, packageCount: 0, exceptionOrders: 0,
|
||||||
|
exceptionPackages: 0, exceptionRate: 0, centerCount: 0, byType: {},
|
||||||
|
},
|
||||||
|
exclusions: {},
|
||||||
|
freshness: {
|
||||||
|
emptyWindow: true,
|
||||||
|
sourceLastUpdate: '2026-11-01 07:55:04',
|
||||||
|
sourceAgeMinutes: 1565,
|
||||||
|
sourceStale: true,
|
||||||
|
hint: '11-01 的发货记录在物流镜像表里一条都没有',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const built = job.buildNotifyText(data, { maxDetailRows: 30 });
|
||||||
|
assert.match(built.text, /物流数据未同步/);
|
||||||
|
assert.match(built.text, /镜像表最后更新 2026-11-01 07:55:04/);
|
||||||
|
assert.match(built.text, /已 26\.1 小时未同步/);
|
||||||
|
assert.match(built.text, /不代表当天没发货/);
|
||||||
|
assert.ok(!built.text.includes('全部正常'), '数据没进库时不能说「全部正常」');
|
||||||
|
// 抬头仍要带「异常」关键词,否则钉钉自定义关键词会拒收
|
||||||
|
assert.match(built.text, /【仓储物流转运异常】/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('数据源正常但当天 0 条记录 → 说明是「库里没有记录」,不说「全部正常」也不谎报停更', () => {
|
||||||
|
const data = sampleData({
|
||||||
|
rows: [],
|
||||||
|
summary: {
|
||||||
|
shippedOrders: 0, packageCount: 0, exceptionOrders: 0,
|
||||||
|
exceptionPackages: 0, exceptionRate: 0, centerCount: 0, byType: {},
|
||||||
|
},
|
||||||
|
exclusions: {},
|
||||||
|
freshness: { emptyWindow: true, sourceLastUpdate: '2026-11-02 09:58:00', sourceAgeMinutes: 2, sourceStale: false },
|
||||||
|
});
|
||||||
|
const built = job.buildNotifyText(data, { maxDetailRows: 30 });
|
||||||
|
assert.match(built.text, /在物流镜像表里 0 条发货记录/);
|
||||||
|
assert.match(built.text, /数据源本身正常/);
|
||||||
|
assert.match(built.text, /请到聚水潭核对/);
|
||||||
|
assert.ok(!built.text.includes('物流数据未同步'), '表是新的,不能谎报停更');
|
||||||
|
assert.ok(!built.text.includes('全部正常'), '0 条不能糊成「全部正常」');
|
||||||
|
});
|
||||||
|
|||||||
@ -252,8 +252,15 @@ function WarehouseLogisticsException() {
|
|||||||
const [activeCenter, setActiveCenter] = useState('');
|
const [activeCenter, setActiveCenter] = useState('');
|
||||||
// 异常类型筛选:为空 = 全部
|
// 异常类型筛选:为空 = 全部
|
||||||
const [activeType, setActiveType] = useState('');
|
const [activeType, setActiveType] = useState('');
|
||||||
// 核验日;null = 交给后端按「现在几点」自动判(未到 9 点自动看昨天那批)
|
// 核验日;默认 null = 交给后端按「现在几点」自动判(未到 9 点自动看昨天那批)
|
||||||
const [verifyDate, setVerifyDate] = useState(null);
|
// 🔥 钉钉消息里的链接带 `?date=YYYY-MM-DD`(见 buildPageLink),页面必须认这个参数 ——
|
||||||
|
// 否则群里点进来看到的是「今天这批」,跟消息里说的不是同一批,历史消息会误导人。
|
||||||
|
const [verifyDate, setVerifyDate] = useState(() => {
|
||||||
|
const raw = String(new URLSearchParams(window.location.search).get('date') || '').trim();
|
||||||
|
if (!/^\d{4}-\d{2}-\d{2}$/u.test(raw)) return null;
|
||||||
|
const parsed = dayjs(raw);
|
||||||
|
return parsed.isValid() ? parsed : null;
|
||||||
|
});
|
||||||
// 导出中(导出比列表慢,按钮要转圈,避免重复点)
|
// 导出中(导出比列表慢,按钮要转圈,避免重复点)
|
||||||
const [exporting, setExporting] = useState(false);
|
const [exporting, setExporting] = useState(false);
|
||||||
// 每天定点把异常文本推到钉钉群(「异常通知」)
|
// 每天定点把异常文本推到钉钉群(「异常通知」)
|
||||||
@ -564,14 +571,16 @@ function WarehouseLogisticsException() {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{freshness && (freshness.coversDeadline === false || freshness.stale) ? (
|
{freshness && (freshness.emptyWindow || freshness.coversDeadline === false || freshness.stale) ? (
|
||||||
<Alert
|
<Alert
|
||||||
type={freshness.coversDeadline === false ? 'error' : 'warning'}
|
type={freshness.emptyWindow || freshness.coversDeadline === false ? 'error' : 'warning'}
|
||||||
showIcon
|
showIcon
|
||||||
className="wle-alert"
|
className="wle-alert"
|
||||||
message={freshness.coversDeadline === false
|
message={freshness.emptyWindow
|
||||||
? '物流快照未覆盖本轮核验时刻,异常量偏大'
|
? '本批 0 单:物流镜像表里没有当天的发货记录,这个 0 不可当结论'
|
||||||
: '物流数据可能滞后'}
|
: (freshness.coversDeadline === false
|
||||||
|
? '物流快照未覆盖本轮核验时刻,异常量偏大'
|
||||||
|
: '物流数据可能滞后')}
|
||||||
description={freshness.hint}
|
description={freshness.hint}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
@ -589,7 +598,11 @@ function WarehouseLogisticsException() {
|
|||||||
formatter={(v) => (notStarted ? '—' : number(v))}
|
formatter={(v) => (notStarted ? '—' : number(v))}
|
||||||
/>
|
/>
|
||||||
<Text type="secondary" className="wle-stat__sub">
|
<Text type="secondary" className="wle-stat__sub">
|
||||||
共 {notStarted ? '—' : number(summary.packageCount)} 个包裹
|
{notStarted
|
||||||
|
? '—'
|
||||||
|
: (freshness?.emptyWindow
|
||||||
|
? '数据未同步,见上方红条'
|
||||||
|
: `共 ${number(summary.packageCount)} 个包裹`)}
|
||||||
</Text>
|
</Text>
|
||||||
</Card>
|
</Card>
|
||||||
<Card bordered={false} className="wle-stat">
|
<Card bordered={false} className="wle-stat">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user