first commit
This commit is contained in:
421
frontend/src/pages/Settings/Settings.tsx
Normal file
421
frontend/src/pages/Settings/Settings.tsx
Normal file
@@ -0,0 +1,421 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
Layout,
|
||||
Menu,
|
||||
Card,
|
||||
Row,
|
||||
Col,
|
||||
Typography,
|
||||
Button,
|
||||
Form,
|
||||
Input,
|
||||
Switch,
|
||||
Select,
|
||||
Divider,
|
||||
message,
|
||||
Spin,
|
||||
Tabs,
|
||||
InputNumber,
|
||||
} from 'antd'
|
||||
import {
|
||||
SettingOutlined,
|
||||
DashboardOutlined,
|
||||
DatabaseOutlined,
|
||||
UserOutlined,
|
||||
BellOutlined,
|
||||
SafetyOutlined,
|
||||
SaveOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { useAuthStore } from '../../stores/auth'
|
||||
|
||||
const { Header, Sider, Content } = Layout
|
||||
const { Title, Text } = Typography
|
||||
const { TabPane } = Tabs
|
||||
|
||||
interface SystemSettings {
|
||||
system_name: string
|
||||
refresh_interval: number
|
||||
auto_refresh: boolean
|
||||
data_retention_days: number
|
||||
max_concurrent_tasks: number
|
||||
}
|
||||
|
||||
interface NotificationSettings {
|
||||
email_enabled: boolean
|
||||
email_address: string
|
||||
critical_alerts: boolean
|
||||
warning_alerts: boolean
|
||||
daily_summary: boolean
|
||||
}
|
||||
|
||||
interface SecuritySettings {
|
||||
session_timeout: number
|
||||
max_login_attempts: number
|
||||
password_policy: string
|
||||
}
|
||||
|
||||
function Settings() {
|
||||
const { user, logout, token, clearAuth } = useAuthStore()
|
||||
const navigate = useNavigate()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [systemSettings, setSystemSettings] = useState<SystemSettings>({
|
||||
system_name: '智能星球',
|
||||
refresh_interval: 60,
|
||||
auto_refresh: true,
|
||||
data_retention_days: 30,
|
||||
max_concurrent_tasks: 5,
|
||||
})
|
||||
const [notificationSettings, setNotificationSettings] = useState<NotificationSettings>({
|
||||
email_enabled: false,
|
||||
email_address: '',
|
||||
critical_alerts: true,
|
||||
warning_alerts: true,
|
||||
daily_summary: false,
|
||||
})
|
||||
const [securitySettings, setSecuritySettings] = useState<SecuritySettings>({
|
||||
session_timeout: 60,
|
||||
max_login_attempts: 5,
|
||||
password_policy: 'medium',
|
||||
})
|
||||
const [form] = Form.useForm()
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
navigate('/')
|
||||
return
|
||||
}
|
||||
fetchSettings()
|
||||
}, [token, navigate])
|
||||
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const res = await fetch('/api/v1/settings/system', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
if (res.status === 401) {
|
||||
clearAuth()
|
||||
navigate('/')
|
||||
return
|
||||
}
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setSystemSettings(data.system || systemSettings)
|
||||
setNotificationSettings(data.notifications || notificationSettings)
|
||||
setSecuritySettings(data.security || securitySettings)
|
||||
form.setFieldsValue({
|
||||
...data.system,
|
||||
...data.notifications,
|
||||
...data.security,
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
message.error('获取设置失败')
|
||||
console.error(err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveSystem = async (values: any) => {
|
||||
try {
|
||||
setSaving(true)
|
||||
const res = await fetch('/api/v1/settings/system', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(values),
|
||||
})
|
||||
if (res.ok) {
|
||||
message.success('系统设置已保存')
|
||||
setSystemSettings(values)
|
||||
} else {
|
||||
message.error('保存失败')
|
||||
}
|
||||
} catch (err) {
|
||||
message.error('保存设置失败')
|
||||
console.error(err)
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveNotifications = async (values: any) => {
|
||||
try {
|
||||
setSaving(true)
|
||||
const res = await fetch('/api/v1/settings/notifications', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(values),
|
||||
})
|
||||
if (res.ok) {
|
||||
message.success('通知设置已保存')
|
||||
setNotificationSettings(values)
|
||||
} else {
|
||||
message.error('保存失败')
|
||||
}
|
||||
} catch (err) {
|
||||
message.error('保存设置失败')
|
||||
console.error(err)
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveSecurity = async (values: any) => {
|
||||
try {
|
||||
setSaving(true)
|
||||
const res = await fetch('/api/v1/settings/security', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(values),
|
||||
})
|
||||
if (res.ok) {
|
||||
message.success('安全设置已保存')
|
||||
setSecuritySettings(values)
|
||||
} else {
|
||||
message.error('保存失败')
|
||||
}
|
||||
} catch (err) {
|
||||
message.error('保存设置失败')
|
||||
console.error(err)
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
logout()
|
||||
navigate('/')
|
||||
}
|
||||
|
||||
const menuItems = [
|
||||
{ key: '/', icon: <DashboardOutlined />, label: <Link to="/">仪表盘</Link> },
|
||||
{ key: '/datasources', icon: <DatabaseOutlined />, label: <Link to="/datasources">数据源</Link> },
|
||||
{ key: '/users', icon: <UserOutlined />, label: <Link to="/users">用户管理</Link> },
|
||||
{ key: '/settings', icon: <SettingOutlined />, label: '系统配置' },
|
||||
]
|
||||
|
||||
if (loading && !token) {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||
<Spin size="large" tip="加载中..." />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout className="dashboard-layout">
|
||||
<Sider width={240} className="dashboard-sider">
|
||||
<div style={{ height: 64, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Title level={4} style={{ color: 'white', margin: 0 }}>智能星球</Title>
|
||||
</div>
|
||||
<Menu theme="dark" mode="inline" defaultSelectedKeys={['/settings']} items={menuItems} />
|
||||
</Sider>
|
||||
<Layout>
|
||||
<Header className="dashboard-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text strong>欢迎, {user?.username}</Text>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<Button type="link" danger onClick={handleLogout}>退出登录</Button>
|
||||
</div>
|
||||
</Header>
|
||||
<Content className="dashboard-content">
|
||||
<Title level={3}><SettingOutlined /> 系统设置</Title>
|
||||
<Tabs defaultActiveKey="system" tabPosition="left">
|
||||
<TabPane
|
||||
tab={<span><SettingOutlined /> 系统配置</span>}
|
||||
key="system"
|
||||
>
|
||||
<Card title="基本设置">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSaveSystem}
|
||||
initialValues={systemSettings}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="system_name"
|
||||
label="系统名称"
|
||||
rules={[{ required: true, message: '请输入系统名称' }]}
|
||||
>
|
||||
<Input placeholder="智能星球" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="refresh_interval"
|
||||
label="数据刷新间隔 (秒)"
|
||||
>
|
||||
<InputNumber min={10} max={3600} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="data_retention_days"
|
||||
label="数据保留天数"
|
||||
>
|
||||
<InputNumber min={1} max={365} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="max_concurrent_tasks"
|
||||
label="最大并发任务数"
|
||||
>
|
||||
<InputNumber min={1} max={20} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
name="auto_refresh"
|
||||
label="自动刷新"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" icon={<SaveOutlined />} loading={saving}>
|
||||
保存设置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={<span><BellOutlined /> 通知设置</span>}
|
||||
key="notifications"
|
||||
>
|
||||
<Card title="通知配置">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSaveNotifications}
|
||||
initialValues={notificationSettings}
|
||||
>
|
||||
<Divider orientation="left">邮件通知</Divider>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="email_enabled"
|
||||
label="启用邮件通知"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="email_address"
|
||||
label="通知邮箱"
|
||||
rules={[{ type: 'email', message: '请输入有效的邮箱地址' }]}
|
||||
>
|
||||
<Input placeholder="admin@example.com" disabled={!notificationSettings.email_enabled} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider orientation="left">告警通知</Divider>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="critical_alerts"
|
||||
label="严重告警"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="warning_alerts"
|
||||
label="警告告警"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="daily_summary"
|
||||
label="每日摘要"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" icon={<SaveOutlined />} loading={saving}>
|
||||
保存设置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={<span><SafetyOutlined /> 安全设置</span>}
|
||||
key="security"
|
||||
>
|
||||
<Card title="安全配置">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSaveSecurity}
|
||||
initialValues={securitySettings}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="session_timeout"
|
||||
label="会话超时 (分钟)"
|
||||
>
|
||||
<InputNumber min={5} max={1440} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="max_login_attempts"
|
||||
label="最大登录尝试次数"
|
||||
>
|
||||
<InputNumber min={1} max={10} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
name="password_policy"
|
||||
label="密码策略"
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value="low">简单 (最低6位)</Select.Option>
|
||||
<Select.Option value="medium">中等 (8位以上,含数字字母)</Select.Option>
|
||||
<Select.Option value="high">严格 (12位以上,含大小写数字特殊字符)</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Button type="primary" htmlType="submit" icon={<SaveOutlined />} loading={saving}>
|
||||
保存设置
|
||||
</Button>
|
||||
</Form>
|
||||
</Card>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Settings
|
||||
Reference in New Issue
Block a user