import 'package:flutter/material.dart'; import 'package:miaoji_zhang/shared/api/api_client.dart'; import 'package:miaoji_zhang/shared/services/current_ledger_store.dart'; import 'package:miaoji_zhang/shared/theme/app_theme.dart'; import 'package:miaoji_zhang/shared/widgets/app_icons.dart'; import 'package:miaoji_zhang/shared/widgets/app_controls.dart'; /// P17 账本切换底部弹层 class LedgerSheet extends StatefulWidget { const LedgerSheet({super.key}); @override State createState() => _LedgerSheetState(); static Future show(BuildContext context) => showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => const LedgerSheet(), ); } class _LedgerSheetState extends State { List _ledgers = []; bool _loading = true; @override void initState() { super.initState(); _load(); } Future _load() async { try { await CurrentLedgerStore.instance.ensureLoaded(force: true); if (mounted) { setState(() { _ledgers = CurrentLedgerStore.instance.ledgers; _loading = false; }); } } catch (error) { if (mounted) { setState(() => _loading = false); ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(apiErrorMessage(error)))); } } } Future _setDefault(int id) async { try { await CurrentLedgerStore.instance.select(id); if (mounted) Navigator.pop(context, true); } catch (error) { if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(apiErrorMessage(error)))); } } } Future _create() async { final name = await showJzTextInputSheet( context, title: '新建账本', label: '账本名称', maxLength: 12, ); if (name == null || name.isEmpty) return; try { await CurrentLedgerStore.instance.create(name); await _load(); } catch (error) { if (mounted) _showError(error); } } Future _rename(LedgerInfo ledger) async { final name = await showJzTextInputSheet( context, title: '重命名账本', label: '账本名称', initialValue: ledger.name, maxLength: 12, ); if (name == null || name.isEmpty || name == ledger.name) return; try { await CurrentLedgerStore.instance.rename(ledger.id, name, ledger.iconKey); await _load(); } catch (error) { if (mounted) _showError(error); } } Future _delete(LedgerInfo ledger) async { final confirmed = await showJzConfirmSheet( context, title: '删除账本', message: '仅能删除没有账单和预算的非默认账本。', confirmLabel: '删除', destructive: true, ); if (!confirmed) return; try { await CurrentLedgerStore.instance.delete(ledger.id); await _load(); } catch (error) { if (mounted) _showError(error); } } Future _showActions(LedgerInfo ledger) async { final action = await showJzOptionSheet( context, title: ledger.name, options: [ const JzOption(value: 'rename', label: '重命名'), if (!ledger.isDefault) const JzOption(value: 'delete', label: '删除账本', subtitle: '仅空账本可以删除'), ], ); if (!mounted) return; if (action == 'rename') await _rename(ledger); if (action == 'delete') await _delete(ledger); } void _showError(Object error) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(apiErrorMessage(error)))); } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: context.jz.card, borderRadius: BorderRadius.vertical(top: Radius.circular(22)), ), padding: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom + 12, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 36, height: 5, margin: const EdgeInsets.only(top: 10, bottom: 6), decoration: BoxDecoration( color: context.jz.line, borderRadius: BorderRadius.circular(3), ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text( '切换账本', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w700), ), ), if (_loading) Padding( padding: EdgeInsets.all(40), child: CircularProgressIndicator( color: AppTheme.primary, strokeWidth: 2, ), ) else ..._ledgers.map( (l) => Padding( padding: const EdgeInsets.symmetric( horizontal: 18, vertical: 4, ), child: InkWell( onTap: l.isDefault ? null : () => _setDefault(l.id), child: Container( padding: const EdgeInsets.all(13), decoration: BoxDecoration( color: context.jz.card, borderRadius: BorderRadius.circular(14), border: Border.all( color: l.isDefault ? AppTheme.primary : context.jz.line, width: l.isDefault ? 1.5 : 0.5, ), ), child: Row( children: [ Container( width: 34, height: 34, decoration: BoxDecoration( color: l.isDefault ? context.jz.primaryBackground : context.jz.background, borderRadius: BorderRadius.circular(10), ), child: Center( child: AppIcons.byKey( l.iconKey, size: 17, color: l.isDefault ? AppTheme.primary : context.jz.text3, ), ), ), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l.name, style: TextStyle( fontSize: 13.5, fontWeight: FontWeight.w600, color: l.isDefault ? AppTheme.primary : context.jz.text, ), ), Text( '${l.transactionCount} 笔', style: TextStyle( fontSize: 11, color: context.jz.text3, ), ), ], ), ), if (l.isDefault) AppIcons.icon( AppIcons.check, size: 18, color: AppTheme.primary, ), IconButton( tooltip: '账本操作', onPressed: () => _showActions(l), icon: Icon(Icons.more_horiz_rounded, size: 19), ), ], ), ), ), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 4), child: InkWell( onTap: _create, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: context.jz.card, borderRadius: BorderRadius.circular(14), border: Border.all(color: context.jz.line, width: 0.5), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ AppIcons.icon( AppIcons.plus, size: 14, color: context.jz.text2, ), SizedBox(width: 6), Text( '新建账本', style: TextStyle(fontSize: 13, color: context.jz.text2), ), ], ), ), ), ), ], ), ); } }