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 '../application/asset_portfolio_controller.dart'; class HoldingDetailPage extends ConsumerWidget { const HoldingDetailPage({super.key, this.id}); final String? id; @override Widget build(BuildContext context, WidgetRef ref) { final summary = ref.watch(assetPortfolioControllerProvider); final holding = summary.holdings .map((item) => item.holding) .firstWhere( (item) => id == null || item.id == id, orElse: () => summary.holdings.first.holding, ); final valuation = summary.holdings .firstWhere((item) => item.holding.id == holding.id); final cs = ShadTheme.of(context).colorScheme; final changeUp = valuation.todayChange >= 0; return Scaffold( backgroundColor: const Color(0xFFFBFAF7), appBar: AppBar( backgroundColor: const Color(0xFFFBFAF7), surfaceTintColor: Colors.transparent, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, leading: IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.chevron_left, size: 20), ), title: Text( holding.name, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600), ), ), body: SafeArea( child: ListView( padding: const EdgeInsets.fromLTRB(0, 8, 0, 28), children: [ PrototypeFrame( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 4), PrototypeCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: holding.metal.color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: Icon(Icons.circle_outlined, color: holding.metal.color, size: 18), ), const SizedBox(width: 10), Text( '${holding.purityLabel} · ${holding.weightGram.toStringAsFixed(1)}g', style: TextStyle(fontSize: 12, color: cs.mutedForeground), ), ], ), const SizedBox(height: 12), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '¥', style: TextStyle(fontSize: 22, fontWeight: FontWeight.w600), ), const SizedBox(width: 2), Text( formatCny(valuation.materialValue).replaceFirst('¥', ''), style: const TextStyle( fontSize: 42, fontWeight: FontWeight.w600, letterSpacing: -1, height: 1, ), ), ], ), const SizedBox(height: 10), Row( children: [ _ChangeChip( amount: valuation.todayChange, positive: changeUp, ), const SizedBox(width: 8), Text( '今日', style: TextStyle(fontSize: 12, color: cs.mutedForeground), ), ], ), ], ), ), const SizedBox(height: 12), PrototypeCard( padding: EdgeInsets.zero, child: Column( children: [ _InfoRow(label: '材料价值(现货)', value: formatCny(valuation.materialValue)), const PrototypeDivider(margin: EdgeInsets.zero), _InfoRow(label: '回收参考价', value: formatCny(valuation.materialValue * 0.97)), if (holding.costAmount != null) ...[ const PrototypeDivider(margin: EdgeInsets.zero), _InfoRow(label: '买入成本', value: formatCny(holding.costAmount!)), const PrototypeDivider(margin: EdgeInsets.zero), _InfoRow( label: '盈亏', value: _signedCny(valuation.materialValue - holding.costAmount!), valueColor: valuation.materialValue - holding.costAmount! >= 0 ? const Color(0xFFC0392B) : const Color(0xFF2E8B6F), ), ], ], ), ), const SizedBox(height: 12), PrototypeCard( padding: EdgeInsets.zero, child: Column( children: [ _InfoRow(label: '金属 / 纯度', value: '${holding.metal.label} · ${holding.purityLabel}'), const PrototypeDivider(margin: EdgeInsets.zero), _InfoRow(label: '克重', value: '${holding.weightGram.toStringAsFixed(1)} g'), const PrototypeDivider(margin: EdgeInsets.zero), _InfoRow(label: '购买日期', value: _dateLabel(holding.purchaseDate)), const PrototypeDivider(margin: EdgeInsets.zero), _InfoRow(label: '购买渠道', value: holding.channel ?? '未填'), ], ), ), const SizedBox(height: 12), PrototypeCard( child: GestureDetector( onTap: () {}, child: SizedBox( height: 156, child: ClipRRect( borderRadius: BorderRadius.circular(10), child: PrototypeLineChart( values: const [18, 20, 21, 23, 22, 25, 29, 31, 30, 33, 35, 37], ), ), ), ), ), const SizedBox(height: 14), Row( children: [ Expanded( child: OutlinedButton( onPressed: () => _showAction(context, '编辑'), child: const Text('编辑'), ), ), const SizedBox(width: 10), Expanded( child: OutlinedButton( onPressed: () => _showAction(context, '标记卖出 / 转赠'), child: const Text('标记卖出 / 转赠'), ), ), ], ), ], ), ), ], ), ), ); } void _showAction(BuildContext context, String text) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$text 占位'))); } } class _InfoRow extends StatelessWidget { const _InfoRow({ required this.label, required this.value, this.valueColor, }); final String label; final String value; final Color? valueColor; @override Widget build(BuildContext context) { final cs = ShadTheme.of(context).colorScheme; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 11), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: TextStyle(fontSize: 13, color: cs.mutedForeground)), Text( value, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: valueColor ?? cs.foreground), ), ], ), ); } } class _ChangeChip extends StatelessWidget { const _ChangeChip({required this.amount, required this.positive}); final double amount; final bool positive; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: positive ? const Color(0xFFFBEDEA) : const Color(0xFFE7F2EE), borderRadius: BorderRadius.circular(999), ), child: Text( '${positive ? '↑' : '↓'}${formatCny(amount.abs())} · ${positive ? '+' : '-'}${amount.abs().toStringAsFixed(2)}', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: positive ? const Color(0xFFC0392B) : const Color(0xFF2E8B6F), ), ), ); } } String _dateLabel(DateTime? value) { if (value == null) return '未填'; return '${value.year}-${value.month.toString().padLeft(2, '0')}-${value.day.toString().padLeft(2, '0')}'; } String _signedCny(double value) { final sign = value >= 0 ? '+' : '-'; return '$sign${formatCny(value.abs())}'; }