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:
@@ -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">我们不把“项目完成”当作终点。</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">这些问题,比“项目是否按时完成”更让我们在意。</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]">不做路过就忘的“一锤子买卖”</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">我们不把“项目完成”当作终点。</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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user