Compare commits

..

9 Commits

Author SHA1 Message Date
jeremygan2021
f51d3b98a4 test
All checks were successful
构建并部署 / build-and-deploy (push) Successful in 2m33s
2026-02-13 23:31:39 +08:00
jeremygan2021
faa50e0dd5 test
All checks were successful
构建并部署 / build-and-deploy (push) Successful in 2m31s
2026-02-13 23:17:54 +08:00
jeremygan2021
4cd4fc8ebd test 2026-02-13 21:59:12 +08:00
jeremygan2021
da2ded71ad new
All checks were successful
构建并部署 / build-and-deploy (push) Successful in 1h13m18s
2026-02-13 21:55:48 +08:00
jeremygan2021
4cdcafa01c test 2026-02-13 21:48:12 +08:00
jeremygan2021
fd247e8c0a new
Some checks failed
构建并部署 / build-and-deploy (push) Has been cancelled
2026-02-13 21:32:50 +08:00
jeremygan2021
086943f0d5 new 2026-02-13 21:20:34 +08:00
jeremygan2021
d9cd3769e7 github 2026-02-13 21:16:01 +08:00
jeremygan2021
e6503ea9e4 github 2026-02-13 21:10:30 +08:00
8 changed files with 756 additions and 134 deletions

64
.github/workflows/deploy.yaml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: 构建并部署 (Docker Hub)
on:
push:
branches:
- main
- master
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 登录到 Docker Hub
uses: docker/login-action@v3
with:
username: jeremygan2021
password: 123quant-speed
- name: 构建并推送 Docker 镜像
uses: docker/build-push-action@v5
with:
context: .
push: true
# 使用 Docker Hub 用户名作为镜像前缀
tags: jeremygan2021/quant-speed-page:latest
- name: 部署到服务器
uses: appleboy/ssh-action@master
env:
DOCKERHUB_USERNAME: jeremygan2021
with:
host: 121.40.192.128
username: root
# 建议在 GitHub Secrets 配置 SSH_PASSWORD这里保留硬编码仅作演示
password: 123quant-speed
port: 22
# 将 Docker Hub 用户名传递给 SSH 脚本
envs: DOCKERHUB_USERNAME
script: |
# 停止并删除旧容器
docker stop quant-speed-page || true
docker rm quant-speed-page || true
# 拉取最新镜像 (从 Docker Hub)
docker pull $DOCKERHUB_USERNAME/quant-speed-page:latest
# 启动新容器
docker run -d \
--name quant-speed-page \
--restart unless-stopped \
--log-opt max-size=10m \
--log-opt max-file=3 \
-p 3000:80 \
$DOCKERHUB_USERNAME/quant-speed-page:latest
# 清理无用的旧镜像
docker image prune -f

View File

