325 lines
12 KiB
JavaScript
325 lines
12 KiB
JavaScript
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||
import { motion, AnimatePresence } from 'framer-motion';
|
||
|
||
const TeamSection = ({ isActive }) => {
|
||
const members = useMemo(() => ([
|
||
{
|
||
id: 'agan',
|
||
name: '阿甘',
|
||
role: '创始人',
|
||
bio: '从0到1推动产品与战略落地,专注AI与工业融合。',
|
||
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/agan.png',
|
||
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/agan.png',
|
||
brandLogo: '/asset/logo-bai.png',
|
||
education: ['本科(待补充)', '硕士(待补充)'],
|
||
experience: [
|
||
'负责公司整体战略与产品路线',
|
||
'推动跨部门协作与商业落地'
|
||
],
|
||
skills: ['战略规划', '产品设计', 'AI应用', '工业融合']
|
||
},
|
||
{
|
||
id: 'liwei',
|
||
name: '立伟',
|
||
role: '市场运营总监',
|
||
bio: '增长策略与品牌叙事负责人,连接产品与客户价值。',
|
||
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/liwei.png',
|
||
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/liwei.png',
|
||
brandLogo: '/asset/logo-bai.png',
|
||
education: ['本科(待补充)'],
|
||
experience: [
|
||
'规划年度增长与渠道策略',
|
||
'打造品牌故事与市场活动'
|
||
],
|
||
skills: ['增长策略', '品牌建设', '整合营销', '渠道拓展']
|
||
},
|
||
{
|
||
id: 'shuangji',
|
||
name: '爽吉',
|
||
role: '硬件工程师 & 硬件部门主管',
|
||
bio: '负责高可靠硬件架构设计与产线可制造性优化。',
|
||
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/shuangji.png',
|
||
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/shuangji.png',
|
||
brandLogo: '/asset/logo-bai.png',
|
||
education: ['本科(待补充)'],
|
||
experience: [
|
||
'主导多款硬件平台架构设计',
|
||
'建立DFM/DFT标准并提升良率'
|
||
],
|
||
skills: ['硬件架构', '嵌入式', 'DFM/DFT', '可靠性']
|
||
},
|
||
{
|
||
id: 'laoxv',
|
||
name: '老许',
|
||
role: '工业设计工程师 & 结构件工程师',
|
||
bio: '负责高可靠硬件架构设计与产线可制造性优化。',
|
||
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/laoxv.png',
|
||
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/laoxv.png',
|
||
brandLogo: '/asset/logo-bai.png',
|
||
education: ['本科(待补充)'],
|
||
experience: [
|
||
'主导多款硬件平台架构设计',
|
||
'建立DFM/DFT标准并提升良率'
|
||
],
|
||
skills: ['硬件架构', '嵌入式', 'DFM/DFT', '可靠性']
|
||
}
|
||
|
||
]), []);
|
||
|
||
const [index, setIndex] = useState(0);
|
||
const trackRef = useRef(null);
|
||
const firstCardRef = useRef(null);
|
||
const [slideStep, setSlideStep] = useState(0);
|
||
const CARD_GAP = 24; // 与样式中的间距保持一致
|
||
const [selected, setSelected] = useState(null);
|
||
|
||
useEffect(() => {
|
||
const measure = () => {
|
||
if (!firstCardRef.current) return;
|
||
const rect = firstCardRef.current.getBoundingClientRect();
|
||
const step = Math.round(rect.width + CARD_GAP);
|
||
setSlideStep(step);
|
||
};
|
||
measure();
|
||
window.addEventListener('resize', measure);
|
||
return () => window.removeEventListener('resize', measure);
|
||
}, []);
|
||
|
||
const goPrev = () => setIndex((prev) => (prev - 1 + members.length) % members.length);
|
||
const goNext = () => setIndex((prev) => (prev + 1) % members.length);
|
||
|
||
const handleDragEnd = (_e, info) => {
|
||
const threshold = 80;
|
||
if (info.offset.x < -threshold) {
|
||
goNext();
|
||
} else if (info.offset.x > threshold) {
|
||
goPrev();
|
||
}
|
||
};
|
||
|
||
const openDetail = (member) => setSelected(member);
|
||
const closeDetail = () => setSelected(null);
|
||
|
||
return (
|
||
<div className="team-section-minimal">
|
||
<motion.div
|
||
className="team-content"
|
||
initial={{ opacity: 0, y: 60 }}
|
||
animate={isActive ? { opacity: 1, y: 0 } : {}}
|
||
transition={{ duration: 1.2, delay: 0.3 }}
|
||
>
|
||
<motion.h1
|
||
className="team-main-title"
|
||
initial={{ opacity: 0, y: 40 }}
|
||
animate={isActive ? { opacity: 1, y: 0 } : {}}
|
||
transition={{ duration: 0.8, delay: 0.6 }}
|
||
>
|
||
量迹AI
|
||
<br />
|
||
<span className="highlight-text">2025</span>
|
||
<br />
|
||
Team
|
||
</motion.h1>
|
||
|
||
<motion.div
|
||
className="team-quote"
|
||
initial={{ opacity: 0, x: -20 }}
|
||
animate={isActive ? { opacity: 1, x: 0 } : {}}
|
||
transition={{ duration: 0.8, delay: 1 }}
|
||
>
|
||
<div className="quote-line"></div>
|
||
<p>
|
||
我们的团队汇聚了行业顶尖人才,
|
||
<br />
|
||
拥有深厚的技术背景和丰富的
|
||
<br />
|
||
创新经验,致力于推动AI技术发展。
|
||
</p>
|
||
</motion.div>
|
||
</motion.div>
|
||
|
||
<motion.div
|
||
className="team-visual"
|
||
initial={{ opacity: 0, scale: 0.9 }}
|
||
animate={isActive ? { opacity: 1, scale: 1 } : {}}
|
||
transition={{ duration: 1.5, delay: 0.5 }}
|
||
>
|
||
<div className="team-visual-container">
|
||
<motion.div
|
||
className="team-visual-element-1"
|
||
animate={isActive ? {
|
||
rotateZ: [0, 180, 360],
|
||
scale: [1, 1.2, 1]
|
||
} : {}}
|
||
transition={{
|
||
duration: 18,
|
||
repeat: Infinity,
|
||
ease: "easeInOut"
|
||
}}
|
||
/>
|
||
<motion.div
|
||
className="team-visual-element-2"
|
||
animate={isActive ? {
|
||
rotateY: [0, 180, 360],
|
||
x: [0, 20, 0]
|
||
} : {}}
|
||
transition={{
|
||
duration: 12,
|
||
repeat: Infinity,
|
||
ease: "linear"
|
||
}}
|
||
/>
|
||
</div>
|
||
</motion.div>
|
||
|
||
{/* 科技感人员滑动卡片 */}
|
||
<motion.div
|
||
className="team-slider"
|
||
initial={{ opacity: 0, y: 30 }}
|
||
animate={isActive ? { opacity: 1, y: 0 } : {}}
|
||
transition={{ duration: 0.9, delay: 0.6 }}
|
||
>
|
||
<div className="team-slider-viewport">
|
||
<motion.div
|
||
ref={trackRef}
|
||
className="team-slider-track"
|
||
drag="x"
|
||
dragConstraints={{ left: 0, right: 0 }}
|
||
onDragEnd={handleDragEnd}
|
||
animate={{ x: -index * slideStep }}
|
||
transition={{ type: 'spring', stiffness: 120, damping: 18 }}
|
||
>
|
||
{members.map((m, i) => (
|
||
<div
|
||
key={m.id}
|
||
ref={i === 0 ? firstCardRef : undefined}
|
||
className="team-card"
|
||
onClick={() => openDetail(m)}
|
||
>
|
||
<div className="card-bg-grid" />
|
||
<div className="card-scanline" />
|
||
<div className="card-glow" />
|
||
<img src={m.avatar} alt={m.name} className="member-photo" />
|
||
<div className="member-info">
|
||
<div className="member-name">{m.name}</div>
|
||
<div className="member-role">{m.role}</div>
|
||
<div className="member-bio-mini">{m.bio}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</motion.div>
|
||
<button className="slider-btn prev" onClick={goPrev} aria-label="prev">
|
||
<span>←</span>
|
||
</button>
|
||
<button className="slider-btn next" onClick={goNext} aria-label="next">
|
||
<span>→</span>
|
||
</button>
|
||
<div className="slider-dots">
|
||
{members.map((m, i) => (
|
||
<button
|
||
key={m.id}
|
||
className={`dot ${i === index ? 'active' : ''}`}
|
||
onClick={() => setIndex(i)}
|
||
aria-label={`go-to-${m.id}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
|
||
{/* 详情面板(桌面端右侧 / 移动端底部弹层) */}
|
||
<AnimatePresence>
|
||
{selected && (
|
||
<>
|
||
<motion.div
|
||
className="team-detail-mask"
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 0.6 }}
|
||
exit={{ opacity: 0 }}
|
||
transition={{ duration: 0.2 }}
|
||
onClick={closeDetail}
|
||
/>
|
||
<motion.div
|
||
className="team-detail-panel"
|
||
initial={{ opacity: 0, x: 40 }}
|
||
animate={{ opacity: 1, x: 0 }}
|
||
exit={{ opacity: 0, x: 40 }}
|
||
transition={{ type: 'spring', stiffness: 180, damping: 20 }}
|
||
>
|
||
<button className="detail-close" onClick={closeDetail} aria-label="close">×</button>
|
||
<div className="detail-header">
|
||
<img src={selected.brandLogo} alt="brand" className="detail-brand" />
|
||
<div className="detail-meta">
|
||
<div className="detail-name">{selected.name}</div>
|
||
<div className="detail-role">{selected.role}</div>
|
||
</div>
|
||
</div>
|
||
<img src={selected.portrait || selected.avatar} alt={selected.name} className="detail-portrait" />
|
||
<div className="detail-body detail-scroller">
|
||
{selected.bio && (
|
||
<div className="detail-card">
|
||
<h5>简介</h5>
|
||
<p>{selected.bio}</p>
|
||
</div>
|
||
)}
|
||
{selected.education && selected.education.length > 0 && (
|
||
<div className="detail-card">
|
||
<h5>毕业院校</h5>
|
||
<ul className="detail-list">
|
||
{selected.education.map((e, i) => (
|
||
<li key={i}>{e}</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
{selected.experience && selected.experience.length > 0 && (
|
||
<div className="detail-card">
|
||
<h5>过往经历</h5>
|
||
<ul className="detail-list">
|
||
{selected.experience.map((e, i) => (
|
||
<li key={i}>{e}</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
{selected.skills && selected.skills.length > 0 && (
|
||
<div className="detail-card">
|
||
<h5>擅长技能</h5>
|
||
<div className="chip-grid">
|
||
{selected.skills.map((s, i) => (
|
||
<span className="chip" key={i}>{s}</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</motion.div>
|
||
</>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
{/* 进度指示器 */}
|
||
<motion.div
|
||
className="team-progress"
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={isActive ? { opacity: 1, y: 0 } : {}}
|
||
transition={{ duration: 0.8, delay: 1.5 }}
|
||
>
|
||
<div className="progress-bar">
|
||
<motion.div
|
||
className="progress-fill"
|
||
initial={{ width: "0%" }}
|
||
animate={isActive ? { width: "67%" } : {}}
|
||
transition={{ duration: 2, delay: 2 }}
|
||
></motion.div>
|
||
</div>
|
||
<div className="progress-info">
|
||
<span className="progress-time">00:12</span>
|
||
<span className="progress-total">00:18</span>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default TeamSection; |