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:
@@ -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,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user