"use client"; import { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { emailConfigsApi, type EmailConfigRead, type EmailConfigCreate, type EmailConfigUpdate, type EmailFolder, } from "@/lib/api/client"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Loader2, Mail, Plus, Pencil, Trash2, FolderOpen } from "lucide-react"; import { toast } from "sonner"; export default function SettingsEmailPage() { const [configs, setConfigs] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); const [editingId, setEditingId] = useState(null); const [saving, setSaving] = useState(false); const [form, setForm] = useState({ host: "", port: "993", user: "", password: "", mailbox: "INBOX", active: true, }); const [folders, setFolders] = useState(null); const [foldersLoading, setFoldersLoading] = useState(false); const loadConfigs = useCallback(async () => { try { const list = await emailConfigsApi.list(); setConfigs(list); } catch { toast.error("加载邮箱列表失败"); } finally { setLoading(false); } }, []); useEffect(() => { loadConfigs(); }, [loadConfigs]); const openAdd = () => { setEditingId(null); setForm({ host: "", port: "993", user: "", password: "", mailbox: "INBOX", active: true, }); setDialogOpen(true); }; const openEdit = (c: EmailConfigRead) => { setEditingId(c.id); setFolders(null); setForm({ host: c.host, port: String(c.port), user: c.user, password: "", mailbox: c.mailbox || "INBOX", active: c.active, }); setDialogOpen(true); }; const loadFolders = async () => { if (!editingId) return; setFoldersLoading(true); try { const res = await emailConfigsApi.listFolders(editingId); setFolders(res.folders); if (res.folders.length > 0 && !form.mailbox) { const inbox = res.folders.find((f) => f.decoded === "INBOX" || f.decoded === "收件箱"); if (inbox) setForm((f) => ({ ...f, mailbox: inbox.decoded })); } toast.success(`已加载 ${res.folders.length} 个邮箱夹`); } catch (e) { toast.error(e instanceof Error ? e.message : "获取邮箱列表失败"); } finally { setFoldersLoading(false); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setSaving(true); try { if (editingId) { const payload: EmailConfigUpdate = { host: form.host, port: parseInt(form.port, 10) || 993, user: form.user, mailbox: form.mailbox, active: form.active, }; if (form.password) payload.password = form.password; await emailConfigsApi.update(editingId, payload); toast.success("已更新"); } else { await emailConfigsApi.create({ host: form.host, port: parseInt(form.port, 10) || 993, user: form.user, password: form.password, mailbox: form.mailbox, active: form.active, }); toast.success("已添加"); } setDialogOpen(false); await loadConfigs(); } catch (e) { toast.error(e instanceof Error ? e.message : "保存失败"); } finally { setSaving(false); } }; const handleDelete = async (id: string) => { if (!confirm("确定删除该邮箱账户?")) return; try { await emailConfigsApi.delete(id); toast.success("已删除"); await loadConfigs(); } catch (e) { toast.error(e instanceof Error ? e.message : "删除失败"); } }; return (
← 设置
邮箱账户

配置多个邮箱用于财务邮件同步(发票、回执、流水)。同步时将遍历所有已启用的账户。

{loading ? (

加载中…

) : configs.length === 0 ? (

暂无邮箱账户。添加后将在「财务归档」同步时使用;未添加时使用环境变量 IMAP_*。

) : ( Host 端口 邮箱 Mailbox 状态 操作 {configs.map((c) => ( {c.host} {c.port} {c.user} {c.mailbox} {c.active ? ( 启用 ) : ( 禁用 )}
))}
)}
网易 163 邮箱配置说明

IMAP 服务器: imap.163.com,端口 993(SSL)。需在网易邮箱网页端开启「IMAP/SMTP 服务」。

密码: 使用「授权码」而非登录密码。在 设置 → POP3/SMTP/IMAP → 授权码管理 中生成。

邮箱夹: 填 INBOX 或 收件箱;若同步失败,请编辑该账户并点击「获取邮箱列表」选择「收件箱」或目标标签。

{editingId ? "编辑邮箱账户" : "添加邮箱账户"}
setForm((f) => ({ ...f, host: e.target.value }))} placeholder="imap.163.com" required />
setForm((f) => ({ ...f, port: e.target.value }))} placeholder="993" />
setForm((f) => ({ ...f, user: e.target.value }))} placeholder="user@example.com" required />
setForm((f) => ({ ...f, password: e.target.value }))} placeholder={editingId ? "••••••••" : "请输入"} autoComplete="off" required={!editingId} />
{editingId && (
)} {folders && folders.length > 0 ? ( ) : ( setForm((f) => ({ ...f, mailbox: e.target.value }))} placeholder="INBOX、收件箱或自定义标签(163 等若 INBOX 失败会自动尝试收件箱)" /> )}
setForm((f) => ({ ...f, active: e.target.checked }))} className="rounded border-input" />
); }