✨ smtp服务

This commit is contained in:
sugarBecomer
2026-03-26 11:37:38 +08:00
parent 223ddf4f66
commit 2603aada26
3 changed files with 139 additions and 2 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import { message } from '@/composables/useMessage'
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 15000,
timeout: 60000,
})
// ---- Token 自动刷新 ----
+20
View File
@@ -132,3 +132,23 @@ export const systemPriceApi = {
update: (data: PriceConfig) =>
request.put<any, ApiResponse>('/v1/system/config/price', data),
}
// ---- SMTP 配置 ----
export interface SmtpConfig {
smtpHost: string
smtpPort: number
smtpUsername: string
smtpPassword: string
fromEmail: string
fromName: string
}
export const systemSmtpApi = {
get: () =>
request.get<any, ApiResponse<SmtpConfig>>('/v1/system/config/smtp'),
update: (data: SmtpConfig) =>
request.put<any, ApiResponse>('/v1/system/config/smtp', data),
test: (toEmail: string) =>
request.post<any, ApiResponse>('/v1/system/config/smtp/test', { toEmail }),
}
+118 -1
View File
@@ -8,7 +8,8 @@ import { Switch } from '@/components/ui/switch'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { MdEditor } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'
import { systemConfigApi, systemPriceApi, type PriceConfig } from '@/api/system'
import { Separator } from '@/components/ui/separator'
import { systemConfigApi, systemPriceApi, systemSmtpApi, type PriceConfig, type SmtpConfig } from '@/api/system'
// ---- 公告 ----
const announcementLoading = ref(false)
@@ -139,10 +140,73 @@ async function savePriceConfig() {
}
}
// ---- SMTP 配置 ----
const smtpLoading = ref(false)
const smtpSaving = ref(false)
const smtpTesting = ref(false)
const testEmail = ref('')
const smtpConfig = ref<SmtpConfig>({
smtpHost: '',
smtpPort: 587,
smtpUsername: '',
smtpPassword: '',
fromEmail: '',
fromName: '',
})
async function fetchSmtpConfig() {
smtpLoading.value = true
try {
const res = await systemSmtpApi.get()
if (res.data) {
smtpConfig.value = { ...smtpConfig.value, ...res.data }
// port 可能是字符串
smtpConfig.value.smtpPort = Number(smtpConfig.value.smtpPort) || 587
}
} catch {
// 使用默认值
} finally {
smtpLoading.value = false
}
}
async function saveSmtpConfig() {
if (!smtpConfig.value.smtpHost || !smtpConfig.value.smtpUsername) {
toast.error('请填写完整的SMTP配置')
return
}
smtpSaving.value = true
try {
await systemSmtpApi.update(smtpConfig.value)
toast.success('SMTP配置保存成功')
} catch {
// 全局拦截器已处理
} finally {
smtpSaving.value = false
}
}
async function testSmtp() {
if (!testEmail.value.trim()) {
toast.error('请输入测试收件邮箱')
return
}
smtpTesting.value = true
try {
await systemSmtpApi.test(testEmail.value.trim())
toast.success('测试邮件发送成功')
} catch {
// 全局拦截器已处理
} finally {
smtpTesting.value = false
}
}
onMounted(() => {
fetchAnnouncement()
fetchRegisterConfig()
fetchPriceConfig()
fetchSmtpConfig()
})
</script>
@@ -246,5 +310,58 @@ onMounted(() => {
</CardContent>
</Card>
</div>
<!-- SMTP 邮件配置 -->
<Card>
<CardHeader>
<CardTitle class="text-base">SMTP 邮件配置</CardTitle>
</CardHeader>
<CardContent class="space-y-4">
<div v-if="smtpLoading" class="text-sm text-muted-foreground py-4 text-center">加载中...</div>
<template v-else>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 max-w-2xl">
<div class="space-y-2">
<Label>SMTP 服务器</Label>
<Input v-model="smtpConfig.smtpHost" placeholder="如:smtp.qq.com" />
</div>
<div class="space-y-2">
<Label>SMTP 端口</Label>
<Input v-model.number="smtpConfig.smtpPort" type="number" placeholder="如:587" />
</div>
<div class="space-y-2">
<Label>SMTP 用户名</Label>
<Input v-model="smtpConfig.smtpUsername" placeholder="SMTP登录用户名" />
</div>
<div class="space-y-2">
<Label>SMTP 密码</Label>
<Input v-model="smtpConfig.smtpPassword" type="password" placeholder="SMTP登录密码/授权码" />
</div>
<div class="space-y-2">
<Label>发件人邮箱</Label>
<Input v-model="smtpConfig.fromEmail" placeholder="发件人邮箱地址" />
</div>
<div class="space-y-2">
<Label>发件人名称</Label>
<Input v-model="smtpConfig.fromName" placeholder="发件人显示名称" />
</div>
</div>
<Separator />
<div class="flex items-end gap-3 max-w-2xl">
<div class="flex-1 space-y-2">
<Label>测试收件邮箱</Label>
<Input v-model="testEmail" placeholder="输入邮箱地址发送测试邮件" />
</div>
<Button variant="outline" size="sm" :disabled="smtpTesting" @click="testSmtp">
{{ smtpTesting ? '发送中...' : '发送测试' }}
</Button>
</div>
<div class="flex justify-end max-w-2xl">
<Button size="sm" :disabled="smtpSaving" @click="saveSmtpConfig">
{{ smtpSaving ? '保存中...' : '保存配置' }}
</Button>
</div>
</template>
</CardContent>
</Card>
</div>
</template>