Files
PolyHermes/frontend/src/pages/ProxySettings.tsx
T
WrBug 1a9407c544 feat: 完成前端多语言支持和菜单优化
- 缩短菜单标题,解决显示不全问题
- 添加跟随系统语言选项,作为默认选项
- 移除语言切换时的页面刷新,实现无刷新切换
- 完成主要页面的多语言替换:
  * ConfigPage - 全局配置页面
  * ResetPassword - 重置密码页面
  * LeaderList - Leader 列表页面
  * UserList - 用户列表页面
  * Statistics - 统计信息页面
  * OrderList - 订单列表页面
  * TemplateList - 模板列表页面
  * CopyTradingList - 跟单配置列表页面
- 添加简体中文、繁体中文和英文的完整翻译键
- 优化语言设置页面,支持跟随系统语言
2025-12-04 10:36:28 +08:00

240 lines
7.9 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Card, Form, Button, Switch, Input, InputNumber, message, Typography, Space, Alert } from 'antd'
import { SaveOutlined, CheckCircleOutlined, ReloadOutlined } from '@ant-design/icons'
import { apiService } from '../services/api'
import { useTranslation } from 'react-i18next'
import { useMediaQuery } from 'react-responsive'
const { Title, Text } = Typography
interface ProxyConfig {
id?: number
type: string
enabled: boolean
host?: string
port?: number
username?: string
subscriptionUrl?: string
lastSubscriptionUpdate?: number
createdAt: number
updatedAt: number
}
interface ProxyCheckResponse {
success: boolean
message: string
responseTime?: number
latency?: number
}
const ProxySettings: React.FC = () => {
const { t } = useTranslation()
const isMobile = useMediaQuery({ maxWidth: 768 })
const [form] = Form.useForm()
const [loading, setLoading] = useState(false)
const [checking, setChecking] = useState(false)
const [checkResult, setCheckResult] = useState<ProxyCheckResponse | null>(null)
const [currentConfig, setCurrentConfig] = useState<ProxyConfig | null>(null)
useEffect(() => {
fetchConfig()
}, [])
const fetchConfig = async () => {
try {
const response = await apiService.proxyConfig.get()
if (response.data.code === 0) {
const data = response.data.data
setCurrentConfig(data)
if (data) {
form.setFieldsValue({
enabled: data.enabled,
host: data.host || '',
port: data.port || undefined,
username: data.username || '',
password: '', // 密码不预填充
})
} else {
form.resetFields()
}
} else {
message.error(response.data.msg || t('proxySettings.getFailed') || '获取代理配置失败')
}
} catch (error: any) {
message.error(error.message || t('proxySettings.getFailed') || '获取代理配置失败')
}
}
const handleSubmit = async (values: any) => {
setLoading(true)
try {
const requestData: any = {
enabled: values.enabled || false,
host: values.host,
port: values.port,
username: values.username || undefined,
}
// 只有在输入了新密码时才包含密码字段
if (values.password && values.password.trim()) {
requestData.password = values.password
}
const response = await apiService.proxyConfig.saveHttp(requestData)
if (response.data.code === 0) {
message.success(t('proxySettings.saveSuccess') || '保存配置成功')
setCheckResult(null)
fetchConfig()
} else {
message.error(response.data.msg || t('proxySettings.saveFailed') || '保存配置失败')
}
} catch (error: any) {
message.error(error.message || t('proxySettings.saveFailed') || '保存配置失败')
} finally {
setLoading(false)
}
}
const handleCheck = async () => {
setChecking(true)
setCheckResult(null)
try {
const response = await apiService.proxyConfig.check()
if (response.data.code === 0 && response.data.data) {
setCheckResult(response.data.data)
} else {
setCheckResult({
success: false,
message: response.data.msg || t('proxySettings.checkFailed') || '代理检查失败'
})
}
} catch (error: any) {
setCheckResult({
success: false,
message: error.message || t('proxySettings.checkFailed') || '代理检查失败'
})
} finally {
setChecking(false)
}
}
return (
<div>
<div style={{ marginBottom: '16px' }}>
<Title level={2} style={{ margin: 0 }}>{t('proxySettings.title') || '代理设置'}</Title>
</div>
<Card>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
size={isMobile ? 'middle' : 'large'}
>
<Form.Item
label={t('proxySettings.enabled') || '启用代理'}
name="enabled"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t('proxySettings.host') || '代理主机'}
name="host"
rules={[
{ required: true, message: t('proxySettings.hostRequired') || '请输入代理主机地址' },
{ pattern: /^[\w\.-]+$/, message: t('proxySettings.hostInvalid') || '请输入有效的主机地址' }
]}
>
<Input placeholder={t('proxySettings.hostPlaceholder') || '例如:127.0.0.1 或 proxy.example.com'} />
</Form.Item>
<Form.Item
label={t('proxySettings.port') || '代理端口'}
name="port"
rules={[
{ required: true, message: t('proxySettings.portRequired') || '请输入代理端口' },
{ type: 'number', min: 1, max: 65535, message: t('proxySettings.portInvalid') || '端口必须在 1-65535 之间' }
]}
>
<InputNumber
min={1}
max={65535}
style={{ width: '100%' }}
placeholder={t('proxySettings.portPlaceholder') || '例如:8888'}
/>
</Form.Item>
<Form.Item
label={t('proxySettings.username') || '代理用户名(可选)'}
name="username"
>
<Input placeholder={t('proxySettings.usernamePlaceholder') || '如果代理需要认证,请输入用户名'} />
</Form.Item>
<Form.Item
label={t('proxySettings.password') || '代理密码(可选)'}
name="password"
help={currentConfig ? (t('proxySettings.passwordHelpUpdate') || '留空则不更新密码,输入新密码则更新') : (t('proxySettings.passwordHelp') || '如果代理需要认证,请输入密码')}
>
<Input.Password placeholder={currentConfig ? (t('proxySettings.passwordPlaceholderUpdate') || '留空则不更新密码') : (t('proxySettings.passwordPlaceholder') || '如果代理需要认证,请输入密码')} />
</Form.Item>
<Form.Item>
<Space>
<Button
type="primary"
htmlType="submit"
icon={<SaveOutlined />}
loading={loading}
>
{t('common.save') || '保存配置'}
</Button>
<Button
icon={<CheckCircleOutlined />}
onClick={handleCheck}
loading={checking}
>
{t('proxySettings.check') || '检查代理'}
</Button>
{checkResult && (
<Button
icon={<ReloadOutlined />}
onClick={fetchConfig}
>
{t('common.refresh') || '刷新配置'}
</Button>
)}
</Space>
</Form.Item>
</Form>
{checkResult && (
<Alert
type={checkResult.success ? 'success' : 'error'}
message={checkResult.success ? (t('proxySettings.checkSuccess') || '代理检查成功') : (t('proxySettings.checkFailed') || '代理检查失败')}
description={
<div>
<Text>{checkResult.message}</Text>
{(checkResult.responseTime !== undefined || checkResult.latency !== undefined) && (
<div style={{ marginTop: '8px' }}>
<Text type="secondary">
{t('proxySettings.latency') || '延迟'}: {(checkResult.latency ?? checkResult.responseTime) ?? 0}ms
</Text>
</div>
)}
</div>
}
style={{ marginTop: '16px' }}
showIcon
/>
)}
</Card>
</div>
)
}
export default ProxySettings