import type { TimeSeriesQuery } from '@jeesite/otg/api/types/query'; import type { LocationQueryValue } from 'vue-router'; export const BEHAVIOR_SPACETIME_JUMP_QUERY_KEY = 'jumpPayload'; export const BEHAVIOR_SPACETIME_JUMP_RESULT_KEY = 'behaviorSpacetimeJumpResult'; export interface BehaviorSpacetimeJumpPayload extends Partial { tableName?: string; personName?: string; personNames?: string[]; keyword?: string; startDate?: string; endDate?: string; seriesDateTime?: string; } type TimeSeriesJumpResponseLike = | BehaviorSpacetimeJumpPayload | Array> | { data?: BehaviorSpacetimeJumpPayload; query?: BehaviorSpacetimeJumpPayload; record?: BehaviorSpacetimeJumpPayload; row?: BehaviorSpacetimeJumpPayload; obj?: BehaviorSpacetimeJumpPayload; result?: BehaviorSpacetimeJumpPayload; } | null | undefined; const pickRouteQueryValue = (value?: LocationQueryValue | LocationQueryValue[]) => { return Array.isArray(value) ? value[0] : value; }; const normalizeText = (value: unknown) => { if (value === 0) return '0'; const text = String(value ?? '').trim(); if (!text || text === 'null' || text === 'undefined') return undefined; return text; }; const normalizeStringArray = (value: unknown) => { if (Array.isArray(value)) { const values = value.map((item) => normalizeText(item)).filter((item): item is string => Boolean(item)); return values.length ? Array.from(new Set(values)) : undefined; } const text = normalizeText(value); if (!text) return undefined; if (text.startsWith('[') && text.endsWith(']')) { try { const parsed = JSON.parse(text); if (Array.isArray(parsed)) return normalizeStringArray(parsed); } catch { // Ignore invalid JSON and fall back to comma split. } } const values = text .split(',') .map((item) => normalizeText(item)) .filter((item): item is string => Boolean(item)); return values.length ? Array.from(new Set(values)) : undefined; }; const extractIndexedObjectList = (value: unknown) => { if (!value || Array.isArray(value) || typeof value !== 'object') return []; return Object.entries(value as Record) .filter(([key, item]) => /^\d+$/.test(key) && !!item && typeof item === 'object') .sort((left, right) => Number(left[0]) - Number(right[0])) .map(([, item]) => item as Record); }; const normalizeResponseList = (resp?: TimeSeriesJumpResponseLike) => { if (Array.isArray(resp)) return resp; if (Array.isArray(resp?.data)) return resp.data; const indexedDataList = extractIndexedObjectList(resp?.data); if (indexedDataList.length) return indexedDataList; if (Array.isArray((resp as Record | undefined)?.records)) { return (resp as Record).records as Array>; } if (Array.isArray((resp as Record | undefined)?.list)) { return (resp as Record).list as Array>; } if (Array.isArray((resp as Record | undefined)?.result)) { return (resp as Record).result as Array>; } const indexedRespList = extractIndexedObjectList(resp); if (indexedRespList.length) return indexedRespList; return []; }; const pickJumpSource = (resp?: TimeSeriesJumpResponseLike) => { if (!resp || Array.isArray(resp)) return {}; return resp.data ?? resp.query ?? resp.record ?? resp.row ?? resp.obj ?? resp.result ?? resp; }; const normalizePayload = (source?: Record) => { const tableNameEn = normalizeText(source?.tableNameEn) || normalizeText(source?.tableName); const tableName = normalizeText(source?.tableName) || tableNameEn; const personNames = normalizeStringArray(source?.personNames); const personName = normalizeText(source?.personName) || (personNames?.length === 1 ? personNames[0] : undefined); const id = normalizeText(source?.id); const rowId = normalizeText(source?.rowId); const startDate = normalizeText(source?.startDate); const endDate = normalizeText(source?.endDate); const seriesDateTime = normalizeText(source?.seriesDateTime); return { hasLocal: normalizeText(source?.hasLocal), types: normalizeStringArray(source?.types), tableName, tableNameEn, tableNameCn: normalizeText(source?.tableNameCn), id, rowId, personName, personNames, keyword: normalizeText(source?.keyword), startDate, endDate, seriesDateTime, } satisfies BehaviorSpacetimeJumpPayload; }; export const buildTimeSeriesJumpQuery = (query?: Partial) => { return normalizePayload((query || {}) as Record); }; export const hasMeaningfulJumpPayload = (payload?: Partial | null) => { if (!payload) return false; return Boolean( payload.id || payload.rowId || payload.tableNameEn || payload.tableName || payload.personName || payload.personNames?.length || payload.types?.length || payload.keyword || payload.startDate || payload.endDate || payload.seriesDateTime, ); }; export const resolveBehaviorSpacetimeJumpPayload = ( resp?: TimeSeriesJumpResponseLike, fallback?: Partial, ) => { const responseList = normalizeResponseList(resp); if (responseList.length) { const fallbackPayload = buildTimeSeriesJumpQuery(fallback); const responsePersonNames = normalizeStringArray( responseList.map((item) => normalizeText(item?.personName)).filter(Boolean), ); const personNames = fallbackPayload.personNames?.length ? fallbackPayload.personNames : responsePersonNames; const orderedDateTimes = responseList .map((item) => normalizeText(item?.seriesDateTime)) .filter((item): item is string => Boolean(item)) .sort((left, right) => new Date(left).getTime() - new Date(right).getTime()); const startDate = orderedDateTimes[0]; const endDate = orderedDateTimes[orderedDateTimes.length - 1]; return buildTimeSeriesJumpQuery({ ...fallbackPayload, personName: fallbackPayload.personName || (personNames?.length === 1 ? personNames[0] : undefined), personNames, keyword: undefined, startDate: fallbackPayload.startDate || startDate, endDate: fallbackPayload.endDate || endDate, }); } const normalizedResp = normalizePayload(pickJumpSource(resp) as Record); if (hasMeaningfulJumpPayload(normalizedResp)) return normalizedResp; return buildTimeSeriesJumpQuery(fallback); }; export const persistBehaviorSpacetimeJumpResult = (resp?: unknown) => { if (typeof window === 'undefined') return; try { if (!resp) { window.sessionStorage.removeItem(BEHAVIOR_SPACETIME_JUMP_RESULT_KEY); return; } window.sessionStorage.setItem(BEHAVIOR_SPACETIME_JUMP_RESULT_KEY, JSON.stringify(resp)); } catch { // Ignore storage errors. } }; export const consumeBehaviorSpacetimeJumpResult = () => { if (typeof window === 'undefined') return null; try { const raw = window.sessionStorage.getItem(BEHAVIOR_SPACETIME_JUMP_RESULT_KEY); if (!raw) return null; window.sessionStorage.removeItem(BEHAVIOR_SPACETIME_JUMP_RESULT_KEY); return JSON.parse(raw); } catch { return null; } }; const buildBehaviorSpacetimeNavigationPayload = (payload?: Partial) => { const normalized = buildTimeSeriesJumpQuery(payload); return { ...normalized, id: normalized.id || normalized.rowId, rowId: undefined, } satisfies BehaviorSpacetimeJumpPayload; }; export const serializeBehaviorSpacetimeJumpPayload = (payload?: Partial) => { return JSON.stringify(buildBehaviorSpacetimeNavigationPayload(payload)); }; export const parseBehaviorSpacetimeJumpPayload = (raw?: LocationQueryValue | LocationQueryValue[]) => { const text = pickRouteQueryValue(raw); if (!text) return null; try { const parsed = JSON.parse(String(text)); const payload = buildTimeSeriesJumpQuery(parsed as Partial); return hasMeaningfulJumpPayload(payload) ? payload : null; } catch { return null; } };