From 0501e6e8c26dbf220c24da25501b073b663a5521 Mon Sep 17 00:00:00 2001 From: freedakgmail Date: Sun, 14 Jun 2026 10:15:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(profitability):=20=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E5=89=8D=E6=A0=A1=E9=AA=8C=E6=8A=A5=E4=BB=B7=E5=8F=A3=E5=BE=84?= =?UTF-8?q?,=E9=81=BF=E5=85=8D=E9=9D=99=E9=BB=98=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=94=B6=E5=85=A50=E5=85=83=E7=9A=84=E7=9B=88=E5=88=A9?= =?UTF-8?q?=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - handleRun 在运行前按报价模式校验收入口径: - per_head/volume: 无对客单价且无管理费 → 提示并阻止 - cost_plus: 无成本加成率且无岗位人月单价 → 提示并阻止 - fixed_total: 无合同总额 → 提示并阻止 - 成本加成率字段 hint 补充必填说明 --- web/src/pages/NewAssessment.tsx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/web/src/pages/NewAssessment.tsx b/web/src/pages/NewAssessment.tsx index d2cff7c..38ffa51 100644 --- a/web/src/pages/NewAssessment.tsx +++ b/web/src/pages/NewAssessment.tsx @@ -681,6 +681,25 @@ export function NewAssessment(): JSX.Element { } } + // —— 收入口径校验:避免报价信息不足时静默生成「收入 0 元」的盈利分析。—— + const anyUnitPrice = apiPositions.some((p) => p.unitPrice !== undefined && p.unitPrice > 0); + if (pricingModel === 'per_head' || pricingModel === 'volume') { + if (!anyUnitPrice && mf === undefined) { + setError('报价信息不足:请在「报价与成本」步骤为岗位填写对客月单价,或填写管理费(元/人/月),否则盈利分析收入将为 0。'); + return; + } + } else if (pricingModel === 'cost_plus') { + if (mr === undefined && !anyUnitPrice) { + setError('报价信息不足:业务/服务外包(成本加成)需填写「成本加成率」(如 0.15)或为岗位填写「人月单价」,否则盈利分析收入将为 0。'); + return; + } + } else if (pricingModel === 'fixed_total') { + if (ct === undefined || ct <= 0) { + setError('报价信息不足:固定总价模式请填写「合同总额(含税,元)」,否则盈利分析收入将为 0。'); + return; + } + } + // 估算基准报价(供既有报告费用区):人头/产能=Σ人数×单价×月;固定总价=合同总额。 let baselineQuote = 0; if (pricingModel === 'fixed_total') { @@ -996,7 +1015,7 @@ export function NewAssessment(): JSX.Element { setClientTotalHeadcount(e.target.value)} placeholder="如 500" inputMode="numeric" /> {pricingModel === 'cost_plus' && ( - + setMarkupRate(e.target.value)} placeholder="0.15" inputMode="decimal" /> )}