fix: 🐛 修复系统配置页面报错的问题
This commit is contained in:
@@ -30,7 +30,7 @@ interface BasicConfigTabProps {
|
|||||||
|
|
||||||
const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
||||||
configs,
|
configs,
|
||||||
loading,
|
// loading,
|
||||||
saving,
|
saving,
|
||||||
onSave,
|
onSave,
|
||||||
onReset,
|
onReset,
|
||||||
@@ -38,20 +38,26 @@ const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
|||||||
onConfigChange
|
onConfigChange
|
||||||
}) => {
|
}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [formData, setFormData] = useState<Record<string, any>>({});
|
const [formData, setFormData] = useState<Record<string, unknown>>(() => {
|
||||||
|
// 初始化时设置默认数据
|
||||||
// 初始化表单数据
|
const initialData: Record<string, unknown> = {};
|
||||||
useEffect(() => {
|
configs.forEach(config => {
|
||||||
const initialData: Record<string, any> = {};
|
initialData[config.config_key] = config.config_value;
|
||||||
|
});
|
||||||
|
return initialData;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化表单数据
|
||||||
|
useEffect(() => {
|
||||||
|
const initialData: Record<string, unknown> = {};
|
||||||
configs.forEach(config => {
|
configs.forEach(config => {
|
||||||
initialData[config.config_key] = config.config_value;
|
initialData[config.config_key] = config.config_value;
|
||||||
});
|
});
|
||||||
setFormData(initialData);
|
|
||||||
form.setFieldsValue(initialData);
|
form.setFieldsValue(initialData);
|
||||||
}, [configs, form]);
|
}, [configs, form]);
|
||||||
|
|
||||||
// 处理表单值变化
|
// 处理表单值变化
|
||||||
const handleValuesChange = (changedValues: any, allValues: any) => {
|
const handleValuesChange = (_changedValues: Record<string, unknown>, allValues: Record<string, unknown>) => {
|
||||||
setFormData(allValues);
|
setFormData(allValues);
|
||||||
onConfigChange();
|
onConfigChange();
|
||||||
};
|
};
|
||||||
@@ -61,9 +67,10 @@ const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
|||||||
form.validateFields().then(() => {
|
form.validateFields().then(() => {
|
||||||
const saveRequests: SaveConfigRequest[] = configs.map(config => ({
|
const saveRequests: SaveConfigRequest[] = configs.map(config => ({
|
||||||
config_key: config.config_key,
|
config_key: config.config_key,
|
||||||
config_value: formData[config.config_key] || '',
|
config_value: String(formData[config.config_key] || ''),
|
||||||
config_label: config.config_label,
|
config_label: config.config_label,
|
||||||
config_group: config.config_group
|
config_group: config.config_group,
|
||||||
|
config_type: config.config_type
|
||||||
}));
|
}));
|
||||||
onSave(saveRequests);
|
onSave(saveRequests);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
@@ -155,7 +162,7 @@ const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
|||||||
style={{ marginBottom: '24px' }}
|
style={{ marginBottom: '24px' }}
|
||||||
>
|
>
|
||||||
<Row gutter={[24, 0]}>
|
<Row gutter={[24, 0]}>
|
||||||
{configItems.map((item, index) => {
|
{configItems.map((item) => {
|
||||||
const config = configs.find(c => c.config_key === item.key);
|
const config = configs.find(c => c.config_key === item.key);
|
||||||
if (!config) return null;
|
if (!config) return null;
|
||||||
|
|
||||||
@@ -218,11 +225,10 @@ const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
|||||||
|
|
||||||
{/* 输入控件 */}
|
{/* 输入控件 */}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={item.key}
|
|
||||||
rules={[
|
rules={[
|
||||||
{ required: item.required, message: `请输入${item.title}` },
|
{ required: item.required, message: `请输入${item.title}` },
|
||||||
...(item.key === 'admin_email' ? [
|
...(item.key === 'admin_email' ? [
|
||||||
{ type: 'email', message: '请输入有效的邮箱地址' }
|
{ type: 'email' as const, message: '请输入有效的邮箱地址' }
|
||||||
] : [])
|
] : [])
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -230,16 +236,33 @@ const BasicConfigTab: React.FC<BasicConfigTabProps> = ({
|
|||||||
<Input
|
<Input
|
||||||
placeholder={item.placeholder}
|
placeholder={item.placeholder}
|
||||||
disabled={config.config_type === 'boolean'}
|
disabled={config.config_type === 'boolean'}
|
||||||
|
value={String(formData[item.key] || '')}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setFieldValue(item.key, e.target.value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === 'textarea' && (
|
{item.type === 'textarea' && (
|
||||||
<TextArea
|
<TextArea
|
||||||
placeholder={item.placeholder}
|
placeholder={item.placeholder}
|
||||||
rows={item.rows || 2}
|
rows={item.rows || 2}
|
||||||
|
value={String(formData[item.key] || '')}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setFieldValue(item.key, e.target.value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === 'select' && (
|
{item.type === 'select' && (
|
||||||
<Select placeholder={item.placeholder}>
|
<Select
|
||||||
|
placeholder={item.placeholder}
|
||||||
|
value={String(formData[item.key] || '')}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(item.key, value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
|
>
|
||||||
{item.options?.map(option => (
|
{item.options?.map(option => (
|
||||||
<Option key={option.value} value={option.value}>
|
<Option key={option.value} value={option.value}>
|
||||||
{option.label}
|
{option.label}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Form, Input, InputNumber, Switch, Card, Button, Space, Row, Col, Tooltip, Alert } from 'antd';
|
import { Form, Input, InputNumber, Switch, Select, Card, Button, Space, Row, Col, Tooltip, Alert } from 'antd';
|
||||||
import {
|
import {
|
||||||
InfoCircleOutlined,
|
InfoCircleOutlined,
|
||||||
SaveOutlined,
|
SaveOutlined,
|
||||||
@@ -21,10 +21,10 @@ import { SystemConfig, SaveConfigRequest } from '../../types/systemConfig';
|
|||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
|
||||||
interface GameConfigTabProps {
|
interface GameConfigTabProps {
|
||||||
configs: SystemConfig[];
|
configs: SystemConfig[];
|
||||||
loading: boolean;
|
saving?: boolean;
|
||||||
saving: boolean;
|
|
||||||
onSave: (requests: SaveConfigRequest[]) => void;
|
onSave: (requests: SaveConfigRequest[]) => void;
|
||||||
onReset: (configKey: string) => void;
|
onReset: (configKey: string) => void;
|
||||||
onShowHistory: (configKey: string) => void;
|
onShowHistory: (configKey: string) => void;
|
||||||
@@ -33,7 +33,6 @@ interface GameConfigTabProps {
|
|||||||
|
|
||||||
const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
||||||
configs,
|
configs,
|
||||||
loading,
|
|
||||||
saving,
|
saving,
|
||||||
onSave,
|
onSave,
|
||||||
onReset,
|
onReset,
|
||||||
@@ -41,11 +40,11 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
onConfigChange
|
onConfigChange
|
||||||
}) => {
|
}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [formData, setFormData] = useState<Record<string, any>>({});
|
const [formData, setFormData] = useState<Record<string, unknown>>({});
|
||||||
|
|
||||||
// 初始化表单数据
|
// 初始化表单数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialData: Record<string, any> = {};
|
const initialData: Record<string, unknown> = {};
|
||||||
configs.forEach(config => {
|
configs.forEach(config => {
|
||||||
if (config.config_type === 'boolean') {
|
if (config.config_type === 'boolean') {
|
||||||
initialData[config.config_key] = config.config_value === '1' || config.config_value === 'true';
|
initialData[config.config_key] = config.config_value === '1' || config.config_value === 'true';
|
||||||
@@ -53,12 +52,17 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
initialData[config.config_key] = config.config_value;
|
initialData[config.config_key] = config.config_value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 延迟设置表单值,避免在渲染期间直接修改
|
||||||
|
const timer = setTimeout(() => {
|
||||||
setFormData(initialData);
|
setFormData(initialData);
|
||||||
form.setFieldsValue(initialData);
|
form.setFieldsValue(initialData);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}, [configs, form]);
|
}, [configs, form]);
|
||||||
|
|
||||||
// 处理表单值变化
|
// 处理表单值变化
|
||||||
const handleValuesChange = (changedValues: any, allValues: any) => {
|
const handleValuesChange = (changedValues: Record<string, unknown>, allValues: Record<string, unknown>) => {
|
||||||
setFormData(allValues);
|
setFormData(allValues);
|
||||||
onConfigChange();
|
onConfigChange();
|
||||||
};
|
};
|
||||||
@@ -69,8 +73,9 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
const saveRequests: SaveConfigRequest[] = configs.map(config => ({
|
const saveRequests: SaveConfigRequest[] = configs.map(config => ({
|
||||||
config_key: config.config_key,
|
config_key: config.config_key,
|
||||||
config_value: config.config_type === 'boolean'
|
config_value: config.config_type === 'boolean'
|
||||||
? (formData[config.config_key] ? '1' : '0')
|
? ((formData[config.config_key] as boolean) ? '1' : '0')
|
||||||
: String(formData[config.config_key] || ''),
|
: String(formData[config.config_key] || ''),
|
||||||
|
config_type: config.config_type,
|
||||||
config_label: config.config_label,
|
config_label: config.config_label,
|
||||||
config_group: config.config_group
|
config_group: config.config_group
|
||||||
}));
|
}));
|
||||||
@@ -92,8 +97,9 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
|
|
||||||
// 检查API地址格式
|
// 检查API地址格式
|
||||||
const validateApiUrl = (url: string) => {
|
const validateApiUrl = (url: string) => {
|
||||||
if (!url) return { valid: false, message: 'API地址不能为空' };
|
const urlStr = String(url || '');
|
||||||
if (!/^https?:\/\/.+/.test(url)) {
|
if (!urlStr) return { valid: false, message: 'API地址不能为空' };
|
||||||
|
if (!/^https?:\/\/.+/.test(urlStr)) {
|
||||||
return { valid: false, message: '请输入有效的HTTP/HTTPS地址' };
|
return { valid: false, message: '请输入有效的HTTP/HTTPS地址' };
|
||||||
}
|
}
|
||||||
return { valid: true, message: 'API地址格式正确' };
|
return { valid: true, message: 'API地址格式正确' };
|
||||||
@@ -101,10 +107,12 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
|
|
||||||
// 检查PSK密钥强度
|
// 检查PSK密钥强度
|
||||||
const checkPskStrength = (psk: string) => {
|
const checkPskStrength = (psk: string) => {
|
||||||
if (!psk) return { level: 'weak', message: 'PSK密钥不能为空' };
|
const pskStr = String(psk || '');
|
||||||
if (psk.length < 32) return { level: 'weak', message: 'PSK密钥至少需要32位字符' };
|
if (!pskStr) return { level: 'weak', message: 'PSK密钥不能为空' };
|
||||||
if (psk.length < 64) return { level: 'medium', message: 'PSK密钥长度适中,建议使用更长的密钥' };
|
if (pskStr.length < 8) return { level: 'weak', message: 'PSK密钥长度不足8位' };
|
||||||
return { level: 'strong', message: 'PSK密钥强度良好' };
|
if (pskStr.length < 32) return { level: 'medium', message: 'PSK密钥长度不足32位,建议使用更强的密钥' };
|
||||||
|
if (!/[!@#$%^&*(),.?":{}|<>]/.test(pskStr)) return { level: 'medium', message: 'PSK密钥缺少特殊字符,建议添加特殊字符' };
|
||||||
|
return { level: 'strong', message: 'PSK密钥强度符合要求' };
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiUrlValidation = validateApiUrl(formData.game_server_api || '');
|
const apiUrlValidation = validateApiUrl(formData.game_server_api || '');
|
||||||
@@ -223,7 +231,7 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
style={{ marginBottom: '24px' }}
|
style={{ marginBottom: '24px' }}
|
||||||
>
|
>
|
||||||
<Row gutter={[24, 0]}>
|
<Row gutter={[24, 0]}>
|
||||||
{configItems.map((item, index) => {
|
{configItems.map((item) => {
|
||||||
const config = configs.find(c => c.config_key === item.key);
|
const config = configs.find(c => c.config_key === item.key);
|
||||||
if (!config) return null;
|
if (!config) return null;
|
||||||
|
|
||||||
@@ -278,25 +286,55 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 输入控件 */}
|
{/* 输入控件 */}
|
||||||
|
{item.type === 'switch' ? (
|
||||||
|
// Switch组件不需要表单验证,直接渲染
|
||||||
|
<div style={{ marginTop: '8px' }}>
|
||||||
|
<Switch
|
||||||
|
checked={Boolean(formData[item.key])}
|
||||||
|
onChange={(checked) => {
|
||||||
|
form.setFieldValue(item.key, checked);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span style={{ marginLeft: '8px', fontSize: '12px', color: '#666' }}>
|
||||||
|
{String(formData[item.key] || '') ? '已启用' : '未启用'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// 其他输入控件使用Form.Item进行布局,但使用value/onChange进行状态管理
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={item.key}
|
|
||||||
rules={[
|
rules={[
|
||||||
{ required: item.required, message: `请输入${item.title}` },
|
{
|
||||||
|
required: item.required,
|
||||||
|
message: `请输入${item.title}`
|
||||||
|
},
|
||||||
...(item.key === 'game_server_api' ? [
|
...(item.key === 'game_server_api' ? [
|
||||||
{
|
{
|
||||||
pattern: /^https?:\/\/.+/,
|
validator: (_: unknown, value: string) => {
|
||||||
message: '请输入有效的HTTP/HTTPS地址'
|
if (!value || !/^https?:\/\/.+/.test(value)) {
|
||||||
|
return Promise.reject(new Error('请输入有效的API地址'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
] : []),
|
] : []),
|
||||||
...(item.key === 'game_server_psk' ? [
|
...(item.key === 'game_server_psk' ? [
|
||||||
{ min: 32, message: 'PSK密钥至少需要32位字符' }
|
{
|
||||||
|
min: 32,
|
||||||
|
message: 'PSK密钥至少需要32位字符'
|
||||||
|
}
|
||||||
] : [])
|
] : [])
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{item.type === 'input' && (
|
{item.type === 'input' && (
|
||||||
<Input
|
<Input
|
||||||
placeholder={item.placeholder}
|
placeholder={item.placeholder}
|
||||||
style={{ fontFamily: item.sensitive ? 'monospace' : 'inherit' }}
|
disabled={config.config_type === 'boolean'}
|
||||||
|
value={formData[item.key]}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setFieldValue(item.key, e.target.value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === 'textarea' && (
|
{item.type === 'textarea' && (
|
||||||
@@ -307,6 +345,11 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
showCount={!item.sensitive}
|
showCount={!item.sensitive}
|
||||||
autoSize={{ minRows: item.rows || 3, maxRows: 6 }}
|
autoSize={{ minRows: item.rows || 3, maxRows: 6 }}
|
||||||
style={{ fontFamily: item.sensitive ? 'monospace' : 'inherit' }}
|
style={{ fontFamily: item.sensitive ? 'monospace' : 'inherit' }}
|
||||||
|
value={formData[item.key]}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setFieldValue(item.key, e.target.value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === 'inputnumber' && (
|
{item.type === 'inputnumber' && (
|
||||||
@@ -316,36 +359,31 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
max={item.max}
|
max={item.max}
|
||||||
placeholder={item.placeholder}
|
placeholder={item.placeholder}
|
||||||
suffix={item.suffix}
|
suffix={item.suffix}
|
||||||
/>
|
value={formData[item.key]}
|
||||||
)}
|
onChange={(value) => {
|
||||||
{item.type === 'select' && (
|
form.setFieldValue(item.key, value);
|
||||||
<Input.Group compact>
|
|
||||||
<Input
|
|
||||||
style={{ width: 'calc(100% - 80px)' }}
|
|
||||||
placeholder={item.placeholder}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '80px' }}
|
|
||||||
placeholder="级别"
|
|
||||||
/>
|
|
||||||
</Input.Group>
|
|
||||||
)}
|
|
||||||
{item.type === 'switch' && (
|
|
||||||
<div style={{ marginTop: '8px' }}>
|
|
||||||
<Switch
|
|
||||||
checked={formData[item.key]}
|
|
||||||
onChange={(checked) => {
|
|
||||||
form.setFieldValue(item.key, checked);
|
|
||||||
onConfigChange();
|
onConfigChange();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ marginLeft: '8px', fontSize: '12px', color: '#666' }}>
|
)}
|
||||||
{formData[item.key] ? '已启用' : '未启用'}
|
{item.type === 'select' && (
|
||||||
</span>
|
<Select
|
||||||
</div>
|
placeholder={item.placeholder}
|
||||||
|
value={formData[item.key]}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(item.key, value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.options?.map((option, optionIndex) => (
|
||||||
|
<Select.Option key={`${item.key}-${option.value}-${optionIndex}`} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
)}
|
)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 特殊字段验证信息 */}
|
{/* 特殊字段验证信息 */}
|
||||||
{item.key === 'game_server_api' && formData.game_server_api && (
|
{item.key === 'game_server_api' && formData.game_server_api && (
|
||||||
@@ -362,7 +400,7 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
{apiUrlValidation.valid ? '✅ 格式正确' : '❌ 格式错误'}
|
{apiUrlValidation.valid ? '✅ 格式正确' : '❌ 格式错误'}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
协议:{formData.game_server_api.startsWith('https') ? 'HTTPS' : 'HTTP'}
|
协议:{String(formData.game_server_api || '').startsWith('https') ? 'HTTPS' : 'HTTP'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -377,11 +415,11 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
|
|||||||
}}>
|
}}>
|
||||||
<strong>密钥强度评估:</strong>
|
<strong>密钥强度评估:</strong>
|
||||||
<div style={{ marginTop: '4px' }}>
|
<div style={{ marginTop: '4px' }}>
|
||||||
长度: {formData.game_server_psk.length} 字符
|
长度: {String(formData.game_server_psk || '').length} 字符
|
||||||
{formData.game_server_psk.length >= 32 && ' ✅'}
|
{String(formData.game_server_psk || '').length >= 32 && ' ✅'}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
包含特殊字符: {/[!@#$%^&*(),.?":{}|<>]/.test(formData.game_server_psk) ? '✅' : '❌'}
|
包含特殊字符: {/[!@#$%^&*(),.?":{}|<>]/.test(String(formData.game_server_psk || '')) ? '✅' : '❌'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ type FormData = Record<string, FormFieldValue>;
|
|||||||
key: string;
|
key: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
type: 'input' | 'textarea' | 'inputnumber' | 'switch';
|
type: 'input' | 'textarea' | 'inputnumber' | 'switch' | 'select';
|
||||||
required: boolean;
|
required: boolean;
|
||||||
sensitive?: boolean;
|
sensitive?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@@ -48,10 +48,12 @@ type FormData = Record<string, FormFieldValue>;
|
|||||||
rows?: number;
|
rows?: number;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
|
options?: Array<{ value: string; label: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
||||||
configs,
|
configs,
|
||||||
|
// loading,
|
||||||
saving,
|
saving,
|
||||||
onSave,
|
onSave,
|
||||||
onReset,
|
onReset,
|
||||||
@@ -76,7 +78,7 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
|||||||
}, [configs, form]);
|
}, [configs, form]);
|
||||||
|
|
||||||
// 处理表单值变化
|
// 处理表单值变化
|
||||||
const handleValuesChange = (_changedValues: Partial<FormData>, allValues: FormData) => {
|
const handleValuesChange = (changedValues: Partial<FormData>, allValues: FormData) => {
|
||||||
setFormData(allValues);
|
setFormData(allValues);
|
||||||
onConfigChange();
|
onConfigChange();
|
||||||
};
|
};
|
||||||
@@ -291,12 +293,27 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 输入控件 */}
|
{/* 输入控件 */}
|
||||||
|
{item.type === 'switch' ? (
|
||||||
|
// Switch组件不需要表单验证,直接渲染
|
||||||
|
<div style={{ marginTop: '8px' }}>
|
||||||
|
<Switch
|
||||||
|
checked={Boolean(formData[item.key])}
|
||||||
|
onChange={(checked) => {
|
||||||
|
form.setFieldValue(item.key, checked);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span style={{ marginLeft: '8px', fontSize: '12px', color: '#666' }}>
|
||||||
|
{String(formData[item.key] || '') ? '已启用' : '未启用'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// 其他输入控件使用Form.Item进行布局,但使用value/onChange进行状态管理
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={item.key}
|
|
||||||
rules={[
|
rules={[
|
||||||
{ required: item.required, message: `请输入${item.title}` },
|
{ required: item.required, message: `请输入${item.title}` },
|
||||||
...(item.key === 'jwt_secret' ? [
|
...(item.key === 'jwt_secret' ? [
|
||||||
{ min: 32, message: 'JWT密钥至少需要32位字符' }
|
{ min: 32 as const, message: 'JWT密钥至少需要32位字符' }
|
||||||
] : [])
|
] : [])
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -305,6 +322,11 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
|||||||
placeholder={item.placeholder}
|
placeholder={item.placeholder}
|
||||||
disabled={config.config_type === 'boolean'}
|
disabled={config.config_type === 'boolean'}
|
||||||
visibilityToggle={!item.sensitive}
|
visibilityToggle={!item.sensitive}
|
||||||
|
value={formData[item.key]}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setFieldValue(item.key, e.target.value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === 'textarea' && (
|
{item.type === 'textarea' && (
|
||||||
@@ -315,6 +337,11 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
|||||||
showCount={!item.sensitive}
|
showCount={!item.sensitive}
|
||||||
autoSize={{ minRows: item.rows || 3, maxRows: 6 }}
|
autoSize={{ minRows: item.rows || 3, maxRows: 6 }}
|
||||||
style={{ fontFamily: item.sensitive ? 'monospace' : 'inherit' }}
|
style={{ fontFamily: item.sensitive ? 'monospace' : 'inherit' }}
|
||||||
|
value={formData[item.key]}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setFieldValue(item.key, e.target.value);
|
||||||
|
onConfigChange();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === 'inputnumber' && (
|
{item.type === 'inputnumber' && (
|
||||||
@@ -324,23 +351,15 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
|
|||||||
max={item.max}
|
max={item.max}
|
||||||
placeholder={item.placeholder}
|
placeholder={item.placeholder}
|
||||||
suffix={item.suffix}
|
suffix={item.suffix}
|
||||||
/>
|
value={formData[item.key]}
|
||||||
)}
|
onChange={(value) => {
|
||||||
{item.type === 'switch' && (
|
form.setFieldValue(item.key, value);
|
||||||
<div style={{ marginTop: '8px' }}>
|
|
||||||
<Switch
|
|
||||||
checked={Boolean(formData[item.key])}
|
|
||||||
onChange={(checked) => {
|
|
||||||
form.setFieldValue(item.key, checked);
|
|
||||||
onConfigChange();
|
onConfigChange();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ marginLeft: '8px', fontSize: '12px', color: '#666' }}>
|
|
||||||
{formData[item.key] ? '已启用' : '未启用'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* JWT密钥特殊提示 */}
|
{/* JWT密钥特殊提示 */}
|
||||||
{item.key === 'jwt_secret' && formData.jwt_secret && (
|
{item.key === 'jwt_secret' && formData.jwt_secret && (
|
||||||
|
|||||||
Reference in New Issue
Block a user