fix(selfop): show inventory observation time range

This commit is contained in:
2026-10-13 00:10:54 +08:00
parent 600fdcb4b4
commit cd3a32f372
2 changed files with 20 additions and 0 deletions
@@ -201,6 +201,7 @@ onUnmounted(() => { poller.stop(); dataFence.cancel(); dailyFence.cancel(); docu
<p v-if="runtimeError" class="analytics-note" role="status">{{ runtimeError }};最近观测:{{ beijingTime(overview?.observedAt) }}</p> <p v-if="runtimeError" class="analytics-note" role="status">{{ runtimeError }};最近观测:{{ beijingTime(overview?.observedAt) }}</p>
<LoadErrorState v-if="error" title="查询未完成" :message="error" :loading="loading" compact @retry="load(true)"><template #details><ApiErrorMeta v-if="errorDetails" :details="errorDetails" /></template></LoadErrorState> <LoadErrorState v-if="error" title="查询未完成" :message="error" :loading="loading" compact @retry="load(true)"><template #details><ApiErrorMeta v-if="errorDetails" :details="errorDetails" /></template></LoadErrorState>
<p v-if="newData || newDay" class="analytics-note" role="status">{{ newDay ? '北京时间已跨日。' : '有新数据。' }}点击查询更新数据。</p> <p v-if="newData || newDay" class="analytics-note" role="status">{{ newDay ? '北京时间已跨日。' : '有新数据。' }}点击查询更新数据。</p>
<p v-if="mode === 'inventory' && context" class="analytics-note" data-testid="inventory-observation">本仓库存采集时间(北京时间):{{ beijingTime(context.inventoryObservedFrom) }} 至 {{ beijingTime(context.inventoryObservedUntil) }}。{{ context.inventoryObservedFrom && context.inventoryObservedUntil ? '当前列表来自本仓一轮完整分页观测,各页在该时段内采集,不代表同一瞬间的库存。' : '尚无完整库存观测时段,缺失时间不以发布时间代替。' }}</p>
<details class="analytics-rules"> <details class="analytics-rules">
<summary>统计说明 · 北京时间/件</summary> <summary>统计说明 · 北京时间/件</summary>
<p>{{ summaryMode ? '今日显示已采集量,完整日期显示出库量;未知数据不按零计算。' : '使用截至昨日的完整自然日;日均和可售天数由已采集数据计算。' }}同一SKU属于不同货主时分别展示。</p> <p>{{ summaryMode ? '今日显示已采集量,完整日期显示出库量;未知数据不按零计算。' : '使用截至昨日的完整自然日;日均和可售天数由已采集数据计算。' }}同一SKU属于不同货主时分别展示。</p>
@@ -50,6 +50,25 @@ beforeEach(() => {
}) })
afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()) }) afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()) })
describe('self-operated actual workbench request state', () => { describe('self-operated actual workbench request state', () => {
it('shows the pinned warehouse inventory collection interval as one full pagination observation', async () => {
mocks.api.inventory.mockResolvedValue({ context: { ...context, inventoryObservedFrom: '2026-10-12T00:00:00Z', inventoryObservedUntil: '2026-10-12T00:12:00Z' }, records: [metric], total: 1 })
mocks.api.overview.mockResolvedValue({ ...overview, warehouses: [{ warehouseCode: 'WH-A', dataFamily: 'INVENTORY', lastPublishedAt: '2026-10-12T02:00:00Z', inventoryObservedFrom: '2026-10-12T01:30:00Z', inventoryObservedUntil: '2026-10-12T01:45:00Z' } as WarehouseOverview] })
const wrapper = mount(AnalyticsView, { ...options, props: { mode: 'inventory' } }); wrappers.push(wrapper)
await flushPromises()
const note = wrapper.get('[data-testid="inventory-observation"]')
expect(note.text()).toContain('2026/10/12 08:00:00 至 2026/10/12 08:12:00')
expect(note.text()).toContain('本仓一轮完整分页观测')
expect(note.text()).toContain('不代表同一瞬间的库存')
expect(note.text()).not.toContain('09:30:00')
expect(note.text()).not.toContain('10:00:00')
expect(wrapper.findComponent(tableStub).props('columns')[0].prop).toBe('sku')
})
it('does not invent inventory observation times when no published interval is available', async () => {
const wrapper = mount(AnalyticsView, { ...options, props: { mode: 'inventory' } }); wrappers.push(wrapper)
await flushPromises()
expect(wrapper.get('[data-testid="inventory-observation"]').text()).toContain('— 至 —')
expect(wrapper.get('[data-testid="inventory-observation"]').text()).toContain('尚无完整库存观测时段')
})
it.each(['sales', 'inventory', 'collection'] as const)('uses the public integrated filter region and layout settings for %s', async mode => { it.each(['sales', 'inventory', 'collection'] as const)('uses the public integrated filter region and layout settings for %s', async mode => {
const wrapper = mode === 'collection' ? mount(CollectionView, options) : mount(AnalyticsView, { ...options, props: { mode } }) const wrapper = mode === 'collection' ? mount(CollectionView, options) : mount(AnalyticsView, { ...options, props: { mode } })
wrappers.push(wrapper) wrappers.push(wrapper)