This commit is contained in:
jeremygan2021
2025-10-13 14:59:36 +08:00
parent 98cca278b0
commit bcbec42185
4 changed files with 644 additions and 23 deletions

View File

@@ -1,7 +1,105 @@
import React from 'react';
import { motion } from 'framer-motion';
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: '/team_image/agan.png',
portrait: '/team_image/agan.png',
brandLogo: '/asset/logo-bai.png',
education: ['本科(待补充)', '硕士(待补充)'],
experience: [
'负责公司整体战略与产品路线',
'推动跨部门协作与商业落地'
],
skills: ['战略规划', '产品设计', 'AI应用', '工业融合']
},
{
id: 'liwei',
name: '立伟',
role: '市场运营总监',
bio: '增长策略与品牌叙事负责人,连接产品与客户价值。',
avatar: '/team_image/liwei.png',
portrait: '/team_image/liwei.png',
brandLogo: '/asset/logo-bai.png',
education: ['本科(待补充)'],
experience: [
'规划年度增长与渠道策略',
'打造品牌故事与市场活动'
],
skills: ['增长策略', '品牌建设', '整合营销', '渠道拓展']
},
{
id: 'shuangji',
name: '爽吉',
role: '硬件工程师 & 硬件部门主管',
bio: '负责高可靠硬件架构设计与产线可制造性优化。',
avatar: '/team_image/shuangji.png',
portrait: '/team_image/shuangji.png',
brandLogo: '/asset/logo-bai.png',
education: ['本科(待补充)'],
experience: [
'主导多款硬件平台架构设计',
'建立DFM/DFT标准并提升良率'
],
skills: ['硬件架构', '嵌入式', 'DFM/DFT', '可靠性']
},
{
id: 'laoxv',
name: '老许',
role: '工业设计工程师 & 结构件工程师',
bio: '负责高可靠硬件架构设计与产线可制造性优化。',
avatar: '/team_image/laoxv.png',
portrait: '/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
@@ -16,11 +114,11 @@ const TeamSection = ({ isActive }) => {
animate={isActive ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.6 }}
>
团队专业
量迹AI
<br />
<span className="highlight-text">知识</span>
<span className="highlight-text">2025</span>
<br />
与经验
Team
</motion.h1>
<motion.div
@@ -74,6 +172,132 @@ const TeamSection = ({ isActive }) => {
</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"