import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/bill_api.dart'; import '../core/network/api_exception.dart'; import '../models/bill_request.dart'; import '../models/ocr_result.dart'; import '../utils/date_util.dart'; import 'bill_refresh.dart'; /// 手动记账表单状态 class AddBillFormState { final int type; // 1=支出 2=收入 final int? categoryId; final double? amount; final String billDate; // yyyy-MM-dd final String description; final bool submitting; final int revision; // 每次 fillFromOcr 外部填充时 +1,供 UI 同步非受控输入框 const AddBillFormState({ this.type = 1, this.categoryId, this.amount, required this.billDate, this.description = '', this.submitting = false, this.revision = 0, }); AddBillFormState copyWith({ int? type, int? categoryId, Object? amount = _sentinel, String? billDate, String? description, bool? submitting, int? revision, }) { return AddBillFormState( type: type ?? this.type, categoryId: categoryId ?? this.categoryId, amount: amount == _sentinel ? this.amount : amount as double?, billDate: billDate ?? this.billDate, description: description ?? this.description, submitting: submitting ?? this.submitting, revision: revision ?? this.revision, ); } static const _sentinel = Object(); } class AddBillFormNotifier extends Notifier { @override AddBillFormState build() => AddBillFormState(billDate: formatDate(DateTime.now())); void setType(int type) { if (type == state.type) return; // 类型切换后分类需重选(对齐 uniApp watch 行为) state = state.copyWith(type: type, categoryId: null); } void setCategory(int id) => state = state.copyWith(categoryId: id); void setAmount(double? value) => state = state.copyWith(amount: value); void setDate(String date) => state = state.copyWith(billDate: date); void setDescription(String desc) => state = state.copyWith(description: desc); /// 保存账单,成功返回 true Future submit() async { final s = state; if (s.categoryId == null) throw ApiException(-1, '请选择分类'); if (s.amount == null || s.amount! <= 0) throw ApiException(-1, '请输入有效金额'); state = state.copyWith(submitting: true); try { await ref.read(billApiProvider).createBill(BillRequest( categoryId: s.categoryId!, amount: s.amount!, description: s.description, billDate: s.billDate, type: s.type, )); // 让首页列表与统计页重新取数 invalidateBillData(ref); _resetAfterSubmit(); return true; } finally { state = state.copyWith(submitting: false); } } void _resetAfterSubmit() { state = AddBillFormState( type: state.type, billDate: formatDate(DateTime.now()), ); } /// OCR「选择此条」:按金额正负推断类型并填充表单 void fillFromOcr(OcrResult ocr) { state = AddBillFormState( type: ocr.isExpense ? 1 : 2, amount: ocr.absAmount, description: ocr.merchant ?? '', billDate: parseBillDate(ocr.date), revision: state.revision + 1, ); } } final addBillFormProvider = NotifierProvider(AddBillFormNotifier.new);