From 9d02dd3b7508293e9d431cdb8a1d1b1d9541874d Mon Sep 17 00:00:00 2001 From: reputationly <197039020@qq.com> Date: Mon, 15 Jun 2026 21:43:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(pricing):=20=E5=8F=AF=E8=A7=86=E5=8C=96?= =?UTF-8?q?=E5=AE=9A=E4=BB=B7=E7=BC=96=E8=BE=91=E5=99=A8=E4=BA=BA=E6=B0=91?= =?UTF-8?q?=E5=B8=81=E5=8C=96=EF=BC=88=E6=8C=89=E6=B1=87=E7=8E=87=E6=8A=98?= =?UTF-8?q?=E7=AE=97=EF=BC=8C=E5=AD=98=E5=82=A8=E5=80=8D=E7=8E=87=E4=B8=8D?= =?UTF-8?q?=E5=8F=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 换算内核注入美元兑人民币汇率:ModelRatio/ModelPrice 两个货币边界 ×/÷ rate, 派生倍率为同币种比值不参与换算;存储倍率与计费完全不变 - 显示按实际精度 + 保底 2 位补 0 + 去往返浮点漂移(formatDisplayPrice) - 汇率优先取页面级 options.USDExchangeRate(随保存刷新),StatusContext 仅兜底, 避免改汇率后同会话不刷新折算错 - PriceInput 聚焦用原始高精度串编辑,失焦不回写圆整值,廉价模型不丢精度 - 顶部提示条说明单位/汇率;表达式/阶梯计费仍按美元,标注口径差异 - 手动模式更名「倍率(高级)」并加美元口径警告条 --- .../Setting/Ratio/ModelPricingCombined.jsx | 18 +++- .../Ratio/components/ModelPricingEditor.jsx | 93 +++++++++++++------ .../Ratio/hooks/useModelPricingEditorState.js | 74 +++++++++++---- 3 files changed, 135 insertions(+), 50 deletions(-) diff --git a/web/classic/src/pages/Setting/Ratio/ModelPricingCombined.jsx b/web/classic/src/pages/Setting/Ratio/ModelPricingCombined.jsx index 51dfe27d3a6..c008e942ba2 100644 --- a/web/classic/src/pages/Setting/Ratio/ModelPricingCombined.jsx +++ b/web/classic/src/pages/Setting/Ratio/ModelPricingCombined.jsx @@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useState } from 'react'; -import { Radio, RadioGroup } from '@douyinfe/semi-ui'; +import { Banner, Radio, RadioGroup } from '@douyinfe/semi-ui'; import { useTranslation } from 'react-i18next'; import ModelPricingEditor from './components/ModelPricingEditor'; import ModelRatioSettings from './ModelRatioSettings'; @@ -37,13 +37,25 @@ export default function ModelPricingCombined({ options, refresh }) { onChange={(e) => setEditMode(e.target.value)} > {t('可视化编辑')} - {t('手动编辑')} + {t('倍率(高级)')} {editMode === 'visual' ? ( ) : ( - + <> + + + )} ); diff --git a/web/classic/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx b/web/classic/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx index 2beafe01d93..2e27fa2e748 100644 --- a/web/classic/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx +++ b/web/classic/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx @@ -17,7 +17,7 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useMemo, useState } from 'react'; import { Banner, Button, @@ -45,15 +45,20 @@ import { PAGE_SIZE, PRICE_SUFFIX, buildSummaryText, + formatDisplayPrice, hasValue, + normalizeRate, useModelPricingEditorState, } from '../hooks/useModelPricingEditorState'; import { useIsMobile } from '../../../../hooks/common/useIsMobile'; +import { StatusContext } from '../../../../context/Status'; import TieredPricingEditor from './TieredPricingEditor'; const { Text } = Typography; const EMPTY_CANDIDATE_MODEL_NAMES = []; +// 显示固定 2 位(¥4.00),编辑时才展开原始精度串;失焦不回写圆整值, +// 故 model state 始终保持高精度,序列化不受 2 位显示影响(见设计 §6.1.1)。 const PriceInput = ({ label, value, @@ -64,26 +69,40 @@ const PriceInput = ({ extraText = '', headerAction = null, hidden = false, -}) => ( -
-
- {label} - {headerAction} +}) => { + const [focused, setFocused] = useState(false); + const [draft, setDraft] = useState(''); + const displayValue = focused ? draft : formatDisplayPrice(value); + return ( +
+
+ {label} + {headerAction} +
+ {!hidden ? ( + { + // 编辑起点用原始高精度串,避免廉价模型(如 ¥0.0146)被 2 位显示圆整后丢精度。 + setDraft(value ?? ''); + setFocused(true); + }} + onBlur={() => setFocused(false)} + onChange={(v) => { + setDraft(v); + onChange(v); + }} + suffix={suffix} + disabled={disabled} + /> + ) : null} + {extraText ? ( +
{extraText}
+ ) : null}
- {!hidden ? ( - - ) : null} - {extraText ? ( -
{extraText}
- ) : null} -
-); + ); +}; export default function ModelPricingEditor({ options, @@ -99,6 +118,12 @@ export default function ModelPricingEditor({ }) { const { t } = useTranslation(); const isMobile = useIsMobile(); + const [statusState] = useContext(StatusContext); + // 优先用页面级、随保存刷新的 options.USDExchangeRate;StatusContext 仅 app 启动加载一次, + // 改汇率后同会话不刷新会折算错(写错计费倍率),故仅作兜底。 + const rate = normalizeRate( + options?.USDExchangeRate ?? statusState?.status?.usd_exchange_rate, + ); const [addVisible, setAddVisible] = useState(false); const [batchVisible, setBatchVisible] = useState(false); const [newModelName, setNewModelName] = useState(''); @@ -136,6 +161,7 @@ export default function ModelPricingEditor({ t, candidateModelNames, filterMode, + rate, }); const getExprModeLabel = useCallback((model) => { @@ -253,6 +279,17 @@ export default function ModelPricingEditor({ return ( <> + {allowAddModel ? (