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 && (