import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/record.dart'; import '../data/categories.dart'; import 'package:uuid/uuid.dart'; import 'package:decimal/decimal.dart'; class EditPage extends StatefulWidget { final Record? record; final Function(Record) onSave; // 添加保存回调 const EditPage({ Key? key, this.record, required this.onSave, }) : super(key: key); @override State createState() => _EditPageState(); } class _EditPageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; String? _selectedCategoryId; String _note = ''; double _amount = 0.0; DateTime _selectedDate = DateTime.now(); final _uuid = const Uuid(); String _inputBuffer = '0.0'; // 用于显示的字符串 String? _pendingOperation; // 等待执行的运算符 String? _pendingValue; // 等待计算的第一个值 String? _inputValue; // 当前输入的值 final d = (String s) => Decimal.parse(s); @override void initState() { super.initState(); // 先初始化 TabController _tabController = TabController( length: 2, vsync: this, initialIndex: widget.record?.type == RecordType.income ? 1 : 0, ); _tabController.addListener(_onTabChanged); // 初始化数据 if (widget.record != null) { // 编辑模式:初始化现有记录的数据 _selectedCategoryId = widget.record!.categoryId; _note = widget.record!.note ?? ''; _amount = widget.record!.amount; _selectedDate = widget.record!.createTime; // 设置输入缓冲区为当前金额的格式化字符串 _inputValue = _formatNumberForDisplay(widget.record!.amount); } else { // 新建模式:默认选中第一个分类 _selectedCategoryId = _currentCategories.first.id; } } /// 标签切换监听 void _onTabChanged() { setState(() { // 切换类型时选中新类型的第一个分类 _selectedCategoryId = _currentCategories.first.id; }); } /// 获取当前记录类型 RecordType get _currentType => _tabController.index == 0 ? RecordType.expense : RecordType.income; /// 获取当前分类列表 List get _currentCategories => CategoryConfig.getCategoriesByType(_currentType); /// 保存记录 void _saveRecord() { if (_selectedCategoryId == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请选择分类')), ); return; } if (_amount <= 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('输入金额请大于0')), ); return; } final record = Record( id: widget.record?.id ?? _uuid.v4(), type: _currentType, categoryId: _selectedCategoryId!, note: _note.isEmpty ? null : _note, amount: _amount, createTime: _selectedDate, ); widget.onSave(record); Navigator.of(context).pop(); } /// 显示金额 String get _displayAmount { if (_pendingOperation != null || _pendingValue != null || _inputValue != null) { _inputBuffer = '${_pendingValue ?? ""} ${_pendingOperation ?? ""} ${_inputValue ?? ""}'; } else { _inputBuffer = '0.0'; } return _inputBuffer; } /// 处理数字输入 void _handleNumber(String digit) { setState(() { if (_inputValue == '0' || _inputValue == null) { // 当前是0 _inputValue = digit; } else { // 其他情况追加数字 _inputValue = _inputValue! + digit; } }); } /// 处理小数点 void _handleDecimal() { setState(() { if (_inputValue == null || _inputValue == '0') { _inputValue = '0.'; } else if (_inputValue!.contains('.')) { HapticFeedback.heavyImpact(); } else { _inputValue = '$_inputValue.'; } }); } /// 处理删除 void _handleDelete() { setState(() { if (_inputValue != null) { if (_inputValue!.length > 1) { _inputValue = _inputValue!.substring(0, _inputValue!.length - 1); } else { _inputValue = null; } } else if (_pendingOperation != null) { _pendingOperation = null; _inputValue = _pendingValue; _pendingValue = null; } else if (_pendingValue != null) { if (_pendingValue!.length > 1) { _pendingValue = _pendingValue!.substring(0, _pendingValue!.length - 1); } else { _pendingValue = null; } } else { _pendingValue = null; } }); } /// 处理运算符 void _handleOperator(String operator) { // 如果有待处理的运算,先计算结果 if (_pendingOperation != null) { // 先保存当前运算符,因为计算可能会重置状态 final newOperator = operator; _calculateResult(); _pendingValue = _inputValue; _inputValue = null; // 计算完成后,设置新的运算符 setState(() { // 如果计算导致重置(结果为0),不添加新的运算符 if (_inputBuffer == '0.0') { return; } else { _pendingOperation = newOperator; } }); } else if (_inputValue != null ) { // 正常设置运算符 setState(() { _pendingOperation = operator; _pendingValue = _inputValue!; _inputValue = null; }); } else { HapticFeedback.heavyImpact(); } } /// 计算结果 void _calculateResult() { double result = 0; if(_pendingValue == null && _inputValue== null){ return; } else if (_pendingOperation != null && _pendingValue != null && _inputValue != null) { switch (_pendingOperation) { case '+': result = (d(_pendingValue!) + d(_inputValue!)).toDouble(); if (result == 0) { _resetToDefault(); return; } break; case '-': result = (d(_pendingValue!) - d(_inputValue!)).toDouble(); if (result <= 0) { _resetToDefault(); return; } break; default: return; } } else { result = double.parse(_inputValue ?? _pendingValue!); } setState(() { // 格式化结果,去掉不必要的小数位 _pendingValue = null; _amount = result; _pendingOperation = null; _inputValue = _formatNumberForDisplay(result); }); } /// 重置到默认状态 void _resetToDefault() { setState(() { _inputBuffer = '0.0'; _amount = 0; _pendingOperation = null; _pendingValue = null; _inputValue = null; }); } /// 格式化数字用于显示 String _formatNumberForDisplay(double number) { // 如果是整数,直接返回整数部分 if (number % 1 == 0) { return number.toInt().toString(); } // 如果是小数,去掉末尾的0 String numStr = number.toString(); while (numStr.endsWith('0')) { numStr = numStr.substring(0, numStr.length - 1); } if (numStr.endsWith('.')) { numStr = numStr.substring(0, numStr.length - 1); } return numStr; } /// 修改键盘按钮处理逻辑 void _onKeyboardButtonPressed(String value) { if (_inputBuffer.length >= 20 && value != '删除' && value != '保存'&& value != 'again'){ ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('输入数字过大')), ); return; } switch (value) { case '删除': _handleDelete(); break; case '保存': _calculateResult(); _saveRecord(); break; case 'again': // TODO: 实现again功能 break; case '+': case '-': _handleOperator(value); break; case '.': _handleDecimal(); break; default: _handleNumber(value); } } /// 格式化日期显示 String _formatDate(DateTime date) { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final dateToCheck = DateTime(date.year, date.month, date.day); final difference = today.difference(dateToCheck).inDays; // 检查是否是今天、昨天、前天 switch (difference) { case 0: return '今天'; case 1: return '昨天'; case 2: return '前天'; default: // 如果是当年 if (date.year == now.year) { return '${date.month}/${date.day}'; } // 不是当年 return '${date.year}/${date.month}/${date.day}'; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: TabBar( controller: _tabController, tabs: const [ Tab(text: '支出'), Tab(text: '收入'), ], ), ), body: Column( children: [ // 分类网格 Expanded( child: GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 16, crossAxisSpacing: 16, ), itemCount: _currentCategories.length, itemBuilder: (context, index) { final category = _currentCategories[index]; final isSelected = category.id == _selectedCategoryId; return _buildCategoryItem(category, isSelected); }, ), ), // 底部编辑区域 _buildBottomEditor(), ], ), ); } /// 构建分类项 Widget _buildCategoryItem(Category category, bool isSelected) { return InkWell( onTap: () => setState(() => _selectedCategoryId = category.id), child: Container( decoration: BoxDecoration( color: isSelected ? Theme.of(context).primaryColor.withOpacity(0.1) : null, border: Border.all( color: isSelected ? Theme.of(context).primaryColor : Colors.grey, ), borderRadius: BorderRadius.circular(8), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( category.icon, color: isSelected ? Theme.of(context).primaryColor : Colors.grey, ), const SizedBox(height: 4), Text( category.name, style: TextStyle( color: isSelected ? Theme.of(context).primaryColor : Colors.grey, ), ), ], ), ), ); } /// 构建底部编辑区域 Widget _buildBottomEditor() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, -2), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 备注和金额 _buildNoteAndAmount(), // 配置按钮 Row( children: [ TextButton.icon( onPressed: () { // TODO: 实现账户选择 }, icon: const Icon(Icons.account_balance_wallet), label: const Text('默认账户'), ), TextButton.icon( onPressed: () async { final date = await showDatePicker( context: context, initialDate: _selectedDate, firstDate: DateTime(2000), lastDate: DateTime.now(), ); if (date != null) { setState(() => _selectedDate = date); } }, icon: const Icon(Icons.calendar_today), label: Text(_formatDate(_selectedDate)), ), TextButton.icon( onPressed: () { // TODO: 实现图片选择 }, icon: const Icon(Icons.photo), label: const Text('图片'), ), ], ), // 数字键盘 _buildNumberKeyboard(), ], ), ); } /// 构建数字键盘 Widget _buildNumberKeyboard() { return GridView.count( shrinkWrap: true, crossAxisCount: 4, childAspectRatio: 2, children: [ _buildKeyboardButton('7'), _buildKeyboardButton('8'), _buildKeyboardButton('9'), _buildKeyboardButton('删除', isFunction: true), _buildKeyboardButton('4'), _buildKeyboardButton('5'), _buildKeyboardButton('6'), _buildKeyboardButton('+'), _buildKeyboardButton('1'), _buildKeyboardButton('2'), _buildKeyboardButton('3'), _buildKeyboardButton('-'), _buildKeyboardButton('again'), _buildKeyboardButton('0'), _buildKeyboardButton('.'), _buildKeyboardButton('保存', isFunction: true), ], ); } /// 构建键盘按钮 Widget _buildKeyboardButton(String text, {bool isFunction = false}) { return TextButton( onPressed: () => _onKeyboardButtonPressed(text), child: Text( text, style: TextStyle( fontSize: 20, color: isFunction ? Theme.of(context).primaryColor : Colors.black, ), ), ); } /// 构建底部编辑区域中的备注和金额行 Widget _buildNoteAndAmount() { return Row( children: [ Expanded( child: TextField( controller: TextEditingController(text: _note), // 使用控制器设置初始值 decoration: const InputDecoration( hintText: '添加备注', border: InputBorder.none, ), onChanged: (value) => setState(() => _note = value), ), ), Text( _displayAmount, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), ], ); } @override void dispose() { _tabController.dispose(); super.dispose(); } }