import type { ScreenData, TimeRange } from './screen';
|
import { FieldTracker, resolveDataMode, type ScreenDataMode } from './screen-field-meta';
|
import { structureScreen } from './screen-structure';
|
|
export type { ScreenDataMode };
|
|
export interface ResolvedScreenData {
|
data: ScreenData;
|
mode: ScreenDataMode;
|
demoFields: string[];
|
liveFields: string[];
|
}
|
|
function num(v: unknown, fallback = 0) {
|
const n = Number(v);
|
return Number.isFinite(n) ? n : fallback;
|
}
|
|
function alignSeries(source: number[] | undefined, len: number) {
|
const values = (source || []).map(v => num(v));
|
if (!values.length) return Array.from({ length: len }, () => 0);
|
if (values.length === len) return values;
|
if (values.length > len) return values.slice(values.length - len);
|
return [...Array.from({ length: len - values.length }, () => 0), ...values];
|
}
|
|
/** 用接口结果覆盖零值结构。缺字段保持 0 或空列表,不再回落到演示数据。 */
|
export function resolveScreenData(live: ScreenData | null | undefined, range: TimeRange): ResolvedScreenData {
|
const base = structureScreen(range);
|
const ctx = new FieldTracker();
|
|
if (!live) {
|
return {
|
data: base,
|
mode: 'live',
|
demoFields: [],
|
liveFields: []
|
};
|
}
|
|
const axis = live.axis?.length ? live.axis : base.axis;
|
ctx.markLive('axis');
|
|
const kpis = { ...base.kpis };
|
Object.keys(kpis).forEach(key => {
|
if (live.kpis && live.kpis[key] != null) {
|
kpis[key] = num(live.kpis[key]);
|
}
|
ctx.markLive(`kpis.${key}`);
|
});
|
|
const wasteMix = live.wasteMix?.length
|
? live.wasteMix.map((item, i) => ({
|
...base.wasteMix[i],
|
...item,
|
weight: num(item.weight),
|
value: num(item.value),
|
count: num(item.count),
|
color: item.color || base.wasteMix[i]?.color
|
}))
|
: base.wasteMix;
|
ctx.markLive('wasteMix');
|
|
const recycleItems = live.recycleItems?.length
|
? live.recycleItems.map((item, i) => ({
|
...base.recycleItems[i],
|
...item,
|
weight: num(item.weight),
|
value: num(item.value),
|
carbon: num(item.carbon),
|
color: item.color || base.recycleItems[i]?.color
|
}))
|
: base.recycleItems;
|
ctx.markLive('recycleItems');
|
|
const trend = {
|
weight: alignSeries(live.trend?.weight, axis.length),
|
value: alignSeries(live.trend?.value, axis.length),
|
carbon: alignSeries(live.trend?.carbon, axis.length)
|
};
|
ctx.markLive('trend');
|
|
const yearCarbon = live.yearCarbon?.length
|
? alignSeries(live.yearCarbon, 12)
|
: base.yearCarbon;
|
ctx.markLive('yearCarbon');
|
|
const deptRank = Array.isArray(live.deptRank) ? live.deptRank : [];
|
const classRank = Array.isArray(live.classRank) ? live.classRank : [];
|
const majorRank = Array.isArray(live.majorRank) ? live.majorRank : [];
|
ctx.markLive('deptRank');
|
ctx.markLive('classRank');
|
ctx.markLive('majorRank');
|
|
const pointFlow = {
|
issued: num(live.pointFlow?.issued),
|
transfer: num(live.pointFlow?.transfer),
|
donate: num(live.pointFlow?.donate),
|
mall: num(live.pointFlow?.mall),
|
balance: num(live.pointFlow?.balance)
|
};
|
ctx.markLive('pointFlow');
|
|
const participateTrend = alignSeries(live.participateTrend, axis.length);
|
const deliverTrend = alignSeries(live.deliverTrend, axis.length);
|
ctx.markLive('participateTrend');
|
ctx.markLive('deliverTrend');
|
|
const ordersByType = live.ordersByType?.axis?.length
|
? {
|
axis: live.ordersByType.axis,
|
现场回收: alignSeries(live.ordersByType.现场回收, live.ordersByType.axis.length),
|
预约上门: alignSeries(live.ordersByType.预约上门, live.ordersByType.axis.length),
|
大屏投递: alignSeries(live.ordersByType.大屏投递, live.ordersByType.axis.length),
|
活动回收: alignSeries(live.ordersByType.活动回收, live.ordersByType.axis.length)
|
}
|
: base.ordersByType;
|
ctx.markLive('ordersByType');
|
|
const appointmentFlow = live.appointmentFlow?.length ? live.appointmentFlow : base.appointmentFlow;
|
const sorterStatus = live.sorterStatus?.length ? live.sorterStatus : base.sorterStatus;
|
const vehicles = Array.isArray(live.vehicles) ? live.vehicles : [];
|
const sites = Array.isArray(live.sites) ? live.sites : [];
|
ctx.markLive('appointmentFlow');
|
ctx.markLive('sorterStatus');
|
ctx.markLive('vehicles');
|
ctx.markLive('sites');
|
|
const data: ScreenData = {
|
range,
|
axis,
|
kpis,
|
wasteMix,
|
recycleItems,
|
trend,
|
yearCarbon,
|
deptRank,
|
classRank,
|
majorRank,
|
pointFlow,
|
participateTrend,
|
deliverTrend,
|
ordersByType,
|
appointmentFlow,
|
sorterStatus,
|
vehicles,
|
sites,
|
unavailable: live.unavailable || []
|
};
|
|
return {
|
data,
|
mode: resolveDataMode(live, ctx),
|
demoFields: [],
|
liveFields: ctx.listLive()
|
};
|
}
|
|
/** 接口尚未返回时先展示零值,避免先闪出演示数字 */
|
export function initialScreenData(range: TimeRange = 'month') {
|
return resolveScreenData(null, range);
|
}
|
|
export { structureScreen, buildScreenAxis } from './screen-structure';
|