"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { App, Button, Card, Form, Input, Popconfirm, Select, Space, Spin, Tag, Typography, } from "antd"; import { createImapFilterApi, deleteImapFilterApi, getImapPullSettingsApi, updateImapFilterApi, updateImapPollIntervalApi, } from "@/lib/client-api"; import type { ImapFilterKind, ImapFilterRuleItem } from "@/types"; const INTERVAL_PRESETS: Array<{ label: string; value: number }> = [ { label: "3 分钟", value: 3 * 60_000 }, { label: "5 分钟", value: 5 * 60_000 }, { label: "10 分钟", value: 10 * 60_000 }, { label: "15 分钟", value: 15 * 60_000 }, { label: "30 分钟(默认)", value: 30 * 60_000 }, { label: "1 小时", value: 60 * 60_000 }, { label: "6 小时", value: 6 * 60 * 60_000 }, { label: "12 小时", value: 12 * 60 * 60_000 }, { label: "24 小时", value: 24 * 60 * 60_000 }, { label: "3 天", value: 3 * 24 * 60 * 60_000 }, { label: "7 天", value: 7 * 24 * 60 * 60_000 }, ]; const KIND_ORDER: ImapFilterKind[] = [ "keyword", "sender_whitelist", "sender_blacklist", "keyword_blacklist", ]; const KIND_LABEL: Record = { keyword: "关键词(白)", sender_whitelist: "发件人白名单", sender_blacklist: "发件人黑名单", keyword_blacklist: "关键词黑名单", }; const KIND_COLOR: Record = { keyword: "blue", sender_whitelist: "green", sender_blacklist: "magenta", keyword_blacklist: "orange", }; const KIND_OPTIONS = KIND_ORDER.map((value) => ({ value, label: KIND_LABEL[value], })); export function ImapPullSettingsCard() { const { message } = App.useApp(); const [loading, setLoading] = useState(true); const [savingInterval, setSavingInterval] = useState(false); const [adding, setAdding] = useState(false); const [intervalMs, setIntervalMs] = useState(30 * 60_000); const [filters, setFilters] = useState([]); const [form] = Form.useForm<{ kind: ImapFilterKind; value: string }>(); const grouped = useMemo(() => { const map = new Map(); for (const kind of KIND_ORDER) map.set(kind, []); for (const f of filters) { const list = map.get(f.kind) || []; list.push(f); map.set(f.kind, list); } return KIND_ORDER.map((kind) => ({ kind, label: KIND_LABEL[kind], color: KIND_COLOR[kind] || "default", items: map.get(kind) || [], })).filter((g) => g.items.length > 0); }, [filters]); const reload = useCallback(async () => { setLoading(true); const res = await getImapPullSettingsApi(); setLoading(false); if (!res.ok) { message.error(res.error.message); return; } setIntervalMs(res.data.poll_interval_ms); setFilters(res.data.filters || []); }, [message]); useEffect(() => { void reload(); }, [reload]); const onSaveInterval = async () => { setSavingInterval(true); const res = await updateImapPollIntervalApi(intervalMs); setSavingInterval(false); if (!res.ok) { message.error(res.error.message); return; } message.success("拉取间隔已保存(worker 下一轮生效)"); setIntervalMs(res.data.poll_interval_ms); }; const onAdd = async () => { const values = await form.validateFields(); setAdding(true); const res = await createImapFilterApi({ kind: values.kind, value: values.value, enabled: true, }); setAdding(false); if (!res.ok) { message.error(res.error.message); return; } message.success("已添加规则"); form.resetFields(["value"]); void reload(); }; const onToggle = async (row: ImapFilterRuleItem) => { const res = await updateImapFilterApi(row.id, { enabled: !row.enabled }); if (!res.ok) message.error(res.error.message); else void reload(); }; const onDelete = async (row: ImapFilterRuleItem) => { const res = await deleteImapFilterApi(row.id); if (!res.ok) message.error(res.error.message); else { message.success("已删除"); void reload(); } }; return (
拉取间隔 {filters.length === 0 ? ( 暂无规则(将使用业务相关兜底) ) : (
{grouped.map((g) => (
{g.label} {g.items.map((f) => ( void onDelete(f)} okText="删除" cancelText="取消" > e.stopPropagation()} > × } onClose={(e) => e.preventDefault()} onClick={() => void onToggle(f)} style={{ cursor: "pointer", marginInlineEnd: 0, opacity: f.enabled ? 1 : 0.45, textDecoration: f.enabled ? "none" : "line-through", userSelect: "none", }} title={f.enabled ? "点击停用" : "点击启用"} > {f.value} ))}
))}
)}
); }