fix(metrics): 数字口径 basis 结构强制,删除详情页虚构佐证

复评 P0 收尾:把「每个数字带口径」从个别页自觉升级为结构性强制。
- metrics-basis.ts:METRIC_BASES / METRIC_BASIS_NOTES + resolveMetricBasis
  (缺失·非法·大小写错一律回落最弱 target)+ weakestBasis(一组数字取最弱者聚合)
  + FORBIDDEN_PROOF_PHRASES;MetricsBasisNote 收 basis prop
- 类型层 Product.metrics / DataProof / ServiceMetric / Solution.outcomes /
  DataMetric / about keyMetrics 全部加 basis?;9 处调用点改为 basis 驱动
- content-types.ts 新增共享 metricBasisField(select)展开进 9 处字段定义;
  trustSignals 有意跳过(已有自己的 source)。seed 显式声明 55 条 target
- 删除字面虚构佐证:详情页「每一项指标都源自真实客户案例」、「经过实战验证」×3、
  「来自真实客户的使用数据」、「经实际场景验证」;给此前完全裸奔的 3 个共享
  数字渲染器(DataProofSection / detail-trust / detail-product-value)补上角注
- metrics-basis.test 3→16 例:basis 解析正控制、weakestBasis 次序、seed 全量
  显式声明 + 覆盖证明(防空跑假绿)+ 禁任何条目自称 verified + 禁词全源码扫描
- 文档同源:DESIGN.md 立 The Declared-Basis Rule;PRODUCT.md 常驻禁令;
  CONTEXT.md 记决策并警示 admin 字段需重跑 seed 才激活

边界:CMS 字段定义存 ContentModel.fields(DB 内 JSON),basis 下拉须重跑 seed
才会出现在 admin 表单;本次 DB 与 seed 数据值零改动,渲染端对缺 basis 兜底 target。

