From bf9400d4198094194439214b5e90036b2f040052 Mon Sep 17 00:00:00 2001
From: wangrong <wangrong@shsening.com>
Date: 星期四, 24 九月 2026 16:03:10 +0800
Subject: [PATCH] bug修复,用真实数据

---
 src/modules/report/data/screen-merge.ts |  371 +++++++++++++++-------------------------------------
 1 files changed, 106 insertions(+), 265 deletions(-)

diff --git a/src/modules/report/data/screen-merge.ts b/src/modules/report/data/screen-merge.ts
index 3f76e64..b820412 100644
--- a/src/modules/report/data/screen-merge.ts
+++ b/src/modules/report/data/screen-merge.ts
@@ -1,12 +1,5 @@
 import type { ScreenData, TimeRange } from './screen';
-import { demoScreen } from './screen-demo';
-import {
-	FieldTracker,
-	KPI_GROUPS,
-	groupHasLive,
-	resolveDataMode,
-	type ScreenDataMode
-} from './screen-field-meta';
+import { FieldTracker, resolveDataMode, type ScreenDataMode } from './screen-field-meta';
 import { structureScreen } from './screen-structure';
 
 export type { ScreenDataMode };
@@ -18,271 +11,120 @@
 	liveFields: string[];
 }
 
-function sum(nums: number[]) {
-	return nums.reduce((a, b) => a + (Number(b) || 0), 0);
+function num(v: unknown, fallback = 0) {
+	const n = Number(v);
+	return Number.isFinite(n) ? n : fallback;
 }
 
-function hasPositive(nums: number[]) {
-	return nums.some(v => Number(v) > 0);
+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];
 }
 
