feat(ui): 营销站 UI/UE/UX 治理 P0→P3 收官

复评(impeccable critique)全部 Priority Issues 与 Minor Observations 落地:
- P0 数字口径:MetricsBasisNote 单一真源 + metrics-basis.test fs 防线,接入
  products/solutions/services/home/about;纯渲染端加角注,不动 DB/seed 值
- P1 转化断头路:敬请期待态 + 404 语境出口 + services 空态双出口
- P1 首页并卡:TrustSection 早鸟横幅并入 CasesSection 单张深色共创卡
- P2 CTA 治理:CONSULT_CTA_ALLOWLIST 白名单 + 逐字符扫描器,/contact CTA 全归一
- P3 polish:footer 补 服务/公司列、contact 死三元/空槽/toast、GlobalErrorTracker 幂等日志
- 文档同源:PRODUCT/CONTEXT 动效带统一 180–280ms;DESIGN.md 立 The 10px Floor;
  font-floor.test 守卫禁止再引入 <10px(保留 10/11px 有意 Bain 微标签)

Gates: type-check 0 · jest 1581/132 · lint 0e/126w · contrast 7/7 · headings 10/10
This commit is contained in:
2026-09-20 11:50:58 +08:00
parent dcf8ed6f20
commit f8917d4fda
66 changed files with 1448 additions and 1313 deletions
+23 -74
View File
@@ -10,7 +10,10 @@ import { ArrowRight, Lightbulb, Database, Layers, Code, Puzzle, Server, Quote, C
import { cn } from '@/lib/utils';
import { EASE_OUT } from '@/components/ui/page-decoration';
import { CTAButton } from '@/components/ui/cta-button';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { COMPANY_INFO, TRUST_SIGNALS, EARLY_ACCESS } from '@/lib/constants';
import { canonicalCtaLabel, CTA_LABELS } from '@/lib/constants/cta-labels';
import { canonicalTrustSource } from '@/lib/constants/company';
import { useSiteConfig } from '@/lib/site-config';
import { useReducedMotion } from '@/hooks/use-reduced-motion';
import { type NewsItem } from '@/lib/constants/news';
@@ -100,13 +103,6 @@ const FALLBACK_INSIGHTS = [
date: '2026-07',
href: '/services/software',
},
{
tag: '共创',
title: '首批客户共创计划:与早期客户共同打磨产品',
desc: '以真实运营数据验证业务改善,成果在获得客户授权后公开。',
date: '2026-06',
href: '/contact',
},
];
// ===== 创始人观点兜底(对标 Accenture CEO Quote,人格化背书)=====
@@ -171,7 +167,7 @@ function HeroSection({ heroData }: {
const heading = heroData?.heading || heroData?.headingBottom || '让每一家企业都拥有数据驱动的决策能力';
const subheading = heroData?.subheading || heroData?.description || '';
const ctaLabel = heroData?.ctaLabel || '预约咨询';
const ctaLabel = canonicalCtaLabel(heroData?.ctaLabel) || CTA_LABELS.consult;
const ctaHref = heroData?.ctaHref || '/contact';
const secondaryCtaLabel = heroData?.secondaryCtaLabel || '查看产品矩阵';
const secondaryCtaHref = heroData?.secondaryCtaHref || '/products';
@@ -300,28 +296,17 @@ function HeroStatsBand({ stats }: { stats?: Record<string, any>[] }) {
))}
</div>
</ScrollReveal>
<MetricsBasisNote className="mt-6" />
</div>
</section>
);
}
// ===== 信任/成果区:可验证信任信号 + 首批客户共创标签 =====
// 共创计划默认状态条目(如实呈现,无真实案例时不虚构客户)
const DEFAULT_EARLY_ACCESS_STATUS = [
{ label: '共创进行中', desc: '我们正与首批客户共同打磨产品与方案,以真实运营数据验证业务改善。' },
{ label: '产品内测中', desc: '自研产品(如睿视 NovaVis)处于内测阶段,成熟后正式发布。' },
{ label: '成果授权公开', desc: '案例与可量化成果在获得客户授权后公示——经得起衡量。' },
];
// ===== 信任/成果区:可验证信任信号 =====
function TrustSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
const eyebrow = pageCopy?.trustEyebrow || '值得信赖';
const titleLines = (pageCopy?.trustTitle || '以可衡量的成果\n赢得长期信任').split('\n');
const signals = (pageCopy?.trustSignals?.length ? pageCopy.trustSignals : TRUST_SIGNALS) as typeof TRUST_SIGNALS;
const earlyLabel = pageCopy?.earlyAccessLabel || EARLY_ACCESS.label;
const earlyDesc = pageCopy?.earlyAccessDesc || EARLY_ACCESS.desc;
const earlyTitle = (pageCopy?.earlyAccessTitle as string) || '首批客户共创计划';
const earlyCtaLabel = (pageCopy?.earlyAccessCtaLabel as string) || '成为共创客户';
const earlyStatus = (pageCopy?.earlyAccessStatus?.length ? pageCopy.earlyAccessStatus : DEFAULT_EARLY_ACCESS_STATUS) as typeof DEFAULT_EARLY_ACCESS_STATUS;
return (
<section className="relative py-20 sm:py-28 md:py-36 overflow-hidden bg-bg-secondary" data-testid="trust-section">
@@ -350,44 +335,12 @@ function TrustSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
</div>
<p className="text-sm text-text-secondary leading-relaxed mb-4 flex-1">{item.desc}</p>
<div className="text-xs text-text-muted border-t border-border-primary pt-3">
数据来源:{item.source}
数据来源:{canonicalTrustSource(item.source)}
</div>
</div>
</StaggerReveal>
))}
</div>
{/* 首批客户共创计划:如实呈现共创/内测/授权公开状态,不虚构客户 */}
<StaggerReveal staggerDelay={0.05}>
<div className="p-6 sm:p-8 bg-brand-bg border border-[rgba(196,30,58,0.15)]" data-testid="early-access-banner">
<div className="flex items-center gap-3 mb-4">
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
{earlyLabel}
</span>
</div>
<h3 className="text-xl sm:text-2xl font-bold text-ink mb-3">
{earlyTitle}
</h3>
<p className="text-sm text-text-secondary leading-relaxed mb-6 max-w-2xl">
{earlyDesc}
</p>
<div className="grid sm:grid-cols-3 gap-4 mb-8">
{earlyStatus.map((s) => (
<div key={s.label} className="p-4 bg-bg-primary border border-brand/10">
<div className="text-sm font-semibold text-brand-ink mb-1">{s.label}</div>
<div className="text-xs text-text-secondary leading-relaxed">{s.desc}</div>
</div>
))}
</div>
<StaticLink
href="/contact"
className="group inline-flex shrink-0 items-center min-h-6 gap-2 text-sm font-semibold text-ink hover:text-brand-ink transition-colors"
>
<span>{earlyCtaLabel}</span>
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
</StaticLink>
</div>
</StaggerReveal>
</div>
</section>
);
@@ -538,7 +491,7 @@ function InsightsSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
</p>
<div className="pt-6 border-t border-border-primary">
<span className="inline-flex items-center gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors">
阅读全文
了解详情
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
</span>
</div>
@@ -596,13 +549,16 @@ function ServicesSection({ services, pageCopy }: { services?: Record<string, any
))}
</div>
{(service as any).metrics && (
<div className="flex gap-6 mb-8 pb-8 border-b border-border-primary">
{(service as any).metrics.map((m: any, j: number) => (
<div key={j}>
<div className="text-2xl sm:text-3xl font-black text-ink leading-none mb-1">{m.value}</div>
<div className="text-xs text-text-muted font-medium">{m.label}</div>
</div>
))}
<div className="mb-8 pb-8 border-b border-border-primary">
<div className="flex gap-6 mb-2">
{(service as any).metrics.map((m: any, j: number) => (
<div key={j}>
<div className="text-2xl sm:text-3xl font-black text-ink leading-none mb-1">{m.value}</div>
<div className="text-xs text-text-muted font-medium">{m.label}</div>
</div>
))}
</div>
<MetricsBasisNote />
</div>
)}
<div className="mt-auto pt-2">
@@ -700,16 +656,9 @@ function CasesSection({ cases, pageCopy }: { cases?: Record<string, any>[]; page
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[1.05] mb-8">
<StyledTitle lines={casesTitleLines} />
</h2>
<p className="text-text-secondary leading-relaxed text-lg mb-8">
<p className="text-text-secondary leading-relaxed text-lg">
{casesDesc}
</p>
<StaticLink
href="/contact"
className="group inline-flex items-center min-h-6 gap-3 text-ink font-semibold text-sm"
>
成为首批共创客户
<ArrowRight className="w-4 h-5 transition-transform duration-300 group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
</StaticLink>
</ScrollReveal>
<ScrollReveal delay={0.15}>
<div className="relative overflow-hidden rounded-[2rem] bg-dark-bg p-10 sm:p-12 h-full flex flex-col shadow-card">
@@ -748,7 +697,7 @@ function CasesSection({ cases, pageCopy }: { cases?: Record<string, any>[]; page
</ol>
<div className="mt-auto">
<CTAButton href="/contact" dataTestId="cases-co-creation-cta">
成为首批共创客户
{CTA_LABELS.consult}
</CTAButton>
</div>
</div>
@@ -980,7 +929,7 @@ function NewsSection({ news, pageCopy }: { news?: NewsItem[]; pageCopy?: HomePag
function CTASection({ heroData, pageCopy }: { heroData?: Record<string, any> | null; pageCopy?: HomePageCopy | null }) {
const ctaTitleLine1 = heroData?.ctaTitleLine1 || pageCopy?.ctaTitleLine1 || '准备好开始了吗?';
const ctaTitleLine2 = heroData?.ctaTitleLine2 || pageCopy?.ctaTitleLine2 || '让数据真正转化为业务增长';
const ctaLabel = heroData?.ctaLabel || pageCopy?.ctaLabel || '预约咨询';
const ctaLabel = canonicalCtaLabel(heroData?.ctaLabel || (pageCopy?.ctaLabel as string)) || CTA_LABELS.consult;
const ctaHref = heroData?.ctaHref || '/contact';
return (
@@ -1009,7 +958,7 @@ export default function HomeContentV15({ services, cases, stats, heroData, news,
pageCopy?: HomePageCopy | null;
}) {
return (
<main>
<div>
{/* 1. Hero:浅色 editorial 画布 + 品牌红单电压(参考 Accenture Radical Relevance) */}
<HeroSection heroData={heroData} />
@@ -1039,6 +988,6 @@ export default function HomeContentV15({ services, cases, stats, heroData, news,
{/* 9. CTA:克制强调,品牌红仅用于按钮 */}
<CTASection heroData={heroData} pageCopy={pageCopy} />
</main>
</div>
);
}