test
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user