feat(web): 增加参数即时估价与显式免费编辑
估价请求统一使用 350ms 防抖、AbortController 和请求序号,区分免费、计算中、价格不可用与失败状态,并展示候选冻结上限。价格未就绪时阻止生产提交。\n\n计价规则编辑器新增显式免费开关和 9 位价格步进。\n\n验证:Web 88 项测试和生产构建通过。
This commit is contained in:
@@ -221,7 +221,17 @@ function ModeRuleEditor(props: {
|
||||
<Select size="sm" value={rule.status ?? 'active'} onChange={(event) => patch({ status: event.target.value })}>{statuses.map((item) => <option key={item} value={item}>{item}</option>)}</Select>
|
||||
</PricingFormRow>
|
||||
<PricingFormRow label={`基础单价/${unitLabel(rule.unit)}`}>
|
||||
<Input size="sm" min="0" step="0.0001" type="number" value={rule.basePrice} onChange={(event) => patch({ basePrice: Number(event.target.value) })} />
|
||||
<Input disabled={rule.isFree} size="sm" min="0" step="0.000000001" type="number" value={rule.basePrice} onChange={(event) => patch({ basePrice: Number(event.target.value) })} />
|
||||
</PricingFormRow>
|
||||
<PricingFormRow label="显式免费">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={rule.isFree ?? false}
|
||||
onChange={(event) => patch({ isFree: event.target.checked, basePrice: event.target.checked ? 0 : rule.basePrice })}
|
||||
/>
|
||||
<span>允许该规则以零费用运行</span>
|
||||
</label>
|
||||
</PricingFormRow>
|
||||
<PricingReadonlyRow label="计价单位" value={unitLabel(rule.unit)} />
|
||||
<PricingReadonlyRow label="计算方式" value={calculatorLabel(rule.calculatorType)} />
|
||||
@@ -311,11 +321,32 @@ function TextRuleEditor(props: {
|
||||
<PricingFormRow label="状态">
|
||||
<Select size="sm" value={props.rule.status ?? 'active'} onChange={(event) => props.onChange({ ...props.rule, status: event.target.value })}>{statuses.map((item) => <option key={item} value={item}>{item}</option>)}</Select>
|
||||
</PricingFormRow>
|
||||
<PricingFormRow label="显式免费">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.rule.isFree ?? false}
|
||||
onChange={(event) => {
|
||||
const isFree = event.target.checked;
|
||||
props.onChange({
|
||||
...props.rule,
|
||||
isFree,
|
||||
basePrice: isFree ? 0 : props.rule.basePrice,
|
||||
formulaConfig: isFree
|
||||
? { ...(props.rule.formulaConfig ?? {}), inputTokenPrice: 0, cachedInputTokenPrice: 0, outputTokenPrice: 0 }
|
||||
: props.rule.formulaConfig,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<span>允许该规则以零费用运行</span>
|
||||
</label>
|
||||
</PricingFormRow>
|
||||
<PricingFormRow label="输入单价/1K tokens">
|
||||
<Input
|
||||
disabled={props.rule.isFree}
|
||||
size="sm"
|
||||
min="0"
|
||||
step="0.0001"
|
||||
step="0.000000001"
|
||||
type="number"
|
||||
value={prices.inputTokenPrice}
|
||||
onChange={(event) =>
|
||||
@@ -329,9 +360,10 @@ function TextRuleEditor(props: {
|
||||
</PricingFormRow>
|
||||
<PricingFormRow label="缓存输入单价/1K tokens">
|
||||
<Input
|
||||
disabled={props.rule.isFree}
|
||||
size="sm"
|
||||
min="0"
|
||||
step="0.0001"
|
||||
step="0.000000001"
|
||||
type="number"
|
||||
value={prices.cachedInputTokenPrice}
|
||||
onChange={(event) =>
|
||||
@@ -345,9 +377,10 @@ function TextRuleEditor(props: {
|
||||
</PricingFormRow>
|
||||
<PricingFormRow label="输出单价/1K tokens">
|
||||
<Input
|
||||
disabled={props.rule.isFree}
|
||||
size="sm"
|
||||
min="0"
|
||||
step="0.0001"
|
||||
step="0.000000001"
|
||||
type="number"
|
||||
value={prices.outputTokenPrice}
|
||||
onChange={(event) =>
|
||||
|
||||
@@ -225,6 +225,7 @@ function ruleToInput(rule: PricingRule): PricingRuleInput {
|
||||
resourceType: rule.resourceType,
|
||||
unit: rule.unit,
|
||||
basePrice: rule.basePrice,
|
||||
isFree: rule.isFree,
|
||||
currency: rule.currency,
|
||||
baseWeight: rule.baseWeight ?? {},
|
||||
dynamicWeight: rule.dynamicWeight ?? {},
|
||||
@@ -234,6 +235,8 @@ function ruleToInput(rule: PricingRule): PricingRuleInput {
|
||||
priority: rule.priority,
|
||||
status: rule.status,
|
||||
metadata: rule.metadata ?? {},
|
||||
effectiveFrom: rule.effectiveFrom,
|
||||
effectiveTo: rule.effectiveTo,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user