import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import '../../../common/domain/money.dart'; import '../../../common/widgets/prototype_ui.dart'; import '../../market/application/market_controller.dart'; import '../application/asset_portfolio_controller.dart'; import '../data/asset_models.dart'; class AssetEditPage extends ConsumerStatefulWidget { const AssetEditPage({super.key, required this.id}); final String id; @override ConsumerState createState() => _AssetEditPageState(); } class _AssetEditPageState extends ConsumerState { late GoldAssetHolding _holding; late final TextEditingController _weightCtrl; late final TextEditingController _costCtrl; late final TextEditingController _dateCtrl; late final TextEditingController _channelCtrl; double _gram = 0; @override void initState() { super.initState(); _holding = ref .read(assetPortfolioControllerProvider) .holdings .map((item) => item.holding) .firstWhere((item) => item.id == widget.id); _gram = _holding.weightGram; _weightCtrl = TextEditingController( text: _holding.weightGram.toStringAsFixed( _holding.weightGram % 1 == 0 ? 0 : 1, ), ); _costCtrl = TextEditingController( text: _holding.costAmount?.toStringAsFixed(0) ?? '', ); _dateCtrl = TextEditingController( text: _holding.purchaseDate == null ? '' : '${_holding.purchaseDate!.year}-${_holding.purchaseDate!.month.toString().padLeft(2, '0')}-${_holding.purchaseDate!.day.toString().padLeft(2, '0')}', ); _channelCtrl = TextEditingController(text: _holding.channel ?? ''); } @override void dispose() { _weightCtrl.dispose(); _costCtrl.dispose(); _dateCtrl.dispose(); _channelCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final summary = ref.watch(assetPortfolioControllerProvider); final valuation = summary.holdings.firstWhere( (item) => item.holding.id == widget.id, ); _holding = valuation.holding; final cs = ShadTheme.of(context).colorScheme; final market = ref.watch(marketControllerProvider); final quote = market.quotes.firstWhere( (item) => item.metal == _holding.metal, ); final value = _gram * _holding.purity * quote.spotPrice; return Scaffold( backgroundColor: const Color(0xFFFBFAF7), appBar: AppBar( backgroundColor: const Color(0xFFFBFAF7), surfaceTintColor: Colors.transparent, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, title: Text( '编辑·${_holding.name}', style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600), ), leading: IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.chevron_left, size: 20), ), ), body: SafeArea( top: false, child: ListView( padding: const EdgeInsets.fromLTRB(0, 8, 0, 24), children: [ PrototypeFrame( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ PrototypeCard( child: Row( children: [ Text( '金属 / 纯度', style: TextStyle( fontSize: 13, color: cs.mutedForeground, ), ), const Spacer(), Text( '${_holding.metal.label} · ${_holding.purityLabel}', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), ), ], ), ), const SizedBox(height: 16), const Text( '克重(克)', style: TextStyle(fontSize: 13, color: Color(0xFF8A8780)), ), const SizedBox(height: 9), Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 16, ), decoration: BoxDecoration( color: Colors.white, border: Border.all( color: const Color(0xFFE3D6B4), width: 0.5, ), borderRadius: BorderRadius.circular(13), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _gram.toStringAsFixed(_gram % 1 == 0 ? 0 : 1), style: const TextStyle( fontSize: 32, fontWeight: FontWeight.w600, height: 1, ), ), const SizedBox(width: 6), const Padding( padding: EdgeInsets.only(bottom: 2), child: Text( '克', style: TextStyle( fontSize: 15, color: Color(0xFF8A8780), ), ), ), ], ), ), Slider( min: 1, max: 200, value: _gram.clamp(1, 200), onChanged: (value) => setState(() { _gram = value; _weightCtrl.text = value.toStringAsFixed( value % 1 == 0 ? 0 : 1, ); }), activeColor: const Color(0xFFB5862A), inactiveColor: const Color(0xFFECEAE3), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.fromLTRB(16, 15, 16, 16), decoration: BoxDecoration( color: const Color(0xFFF6ECD6), borderRadius: BorderRadius.circular(14), ), child: Row( children: [ const Expanded( child: Text( '实时材料价值', style: TextStyle( fontSize: 13, color: Color(0xFF7A5B12), ), ), ), Text( formatCny(value), style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w600, ), ), ], ), ), const SizedBox(height: 14), _FieldRow( label: '买入成本(含工费)', child: TextField( controller: _costCtrl, keyboardType: const TextInputType.numberWithOptions( decimal: true, ), textAlign: TextAlign.right, decoration: const InputDecoration( border: InputBorder.none, isDense: true, hintText: '选填', ), ), ), const SizedBox(height: 10), _FieldRow( label: '购买日期', child: TextField( controller: _dateCtrl, textAlign: TextAlign.right, decoration: const InputDecoration( border: InputBorder.none, isDense: true, hintText: 'YYYY-MM-DD', ), ), ), const SizedBox(height: 10), _FieldRow( label: '购买渠道', child: Wrap( spacing: 7, runSpacing: 7, alignment: WrapAlignment.end, children: [ for (final channel in ['银行', '金店', '电商', '回收市场', '其他']) _Chip( label: channel, selected: _channelCtrl.text == channel, onTap: () => setState(() => _channelCtrl.text = channel), ), ], ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: FilledButton( onPressed: () { final grams = double.tryParse( _weightCtrl.text.trim(), ); final cost = double.tryParse(_costCtrl.text.trim()); final parsedDate = DateTime.tryParse( _dateCtrl.text.trim(), ); ref .read(assetPortfolioControllerProvider.notifier) .updateHolding( GoldAssetHolding( id: _holding.id, name: _holding.name, metal: _holding.metal, category: _holding.category, purity: _holding.purity, purityLabel: _holding.purityLabel, weightGram: grams ?? _holding.weightGram, costAmount: cost, purchaseDate: parsedDate, channel: _channelCtrl.text.trim().isEmpty ? null : _channelCtrl.text.trim(), createdAt: _holding.createdAt, updatedAt: DateTime.now(), status: _holding.status, ), ); Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('已保存修改 · 总值已重算')), ); }, child: const Text('保存修改'), ), ), ], ), const SizedBox(height: 10), Text( '修改后会即时重算资产总值', textAlign: TextAlign.center, style: TextStyle(fontSize: 11, color: cs.mutedForeground), ), ], ), ), ], ), ), ); } } class _FieldRow extends StatelessWidget { const _FieldRow({required this.label, required this.child}); final String label; final Widget child; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.fromLTRB(14, 12, 14, 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFECEAE3), width: 0.5), ), child: Row( children: [ Text( label, style: const TextStyle(fontSize: 13, color: Color(0xFF8A8780)), ), const SizedBox(width: 10), Expanded(child: child), ], ), ); } } class _Chip extends StatelessWidget { const _Chip({ required this.label, required this.selected, required this.onTap, }); final String label; final bool selected; final VoidCallback onTap; @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 6), decoration: BoxDecoration( color: selected ? const Color(0xFFF6ECD6) : Colors.white, borderRadius: BorderRadius.circular(999), border: Border.all( color: selected ? const Color(0xFFE3D6B4) : const Color(0xFFECEAE3), width: 0.5, ), ), child: Text( label, style: TextStyle( fontSize: 12, fontWeight: selected ? FontWeight.w600 : FontWeight.w500, color: selected ? const Color(0xFF7A5B12) : const Color(0xFF5F5E5A), ), ), ), ); } }