Files
app_02_bill/components/EditTransactionModal.tsx
karlcarlo 194de6ec06 feat(api): 集成后端API,实现数据持久化和用户登录
- 新增token管理和用户信息状态,支持基于token的自动登录
- 实现登录或注册接口调用,自动根据URL参数完成身份验证
- 从后端加载交易记录、消息和待办提醒,替代本地缓存读写
- 对交易记录的增删改操作同步到数据库,确保数据一致性
- 对消息和提醒的增删改操作支持服务器同步,提升数据可靠性
- 优化消息发送逻辑,先保存用户消息,再处理AI回复并存库
- 添加待办提醒切换状态API调用,确保状态同步服务器
- 改进界面互动,支持文件与头像上传功能,更新管家形象
- 采用异步处理并增加错误捕获和用户提示,增强稳定性
2026-01-22 18:25:42 +08:00

116 lines
5.8 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Transaction, TransactionType, ExpenseCategory, IncomeCategory } from '../types';
interface EditTransactionModalProps {
transaction: Transaction;
onSave: (updated: Transaction) => void;
onDelete: (id: string) => void;
onClose: () => void;
hideDelete?: boolean;
}
const EditTransactionModal: React.FC<EditTransactionModalProps> = ({ transaction, onSave, onDelete, onClose, hideDelete = false }) => {
const [draft, setDraft] = useState<Transaction>(transaction);
useEffect(() => {
setDraft(transaction);
}, [transaction]);
const handleDelete = () => {
if (confirm('确定要删除这条记录吗?崔经理会盯着你的。')) {
onDelete(draft.id);
}
};
return (
<div className="fixed inset-0 z-[60] flex items-end md:items-center justify-center bg-black/50 backdrop-blur-sm p-4 pb-28 md:pb-4">
<div className="bg-white w-full max-w-md rounded-2xl p-6 shadow-2xl md:animate-none max-h-[calc(100vh-120px)] overflow-y-auto no-scrollbar">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-bold text-gray-900">编辑账单</h3>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
</button>
</div>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">金额</label>
<input
type="number"
value={draft.amount}
onChange={(e) => setDraft({...draft, amount: parseFloat(e.target.value) || 0})}
className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none font-bold text-lg bg-white shadow-sm transition-all"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">内容</label>
<input
type="text"
value={draft.item}
onChange={(e) => setDraft({...draft, item: e.target.value})}
className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none bg-white shadow-sm transition-all"
/>
</div>
<div className="flex gap-4">
<div className="flex-1">
<label className="block text-sm font-medium text-gray-700 mb-1">类型</label>
<select
value={draft.type}
onChange={(e) => setDraft({
...draft,
type: e.target.value as TransactionType,
category: e.target.value === TransactionType.EXPENSE ? ExpenseCategory.MISC : IncomeCategory.OTHER
})}
className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-indigo-500 outline-none bg-white shadow-sm transition-all"
>
<option value={TransactionType.EXPENSE}>支出</option>
<option value={TransactionType.INCOME}>收入</option>
</select>
</div>
<div className="flex-1">
<label className="block text-sm font-medium text-gray-700 mb-1">分类</label>
<select
value={draft.category}
onChange={(e) => setDraft({...draft, category: e.target.value})}
className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-indigo-500 outline-none bg-white shadow-sm transition-all"
>
{draft.type === TransactionType.EXPENSE
? Object.values(ExpenseCategory).map(c => <option key={c} value={c}>{c}</option>)
: Object.values(IncomeCategory).map(c => <option key={c} value={c}>{c}</option>)
}
</select>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">日期</label>
<input
type="date"
value={draft.date}
onChange={(e) => setDraft({...draft, date: e.target.value})}
className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-indigo-500 outline-none bg-white shadow-sm transition-all"
/>
</div>
<div className="pt-4 flex gap-3">
{!hideDelete && (
<button
onClick={handleDelete}
className="flex-1 py-3 px-4 bg-red-50 text-red-600 rounded-xl font-semibold hover:bg-red-100 transition-colors"
>
删除
</button>
)}
<button
onClick={() => onSave(draft)}
className={`py-3 px-4 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition-all shadow-md shadow-indigo-200 ${hideDelete ? 'w-full' : 'flex-[2]'}`}
>
保存修改
</button>
</div>
</div>
</div>
</div>
);
};
export default EditTransactionModal;