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
+39
View File
@@ -0,0 +1,39 @@
import { describe, it, expect } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import { MethodologyFramework } from './sections';
describe('MethodologyFramework', () => {
const layers = [
{
title: '诊断',
description: '业务现状诊断',
items: ['访谈', '数据分析'],
},
{
title: '设计',
description: '解决方案设计',
items: ['架构设计'],
},
];
it('renders title and subtitle', () => {
render(<MethodologyFramework title="方法论" subtitle="分层推进" layers={layers} />);
expect(screen.getByText('方法论')).toBeInTheDocument();
expect(screen.getByText('分层推进')).toBeInTheDocument();
});
it('renders all layers and items', () => {
render(<MethodologyFramework title="方法论" layers={layers} />);
expect(screen.getByText('诊断')).toBeInTheDocument();
expect(screen.getByText('设计')).toBeInTheDocument();
expect(screen.getByText('业务现状诊断')).toBeInTheDocument();
expect(screen.getByText('数据分析')).toBeInTheDocument();
});
it('renders without subtitle', () => {
const { container } = render(<MethodologyFramework title="方法论" layers={layers} />);
expect(container.querySelector('h2')).toHaveTextContent('方法论');
});
});
+2
View File
@@ -327,6 +327,8 @@ export interface DataProofSectionProps {
}
export function DataProofSection({ title, subtitle, metrics }: DataProofSectionProps) {
if (!metrics || metrics.length === 0) return null;
return (
<section className="relative py-20 sm:py-28 md:py-32 lg:py-40 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
@@ -0,0 +1,55 @@
import { describe, it, expect } from '@jest/globals';
import { render, screen, fireEvent } from '@testing-library/react';
import { TestimonialSection, LogoWall } from './testimonials';
describe('TestimonialSection', () => {
const testimonials = [
{
quote: '服务非常专业',
author: '张三',
title: 'CEO',
company: 'A 公司',
industry: '科技',
},
{
quote: '交付准时',
author: '李四',
title: 'CTO',
company: 'B 公司',
industry: '制造',
},
];
it('renders title and subtitle', () => {
render(<TestimonialSection title="客户评价" subtitle="来自客户的真实反馈" testimonials={testimonials} />);
expect(screen.getByText('客户评价')).toBeInTheDocument();
expect(screen.getByText('来自客户的真实反馈')).toBeInTheDocument();
});
it('renders first testimonial by default', () => {
render(<TestimonialSection title="客户评价" testimonials={testimonials} />);
expect(screen.getByText('张三')).toBeInTheDocument();
expect(screen.getByText('科技')).toBeInTheDocument();
});
it('switches testimonial on dot click', () => {
render(<TestimonialSection title="客户评价" testimonials={testimonials} />);
const dots = screen.getAllByRole('button');
fireEvent.click(dots[1]!);
expect(screen.getByText('李四')).toBeInTheDocument();
expect(screen.getByText('制造')).toBeInTheDocument();
});
it('renders without subtitle', () => {
const { container } = render(<TestimonialSection title="客户评价" testimonials={testimonials} />);
expect(container.querySelector('h2')).toHaveTextContent('客户评价');
});
});
describe('LogoWall (testimonials)', () => {
it('renders logo wall', () => {
render(<LogoWall title="合作企业" logos={[{ name: 'A 公司' }, { name: 'B 公司', industry: '制造' }]} />);
expect(screen.getByText('合作企业')).toBeInTheDocument();
expect(screen.getByText('A 公司')).toBeInTheDocument();
});
});