gcx/frontend/admin-app/lib/features/finance/presentation/pages/finance_page.dart

337 lines
12 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import '../../../../app/theme/app_colors.dart';
/// 财务管理页面
///
/// 法币展示,不暴露链上稳定币细节
/// 包含销售收入、Breakage收入、手续费、保证金、冻结款、提现、对账报表
class FinancePage extends StatelessWidget {
const FinancePage({super.key});
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('财务管理'),
actions: [
IconButton(
icon: const Icon(Icons.download_rounded),
onPressed: () => _showExportDialog(context),
),
],
bottom: const TabBar(
tabs: [
Tab(text: '概览'),
Tab(text: '交易明细'),
Tab(text: '对账报表'),
],
),
),
body: const TabBarView(
children: [
_OverviewTab(),
_TransactionDetailTab(),
_ReconciliationTab(),
],
),
),
);
}
void _showExportDialog(BuildContext context) {
showDialog(
context: context,
builder: (ctx) => SimpleDialog(
title: const Text('导出数据'),
children: [
SimpleDialogOption(onPressed: () => Navigator.pop(ctx), child: const Text('导出 CSV')),
SimpleDialogOption(onPressed: () => Navigator.pop(ctx), child: const Text('导出 Excel')),
SimpleDialogOption(onPressed: () => Navigator.pop(ctx), child: const Text('导出 PDF')),
],
),
);
}
}
class _OverviewTab extends StatelessWidget {
const _OverviewTab();
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
children: [
// Balance Card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: AppColors.primaryGradient,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('可提现余额', style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.7))),
const SizedBox(height: 4),
const Text('\$42,300.00', style: TextStyle(fontSize: 32, fontWeight: FontWeight.w700, color: Colors.white)),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: AppColors.primary,
),
child: const Text('提现'),
),
),
],
),
),
const SizedBox(height: 20),
// Financial Stats
_buildFinanceStatsGrid(),
const SizedBox(height: 20),
// Guarantee Fund
_buildGuaranteeFundCard(),
const SizedBox(height: 20),
// Revenue Trend
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColors.borderLight),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('收入趋势', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
const SizedBox(height: 16),
Container(
height: 160,
decoration: BoxDecoration(
color: AppColors.gray50,
borderRadius: BorderRadius.circular(8),
),
child: const Center(child: Text('月度收入趋势图', style: TextStyle(color: AppColors.textTertiary))),
),
],
),
),
],
),
);
}
Widget _buildFinanceStatsGrid() {
final stats = [
('销售收入', '\$125,800', AppColors.success),
('Breakage收入', '\$8,200', AppColors.info),
('平台手续费', '-\$1,510', AppColors.error),
('待结算', '\$15,400', AppColors.warning),
('已提现', '\$66,790', AppColors.textSecondary),
('总收入', '\$132,490', AppColors.primary),
];
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 2,
),
itemCount: stats.length,
itemBuilder: (context, index) {
final (label, value, color) = stats[index];
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColors.borderLight),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)),
Text(value, style: TextStyle(fontSize: 17, fontWeight: FontWeight.w700, color: color)),
],
),
);
},
);
}
Widget _buildGuaranteeFundCard() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColors.borderLight),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.shield_rounded, color: AppColors.info, size: 20),
SizedBox(width: 8),
Text('保证金与冻结款', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
],
),
const SizedBox(height: 16),
_buildRow('已缴纳保证金', '\$10,000'),
_buildRow('冻结销售款', '\$5,200'),
_buildRow('冻结比例', '20%'),
const SizedBox(height: 12),
SwitchListTile(
title: const Text('自动冻结销售款', style: TextStyle(fontSize: 14)),
subtitle: const Text('开启后自动冻结20%销售额以提升信用'),
value: true,
onChanged: (_) {},
activeColor: AppColors.primary,
contentPadding: EdgeInsets.zero,
),
],
),
);
}
Widget _buildRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontSize: 13, color: AppColors.textSecondary)),
Text(value, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500)),
],
),
);
}
}
class _TransactionDetailTab extends StatelessWidget {
const _TransactionDetailTab();
@override
Widget build(BuildContext context) {
final transactions = [
('售出 ¥25 礼品卡 x5', '+\$106.25', '今天 14:32', AppColors.success),
('核销结算 ¥100 购物券 x2', '+\$200.00', '今天 12:15', AppColors.success),
('平台手续费', '-\$3.19', '今天 14:32', AppColors.error),
('退款 ¥25 礼品卡', '-\$21.25', '今天 10:08', AppColors.warning),
('售出 ¥50 生活券 x3', '+\$127.50', '昨天 18:45', AppColors.success),
('提现至银行账户', '-\$5,000.00', '昨天 16:00', AppColors.info),
];
return ListView.separated(
padding: const EdgeInsets.all(20),
itemCount: transactions.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, index) {
final (desc, amount, time, color) = transactions[index];
return ListTile(
contentPadding: const EdgeInsets.symmetric(vertical: 6),
title: Text(desc, style: const TextStyle(fontSize: 14)),
subtitle: Text(time, style: const TextStyle(fontSize: 12, color: AppColors.textTertiary)),
trailing: Text(
amount,
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: color),
),
);
},
);
}
}
class _ReconciliationTab extends StatelessWidget {
const _ReconciliationTab();
@override
Widget build(BuildContext context) {
final reports = [
('2026年1月对账单', '总收入: \$28,450 | 总支出: \$3,210', '已生成'),
('2025年12月对账单', '总收入: \$32,100 | 总支出: \$4,080', '已生成'),
('2025年11月对账单', '总收入: \$25,800 | 总支出: \$2,900', '已生成'),
];
return ListView(
padding: const EdgeInsets.all(20),
children: [
// Generate New
OutlinedButton.icon(
onPressed: () {},
icon: const Icon(Icons.add_rounded),
label: const Text('生成新对账单'),
style: OutlinedButton.styleFrom(
minimumSize: const Size(double.infinity, 48),
),
),
const SizedBox(height: 20),
...reports.map((r) {
final (title, summary, status) = r;
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColors.borderLight),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: AppColors.successLight,
borderRadius: BorderRadius.circular(999),
),
child: Text(status, style: const TextStyle(fontSize: 11, color: AppColors.success)),
),
],
),
const SizedBox(height: 6),
Text(summary, style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)),
const SizedBox(height: 12),
Row(
children: [
TextButton.icon(
onPressed: () {},
icon: const Icon(Icons.visibility_rounded, size: 16),
label: const Text('查看'),
),
TextButton.icon(
onPressed: () {},
icon: const Icon(Icons.download_rounded, size: 16),
label: const Text('导出'),
),
],
),
],
),
);
}),
],
);
}
}