chore: sync marketing pages, CMS extensions, tests and project docs

同步工作区剩余变更,主要包括:
- 营销页面组件与布局持续优化(about/news/services/solutions/team 等)
- 详情页四层叙事组件、布局组件、UI 组件调整
- CMS 数据模型、API 路由、权限、工作流、站内通知、媒体管理扩展
- 新增/补充单元测试与 E2E 测试(cms-workflow.spec.ts 等)
- ESLint 9 迁移、jest/tsconfig 配置更新、依赖调整
- 新增 ADR、CMS 评估文档、Release Review / Acceptance 报告
- 移除水墨装饰组件与大体积未使用字体文件
This commit is contained in:
张翔
2026-07-25 08:04:01 +08:00
parent e35090b914
commit 10404dbb36
208 changed files with 18107 additions and 8330 deletions
+90 -77
View File
@@ -7,8 +7,6 @@ import {
Target,
TrendingUp,
} from 'lucide-react';
import { TiltCard } from './micro-interactions';
import { SectionHeader, InkDivider } from './brand-elements';
import type { Solution } from '@/lib/constants/solutions';
interface SolutionValueSectionProps {
@@ -17,32 +15,45 @@ interface SolutionValueSectionProps {
export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
return (
<section className="relative bg-white py-24 lg:py-32 overflow-hidden">
<div className="container-wide relative">
<SectionHeader
title={
<>
<span className="text-brand">{solution.industry}</span>
</>
}
subtitle={solution.description}
/>
<section className="relative bg-white py-20 sm:py-28 md:py-36 overflow-hidden">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative">
<motion.div
initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-120px' }}
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
className="mb-16 sm:mb-20"
>
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-px bg-brand" />
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
</span>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95] max-w-3xl">
<span className="text-brand">{solution.industry}</span>
</h2>
<p className="text-lg text-text-secondary max-w-2xl mt-6 leading-relaxed">
{solution.description}
</p>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10 mt-16">
{/* 痛点 + 方案:不对称布局 */}
<div className="asymmetric-wide-narrow mb-16">
{/* 行业痛点 */}
<motion.div
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] as const }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }}
>
<div className="bg-brand-soft/30 rounded-2xl p-8 border border-brand/20">
<div className="bain-card p-8">
<h3 className="text-xl font-bold text-ink mb-6 flex items-center gap-3">
<span className="w-10 h-10 rounded-xl bg-brand flex items-center justify-center">
<Target className="w-5 h-5 text-white" />
<span className="w-10 h-10 flex items-center justify-center bg-brand text-white">
<Target className="w-5 h-5" />
</span>
</h3>
<ul className="space-y-4">
{solution.challenges.map((challenge, index) => (
<motion.li
@@ -53,7 +64,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
transition={{
delay: index * 0.08,
duration: 0.45,
ease: [0.22, 1, 0.36, 1] as const,
ease: [0.22, 1, 0.36, 1],
}}
className="flex items-start gap-3 text-text-secondary"
>
@@ -65,20 +76,20 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
</div>
</motion.div>
{/* 解决方案 */}
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, delay: 0.15, ease: [0.22, 1, 0.36, 1] as const }}
transition={{ duration: 0.7, delay: 0.15, ease: [0.22, 1, 0.36, 1] }}
>
<div className="bg-blue-50/50 rounded-2xl p-8 border border-blue-100/60">
<div className="bain-card p-8">
<h3 className="text-xl font-bold text-ink mb-6 flex items-center gap-3">
<span className="w-10 h-10 rounded-xl bg-blue-600 flex items-center justify-center">
<Lightbulb className="w-5 h-5 text-white" />
<span className="w-10 h-10 flex items-center justify-center bg-accent-blue text-white">
<Lightbulb className="w-5 h-5" />
</span>
</h3>
<ul className="space-y-4">
{solution.solutions.map((solutionItem, index) => (
<motion.li
@@ -89,11 +100,11 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
transition={{
delay: 0.25 + index * 0.08,
duration: 0.45,
ease: [0.22, 1, 0.36, 1] as const,
ease: [0.22, 1, 0.36, 1],
}}
className="flex items-start gap-3 text-text-secondary"
>
<CheckCircle2 className="w-4.5 h-4.5 text-blue-600 mt-0.5 shrink-0" />
<CheckCircle2 className="w-4 h-4 text-accent-blue mt-0.5 shrink-0" />
<span className="text-sm leading-relaxed">{solutionItem}</span>
</motion.li>
))}
@@ -102,40 +113,52 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
</motion.div>
</div>
{/* 价值主张 */}
{solution.valueProposition && (
<motion.div
initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.75, delay: 0.3 }}
className="mt-20"
>
<InkDivider variant="decorative" />
<div className="text-center mb-12">
<h3 className="text-2xl md:text-3xl font-bold tracking-tight text-ink">
<div className="mb-12">
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-px bg-brand" />
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
</span>
</div>
<h3 className="text-2xl sm:text-3xl md:text-4xl font-black text-ink tracking-tight leading-[0.95]">
{solution.valueProposition.headline}
</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-7">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{solution.valueProposition.points.map((point, index) => (
<TiltCard
<motion.div
key={index}
tiltAmount={6}
className="p-7 rounded-2xl bg-white border border-border-primary hover:border-blue-200 hover:shadow-md transition-shadow"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{
delay: index * 0.08,
duration: 0.5,
ease: [0.22, 1, 0.36, 1],
}}
className="bain-card p-7"
>
<div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center mb-5 shadow-md shadow-blue-500/20">
<TrendingUp className="w-7 h-7 text-white" />
<div className="w-12 h-12 flex items-center justify-center bg-accent-blue text-white mb-5">
<TrendingUp className="w-6 h-6" />
</div>
<h4 className="text-lg font-bold text-ink mb-2">{point.title}</h4>
<p className="text-sm text-text-secondary leading-relaxed">{point.description}</p>
</TiltCard>
</motion.div>
))}
</div>
</motion.div>
)}
{/* 产品组合 */}
{solution.suiteCombination && (
<motion.div
initial={{ opacity: 0, y: 32 }}
@@ -144,76 +167,66 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
transition={{ duration: 0.75, delay: 0.4 }}
className="mt-20"
>
<InkDivider variant="subtle" />
<div className="mb-12">
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-px bg-brand" />
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
</span>
</div>
<h3 className="text-2xl sm:text-3xl font-black text-ink tracking-tight leading-[0.95]">
</h3>
</div>
<div className="bg-bg-secondary rounded-2xl p-10 border border-border-primary">
<div className="bain-card p-8 sm:p-10">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10">
<div>
<h4 className="text-lg font-bold text-ink mb-5 flex items-center gap-2.5">
<span className="w-8 h-8 rounded-lg bg-brand flex items-center justify-center">
<span className="text-white text-xs font-bold"></span>
<h4 className="text-sm font-bold text-ink mb-5 flex items-center gap-2.5">
<span className="w-8 h-8 flex items-center justify-center bg-brand text-white text-xs font-bold">
</span>
</h4>
<div className="flex flex-wrap gap-3">
{solution.suiteCombination.primaryProducts.map((product, index) => (
<motion.span
{solution.suiteCombination.primaryProducts.map((product) => (
<span
key={product}
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{
delay: 0.5 + index * 0.06,
duration: 0.35,
}}
className="px-5 py-2.5 bg-brand-soft/50 text-brand text-sm font-semibold rounded-xl border border-brand/20"
className="px-4 py-2 bg-brand-soft/50 text-brand text-sm font-semibold border border-brand/20"
>
{product}
</motion.span>
</span>
))}
</div>
</div>
<div>
<h4 className="text-lg font-bold text-ink mb-5 flex items-center gap-2.5">
<span className="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center">
<span className="text-white text-xs font-bold"></span>
<h4 className="text-sm font-bold text-ink mb-5 flex items-center gap-2.5">
<span className="w-8 h-8 flex items-center justify-center bg-accent-blue text-white text-xs font-bold">
</span>
</h4>
<div className="flex flex-wrap gap-3">
{solution.suiteCombination.complementaryServices.map((service, index) => (
<motion.span
{solution.suiteCombination.complementaryServices.map((service) => (
<span
key={service}
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{
delay: 0.65 + index * 0.06,
duration: 0.35,
}}
className="px-5 py-2.5 bg-blue-50/80 text-blue-700 text-sm font-semibold rounded-xl border border-blue-200"
className="px-4 py-2 bg-blue-50 text-blue-700 text-sm font-semibold border border-blue-200"
>
{service}
</motion.span>
</span>
))}
</div>
</div>
</div>
<motion.p
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ delay: 0.85, duration: 0.55 }}
className="mt-8 p-5 bg-white rounded-xl text-sm text-text-secondary leading-relaxed border border-border-primary"
>
<p className="mt-8 p-5 bg-bg-secondary text-sm text-text-secondary leading-relaxed">
<strong className="text-ink"></strong>{solution.suiteCombination.rationale}
</motion.p>
</p>
</div>
</motion.div>
)}
</div>
</section>
);
}
}