export type ScreenDataMode = 'live' | 'demo' | 'mixed';
|
|
/** KPI 分组:组内任一指标有真实值,则整组采用 live(含合法 0 值) */
|
export const KPI_GROUPS: Record<string, string[]> = {
|
'kpis.weight': [
|
'todayWeightKg',
|
'periodWeightTon',
|
'totalWeightTon',
|
'recyclableTon',
|
'bulkTon',
|
'elecTon',
|
'fabricTon',
|
'resourceRate',
|
'periodValue'
|
],
|
'kpis.carbon': ['periodCarbonTon', 'yearCarbonTon', 'monthCarbonTon', 'totalCarbonTon', 'greenActions'],
|
'kpis.participation': [
|
'totalParticipants',
|
'totalPoints',
|
'newParticipants',
|
'participateRate',
|
'pointsIssued',
|
'pointsConsumed',
|
'pointsBalance'
|
],
|
'kpis.operation': [
|
'sorterOnline',
|
'sorterTotal',
|
'vehicleOnline',
|
'vehicleTotal',
|
'siteOnline',
|
'siteTotal',
|
'todayOrders',
|
'todayAppointments',
|
'pendingAppointments',
|
'alerts',
|
'healthAlerts'
|
]
|
};
|
|
export const SECTION_LABELS: Record<string, string> = {
|
axis: '时间轴',
|
'kpis.weight': '回收与资源化指标',
|
'kpis.carbon': '碳减排指标',
|
'kpis.participation': '参与与碳积分指标',
|
'kpis.operation': '运营监控指标',
|
wasteMix: '垃圾分类结构',
|
recycleItems: '可回收物品类',
|
trend: '回收趋势',
|
yearCarbon: '年度月度碳减排',
|
deptRank: '院系排行',
|
classRank: '班级排行',
|
majorRank: '专业排行',
|
pointFlow: '碳积分流转',
|
participateTrend: '参与人数趋势',
|
deliverTrend: '投递次数趋势',
|
ordersByType: '订单类型趋势',
|
appointmentFlow: '预约订单漏斗',
|
sorterStatus: '分拣员状态',
|
vehicles: '车辆运行',
|
sites: '分拣点吞吐'
|
};
|
|
export class FieldTracker {
|
readonly demo = new Set<string>();
|
readonly live = new Set<string>();
|
|
markDemo(field: string) {
|
this.demo.add(field);
|
this.live.delete(field);
|
}
|
|
markLive(field: string) {
|
this.live.add(field);
|
this.demo.delete(field);
|
}
|
|
listDemo() {
|
return [...this.demo];
|
}
|
|
listLive() {
|
return [...this.live];
|
}
|
}
|
|
export function groupHasLive(keys: string[], live: Record<string, number>) {
|
return keys.some(k => Number(live[k]) > 0);
|
}
|
|
export function resolveDataMode(
|
live: unknown,
|
tracker: FieldTracker
|
): ScreenDataMode {
|
if (!live) return 'demo';
|
if (tracker.demo.size === 0) return 'live';
|
if (tracker.live.size === 0) return 'demo';
|
return 'mixed';
|
}
|
|
function kpiGroupOf(field: string) {
|
if (!field.startsWith('kpis.')) return null;
|
const key = field.slice(5);
|
for (const [group, keys] of Object.entries(KPI_GROUPS)) {
|
if (keys.includes(key)) return group;
|
}
|
return null;
|
}
|
|
function fieldBelongsToSection(field: string, section: string) {
|
if (field === section || field.startsWith(`${section}.`)) return true;
|
if (section.startsWith('kpis.')) {
|
return kpiGroupOf(field) === section;
|
}
|
return false;
|
}
|
|
/** 将字段级 demo 标记折叠为可读区块列表 */
|
export function groupDemoFields(fields: string[]) {
|
const sections = new Set<string>();
|
for (const field of fields) {
|
const group = kpiGroupOf(field);
|
if (group) {
|
sections.add(group);
|
continue;
|
}
|
if (field.startsWith('pointFlow.')) {
|
sections.add('pointFlow');
|
continue;
|
}
|
sections.add(field.split('.')[0]);
|
}
|
return [...sections]
|
.sort()
|
.map(section => ({
|
section,
|
label: SECTION_LABELS[section] || section
|
}));
|
}
|
|
export function isDemoSection(
|
section: string,
|
demoFields: string[],
|
dataMode: ScreenDataMode
|
) {
|
if (dataMode === 'live') return false;
|
if (dataMode === 'demo') return true;
|
return demoFields.some(field => fieldBelongsToSection(field, section));
|
}
|
|
export function isDemoField(field: string, demoFields: string[], dataMode: ScreenDataMode) {
|
if (dataMode === 'live') return false;
|
if (dataMode === 'demo') return true;
|
if (demoFields.includes(field)) return true;
|
const group = kpiGroupOf(field);
|
if (!group) return false;
|
return demoFields.some(item => fieldBelongsToSection(item, group));
|
}
|