fix: keep analytics date ranges together and apply atomically
This commit is contained in:
@@ -24,13 +24,14 @@ const router = useRouter()
|
||||
const access = useAccessPreviewStore()
|
||||
const permitted = computed(() => access.can('platform:self-operated-analytics:list'))
|
||||
const sourceAccount = 'lingxing-primary'
|
||||
const filters = ref<QueryModel>({ warehouseCode: typeof route.query.warehouseCode === 'string' ? route.query.warehouseCode : '', rangePreset: 'LAST_15_DAYS', startDate: '', endDate: '' })
|
||||
const filters = ref<QueryModel>({ warehouseCode: typeof route.query.warehouseCode === 'string' ? route.query.warehouseCode : '', rangePreset: 'LAST_15_DAYS', dateRange: [] })
|
||||
let selectedWarehouse = String(filters.value.warehouseCode)
|
||||
const context = ref<QueryContext | null>(null)
|
||||
const data = ref<Dashboard | null>(null)
|
||||
const runtime = ref<Overview | null>(null)
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
const rangeValidationError = ref('')
|
||||
const errorDetails = ref<ApiErrorDetails | null>(null)
|
||||
const runtimeError = ref('')
|
||||
const routeError = ref('')
|
||||
@@ -45,7 +46,7 @@ try {
|
||||
if (typeof route.query.context !== 'string' || typeof route.query.overviewReturn !== 'string' || !filters.value.warehouseCode) throw new Error('经营总览返回参数无效')
|
||||
context.value = parseContext(route.query.context)
|
||||
appliedRange = parseOverviewReturn(route.query.overviewReturn)
|
||||
Object.assign(filters.value, { rangePreset: appliedRange.rangePreset, startDate: appliedRange.startDate ?? '', endDate: appliedRange.endDate ?? '' })
|
||||
Object.assign(filters.value, { rangePreset: appliedRange.rangePreset, dateRange: appliedRange.startDate && appliedRange.endDate ? [appliedRange.startDate, appliedRange.endDate] : [] })
|
||||
restoreScroll = appliedRange.scroll
|
||||
}
|
||||
} catch (cause) { routeError.value = cause instanceof Error ? cause.message : '经营总览返回参数无效' }
|
||||
@@ -55,7 +56,7 @@ const stockExpired = computed(() => Boolean(context.value && inventoryExpired(co
|
||||
const fields = computed<QueryField[]>(() => [
|
||||
{ key: 'warehouseCode', type: 'select', label: '仓库', width: 220, placeholder: '切换仓库', props: { clearable: false }, options: [...new Map((runtime.value?.warehouses ?? []).map(item => [item.warehouseCode, { value: item.warehouseCode, label: item.warehouseName || item.warehouseCode }])).values()] },
|
||||
{ key: 'rangePreset', type: 'select', label: '出库趋势范围', width: 180, props: { clearable: false }, options: rangeOptions },
|
||||
...(filters.value.rangePreset === 'CUSTOM' ? [{ key: 'startDate', type: 'date' as const, label: '开始日期', width: 160 }, { key: 'endDate', type: 'date' as const, label: '结束日期', width: 160 }] : []),
|
||||
...(filters.value.rangePreset === 'CUSTOM' ? [{ key: 'dateRange', type: 'daterange' as const, label: '日期范围', width: 300 }] : []),
|
||||
])
|
||||
const metricNames: { key: keyof Dashboard['kpis']; label: string; inventory?: boolean }[] = [
|
||||
{ key: 'intervalOutboundQuantity', label: '区间出库量' }, { key: 'averageDailyOutboundQuantity30d', label: '近30日日均出库' },
|
||||
@@ -82,13 +83,14 @@ async function load(refresh = false) {
|
||||
if (!refresh && contextExpired.value) { error.value = '原图查询已过期,请整体刷新经营总览'; return }
|
||||
const range = String(filters.value.rangePreset) as RangePreset
|
||||
if (!rangeOptions.some(option => option.value === range)) { error.value = '请选择有效的出库趋势范围'; return }
|
||||
const start = String(filters.value.startDate ?? '')
|
||||
const end = String(filters.value.endDate ?? '')
|
||||
if (range === 'CUSTOM') { const problem = validateCustomRange(start, end, context.value?.anchorDate); if (problem) { error.value = problem; return } }
|
||||
const selected = Array.isArray(filters.value.dateRange) ? filters.value.dateRange : []
|
||||
const start = typeof selected[0] === 'string' ? selected[0] : ''
|
||||
const end = typeof selected[1] === 'string' ? selected[1] : ''
|
||||
if (range === 'CUSTOM') { const problem = validateCustomRange(start, end, context.value?.anchorDate); if (problem) { rangeValidationError.value = problem; error.value = problem; errorDetails.value = null; return } }
|
||||
const pinned = refresh ? null : context.value
|
||||
const request = dataFence.begin()
|
||||
trendOpen.value = false
|
||||
loading.value = true; error.value = ''; errorDetails.value = null
|
||||
loading.value = true; error.value = ''; rangeValidationError.value = ''; errorDetails.value = null
|
||||
try {
|
||||
const params: DashboardQuery = { sourceAccount, warehouseCode: warehouseCode.value, rangePreset: range, ...contextParams(pinned), ...(range === 'CUSTOM' ? { startDate: start, endDate: end } : {}) }
|
||||
const result = await analyticsApi.dashboard(params, request.signal)
|
||||
@@ -144,7 +146,7 @@ async function showTrend(item: DashboardItem) {
|
||||
finally { if (request.current()) trendLoading.value = false }
|
||||
}
|
||||
async function reset() {
|
||||
filters.value = { warehouseCode: warehouseCode.value || selectedWarehouse || runtime.value?.warehouses.find(item => item.warehouseCode)?.warehouseCode || '', rangePreset: 'LAST_15_DAYS', startDate: '', endDate: '' }
|
||||
filters.value = { warehouseCode: warehouseCode.value || selectedWarehouse || runtime.value?.warehouses.find(item => item.warehouseCode)?.warehouseCode || '', rangePreset: 'LAST_15_DAYS', dateRange: [] }
|
||||
routeError.value = ''
|
||||
await nextTick()
|
||||
void load(true)
|
||||
@@ -153,6 +155,12 @@ watch(warehouseCode, code => {
|
||||
dataFence.cancel(); trendFence.cancel(); data.value = null; context.value = null; trendOpen.value = false; loading.value = false; error.value = ''; errorDetails.value = null; newData.value = false; routeError.value = ''; restoreScroll = null
|
||||
if (code) { selectedWarehouse = code; void load(true) }
|
||||
})
|
||||
watch(() => [filters.value.rangePreset, filters.value.dateRange], () => {
|
||||
if (rangeValidationError.value) {
|
||||
if (error.value === rangeValidationError.value) error.value = ''
|
||||
rangeValidationError.value = ''
|
||||
}
|
||||
}, { deep: true })
|
||||
watch(trendOpen, open => { if (!open) { trendFence.cancel(); trend.value = null; trendLoading.value = false } })
|
||||
onMounted(() => { visibility(); poller.start(); timer = setInterval(() => { now.value = Date.now() }, 1000); document.addEventListener('visibilitychange', visibility); if (context.value && !routeError.value) void load() })
|
||||
onUnmounted(() => { dataFence.cancel(); trendFence.cancel(); poller.stop(); clearInterval(timer); document.removeEventListener('visibilitychange', visibility) })
|
||||
|
||||
Reference in New Issue
Block a user