Files
monisuo/flutter_monisuo/lib/ui/pages/auth/login_page.dart
sion c3f196ded4 feat: 添加业务分析后端接口
新增 AnalysisController 提供 6 个分析接口:
- /admin/analysis/profit - 盈利分析(交易手续费/充提手续费/资金利差)
- /admin/analysis/cash-flow - 资金流动趋势(按月统计充值/提现/净流入)
- /admin/analysis/trade - 交易分析(买入/卖出统计+趋势)
- /admin/analysis/coin-distribution - 币种交易分布
- /admin/analysis/user-growth - 用户增长分析(新增/活跃用户)
- /admin/analysis/risk - 风险指标(大额交易/异常提现/KYC/冻结账户)
- /admin/analysis/health - 综合健康度评分

更新 Mapper 添加分析查询方法:
- OrderFundMapper: 手续费统计、时间范围查询、大额交易、异常提现
- OrderTradeMapper: 交易金额统计、活跃用户、币种分布

前端 API 对接:
- 新增 6 个分析相关 Query hooks
- 更新 analytics.vue 使用真实数据
- 动态决策建议基于实际数据
2026-03-22 04:50:19 +08:00

167 lines
6.3 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 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../providers/auth_provider.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final formKey = GlobalKey<ShadFormState>();
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return Scaffold(
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: Padding(
padding: const EdgeInsets.all(24),
child: ShadForm(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo 和标题
Icon(
LucideIcons.trendingUp,
size: 64,
color: theme.colorScheme.primary,
),
const SizedBox(height: 24),
Text(
'模拟所',
style: theme.textTheme.h1,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'虚拟货币模拟交易平台',
style: theme.textTheme.muted,
textAlign: TextAlign.center,
),
const SizedBox(height: 48),
// 用户名输入
ShadInputFormField(
id: 'username',
label: const Text('用户名'),
placeholder: const Text('请输入用户名'),
leading: const Icon(LucideIcons.user),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入用户名';
}
if (value.length < 3) {
return '用户名至少 3 个字符';
}
return null;
},
),
const SizedBox(height: 16),
// 密码输入
ShadInputFormField(
id: 'password',
label: const Text('密码'),
placeholder: const Text('请输入密码'),
obscureText: true,
leading: const Icon(LucideIcons.lock),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入密码';
}
if (value.length < 6) {
return '密码至少 6 个字符';
}
return null;
},
),
const SizedBox(height: 24),
// 登录按钮
Consumer<AuthProvider>(
builder: (context, auth, _) {
return ShadButton(
onPressed: auth.isLoading
? null
: () async {
if (formKey.currentState!.saveAndValidate()) {
final values = formKey.currentState!.value;
final response = await auth.login(
values['username'],
values['password'],
);
// 登录成功后Provider 会自动更新状态
// MaterialApp 的 Consumer 会自动切换到 MainPage
if (!response.success && mounted) {
// 只在失败时显示错误
showShadDialog(
context: context,
builder: (context) => ShadDialog.alert(
title: const Text('登录失败'),
description: Text(
response.message ?? '用户名或密码错误',
),
actions: [
ShadButton(
child: const Text('确定'),
onPressed: () =>
Navigator.of(context).pop(),
),
],
),
);
}
}
},
child: auth.isLoading
? const SizedBox.square(
dimension: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text('登录'),
);
},
),
const SizedBox(height: 16),
// 注册链接
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'还没有账号?',
style: theme.textTheme.muted,
),
ShadButton.link(
onPressed: () {
// 跳转到注册页面
// context.go('/register');
},
child: const Text('立即注册'),
),
],
),
],
),
),
),
),
),
);
}
}