oa-ai/src/app/page.tsx

110 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
import { verifySharedJwt } from '@/lib/jwt'
import Header from '@/components/Header'
function siteUrl(url: string, domain: string): string {
if (process.env.NODE_ENV === 'production') {
return `https://${domain}`
}
return url
}
const CORE_SITES = [
{ name: '资产管理', url: 'http://127.0.0.1:6177', desc: 'GPU 服务器、存储服务器等硬件设备信息管理与实时监控', tag: 'CMDB', dot: '#2563eb', domain: 'assets.tlyq.ai' },
{ name: '工单跟踪', url: 'http://127.0.0.1:6176', desc: '故障工单全流程管理SLA 自动计算,月度/周度报告导出', tag: 'ITS', dot: '#7c3aed', domain: 'issue.tlyq.ai' },
]
const OTHER_SITES = [
{ name: '官网', url: 'http://127.0.0.1:6173', desc: 'tlyq.ai 企业官方网站', tag: 'WWW', dot: '#059669', domain: 'www.tlyq.ai' },
{ name: '云平台', url: 'http://127.0.0.1:6174', desc: '云服务登录入口与资源概览', tag: 'CLOUD', dot: '#d97706', domain: 'cloud.tlyq.ai' },
{ name: 'Token 工厂', url: 'http://127.0.0.1:6175', desc: 'Token 管理与发放平台', tag: 'TOKEN', dot: '#e11d48', domain: 'token.tlyq.ai' },
{ name: '代码仓库', url: 'https://git.tlyq.ai', desc: 'Gitea 代码托管与版本管理', tag: 'GIT', dot: '#db2777', domain: 'git.tlyq.ai' },
]
const COLORS: Record<string, { light: string; tag: string }> = {
'#2563eb': { light: 'rgba(37,99,235,0.08)', tag: '#2563eb' },
'#7c3aed': { light: 'rgba(124,58,237,0.08)', tag: '#7c3aed' },
'#059669': { light: 'rgba(5,150,105,0.08)', tag: '#059669' },
'#d97706': { light: 'rgba(217,119,6,0.08)', tag: '#d97706' },
'#e11d48': { light: 'rgba(225,29,72,0.08)', tag: '#e11d48' },
'#db2777': { light: 'rgba(219,39,119,0.08)', tag: '#db2777' },
}
export default async function HomePage() {
const cookieStore = await cookies()
const sessionCookie = cookieStore.get('session')?.value
let username = ''
if (sessionCookie) {
try { username = JSON.parse(sessionCookie).username || '' } catch { }
}
if (!username) redirect('/login')
const tlyqToken = cookieStore.get('tlyq_session')?.value
let displayName = username
if (tlyqToken) {
const shared = verifySharedJwt(tlyqToken)
if (shared && shared.displayName && shared.displayName !== shared.username) {
displayName = shared.displayName
}
}
return (
<div style={{ minHeight: '100vh', background: 'var(--bg)' }}>
<Header />
<div style={{ maxWidth: 1160, margin: '0 auto', padding: '32px 28px 60px' }}>
<div style={{ marginBottom: 28 }}>
<div style={{ fontSize: 11, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>tlyq.ai / OA PORTAL</div>
<h2 style={{ fontSize: 24, fontWeight: 700, color: 'var(--text)', margin: 0 }}>{displayName}</h2>
</div>
<div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.05em', margin: '0 0 12px', paddingBottom: 8, borderBottom: '1px solid var(--border)' }}></div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
{CORE_SITES.map(site => {
const c = COLORS[site.dot]
return (
<a key={site.name} href={siteUrl(site.url, site.domain)} target="_blank" rel="noopener noreferrer" className="sc" style={{
display: 'flex', flexDirection: 'column', background: 'var(--bg-card)', border: '1px solid var(--border)',
borderRadius: 12, padding: 22, textDecoration: 'none', minHeight: 140,
boxShadow: '0 1px 2px rgba(0,0,0,0.04)', transition: 'all 0.2s',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
<div style={{ width: 7, height: 7, borderRadius: '50%', background: site.dot, flexShrink: 0 }}></div>
<div style={{ fontSize: 15, fontWeight: 600, color: 'var(--text)', flex: 1 }}>{site.name}</div>
<span style={{ fontSize: 11, padding: '2px 8px', borderRadius: 10, background: c.light, color: c.tag, fontWeight: 500, letterSpacing: '0.03em' }}>{site.tag}</span>
</div>
<div style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.5, flex: 1 }}>{site.desc}</div>
<div className="ch" style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 'auto', paddingTop: 8, opacity: 0, transform: 'translateY(4px)', transition: 'all 0.25s ease' }}>{site.domain} &rarr;</div>
</a>
)
})}
</div>
<div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.05em', margin: '36px 0 12px', paddingBottom: 8, borderBottom: '1px solid var(--border)' }}></div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
{OTHER_SITES.map(site => {
const c = COLORS[site.dot]
return (
<a key={site.name} href={siteUrl(site.url, site.domain)} target="_blank" rel="noopener noreferrer" className="sc" style={{
display: 'flex', flexDirection: 'column', background: 'var(--bg-card)', border: '1px solid var(--border)',
borderRadius: 12, padding: 22, textDecoration: 'none', minHeight: 140,
boxShadow: '0 1px 2px rgba(0,0,0,0.04)', transition: 'all 0.2s',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
<div style={{ width: 7, height: 7, borderRadius: '50%', background: site.dot, flexShrink: 0 }}></div>
<div style={{ fontSize: 15, fontWeight: 600, color: 'var(--text)', flex: 1 }}>{site.name}</div>
<span style={{ fontSize: 11, padding: '2px 8px', borderRadius: 10, background: c.light, color: c.tag, fontWeight: 500, letterSpacing: '0.03em' }}>{site.tag}</span>
</div>
<div style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.5, flex: 1 }}>{site.desc}</div>
<div className="ch" style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 'auto', paddingTop: 8, opacity: 0, transform: 'translateY(4px)', transition: 'all 0.25s ease' }}>{site.domain} &rarr;</div>
</a>
)
})}
</div>
<style>{`.sc:hover { border-color: #2563eb !important; box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04) !important; transform: translateY(-1px); } .sc:hover .ch { opacity: 1 !important; transform: translateY(0) !important; } .lo:hover { background: var(--bg-hover) !important; color: var(--text) !important; border-color: var(--text-muted) !important; }`}</style>
</div>
</div>
)
}