feat: init ai-platform - 源码 + 配置

This commit is contained in:
2026-08-25 16:27:43 +08:00
commit 83210a606a
122 changed files with 27401 additions and 0 deletions
@@ -0,0 +1,217 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import type { Order } from '../data/orders';
import type { Theme } from '../context/ThemeContext';
import { darkTokens, lightTokens } from '../context/ThemeContext';
import DataSourceToggle from './DataSourceToggle';
interface CostHeaderProps {
theme?: Theme;
onToggleTheme?: () => void;
orders: Order[];
}
export default function CostHeader({ theme = 'dark', onToggleTheme, orders }: CostHeaderProps) {
const navigate = useNavigate();
const tokens = theme === 'dark' ? darkTokens : lightTokens;
const isLight = theme === 'light';
const [time, setTime] = useState(new Date());
useEffect(() => {
const t = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(t);
}, []);
const pad = (n: number) => String(n).padStart(2, '0');
const timeStr = `${pad(time.getHours())}:${pad(time.getMinutes())}:${pad(time.getSeconds())}`;
const dateStr = `${time.getFullYear()}-${pad(time.getMonth() + 1)}-${pad(time.getDate())}`;
const totalOrders = orders.length;
const closedOrders = orders.filter(o => o.status === 'closed').length;
const dangerOrders = orders.filter(o => o.riskLevel === 'danger').length;
const warningOrders = orders.filter(o => o.riskLevel === 'warning').length;
// 计算所有已结单订单的平均净利率(管理成本口径)
const closedList = orders.filter(o => o.status === 'closed');
const avgNetMargin = closedList.length > 0
? closedList.reduce((sum, o) => sum + (o.rows.find(r => r.type === '管理成本')?.netMargin ?? 0), 0) / closedList.length
: 0;
const stats = [
{ label: '在管订单', value: String(totalOrders), color: tokens.accent },
{ label: '已结单', value: String(closedOrders), color: tokens.success },
{ label: '成本预警', value: String(warningOrders), color: tokens.warning },
{ label: '亏损风险', value: String(dangerOrders), color: tokens.danger },
{ label: '平均净利率', value: `${(avgNetMargin * 100).toFixed(1)}%`, color: avgNetMargin >= 0.05 ? tokens.success : avgNetMargin >= 0 ? tokens.warning : tokens.danger },
];
return (
<header style={{
height: 88, flexShrink: 0,
display: 'flex', alignItems: 'center',
padding: '0 24px',
background: 'transparent',
position: 'relative', zIndex: 10, overflow: 'visible',
}}>
{/* 顶部扫光线 */}
<div style={{
position: 'absolute', top: 0, left: 0, right: 0, height: 1,
background: tokens.headerScanLine,
boxShadow: `0 0 12px ${tokens.accentGlow}`, zIndex: 2,
}} />
{/* 底部弧形 */}
<svg style={{
position: 'absolute', bottom: -26, left: 0,
width: '100%', height: 28,
pointerEvents: 'none', zIndex: 0, overflow: 'visible',
}} viewBox="0 0 1000 28" preserveAspectRatio="none">
<path d="M 0,0 C 250,28 750,28 1000,0" fill="none" stroke={tokens.svgStroke} strokeWidth="1" vectorEffect="non-scaling-stroke" />
<path d="M 0,0 C 250,28 750,28 1000,0" fill="none" stroke={tokens.svgDot.replace('0.9', '0.1')} strokeWidth="6" vectorEffect="non-scaling-stroke" />
<circle cx="0" cy="0" r="2.5" fill={tokens.svgDot.replace('0.9', '0.6')} />
<circle cx="1000" cy="0" r="2.5" fill={tokens.svgDot.replace('0.9', '0.6')} />
</svg>
{/* Logo + 标题 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 14, flex: 1, position: 'relative', zIndex: 1 }}>
<div style={{
height: 50, borderRadius: 8,
display: 'flex', alignItems: 'center', justifyContent: 'center',
overflow: 'hidden',
background: isLight ? '#ffffff' : 'rgba(235,242,255,0.95)',
padding: '0 10px',
boxShadow: '0 2px 10px rgba(0,0,0,0.15)',
}}>
<img
src="/regal-rexnord-logo.png"
alt="Regal Rexnord"
style={{ height: '92%', objectFit: 'contain' }}
/>
</div>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<h1 style={{
color: tokens.text, fontSize: 19, fontWeight: 700,
letterSpacing: '0.05em', margin: 0,
textShadow: tokens.titleGlow,
}}>
订单成本枢纽平台
</h1>
<span style={{
display: 'inline-flex', alignItems: 'center', gap: 4,
padding: '2px 8px', borderRadius: 10,
background: tokens.accentBg, border: `1px solid ${tokens.accent}55`,
fontSize: 12, color: tokens.accent, fontWeight: 600,
}}>
<span style={{
width: 5, height: 5, borderRadius: '50%',
backgroundColor: tokens.success,
boxShadow: `0 0 8px ${tokens.success}`,
animation: 'space-pulse 2s ease-in-out infinite',
}} />
LIVE
</span>
</div>
</div>
</div>
{/* 分隔线 */}
<div style={{ width: 1, height: 44, background: `linear-gradient(to bottom, transparent, ${tokens.accent}33, transparent)`, flexShrink: 0, marginRight: 24, position: 'relative', zIndex: 1 }} />
{/* 统计指标 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 6, flex: 1, justifyContent: 'center', position: 'relative', zIndex: 1, marginTop: 24 }}>
{stats.map((item, idx) => (
<div key={item.label} style={{ display: 'flex', alignItems: 'center' }}>
<div style={{
textAlign: 'center', padding: '6px 16px', borderRadius: 10,
background: tokens.kpiCardBg, border: `1px solid ${tokens.kpiCardBorder}`,
backdropFilter: 'blur(8px)',
}}>
<div style={{ color: item.color, fontSize: 24, fontWeight: 800, lineHeight: 1, textShadow: isLight ? 'none' : `0 0 12px ${item.color}80` }}>
{item.value}
</div>
<div style={{ color: isLight ? tokens.textSub : tokens.textMuted, fontSize: 13, marginTop: 4, letterSpacing: '0.04em', fontWeight: 600 }}>
{item.label}
</div>
</div>
{idx < stats.length - 1 && <div style={{ width: 1, height: 36, background: `${tokens.accent}18`, margin: '0 3px' }} />}
</div>
))}
</div>
{/* 右分隔线 */}
<div style={{ width: 1, height: 44, background: `linear-gradient(to bottom, transparent, ${tokens.accent}33, transparent)`, flexShrink: 0, marginLeft: 24, position: 'relative', zIndex: 1 }} />
{/* 时间 + 切换按钮 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 14, flex: 1, justifyContent: 'flex-end', position: 'relative', zIndex: 1 }}>
<button
onClick={() => navigate('/')}
style={{
padding: '6px 12px', borderRadius: 8, cursor: 'pointer',
border: `1px solid ${tokens.accent}55`,
background: tokens.accentBg,
color: tokens.accent, fontSize: 12, fontWeight: 600,
transition: 'all 0.18s ease',
}}
onMouseEnter={e => { e.currentTarget.style.background = `${tokens.accent}18`; e.currentTarget.style.borderColor = tokens.accent; }}
onMouseLeave={e => { e.currentTarget.style.background = tokens.accentBg; e.currentTarget.style.borderColor = `${tokens.accent}55`; }}
>
← 返回平台
</button>
<div style={{ textAlign: 'right' }}>
<div style={{ color: tokens.accent, fontSize: 20, fontWeight: 700, fontVariantNumeric: 'tabular-nums', lineHeight: 1, textShadow: isLight ? 'none' : `0 0 16px ${tokens.accentGlow}`, letterSpacing: '0.05em' }}>
{timeStr}
</div>
<div style={{ color: tokens.textMuted, fontSize: 11, marginTop: 2, letterSpacing: '0.04em' }}>
{dateStr}
</div>
<div style={{ color: `${tokens.accent}88`, fontSize: 11, marginTop: 4, letterSpacing: '0.06em' }}>
Powered by EIOS
</div>
</div>
{/* 切换风格按钮 */}
{onToggleTheme && (
<button
onClick={onToggleTheme}
title={theme === 'dark' ? '切换浅色风格' : '切换深色风格'}
style={{
display: 'flex', alignItems: 'center', gap: 5,
padding: '5px 12px', borderRadius: 20,
border: `1px solid ${tokens.accent}55`,
background: tokens.accentBg,
color: tokens.accent,
fontSize: 12, fontWeight: 600, cursor: 'pointer',
transition: 'all 0.2s ease',
backdropFilter: 'blur(6px)',
flexShrink: 0,
}}
onMouseEnter={e => {
e.currentTarget.style.background = `${tokens.accent}18`;
e.currentTarget.style.borderColor = tokens.accent;
}}
onMouseLeave={e => {
e.currentTarget.style.background = tokens.accentBg;
e.currentTarget.style.borderColor = `${tokens.accent}55`;
}}
>
{theme === 'dark' ? (
<svg width="13" height="13" viewBox="0 0 13 13" fill="none">
<circle cx="6.5" cy="6.5" r="3" stroke="currentColor" strokeWidth="1.3"/>
<path d="M6.5 1 L6.5 2.2 M6.5 10.8 L6.5 12 M1 6.5 L2.2 6.5 M10.8 6.5 L12 6.5 M2.7 2.7 L3.6 3.6 M9.4 9.4 L10.3 10.3 M10.3 2.7 L9.4 3.6 M3.6 9.4 L2.7 10.3" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round"/>
</svg>
) : (
<svg width="13" height="13" viewBox="0 0 13 13" fill="none">
<path d="M10.5 7.5 C9.8 9.8 7.6 11.5 5 11.5 C2.2 11.5 0 9.3 0 6.5 C0 3.9 1.7 1.7 4 1 C3.2 2 2.8 3.2 2.8 4.5 C2.8 7.5 5.3 10 8.3 10 C9.1 10 9.9 9.8 10.5 9.4 Z" fill="currentColor" opacity="0.8"/>
</svg>
)}
{theme === 'dark' ? '浅色' : '深色'}
</button>
)}
{/* 数据源切换 */}
<DataSourceToggle />
</div>
</header>
);
}