@@ -1002,7 +1002,7 @@ body, html {
grid-template-columns: 1fr 1fr;
align-items: center;
padding: 0 5rem;
gap: 4rem;
gap: 1rem;
}
.team-content {
@@ -1082,7 +1082,9 @@ body, html {
/* 团队滑动组件 - 科技风 */
.team-slider {
width: 100%;
margin-top: 2rem;
margin-top: 0;
grid-column: 1 / -1;
margin-bottom: 4rem;
}
.team-slider-viewport {
@@ -1096,6 +1098,7 @@ body, html {
.team-slider-track {
display: flex;
width: max-content;
gap: 24px;
will-change: transform;
}
@@ -1262,27 +1265,27 @@ body, html {
.team-detail-mask {
position: fixed;
inset: 0;
background: #000;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
z-index: 980;
}
/* //// / */
.team-detail-panel {
position: fixed;
right: 84px;
top: 10%;
transform: translateY(-50%);
width: min(420px, 40vw);
max-width: 92vw;
background: rgba(10, 15, 28, 0.9);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 16px;
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
right: 0;
top: 0;
height: 100vh;
transform: none;
width: 600px;
max-width: 90vw;
background: rgba(10, 15, 28, 0.95);
border-left: 1px solid rgba(255,255,255,0.1);
box-shadow: -10px 0 40px rgba(0,0,0,0.5);
z-index: 990;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
padding: 1.2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
padding: 2.5rem;
color: var(--text-primary);
max-height: calc(100vh - 96px);
display: flex;
flex-direction: column;
overflow: hidden;
@@ -1290,56 +1293,101 @@ body, html {
.detail-close {
position: absolute;
right: 10px;
top: 8px;
width: 32px;
height: 32px;
right: 20px;
top: 20px;
width: 36px;
height: 36px;
border-radius: 50%;
border: 1px solid rgba(255,255,255,0.2);
background: rgba(255,255,255,0.06);
color: var(--text-primary);
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
}
.detail-close:hover {
background: rgba(255, 255, 255, 0.15);
transform: rotate(90deg);
}
.detail-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 0.8rem;
gap: 16px;
margin-bottom: 2rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.detail-brand {
width: 28px;
height: 28px;
width: 48px;
height: 48px;
object-fit: contain;
opacity: 0.9;
opacity: 1;
background: rgba(255, 255, 255, 0.05);
padding: 8px;
border-radius: 12px;
filter: brightness(0) invert(1);
}
.detail-portrait {
width: 100%;
height: auto;
object-fit: contain;
background: transparent;
margin: 6px 0 10px 0;
}
.detail-photo {
width: 64px;
height: 64px;
object-fit: contain;
border-radius: 10px;
background: rgba(255,255,255,0.06);
.detail-meta {
display: flex;
flex-direction: column;
gap: 4px;
}
.detail-name {
font-size: 1.2rem;
font-size: 1.8rem;
font-weight: 700;
letter-spacing: 0.02em;
}
.detail-role {
color: var(--primary-color);
font-weight: 600;
margin-top: 2px;
font-size: 1rem;
opacity: 0.9;
}
/* 限制详情头像高度,避免占满面板导致无可滚动空间 */
.detail-portrait {
width: auto;
max-width: 100%;
max-height: 280px;
object-fit: contain;
background: transparent;
/* margin: 0 auto 2rem auto; Removed margin, handled by wrapper now */
display: block;
filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.3));
}
.detail-portrait-wrapper {
position: relative;
width: fit-content;
margin: 0 auto 2rem auto;
}
.detail-floating-role {
position: absolute;
bottom: 0;
right: 0;
background: rgba(10, 15, 28, 0.85);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 245, 212, 0.3);
color: var(--primary-color);
padding: 6px 14px;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 600;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
z-index: 5;
transform: rotate(2deg);
white-space: nowrap;
}
.detail-body p {
@@ -1372,23 +1420,60 @@ body, html {
}
.detail-card {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
padding: 0.9rem 1rem;
margin-bottom: 0.8rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
padding: 1.5rem;
margin-bottom: 1.5rem;
transition: all 0.3s ease;
}
.detail-card:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.detail-card h5 {
font-size: 0.95rem;
margin-bottom: 0.4rem;
color: var(--text-primary);
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.8rem;
color: var(--text-muted);
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.detail-card h5::before {
content: '';
display: block;
width: 4px;
height: 14px;
background: var(--primary-color);
border-radius: 2px;
}
.detail-list {
padding-left: 1rem;
padding-left: 0;
list-style: none;
}
.detail-list li {
color: var(--text-secondary);
margin-bottom: 0.25rem;
margin-bottom: 0.6rem;
padding-left: 1.2rem;
position: relative;
line-height: 1.6;
}
.detail-list li::before {
content: '•';
color: var(--primary-color);
position: absolute;
left: 0;
font-weight: bold;
}
.chip-grid {
display: flex;
@@ -1396,12 +1481,24 @@ body, html {
gap: 6px;
}
.chip {
padding: 4px 10px;
padding: 6px 14px;
border-radius: 999px;
background: rgba(0, 245, 212, 0.12);
background: rgba(0, 245, 212, 0.08);
color: var(--primary-color);
border: 1px solid rgba(0, 245, 212, 0.3);
border: 1px solid rgba(0, 245, 212, 0.2);
font-size: 0.85rem;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
backdrop-filter: blur(4px);
}
.chip:hover {
background: rgba(0, 245, 212, 0.15);
border-color: rgba(0, 245, 212, 0.4);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 245, 212, 0.15);
}
.team-section {
@@ -1927,10 +2024,53 @@ body, html {
/* 移动端页面指示器优化 */
@media (max-width: 768px) {
.section-indicators {
right: 1rem;
padding: 0.8rem 0.4rem;
gap: 0.8rem;
border-radius: 15px;
right: 1.5rem;
padding: 1.2rem 0.8rem; /* 放大容器 */
gap: 1.5rem; /* 增加间距 */
border-radius: 20px;
background: rgba(10, 15, 28, 0.8);
backdrop-filter: blur(16px);
}
.indicator {
width: 16px; /* 放大圆点 */
height: 16px;
}
/* 移动端显示标签 */
.indicator::after {
content: attr(title);
position: absolute;
right: 35px; /* 文字在圆点左侧 */
top: 50%;
transform: translateY(-50%);
background: rgba(10, 15, 28, 0.9);
color: rgba(255, 255, 255, 0.7);
padding: 4px 10px;
border-radius: 6px;
font-size: 12px;
white-space: nowrap;
opacity: 0; /* 默认隐藏,保持界面清爽 */
visibility: hidden;
transition: all 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.1);
pointer-events: none;
}
/* 激活项始终显示标签 */
.indicator.active::after {
opacity: 1;
visibility: visible;
color: var(--primary-color);
border-color: rgba(0, 245, 212, 0.3);
font-weight: 500;
}
/* 触摸/点击指示器区域时显示所有标签,方便用户预览跳转 */
.section-indicators:active .indicator::after,
.section-indicators:hover .indicator::after {
opacity: 1;
visibility: visible;
}
}
@@ -2485,12 +2625,13 @@ body, html {
/* 响应式:团队滑动卡片 */
.team-slider-viewport {
max-width: 92vw;
max-width: 100vw;
padding: 0.5rem 0;
overflow: visible; /* 允许超出视口以便看到前后卡片需配合父容器overflow控制 */
}
.team-card {
width: 84vw;
min-width: 84vw;
width: 80vw;
min-width: 80vw;
height: auto;
aspect-ratio: 3 / 4;
border-radius: 16px;
@@ -2510,14 +2651,19 @@ body, html {
transform: translateY(0);
bottom: 0;
top: auto;
height: auto;
width: 100vw;
max-width: 100vw;
border-radius: 18px 18px 0 0;
padding-bottom: calc(env(safe-area-inset-bottom, 0) + 16px);
max-height: 85vh;
max-height: 95vh;
overflow: hidden;
}
.team-detail-mask { opacity: 0.7; }
.detail-portrait {
margin: 0 auto 1rem auto;
}
}
/* 小屏幕设备额外优化 */
@@ -2575,9 +2721,15 @@ body, html {
/* 限制详情头像高度,避免占满面板导致无可滚动空间 */
.detail-portrait {
max-height: 36vh;
max-height: 25vh;
height: auto;
object-fit: contain;
/* margin handled by wrapper now */
margin: 0;
}
.detail-portrait-wrapper {
margin: 0 auto 1rem auto; /* Consistent margin with mobile adjustment */
}
.section-title .subtitle {
@@ -2653,3 +2805,129 @@ body, html {
.team-card { width: 88vw; min-width: 88vw; aspect-ratio: 3 / 4; }
.member-photo { height: 66%; object-position: 50% 14%; }
}
/* Bottom Right Controls */
.team-controls-bottom {
position: absolute;
bottom: 3rem;
right: 5rem;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 1.5rem;
z-index: 10;
}
.control-status {
display: flex;
align-items: center;
gap: 8px;
background: rgba(0, 245, 212, 0.05);
padding: 6px 12px;
border-radius: 20px;
border: 1px solid rgba(0, 245, 212, 0.15);
}
.status-dot {
width: 6px;
height: 6px;
background: var(--primary-color);
border-radius: 50%;
box-shadow: 0 0 8px var(--primary-color);
animation: pulse-dot 2s infinite;
}
@keyframes pulse-dot {
0% { opacity: 0.5; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
100% { opacity: 0.5; transform: scale(1); }
}
.status-text {
font-size: 0.75rem;
letter-spacing: 0.1em;
color: var(--primary-color);
font-weight: 600;
}
.control-group {
display: flex;
align-items: center;
gap: 2rem;
}
.control-numbers {
display: flex;
align-items: baseline;
gap: 4px;
font-family: var(--font-sans); /* Or a monospace font if available */
}
.current-idx {
font-size: 2.5rem;
font-weight: 300;
color: var(--text-primary);
line-height: 1;
}
.divider {
font-size: 1.5rem;
color: var(--text-secondary);
font-weight: 200;
margin: 0 4px;
}
.total-idx {
font-size: 1.5rem;
color: var(--text-secondary);
font-weight: 300;
}
.control-buttons {
display: flex;
gap: 12px;
}
.bottom-btn {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.bottom-btn:hover {
background: rgba(0, 245, 212, 0.1);
border-color: var(--primary-color);
color: var(--primary-color);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 245, 212, 0.2);
}
.bottom-btn svg {
width: 20px;
height: 20px;
}
/* Mobile adjustments */
@media (max-width: 768px) {
.team-controls-bottom {
bottom: 6rem; /* Above the detail panel handle area */
right: 1.5rem;
gap: 1rem;
}
.current-idx { font-size: 2rem; }
.total-idx { font-size: 1.2rem; }
.bottom-btn { width: 40px; height: 40px; }
/* Hide status on small screens if too crowded */
.control-status { display: none; }
}

View File

@@ -161,12 +161,12 @@ const App = () => {
touchEndY = e.changedTouches[0].clientY;
const deltaY = touchStartY - touchEndY;
const minSwipeDistance = 50; // 最小滑动距离
const minSwipeDistance = 30; // 降低滑动阈值,提高灵敏度 (原 50)
// 获取当前激活的 section 容器
const scroller = containerRef.current?.querySelector(`.section[data-index="${currentSectionRef.current}"]`);
const tolerance = 5;
const tolerance = 10; // 增加边界容差 (原 5)
const isScrollable = scroller ? scroller.scrollHeight > scroller.clientHeight + 1 : false;
const atTop = scroller ? scroller.scrollTop <= tolerance : true;
const atBottom = scroller ? Math.abs(scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop) <= tolerance : true;

View File

@@ -7,33 +7,43 @@ const CaseSection = ({ isActive }) => {
const cases = [
{
id: 1,
title: '智能金融风控系统',
client: '某大型银行',
category: '金融科技',
description: '基于机器学习的实时风险评估系统将欺诈检测准确率提升至99.8%',
results: ['风险识别提升85%', '处理速度提高300%', '年节省成本2000万'],
image: '🏦',
technologies: ['深度学习', '实时计算', '大数据分析']
title: '智慧茶桌·小盏',
client: '餐饮商户',
category: '餐饮场景',
description: '定制小盖3D立体IP茶宠硬件搭载声识WiFi语音交互与2寸显示屏实现语音问答点餐、多语言支持与菜品推荐。',
results: ['兼顾情绪价值与实用价值', '硬件成本低至200元/个', '实现“交互-解决-沉淀”闭环'],
image: '',
technologies: ['声识WiFi语音交互', '3D打印IP', '硬件电量管理']
},
{
id: 2,
title: '智能医疗诊断助手',
client: '顶级三甲医院',
category: '医疗健康',
description: 'AI辅助医生进行疾病诊断显著提高诊断效率和准确性',
results: ['诊断准确率提升20%', '诊疗时间缩短40%', '患者满意度95%'],
image: '🏥',
technologies: ['计算机视觉', '自然语言处理', '知识图谱']
title: '幂次科技小秘书',
client: '办公企业',
category: '办公场景',
description: '将智能体技术与办公OA平台深度结合通过语音控制办公系统实现智慧周报总结与任务自动跟进。',
results: ['显著提升办公效率', '语音对话发布任务', '下发任务自动跟进'],
image: '💼',
technologies: ['智能体OA结合', '语音控制', '智慧周报']
},
{
id: 3,
title: '智能制造优化平台',
client: '制造业龙头企业',
category: '工业4.0',
description: '通过AI优化生产流程实现智能化生产管理和质量控制',
results: ['生产效率提升45%', '次品率降低90%', 'ROI达到280%'],
image: '🏭',
technologies: ['预测性维护', 'IoT集成', '优化算法']
title: '万达双塔智慧引导',
client: '万达双塔',
category: '商业楼宇',
description: '打造AI小程序+智慧前台硬件组合方案,满足楼内商户信息检索、对话式模糊推荐与智慧楼层引导需求。',
results: ['适配客流引导需求', '对话式模糊推荐', '提升商户检索效率'],
image: '🏢',
technologies: ['AI小程序', '智慧前台硬件', '模糊推荐算法']
},
{
id: 4,
title: '红河州工商联“小红”',
client: '红河州工商联',
category: '政务场景',
description: '定制政务人偶硬件,搭载麦克风与显示屏,实现直接语音唤醒与服务器直连,简化政务查询操作。',
results: ['对老年人友好', '短询问链路响应快', '支持扫码通话/软件访问'],
image: '🏛',
technologies: ['语音唤醒', '服务器直连', 'AI大模型']
}
];
@@ -126,8 +136,9 @@ const CaseSection = ({ isActive }) => {
className="case-cta"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => window.location.href = 'https://market.quant-speed.com'}
>
查看详细案例
加入量迹商城
</motion.button>
</motion.div>
</AnimatePresence>

View File

@@ -6,19 +6,19 @@ const ContactSection = ({ isActive }) => {
{
icon: '📧',
title: '邮件联系',
content: 'contact@radiant-ai.com',
content: 'jeremygan2021@163.com',
description: '7x24小时响应您的咨询'
},
{
icon: '📞',
title: '电话咨询',
content: '+86 400-888-9999',
content: '18585164448',
description: '专业顾问为您答疑解惑'
},
{
icon: '📍',
title: '公司地址',
content: '北京市朝阳区',
content: '云南省昆明市西山区云纺国际商厦 B 座 1406 号',
description: '欢迎莅临参观交流'
}
];

View File

@@ -58,10 +58,25 @@ const HeroSection = ({ isActive }) => {
boxShadow: "0 10px 30px rgba(0, 245, 212, 0.3)"
}}
whileTap={{ scale: 0.95 }}
onClick={(e) => {
e.stopPropagation();
window.open('https://market.quant-speed.com', '_blank');
}}
>
<span className="btn-text">了解更多</span>
<div className="btn-glow"></div>
<div className="btn-arrow"></div>
<img
src="/logo.svg"
alt="logo"
style={{
height: '20px',
marginRight: '8px',
filter: 'brightness(0) invert(1)',
position: 'relative',
zIndex: 2
}}
/>
<span className="btn-text" style={{ position: 'relative', zIndex: 2 }}>量迹商城</span>
<div className="btn-glow" style={{ pointerEvents: 'none' }}></div>
<div className="btn-arrow" style={{ position: 'relative', zIndex: 2 }}></div>
</motion.button>
</motion.div>

View File

@@ -245,10 +245,31 @@ const ProductSection = ({ isActive }) => {
boxShadow: "0 10px 30px rgba(0, 245, 212, 0.3)"
}}
whileTap={{ scale: 0.95 }}
onClick={(e) => {
e.stopPropagation();
const urls = {
v3: 'https://market.quant-speed.com/product/2',
mini: 'https://market.quant-speed.com/product/1',
v2: 'https://market.quant-speed.com/product/3'
};
const targetUrl = urls[modelKey] || 'https://market.quant-speed.com';
window.open(targetUrl, '_blank');
}}
>
<span className="btn-text">了解更多</span>
<div className="btn-glow"></div>
<div className="btn-arrow"></div>
<img
src="/logo.svg"
alt="logo"
style={{
height: '20px',
marginRight: '8px',
filter: 'brightness(0) invert(1)',
position: 'relative',
zIndex: 2
}}
/>
<span className="btn-text" style={{ position: 'relative', zIndex: 2 }}>量迹商城</span>
<div className="btn-glow" style={{ pointerEvents: 'none' }}></div>
<div className="btn-arrow" style={{ position: 'relative', zIndex: 2 }}></div>
</motion.button>
</motion.div>

View File

@@ -1,53 +1,181 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import logoBai from '../asset/logo-bai.png';
const SkillIcon = ({ skill }) => {
const s = skill.toLowerCase();
// Code / Tech / Dev
if (s.includes('react') || s.includes('code') || s.includes('开发') || s.includes('embedded') || s.includes('嵌入式') || s.includes('javascript') || s.includes('技术') || s.includes('stack')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
</svg>
);
}
// Design / Art / UI
if (s.includes('photoshop') || s.includes('design') || s.includes('设计') || s.includes('art') || s.includes('ui') || s.includes('ux')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 19l7-7 3 3-7 7-3-3z"></path>
<path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"></path>
<path d="M2 2l7.586 7.586"></path>
</svg>
);
}
// AI / Brain / Data / Algo
if (s.includes('ai') || s.includes('智能') || s.includes('data') || s.includes('算法') || s.includes('memory') || s.includes('记忆') || s.includes('交互')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2a10 10 0 1 0 10 10H12V2z"></path>
<path d="M12 2a10 10 0 0 1 10 10H12V2z" opacity="0.5"></path>
<path d="M12 12L2.5 10.5M12 12l9.5-1.5M12 12V2.5"></path>
</svg>
);
}
// Strategy / Business / Growth / Marketing
if (s.includes('战略') || s.includes('growth') || s.includes('增长') || s.includes('market') || s.includes('市场') || s.includes('brand') || s.includes('品牌') || s.includes('manage') || s.includes('管理') || s.includes('营销') || s.includes('策划') || s.includes('经营')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M2 20h20"></path>
<path d="M5 20V8h4v12"></path>
<path d="M15 20V4h4v16"></path>
</svg>
);
}
// Hardware / Engineering / Structure
if (s.includes('hardware') || s.includes('硬件') || s.includes('structure') || s.includes('结构') || s.includes('reliability') || s.includes('可靠性') || s.includes('dft') || s.includes('dfm')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
<rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
<line x1="6" y1="6" x2="6.01" y2="6"></line>
<line x1="6" y1="18" x2="6.01" y2="18"></line>
</svg>
);
}
// Process / SOP / Solution
if (s.includes('sop') || s.includes('process') || s.includes('流程') || s.includes('scheme') || s.includes('方案')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="9 11 12 14 22 4"></polyline>
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
</svg>
);
}
// Default Star
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
</svg>
);
};
const RoleIcon = ({ role }) => {
const r = role.toLowerCase();
// CEO / Founder / COO / Management
if (r.includes('ceo') || r.includes('coo') || r.includes('创始人') || r.includes('总监') || r.includes('主管')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2l8 4-8 4-8-4 8-4z"/>
<path d="M2 14l8 4 8-4"/>
<path d="M2 10l8 4 8-4"/>
</svg>
);
}
// CTO / Tech / Engineer / Dev
if (r.includes('cto') || r.includes('工程师') || r.includes('技术') || r.includes('开发')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
</svg>
);
}
// Product / Manager
if (r.includes('产品') || r.includes('经理') || r.includes('manager')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
</svg>
);
}
// Design / Art
if (r.includes('设计') || r.includes('design') || r.includes('art')) {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<path d="M14.31 8l5.74 9.94M9.69 8h11.48M7.38 12l5.74-9.94M9.69 16L3.95 6.06M14.31 16H2.83M16.62 12l-5.74 9.94"></path>
</svg>
);
}
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="16" x2="12" y2="12"></line>
<line x1="12" y1="8" x2="12.01" y2="8"></line>
</svg>
);
};
const TeamSection = ({ isActive }) => {
const members = useMemo(() => ([
{
id: 'agan',
name: '阿甘',
role: '创始人',
role: 'CEO & 创始人',
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: ['本科(待补充)', '硕士(待补充)'],
brandLogo: logoBai,
education: ['SFC 西蒙菲莎数据科学系'],
experience: [
'负责公司整体战略与产品路线',
'推动跨部门协作与商业落地'
],
skills: ['战略规划', '产品设计', 'AI应用', '业融']
skills: ['战略规划', '产品设计', 'AI智能体', '业融']
},
{
id: 'azhi',
name: '汤炜志',
role: 'CTO & 技术总监',
bio: '深耕AI智能体控制论团队核心技术研发带头人。',
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/azhi.png',
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/azhi.png',
brandLogo: logoBai,
education: ['本科UBC', '硕士3D AI重建领域论文发表(3DV)'],
experience: [
'主导核心技术智能体控制技术的攻坚',
'掌握3D AI重建技术支持硬件IP形象建模'
],
skills: ['智能体控制', '3D AI重建', '记忆调取', '多主体交互']
},
{
id: 'liwei',
name: '立伟',
role: '市场运营总监',
role: 'COO & 市场运营总监',
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: ['本科(待补充)'],
brandLogo: logoBai,
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: '老许',
@@ -55,13 +183,54 @@ const TeamSection = ({ isActive }) => {
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: ['本科(待补充)'],
brandLogo: logoBai,
experience: [
'硬件结构件建模设计',
'前后端运维全栈工程师'
],
skills: ['3D打印', '3D结构件建模', '后端运维', '材料学']
},
{
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: logoBai,
experience: [
'主导多款硬件平台架构设计',
'建立DFM/DFT标准并提升良率'
'建立PCB/DFT标准并提升良率'
],
skills: ['硬件架构', '嵌入式', 'DFM/DFT', '可靠性']
skills: ['PCB设计', '嵌入式', 'C++', '固件开发']
},
{
id: 'jingwei',
name: '经纬',
role: '产品研发经理',
bio: '技术与市场的桥梁确保AI方案精准匹配线下场景需求。',
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/jingwei.png',
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/jingwei.png',
brandLogo: logoBai,
experience: [
'主导餐饮、办公、政务等场景的AI方案设计',
'3D空间智能资深产品经理将技术转化为可落地的商业产品'
],
skills: ['SOP流程化', '行业方案设计', '创新产品策划', '3D空间智能']
},
{
id: 'xiaoma',
name: '小马',
role: '全栈工程师',
bio: '全链路技术开发多面手支撑AI交互体验的流畅性与完整性。',
avatar: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/xiaoma.png',
portrait: 'https://tangledup-ai-staging.oss-cn-shanghai.aliyuncs.com/web/quant-speed_page/public/team_image/xiaoma.png',
brandLogo: logoBai,
experience: [
'负责前端页面设计打造AI小程序、硬件配套的交互界面',
'主导后端API服务开发保障软件与硬件的无缝对接'
],
skills: ['React', 'Photoshop', 'JavaScript', '全栈开发']
}
]), []);
@@ -70,29 +239,51 @@ const TeamSection = ({ isActive }) => {
const trackRef = useRef(null);
const firstCardRef = useRef(null);
const [slideStep, setSlideStep] = useState(0);
const [centerOffset, setCenterOffset] = 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);
if (!firstCardRef.current || !trackRef.current) return;
const cardRect = firstCardRef.current.getBoundingClientRect();
// 获取视口宽度(父容器宽度)
const viewportWidth = trackRef.current.parentElement.offsetWidth;
const step = Math.round(cardRect.width + CARD_GAP);
// 计算居中偏移量:(视口宽度 - 卡片宽度) / 2
const offset = (viewportWidth - cardRect.width) / 2;
setSlideStep(step);
setCenterOffset(offset);
};
// 初始测量
measure();
// 延时测量确保布局稳定
const timer = setTimeout(measure, 100);
window.addEventListener('resize', measure);
return () => window.removeEventListener('resize', measure);
return () => {
window.removeEventListener('resize', measure);
clearTimeout(timer);
};
}, []);
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) {
const offsetThreshold = 40; // 降低滑动阈值,更容易触发
const velocityThreshold = 100; // 速度阈值,快速轻扫也能切换
const { offset, velocity } = info;
// 向左滑动 (下一张)
if (offset.x < -offsetThreshold || velocity.x < -velocityThreshold) {
goNext();
} else if (info.offset.x > threshold) {
}
// 向右滑动 (上一张)
else if (offset.x > offsetThreshold || velocity.x > velocityThreshold) {
goPrev();
}
};
@@ -185,9 +376,10 @@ const TeamSection = ({ isActive }) => {
className="team-slider-track"
drag="x"
dragConstraints={{ left: 0, right: 0 }}
dragElastic={0.2}
onDragEnd={handleDragEnd}
animate={{ x: -index * slideStep }}
transition={{ type: 'spring', stiffness: 120, damping: 18 }}
animate={{ x: centerOffset - index * slideStep }}
transition={{ type: 'spring', stiffness: 150, damping: 20 }}
>
{members.map((m, i) => (
<div
@@ -254,7 +446,13 @@ const TeamSection = ({ isActive }) => {
<div className="detail-role">{selected.role}</div>
</div>
</div>
<img src={selected.portrait || selected.avatar} alt={selected.name} className="detail-portrait" />
<div className="detail-portrait-wrapper">
<img src={selected.portrait || selected.avatar} alt={selected.name} className="detail-portrait" />
<div className="detail-floating-role">
<RoleIcon role={selected.role} />
<span>{selected.role}</span>
</div>
</div>
<div className="detail-body detail-scroller">
{selected.bio && (
<div className="detail-card">
@@ -274,7 +472,7 @@ const TeamSection = ({ isActive }) => {
)}
{selected.experience && selected.experience.length > 0 && (
<div className="detail-card">
<h5>过往经历</h5>
<h5>职业经历</h5>
<ul className="detail-list">
{selected.experience.map((e, i) => (
<li key={i}>{e}</li>
@@ -287,7 +485,10 @@ const TeamSection = ({ isActive }) => {
<h5>擅长技能</h5>
<div className="chip-grid">
{selected.skills.map((s, i) => (
<span className="chip" key={i}>{s}</span>
<span className="chip" key={i}>
<SkillIcon skill={s} />
{s}
</span>
))}
</div>
</div>
@@ -318,6 +519,38 @@ const TeamSection = ({ isActive }) => {
<span className="progress-total">00:18</span>
</div>
</motion.div>
{/* 右下角控制区域 */}
<motion.div
className="team-controls-bottom"
initial={{ opacity: 0, x: 20 }}
animate={isActive ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.8, delay: 1.5 }}
>
<div className="control-status">
<span className="status-dot"></span>
<span className="status-text">SYSTEM ONLINE</span>
</div>
<div className="control-group">
<div className="control-numbers">
<span className="current-idx">{String(index + 1).padStart(2, '0')}</span>
<span className="divider">/</span>
<span className="total-idx">{String(members.length).padStart(2, '0')}</span>
</div>
<div className="control-buttons">
<button className="bottom-btn" onClick={goPrev}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
</button>
<button className="bottom-btn" onClick={goNext}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</button>
</div>
</div>
</motion.div>
</div>
);
};