From 1b4b881eeb95dcecd8854493b3091df01e28442a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sun, 18 Jan 2026 22:44:50 +0800 Subject: [PATCH] a --- backend/api/routes/config.py | 3 +- frontend/src/components/ConfigPanel.jsx | 88 ++++++++++--------------- trading_system/position_manager.py | 14 ++-- 3 files changed, 48 insertions(+), 57 deletions(-) diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index 139d07c..bb3b9f4 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -379,7 +379,8 @@ async def update_config(key: str, item: ConfigUpdate): item.value = bool(item.value) # 特殊验证:百分比配置应该在0-1之间 - if 'PERCENT' in key and config_type == 'number': + # 兼容:PERCENT / PCT + if ('PERCENT' in key or 'PCT' in key) and config_type == 'number': if not (0 <= float(item.value) <= 1): raise HTTPException( status_code=400, diff --git a/frontend/src/components/ConfigPanel.jsx b/frontend/src/components/ConfigPanel.jsx index 9fbc604..8830fdd 100644 --- a/frontend/src/components/ConfigPanel.jsx +++ b/frontend/src/components/ConfigPanel.jsx @@ -765,9 +765,17 @@ const ConfigPanel = () => { } const ConfigItem = ({ label, config, onUpdate, disabled }) => { - // 初始化显示值:百分比配置转换为整数形式显示 + const isPercentKey = label.includes('PERCENT') || label.includes('PCT') + + const formatPercent = (n) => { + // 最多 4 位小数,去掉尾随 0(例如 2.3000 -> 2.3) + if (typeof n !== 'number' || isNaN(n)) return '' + return n.toFixed(4).replace(/\.?0+$/, '') + } + + // 初始化显示值:百分比配置转为“百分比数值”(支持小数) const getInitialDisplayValue = (val) => { - if (config.type === 'number' && label.includes('PERCENT')) { + if (config.type === 'number' && isPercentKey) { if (val === null || val === undefined || val === '') { return '' } @@ -775,12 +783,9 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { if (isNaN(numVal)) { return '' } - // 如果是小数形式(0.05),转换为整数显示(5) - if (numVal < 1) { - return Math.round(numVal * 100) - } - // 如果已经是整数形式(5),直接显示 - return numVal + // 存储为 0~1,小于等于 1 则换算成百分比;异常旧值则原样展示 + const percent = numVal <= 1 ? numVal * 100 : numVal + return formatPercent(percent) } return val === null || val === undefined ? '' : val } @@ -823,8 +828,8 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { numValue = parseFloat(localValue) } else if (localValue === '.' || localValue === '-.') { // "." 或 "-." 视为无效,恢复原值 - const restoreValue = label.includes('PERCENT') - ? (typeof value === 'number' && value < 1 ? Math.round(value * 100) : value) + const restoreValue = isPercentKey + ? (typeof value === 'number' ? formatPercent((value <= 1 ? value * 100 : value)) : value) : value setLocalValue(restoreValue) return @@ -837,23 +842,22 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { if (isNaN(numValue)) { // 如果输入无效,恢复原值 - const restoreValue = label.includes('PERCENT') - ? (typeof value === 'number' && value < 1 ? Math.round(value * 100) : value) + const restoreValue = isPercentKey + ? (typeof value === 'number' ? formatPercent((value <= 1 ? value * 100 : value)) : value) : value setLocalValue(restoreValue) return } processedValue = numValue - // 百分比配置需要转换:用户输入的是整数(如5),需要转换为小数(0.05) - if (label.includes('PERCENT')) { - // 用户输入的是整数形式(如5表示5%),需要转换为小数(0.05) - // 如果输入值大于等于1,说明是百分比形式,需要除以100 - // 如果输入值小于1,说明已经是小数形式,直接使用 - if (processedValue >= 1) { - processedValue = processedValue / 100 + // 百分比配置:前端统一按“百分比”输入(支持小数),存储为 0~1 + if (isPercentKey) { + // 限制范围 0-100 + if (processedValue < 0 || processedValue > 100) { + setLocalValue(getInitialDisplayValue(value)) + return } - // 如果小于1,说明已经是小数形式,直接使用 + processedValue = processedValue / 100 } } else if (config.type === 'boolean') { processedValue = localValue === 'true' || localValue === true @@ -881,24 +885,7 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { } } - // 显示值:百分比配置显示为整数(如5),其他保持原样 - // 处理localValue可能是字符串的情况 - const getDisplayValue = () => { - if (config.type === 'number' && label.includes('PERCENT')) { - if (localValue === '' || localValue === null || localValue === undefined) { - return '' - } - const numValue = typeof localValue === 'string' ? parseFloat(localValue) : localValue - if (isNaN(numValue)) { - return '' - } - // localValue已经是显示值(整数形式),直接返回 - return numValue - } - return localValue === null || localValue === undefined ? '' : localValue - } - - const displayValue = getDisplayValue() + const displayValue = localValue === null || localValue === undefined ? '' : localValue if (config.type === 'boolean') { return ( @@ -1007,9 +994,7 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { { // 使用文本输入,避免number类型的自动补0问题 let newValue = e.target.value @@ -1024,7 +1009,7 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { // } // 如果是百分比配置,限制输入范围(0-100) - if (label.includes('PERCENT')) { + if (isPercentKey) { const numValue = parseFloat(newValue) if (newValue !== '' && !isNaN(numValue) && (numValue < 0 || numValue > 100)) { // 超出范围,不更新 @@ -1034,16 +1019,15 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { } // 更新本地值 - if (config.type === 'number' && label.includes('PERCENT')) { - // 百分比配置:保持数字形式(整数),允许空值 + if (config.type === 'number' && isPercentKey) { + // 百分比配置:允许字符串中间态(如 "2."),并支持小数 if (newValue === '') { handleChange('') } else { - const numValue = parseFloat(newValue) - if (!isNaN(numValue)) { - handleChange(numValue) + const validPattern = /^-?\d*\.?\d*$/ + if (validPattern.test(newValue)) { + handleChange(newValue) } - // 如果无效,不更新(保持原值) } } else if (config.type === 'number') { // 其他数字配置:允许字符串形式的中间状态(如 "0." 或 "."),支持小数输入 @@ -1091,9 +1075,9 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { }} disabled={disabled} className={isEditing ? 'editing' : ''} - placeholder={label.includes('PERCENT') ? '输入百分比' : '输入数值'} + placeholder={isPercentKey ? '输入百分比(可小数)' : '输入数值'} /> - {label.includes('PERCENT') && %} + {isPercentKey && %} {isEditing && (