fix(marketing): resolve dogfood findings on cases, products, and contact

修复首页/案例/产品/联系页 dogfood 测试发现的 7 个问题:
- 案例页行业筛选按钮失效
- 产品矩阵页计数器显示 "0+"
- ERP 产品详情页核心功能区空白占位
- 联系表单无提交反馈
- 服务卡片图标被屏幕阅读器读取
- 产品卡片链接文本重复类别标签
- 联系页装饰图标缺少 aria-hidden

添加 dogfood-regression-output 报告与截图。
This commit is contained in:
张翔
2026-07-25 08:02:38 +08:00
parent d2a4e702da
commit f6f5766bb9
18 changed files with 218 additions and 40 deletions
@@ -243,6 +243,9 @@ export default function CasesContentV3({
{industries.map((industry) => (
<button
key={industry.id}
type="button"
role="radio"
aria-checked={selectedIndustry === industry.id}
onClick={() => setSelectedIndustry(industry.id)}
className={cn(
'px-4 py-2 text-xs font-medium transition-all duration-300',
@@ -281,7 +281,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
<h2 className="text-xl font-bold text-ink mb-8">{data.contactTitle || ''}</h2>
<div className="space-y-6" data-testid="contact-info">
<div className="flex items-start gap-4" data-testid="email-info">
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0">
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0" aria-hidden="true">
<Mail className="w-5 h-5 text-text-secondary" />
</div>
<div>
@@ -297,7 +297,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
</div>
<div className="flex items-start gap-4" data-testid="address-info">
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0">
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0" aria-hidden="true">
<MapPin className="w-5 h-5 text-text-secondary" />
</div>
<div>
@@ -313,7 +313,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
<div className="relative border border-border-primary bg-white p-8 overflow-hidden">
<div className="absolute top-0 left-0 bottom-0 w-0.5 bg-brand/60" />
<div className="flex items-center gap-3 mb-4">
<Clock className="w-5 h-5 text-brand" />
<Clock className="w-5 h-5 text-brand" aria-hidden="true" />
<h2 className="text-base font-bold text-ink">{data.workHoursTitle || ''}</h2>
</div>
<div className="flex justify-between" data-testid="work-hours-row">
@@ -325,7 +325,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
<div className="relative border border-border-primary bg-white p-8 overflow-hidden">
<div className="absolute top-0 left-0 bottom-0 w-0.5 bg-brand/60" />
<div className="flex items-center gap-3 mb-4">
<HeadphonesIcon className="w-5 h-5 text-text-secondary" />
<HeadphonesIcon className="w-5 h-5 text-text-secondary" aria-hidden="true" />
<h2 className="text-base font-bold text-ink">{data.commitmentTitle || ''}</h2>
</div>
<div className="space-y-4">
@@ -119,7 +119,7 @@ function FeaturesSection({ features }: FeaturesSectionProps) {
<div className="flex items-center gap-5">
<div className="w-14 h-px bg-brand" />
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
Core Features
</span>
<div className="w-14 h-px bg-brand" />
</div>
@@ -129,25 +129,25 @@ function FeaturesSection({ features }: FeaturesSectionProps) {
</h2>
</ScrollReveal>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-px bg-border-primary">
<StaggerReveal staggerDelay={0.08} delayChildren={0.05}>
{features.map((feature, idx) => (
<div
key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500"
>
<div className="absolute top-0 left-0 h-full w-1 scale-y-0 group-hover:scale-y-100 transition-transform duration-700 origin-top bg-brand" />
<div className="flex items-start gap-5">
<div className="w-10 h-10 rounded-xl bg-brand-soft flex items-center justify-center text-brand shrink-0 mt-0.5">
<Check className="w-5 h-5" />
</div>
<p className="text-lg text-ink font-medium leading-relaxed">{feature}</p>
<StaggerReveal
className="grid md:grid-cols-2 lg:grid-cols-3 gap-px bg-border-primary"
staggerDelay={0.08}
delayChildren={0.05}
>
{features.map((feature, idx) => (
<div
key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500"
>
<div className="flex items-start gap-5">
<div className="w-10 h-10 rounded-xl bg-brand-soft flex items-center justify-center text-brand shrink-0 mt-0.5">
<Check className="w-5 h-5" />
</div>
<p className="text-lg text-ink font-medium leading-relaxed">{feature}</p>
</div>
))}
</StaggerReveal>
</div>
</div>
))}
</StaggerReveal>
</div>
</section>
);
@@ -166,7 +166,7 @@ function BenefitsSection({ benefits }: BenefitsSectionProps) {
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
<div className="grid lg:grid-cols-2 gap-20 items-start">
<ScrollReveal>
<SectionLabel>Benefits</SectionLabel>
<SectionLabel></SectionLabel>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-ink tracking-tight leading-tight mb-8">
</h2>
@@ -182,7 +182,6 @@ function BenefitsSection({ benefits }: BenefitsSectionProps) {
key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500"
>
<div className="absolute left-0 top-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-500 origin-top" />
<div className="flex items-start gap-6">
<div className="w-12 h-12 rounded-2xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0">
<TrendingUp className="w-6 h-6" />
@@ -215,7 +214,7 @@ function ProcessSection({ process }: ProcessSectionProps) {
<div className="flex items-center gap-5">
<div className="w-14 h-px bg-brand" />
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
Implementation
</span>
<div className="w-14 h-px bg-brand" />
</div>
@@ -269,7 +268,7 @@ function CaseStudiesSection({ caseStudies }: CaseStudiesSectionProps) {
<div className="flex items-center gap-5">
<div className="w-14 h-px bg-brand" />
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
Case Studies
</span>
<div className="w-14 h-px bg-brand" />
</div>
@@ -338,7 +337,7 @@ function CertificationsSection({ certifications }: CertificationsSectionProps) {
<div className="flex items-center gap-5">
<div className="w-14 h-px bg-brand" />
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
Certifications
</span>
<div className="w-14 h-px bg-brand" />
</div>
@@ -387,7 +386,7 @@ function CTASection({ product }: CTASectionProps) {
<div className="flex items-center gap-5">
<div className="w-14 h-px bg-brand" />
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
Get Started
使
</span>
<div className="w-14 h-px bg-brand" />
</div>
@@ -444,15 +443,17 @@ export default function ProductDetailContentV3({ product }: ProductDetailContent
categories={product.techStack}
/>
)}
<DataProofSection
title="可衡量的成果"
subtitle="用数据说话,每一项指标都源自真实客户案例"
metrics={product.dataProofs.map((dp) => ({
value: dp.value,
label: dp.metric,
description: dp.description,
}))}
/>
{product.dataProofs && product.dataProofs.length > 0 && (
<DataProofSection
title="可衡量的成果"
subtitle="用数据说话,每一项指标都源自真实客户案例"
metrics={product.dataProofs.map((dp) => ({
value: dp.value,
label: dp.metric,
description: dp.description,
}))}
/>
)}
<CaseStudiesSection caseStudies={product.caseStudies} />
<CertificationsSection certifications={product.certifications} />
{product.faqs && product.faqs.length > 0 && (
@@ -21,7 +21,8 @@ const PRODUCT_ICONS: Record<string, React.ReactNode> = {
};
function useCountUp(target: number, start: boolean, duration: number = 2000) {
const [count, setCount] = useState(0);
// 初始值设为 target,避免动画触发前或用户偏好减少动效时显示 0
const [count, setCount] = useState(target);
const prefersReducedMotion = useReducedMotion();
useEffect(() => {
@@ -162,7 +163,7 @@ function ProductCard({ product }: { product: Product }) {
<div className="text-xs text-text-muted mb-1">{bundleLabel}</div>
<h3 className="text-lg font-bold text-text-primary">{product.title}</h3>
</div>
<span className="px-2 py-1 text-xs bg-brand/10 text-brand">{bundleLabel}</span>
<span className="px-2 py-1 text-xs bg-brand/10 text-brand" aria-hidden="true">{bundleLabel}</span>
</div>
<p className="text-sm text-text-secondary mb-4 leading-relaxed flex-1">
@@ -241,7 +242,7 @@ function BundleOverview({ products }: { products: Product[] }) {
<Cpu className="w-5 h-5" />
</div>
<h3 className="text-xl font-bold text-ink"></h3>
<span className="px-2 py-1 text-xs bg-accent-blue/10 text-accent-blue"></span>
<span className="px-2 py-1 text-xs bg-accent-blue-soft text-text-primary"></span>
</div>
<p className="text-sm text-text-secondary leading-relaxed mb-6">