Files
makemd/dashboard/src/pages/Settings/index.tsx
wurenzhi 0dac26d781 feat: 添加MSW模拟服务和数据源集成
refactor: 重构页面组件移除冗余Layout组件

feat: 实现WebSocket和事件总线系统

feat: 添加队列和调度系统

docs: 更新架构文档和服务映射

style: 清理重复接口定义使用数据源

chore: 更新依赖项配置

feat: 添加运行时系统和领域引导

ci: 配置ESLint边界检查规则

build: 添加Redis和WebSocket依赖

test: 添加MSW浏览器环境入口

perf: 优化数据获取逻辑使用统一数据源

fix: 修复类型定义和状态管理问题
2026-03-19 01:39:34 +08:00

1192 lines
37 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import {
Card,
Row,
Col,
Button,
Tabs,
Form,
Input,
Switch,
Select,
Avatar,
Upload,
message,
Divider,
Typography,
Space,
Tag,
Table,
Modal,
Descriptions,
List,
Badge,
Popconfirm,
TimePicker,
Checkbox,
Radio,
Slider,
Tooltip,
Alert,
} from 'antd';
import {
UserOutlined,
TeamOutlined,
SettingOutlined,
LockOutlined,
BellOutlined,
GlobalOutlined,
SafetyOutlined,
UploadOutlined,
EditOutlined,
DeleteOutlined,
PlusOutlined,
KeyOutlined,
MailOutlined,
MobileOutlined,
DesktopOutlined,
ApiOutlined,
DatabaseOutlined,
CloudOutlined,
FileTextOutlined,
QuestionCircleOutlined,
SaveOutlined,
ReloadOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import moment from 'moment';
const { Title, Text, Paragraph } = Typography;
const { TabPane } = Tabs;
const { Option } = Select;
const { TextArea } = Input;
// 用户设置接口
interface UserProfile {
id: string;
username: string;
email: string;
phone: string;
avatar: string;
role: string;
department: string;
timezone: string;
language: string;
createdAt: string;
lastLoginAt: string;
}
// 通知设置接口
interface NotificationSettings {
emailEnabled: boolean;
smsEnabled: boolean;
pushEnabled: boolean;
orderAlerts: boolean;
inventoryAlerts: boolean;
complianceAlerts: boolean;
systemAlerts: boolean;
marketingEmails: boolean;
digestFrequency: 'realtime' | 'hourly' | 'daily' | 'weekly';
quietHoursStart: string;
quietHoursEnd: string;
}
// 安全设置接口
interface SecuritySettings {
twoFactorEnabled: boolean;
loginNotification: boolean;
passwordExpiryDays: number;
sessionTimeout: number;
ipWhitelist: string[];
apiKey: string;
lastPasswordChange: string;
}
// 系统设置接口
interface SystemSettings {
autoSync: boolean;
syncInterval: number;
dataRetentionDays: number;
backupEnabled: boolean;
backupFrequency: 'daily' | 'weekly' | 'monthly';
logLevel: 'debug' | 'info' | 'warn' | 'error';
theme: 'light' | 'dark' | 'auto';
dateFormat: string;
currency: string;
}
// API密钥接口
interface ApiKey {
id: string;
name: string;
key: string;
permissions: string[];
createdAt: string;
lastUsedAt: string;
status: 'active' | 'inactive';
}
// 操作日志接口
interface OperationLog {
id: string;
userId: string;
username: string;
action: string;
module: string;
details: string;
ip: string;
userAgent: string;
createdAt: string;
}
const MOCK_USER_PROFILE: UserProfile = {
id: '1',
username: 'admin',
email: 'admin@crawlful.com',
phone: '+86 13800138000',
avatar: '',
role: 'ADMIN',
department: '技术部',
timezone: 'Asia/Shanghai',
language: 'zh-CN',
createdAt: '2025-01-15',
lastLoginAt: '2026-03-18 10:30:00',
};
const MOCK_NOTIFICATION_SETTINGS: NotificationSettings = {
emailEnabled: true,
smsEnabled: false,
pushEnabled: true,
orderAlerts: true,
inventoryAlerts: true,
complianceAlerts: true,
systemAlerts: true,
marketingEmails: false,
digestFrequency: 'daily',
quietHoursStart: '22:00',
quietHoursEnd: '08:00',
};
const MOCK_SECURITY_SETTINGS: SecuritySettings = {
twoFactorEnabled: false,
loginNotification: true,
passwordExpiryDays: 90,
sessionTimeout: 30,
ipWhitelist: [],
apiKey: 'sk_live_xxxxxxxxxxxxxxxx',
lastPasswordChange: '2026-01-01',
};
const MOCK_SYSTEM_SETTINGS: SystemSettings = {
autoSync: true,
syncInterval: 15,
dataRetentionDays: 365,
backupEnabled: true,
backupFrequency: 'daily',
logLevel: 'info',
theme: 'light',
dateFormat: 'YYYY-MM-DD',
currency: 'CNY',
};
const MOCK_API_KEYS: ApiKey[] = [
{
id: '1',
name: 'Production API Key',
key: 'sk_live_prod_xxxxxxxx',
permissions: ['read', 'write'],
createdAt: '2026-01-01',
lastUsedAt: '2026-03-18 09:00:00',
status: 'active',
},
{
id: '2',
name: 'Test API Key',
key: 'sk_test_xxxxxxxx',
permissions: ['read'],
createdAt: '2026-02-01',
lastUsedAt: '2026-03-17 15:30:00',
status: 'active',
},
];
const MOCK_OPERATION_LOGS: OperationLog[] = [
{
id: '1',
userId: '1',
username: 'admin',
action: 'LOGIN',
module: 'AUTH',
details: '用户登录成功',
ip: '192.168.1.100',
userAgent: 'Mozilla/5.0',
createdAt: '2026-03-18 10:30:00',
},
{
id: '2',
userId: '1',
username: 'admin',
action: 'UPDATE_PRODUCT',
module: 'PRODUCT',
details: '更新产品信息: P001',
ip: '192.168.1.100',
userAgent: 'Mozilla/5.0',
createdAt: '2026-03-18 10:35:00',
},
{
id: '3',
userId: '1',
username: 'admin',
action: 'CREATE_ORDER',
module: 'ORDER',
details: '创建订单: ORD-2026-001',
ip: '192.168.1.100',
userAgent: 'Mozilla/5.0',
createdAt: '2026-03-18 11:00:00',
},
];
const Settings: React.FC = () => {
const [activeTab, setActiveTab] = useState('profile');
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
// 表单实例
const [profileForm] = Form.useForm();
const [notificationForm] = Form.useForm();
const [securityForm] = Form.useForm();
const [systemForm] = Form.useForm();
const [passwordForm] = Form.useForm();
const [apiKeyForm] = Form.useForm();
// 数据状态
const [userProfile, setUserProfile] = useState<UserProfile>(MOCK_USER_PROFILE);
const [notificationSettings, setNotificationSettings] = useState<NotificationSettings>(MOCK_NOTIFICATION_SETTINGS);
const [securitySettings, setSecuritySettings] = useState<SecuritySettings>(MOCK_SECURITY_SETTINGS);
const [systemSettings, setSystemSettings] = useState<SystemSettings>(MOCK_SYSTEM_SETTINGS);
const [apiKeys, setApiKeys] = useState<ApiKey[]>(MOCK_API_KEYS);
const [operationLogs, setOperationLogs] = useState<OperationLog[]>(MOCK_OPERATION_LOGS);
// 模态框状态
const [passwordModalVisible, setPasswordModalVisible] = useState(false);
const [apiKeyModalVisible, setApiKeyModalVisible] = useState(false);
const [twoFactorModalVisible, setTwoFactorModalVisible] = useState(false);
useEffect(() => {
// 初始化表单值
profileForm.setFieldsValue(userProfile);
notificationForm.setFieldsValue(notificationSettings);
securityForm.setFieldsValue(securitySettings);
systemForm.setFieldsValue(systemSettings);
}, []);
// ==================== 个人资料 ====================
const handleProfileSave = async () => {
try {
setSaving(true);
const values = await profileForm.validateFields();
await new Promise(resolve => setTimeout(resolve, 500));
setUserProfile({ ...userProfile, ...values });
message.success('个人资料已保存');
} catch (error) {
console.error('Save failed:', error);
} finally {
setSaving(false);
}
};
const handleAvatarUpload = (info: any) => {
if (info.file.status === 'done') {
message.success('头像上传成功');
}
};
// ==================== 通知设置 ====================
const handleNotificationSave = async () => {
try {
setSaving(true);
const values = await notificationForm.validateFields();
await new Promise(resolve => setTimeout(resolve, 500));
setNotificationSettings(values);
message.success('通知设置已保存');
} catch (error) {
console.error('Save failed:', error);
} finally {
setSaving(false);
}
};
// ==================== 安全设置 ====================
const handlePasswordChange = async () => {
try {
const values = await passwordForm.validateFields();
if (values.newPassword !== values.confirmPassword) {
message.error('两次输入的密码不一致');
return;
}
await new Promise(resolve => setTimeout(resolve, 500));
message.success('密码修改成功');
setPasswordModalVisible(false);
passwordForm.resetFields();
} catch (error) {
console.error('Password change failed:', error);
}
};
const handleTwoFactorToggle = async (enabled: boolean) => {
if (enabled) {
setTwoFactorModalVisible(true);
} else {
setSecuritySettings({ ...securitySettings, twoFactorEnabled: false });
message.success('双因素认证已关闭');
}
};
const handleSecuritySave = async () => {
try {
setSaving(true);
const values = await securityForm.validateFields();
await new Promise(resolve => setTimeout(resolve, 500));
setSecuritySettings({ ...securitySettings, ...values });
message.success('安全设置已保存');
} catch (error) {
console.error('Save failed:', error);
} finally {
setSaving(false);
}
};
// ==================== API密钥管理 ====================
const handleCreateApiKey = async () => {
try {
const values = await apiKeyForm.validateFields();
const newKey: ApiKey = {
id: `${Date.now()}`,
name: values.name,
key: `sk_${values.environment}_${Math.random().toString(36).substring(2, 15)}`,
permissions: values.permissions,
createdAt: moment().format('YYYY-MM-DD'),
lastUsedAt: '-',
status: 'active',
};
setApiKeys([newKey, ...apiKeys]);
setApiKeyModalVisible(false);
apiKeyForm.resetFields();
message.success('API密钥创建成功');
} catch (error) {
console.error('Create API key failed:', error);
}
};
const handleDeleteApiKey = (id: string) => {
setApiKeys(apiKeys.filter(key => key.id !== id));
message.success('API密钥已删除');
};
const handleCopyApiKey = (key: string) => {
navigator.clipboard.writeText(key);
message.success('API密钥已复制到剪贴板');
};
// ==================== 系统设置 ====================
const handleSystemSave = async () => {
try {
setSaving(true);
const values = await systemForm.validateFields();
await new Promise(resolve => setTimeout(resolve, 500));
setSystemSettings(values);
message.success('系统设置已保存');
} catch (error) {
console.error('Save failed:', error);
} finally {
setSaving(false);
}
};
// ==================== 表格列定义 ====================
const apiKeyColumns: ColumnsType<ApiKey> = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '密钥',
dataIndex: 'key',
key: 'key',
render: (key) => (
<Space>
<Text copyable={{ text: key }}>{key.substring(0, 20)}...</Text>
</Space>
),
},
{
title: '权限',
dataIndex: 'permissions',
key: 'permissions',
render: (permissions) => (
<Space>
{permissions.map((perm: string) => (
<Tag key={perm} color={perm === 'write' ? 'blue' : 'green'}>
{perm === 'read' ? '读取' : '写入'}
</Tag>
))}
</Space>
),
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status) => (
<Badge status={status === 'active' ? 'success' : 'default'} text={status === 'active' ? '活跃' : '已停用'} />
),
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
},
{
title: '最后使用',
dataIndex: 'lastUsedAt',
key: 'lastUsedAt',
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space size="small">
<Button type="link" size="small" onClick={() => handleCopyApiKey(record.key)}>
</Button>
<Popconfirm
title="确认删除"
description="确定要删除此API密钥吗此操作不可恢复。"
onConfirm={() => handleDeleteApiKey(record.id)}
okText="删除"
cancelText="取消"
>
<Button type="link" size="small" danger>
</Button>
</Popconfirm>
</Space>
),
},
];
const logColumns: ColumnsType<OperationLog> = [
{
title: '时间',
dataIndex: 'createdAt',
key: 'createdAt',
width: 160,
},
{
title: '用户',
dataIndex: 'username',
key: 'username',
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
render: (action) => {
const actionMap: Record<string, { color: string; text: string }> = {
LOGIN: { color: 'green', text: '登录' },
LOGOUT: { color: 'default', text: '登出' },
CREATE: { color: 'blue', text: '创建' },
UPDATE: { color: 'orange', text: '更新' },
DELETE: { color: 'red', text: '删除' },
};
const config = actionMap[action] || { color: 'default', text: action };
return <Tag color={config.color}>{config.text}</Tag>;
},
},
{
title: '模块',
dataIndex: 'module',
key: 'module',
},
{
title: '详情',
dataIndex: 'details',
key: 'details',
},
{
title: 'IP地址',
dataIndex: 'ip',
key: 'ip',
},
];
return (
<div className="settings-page" style={{ padding: 24 }}>
<div style={{ marginBottom: 24 }}>
<Title level={4}></Title>
<Text type="secondary"></Text>
</div>
<Tabs activeKey={activeTab} onChange={setActiveTab} type="card">
{/* 个人资料 */}
<TabPane
tab={
<span>
<UserOutlined />
</span>
}
key="profile"
>
<Card>
<Row gutter={24}>
<Col span={8} style={{ textAlign: 'center' }}>
<Avatar size={120} icon={<UserOutlined />} src={userProfile.avatar} />
<div style={{ marginTop: 16 }}>
<Upload
name="avatar"
showUploadList={false}
onChange={handleAvatarUpload}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</div>
<Divider />
<Descriptions column={1} size="small">
<Descriptions.Item label="用户ID">{userProfile.id}</Descriptions.Item>
<Descriptions.Item label="角色">
<Tag color="blue">{userProfile.role}</Tag>
</Descriptions.Item>
<Descriptions.Item label="部门">{userProfile.department}</Descriptions.Item>
<Descriptions.Item label="注册时间">{userProfile.createdAt}</Descriptions.Item>
<Descriptions.Item label="最后登录">{userProfile.lastLoginAt}</Descriptions.Item>
</Descriptions>
</Col>
<Col span={16}>
<Form form={profileForm} layout="vertical">
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="username"
label="用户名"
rules={[{ required: true, message: '请输入用户名' }]}
>
<Input prefix={<UserOutlined />} placeholder="用户名" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="email"
label="邮箱"
rules={[
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入有效的邮箱地址' },
]}
>
<Input prefix={<MailOutlined />} placeholder="邮箱" />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="phone"
label="手机号"
rules={[{ required: true, message: '请输入手机号' }]}
>
<Input prefix={<MobileOutlined />} placeholder="手机号" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="department"
label="部门"
>
<Input placeholder="部门" />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="timezone"
label="时区"
>
<Select placeholder="选择时区">
<Option value="Asia/Shanghai">Asia/Shanghai (GMT+8)</Option>
<Option value="Asia/Tokyo">Asia/Tokyo (GMT+9)</Option>
<Option value="America/New_York">America/New_York (GMT-5)</Option>
<Option value="Europe/London">Europe/London (GMT+0)</Option>
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="language"
label="语言"
>
<Select placeholder="选择语言">
<Option value="zh-CN"></Option>
<Option value="en-US">English</Option>
<Option value="ja-JP"></Option>
</Select>
</Form.Item>
</Col>
</Row>
<Form.Item>
<Button type="primary" icon={<SaveOutlined />} onClick={handleProfileSave} loading={saving}>
</Button>
</Form.Item>
</Form>
</Col>
</Row>
</Card>
</TabPane>
{/* 通知设置 */}
<TabPane
tab={
<span>
<BellOutlined />
</span>
}
key="notifications"
>
<Card>
<Form form={notificationForm} layout="vertical">
<Title level={5}></Title>
<Row gutter={16}>
<Col span={8}>
<Card size="small">
<Form.Item name="emailEnabled" valuePropName="checked" noStyle>
<Switch />
</Form.Item>
<div style={{ marginTop: 8 }}>
<MailOutlined style={{ fontSize: 24, color: '#1890ff' }} />
<div style={{ marginTop: 8 }}>
<Text strong></Text>
<div><Text type="secondary"></Text></div>
</div>
</div>
</Card>
</Col>
<Col span={8}>
<Card size="small">
<Form.Item name="smsEnabled" valuePropName="checked" noStyle>
<Switch />
</Form.Item>
<div style={{ marginTop: 8 }}>
<MobileOutlined style={{ fontSize: 24, color: '#52c41a' }} />
<div style={{ marginTop: 8 }}>
<Text strong></Text>
<div><Text type="secondary"></Text></div>
</div>
</div>
</Card>
</Col>
<Col span={8}>
<Card size="small">
<Form.Item name="pushEnabled" valuePropName="checked" noStyle>
<Switch />
</Form.Item>
<div style={{ marginTop: 8 }}>
<DesktopOutlined style={{ fontSize: 24, color: '#faad14' }} />
<div style={{ marginTop: 8 }}>
<Text strong></Text>
<div><Text type="secondary"></Text></div>
</div>
</div>
</Card>
</Col>
</Row>
<Divider />
<Title level={5}></Title>
<Form.Item name="orderAlerts" valuePropName="checked">
<Checkbox> - </Checkbox>
</Form.Item>
<Form.Item name="inventoryAlerts" valuePropName="checked">
<Checkbox> - </Checkbox>
</Form.Item>
<Form.Item name="complianceAlerts" valuePropName="checked">
<Checkbox> - </Checkbox>
</Form.Item>
<Form.Item name="systemAlerts" valuePropName="checked">
<Checkbox> - </Checkbox>
</Form.Item>
<Form.Item name="marketingEmails" valuePropName="checked">
<Checkbox> - </Checkbox>
</Form.Item>
<Divider />
<Title level={5}></Title>
<Row gutter={16}>
<Col span={12}>
<Form.Item name="digestFrequency" label="通知摘要频率">
<Select>
<Option value="realtime"></Option>
<Option value="hourly"></Option>
<Option value="daily"></Option>
<Option value="weekly"></Option>
</Select>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item name="quietHoursStart" label="免打扰开始时间">
<TimePicker format="HH:mm" style={{ width: '100%' }} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="quietHoursEnd" label="免打扰结束时间">
<TimePicker format="HH:mm" style={{ width: '100%' }} />
</Form.Item>
</Col>
</Row>
<Form.Item>
<Button type="primary" icon={<SaveOutlined />} onClick={handleNotificationSave} loading={saving}>
</Button>
</Form.Item>
</Form>
</Card>
</TabPane>
{/* 安全设置 */}
<TabPane
tab={
<span>
<SafetyOutlined />
</span>
}
key="security"
>
<Card>
<List>
<List.Item
actions={[
<Button type="primary" onClick={() => setPasswordModalVisible(true)}>
</Button>,
]}
>
<List.Item.Meta
avatar={<LockOutlined style={{ fontSize: 24, color: '#1890ff' }} />}
title="登录密码"
description={`上次修改: ${securitySettings.lastPasswordChange}`}
/>
</List.Item>
<List.Item
actions={[
<Switch
checked={securitySettings.twoFactorEnabled}
onChange={handleTwoFactorToggle}
/>,
]}
>
<List.Item.Meta
avatar={<SafetyOutlined style={{ fontSize: 24, color: '#52c41a' }} />}
title="双因素认证 (2FA)"
description="启用后,登录时需要输入手机验证码"
/>
</List.Item>
<List.Item
actions={[
<Switch
checked={securitySettings.loginNotification}
onChange={(checked) => setSecuritySettings({ ...securitySettings, loginNotification: checked })}
/>,
]}
>
<List.Item.Meta
avatar={<BellOutlined style={{ fontSize: 24, color: '#faad14' }} />}
title="登录通知"
description="新设备登录时发送通知"
/>
</List.Item>
</List>
<Divider />
<Form form={securityForm} layout="vertical">
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="passwordExpiryDays"
label="密码过期天数"
tooltip="密码将在指定天数后过期,需要重新设置"
>
<Slider min={30} max={180} marks={{ 30: '30天', 90: '90天', 180: '180天' }} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="sessionTimeout"
label="会话超时时间(分钟)"
tooltip="超过指定时间无操作将自动登出"
>
<Slider min={5} max={120} marks={{ 15: '15分', 30: '30分', 60: '60分' }} />
</Form.Item>
</Col>
</Row>
<Form.Item>
<Button type="primary" icon={<SaveOutlined />} onClick={handleSecuritySave} loading={saving}>
</Button>
</Form.Item>
</Form>
</Card>
</TabPane>
{/* API密钥 */}
<TabPane
tab={
<span>
<ApiOutlined />
API密钥
</span>
}
key="apikeys"
>
<Card
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={() => setApiKeyModalVisible(true)}>
API密钥
</Button>
}
>
<Alert
message="API密钥安全提示"
description="请妥善保管您的API密钥不要在客户端代码中暴露。如怀疑密钥泄露请立即删除并重新创建。"
type="warning"
showIcon
style={{ marginBottom: 16 }}
/>
<Table
columns={apiKeyColumns}
dataSource={apiKeys}
rowKey="id"
pagination={{ pageSize: 5 }}
/>
</Card>
</TabPane>
{/* 系统设置 */}
<TabPane
tab={
<span>
<SettingOutlined />
</span>
}
key="system"
>
<Card>
<Form form={systemForm} layout="vertical">
<Title level={5}></Title>
<Row gutter={16}>
<Col span={12}>
<Form.Item name="autoSync" valuePropName="checked" label="自动同步">
<Switch />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="syncInterval" label="同步间隔(分钟)">
<Select>
<Option value={5}>5</Option>
<Option value={15}>15</Option>
<Option value={30}>30</Option>
<Option value={60}>1</Option>
</Select>
</Form.Item>
</Col>
</Row>
<Divider />
<Title level={5}></Title>
<Row gutter={16}>
<Col span={12}>
<Form.Item name="dataRetentionDays" label="数据保留天数">
<Slider min={30} max={1095} marks={{ 90: '3月', 365: '1年', 730: '2年', 1095: '3年' }} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="backupEnabled" valuePropName="checked" label="启用自动备份">
<Switch />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item name="backupFrequency" label="备份频率">
<Select>
<Option value="daily"></Option>
<Option value="weekly"></Option>
<Option value="monthly"></Option>
</Select>
</Form.Item>
</Col>
</Row>
<Divider />
<Title level={5}></Title>
<Row gutter={16}>
<Col span={8}>
<Form.Item name="theme" label="主题">
<Select>
<Option value="light"></Option>
<Option value="dark"></Option>
<Option value="auto"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="dateFormat" label="日期格式">
<Select>
<Option value="YYYY-MM-DD">YYYY-MM-DD</Option>
<Option value="DD/MM/YYYY">DD/MM/YYYY</Option>
<Option value="MM/DD/YYYY">MM/DD/YYYY</Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="currency" label="货币">
<Select>
<Option value="CNY"> (CNY)</Option>
<Option value="USD"> (USD)</Option>
<Option value="EUR"> (EUR)</Option>
<Option value="JPY"> (JPY)</Option>
</Select>
</Form.Item>
</Col>
</Row>
<Divider />
<Title level={5}></Title>
<Form.Item name="logLevel" label="日志级别">
<Radio.Group>
<Radio.Button value="debug"></Radio.Button>
<Radio.Button value="info"></Radio.Button>
<Radio.Button value="warn"></Radio.Button>
<Radio.Button value="error"></Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item>
<Button type="primary" icon={<SaveOutlined />} onClick={handleSystemSave} loading={saving}>
</Button>
</Form.Item>
</Form>
</Card>
</TabPane>
{/* 操作日志 */}
<TabPane
tab={
<span>
<FileTextOutlined />
</span>
}
key="logs"
>
<Card>
<Table
columns={logColumns}
dataSource={operationLogs}
rowKey="id"
pagination={{ pageSize: 10 }}
/>
</Card>
</TabPane>
{/* 配置管理 */}
<TabPane
tab={
<span>
<SettingOutlined />
</span>
}
key="configs"
>
<Row gutter={[16, 16]}>
<Col span={12}>
<Card
hoverable
onClick={() => window.location.href = '/Settings/PlatformAccountConfig'}
>
<div style={{ textAlign: 'center' }}>
<ApiOutlined style={{ fontSize: 48, color: '#1890ff' }} />
<Title level={4} style={{ marginTop: 16 }}></Title>
<Text type="secondary">API账号和授权信息</Text>
</div>
</Card>
</Col>
<Col span={12}>
<Card
hoverable
onClick={() => window.location.href = '/Settings/ExchangeRateConfig'}
>
<div style={{ textAlign: 'center' }}>
<GlobalOutlined style={{ fontSize: 48, color: '#52c41a' }} />
<Title level={4} style={{ marginTop: 16 }}></Title>
<Text type="secondary"></Text>
</div>
</Card>
</Col>
<Col span={12}>
<Card
hoverable
onClick={() => window.location.href = '/Settings/CostTemplateConfig'}
>
<div style={{ textAlign: 'center' }}>
<DollarOutlined style={{ fontSize: 48, color: '#faad14' }} />
<Title level={4} style={{ marginTop: 16 }}></Title>
<Text type="secondary"></Text>
</div>
</Card>
</Col>
<Col span={12}>
<Card
hoverable
onClick={() => window.location.href = '/Settings/WinNodeConfig'}
>
<div style={{ textAlign: 'center' }}>
<DesktopOutlined style={{ fontSize: 48, color: '#722ed1' }} />
<Title level={4} style={{ marginTop: 16 }}>WinNode配置</Title>
<Text type="secondary">Windows执行节点和浏览器实例</Text>
</div>
</Card>
</Col>
</Row>
</TabPane>
</Tabs>
{/* 修改密码模态框 */}
<Modal
title="修改密码"
open={passwordModalVisible}
onOk={handlePasswordChange}
onCancel={() => {
setPasswordModalVisible(false);
passwordForm.resetFields();
}}
>
<Form form={passwordForm} layout="vertical">
<Form.Item
name="currentPassword"
label="当前密码"
rules={[{ required: true, message: '请输入当前密码' }]}
>
<Input.Password placeholder="当前密码" />
</Form.Item>
<Form.Item
name="newPassword"
label="新密码"
rules={[
{ required: true, message: '请输入新密码' },
{ min: 8, message: '密码至少8位' },
]}
>
<Input.Password placeholder="新密码" />
</Form.Item>
<Form.Item
name="confirmPassword"
label="确认新密码"
rules={[{ required: true, message: '请确认新密码' }]}
>
<Input.Password placeholder="确认新密码" />
</Form.Item>
</Form>
</Modal>
{/* 创建API密钥模态框 */}
<Modal
title="创建API密钥"
open={apiKeyModalVisible}
onOk={handleCreateApiKey}
onCancel={() => {
setApiKeyModalVisible(false);
apiKeyForm.resetFields();
}}
>
<Form form={apiKeyForm} layout="vertical">
<Form.Item
name="name"
label="密钥名称"
rules={[{ required: true, message: '请输入密钥名称' }]}
>
<Input placeholder="例如Production API Key" />
</Form.Item>
<Form.Item
name="environment"
label="环境"
rules={[{ required: true, message: '请选择环境' }]}
>
<Select placeholder="选择环境">
<Option value="live"></Option>
<Option value="test"></Option>
</Select>
</Form.Item>
<Form.Item
name="permissions"
label="权限"
rules={[{ required: true, message: '请选择权限' }]}
>
<Checkbox.Group>
<Checkbox value="read"></Checkbox>
<Checkbox value="write"></Checkbox>
</Checkbox.Group>
</Form.Item>
</Form>
</Modal>
{/* 双因素认证模态框 */}
<Modal
title="启用双因素认证"
open={twoFactorModalVisible}
onOk={() => {
setSecuritySettings({ ...securitySettings, twoFactorEnabled: true });
setTwoFactorModalVisible(false);
message.success('双因素认证已启用');
}}
onCancel={() => setTwoFactorModalVisible(false)}
>
<div style={{ textAlign: 'center' }}>
<SafetyOutlined style={{ fontSize: 48, color: '#52c41a' }} />
<p style={{ marginTop: 16 }}>
</p>
<p>
</p>
</div>
</Modal>
</div>
);
};
export default Settings;