Files
PolyHermes/frontend/src/pages/SystemSettings.tsx
T
WrBug 696193c571 feat: 优化系统更新功能
- 系统更新模块移到系统设置页面最上方
- 版本号显示使用 Tag 格式(gitTag)
- 版本号 Tag 根据是否有新版本显示不同颜色(黄色=有新版本,绿色=无新版本)
- 版本号使用镂空样式,字号 8px
- 支持 Markdown 渲染更新内容
- 美化系统更新页面样式
- 在 Layout 中添加版本更新检查,有新版本时显示提示
2026-01-21 05:13:28 +08:00

901 lines
31 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Card, Form, Button, Switch, Input, InputNumber, message, Typography, Space, Alert, Select, Table, Tag, Popconfirm, Modal } from 'antd'
import { SaveOutlined, CheckCircleOutlined, ReloadOutlined, GlobalOutlined, NotificationOutlined, KeyOutlined, LinkOutlined, PlusOutlined, EditOutlined, DeleteOutlined, SendOutlined } from '@ant-design/icons'
import { apiService } from '../services/api'
import { useMediaQuery } from 'react-responsive'
import { useTranslation } from 'react-i18next'
import type { SystemConfig, BuilderApiKeyUpdateRequest, NotificationConfig, NotificationConfigRequest, NotificationConfigUpdateRequest } from '../types'
import { TelegramConfigForm } from '../components/notifications'
import SystemUpdate from './SystemUpdate'
const { Title, Text, Paragraph } = 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 SystemSettings: React.FC = () => {
const { t, i18n: i18nInstance } = useTranslation()
const isMobile = useMediaQuery({ maxWidth: 768 })
// 第一部分:多语言
const [languageForm] = Form.useForm()
const [currentLang, setCurrentLang] = useState<string>('auto')
// 第二部分:消息推送设置
const [notificationConfigs, setNotificationConfigs] = useState<NotificationConfig[]>([])
const [notificationLoading, setNotificationLoading] = useState(false)
const [notificationModalVisible, setNotificationModalVisible] = useState(false)
const [editingNotificationConfig, setEditingNotificationConfig] = useState<NotificationConfig | null>(null)
const [notificationForm] = Form.useForm()
const [testLoading, setTestLoading] = useState(false)
// 第三部分:Relayer配置
const [relayerForm] = Form.useForm()
const [autoRedeemForm] = Form.useForm()
const [systemConfig, setSystemConfig] = useState<SystemConfig | null>(null)
const [relayerLoading, setRelayerLoading] = useState(false)
const [autoRedeemLoading, setAutoRedeemLoading] = useState(false)
// 第四部分:代理设置
const [proxyForm] = Form.useForm()
const [proxyLoading, setProxyLoading] = useState(false)
const [proxyChecking, setProxyChecking] = useState(false)
const [proxyCheckResult, setProxyCheckResult] = useState<ProxyCheckResponse | null>(null)
const [currentProxyConfig, setCurrentProxyConfig] = useState<ProxyConfig | null>(null)
useEffect(() => {
// 初始化多语言设置
const savedLanguage = localStorage.getItem('i18n_language') || 'auto'
setCurrentLang(savedLanguage)
languageForm.setFieldsValue({ language: savedLanguage })
// 加载其他配置
fetchNotificationConfigs()
fetchSystemConfig()
fetchProxyConfig()
}, [])
// ==================== 第一部分:多语言 ====================
const detectSystemLanguage = (): string => {
const systemLanguage = navigator.language || navigator.languages?.[0] || 'en'
const lang = systemLanguage.toLowerCase()
if (lang.startsWith('zh')) {
if (lang.includes('tw') || lang.includes('hk') || lang.includes('mo')) {
return 'zh-TW'
}
return 'zh-CN'
}
return 'en'
}
const handleLanguageSubmit = async (values: { language: string }) => {
try {
let actualLang = values.language
if (values.language === 'auto') {
actualLang = detectSystemLanguage()
localStorage.setItem('i18n_language', 'auto')
} else {
localStorage.setItem('i18n_language', values.language)
}
setCurrentLang(values.language)
await i18nInstance.changeLanguage(actualLang)
message.success(t('languageSettings.changeSuccess') || '语言设置已保存')
} catch (error) {
message.error(t('languageSettings.changeFailed') || '语言设置保存失败')
}
}
// ==================== 第二部分:消息推送设置 ====================
const fetchNotificationConfigs = async () => {
setNotificationLoading(true)
try {
const response = await apiService.notifications.list({ type: 'telegram' })
if (response.data.code === 0 && response.data.data) {
setNotificationConfigs(response.data.data)
} else {
message.error(response.data.msg || t('notificationSettings.fetchFailed'))
}
} catch (error: any) {
message.error(error.message || t('notificationSettings.fetchFailed'))
} finally {
setNotificationLoading(false)
}
}
const handleNotificationCreate = () => {
setEditingNotificationConfig(null)
notificationForm.resetFields()
notificationForm.setFieldsValue({
type: 'telegram',
enabled: true,
config: {
botToken: '',
chatIds: []
}
})
setNotificationModalVisible(true)
}
const handleNotificationEdit = (config: NotificationConfig) => {
setEditingNotificationConfig(config)
let botToken = ''
let chatIds = ''
if (config.config) {
if ('data' in config.config && config.config.data) {
const data = config.config.data as any
botToken = data.botToken || ''
if (data.chatIds) {
if (Array.isArray(data.chatIds)) {
chatIds = data.chatIds.join(',')
} else if (typeof data.chatIds === 'string') {
chatIds = data.chatIds
}
}
} else {
if ('botToken' in config.config) {
botToken = (config.config as any).botToken || ''
}
if ('chatIds' in config.config) {
const ids = (config.config as any).chatIds
if (Array.isArray(ids)) {
chatIds = ids.join(',')
} else if (typeof ids === 'string') {
chatIds = ids
}
}
}
}
notificationForm.setFieldsValue({
type: config.type,
name: config.name,
enabled: config.enabled,
config: {
botToken: botToken,
chatIds: chatIds
}
})
setNotificationModalVisible(true)
}
const handleNotificationDelete = async (id: number) => {
try {
const response = await apiService.notifications.delete({ id })
if (response.data.code === 0) {
message.success(t('notificationSettings.deleteSuccess'))
fetchNotificationConfigs()
} else {
message.error(response.data.msg || t('notificationSettings.deleteFailed'))
}
} catch (error: any) {
message.error(error.message || t('notificationSettings.deleteFailed'))
}
}
const handleNotificationUpdateEnabled = async (id: number, enabled: boolean) => {
try {
const response = await apiService.notifications.updateEnabled({ id, enabled })
if (response.data.code === 0) {
message.success(enabled ? t('notificationSettings.enableSuccess') : t('notificationSettings.disableSuccess'))
fetchNotificationConfigs()
} else {
message.error(response.data.msg || t('notificationSettings.updateStatusFailed'))
}
} catch (error: any) {
message.error(error.message || t('notificationSettings.updateStatusFailed'))
}
}
const handleNotificationTest = async () => {
setTestLoading(true)
try {
const response = await apiService.notifications.test({ message: '这是一条测试消息' })
if (response.data.code === 0 && response.data.data) {
message.success(t('notificationSettings.testSuccess'))
} else {
message.error(response.data.msg || t('notificationSettings.testFailed'))
}
} catch (error: any) {
message.error(error.message || t('notificationSettings.testFailed'))
} finally {
setTestLoading(false)
}
}
const handleNotificationSubmit = async () => {
try {
const values = await notificationForm.validateFields()
const chatIds = typeof values.config.chatIds === 'string'
? values.config.chatIds.split(',').map((id: string) => id.trim()).filter((id: string) => id)
: values.config.chatIds || []
const configData: NotificationConfigRequest | NotificationConfigUpdateRequest = {
type: values.type,
name: values.name,
enabled: values.enabled,
config: {
botToken: values.config.botToken,
chatIds: chatIds
}
}
if (editingNotificationConfig?.id) {
const updateData = {
...configData,
id: editingNotificationConfig.id
} as NotificationConfigUpdateRequest
const response = await apiService.notifications.update(updateData)
if (response.data.code === 0) {
message.success(t('notificationSettings.updateSuccess'))
setNotificationModalVisible(false)
fetchNotificationConfigs()
} else {
message.error(response.data.msg || t('notificationSettings.updateFailed'))
}
} else {
const response = await apiService.notifications.create(configData)
if (response.data.code === 0) {
message.success(t('notificationSettings.createSuccess'))
setNotificationModalVisible(false)
fetchNotificationConfigs()
} else {
message.error(response.data.msg || t('notificationSettings.createFailed'))
}
}
} catch (error: any) {
if (error.errorFields) {
return
}
message.error(error.message || t('message.error'))
}
}
const notificationColumns = [
{
title: t('notificationSettings.configName'),
dataIndex: 'name',
key: 'name',
},
{
title: t('notificationSettings.type'),
dataIndex: 'type',
key: 'type',
render: (type: string) => <Tag color="blue">{type.toUpperCase()}</Tag>
},
{
title: t('notificationSettings.status'),
dataIndex: 'enabled',
key: 'enabled',
render: (enabled: boolean) => (
<Tag color={enabled ? 'green' : 'default'}>
{enabled ? t('notificationSettings.enabledStatus') : t('notificationSettings.disabledStatus')}
</Tag>
)
},
{
title: t('common.actions'),
key: 'action',
width: isMobile ? 120 : 200,
render: (_: any, record: NotificationConfig) => (
<Space size="small" wrap>
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => handleNotificationEdit(record)}
>
{t('notificationSettings.edit')}
</Button>
<Switch
checked={record.enabled}
size="small"
onChange={(checked) => handleNotificationUpdateEnabled(record.id!, checked)}
/>
<Button
type="link"
size="small"
icon={<SendOutlined />}
loading={testLoading}
onClick={handleNotificationTest}
>
{t('notificationSettings.test')}
</Button>
<Popconfirm
title={t('notificationSettings.deleteConfirm')}
onConfirm={() => handleNotificationDelete(record.id!)}
okText={t('common.confirm')}
cancelText={t('common.cancel')}
>
<Button
type="link"
danger
size="small"
icon={<DeleteOutlined />}
>
{t('notificationSettings.delete')}
</Button>
</Popconfirm>
</Space>
)
}
]
// ==================== 第三部分:Relayer配置 ====================
const fetchSystemConfig = async () => {
try {
const response = await apiService.systemConfig.get()
if (response.data.code === 0 && response.data.data) {
const config = response.data.data
setSystemConfig(config)
// 将已配置的值填充到输入框中
relayerForm.setFieldsValue({
builderApiKey: config.builderApiKeyDisplay || '',
builderSecret: config.builderSecretDisplay || '',
builderPassphrase: config.builderPassphraseDisplay || '',
})
autoRedeemForm.setFieldsValue({
autoRedeemEnabled: config.autoRedeemEnabled
})
}
} catch (error: any) {
console.error('获取系统配置失败:', error)
}
}
const handleRelayerSubmit = async (values: BuilderApiKeyUpdateRequest) => {
setRelayerLoading(true)
try {
const updateData: BuilderApiKeyUpdateRequest = {}
if (values.builderApiKey && values.builderApiKey.trim()) {
updateData.builderApiKey = values.builderApiKey.trim()
}
if (values.builderSecret && values.builderSecret.trim()) {
updateData.builderSecret = values.builderSecret.trim()
}
if (values.builderPassphrase && values.builderPassphrase.trim()) {
updateData.builderPassphrase = values.builderPassphrase.trim()
}
if (!updateData.builderApiKey && !updateData.builderSecret && !updateData.builderPassphrase) {
message.warning(t('builderApiKey.noChanges') || '没有需要更新的字段')
setRelayerLoading(false)
return
}
const response = await apiService.systemConfig.updateBuilderApiKey(updateData)
if (response.data.code === 0) {
message.success(t('builderApiKey.saveSuccess'))
fetchSystemConfig()
relayerForm.resetFields()
} else {
message.error(response.data.msg || t('builderApiKey.saveFailed'))
}
} catch (error: any) {
message.error(error.message || t('builderApiKey.saveFailed'))
} finally {
setRelayerLoading(false)
}
}
const handleAutoRedeemSubmit = async (values: { autoRedeemEnabled: boolean }) => {
setAutoRedeemLoading(true)
try {
const response = await apiService.systemConfig.updateAutoRedeem({ enabled: values.autoRedeemEnabled })
if (response.data.code === 0) {
message.success(t('systemSettings.autoRedeem.saveSuccess') || '自动赎回配置已保存')
fetchSystemConfig()
} else {
message.error(response.data.msg || t('systemSettings.autoRedeem.saveFailed') || '保存自动赎回配置失败')
}
} catch (error: any) {
message.error(error.message || t('systemSettings.autoRedeem.saveFailed') || '保存自动赎回配置失败')
} finally {
setAutoRedeemLoading(false)
}
}
// ==================== 第四部分:代理设置 ====================
const fetchProxyConfig = async () => {
try {
const response = await apiService.proxyConfig.get()
if (response.data.code === 0) {
const data = response.data.data
setCurrentProxyConfig(data)
if (data) {
proxyForm.setFieldsValue({
enabled: data.enabled,
host: data.host || '',
port: data.port || undefined,
username: data.username || '',
password: '',
})
} else {
proxyForm.resetFields()
}
} else {
message.error(response.data.msg || '获取代理配置失败')
}
} catch (error: any) {
message.error(error.message || '获取代理配置失败')
}
}
const handleProxySubmit = async (values: any) => {
setProxyLoading(true)
try {
const response = await apiService.proxyConfig.saveHttp({
enabled: values.enabled || false,
host: values.host,
port: values.port,
username: values.username || undefined,
password: values.password || undefined,
})
if (response.data.code === 0) {
message.success('保存代理配置成功。新配置将立即生效,已建立的 WebSocket 连接需要重新连接才能使用新代理。')
fetchProxyConfig()
setProxyCheckResult(null)
} else {
message.error(response.data.msg || '保存代理配置失败')
}
} catch (error: any) {
message.error(error.message || '保存代理配置失败')
} finally {
setProxyLoading(false)
}
}
const handleProxyCheck = async () => {
setProxyChecking(true)
setProxyCheckResult(null)
try {
const response = await apiService.proxyConfig.check()
if (response.data.code === 0 && response.data.data) {
const result = response.data.data
setProxyCheckResult(result)
if (result.success) {
message.success(`代理检查成功:${result.message}${result.responseTime ? ` (响应时间: ${result.responseTime}ms)` : ''}`)
} else {
message.warning(`代理检查失败:${result.message}`)
}
} else {
message.error(response.data.msg || '代理检查失败')
}
} catch (error: any) {
message.error(error.message || '代理检查失败')
} finally {
setProxyChecking(false)
}
}
return (
<div>
<div style={{ marginBottom: '16px' }}>
<Title level={2} style={{ margin: 0 }}>{t('systemSettings.title') || '通用设置'}</Title>
</div>
{/* 系统更新 */}
<SystemUpdate />
{/* 第一部分:多语言 */}
<Card
title={
<Space>
<GlobalOutlined />
<span>{t('systemSettings.language.title') || '多语言设置'}</span>
</Space>
}
style={{ marginBottom: '16px' }}
>
<Form
form={languageForm}
layout="vertical"
onFinish={handleLanguageSubmit}
size={isMobile ? 'middle' : 'large'}
initialValues={{ language: currentLang }}
>
<Form.Item
label={t('systemSettings.language.currentLanguage') || '当前语言'}
name="language"
rules={[{ required: true, message: t('systemSettings.language.languageRequired') || '请选择语言' }]}
>
<Select
options={[
{ value: 'auto', label: t('languageSettings.followSystem') || '跟随系统' },
{ value: 'zh-CN', label: '简体中文' },
{ value: 'zh-TW', label: '繁體中文' },
{ value: 'en', label: 'English' }
]}
/>
</Form.Item>
{currentLang === 'auto' && (
<Form.Item>
<Text type="secondary" style={{ fontSize: '12px' }}>
{t('languageSettings.currentSystemLanguage') || '当前系统语言'}: {
detectSystemLanguage() === 'zh-CN' ? '简体中文' :
detectSystemLanguage() === 'zh-TW' ? '繁體中文' : 'English'
}
</Text>
</Form.Item>
)}
<Form.Item>
<Button
type="primary"
htmlType="submit"
icon={<SaveOutlined />}
>
{t('common.save') || '保存设置'}
</Button>
</Form.Item>
</Form>
</Card>
{/* 第二部分:消息推送设置 */}
<Card
title={
<Space>
<NotificationOutlined />
<span>{t('systemSettings.notification.title') || '消息推送设置'}</span>
</Space>
}
style={{ marginBottom: '16px' }}
extra={
<Button
type="primary"
icon={<PlusOutlined />}
onClick={handleNotificationCreate}
>
{t('notificationSettings.addConfig')}
</Button>
}
>
<Table
columns={notificationColumns}
dataSource={notificationConfigs}
loading={notificationLoading}
rowKey="id"
pagination={false}
scroll={{ x: isMobile ? 600 : 'auto' }}
/>
<Modal
title={editingNotificationConfig ? t('notificationSettings.editConfig') : t('notificationSettings.addConfig')}
open={notificationModalVisible}
onOk={handleNotificationSubmit}
onCancel={() => setNotificationModalVisible(false)}
width={isMobile ? '90%' : 600}
okText={t('common.confirm')}
cancelText={t('common.cancel')}
>
<Form
form={notificationForm}
layout="vertical"
>
<Form.Item
name="type"
label={t('notificationSettings.type')}
rules={[{ required: true, message: t('notificationSettings.typeRequired') }]}
>
<Input disabled value="telegram" />
</Form.Item>
<Form.Item
name="name"
label={t('notificationSettings.configName')}
rules={[{ required: true, message: t('notificationSettings.configNameRequired') }]}
>
<Input placeholder={t('notificationSettings.configNamePlaceholder')} />
</Form.Item>
<Form.Item
name="enabled"
label={t('notificationSettings.enabled')}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate={(prevValues, currentValues) => {
return prevValues.type !== currentValues.type ||
prevValues.config !== currentValues.config
}}>
{() => {
const currentType = notificationForm.getFieldValue('type') || 'telegram'
if (currentType === 'telegram') {
return <TelegramConfigForm form={notificationForm} />
}
return null
}}
</Form.Item>
</Form>
</Modal>
</Card>
{/* 第三部分:Relayer配置 */}
<Card
title={
<Space>
<KeyOutlined />
<span>{t('systemSettings.relayer.title') || 'Relayer 配置'}</span>
</Space>
}
style={{ marginBottom: '16px' }}
>
{/* Builder API Key 配置 */}
<div style={{ marginBottom: '24px' }}>
<Title level={4} style={{ marginBottom: '16px' }}>
{t('builderApiKey.title') || 'Builder API Key'}
</Title>
<Alert
message={t('builderApiKey.alertTitle')}
description={
<div>
<Paragraph style={{ marginBottom: '8px' }}>
{t('builderApiKey.description')}
</Paragraph>
<Paragraph style={{ marginBottom: '8px' }}>
<Text strong>{t('builderApiKey.purposeTitle')}</Text>
<ul style={{ marginTop: '8px', marginBottom: 0, paddingLeft: '20px' }}>
<li>{t('builderApiKey.purpose1')}</li>
<li>{t('builderApiKey.purpose2')}</li>
<li>{t('builderApiKey.purpose3')}</li>
</ul>
</Paragraph>
<Paragraph style={{ marginBottom: 0 }}>
<Text strong>{t('builderApiKey.getApiKey')}</Text>
<Space style={{ marginLeft: '8px' }}>
<a
href="https://polymarket.com/settings?tab=builder"
target="_blank"
rel="noopener noreferrer"
>
<LinkOutlined /> {t('builderApiKey.openSettings')}
</a>
</Space>
</Paragraph>
</div>
}
type="info"
showIcon
style={{ marginBottom: '16px' }}
/>
<Form
form={relayerForm}
layout="vertical"
onFinish={handleRelayerSubmit}
size={isMobile ? 'middle' : 'large'}
>
<Form.Item
label={t('builderApiKey.apiKey')}
name="builderApiKey"
>
<Input
placeholder={t('builderApiKey.apiKeyPlaceholder')}
style={{ fontFamily: 'monospace' }}
/>
</Form.Item>
<Form.Item
label={t('builderApiKey.secret')}
name="builderSecret"
>
<Input.Password
placeholder={t('builderApiKey.secretPlaceholder')}
style={{ fontFamily: 'monospace' }}
iconRender={(visible) => (visible ? <span>👁️</span> : <span>👁️‍🗨️</span>)}
/>
</Form.Item>
<Form.Item
label={t('builderApiKey.passphrase')}
name="builderPassphrase"
>
<Input.Password
placeholder={t('builderApiKey.passphrasePlaceholder')}
style={{ fontFamily: 'monospace' }}
iconRender={(visible) => (visible ? <span>👁️</span> : <span>👁️‍🗨️</span>)}
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
icon={<SaveOutlined />}
loading={relayerLoading}
>
{t('common.save') || '保存配置'}
</Button>
</Form.Item>
</Form>
</div>
{/* 自动赎回配置 */}
<div style={{ borderTop: '1px solid #f0f0f0', paddingTop: '24px' }}>
<Title level={4} style={{ marginBottom: '16px' }}>
{t('systemSettings.autoRedeem.title') || '自动赎回'}
</Title>
<Form
form={autoRedeemForm}
layout="vertical"
onFinish={handleAutoRedeemSubmit}
size={isMobile ? 'middle' : 'large'}
>
<Form.Item
label={t('systemSettings.autoRedeem.label') || '启用自动赎回'}
name="autoRedeemEnabled"
tooltip={t('systemSettings.autoRedeem.tooltip') || '开启后,系统将自动赎回所有账户中可赎回的仓位。需要配置 Builder API Key 才能生效。'}
valuePropName="checked"
>
<Switch loading={autoRedeemLoading} />
</Form.Item>
{!systemConfig?.builderApiKeyConfigured && (
<Alert
message={t('systemSettings.autoRedeem.builderApiKeyNotConfigured') || 'Builder API Key 未配置'}
description={t('systemSettings.autoRedeem.builderApiKeyNotConfiguredDesc') || '自动赎回功能需要配置 Builder API Key 才能生效。'}
type="warning"
showIcon
style={{ marginBottom: '16px' }}
/>
)}
<Form.Item>
<Button
type="primary"
htmlType="submit"
icon={<SaveOutlined />}
loading={autoRedeemLoading}
>
{t('common.save') || '保存配置'}
</Button>
</Form.Item>
</Form>
</div>
</Card>
{/* 第四部分:代理设置 */}
<Card
title={
<Space>
<LinkOutlined />
<span>{t('systemSettings.proxy.title') || '代理设置'}</span>
</Space>
}
style={{ marginBottom: '16px' }}
>
<Form
form={proxyForm}
layout="vertical"
onFinish={handleProxySubmit}
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={currentProxyConfig ? (t('proxySettings.passwordHelpUpdate') || '留空则不更新密码,输入新密码则更新') : (t('proxySettings.passwordHelp') || '如果代理需要认证,请输入密码')}
>
<Input.Password placeholder={currentProxyConfig ? (t('proxySettings.passwordPlaceholderUpdate') || '留空则不更新密码') : (t('proxySettings.passwordPlaceholder') || '如果代理需要认证,请输入密码')} />
</Form.Item>
<Form.Item>
<Space>
<Button
type="primary"
htmlType="submit"
icon={<SaveOutlined />}
loading={proxyLoading}
>
{t('common.save') || '保存配置'}
</Button>
<Button
icon={<CheckCircleOutlined />}
onClick={handleProxyCheck}
loading={proxyChecking}
>
{t('proxySettings.check') || '检查代理'}
</Button>
{proxyCheckResult && (
<Button
icon={<ReloadOutlined />}
onClick={fetchProxyConfig}
>
{t('common.refresh') || '刷新配置'}
</Button>
)}
</Space>
</Form.Item>
</Form>
{proxyCheckResult && (
<Alert
type={proxyCheckResult.success ? 'success' : 'error'}
message={proxyCheckResult.success ? (t('proxySettings.checkSuccess') || '代理检查成功') : (t('proxySettings.checkFailed') || '代理检查失败')}
description={
<div>
<Text>{proxyCheckResult.message}</Text>
{(proxyCheckResult.responseTime !== undefined || proxyCheckResult.latency !== undefined) && (
<div style={{ marginTop: '8px' }}>
<Text type="secondary">
{t('proxySettings.latency') || '延迟'}: {(proxyCheckResult.latency ?? proxyCheckResult.responseTime) ?? 0}ms
</Text>
</div>
)}
</div>
}
style={{ marginTop: '16px' }}
showIcon
/>
)}
</Card>
</div>
)
}
export default SystemSettings