import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import { isLoggedIn, getRecurringBills, createRecurringBill, updateRecurringBill, deleteRecurringBill, applyRecurringBill } from '../lib/api'; import { DEFAULT_CATEGORIES } from '../lib/categories'; import AppShell from '../components/AppShell'; import { Icon } from '../components/icons'; import { useToast } from '../components/ToastProvider'; import { useConfirm } from '../components/ConfirmProvider'; export default function Recurring() { const router = useRouter(); const { showToast } = useToast(); const { confirm } = useConfirm(); const [bills, setBills] = useState([]); const [modalOpen, setModalOpen] = useState(false); const [editingBill, setEditingBill] = useState(null); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [deleting, setDeleting] = useState(false); const [applyingId, setApplyingId] = useState(null); const [formError, setFormError] = useState(''); const [formData, setFormData] = useState({ type: 'expense', amount: '', category: '', day: '1', note: '', is_active: true, }); useEffect(() => { if (!isLoggedIn()) { router.push('/login'); return; } loadBills(); }, [router]); const loadBills = async () => { setLoading(true); try { const data = await getRecurringBills(); setBills(data); } catch (err) { console.error(err); showToast(err.message, { tone: 'error' }); } finally { setLoading(false); } }; const openAddModal = () => { setEditingBill(null); setFormData({ type: 'expense', amount: '', category: '', day: '1', note: '', is_active: true, }); setFormError(''); setModalOpen(true); }; const openEditModal = (bill) => { setEditingBill(bill); setFormData({ type: bill.type, amount: bill.amount.toString(), category: bill.category, day: bill.day.toString(), note: bill.note || '', is_active: !!bill.is_active, }); setFormError(''); setModalOpen(true); }; const closeModal = () => { if (submitting || deleting) return; setModalOpen(false); setEditingBill(null); setFormError(''); }; const handleSubmit = async (e) => { e.preventDefault(); if (!formData.amount || !formData.category || !formData.day) { setFormError('请填写完整信息'); return; } setSubmitting(true); setFormError(''); try { const data = { type: formData.type, amount: parseFloat(formData.amount), category: formData.category, day: parseInt(formData.day), note: formData.note, is_active: formData.is_active ? 1 : 0, }; if (editingBill) { await updateRecurringBill(editingBill.id, data); showToast('周期账单已更新', { tone: 'success' }); } else { await createRecurringBill(data); showToast('周期账单已创建', { tone: 'success' }); } closeModal(); await loadBills(); } catch (err) { setFormError(err.message); showToast(err.message, { tone: 'error' }); } finally { setSubmitting(false); } }; const handleDelete = async () => { if (!editingBill) return; const shouldDelete = await confirm({ title: '删除这条周期账单?', description: '删除后将无法恢复,请确认是否继续。', confirmText: '删除', }); if (!shouldDelete) return; setDeleting(true); try { await deleteRecurringBill(editingBill.id); closeModal(); showToast('周期账单已删除', { tone: 'success' }); await loadBills(); } catch (err) { setFormError(err.message); showToast(err.message, { tone: 'error' }); } finally { setDeleting(false); } }; const handleApply = async (id) => { setApplyingId(id); try { await applyRecurringBill(id); showToast('已添加到账目', { tone: 'success' }); } catch (err) { showToast(err.message, { tone: 'error' }); } finally { setApplyingId(null); } }; const handleToggleActive = async (bill, event) => { event.stopPropagation(); try { await updateRecurringBill(bill.id, { ...bill, is_active: bill.is_active ? 0 : 1, }); showToast(bill.is_active ? '已停用周期账单' : '已启用周期账单', { tone: 'success' }); await loadBills(); } catch (err) { showToast(err.message, { tone: 'error' }); } }; const days = Array.from({ length: 28 }, (_, index) => index + 1); const actions = ( ); return ( {loading ? (
加载中...
) : bills.length === 0 ? (

暂无周期性账单

点击右下角新增定期收入或支出。

) : (

账单列表

{bills.length} 条周期账单

{bills.map((bill) => { const categories = DEFAULT_CATEGORIES[bill.type]; const cat = categories.find((item) => item.name === bill.category) || { icon: '📝' }; return (
); })}
)} {modalOpen && (
e.stopPropagation()}>

{editingBill ? '编辑账单' : '新增账单'}

{editingBill ? '更新周期账单' : '创建周期账单'}

{formError &&
{formError}
}
setFormData({ ...formData, amount: e.target.value })} placeholder="0.00" step="0.01" required disabled={submitting || deleting} />
{DEFAULT_CATEGORIES[formData.type].map((cat) => ( ))}
setFormData({ ...formData, note: e.target.value })} placeholder="添加备注..." disabled={submitting || deleting} />
{editingBill && (
)}
{editingBill && ( )}
)}
); }