feat: establish core backend API server with S2A proxy, configuration, and mail services, alongside frontend email configuration page and toast component.

This commit is contained in:
2026-01-30 15:47:18 +08:00
parent 1fd984e1ab
commit 2e10b900fa
3 changed files with 76 additions and 5 deletions

View File

@@ -78,7 +78,7 @@ function ToastContainer({ toasts, onRemove }: ToastContainerProps) {
if (toasts.length === 0) return null
return (
<div className="fixed bottom-6 right-6 z-[9999] space-y-3">
<div className="fixed top-6 right-6 z-[9999] space-y-3">
{toasts.map((toast) => (
<ToastItem key={toast.id} toast={toast} onRemove={onRemove} />
))}

View File

@@ -1,13 +1,15 @@
import { useState, useEffect } from 'react'
import { CheckCircle, Save, Mail, Plus, Trash2, TestTube, Loader2, Settings, Server } from 'lucide-react'
import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common'
import { Card, CardHeader, CardTitle, CardContent, Button, Input, useToast } from '../components/common'
import { useConfig } from '../hooks/useConfig'
import type { MailServiceConfig } from '../types'
export default function EmailConfig() {
const { config, updateEmailConfig } = useConfig()
const toast = useToast()
const [saved, setSaved] = useState(false)
const [saving, setSaving] = useState(false)
const [services, setServices] = useState<MailServiceConfig[]>(config.email?.services || [])
const [testingIndex, setTestingIndex] = useState<number | null>(null)
const [testResults, setTestResults] = useState<Record<number, { success: boolean; message: string }>>({})
@@ -20,6 +22,12 @@ export default function EmailConfig() {
}, [config.email?.services])
const handleSave = async () => {
if (services.length === 0) {
toast.warning('请至少添加一个邮箱服务')
return
}
setSaving(true)
// 保存到前端 context
updateEmailConfig({ services })
@@ -31,13 +39,20 @@ export default function EmailConfig() {
body: JSON.stringify({ services }),
})
if (res.ok) {
const data = await res.json()
if (res.ok && data.code === 0) {
setSaved(true)
setTimeout(() => setSaved(false), 2000)
toast.success(`保存成功: ${data.data.message}`)
} else {
toast.error('保存失败: ' + (data.message || '未知错误'))
}
} catch (error) {
console.error('保存失败:', error)
toast.error('保存失败: ' + (error instanceof Error ? error.message : '网络错误'))
}
setSaving(false)
}
const handleAddService = () => {
@@ -121,9 +136,11 @@ export default function EmailConfig() {
</Button>
<Button
onClick={handleSave}
disabled={saving}
loading={saving}
icon={saved ? <CheckCircle className="h-4 w-4" /> : <Save className="h-4 w-4" />}
>
{saved ? '已保存' : '保存配置'}
{saved ? '已保存' : saving ? '保存中...' : '保存配置'}
</Button>
</div>
</div>