Files
nl-blogs/client/src/pages/admin/EmailSuffixes.vue
2026-01-16 17:03:34 +08:00

242 lines
5.8 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="suffixes-container">
<h1 class="page-title">邮箱后缀配置</h1>
<!-- Add Form -->
<div class="add-form mb-8 bg-white/5 p-6 rounded-lg border border-white/10">
<h3 class="text-lg font-medium text-[#d4b383] mb-4">添加后缀</h3>
<form @submit.prevent="handleAdd" class="flex gap-4">
<input
v-model="newSuffix"
type="text"
placeholder="@example.com"
class="flex-1 bg-white/5 border border-white/10 rounded px-4 py-2 text-white focus:border-[#d4b383] outline-none"
required
>
<input
v-model.number="newSortOrder"
type="number"
placeholder="排序 (0-99)"
class="w-32 bg-white/5 border border-white/10 rounded px-4 py-2 text-white focus:border-[#d4b383] outline-none"
>
<button type="submit" class="btn-primary whitespace-nowrap">添加</button>
</form>
</div>
<!-- Suffixes Table -->
<div class="table-container">
<table class="admin-table">
<thead>
<tr>
<th>ID</th>
<th>后缀</th>
<th>排序</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="s in suffixes" :key="s.id">
<td class="table-cell">{{ s.id }}</td>
<td class="table-cell">{{ s.suffix }}</td>
<td class="table-cell">{{ s.sortOrder }}</td>
<td class="table-cell">
<span :class="s.isActive ? 'text-green-400' : 'text-red-400'">
{{ s.isActive ? '启用' : '禁用' }}
</span>
</td>
<td class="table-cell actions">
<button class="btn-delete" @click="handleDelete(s.id)" title="删除">
🗑
</button>
</td>
</tr>
</tbody>
</table>
<!-- Empty State -->
<div v-if="suffixes.length === 0" class="empty-state">
<p>暂无配置数据</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { API_BASE, getAuthHeaders, type EmailSuffix } from '../../services/api'
import { useToast } from '../../composables/useToast'
const toast = useToast()
const suffixes = ref<EmailSuffix[]>([])
const newSuffix = ref('')
const newSortOrder = ref(0)
const fetchSuffixes = async () => {
try {
const response = await fetch(`${API_BASE}/admin/email-suffixes`, {
headers: getAuthHeaders()
})
if (response.ok) {
const data = await response.json()
suffixes.value = data.result || []
}
} catch (error) {
console.error('Failed to fetch suffixes:', error)
}
}
const handleAdd = async () => {
if (!newSuffix.value.startsWith('@')) {
toast.error('后缀必须以 @ 开头')
return
}
try {
const response = await fetch(`${API_BASE}/admin/email-suffixes`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({
suffix: newSuffix.value,
isActive: true,
sortOrder: newSortOrder.value
})
})
if (response.ok) {
toast.success('添加成功')
newSuffix.value = ''
newSortOrder.value = 0
fetchSuffixes()
} else {
const errorData = await response.json()
toast.error(errorData.message || '添加失败')
}
} catch (error) {
console.error(error)
toast.error('添加失败')
}
}
const handleDelete = async (id: number) => {
if (!confirm('确定删除该后缀吗?')) return
try {
const response = await fetch(`${API_BASE}/admin/email-suffixes/${id}`, {
method: 'DELETE',
headers: getAuthHeaders()
})
if (response.ok) {
toast.success('删除成功')
fetchSuffixes()
} else {
const errorData = await response.json()
toast.error(errorData.message || '删除失败')
}
} catch (error) {
console.error(error)
toast.error('删除失败')
}
}
onMounted(() => {
fetchSuffixes()
})
</script>
<style scoped>
.suffixes-container {
width: 100%;
}
.page-title {
font-size: 1.75rem;
font-weight: 600;
color: #d4b383;
margin-bottom: 1.5rem;
font-family: 'Inter', sans-serif;
}
.btn-primary {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1.25rem;
background: rgba(212, 179, 131, 0.1);
border: 1px solid #d4b383;
color: #d4b383;
border-radius: 0.5rem;
cursor: pointer;
font-size: 0.95rem;
font-weight: 500;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.btn-primary:hover {
background: rgba(212, 179, 131, 0.2);
}
.table-container {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 0.5rem;
border: 1px solid rgba(255, 255, 255, 0.1);
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.admin-table {
width: 100%;
border-collapse: collapse;
font-family: 'Inter', sans-serif;
}
.admin-table thead {
background: rgba(255, 255, 255, 0.08);
}
.admin-table th {
padding: 1rem;
text-align: left;
font-size: 0.875rem;
font-weight: 600;
color: #d4b383;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.admin-table td {
padding: 1rem;
font-size: 0.95rem;
color: rgba(255, 255, 255, 0.8);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.admin-table tr:last-child td {
border-bottom: none;
}
.btn-delete {
padding: 0.5rem;
border: none;
background: rgba(239, 68, 68, 0.1);
color: rgba(239, 68, 68, 0.8);
border-radius: 0.375rem;
cursor: pointer;
transition: all 0.3s;
}
.btn-delete:hover {
background: rgba(239, 68, 68, 0.2);
}
.empty-state {
padding: 3rem;
text-align: center;
color: rgba(255, 255, 255, 0.5);
}
</style>