| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import 'package:intl/intl.dart';
- import '../core/network/dio_client.dart';
- import '../data/models/asset/asset_statement.dart';
- import '../data/services/asset_service.dart';
- // ══════════════════════════════════════════════════════════════
- // 全局缓存:币种列表 + 类型列表(不随页面销毁)
- // ══════════════════════════════════════════════════════════════
- class _StatementFilterCache extends Notifier<({List<StatementCoin> coins, List<StatementType> types})> {
- @override
- ({List<StatementCoin> coins, List<StatementType> types}) build() {
- Future.microtask(_load);
- return (coins: const [], types: const []);
- }
- bool get isLoaded => state.coins.isNotEmpty || state.types.isNotEmpty;
- Future<void> _load() async {
- try {
- final dio = ref.read(dioClientProvider);
- final service = AssetService(dio);
- final results = await Future.wait([
- service.getStatementCoins(),
- service.getStatementTypes(),
- ]);
- state = (
- coins: results[0] as List<StatementCoin>,
- types: results[1] as List<StatementType>,
- );
- } catch (_) {}
- }
- /// 强制刷新(下拉刷新时调用)
- Future<void> reload() => _load();
- }
- final statementFilterCacheProvider =
- NotifierProvider<_StatementFilterCache, ({List<StatementCoin> coins, List<StatementType> types})>(
- _StatementFilterCache.new,
- );
- // ══════════════════════════════════════════════════════════════
- // Statement State
- // ══════════════════════════════════════════════════════════════
- class StatementState {
- /// 流水记录
- final List<AssetStatement> records;
- // ── 当前筛选条件(UI 选择,点搜索后才生效)──
- final String selectedCoinCode;
- final String selectedTypeId;
- final DateTime? startDate;
- final DateTime? endDate;
- final bool isLoading;
- final bool hasMore;
- final int currentPage;
- final String? errorMessage;
- const StatementState({
- this.records = const [],
- this.selectedCoinCode = '',
- this.selectedTypeId = '',
- this.startDate,
- this.endDate,
- this.isLoading = false,
- this.hasMore = true,
- this.currentPage = 1,
- this.errorMessage,
- });
- StatementState copyWith({
- List<AssetStatement>? records,
- String? selectedCoinCode,
- String? selectedTypeId,
- DateTime? startDate,
- DateTime? endDate,
- bool clearStartDate = false,
- bool clearEndDate = false,
- bool? isLoading,
- bool? hasMore,
- int? currentPage,
- String? errorMessage,
- }) =>
- StatementState(
- records: records ?? this.records,
- selectedCoinCode: selectedCoinCode ?? this.selectedCoinCode,
- selectedTypeId: selectedTypeId ?? this.selectedTypeId,
- startDate: clearStartDate ? null : (startDate ?? this.startDate),
- endDate: clearEndDate ? null : (endDate ?? this.endDate),
- isLoading: isLoading ?? this.isLoading,
- hasMore: hasMore ?? this.hasMore,
- currentPage: currentPage ?? this.currentPage,
- errorMessage: errorMessage,
- );
- }
- // ══════════════════════════════════════════════════════════════
- // Statement Notifier
- // ══════════════════════════════════════════════════════════════
- class StatementNotifier extends AutoDisposeNotifier<StatementState> {
- static const _pageSize = 10;
- static final _dateFmt = DateFormat('yyyy-MM-dd HH:mm:ss');
- // 搜索时才生效的筛选参数
- String _searchCoin = '';
- String _searchType = '';
- String _searchStart = '';
- String _searchEnd = '';
- @override
- StatementState build() {
- // 只触发缓存加载,不建立 rebuild 依赖(避免缓存更新时重置用户选择)
- ref.read(statementFilterCacheProvider.notifier);
- Future.microtask(_loadRecords);
- return const StatementState(isLoading: true);
- }
- /// 只加载记录(币种/类型从缓存读取)
- Future<void> _loadRecords() async {
- state = state.copyWith(isLoading: true, errorMessage: null);
- try {
- final dio = ref.read(dioClientProvider);
- final records = await AssetService(dio).getStatementList(
- pageNo: 1,
- pageSize: _pageSize,
- );
- state = state.copyWith(
- records: records,
- isLoading: false,
- currentPage: 1,
- hasMore: records.length >= _pageSize,
- );
- } catch (e) {
- state = state.copyWith(isLoading: false, errorMessage: e.toString());
- }
- }
- // ── 筛选条件选择(仅更新 UI,不请求)──
- void selectCoin(String code) {
- state = state.copyWith(selectedCoinCode: code);
- }
- void selectType(String typeId) {
- state = state.copyWith(selectedTypeId: typeId);
- }
- void selectStartDate(DateTime date) {
- if (state.endDate != null && date.isAfter(state.endDate!)) return;
- state = state.copyWith(startDate: date);
- }
- void selectEndDate(DateTime date) {
- if (state.startDate != null && state.startDate!.isAfter(date)) return;
- state = state.copyWith(endDate: date);
- }
- // ── 搜索(收集筛选条件,重新请求)──
- Future<void> search() async {
- if (state.startDate != null && state.endDate == null) {
- state = state.copyWith(errorMessage: 'errEnterEndTime');
- return;
- }
- if (state.endDate != null && state.startDate == null) {
- state = state.copyWith(errorMessage: 'errEnterStartTime');
- return;
- }
- _searchCoin = state.selectedCoinCode;
- _searchType = state.selectedTypeId;
- _searchStart = state.startDate != null ? _dateFmt.format(state.startDate!) : '';
- _searchEnd = state.endDate != null
- ? _dateFmt.format(DateTime(state.endDate!.year, state.endDate!.month, state.endDate!.day, 23, 59, 59))
- : '';
- state = state.copyWith(isLoading: true, errorMessage: null);
- try {
- final dio = ref.read(dioClientProvider);
- final records = await AssetService(dio).getStatementList(
- type: _searchType,
- symbol: _searchCoin,
- startTime: _searchStart,
- endTime: _searchEnd,
- pageNo: 1,
- pageSize: _pageSize,
- );
- state = state.copyWith(
- records: records,
- isLoading: false,
- currentPage: 1,
- hasMore: records.length >= _pageSize,
- );
- } catch (e) {
- state = state.copyWith(isLoading: false, errorMessage: e.toString());
- }
- }
- // ── 重置 ──
- void reset() {
- _searchCoin = '';
- _searchType = '';
- _searchStart = '';
- _searchEnd = '';
- state = state.copyWith(
- selectedCoinCode: '',
- selectedTypeId: '',
- clearStartDate: true,
- clearEndDate: true,
- );
- search();
- }
- // ── 下拉刷新(同时刷新缓存)──
- Future<void> refresh() async {
- ref.read(statementFilterCacheProvider.notifier).reload();
- await search();
- }
- // ── 上拉加载更多 ──
- Future<void> loadMore() async {
- if (state.isLoading || !state.hasMore) return;
- state = state.copyWith(isLoading: true);
- try {
- final nextPage = state.currentPage + 1;
- final dio = ref.read(dioClientProvider);
- final records = await AssetService(dio).getStatementList(
- type: _searchType,
- symbol: _searchCoin,
- startTime: _searchStart,
- endTime: _searchEnd,
- pageNo: nextPage,
- pageSize: _pageSize,
- );
- state = state.copyWith(
- records: [...state.records, ...records],
- isLoading: false,
- currentPage: nextPage,
- hasMore: records.length >= _pageSize,
- );
- } catch (e) {
- state = state.copyWith(isLoading: false, errorMessage: e.toString());
- }
- }
- }
- final statementProvider =
- AutoDisposeNotifierProvider<StatementNotifier, StatementState>(
- StatementNotifier.new,
- );
|