feat(profitability): 第⑤步报价填写时实时提示报价口径不足
- 新增 revenueWarning 实时计算:随岗位单价/加成率/管理费/合同总额变化即时判断 - 报价信息不足时在报价模式区下方显示醒目橙色 alert 横幅,明确指出该填哪个字段 - 与运行前 handleRun 校验形成双重保障(填写时提示+运行时拦截)
This commit is contained in:
@@ -763,6 +763,31 @@ export function NewAssessment(): JSX.Element {
|
||||
const answeredCount = Object.values(answers).filter((v) => typeof v === 'number').length;
|
||||
const totalIndicators = indicators.length;
|
||||
|
||||
/* --------- 实时报价口径校验:判断当前填写是否足以产生收入(用于第⑤步即时提示) --------- */
|
||||
const revenueWarning: string | null = (() => {
|
||||
const toNum = (s: string): number | undefined => {
|
||||
const t = s.replace(/,/g, '').trim();
|
||||
return t !== '' && Number.isFinite(Number(t)) ? Number(t) : undefined;
|
||||
};
|
||||
const anyUnitPrice = positions.some(
|
||||
(p) => p.name.trim() !== '' && (toNum(p.unitPrice) ?? 0) > 0,
|
||||
);
|
||||
if (pricingModel === 'per_head' || pricingModel === 'volume') {
|
||||
if (!anyUnitPrice && toNum(mgmtFeePerHead) === undefined) {
|
||||
return '尚未填写对客月单价或管理费(元/人/月)。当前报价信息不足,盈利分析收入将为 0。请为岗位填写「对客月单价」,或填写「管理费」。';
|
||||
}
|
||||
} else if (pricingModel === 'cost_plus') {
|
||||
if (toNum(markupRate) === undefined && !anyUnitPrice) {
|
||||
return '尚未填写成本加成率或人月单价。业务/服务外包(成本加成)需填写「成本加成率」(如 0.15)或为岗位填写「人月单价」,否则盈利分析收入将为 0。';
|
||||
}
|
||||
} else if (pricingModel === 'fixed_total') {
|
||||
if ((toNum(contractTotal) ?? 0) <= 0) {
|
||||
return '尚未填写合同总额。固定总价模式请填写「合同总额(含税,元)」,否则盈利分析收入将为 0。';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
|
||||
/* ----------------------------- 渲染 ----------------------------- */
|
||||
return (
|
||||
<div style={{ fontFamily: FONT_FAMILY, maxWidth: 1200, margin: '0 auto' }}>
|
||||
@@ -1031,6 +1056,29 @@ export function NewAssessment(): JSX.Element {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{revenueWarning !== null && (
|
||||
<div
|
||||
role="alert"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: `${space(2)}px`,
|
||||
padding: `${space(2)}px ${space(3)}px`,
|
||||
marginBottom: `${space(3)}px`,
|
||||
borderRadius: `${RADIUS.md}px`,
|
||||
border: `1px solid ${colorVar('color.risk.high')}`,
|
||||
backgroundColor: 'rgba(245,158,11,0.10)',
|
||||
color: colorVar('color.text.primary'),
|
||||
...typographyStyle('caption'),
|
||||
}}
|
||||
>
|
||||
<span style={{ color: colorVar('color.risk.high'), flexShrink: 0, marginTop: 1 }}>
|
||||
<Icon name="alert" size={16} />
|
||||
</span>
|
||||
<span><strong>报价信息不足:</strong>{revenueWarning}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ ...typographyStyle('caption'), fontWeight: 700, color: colorVar('color.text.secondary'), marginBottom: `${space(2)}px` }}>
|
||||
岗位明细
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user