feat(website): 三轮视觉改造与页面过渡动画

改造概要(30项):
- 第一轮:Hero重构/Section差异化/SocialProof强化/CTA对比度/About架构
- 第二轮:字体优化/背景交替/Solutions差异化/Footer五列/MegaDropdown修复
- 第三轮:卡片交互/表单层级/CTA统一/时间线标记/连接线/三列布局/移动导航/Button微交互/SEO Schema
- P3-2:template.tsx+Framer Motion页面过渡/loading.tsx加载状态
- 清理:删除未用组件/hooks,修复重复移动导航,清理冗余CSS
This commit is contained in:
张翔
2026-05-10 08:20:27 +08:00
parent 747405dc96
commit 37296b5717
133 changed files with 2583 additions and 13487 deletions
+140 -174
View File
@@ -1,31 +1,16 @@
'use client';
import { motion } from 'framer-motion';
import { useState, useEffect, useMemo } from 'react';
import { COMPANY_INFO, STATS } from '@/lib/constants';
import { FlipClock } from '@/components/ui/flip-clock';
import { useMemo } from 'react';
import { useReducedMotion } from '@/hooks/use-reduced-motion';
import { COMPANY_INFO } from '@/lib/constants';
import { PageNav } from '@/components/layout/page-nav';
import { Users, Target, Award, MapPin, Mail } from 'lucide-react';
import { differenceInYears, differenceInMonths, differenceInDays, subYears, subMonths } from 'date-fns';
import { BreadcrumbSchema } from '@/components/seo/structured-data';
import { Users, Target, Award } from 'lucide-react';
export function AboutClient() {
const [operationTime, setOperationTime] = useState({ days: 0, months: 0, years: 0 });
useEffect(() => {
const foundingDate = new Date('2026-01-15');
const calculateTime = () => {
const now = new Date();
const years = differenceInYears(now, foundingDate);
const afterYears = subYears(now, years);
const months = differenceInMonths(afterYears, foundingDate);
const afterMonths = subMonths(afterYears, months);
const days = differenceInDays(afterMonths, foundingDate);
setOperationTime({ days, months, years });
};
calculateTime();
const timer = setInterval(calculateTime, 60000);
return () => clearInterval(timer);
}, []);
const shouldReduceMotion = useReducedMotion();
const fadeUp = shouldReduceMotion ? {} : { initial: { opacity: 0, y: 20 } };
const values = useMemo(() => [
{ icon: Target, title: '务实', description: '不追逐风口,只做真正为客户创造价值的事。' },
@@ -34,193 +19,174 @@ export function AboutClient() {
], []);
const milestones = useMemo(() => [
{ date: '2026年1月', title: '公司成立', description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立' },
{ date: '2026年1月', title: '团队组建', description: '核心团队到位,成员来自多个大型传统IT企业,具备扎实的工程能力和规范化交付经验' },
{ date: '2026年2月', title: '业务启动', description: '推出企业数字化转型咨询与解决方案服务,开始接触首批意向客户' },
{ date: '2026年3月', title: '产品研发', description: '自主研发的ERP、CRM等产品启动研发,逐步构建产品矩阵' },
{ date: '2026年5月', title: '研发推进', description: '多款产品进入核心功能开发阶段,同步开展早期用户体验计划' },
{ date: '2026.01', title: '公司成立', description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立' },
{ date: '2026.01', title: '团队组建', description: '核心团队到位,成员来自多个大型传统IT企业,具备扎实的工程能力和规范化交付经验' },
{ date: '2026.02', title: '业务启动', description: '推出企业数字化转型咨询与解决方案服务,开始接触首批意向客户' },
{ date: '2026.03', title: '产品研发', description: '自主研发的ERP、CRM等产品启动研发,逐步构建产品矩阵' },
{ date: '2026.05', title: '研发推进', description: '多款产品进入核心功能开发阶段,同步开展早期用户体验计划' },
], []);
return (
<div className="min-h-screen bg-white">
<div className="min-h-screen bg-[var(--color-bg-primary)]">
<BreadcrumbSchema items={[{ name: '首页', href: '/' }, { name: '关于我们', href: '/about' }]} />
<section className="pt-32 pb-16">
<div className="container-wide">
<PageNav items={[{ label: '关于我们' }]} />
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
{...fadeUp}
animate={shouldReduceMotion ? {} : { opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="max-w-3xl"
>
<p className="text-sm font-medium text-[#C41E3A] mb-4 tracking-wide uppercase">About</p>
<h1 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6 tracking-tight">
<p className="text-sm font-medium text-[var(--color-brand-primary)] mb-4 tracking-wide uppercase">About</p>
<h1 className="text-4xl md:text-5xl font-bold text-[var(--color-text-primary)] mb-6 tracking-tight">
关于我们
</h1>
<p className="text-lg text-[#595959] leading-relaxed">
<p className="text-lg text-[var(--color-text-muted)] leading-relaxed">
以智慧连接数字趋势,以伙伴身份陪您成长——您的数字化转型同行者。
</p>
</motion.div>
</div>
</section>
<section className="pb-16">
<section className="pb-20">
<div className="container-wide">
<div className="max-w-4xl mx-auto space-y-8">
<div className="max-w-4xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
{...fadeUp}
whileInView={shouldReduceMotion ? {} : { opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="p-8 rounded-xl border border-[#E5E5E5]"
className="bg-[var(--color-bg-section)] rounded-2xl p-8 md:p-12"
>
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6">关于 {COMPANY_INFO.shortName}</h2>
<p className="text-xl font-semibold text-[#1C1C1C] mb-4">智连未来,成长伙伴</p>
<p className="text-[#595959] mb-6 leading-relaxed">企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。</p>
<h2 className="text-2xl font-bold text-[var(--color-text-primary)] mb-8">
关于 {COMPANY_INFO.shortName}
</h2>
<div className="mb-6">
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-3">智连未来</h3>
<p className="text-[#595959] mb-2 leading-relaxed">我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。</p>
<p className="text-[#595959] mb-2 leading-relaxed">每一次方案,都源于对您业务场景的洞察;</p>
<p className="text-[#595959] leading-relaxed">每一次连接,都为了让技术真正服务于您的未来。</p>
</div>
<div>
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-3">成长伙伴</h3>
<p className="text-[#595959] mb-2 leading-relaxed">我们不把&ldquo;项目完成&rdquo;当作终点。</p>
<p className="text-[#595959] mb-2 leading-relaxed">您的业务增长了吗?您的团队能力提升了吗?</p>
<p className="text-[#595959] mb-2 leading-relaxed">您下一次遇到难题时,还会第一个想到我们吗?</p>
<p className="text-[#595959] mt-3 leading-relaxed">这些问题,比&ldquo;项目是否按时完成&rdquo;更让我们在意。</p>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.1 }}
className="p-8 rounded-xl border border-[#E5E5E5]"
>
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6">品牌承诺</h2>
<p className="text-[#595959] mb-4 leading-relaxed">我们承诺:</p>
<ul className="space-y-2 mb-6">
<li className="flex items-start gap-3">
<span className="text-[#C41E3A] font-bold">✓</span>
<span className="text-[#595959]">不卖您用不上的技术</span>
</li>
<li className="flex items-start gap-3">
<span className="text-[#C41E3A] font-bold">✓</span>
<span className="text-[#595959]">不说不懂业务的术语</span>
</li>
<li className="flex items-start gap-3">
<span className="text-[#C41E3A] font-bold">✓</span>
<span className="text-[#595959]">不做路过就忘的&ldquo;一锤子买卖&rdquo;</span>
</li>
</ul>
<p className="text-[#1C1C1C] leading-relaxed font-medium">
我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。
</p>
</motion.div>
<FlipClock
years={operationTime.years}
months={operationTime.months}
days={operationTime.days}
/>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="grid grid-cols-2 md:grid-cols-4 gap-4"
>
{STATS.map((stat, idx) => (
<div key={idx} className="p-6 bg-[#F5F5F5] rounded-lg text-center">
<div className="text-3xl font-bold text-[#C41E3A] mb-1">{stat.value}</div>
<div className="text-sm text-[#595959]">{stat.label}</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12">
<div>
<h3 className="text-lg font-semibold text-[var(--color-brand-primary)] mb-3">智连未来</h3>
<p className="text-[var(--color-text-muted)] mb-2 leading-relaxed">我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。</p>
<p className="text-[var(--color-text-muted)] mb-2 leading-relaxed">每一次方案,都源于对您业务场景的洞察;</p>
<p className="text-[var(--color-text-muted)] leading-relaxed">每一次连接,都为了让技术真正服务于您的未来。</p>
</div>
<div>
<h3 className="text-lg font-semibold text-[var(--color-brand-primary)] mb-3">成长伙伴</h3>
<p className="text-[var(--color-text-muted)] mb-2 leading-relaxed">我们不把&ldquo;项目完成&rdquo;当作终点。</p>
<p className="text-[var(--color-text-muted)] mb-2 leading-relaxed">您的业务增长了吗?您的团队能力提升了吗?</p>
<p className="text-[var(--color-text-muted)] leading-relaxed">您下一次遇到难题时,还会第一个想到我们吗?</p>
</div>
</div>
<div className="mt-8 pt-8 border-t border-[var(--color-border-light)]">
<p className="text-[var(--color-text-muted)] mb-4 leading-relaxed">我们承诺:</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
{[
'不卖您用不上的技术',
'不说不懂业务的术语',
'不做路过就忘的"一锤子买卖"',
].map((promise) => (
<div key={promise} className="flex items-start gap-2">
<span className="text-[var(--color-brand-primary)] font-bold mt-0.5">✓</span>
<span className="text-[var(--color-text-muted)] text-sm">{promise}</span>
</div>
))}
</div>
<p className="text-[var(--color-text-primary)] leading-relaxed font-medium mt-4">
我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。
</p>
</div>
</motion.div>
</div>
</div>
</section>
<section className="py-20 bg-[var(--color-bg-section)]">
<div className="container-wide">
<div className="max-w-4xl mx-auto">
<motion.h2
{...fadeUp}
whileInView={shouldReduceMotion ? {} : { opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="text-2xl font-bold text-[var(--color-text-primary)] mb-10"
>
核心价值观
</motion.h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{values.map((value) => (
<motion.div
key={value.title}
{...fadeUp}
whileInView={shouldReduceMotion ? {} : { opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="flex items-start gap-4 p-6 rounded-xl bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] hover:border-[rgba(var(--color-brand-primary-rgb),0.3)] transition-colors"
>
<div className="w-10 h-10 rounded-lg bg-[var(--color-brand-primary-bg)] flex items-center justify-center shrink-0">
<value.icon className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div>
<h3 className="font-semibold text-[var(--color-text-primary)] mb-1">{value.title}</h3>
<p className="text-sm text-[var(--color-text-muted)]">{value.description}</p>
</div>
</motion.div>
))}
</motion.div>
</div>
</div>
</div>
</section>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
<section className="py-20">
<div className="container-wide">
<div className="max-w-4xl mx-auto">
<motion.h2
{...fadeUp}
whileInView={shouldReduceMotion ? {} : { opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="text-2xl font-bold text-[var(--color-text-primary)] mb-10"
>
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">核心价值观</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{values.map((value) => (
<div
key={value.title}
className="flex items-start gap-4 p-6 rounded-xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-colors"
>
<div className="w-10 h-10 rounded-lg bg-[#FEF2F4] flex items-center justify-center shrink-0">
<value.icon className="w-5 h-5 text-[#C41E3A]" />
</div>
<div>
<h3 className="font-semibold text-[#1C1C1C] mb-1">{value.title}</h3>
<p className="text-sm text-[#595959]">{value.description}</p>
</div>
</div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
>
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">发展历程</h2>
<div className="space-y-4">
{milestones.map((milestone) => (
<div
发展历程
</motion.h2>
<div className="relative">
<div className="absolute left-[4.5rem] top-0 bottom-0 w-px bg-[var(--color-border-primary)] hidden md:block" />
<div className="space-y-8">
{milestones.map((milestone, idx) => {
const isLatest = idx === milestones.length - 1;
return (
<motion.div
key={milestone.title}
className="flex flex-col md:flex-row md:items-start gap-3 p-5 rounded-lg border border-[#E5E5E5]"
{...fadeUp}
whileInView={shouldReduceMotion ? {} : { opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: idx * 0.1 }}
className="flex items-start gap-6"
>
<div className="md:w-28 shrink-0">
<span className="text-sm font-medium text-[#C41E3A]">{milestone.date}</span>
<div className="md:w-20 shrink-0 text-right hidden md:block">
<span className="text-sm font-mono font-medium text-[var(--color-brand-primary)]">{milestone.date}</span>
</div>
<div className="flex-1">
<h3 className="font-semibold text-[#1C1C1C] mb-1 text-sm">{milestone.title}</h3>
<p className="text-sm text-[#595959]">{milestone.description}</p>
<div className="relative shrink-0 hidden md:block">
<div className={`w-3 h-3 rounded-full border-2 border-white shadow-sm mt-1 ${isLatest ? 'bg-[var(--color-brand-primary)] animate-pulse' : 'bg-[var(--color-brand-primary)]'}`} />
</div>
</div>
))}
<div className="flex-1 pb-2">
<div className="md:hidden text-sm font-mono font-medium text-[var(--color-brand-primary)] mb-1">{milestone.date}</div>
<div className="flex items-center gap-2">
<h3 className="font-semibold text-[var(--color-text-primary)]">{milestone.title}</h3>
{isLatest && (
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-medium bg-[var(--color-brand-primary-bg)] text-[var(--color-brand-primary)] border border-[var(--color-brand-primary)]/10">
进行中
</span>
)}
</div>
<p className="text-sm text-[var(--color-text-muted)] leading-relaxed">{milestone.description}</p>
</div>
</motion.div>
);
})}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="p-8 rounded-xl border border-[#E5E5E5]"
>
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">联系我们</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-[#FEF2F4] rounded-lg flex items-center justify-center">
<MapPin className="w-5 h-5 text-[#C41E3A]" />
</div>
<div>
<p className="text-xs text-[#595959]">公司地址</p>
<p className="text-sm font-medium text-[#1C1C1C]">{COMPANY_INFO.address}</p>
</div>
</div>
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-[#FEF2F4] rounded-lg flex items-center justify-center">
<Mail className="w-5 h-5 text-[#C41E3A]" />
</div>
<div>
<p className="text-xs text-[#595959]">电子邮箱</p>
<p className="text-sm font-medium text-[#1C1C1C]">{COMPANY_INFO.email}</p>
</div>
</div>
</div>
</motion.div>
</div>
</div>
</div>
</section>
-11
View File
@@ -72,17 +72,6 @@ jest.mock('@/components/ui/card', () => {
return { Card, CardContent };
});
jest.mock('@/components/ui/page-header', () => {
const PageHeader = ({ title, description }: { title: string; description?: string }) => (
<header>
<h1>{title}</h1>
<p>{description}</p>
</header>
);
PageHeader.displayName = 'PageHeader';
return { PageHeader };
});
jest.mock('@/components/ui/flip-clock', () => {
const FlipClock = ({ years, months, days }: { years: number; months: number; days: number }) => (
<div data-testid="flip-clock">