-function hasRankData(list: any[]) {
-	return list.length > 0 && list.some(e => Number(e?.people) > 0 || Number(e?.weight) > 0);
-}
-
-function pickKpis(
-	live: Record<string, number> | undefined,
-	demo: Record<string, number>,
-	ctx: FieldTracker
-) {
-	const out = { ...demo };
-	if (!live) {
-		Object.values(KPI_GROUPS)
-			.flat()
-			.forEach(key => ctx.markDemo(`kpis.${key}`));
-		return out;
-	}
-
-	for (const [group, keys] of Object.entries(KPI_GROUPS)) {
-		if (groupHasLive(keys, live)) {
-			keys.forEach(key => {
-				const v = Number(live[key]);
-				out[key] = Number.isFinite(v) ? v : demo[key];
-				ctx.markLive(`kpis.${key}`);
-			});
-		} else {
-			keys.forEach(key => ctx.markDemo(`kpis.${key}`));
-		}
-	}
-	return out;
-}
-
-function pickArray<T>(
-	field: string,
-	live: T[] | undefined,
-	demo: T[],
-	ctx: FieldTracker,
-	valid: (v: T[]) => boolean
-) {
-	if (live && valid(live)) {
-		ctx.markLive(field);
-		return live;
-	}
-	ctx.markDemo(field);
-	return demo;
-}
-
-function pickTrend(
-	field: string,
-	live: { weight: number[]; value: number[]; carbon: number[] } | undefined,
-	demo: ScreenData['trend'],
-	axis: string[],
-	ctx: FieldTracker
-) {
-	const valid =
-		live &&
-		live.weight?.length &&
-		(sum(live.weight) > 0 || sum(live.value) > 0 || sum(live.carbon) > 0);
-	if (valid) {
-		ctx.markLive(field);
-		return {
-			weight: alignSeries(live!.weight, axis.length, live!.weight[live!.weight.length - 1] || 0),
-			value: alignSeries(live!.value, axis.length, live!.value[live!.value.length - 1] || 0),
-			carbon: alignSeries(live!.carbon, axis.length, live!.carbon[live!.carbon.length - 1] || 0)
-		};
-	}
-	ctx.markDemo(field);
-	return alignTrend(demo, axis.length);
-}
-
-function alignSeries(source: number[], len: number, fill = 0) {
-	if (!source?.length) return Array.from({ length: len }, () => fill);
-	if (source.length === len) return [...source];
-	if (source.length > len) return source.slice(source.length - len);
-	return [...Array.from({ length: len - source.length }, () => fill), ...source];
-}
-
-function alignTrend(trend: ScreenData['trend'], len: number) {
-	return {
-		weight: alignSeries(trend.weight, len),
-		value: alignSeries(trend.value, len),
-		carbon: alignSeries(trend.carbon, len)
-	};
-}
-
-function pickWasteMix(
-	live: ScreenData['wasteMix'] | undefined,
-	demo: ScreenData['wasteMix'],
-	ctx: FieldTracker
-) {
-	if (
-		live?.length &&
-		live.some(e => Number(e.weight) > 0 || Number(e.count) > 0 || Number(e.value) > 0)
-	) {
-		ctx.markLive('wasteMix');
-		return live.map((item, i) => ({
-			...demo[i],
-			...item,
-			color: item.color || demo[i]?.color
-		}));
-	}
-	ctx.markDemo('wasteMix');
-	return demo;
-}
-
-function pickRecycleItems(
-	live: ScreenData['recycleItems'] | undefined,
-	demo: ScreenData['recycleItems'],
-	ctx: FieldTracker
-) {
-	if (live?.length && live.some(e => Number(e.weight) > 0 || Number(e.value) > 0 || Number(e.carbon) > 0)) {
-		ctx.markLive('recycleItems');
-		return live.map((item, i) => ({
-			...demo[i],
-			...item,
-			color: item.color || demo[i]?.color
-		}));
-	}
-	ctx.markDemo('recycleItems');
-	return demo;
-}
-
-function pickPointFlow(
-	live: ScreenData['pointFlow'] | undefined,
-	demo: ScreenData['pointFlow'],
-	ctx: FieldTracker
-) {
-	const out = { ...demo };
-	if (!live) {
-		ctx.markDemo('pointFlow');
-		(['issued', 'transfer', 'donate', 'mall', 'balance'] as const).forEach(key =>
-			ctx.markDemo(`pointFlow.${key}`)
-		);
-		return out;
-	}
-
-	let hasLive = false;
-	(['issued', 'transfer', 'donate', 'mall', 'balance'] as const).forEach(key => {
-		if (Number(live[key]) > 0) {
-			out[key] = live[key];
-			ctx.markLive(`pointFlow.${key}`);
-			hasLive = true;
-		} else {
-			ctx.markDemo(`pointFlow.${key}`);
-		}
-	});
-	if (hasLive) ctx.markLive('pointFlow');
-	else ctx.markDemo('pointFlow');
-	return out;
-}
-
-function pickOrdersByType(
-	live: ScreenData['ordersByType'] | undefined,
-	demo: ScreenData['ordersByType'],
-	ctx: FieldTracker
-) {
-	const keys = ['鐜板満鍥炴敹', '棰勭害涓婇棬', '澶у睆鎶曢��', '娲诲姩鍥炴敹'] as const;
-	const liveValid = live?.axis?.length && keys.some(k => hasPositive(live[k] || []));
-	if (liveValid) {
-		ctx.markLive('ordersByType');
-		return live!;
-	}
-	ctx.markDemo('ordersByType');
-	return demo;
-}
-
-function pickAppointmentFlow(
-	live: ScreenData['appointmentFlow'] | undefined,
-	demo: ScreenData['appointmentFlow'],
-	ctx: FieldTracker
-) {
-	if (live?.length && live.some(e => Number(e.value) > 0)) {
-		ctx.markLive('appointmentFlow');
-		return live;
-	}
-	ctx.markDemo('appointmentFlow');
-	return demo;
-}
-
-function pickSorterStatus(
-	live: ScreenData['sorterStatus'] | undefined,
-	demo: ScreenData['sorterStatus'],
-	ctx: FieldTracker
-) {
-	if (live?.length && sum(live.map(e => Number(e.value))) > 0) {
-		ctx.markLive('sorterStatus');
-		return live;
-	}
-	ctx.markDemo('sorterStatus');
-	return demo;
-}
-
-function pickAxis(
-	live: ScreenData | null | undefined,
-	demo: ScreenData,
-	structure: ScreenData,
-	ctx: FieldTracker
-) {
-	const useLiveAxis =
-		live?.axis?.length &&
-		(sum(live.trend?.weight || []) > 0 ||
-			sum(live.participateTrend || []) > 0 ||
-			sum(live.deliverTrend || []) > 0 ||
-			live.axis.length > 1);
-	if (useLiveAxis) {
-		ctx.markLive('axis');
-		return live!.axis;
-	}
-	ctx.markDemo('axis');
-	return demo.axis.length ? demo.axis : structure.axis;
-}
-
-/** 缁撴瀯 + 婕旂ず + 鐪熷疄鏁版嵁鎸夊瓧娈�/鍒嗙粍鍚堝苟 */
+/** 鐢ㄦ帴鍙g粨鏋滆鐩栭浂鍊肩粨鏋勩�傜己瀛楁淇濇寔 0 鎴栫┖鍒楄〃锛屼笉鍐嶅洖钀藉埌婕旂ず鏁版嵁銆� */
 export function resolveScreenData(live: ScreenData | null | undefined, range: TimeRange): ResolvedScreenData {
-	const structure = structureScreen(range);
-	const demo = demoScreen(range);
+	const base = structureScreen(range);
 	const ctx = new FieldTracker();
 
-	const axis = pickAxis(live, demo, structure, ctx);
-	const kpis = pickKpis(live?.kpis, demo.kpis, ctx);
-	const wasteMix = pickWasteMix(live?.wasteMix, demo.wasteMix, ctx);
-	const recycleItems = pickRecycleItems(live?.recycleItems, demo.recycleItems, ctx);
-	const trend = pickTrend('trend', live?.trend, demo.trend, axis, ctx);
-
-	let yearCarbon = demo.yearCarbon;
-	if (live?.yearCarbon?.length && sum(live.yearCarbon) > 0) {
-		yearCarbon = live.yearCarbon;
-		ctx.markLive('yearCarbon');
-	} else {
-		ctx.markDemo('yearCarbon');
+	if (!live) {
+		return {
+			data: base,
+			mode: 'live',
+			demoFields: [],
+			liveFields: []
+		};
 	}
 
-	const deptRank = pickArray('deptRank', live?.deptRank, demo.deptRank, ctx, hasRankData);
-	const classRank = pickArray('classRank', live?.classRank, demo.classRank, ctx, hasRankData);
-	const majorRank = pickArray('majorRank', live?.majorRank, demo.majorRank, ctx, hasRankData);
-	const pointFlow = pickPointFlow(live?.pointFlow, demo.pointFlow, ctx);
+	const axis = live.axis?.length ? live.axis : base.axis;
+	ctx.markLive('axis');
 
-	let participateTrend = alignSeries(demo.participateTrend, axis.length);
-	if (live?.participateTrend?.length && sum(live.participateTrend) > 0) {
-		participateTrend = alignSeries(live.participateTrend, axis.length);
-		ctx.markLive('participateTrend');
-	} else {
-		ctx.markDemo('participateTrend');
-	}
+	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}`);
+	});
 
-	let deliverTrend = alignSeries(demo.deliverTrend, axis.length);
-	if (live?.deliverTrend?.length && sum(live.deliverTrend) > 0) {
-		deliverTrend = alignSeries(live.deliverTrend, axis.length);
-		ctx.markLive('deliverTrend');
-	} else {
-		ctx.markDemo('deliverTrend');
-	}
+	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 ordersByType = pickOrdersByType(live?.ordersByType, demo.ordersByType, ctx);
-	const appointmentFlow = pickAppointmentFlow(live?.appointmentFlow, demo.appointmentFlow, ctx);
-	const sorterStatus = pickSorterStatus(live?.sorterStatus, demo.sorterStatus, ctx);
-	const vehicles = pickArray('vehicles', live?.vehicles, demo.vehicles, ctx, v => v.length > 0);
-	const sites = pickArray('sites', live?.sites, demo.sites, ctx, v => v.length > 0);
+	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,
@@ -303,21 +145,20 @@
 		sorterStatus,
 		vehicles,
 		sites,
-		unavailable: live?.unavailable || []
+		unavailable: live.unavailable || []
 	};
 
 	return {
 		data,
 		mode: resolveDataMode(live, ctx),
-		demoFields: ctx.listDemo(),
+		demoFields: [],
 		liveFields: ctx.listLive()
 	};
 }
 
-/** 鎺ュ彛澶辫触鎴栨棤 live 鏁版嵁鏃剁殑鍒濆灞曠ず */
+/** 鎺ュ彛灏氭湭杩斿洖鏃跺厛灞曠ず闆跺�硷紝閬垮厤鍏堥棯鍑烘紨绀烘暟瀛� */
 export function initialScreenData(range: TimeRange = 'month') {
 	return resolveScreenData(null, range);
 }
 
 export { structureScreen, buildScreenAxis } from './screen-structure';
-export { demoScreen } from './screen-demo';

--
Gitblit v1.9.1