feat(ui): CTAButton 可复用组件 + P5 全站 CTA 签名切换(17 页面 27 处)

feat(ui): 抽离 CTAButton 为 src/components/ui/cta-button.tsx
- 4 variant 覆盖全站 CTA 场景:
  * primary:品牌红填充 pill(hero 主行动 / 底部大 CTA)
  * secondary:描边墨色 pill(浅色 section 次行动)
  * dark:描边浅色 pill(深色区块 bg-dark-bg 内次行动,如 brand/team 底部 CTA)
  * inline:透明 + 小箭头(卡片内次行动)
- 品牌箭头签名化:内置 ArrowRight + hover translate-x 微交互
- 200-300ms transition,对齐项目动效四原则
- data-testid 透传避免破坏既有测试

style(cta): P5 全站 CTA 签名切换(17 页面 27 处)
- home V15 / news×3 / products×4(含 erp)/ cases / services×2 / solutions×2
- brand / methodology / team(hero 2 + L4 2 = 4 处,含 dark variant)
- error / not-found(保留 onClick 功能按钮与卡片式导航 StaticLink)
- 清理:StaticLink / Button / ArrowRight / ChevronRight unused import

test(marketing): services/solutions lucide-react mock 补 ArrowRight
- CTAButton 内部用 ArrowRight,原 mock 漏导导致渲染崩(4 测试失败已修)
- 对齐 home-content-v15.test.tsx 既有 mock 范式
This commit is contained in:
2026-08-31 17:25:08 +08:00
parent 011189a38c
commit 5c1b883abf
21 changed files with 215 additions and 268 deletions
@@ -2,9 +2,8 @@
import { motion } from 'framer-motion';
import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal';
import { StaticLink } from '@/components/ui/static-link';
import { Button } from '@/components/ui/button';
import { ArrowRight, Factory, ShoppingCart, Heart, GraduationCap, Lightbulb, Settings, Users, TrendingUp, Package, BookOpen, MessageSquare, Calendar, Smartphone, Route, Shield, Truck } from 'lucide-react';import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration';
import { CTAButton } from '@/components/ui/cta-button';
import { Factory, ShoppingCart, Heart, GraduationCap, Lightbulb, Settings, Users, TrendingUp, Package, BookOpen, MessageSquare, Calendar, Smartphone, Route, Shield, Truck } from 'lucide-react';import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration';
import type { Solution } from '@/lib/constants/solutions';
import type { Product } from '@/lib/constants/products';
@@ -83,15 +82,12 @@ function DetailHero({ solution }: DetailHeroProps) {
transition={{ duration: 0.9, delay: 0.45, ease: EASE_OUT }}
className="flex flex-wrap gap-6"
>
<Button size="xl" asChild>
<StaticLink href="/contact">
获取方案详情
<ArrowRight className="w-5 h-5 ml-2" />
</StaticLink>
</Button>
<Button size="xl" variant="outline" asChild>
<StaticLink href="/products">查看产品</StaticLink>
</Button>
<CTAButton href="/contact" dataTestId="solution-detail-hero-primary">
获取方案详情
</CTAButton>
<CTAButton href="/products" variant="secondary" dataTestId="solution-detail-hero-secondary">
查看产品
</CTAButton>
</motion.div>
</div>
</section>
@@ -491,15 +487,12 @@ function CTASection({ solution }: CTASectionProps) {
</p>
<div className="flex flex-wrap justify-center gap-6">
<Button size="xl" asChild>
<StaticLink href="/contact">
预约咨询
<ArrowRight className="w-5 h-5 ml-2" />
</StaticLink>
</Button>
<Button size="xl" variant="outline" asChild>
<StaticLink href="/solutions">查看更多方案</StaticLink>
</Button>
<CTAButton href="/contact" dataTestId="solution-detail-bottom-primary">
预约咨询
</CTAButton>
<CTAButton href="/solutions" variant="secondary" dataTestId="solution-detail-bottom-secondary">
查看更多方案
</CTAButton>
</div>
</ScrollReveal>
</div>