feat: init ai-platform - 源码 + 配置
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { BrainCircuit, Calculator, Network, Boxes, ReceiptText, CalendarClock, Sparkles } from 'lucide-react';
|
||||
import { useTheme } from '@/shared/context/ThemeContext';
|
||||
import StarBackground from '@/shared/components/StarBackground';
|
||||
import ThemeToggle from '@/shared/components/ThemeToggle';
|
||||
import { modules } from '@/modules/registry';
|
||||
|
||||
const ICON_MAP: Record<string, React.ComponentType<{ size?: number; color?: string }>> = {
|
||||
BrainCircuit,
|
||||
Calculator,
|
||||
Network,
|
||||
Boxes,
|
||||
ReceiptText,
|
||||
CalendarClock,
|
||||
Sparkles,
|
||||
};
|
||||
|
||||
export default function HomePage() {
|
||||
const navigate = useNavigate();
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === 'dark';
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
// 首页模块多时需纵向滚动;离开后恢复全局 overflow:hidden(其他模块自管布局)
|
||||
useEffect(() => {
|
||||
document.body.style.overflow = 'auto';
|
||||
return () => { document.body.style.overflow = ''; };
|
||||
}, []);
|
||||
|
||||
// 接收 URL 参数 userToken,写入 sessionStorage
|
||||
useEffect(() => {
|
||||
const userToken = searchParams.get('userToken');
|
||||
if (userToken) {
|
||||
sessionStorage.setItem('userToken', userToken);
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
width: '100vw', minHeight: '100vh',
|
||||
overflowX: 'hidden',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
alignItems: 'stretch',
|
||||
position: 'relative',
|
||||
paddingTop: '64px',
|
||||
boxSizing: 'border-box',
|
||||
fontFamily: "-apple-system, 'SF Pro Display', 'SF Pro Text', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif",
|
||||
color: isDark ? '#a8d8ff' : '#1e3a6e',
|
||||
}}>
|
||||
{/* 星空背景(dark)/ 浅色渐变(light) */}
|
||||
{isDark ? (
|
||||
<StarBackground theme="dark" />
|
||||
) : (
|
||||
<div style={{
|
||||
position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
|
||||
background: 'linear-gradient(160deg, #f8f8fb 0%, #f2f3f7 50%, #eef1f7 100%)',
|
||||
zIndex: 0,
|
||||
}} />
|
||||
)}
|
||||
|
||||
{/* 顶部导航栏 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: '64px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '0 32px',
|
||||
zIndex: 100,
|
||||
background: isDark ? 'rgba(7,17,31,0.7)' : 'rgba(255,255,255,0.8)',
|
||||
backdropFilter: 'blur(12px)',
|
||||
borderBottom: `1px solid ${isDark ? 'rgba(0,212,255,0.15)' : 'rgba(0,0,0,0.06)'}`,
|
||||
}}>
|
||||
{/* 左上角 Logo + 标题 + 标语 */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||||
<img
|
||||
src="/digiwin.svg"
|
||||
alt="Digiwin"
|
||||
style={{
|
||||
height: '32px',
|
||||
width: 'auto',
|
||||
objectFit: 'contain',
|
||||
filter: isDark ? 'drop-shadow(0 0 8px rgba(0,212,255,0.4))' : 'none'
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
width: '1px',
|
||||
height: '24px',
|
||||
background: isDark ? 'rgba(0,212,255,0.2)' : 'rgba(0,0,0,0.12)'
|
||||
}} />
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
|
||||
<h1 style={{
|
||||
fontSize: 24, fontWeight: 700, letterSpacing: '0.04em', margin: 0,
|
||||
...(isDark ? {
|
||||
color: '#e8f4ff',
|
||||
textShadow: '0 0 12px rgba(0,212,255,0.4)',
|
||||
} : {
|
||||
color: '#111827',
|
||||
}),
|
||||
}}>
|
||||
EIOSpace
|
||||
</h1>
|
||||
<span style={{
|
||||
fontSize: 13, fontWeight: 400, letterSpacing: '0.02em',
|
||||
...(isDark ? { color: 'rgba(168,216,255,0.6)' } : { color: '#6b7280' }),
|
||||
}}>
|
||||
企业智能 · 数据驱动 · 产业赋能
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右上角主题切换 */}
|
||||
<div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center' }}>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 内容层 */}
|
||||
<div style={{ position: 'relative', zIndex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 32, padding: '32px 24px 60px', maxWidth: 1400, width: '100%', margin: '0 auto' }}>
|
||||
|
||||
{/* 模块卡片网格 */}
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
||||
gap: 20,
|
||||
width: '100%',
|
||||
maxWidth: 1400,
|
||||
animation: 'card-in 0.5s 0.2s both',
|
||||
}}>
|
||||
{modules.map((mod, idx) => {
|
||||
const IconComp = ICON_MAP[mod.icon];
|
||||
const accentColor = isDark ? mod.accentColor : mod.accentColorLight;
|
||||
const gradient = isDark ? mod.gradient : mod.gradientLight;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={mod.id}
|
||||
onClick={() => navigate(mod.path)}
|
||||
style={{
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.25s ease',
|
||||
animation: `card-in 0.4s ${0.1 + idx * 0.08}s both`,
|
||||
...(isDark ? {
|
||||
background: 'rgba(0,20,60,0.55)',
|
||||
border: '1px solid rgba(0,212,255,0.25)',
|
||||
boxShadow: '0 0 20px rgba(0,212,255,0.06), inset 0 1px 0 rgba(255,255,255,0.05)',
|
||||
backdropFilter: 'blur(12px)',
|
||||
} : {
|
||||
background: '#ffffff',
|
||||
border: '1px solid rgba(10,122,255,0.12)',
|
||||
boxShadow: '0 2px 12px rgba(0,0,0,0.06)',
|
||||
}),
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (isDark) {
|
||||
e.currentTarget.style.borderColor = `${accentColor}60`;
|
||||
e.currentTarget.style.boxShadow = `0 0 30px ${accentColor}20, inset 0 1px 0 rgba(255,255,255,0.08)`;
|
||||
e.currentTarget.style.transform = 'translateY(-4px)';
|
||||
} else {
|
||||
e.currentTarget.style.borderColor = `${accentColor}40`;
|
||||
e.currentTarget.style.boxShadow = `0 8px 24px rgba(0,0,0,0.10)`;
|
||||
e.currentTarget.style.transform = 'translateY(-4px)';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (isDark) {
|
||||
e.currentTarget.style.borderColor = 'rgba(0,212,255,0.25)';
|
||||
e.currentTarget.style.boxShadow = '0 0 20px rgba(0,212,255,0.06), inset 0 1px 0 rgba(255,255,255,0.05)';
|
||||
e.currentTarget.style.transform = 'translateY(0)';
|
||||
} else {
|
||||
e.currentTarget.style.borderColor = 'rgba(10,122,255,0.12)';
|
||||
e.currentTarget.style.boxShadow = '0 2px 12px rgba(0,0,0,0.06)';
|
||||
e.currentTarget.style.transform = 'translateY(0)';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* 卡片顶部渐变条 */}
|
||||
<div style={{
|
||||
height: 108,
|
||||
background: gradient,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
{/* 扫光动画(dark模式) */}
|
||||
{isDark && (
|
||||
<div style={{
|
||||
position: 'absolute', top: 0, left: '-100%', width: '60%', height: '100%',
|
||||
background: `linear-gradient(to right, transparent, ${accentColor}15, transparent)`,
|
||||
animation: 'space-border-scan 3s linear infinite',
|
||||
}} />
|
||||
)}
|
||||
{IconComp && <IconComp size={48} color={isDark ? accentColor : '#ffffff'} />}
|
||||
</div>
|
||||
|
||||
{/* 卡片内容 */}
|
||||
<div style={{ padding: '20px 24px 24px' }}>
|
||||
<h2 style={{
|
||||
fontSize: 20, fontWeight: 800, letterSpacing: '0.04em', margin: '0 0 8px',
|
||||
...(isDark ? {
|
||||
color: '#e8f4ff',
|
||||
textShadow: `0 0 12px ${accentColor}50`,
|
||||
} : {
|
||||
color: '#111827',
|
||||
}),
|
||||
}}>
|
||||
{mod.name}
|
||||
</h2>
|
||||
<p style={{
|
||||
fontSize: 14, lineHeight: 1.6, margin: '0 0 16px',
|
||||
...(isDark ? { color: 'rgba(168,216,255,0.7)' } : { color: '#6b7280' }),
|
||||
}}>
|
||||
{mod.description}
|
||||
</p>
|
||||
|
||||
{/* 标签 */}
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
|
||||
{mod.tags.map(tag => (
|
||||
<span key={tag} style={{
|
||||
padding: '3px 10px', borderRadius: 20, fontSize: 12, fontWeight: 600,
|
||||
...(isDark ? {
|
||||
background: `${accentColor}10`,
|
||||
border: `1px solid ${accentColor}30`,
|
||||
color: accentColor,
|
||||
} : {
|
||||
background: `${accentColor}08`,
|
||||
border: `1px solid ${accentColor}20`,
|
||||
color: accentColor,
|
||||
}),
|
||||
}}>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 进入按钮 */}
|
||||
<button style={{
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
|
||||
width: '100%', padding: '10px 0', borderRadius: 12,
|
||||
fontSize: 14, fontWeight: 700, cursor: 'pointer',
|
||||
transition: 'all 0.18s ease',
|
||||
...(isDark ? {
|
||||
background: `${accentColor}12`,
|
||||
border: `1px solid ${accentColor}35`,
|
||||
color: accentColor,
|
||||
} : {
|
||||
background: accentColor,
|
||||
border: 'none',
|
||||
color: '#ffffff',
|
||||
}),
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (isDark) {
|
||||
e.currentTarget.style.background = `${accentColor}20`;
|
||||
e.currentTarget.style.borderColor = accentColor;
|
||||
e.currentTarget.style.boxShadow = `0 0 16px ${accentColor}30`;
|
||||
} else {
|
||||
e.currentTarget.style.background = `${accentColor}dd`;
|
||||
e.currentTarget.style.boxShadow = `0 4px 12px ${accentColor}30`;
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (isDark) {
|
||||
e.currentTarget.style.background = `${accentColor}12`;
|
||||
e.currentTarget.style.borderColor = `${accentColor}35`;
|
||||
e.currentTarget.style.boxShadow = 'none';
|
||||
} else {
|
||||
e.currentTarget.style.background = accentColor;
|
||||
e.currentTarget.style.boxShadow = 'none';
|
||||
}
|
||||
}}
|
||||
>
|
||||
进入模块 →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* 底部品牌 */}
|
||||
<div style={{
|
||||
textAlign: 'center',
|
||||
...(isDark ? { color: 'rgba(0,212,255,0.55)' } : { color: '#aeaeb2' }),
|
||||
fontSize: 13, letterSpacing: '0.06em',
|
||||
}}>
|
||||
Powered by Digiwin EIOSpace
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user