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
+65
View File
@@ -0,0 +1,65 @@
/**
* 结果型数字口径机械防线(2026-09-19 critique P0):
* 「目标口径,非既成结果」必须来自 METRICS_BASIS_NOTE 单一真源;
* 且每个呈现结果型数字的营销页必须接入 MetricsBasisNote。
* 扫描范围:src/app + src/components 的 .ts/.tsx(排除测试文件与 _archive 归档)。
*/
import { readdirSync, readFileSync, statSync } from 'fs';
import { join } from 'path';
import { METRICS_BASIS_NOTE } from './metrics-basis';
const SOURCE_ROOTS = ['src/app', 'src/components'];
/** 呈现结果型数字、必须接入口径角注的页面/组件源文件。 */
const REQUIRED_ANNOTATED_SOURCES = [
'src/app/(marketing)/products/products-content-v3.tsx',
'src/app/(marketing)/solutions/solutions-content-v3.tsx',
'src/app/(marketing)/services/services-content-v3.tsx',
'src/app/(marketing)/home-content-v15.tsx',
];
function collectSourceFiles(dir: string, acc: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (entry === '_archive') continue;
const stat = statSync(full);
if (stat.isDirectory()) {
collectSourceFiles(full, acc);
} else if (/\.(ts|tsx)$/.test(entry) && !/\.test\.tsx?$/.test(entry)) {
acc.push(full);
}
}
return acc;
}
describe('结果型数字口径角注防线', () => {
it('METRICS_BASIS_NOTE 固定为「目标口径,非既成结果」', () => {
expect(METRICS_BASIS_NOTE).toBe('目标口径,非既成结果');
});
it('生产源码不得硬编码口径角注字面量(必须引用单一真源)', () => {
const violations: string[] = [];
for (const root of SOURCE_ROOTS) {
for (const file of collectSourceFiles(root)) {
if (file.endsWith('lib/constants/metrics-basis.ts')) continue;
const lines = readFileSync(file, 'utf-8').split('\n');
lines.forEach((line, i) => {
if (line.includes(METRICS_BASIS_NOTE)) {
violations.push(`${file}:${i + 1}`);
}
});
}
}
expect(violations).toEqual([]);
});
it('结果型数字页面均接入 MetricsBasisNote', () => {
for (const file of REQUIRED_ANNOTATED_SOURCES) {
const src = readFileSync(file, 'utf-8');
expect({ file, used: src.includes('<MetricsBasisNote') }).toEqual({
file,
used: true,
});
}
});
});