import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/api/api_client.dart'; import '../core/auth/auth_state.dart'; import '../core/config/app_constants.dart'; import '../core/models/page_result.dart'; import '../models/finance.dart'; import '../repositories/finance_repository.dart'; final financeRepositoryProvider = Provider((ref) { return FinanceRepository(ref.watch(apiClientProvider)); }); final financeListProvider = AsyncNotifierProvider>( FinanceListNotifier.new, ); class FinanceListNotifier extends AsyncNotifier> { int _page = 1; int _pageSize = AppConstants.defaultPageSize; String _type = ''; String _month = ''; String _startDate = ''; String _endDate = ''; @override Future> build() { ref.watch(authStateProvider.select((s) => s.user?.shopId)); return _fetch(); } Future> _fetch() { return ref.read(financeRepositoryProvider).listRecords( type: _type.isEmpty ? null : _type, month: _month.isEmpty ? null : _month, startDate: _startDate.isEmpty ? null : _startDate, endDate: _endDate.isEmpty ? null : _endDate, page: _page, pageSize: _pageSize, ); } void setType(String type) { _type = type; _page = 1; reload(); } void setMonth(String month) { _month = month; _startDate = ''; _endDate = ''; _page = 1; reload(); } /// 日期区间(本季/自定义时间范围);与 month 互斥。 void setRange(String startDate, String endDate) { _startDate = startDate; _endDate = endDate; _month = ''; _page = 1; reload(); } void setPage(int page) { _page = page; reload(); } void setPageSize(int pageSize) { _pageSize = pageSize; _page = 1; reload(); } void reload() { // 保留上一次数据(isReloading)→ 屏幕渲染旧内容 + 半透明进度,不白屏。 state = const AsyncValue>.loading() .copyWithPrevious(state); _fetch().then( (result) => state = AsyncValue.data(result), onError: (e, st) => state = AsyncValue>.error(e, st) .copyWithPrevious(state), ); } } /// 收支趋势(近 6 个自然月,柱状图数据源)。 final financeTrendProvider = FutureProvider>((ref) async { ref.watch(authStateProvider.select((s) => s.user?.shopId)); return ref.read(financeRepositoryProvider).trend(months: 6); }); /// 应收/应付按往来单位汇总行(合并 /finance/summary 的 recv/pay 两类)。 class PartnerFinanceRow { final int? partnerId; final String name; final double recv; final double pay; final int openCount; const PartnerFinanceRow( {this.partnerId, required this.name, this.recv = 0, this.pay = 0, this.openCount = 0}); double get net => recv - pay; } final financePartnerRowsProvider = FutureProvider>((ref) async { ref.watch(authStateProvider.select((s) => s.user?.shopId)); final rows = await ref.read(financeRepositoryProvider).partnerSummary(); final map = {}; for (final r in rows) { final key = '${r.partnerId ?? 0}|${r.partnerName}'; final cur = map[key] ?? PartnerFinanceRow( partnerId: r.partnerId, name: r.partnerName.isEmpty ? '(未指定单位)' : r.partnerName); map[key] = PartnerFinanceRow( partnerId: cur.partnerId, name: cur.name, recv: cur.recv + (r.type == 'receivable' ? r.totalAmount : 0), pay: cur.pay + (r.type == 'payable' ? r.totalAmount : 0), openCount: cur.openCount + r.recordCount, ); } final list = map.values.toList() ..sort((a, b) => (b.recv + b.pay).compareTo(a.recv + a.pay)); return list; });