这是一个巴适伙伴企业官网项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

107 lines
3.2 KiB

import { useState, type CSSProperties } from 'react';
import { getLocalizedPath, type Locale } from '../i18n/utils';
type NetNode = {
id: string;
title: string;
desc: string;
link?: string;
};
export type OrgNetworkData = {
center: { title: string; subtitle: string; desc: string };
nodes: NetNode[];
};
// 6 个子节点均匀分布在圆周上(从正上方开始,顺时针 60° 间隔)
const ANGLES = [-90, -30, 30, 90, 150, 210];
const R = 38; // 半径(占容器百分比)
function nodePos(i: number) {
const a = (ANGLES[i] * Math.PI) / 180;
return {
x: 50 + R * Math.cos(a),
y: 50 + R * Math.sin(a),
};
}
export default function OrgNetwork({ data, locale }: { data: OrgNetworkData; locale: Locale }) {
const [active, setActive] = useState<string>('center');
const current =
active === 'center'
? { title: data.center.title, desc: data.center.desc }
: data.nodes.find((n) => n.id === active) ?? { title: data.center.title, desc: data.center.desc };
const link =
active === 'center' ? undefined : data.nodes.find((n) => n.id === active)?.link;
const learnMore = locale === 'en' ? 'Learn more' : '了解更多';
return (
<div className="org-shell">
{/* 辐射网络图 */}
<div className="org-net">
<svg className="org-lines" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
{data.nodes.map((n, i) => {
const p = nodePos(i);
const on = active === n.id;
return (
<line
key={n.id}
x1={50}
y1={50}
x2={p.x}
y2={p.y}
className={`org-line${on ? ' is-active' : ''}`}
/>
);
})}
</svg>
{/* 中心节点 */}
<button
type="button"
className={`org-node org-node-center${active === 'center' ? ' is-active' : ''}`}
style={{ left: '50%', top: '50%' } as CSSProperties}
onClick={() => setActive('center')}
aria-pressed={active === 'center'}
>
<span className="org-node-title">{data.center.title}</span>
<span className="org-node-sub">{data.center.subtitle}</span>
</button>
{/* 子节点 */}
{data.nodes.map((n, i) => {
const p = nodePos(i);
return (
<button
key={n.id}
type="button"
className={`org-node${active === n.id ? ' is-active' : ''}`}
style={{ left: `${p.x}%`, top: `${p.y}%` } as CSSProperties}
onClick={() => setActive(n.id)}
aria-pressed={active === n.id}
>
<span className="org-node-title">{n.title}</span>
</button>
);
})}
</div>
{/* 详情面板 */}
<div className="org-panel">
<h3 className="org-panel-title">{current.title}</h3>
<p className="org-panel-desc">{current.desc}</p>
{link && (
<a
className="org-panel-link"
href={link.startsWith('#') ? link : getLocalizedPath(link, locale)}
>
{learnMore}
<span aria-hidden="true">→</span>
</a>
)}
</div>
</div>
);
}