Gates: type-check 0 · jest 1594/132 · contrast 7/7 · headings 10/10
lint 0e/128w(与 stash 基线逐行比对,零新增告警)
This commit is contained in:
2026-09-20 11:50:58 +08:00
parent 84e01eb8e8
commit 4dcc15ebd0
27 changed files with 332 additions and 97 deletions
+1
View File
@@ -167,6 +167,7 @@ Novalon 的 6 个核心产品,互为互补关系,常以组合形式出现在
| 方案页删除服务方式区域 | Solutions 列表页"服务方式"区域职责越界(属 Services 层),应删除;替换为方案→服务的轻量关联推荐链接 | ✅ 2026-06-07 确认 |
| L3 信任层内容策略 | 初创阶段无真实案例/数据/资质,L3 暂不渲染;组件结构预留,数据就绪后启用。三档策略:有案例→正常展示;有意向→"正在服务中"标签;无数据→不显示 L3。**阶段 0(2026-08-20)已落地**:信任策略从「结果证据」转向「可验证的过程+能力+治理证据」——首页「共创计划」板块(共创进行中/产品内测中/成果授权公开三档如实状态 + 招募 CTA)、关于页资质区如实「建设中」空态 | ✅ 2026-06-07 确认 / 阶段 0 完成 2026-08-20 |
| 首页共创计划板块 | 成立 <1 年无真实案例时,以主动板块替代被动标签:`page-copy` 字段 `earlyAccessTitle`/`earlyAccessCtaLabel`/`earlyAccessStatus` 驱动首页「首批客户共创计划」板块,如实呈现共创/内测/授权公开状态并引导转化(成为共创客户 → /contact)。零编造:不虚构客户/数据/资质 | ✅ 2026-08-20 确认 |
| 数字口径 basis 结构强制 | 「每个数字带口径」从个别页自觉升级为结构约束:走共享数字样式渲染的指标(`metrics`/`outcomes`/`dataProofs`)必须声明 `basis: target\|team-history\|verified`,渲染端按 basis 自动附角注,缺失/非法保守回落到最弱的 `target`(「目标口径,非既成结果」)。CMS 侧 `basis` 为 select 字段,真实出路由业务在 admin 录入;`metrics-basis.test.ts` 机械校验 seed 全量条目显式声明、禁止条目无据自称 `verified`,并拦截「源自真实客户案例/经过实战验证」式虚构佐证文案。**注意**:字段定义改动需重跑 seed 同步 `ContentModel.fields` 后 admin 才出现该输入项;渲染端已对未同步数据兜底为 `target` | ✅ 2026-09-20 确认(critique P0 收尾) |
| 组件版本统一 | 独立产品迁移后删除 `detail/`V1),将 `detail-v2/` 重命名为 `detail/`,组件文件名去掉版本后缀(如 `DetailHeroV3``DetailHero` | ✅ 2026-06-07 确认(待 Phase 0 执行) |
| 设计定位 | 纯咨询专业风(Accenture + Bain + Porsche 三位一体),水墨元素已移除 | ✅ 2026-07-10 更新(ADR-0006 |
| 设计 DNA 框架 | 三维度模型:Design System + Design Style + Visual Effects,基于 Accenture+Bain+Porsche 整合 | ✅ 2026-06-28 确认 |
+1
View File
@@ -174,6 +174,7 @@ Novalon 的界面是一份可以逐条审计的经营宣言。它模仿顶级咨
**The Answer-First Rule.** 标题写结论断言,不写问句或口号铺垫;紧随其后的数字条是证据。
**The -0.04em Floor.** 追踪最紧 -0.04em(现用最紧 -0.03em);display 不超过 6rem。
**The 10px Floor.** 1011px 大写 tracking 微标签(eyebrow、digest、case 分类签)是 Bain 账本风的有意下限,全站保留、不上坡;绝对像素字号不得 <10px,由 `src/lib/constants/font-floor.test.ts` 机械守卫禁止再引入(rem 因 18px 根字号歧义不纳入)。
**The Declared-Basis Rule.** 凡以大数字样式呈现的指标必须声明口径 `basis: target | team-history | verified`(单一真源 `src/lib/constants/metrics-basis.ts`),渲染端据此自动附角注;缺失或非法一律按最弱的 `target`(「目标口径,非既成结果」)处理,宁可多标注也不裸奔。聚合时取一组数字里最弱的那个。禁止任何「数字源自真实客户案例 / 经过实战验证」式佐证文案——本站 2026-01 成立、零公开客户案例,该措辞由 `metrics-basis.test.ts` 全源码扫描拦截。
## Layout
+1
View File
@@ -34,3 +34,4 @@
- 字距下限 -0.04em,不得更紧
- 暗黑模式由 `html[data-theme='dark']` 覆盖 CSS 变量驱动,组件不得写死颜色字面量
- 楷体(`--font-brand`)为遗留例外,仅存量页面保留,新页面禁用
- 大数字必须有口径:指标声明 `basis: target|team-history|verified`,未声明按「目标口径,非既成结果」自动标注;禁止宣称数字源自真实客户案例或实战验证(公司 2026-01 成立、零公开客户案例)
+2 -1
View File
@@ -36,7 +36,8 @@
- [x] **结构性文案全站 CMS 化(2026-08-19**:新增 `page-copy` 内容模型承载全部营销页面章节标题/眉标/描述/CTA/空状态(首页 + 服务/方案/产品/案例/新闻列表页),「CMS 优先 + 硬编码兜底」不白屏;seed 写入 6 条,本地 db:seed 已生效
- [x] **信任证据补齐·阶段 0(成立 <1 年策略,2026-08-20**:信任策略从「结果证据」转向「可验证的过程 + 能力 + 治理证据」——首页「首批客户共创计划」板块(共创进行中/产品内测中/成果授权公开三档如实状态 + 招募 CTA)+ 关于页资质「建设中」如实空态,零编造
- [x] **生产部署上线(2026-08-20)**`deploy.sh deploy`(本地 dist → 远端容器重建)+ 生产库 seedpage-copy/methodology 数据)完成;生产目录 Drizzle 残留源码归档清理;站点全路由 200、生产库 Prisma 结构
- [ ] **信任证据补齐·阶段 1/2(待内测/试点客户素材)**:如实披露「共创中/内测中/已交付」三档 + 经授权客户(脱敏);有交付成果后经授权公示可量化结果(零编造原则)
- [x] **数字口径结构强制(2026-09-20critique P0 收尾)**:共享数字样式渲染的指标(`metrics`/`outcomes`/`dataProofs`)统一携带 `basis: target|team-history|verified`,渲染端自动附口径角注、未声明保守回落 `target`CMS 新增 `basis` select 录入字段;删除产品详情页「每一项指标都源自真实客户案例」「经实际场景验证」等虚构佐证并补注角注;`metrics-basis.test.ts` 机械校验声明完整性并拦截虚构佐证禁词
- [ ] **信任证据补齐·阶段 1/2(待内测/试点客户素材)**:如实披露「共创中/内测中/已交付」三档 + 经授权客户(脱敏);有交付成果后经授权公示可量化结果(零编造原则)——口径字段与 admin 录入口已就绪,待真实数据把 `basis``target` 升级为 `verified`
## 技术栈
+36 -36
View File
@@ -14,17 +14,17 @@ export const PRODUCTS: Product[] = [
heroThemeId: 'erp',
scenario: '面向中大型制造与零售企业的核心运营系统',
metrics: [
{ value: '10 万级', label: 'SKU 支撑' },
{ value: '200+', label: '标准报表模板' },
{ value: '99.5%', label: '数据准确率' },
{ value: '10 万级', label: 'SKU 支撑', basis: 'target' },
{ value: '200+', label: '标准报表模板', basis: 'target' },
{ value: '99.5%', label: '数据准确率', basis: 'target' },
],
bundle: 'enterprise',
capabilities: ['财务核算', '采购管理', '生产计划', '库存控制'],
caseStudies: [],
dataProofs: [
{ metric: '运营效率提升', value: '40%+', description: '流程自动化减少人工操作' },
{ metric: '数据准确性', value: '99.2%', description: '实时数据同步消除信息孤岛' },
{ metric: '部署周期', value: '3-6个月', description: '从需求调研到正式上线' },
{ metric: '运营效率提升', value: '40%+', description: '流程自动化减少人工操作', basis: 'target' },
{ metric: '数据准确性', value: '99.2%', description: '实时数据同步消除信息孤岛', basis: 'target' },
{ metric: '部署周期', value: '3-6个月', description: '从需求调研到正式上线', basis: 'target' },
],
certifications: [],
overview: '睿新ERP管理系统是面向企业打造的一体化资源管理平台,打通财务、采购、销售、库存、生产等核心模块,让各部门数据实时流转、业务流程自动协同,帮助企业告别信息孤岛。',
@@ -71,17 +71,17 @@ export const PRODUCTS: Product[] = [
heroThemeId: 'crm',
scenario: '面向销售驱动型企业的一体化客户运营管理平台',
metrics: [
{ value: '360°', label: '客户画像' },
{ value: '3x', label: '转化率提升' },
{ value: '<2h', label: '线索响应' },
{ value: '360°', label: '客户画像', basis: 'target' },
{ value: '3x', label: '转化率提升', basis: 'target' },
{ value: '<2h', label: '线索响应', basis: 'target' },
],
bundle: 'enterprise',
capabilities: ['线索管理', '商机跟进', '合同审批', '售后工单'],
caseStudies: [],
dataProofs: [
{ metric: '销售转化率', value: '+113%', description: '从15%提升至32%' },
{ metric: '客户数据整合', value: '100%', description: '多渠道数据统一管理' },
{ metric: '响应速度', value: '<2小时', description: '线索到首次联系平均时间' },
{ metric: '销售转化率', value: '+113%', description: '从15%提升至32%', basis: 'target' },
{ metric: '客户数据整合', value: '100%', description: '多渠道数据统一管理', basis: 'target' },
{ metric: '响应速度', value: '<2小时', description: '线索到首次联系平均时间', basis: 'target' },
],
certifications: [],
overview: '睿新客户关系管理系统是面向企业销售团队的全渠道客户管理平台,覆盖从线索到成交的全流程可视化管理,让销售团队清楚每一步该做什么、每个机会卡在哪里。系统支持多渠道客户数据整合,提供全面的客户画像和销售漏斗分析。',
@@ -128,17 +128,17 @@ export const PRODUCTS: Product[] = [
heroThemeId: 'cms',
scenario: '面向多品牌、多站点企业的统一内容管理平台',
metrics: [
{ value: '50+', label: '站点统管' },
{ value: '60%', label: '发布提效' },
{ value: '100%', label: '私有化部署' },
{ value: '50+', label: '站点统管', basis: 'target' },
{ value: '60%', label: '发布提效', basis: 'target' },
{ value: '100%', label: '私有化部署', basis: 'target' },
],
bundle: 'enterprise',
capabilities: ['多站点管理', '工作流审批', '版本控制', 'SEO 优化'],
caseStudies: [],
dataProofs: [
{ metric: '发布效率提升', value: '60%', description: '多站点统一管理' },
{ metric: '站点支持数', value: '50+', description: '单系统支持多站点' },
{ metric: '部署方式', value: '私有化', description: '数据自主可控' },
{ metric: '发布效率提升', value: '60%', description: '多站点统一管理', basis: 'target' },
{ metric: '站点支持数', value: '50+', description: '单系统支持多站点', basis: 'target' },
{ metric: '部署方式', value: '私有化', description: '数据自主可控', basis: 'target' },
],
certifications: [],
overview: '睿新内容管理系统是企业级内容管理平台,提供所见即所得的编辑体验和多站点统一管理能力,让内容发布像写文档一样简单。系统提供强大的内容编辑功能和灵活的权限管理机制。',
@@ -185,17 +185,17 @@ export const PRODUCTS: Product[] = [
heroThemeId: 'bi',
scenario: '面向业务人员的自助式数据洞察与决策支持平台',
metrics: [
{ value: '70%+', label: '自助分析' },
{ value: '<1天', label: '决策响应' },
{ value: '85%', label: '报表减负' },
{ value: '70%+', label: '自助分析', basis: 'target' },
{ value: '<1天', label: '决策响应', basis: 'target' },
{ value: '85%', label: '报表减负', basis: 'target' },
],
bundle: 'enterprise',
capabilities: ['拖拽分析', '实时看板', '智能预警', '移动报表'],
caseStudies: [],
dataProofs: [
{ metric: '数据整合效率', value: '85%', description: '减少手工报表工作量' },
{ metric: '决策响应时间', value: '<1天', description: '从周级缩短至实时' },
{ metric: '自助分析占比', value: '70%+', description: '业务人员独立完成分析' },
{ metric: '数据整合效率', value: '85%', description: '减少手工报表工作量', basis: 'target' },
{ metric: '决策响应时间', value: '<1天', description: '从周级缩短至实时', basis: 'target' },
{ metric: '自助分析占比', value: '70%+', description: '业务人员独立完成分析', basis: 'target' },
],
certifications: [],
overview: '睿新商业智能平台是数据可视化分析平台,提供拖拽式自助分析能力,让业务人员无需写SQL也能从数据中找到答案。平台支持自助式数据分析和实时数据监控。',
@@ -242,17 +242,17 @@ export const PRODUCTS: Product[] = [
heroThemeId: 'sds',
scenario: '面向零售与制造企业的智能销量预测与库存优化系统',
metrics: [
{ value: '73%', label: '缺货率降低' },
{ value: '38%', label: '周转提升' },
{ value: '<6个月', label: 'ROI 周期' },
{ value: '73%', label: '缺货率降低', basis: 'target' },
{ value: '38%', label: '周转提升', basis: 'target' },
{ value: '<6个月', label: 'ROI 周期', basis: 'target' },
],
bundle: 'enterprise',
capabilities: ['销量预测', '智能补货', '库存诊断', '供应商协同'],
caseStudies: [],
dataProofs: [
{ metric: '缺货率降低', value: '73%', description: '从12%降至3.2%' },
{ metric: '库存周转提升', value: '38%', description: '周转天数从45天降至28天' },
{ metric: 'ROI回报周期', value: '<6个月', description: '投入产出清晰可算' },
{ metric: '缺货率降低', value: '73%', description: '从12%降至3.2%', basis: 'target' },
{ metric: '库存周转提升', value: '38%', description: '周转天数从45天降至28天', basis: 'target' },
{ metric: 'ROI回报周期', value: '<6个月', description: '投入产出清晰可算', basis: 'target' },
],
certifications: [],
overview: '睿新供应链决策支持系统是智能供应链管理平台,自动预测销量、智能补货建议,帮您把库存成本降下来、把缺货损失挽回来。系统采用智能算法,实现从经验驱动到数据驱动的供应链管理升级。',
@@ -299,17 +299,17 @@ export const PRODUCTS: Product[] = [
heroThemeId: 'oa',
scenario: '面向中大型组织的一站式流程审批与协同办公平台',
metrics: [
{ value: '95%', label: '审批提效' },
{ value: '100%', label: '移动覆盖' },
{ value: '10万+', label: '日流程承载' },
{ value: '95%', label: '审批提效', basis: 'target' },
{ value: '100%', label: '移动覆盖', basis: 'target' },
{ value: '10万+', label: '日流程承载', basis: 'target' },
],
bundle: 'enterprise',
capabilities: ['流程审批', '公文管理', '知识中心', '移动办公'],
caseStudies: [],
dataProofs: [
{ metric: '审批效率提升', value: '95%', description: '从4天缩短至4小时' },
{ metric: '移动办公覆盖率', value: '100%', description: 'PC+移动端全覆盖' },
{ metric: '流程电子化率', value: '100%', description: '告别纸质流转' },
{ metric: '审批效率提升', value: '95%', description: '从4天缩短至4小时', basis: 'target' },
{ metric: '移动办公覆盖率', value: '100%', description: 'PC+移动端全覆盖', basis: 'target' },
{ metric: '流程电子化率', value: '100%', description: '告别纸质流转', basis: 'target' },
],
certifications: [],
overview: '睿新协同办公系统是综合型企业协同办公平台,融合传统办公自动化与现代协作理念。集成流程审批、公文管理、即时通讯、知识管理、会议日程等核心模块,支持PC端与移动端全场景覆盖,帮助企业实现办公数字化、流程规范化、协作高效化。',
+12 -12
View File
@@ -37,8 +37,8 @@ export const SOLUTIONS: Solution[] = [
},
painPoints: ['生产数据孤岛,决策缺乏实时数据支撑', '库存管理粗放,资金占用成本高'],
outcomes: [
{ value: '30%', label: '库存周转提升' },
{ value: '40%', label: '生产效率提升' },
{ value: '30%', label: '库存周转提升', basis: 'target' },
{ value: '40%', label: '生产效率提升', basis: 'target' },
],
recommendedProducts: ['erp', 'bi', 'sds'],
},
@@ -77,8 +77,8 @@ export const SOLUTIONS: Solution[] = [
},
painPoints: ['线上线下渠道割裂,会员体验碎片化', '供应链响应慢,库存积压与缺货并存'],
outcomes: [
{ value: '25%', label: '营收增长' },
{ value: '99%', label: '全渠道库存准确率' },
{ value: '25%', label: '营收增长', basis: 'target' },
{ value: '99%', label: '全渠道库存准确率', basis: 'target' },
],
recommendedProducts: ['crm', 'bi', 'sds'],
},
@@ -117,8 +117,8 @@ export const SOLUTIONS: Solution[] = [
},
painPoints: ['招生线索管理分散,转化率难以提升', '教学资源管理混乱,优质内容难以沉淀'],
outcomes: [
{ value: '45%', label: '线索转化率提升' },
{ value: '60%', label: '内容发布效率提升' },
{ value: '45%', label: '线索转化率提升', basis: 'target' },
{ value: '60%', label: '内容发布效率提升', basis: 'target' },
],
recommendedProducts: ['cms', 'oa', 'crm'],
},
@@ -157,8 +157,8 @@ export const SOLUTIONS: Solution[] = [
},
painPoints: ['临床数据分散,医生调阅效率低', '运营决策依赖人工统计,时效性差'],
outcomes: [
{ value: '10x', label: '决策效率提升' },
{ value: '30%', label: '运营成本下降' },
{ value: '10x', label: '决策效率提升', basis: 'target' },
{ value: '30%', label: '运营成本下降', basis: 'target' },
],
recommendedProducts: ['erp', 'sds', 'bi'],
},
@@ -197,8 +197,8 @@ export const SOLUTIONS: Solution[] = [
},
painPoints: ['监管要求持续升级,合规管理成本高', '数据孤岛严重,客户画像难以落地'],
outcomes: [
{ value: '70%', label: '财务效率提升' },
{ value: '0', label: '重大合规事故' },
{ value: '70%', label: '财务效率提升', basis: 'target' },
{ value: '0', label: '重大合规事故', basis: 'target' },
],
recommendedProducts: ['bi', 'sds', 'erp'],
},
@@ -237,8 +237,8 @@ export const SOLUTIONS: Solution[] = [
},
painPoints: ['运输过程不透明,客户体验差', '调度依赖人工经验,车辆空驶率高'],
outcomes: [
{ value: '20%', label: '空驶率降低' },
{ value: '95%+', label: '全程可视化率' },
{ value: '20%', label: '空驶率降低', basis: 'target' },
{ value: '95%+', label: '全程可视化率', basis: 'target' },
],
recommendedProducts: ['erp', 'bi', 'sds'],
},
+7 -7
View File
@@ -15,18 +15,18 @@ export const STANDALONE_PRODUCTS: StandaloneProduct[] = [
heroThemeId: 'novavis',
scenario: '面向执法机关的离线智能数据分析与资金关系图谱平台',
metrics: [
{ value: '100万+', label: '秒级处理' },
{ value: '<30秒', label: '分析耗时' },
{ value: '20层+', label: '资金追踪' },
{ value: '100万+', label: '秒级处理', basis: 'target' },
{ value: '<30秒', label: '分析耗时', basis: 'target' },
{ value: '20层+', label: '资金追踪', basis: 'target' },
],
bundle: 'standalone',
capabilities: ['资金追踪', '关系图谱', 'AI 分析', '离线运行'],
caseStudies: [],
dataProofs: [
{ metric: '数据处理能力', value: '100万+', description: '百万级记录秒级处理' },
{ metric: '分析耗时', value: '<30秒', description: '百万级交易完成分析' },
{ metric: '内存占用降低', value: '70%', description: '相比传统方案显著优化' },
{ metric: '部署时间', value: '5分钟', description: '即插即用,快速启动' },
{ metric: '数据处理能力', value: '100万+', description: '百万级记录秒级处理', basis: 'target' },
{ metric: '分析耗时', value: '<30秒', description: '百万级交易完成分析', basis: 'target' },
{ metric: '内存占用降低', value: '70%', description: '相比传统方案显著优化', basis: 'target' },
{ metric: '部署时间', value: '5分钟', description: '即插即用,快速启动', basis: 'target' },
],
certifications: [],
overview: '睿视 NovaVis 是我们即将推出的面向执法机关的新一代智能数据分析平台。以完全离线架构保障数据安全——所有计算均在本地完成,数据不出本地、不触网络。融合 AI 智能分析与专业关系网络图谱,提供资金追踪、团伙分析、时空碰撞等核心能力,让海量涉案数据秒级响应,为经济犯罪侦查、反腐败调查、涉黑涉恶打击等场景提供全链路数据分析支撑。',
@@ -3,6 +3,7 @@
import { motion } from 'framer-motion';
import { CTAButton } from '@/components/ui/cta-button';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { weakestBasis, type MetricBasis } from '@/lib/constants/metrics-basis';
import { ContentUnavailableState } from '@/components/ui/content-unavailable';
import {
Trophy,
@@ -35,7 +36,7 @@ interface AboutData {
promiseText?: string;
coreValues?: { number: string; title: string; description: string }[];
milestones?: { year: string; title: string; description: string; highlight: string }[];
keyMetrics?: { value: string; label: string; sub: string }[];
keyMetrics?: { value: string; label: string; sub: string; basis?: MetricBasis }[];
certifications?: { name: string; description: string }[];
partners?: string[];
ctaTitle?: string;
@@ -147,7 +148,7 @@ function MetricsStrip({ data }: { data: AboutData }) {
</motion.div>
))}
</div>
<MetricsBasisNote className="mt-8 text-center lg:text-left" />
<MetricsBasisNote basis={weakestBasis(metrics.map((m) => m.basis))} className="mt-8 text-center lg:text-left" />
</div>
</section>
);
+3 -2
View File
@@ -14,6 +14,7 @@ import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { COMPANY_INFO, TRUST_SIGNALS, EARLY_ACCESS } from '@/lib/constants';
import { canonicalCtaLabel, CTA_LABELS } from '@/lib/constants/cta-labels';
import { canonicalTrustSource } from '@/lib/constants/company';
import { weakestBasis } from '@/lib/constants/metrics-basis';
import { useSiteConfig } from '@/lib/site-config';
import { useReducedMotion } from '@/hooks/use-reduced-motion';
import { type NewsItem } from '@/lib/constants/news';
@@ -296,7 +297,7 @@ function HeroStatsBand({ stats }: { stats?: Record<string, any>[] }) {
))}
</div>
</ScrollReveal>
<MetricsBasisNote className="mt-6" />
<MetricsBasisNote basis={weakestBasis(displayStats.map((s) => s.basis))} className="mt-6" />
</div>
</section>
);
@@ -558,7 +559,7 @@ function ServicesSection({ services, pageCopy }: { services?: Record<string, any
</div>
))}
</div>
<MetricsBasisNote />
<MetricsBasisNote basis={weakestBasis(((service as any).metrics as any[]).map((m) => m.basis))} />
</div>
)}
<div className="mt-auto pt-2">
+1 -1
View File
@@ -6,7 +6,7 @@ import ProductsContentV3 from './products-content-v3';
export const metadata: Metadata = {
title: `产品矩阵 - ${COMPANY_INFO.displayName}`,
description: `覆盖企业数字化场景的产品矩阵,从数据智能到业务协同,每一款产品都经过实战验证`,
description: `覆盖企业数字化核心场景的自研产品矩阵,从业务系统到数据智能,套装与专业产品线互为补充`,
};
export const revalidate = 3600;
@@ -325,7 +325,7 @@ export default function ProductDetailContentV3({ product }: ProductDetailContent
{product.methodology && product.methodology.length > 0 && (
<MethodologyFramework
title="产品方法论"
subtitle="经过实战验证的产品设计理念与实施框架"
subtitle="我们做产品设计理念与实施框架"
layers={product.methodology}
/>
)}
@@ -341,12 +341,13 @@ export default function ProductDetailContentV3({ product }: ProductDetailContent
<L3EmptyFallback caseStudies={product.caseStudies} certifications={product.certifications} dataProofs={product.dataProofs} />
{product.dataProofs && product.dataProofs.length > 0 && (
<DataProofSection
title="可衡量的成果"
subtitle="用数据说话,每一项指标都源自真实客户案例"
title="可量化的设计目标"
subtitle="以下指标为产品设计目标口径,用于约束自研交付标准;公司尚无已交付客户案例可提供实测数据。"
metrics={product.dataProofs.map((dp) => ({
value: dp.value,
label: dp.metric,
description: dp.description,
basis: dp.basis,
}))}
/>
)}
@@ -10,6 +10,7 @@ import { BentoItem } from '@/components/sections/bento-grid';
import { ArrowRight, ArrowUpRight, Package, Cpu, TrendingUp, Users, Settings, FileText } from 'lucide-react';
import type { Product } from '@/lib/constants/products';
import { CTA_LABELS } from '@/lib/constants/cta-labels';
import { weakestBasis } from '@/lib/constants/metrics-basis';
import { cn } from '@/lib/utils';
const EASE_SPRING = { type: 'spring', stiffness: 300, damping: 30 } as const;
@@ -146,7 +147,7 @@ function ProductCard({ product, size = 'small' }: { product: Product; size?: 'la
))}
</div>
{product.metrics && product.metrics.length > 0 && product.status !== '已发布' && (
<MetricsBasisNote className="-mt-2 mb-4" />
<MetricsBasisNote basis={weakestBasis(product.metrics.map((m) => m.basis))} className="-mt-2 mb-4" />
)}
<div className="flex flex-wrap gap-2">
@@ -319,7 +320,7 @@ function SuiteCombosSection({ products }: { products: Product[] }) {
</h2>
<p className="text-text-secondary text-lg mt-6 leading-relaxed">
1+1 2
线
</p>
</ScrollReveal>
@@ -178,7 +178,7 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps)
<h2 className="text-2xl font-bold text-ink"></h2>
</div>
<p className="text-text-secondary max-w-2xl">
</p>
</motion.div>
@@ -6,12 +6,13 @@ import { StaticLink } from '@/components/ui/static-link';
import { CTAButton } from '@/components/ui/cta-button';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { CTA_LABELS, canonicalCtaLabel } from '@/lib/constants/cta-labels';
import type { Service } from '@/lib/constants/services';
import type { Service, ServiceMetric } from '@/lib/constants/services';
import { weakestBasis } from '@/lib/constants/metrics-basis';
const EASE_OUT = [0.22, 1, 0.36, 1] as const;
/** Fallback metrics keyed by service ID — used when CMS data does not provide metrics */
const SERVICE_UI_META: Record<string, { metrics: { value: string; label: string }[] }> = {
const SERVICE_UI_META: Record<string, { metrics: ServiceMetric[] }> = {
consulting: { metrics: [{ value: '10+', label: '人核心团队' }, { value: '6', label: '行业场景' }] },
software: { metrics: [{ value: '6', label: '款自研产品' }, { value: 'A+', label: '代码质量' }] },
data: { metrics: [{ value: '99.9%', label: '系统可用性' }, { value: '24/7', label: '运维支持' }] },
@@ -231,7 +232,9 @@ function ServicesGridSection({ services, pageCopy }: { services?: Service[]; pag
</div>
))}
</div>
{service.metrics.length > 0 && <MetricsBasisNote className="mb-6" />}
{service.metrics.length > 0 && (
<MetricsBasisNote basis={weakestBasis(service.metrics.map((m) => m.basis))} className="mb-6" />
)}
<div className="inline-flex items-center gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors duration-300">
<span></span>
@@ -6,6 +6,7 @@ import { StaticLink } from '@/components/ui/static-link';
import { CTAButton } from '@/components/ui/cta-button';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { CTA_LABELS, canonicalCtaLabel } from '@/lib/constants/cta-labels';
import { weakestBasis } from '@/lib/constants/metrics-basis';
import { type Solution } from '@/lib/constants/solutions';
import type { Product } from '@/lib/constants/products';
@@ -135,7 +136,7 @@ function SolutionCard({ solution, index, products, featured = false }: { solutio
))}
</div>
{solution.outcomes && solution.outcomes.length > 0 && (
<MetricsBasisNote className="-mt-2 mb-4" />
<MetricsBasisNote basis={weakestBasis(solution.outcomes.map((o) => o.basis))} className="-mt-2 mb-4" />
)}
<div className="pt-4 border-t border-border-primary">
+5
View File
@@ -5,6 +5,8 @@ import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal';
import { EASE_OUT } from '@/components/ui/page-decoration';
import { CheckCircle2, ChevronDown } from 'lucide-react';
import { cn } from '@/lib/utils';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { weakestBasis, type MetricBasis } from '@/lib/constants/metrics-basis';
import { useState } from 'react';
export interface MethodologyLayer {
@@ -318,6 +320,7 @@ export interface DataMetric {
description?: string;
trend?: string;
trendUp?: boolean;
basis?: MetricBasis;
}
export interface DataProofSectionProps {
@@ -328,6 +331,7 @@ export interface DataProofSectionProps {
export function DataProofSection({ title, subtitle, metrics }: DataProofSectionProps) {
if (!metrics || metrics.length === 0) return null;
const basis = weakestBasis(metrics.map((m) => m.basis));
return (
<section className="relative py-20 sm:py-28 md:py-32 lg:py-40 overflow-hidden bg-bg-primary">
@@ -375,6 +379,7 @@ export function DataProofSection({ title, subtitle, metrics }: DataProofSectionP
))}
</StaggerReveal>
</div>
<MetricsBasisNote basis={basis} className="mt-6" />
</div>
</section>
);
@@ -15,6 +15,8 @@ import {
type LucideIcon,
} from 'lucide-react';
import type { Product } from '@/lib/constants/products';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { weakestBasis } from '@/lib/constants/metrics-basis';
interface ProductValueSectionProps {
product: Product;
@@ -250,11 +252,11 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
<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-ink">
</span>
</div>
<h3 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95]">
</h3>
</div>
@@ -275,6 +277,10 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
);
})}
</div>
<MetricsBasisNote
basis={weakestBasis(product.dataProofs.map((proof) => proof.basis))}
className="mt-8"
/>
</motion.div>
)}
</div>
@@ -5,6 +5,8 @@ import { useRef, useEffect, useState } from 'react';
import { CaseStudyCard } from './detail-case-study';
import { CertificationList } from './detail-certification';
import type { CaseStudy, DataProof, Certification } from '@/lib/constants/products';
import { MetricsBasisNote } from '@/components/ui/metrics-basis-note';
import { weakestBasis } from '@/lib/constants/metrics-basis';
interface DetailTrustSectionProps {
caseStudies?: CaseStudy[];
@@ -84,13 +86,13 @@ export function DetailTrustSection({
<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-ink">
</span>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95]">
使
<br />
</h2>
</motion.div>
@@ -114,6 +116,10 @@ export function DetailTrustSection({
</motion.div>
))}
</div>
<MetricsBasisNote
basis={weakestBasis(dataProofs.map((proof) => proof.basis))}
className="mt-8"
/>
</div>
)}
+6 -4
View File
@@ -435,14 +435,14 @@ describe('ProductValueSection', () => {
it('should render data proofs when available', async () => {
const { ProductValueSection } = await import('./detail-product-value');
render(<ProductValueSection product={mockProduct} />);
expect(screen.getByText('用真实效果证明价值')).toBeInTheDocument();
expect(screen.getByText('以可量化的目标约束交付')).toBeInTheDocument();
});
it('should not render data proofs section when empty', async () => {
const { ProductValueSection } = await import('./detail-product-value');
const productWithoutData = { ...mockProduct, dataProofs: [] };
render(<ProductValueSection product={productWithoutData} />);
expect(screen.queryByText('用真实效果证明价值')).not.toBeInTheDocument();
expect(screen.queryByText('以可量化的目标约束交付')).not.toBeInTheDocument();
});
});
@@ -462,8 +462,10 @@ describe('DetailTrustSection', () => {
dataProofs={mockProduct.dataProofs}
/>
);
expect(screen.getByText('用真实效果证明价值')).toBeInTheDocument();
expect(screen.getByText('目标口径')).toBeInTheDocument();
expect(screen.getByText(mockProduct.dataProofs[0]!.metric)).toBeInTheDocument();
// 未声明 basis 的 dataProofs 必须自动落最弱口径角注,不得裸奔
expect(screen.getByTestId('metrics-basis-note')).toHaveTextContent('目标口径,非既成结果');
});
it('should render case studies', async () => {
@@ -500,7 +502,7 @@ describe('DetailTrustSection', () => {
accentColor="#C41E3A"
/>
);
expect(screen.getByText('用真实效果证明价值')).toBeInTheDocument();
expect(screen.getByText('目标口径')).toBeInTheDocument();
expect(screen.getByText('他们已经实现了转型突破')).toBeInTheDocument();
expect(screen.getByText('资质认证')).toBeInTheDocument();
});
@@ -9,7 +9,7 @@
* - 内容源于 `EARLY_ACCESS` 常量(company.ts L119),单一真源。
*
* 与 L3SignalsSlot 关系:EarlyAccessNotice 是 L3EmptyFallback 的次要兜底块,
* 在 signals(4 类信号卡)下方呈现——回答「为什么没有真实客户案例」这个问题。
* 在 signals(4 类信号卡)下方呈现——回答「为什么没有可公开案例」这个问题。
*/
import Link from 'next/link';
+11 -4
View File
@@ -1,14 +1,21 @@
import { cn } from '@/lib/utils';
import { METRICS_BASIS_NOTE } from '@/lib/constants/metrics-basis';
import { METRIC_BASIS_NOTES, resolveMetricBasis, type MetricBasis } from '@/lib/constants/metrics-basis';
/**
* 结果型数字(提升百分比 / 可用性 / 达标率等)下方的口径角注。
* 全站统一文案,见 METRICS_BASIS_NOTE。供 solutions / services / home / about / products 复用
* 文案按数字声明的 `basis` 口径解析,未声明一律按最弱的 target 处理,见 METRIC_BASIS_NOTES
* 供 solutions / services / home / about / products 与详情页 DataProofSection 复用。
*/
export function MetricsBasisNote({ className }: { className?: string }) {
export function MetricsBasisNote({
className,
basis = 'target',
}: {
className?: string;
basis?: MetricBasis | string | null;
}) {
return (
<p className={cn('text-xs text-text-muted', className)} data-testid="metrics-basis-note">
{METRICS_BASIS_NOTE}
{METRIC_BASIS_NOTES[resolveMetricBasis(basis)]}
</p>
);
}
+25
View File
@@ -1,5 +1,22 @@
import type { FieldDefinition } from './types';
/**
* 数字口径声明字段——所有以大字数字样式呈现的指标(metrics / outcomes / dataProofs)必须携带。
* 渲染端按此自动附口径角注;留空一律按最弱的 target 处理,见 lib/constants/metrics-basis.ts。
* 只有具备可核验实测出处(报告、监控、已交付客户数据)时才应选 verified。
*/
const metricBasisField: FieldDefinition = {
name: 'basis',
type: 'select',
label: '数据口径',
description: '留空按「目标口径」处理并自动标注;仅在有可核验实测出处时选「已验证」',
options: [
{ label: '目标口径(设计/承诺值,非既成结果)', value: 'target' },
{ label: '团队历史项目成果(非本公司交付数据)', value: 'team-history' },
{ label: '已验证(有可核验实测出处)', value: 'verified' },
],
};
export interface ContentTypeConfig {
model: {
id: string;
@@ -164,6 +181,7 @@ const serviceFields: FieldDefinition[] = [
fields: [
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'label', type: 'text', label: '标签', required: true },
{ ...metricBasisField },
],
},
{
@@ -186,6 +204,7 @@ const serviceFields: FieldDefinition[] = [
{ name: 'metric', type: 'text', label: '指标名称', required: true },
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'description', type: 'text', label: '描述', required: true },
{ ...metricBasisField },
],
},
{
@@ -363,6 +382,7 @@ const productFields: FieldDefinition[] = [
fields: [
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'label', type: 'text', label: '标签', required: true },
{ ...metricBasisField },
],
},
{
@@ -385,6 +405,7 @@ const productFields: FieldDefinition[] = [
{ name: 'metric', type: 'text', label: '指标名称', required: true },
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'description', type: 'text', label: '描述', required: true },
{ ...metricBasisField },
],
},
{
@@ -548,6 +569,7 @@ const standaloneProductFields: FieldDefinition[] = [
fields: [
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'label', type: 'text', label: '标签', required: true },
{ ...metricBasisField },
],
},
{
@@ -570,6 +592,7 @@ const standaloneProductFields: FieldDefinition[] = [
{ name: 'metric', type: 'text', label: '指标名称', required: true },
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'description', type: 'text', label: '描述', required: true },
{ ...metricBasisField },
],
},
{
@@ -755,6 +778,7 @@ const solutionFields: FieldDefinition[] = [
fields: [
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'label', type: 'text', label: '标签', required: true },
{ ...metricBasisField },
],
},
{
@@ -1180,6 +1204,7 @@ const aboutPageFields: FieldDefinition[] = [
{ name: 'value', type: 'text', label: '数值', required: true },
{ name: 'label', type: 'text', label: '标签', required: true },
{ name: 'sub', type: 'text', label: '副标题' },
{ ...metricBasisField },
],
},
{
+122 -4
View File
@@ -1,12 +1,25 @@
/**
* 结果型数字口径机械防线(2026-09-19 critique P0):
* 「目标口径,非既成结果」必须来自 METRICS_BASIS_NOTE 单一真源;
* 每个呈现结果型数字的营销页必须接入 MetricsBasisNote
* 数字口径机械防线(2026-09-19 critique P0 → 结构强制收尾):
* 1) 「目标口径,非既成结果」必须来自 METRICS_BASIS_NOTE 单一真源,不得散写字面量
* 2) 每个呈现结果型数字的渲染源(含详情页共享数字组件)必须接入 MetricsBasisNote
* 3) 走共享数字样式渲染的指标必须声明 basistarget|team-history|verified),
* 且口径解析对缺失/非法值保守回落到最弱的 target;
* 4) 禁止再引入「数字源自真实客户案例 / 经过实战验证」式虚构佐证(本站零公开客户案例)。
* 扫描范围:src/app + src/components 的 .ts/.tsx(排除测试文件与 _archive 归档)。
*/
import { readdirSync, readFileSync, statSync } from 'fs';
import { join } from 'path';
import { METRICS_BASIS_NOTE } from './metrics-basis';
import {
METRICS_BASIS_NOTE,
METRIC_BASES,
METRIC_BASIS_NOTES,
FORBIDDEN_PROOF_PHRASES,
resolveMetricBasis,
weakestBasis,
} from './metrics-basis';
import { PRODUCTS } from './products';
import { SOLUTIONS } from './solutions';
import { STANDALONE_PRODUCTS } from '../../../prisma/seeds/standalone-products';
const SOURCE_ROOTS = ['src/app', 'src/components'];
@@ -16,6 +29,10 @@ const REQUIRED_ANNOTATED_SOURCES = [
'src/app/(marketing)/solutions/solutions-content-v3.tsx',
'src/app/(marketing)/services/services-content-v3.tsx',
'src/app/(marketing)/home-content-v15.tsx',
// 详情页同样以大数字样式渲染 dataProofs,不得裸奔
'src/components/content/sections.tsx',
'src/components/detail/detail-trust-section.tsx',
'src/components/detail/detail-product-value.tsx',
];
function collectSourceFiles(dir: string, acc: string[] = []): string[] {
@@ -32,11 +49,29 @@ function collectSourceFiles(dir: string, acc: string[] = []): string[] {
return acc;
}
/** 把三处 seed 里的全部数字条目摊平,用于口径声明完整性断言。 */
function allSeedMetricBases(): Array<string | undefined> {
const bases: Array<string | undefined> = [];
for (const p of [...PRODUCTS, ...STANDALONE_PRODUCTS]) {
(p.metrics ?? []).forEach((m) => bases.push(m.basis));
(p.dataProofs ?? []).forEach((d) => bases.push(d.basis));
}
for (const s of SOLUTIONS) {
(s.outcomes ?? []).forEach((o) => bases.push(o.basis));
(s.dataProofs ?? []).forEach((d) => bases.push(d.basis));
}
return bases;
}
describe('结果型数字口径角注防线', () => {
it('METRICS_BASIS_NOTE 固定为「目标口径,非既成结果」', () => {
expect(METRICS_BASIS_NOTE).toBe('目标口径,非既成结果');
});
it('METRICS_BASIS_NOTE 等于最弱口径 target 的文案', () => {
expect(METRICS_BASIS_NOTE).toBe(METRIC_BASIS_NOTES.target);
});
it('生产源码不得硬编码口径角注字面量(必须引用单一真源)', () => {
const violations: string[] = [];
for (const root of SOURCE_ROOTS) {
@@ -63,3 +98,86 @@ describe('结果型数字口径角注防线', () => {
}
});
});
describe('basis 口径解析', () => {
it('枚举固定为 target / team-history / verified', () => {
expect(METRIC_BASES).toEqual(['target', 'team-history', 'verified']);
});
it('三个口径均有文案,无空串', () => {
for (const basis of METRIC_BASES) {
expect(METRIC_BASIS_NOTES[basis].length).toBeGreaterThan(0);
}
});
it('合法口径原样解析(正控制,防匹配零的假绿)', () => {
expect(resolveMetricBasis('verified')).toBe('verified');
expect(resolveMetricBasis('team-history')).toBe('team-history');
expect(resolveMetricBasis('target')).toBe('target');
});
it('缺失/非法/大小写错误一律保守回落到 target', () => {
expect(resolveMetricBasis(undefined)).toBe('target');
expect(resolveMetricBasis(null)).toBe('target');
expect(resolveMetricBasis('VERIFIED')).toBe('target');
expect(resolveMetricBasis('已验证')).toBe('target');
expect(resolveMetricBasis('verified ')).toBe('target');
});
it('weakestBasis 取最弱口径:任一 target 存在即整体为 target', () => {
expect(weakestBasis(['verified', 'team-history'])).toBe('team-history');
expect(weakestBasis(['verified', 'target'])).toBe('target');
expect(weakestBasis(['verified', 'verified'])).toBe('verified');
});
it('weakestBasis 空数组与含未声明项均落到 target', () => {
expect(weakestBasis([])).toBe('target');
expect(weakestBasis(['verified', undefined])).toBe('target');
});
});
describe('seed 数字必须显式声明口径', () => {
it('存在可检出的数字条目(覆盖证明,防空跑)', () => {
expect(allSeedMetricBases().length).toBeGreaterThan(40);
});
it('每条 metrics / outcomes / dataProofs 均声明 basis', () => {
const missing = allSeedMetricBases().filter((b) => !b);
expect(missing).toEqual([]);
});
it('声明的 basis 全部合法', () => {
const invalid = allSeedMetricBases().filter(
(b) => !(METRIC_BASES as readonly string[]).includes(String(b)),
);
expect(invalid).toEqual([]);
});
it('当前无任何条目自称 verified(零编造:尚无客户实测数据)', () => {
const claimed = allSeedMetricBases().filter((b) => b === 'verified');
expect(claimed).toEqual([]);
});
});
describe('虚构佐证禁词防线', () => {
it('正控制:禁词表非空且每条均可被检出', () => {
expect(FORBIDDEN_PROOF_PHRASES.length).toBeGreaterThan(0);
for (const phrase of FORBIDDEN_PROOF_PHRASES) {
expect(`示例文案${phrase}结尾`).toContain(phrase);
}
});
it('生产源码不含「真实客户案例 / 实战验证」式虚构佐证', () => {
const violations: string[] = [];
for (const root of SOURCE_ROOTS) {
for (const file of collectSourceFiles(root)) {
const lines = readFileSync(file, 'utf-8').split('\n');
lines.forEach((line, i) => {
const hit = FORBIDDEN_PROOF_PHRASES.find((p) => line.includes(p));
if (hit) violations.push(`${file}:${i + 1} ${hit}`);
});
}
}
expect(violations).toEqual([]);
});
});
+56 -6
View File
@@ -1,9 +1,59 @@
/**
* METRICS_BASIS_NOTE — 全站「结果型数字口径角注单一真源。
* 数字口径metric basis单一真源。
*
* 2026-09-19 critique P0solutions / services / home / about 把「30% 库存周转提升」
* 「99.9% 可用」这类目标值/能力值以既成结果口吻呈现,缺出处即构成误导。
* 与 CTA_LABELS 同属渲染入口的诚实性防线:数字保留,但强制附口径说明。
* 根治方式是补充真实案例数据或 admin 修正内容,此处为最后一道防线。
* 2026-09-19 critique P0solutions / services / home / about / products 把「30% 库存周转提升」
* 「99.9% 系统可用」这类目标值/能力值以既成结果口吻呈现,缺出处即构成误导。
*
* 本文件把「每个数字必须声明口径」从个别页的自觉升级为结构约束:数字携带 `basis`,
* 渲染端据此自动附角注;未声明或声明非法一律按最弱口径 `target` 处理(保守默认,
* 宁可多标注也不裸奔)。真实出处由业务在 admin 录入,代码零虚构。
*/
export const METRICS_BASIS_NOTE = '目标口径,非既成结果';
/** 由弱到强:聚合一组数字时取最弱者,保证角注不夸大。 */
export const METRIC_BASES = ['target', 'team-history', 'verified'] as const;
export type MetricBasis = (typeof METRIC_BASES)[number];
const BASIS_STRENGTH: Record<MetricBasis, number> = {
target: 0,
'team-history': 1,
verified: 2,
};
/** 各口径对应的角注文案。 */
export const METRIC_BASIS_NOTES: Record<MetricBasis, string> = {
target: '目标口径,非既成结果',
'team-history': '团队历史项目成果,非本公司交付数据',
verified: '已有可核验的实测出处',
};
/** 全站默认口径角注(= 最弱口径 target 的文案)。 */
export const METRICS_BASIS_NOTE = METRIC_BASIS_NOTES.target;
/** 把数据里的任意值归一为合法口径;缺失/非法一律回落到最弱的 `target`。 */
export function resolveMetricBasis(basis?: string | null): MetricBasis {
return (METRIC_BASES as readonly string[]).includes(String(basis))
? (basis as MetricBasis)
: 'target';
}
/** 一组数字的聚合口径:取最弱的一个。 */
export function weakestBasis(bases: Array<MetricBasis | string | undefined | null>): MetricBasis {
if (bases.length === 0) return 'target';
return bases.map(resolveMetricBasis).reduce(
(weakest, cur) => (BASIS_STRENGTH[cur] < BASIS_STRENGTH[weakest] ? cur : weakest),
resolveMetricBasis(bases[0]),
);
}
/**
* 虚构佐证禁词表:本站 2026-01 成立、零公开客户案例(见 early-access-notice),
* 因此任何「数字源自真实客户案例 / 经过实战验证」的措辞都是编造佐证。
* 由 metrics-basis.test.ts 在 src/app + src/components 全源码扫描强制。
*/
export const FORBIDDEN_PROOF_PHRASES = [
'源自真实客户案例',
'真实客户案例',
'经过实战验证',
'实战验证',
] as const;
+4 -1
View File
@@ -1,3 +1,5 @@
import type { MetricBasis } from './metrics-basis';
export interface CaseStudy {
client: string;
industry: string;
@@ -10,6 +12,7 @@ export interface DataProof {
metric: string;
value: string;
description: string;
basis?: MetricBasis;
}
export interface Certification {
@@ -78,7 +81,7 @@ export interface Product {
techStack?: TechStackCategory[];
faqs?: FAQItem[];
scenario?: string;
metrics?: { value: string; label: string }[];
metrics?: { value: string; label: string; basis?: MetricBasis }[];
bundle: 'enterprise' | 'standalone';
capabilities?: string[];
}
+1
View File
@@ -37,6 +37,7 @@ export interface FAQItem {
export interface ServiceMetric {
value: string;
label: string;
basis?: import('./metrics-basis').MetricBasis;
}
export interface Service {
+1 -1
View File
@@ -24,7 +24,7 @@ export interface Solution {
dataProofs?: import('./products').DataProof[];
certifications?: import('./products').Certification[];
painPoints?: string[];
outcomes?: { value: string; label: string }[];
outcomes?: { value: string; label: string; basis?: import('./metrics-basis').MetricBasis }[];
recommendedProducts?: string[];
}