import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import '../../../common/domain/metal_type.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 AssetAddPage extends ConsumerStatefulWidget { const AssetAddPage({super.key}); @override ConsumerState createState() => _AssetAddPageState(); } class _AssetAddPageState extends ConsumerState { static const _cats = [ AssetCategory.bar, AssetCategory.necklace, AssetCategory.ring, AssetCategory.bracelet, AssetCategory.bean, AssetCategory.earring, AssetCategory.silverware, AssetCategory.platinumPiece, ]; static const _metalPurities = { MetalType.gold: ['足金9999', '足金999', '22K', '18K'], MetalType.platinum: ['PT990', 'PT950'], MetalType.silver: ['999银', '925银'], }; AssetCategory _cat = AssetCategory.bar; MetalType _metal = MetalType.gold; int _purityIndex = 0; double _gram = 50; bool _expanded = false; final _costCtrl = TextEditingController(); final _noteCtrl = TextEditingController(); final _dateCtrl = TextEditingController(text: '2025-08-12'); final _channelCtrl = TextEditingController(text: '银行'); @override void dispose() { _costCtrl.dispose(); _noteCtrl.dispose(); _dateCtrl.dispose(); _channelCtrl.dispose(); super.dispose(); } List get _purityOptions => _metalPurities[_metal]!; String get _purity => _purityOptions[_purityIndex.clamp(0, _purityOptions.length - 1)]; void _cyclePurity() { setState(() { _purityIndex = (_purityIndex + 1) % _purityOptions.length; }); } double _factorFor(String purity) { return switch (purity) { '足金9999' => 0.9999, '足金999' => 0.999, '22K' => 0.916, '18K' => 0.75, 'PT990' => 0.99, 'PT950' => 0.95, '999银' => 0.999, '925银' => 0.925, _ => 1, }; } String _nameForSelection() { return switch (_cat) { AssetCategory.bar => '投资金条', AssetCategory.necklace => '${_metal.label}项链', AssetCategory.ring => '${_metal.label}戒指', AssetCategory.bracelet => '${_metal.label}手镯', AssetCategory.bean => '金豆', AssetCategory.earring => '${_metal.label}耳环', AssetCategory.silverware => '银饰', AssetCategory.platinumPiece => 'PT饰品', }; } @override Widget build(BuildContext context) { final cs = ShadTheme.of(context).colorScheme; final market = ref.watch(marketControllerProvider); final quote = market.quotes.firstWhere((item) => item.metal == _metal); final basePrice = quote.spotPrice; final value = _gram * _factorFor(_purity) * basePrice; return Scaffold( backgroundColor: const Color(0xFFFBFAF7), appBar: AppBar( backgroundColor: const Color(0xFFFBFAF7), surfaceTintColor: Colors.transparent, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, title: const Text( '添加资产', style: 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.start, mainAxisSize: MainAxisSize.min, children: [ const Text( '选择品类', style: TextStyle(fontSize: 13, color: Color(0xFF8A8780)), ), const SizedBox(height: 10), Wrap( spacing: 9, runSpacing: 9, children: [ for (final cat in _cats) _Chip( label: cat.label, selected: cat == _cat, onTap: () => setState(() { _cat = cat; if (cat == AssetCategory.silverware) { _metal = MetalType.silver; } else if (cat == AssetCategory.platinumPiece) { _metal = MetalType.platinum; } else { _metal = MetalType.gold; } _purityIndex = 0; }), ), ], ), const SizedBox(height: 16), Container( padding: const EdgeInsets.fromLTRB(16, 13, 16, 13), decoration: BoxDecoration( color: Colors.white, border: Border.all( color: const Color(0xFFE3D6B4), width: 0.5, ), borderRadius: BorderRadius.circular(13), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '金属 / 纯度', style: TextStyle( fontSize: 13, color: Color(0xFF8A8780), ), ), GestureDetector( onTap: _cyclePurity, child: Row( children: [ Text( '${_metal.label} · $_purity', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), ), const SizedBox(width: 7), const Icon( Icons.unfold_more, size: 16, color: Color(0xFFB3AFA5), ), ], ), ), ], ), ), const SizedBox(height: 16), const Text( '克重(克)', style: TextStyle(fontSize: 13, color: Color(0xFF8A8780)), ), const SizedBox(height: 9), Row( children: [ _StepButton( icon: Icons.remove, onTap: () => setState(() => _gram = (_gram - 1).clamp(1, 200)), ), const SizedBox(width: 12), Expanded( child: Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), 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), ), ), ), ], ), ), ), const SizedBox(width: 12), _StepButton( icon: Icons.add, onTap: () => setState(() => _gram = (_gram + 1).clamp(1, 200)), ), ], ), Slider( min: 1, max: 200, value: _gram.clamp(1, 200), onChanged: (value) => setState(() => _gram = value), 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: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '实时材料价值', style: TextStyle( fontSize: 13, color: Color(0xFF7A5B12), ), ), Text( formatCny(value), style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w600, ), ), ], ), const SizedBox(height: 7), Text( '按${_metal.label}现货 ${basePrice.toStringAsFixed(2)}/克 · 非金店零售价、非回收实收价', style: const TextStyle( fontSize: 11, color: Color(0xFF9A7A2A), ), ), ], ), ), const SizedBox(height: 14), GestureDetector( onTap: () => setState(() => _expanded = !_expanded), child: Container( padding: const EdgeInsets.fromLTRB(16, 13, 16, 13), decoration: BoxDecoration( color: cs.card, borderRadius: BorderRadius.circular(14), border: Border.all(color: cs.border, width: 0.5), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '选填:买入成本 · 日期 · 渠道 · 备注 · 照片', style: TextStyle( fontSize: 13, color: cs.mutedForeground, ), ), Icon( _expanded ? Icons.expand_less : Icons.expand_more, color: cs.mutedForeground, size: 18, ), ], ), ), ), if (_expanded) ...[ const SizedBox(height: 12), _FieldRow( label: '买入成本(含工费)', child: TextField( controller: _costCtrl, keyboardType: const TextInputType.numberWithOptions( decimal: true, ), decoration: const InputDecoration( border: InputBorder.none, isDense: true, hintText: '选填', ), textAlign: TextAlign.right, ), ), const SizedBox(height: 10), _FieldRow( label: '购买日期', child: TextField( controller: _dateCtrl, decoration: const InputDecoration( border: InputBorder.none, isDense: true, ), textAlign: TextAlign.right, ), ), 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: 10), _FieldRow( label: '备注', child: TextField( controller: _noteCtrl, decoration: const InputDecoration( border: InputBorder.none, isDense: true, hintText: '点击填写', ), textAlign: TextAlign.right, ), ), const SizedBox(height: 10), Container( padding: const EdgeInsets.fromLTRB(14, 14, 14, 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: const Color(0xFFECEAE3), width: 0.5, ), ), child: Row( children: [ Container( width: 60, height: 60, decoration: BoxDecoration( borderRadius: BorderRadius.circular(11), border: Border.all( color: const Color(0xFFD3CDBE), width: 0.5, ), ), child: const Icon( Icons.camera_alt_outlined, color: Color(0xFFB3AFA5), ), ), const SizedBox(width: 10), const Expanded( child: Text( '添加发票 / 实物照片(选填,仅存本机)', style: TextStyle( fontSize: 12, color: Color(0xFFB3AFA5), ), ), ), ], ), ), ], const SizedBox(height: 16), SizedBox( width: double.infinity, child: FilledButton( onPressed: () { final holding = GoldAssetHolding( id: 'h${DateTime.now().microsecondsSinceEpoch}', name: _nameForSelection(), metal: _metal, category: _cat, purity: _factorFor(_purity), purityLabel: _purity, weightGram: _gram, costAmount: double.tryParse(_costCtrl.text.trim()), purchaseDate: DateTime.tryParse( _dateCtrl.text.trim(), ), channel: _channelCtrl.text.trim().isEmpty ? null : _channelCtrl.text.trim(), note: _noteCtrl.text.trim().isEmpty ? null : _noteCtrl.text.trim(), createdAt: DateTime.now(), updatedAt: DateTime.now(), ); ref .read(assetPortfolioControllerProvider.notifier) .addHolding(holding); Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('已添加 · 总估值已重算')), ); }, child: const Text('保存'), ), ), ], ), ), ], ), ), ); } } 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), ), ), ), ); } } class _StepButton extends StatelessWidget { const _StepButton({required this.icon, required this.onTap}); final IconData icon; final VoidCallback onTap; @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( width: 34, height: 34, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFE3D6B4), width: 0.5), ), child: Icon(icon, size: 18, color: const Color(0xFFB5862A)), ), ); } } 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( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( label, style: const TextStyle(fontSize: 13, color: Color(0xFF8A8780)), ), const SizedBox(width: 10), Expanded(child: child), ], ), ); } }