From 3cd8527a7dbc267925e32d8b1319c47d50456c5b Mon Sep 17 00:00:00 2001 From: vipg Date: Thu, 13 Nov 2025 17:28:54 +0800 Subject: [PATCH] add --- .../asset_assistant/lib/login/login_page.dart | 242 ------------------ frontend/asset_assistant/lib/main.dart | 2 +- .../asset_assistant/lib/pages/home_page.dart | 61 +++++ .../asset_assistant/lib/pages/login_page.dart | 178 +++++++++++++ .../lib/utils/host_manager.dart | 41 +++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + frontend/asset_assistant/pubspec.lock | 217 +++++++++++++++- frontend/asset_assistant/pubspec.yaml | 4 + 8 files changed, 502 insertions(+), 245 deletions(-) delete mode 100644 frontend/asset_assistant/lib/login/login_page.dart create mode 100644 frontend/asset_assistant/lib/pages/home_page.dart create mode 100644 frontend/asset_assistant/lib/pages/login_page.dart create mode 100644 frontend/asset_assistant/lib/utils/host_manager.dart diff --git a/frontend/asset_assistant/lib/login/login_page.dart b/frontend/asset_assistant/lib/login/login_page.dart deleted file mode 100644 index 8545e63..0000000 --- a/frontend/asset_assistant/lib/login/login_page.dart +++ /dev/null @@ -1,242 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:dio/dio.dart'; - -class LoginPage extends StatefulWidget { - const LoginPage({super.key}); - - @override - State createState() => _LoginPageState(); -} - -class _LoginPageState extends State { - final _formKey = GlobalKey(); - final _usernameController = TextEditingController(); - final _passwordController = TextEditingController(); - bool _isLoading = false; - bool _obscurePassword = true; - final _dio = Dio(); - - Future _submitForm() async { - if (_formKey.currentState!.validate()) { - setState(() { - _isLoading = true; - }); - - try { - final response = await _dio.post( - 'https://api.fishestlife.com/user/login', - data: { - 'username': _usernameController.text.trim(), - 'password': _passwordController.text.trim(), - }, - ); - - // 登录成功处理 - if (response.statusCode == 200) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('登录成功'), - backgroundColor: Color(0xFF2E7D32), // 成功绿色 - ), - ); - // 导航到主页或其他页面 - // Navigator.pushReplacement(...); - } - } - } catch (e) { - // 错误处理 - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('登录失败: ${e.toString()}'), - backgroundColor: Color(0xFFC62828), // 错误红色 - ), - ); - } - } finally { - if (mounted) { - setState(() { - _isLoading = false; - }); - } - } - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - // 增加顶部阴影效果 - appBar: AppBar( - title: const Text('账户登录'), - backgroundColor: Theme.of(context).colorScheme.primary, - elevation: 4, - shadowColor: Colors.black54, - ), - // 主体内容使用卡片包裹增加层次感 - body: Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xFF121212), Color(0xFF1A1A2E)], - ), - ), - child: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.all(16.0), - child: Card( - margin: const EdgeInsets.symmetric(horizontal: 16), - child: Padding( - padding: const EdgeInsets.all(24.0), - child: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 金融风格Logo位置(可替换为实际Logo) - const Icon( - Icons.account_balance, - size: 64, - color: Color(0xFFD4AF37), - ), - const SizedBox(height: 24), - const Text( - '资产助手', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Color(0xFFD4AF37), - ), - ), - const SizedBox(height: 32), - - TextFormField( - controller: _usernameController, - decoration: InputDecoration( - labelText: '用户名', - prefixIcon: const Icon(Icons.person), - border: const OutlineInputBorder(), - // 增加焦点效果 - focusedBorder: OutlineInputBorder( - borderSide: const BorderSide( - color: Color(0xFFD4AF37), - width: 2, - ), - borderRadius: BorderRadius.circular(8), - ), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return '请输入用户名'; - } - return null; - }, - enabled: !_isLoading, - ), - const SizedBox(height: 16), - - TextFormField( - controller: _passwordController, - decoration: InputDecoration( - labelText: '密码', - prefixIcon: const Icon(Icons.lock), - border: const OutlineInputBorder(), - suffixIcon: IconButton( - icon: Icon( - _obscurePassword - ? Icons.visibility_off - : Icons.visibility, - color: Colors.white60, - ), - onPressed: () { - setState(() { - _obscurePassword = !_obscurePassword; - }); - }, - ), - focusedBorder: OutlineInputBorder( - borderSide: const BorderSide( - color: Color(0xFFD4AF37), - width: 2, - ), - borderRadius: BorderRadius.circular(8), - ), - ), - obscureText: _obscurePassword, - validator: (value) { - if (value == null || value.isEmpty) { - return '请输入密码'; - } - return null; - }, - enabled: !_isLoading, - ), - const SizedBox(height: 8), - - // 忘记密码链接 - Align( - alignment: Alignment.centerRight, - child: TextButton( - onPressed: _isLoading - ? null - : () { - // 忘记密码逻辑 - }, - child: const Text( - '忘记密码?', - style: TextStyle( - color: Color(0xFFD4AF37), - fontSize: 14, - ), - ), - ), - ), - const SizedBox(height: 16), - - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: _isLoading ? null : _submitForm, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF0F3460), - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: _isLoading - ? const CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - Colors.white, - ), - ) - : const Text( - '安全登录', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ], - ), - ), - ), - ), - ), - ), - ), - ); - } - - @override - void dispose() { - _usernameController.dispose(); - _passwordController.dispose(); - _dio.close(); - super.dispose(); - } -} diff --git a/frontend/asset_assistant/lib/main.dart b/frontend/asset_assistant/lib/main.dart index 66a6858..3882381 100644 --- a/frontend/asset_assistant/lib/main.dart +++ b/frontend/asset_assistant/lib/main.dart @@ -1,4 +1,4 @@ -import 'package:asset_assistant/login/login_page.dart'; +import 'package:asset_assistant/pages/login_page.dart'; import 'package:flutter/material.dart'; void main() { diff --git a/frontend/asset_assistant/lib/pages/home_page.dart b/frontend/asset_assistant/lib/pages/home_page.dart new file mode 100644 index 0000000..55cce8d --- /dev/null +++ b/frontend/asset_assistant/lib/pages/home_page.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + Future _logout(BuildContext context) async { + final prefs = await SharedPreferences.getInstance(); + // 先确认需要清除的数据存在 + final hasUserID = prefs.getString('user_id') != null; + + if (hasUserID) { + await prefs.remove('user_id'); + debugPrint('移除的用户ID: $hasUserID'); + } + + // Web端强制刷新存储 + await prefs.reload(); + + // 检查组件是否已挂载 + if (!context.mounted) return; + + // 导航到登录页并清除路由栈(使用命名路由) + Navigator.pushNamedAndRemoveUntil( + context, + '/login', // 目标命名路由 + (route) => false, // 清除所有之前的路由 + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.surface, + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.settings), + onPressed: () { + // 点击事件处理 + Navigator.pushNamed(context, '/setting_list'); + }, + ), + title: const Text('首页'), + backgroundColor: Theme.of(context).colorScheme.surface, + elevation: 4, + actions: [ + IconButton( + icon: const Icon(Icons.logout), + onPressed: () => _logout(context), + ), + ], + ), + body: const Center( + child: Text( + '功能列表区域', + style: TextStyle(color: Color(0xFF94A3B8), fontSize: 18), + ), + ), + ); + } +} diff --git a/frontend/asset_assistant/lib/pages/login_page.dart b/frontend/asset_assistant/lib/pages/login_page.dart new file mode 100644 index 0000000..4171988 --- /dev/null +++ b/frontend/asset_assistant/lib/pages/login_page.dart @@ -0,0 +1,178 @@ +import 'package:asset_assistant/pages/home_page.dart'; +import 'package:asset_assistant/utils/host_manager.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:shared_preferences/shared_preferences.dart'; + +// 登录页面 +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + // 用于获取输入框内容的控制器 + final TextEditingController _accountController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + + // 加载状态标识 + bool _isLoading = false; + + // 登录请求方法 + Future _login() async { + // 获取输入的账号密码 + final String account = _accountController.text.trim(); + final String password = _passwordController.text.trim(); + + // 简单验证 + if (account.isEmpty || password.isEmpty) { + _showDialog('提示', '请输入账号和密码'); + return; + } + + setState(() { + _isLoading = true; + }); + + try { + // 构建请求URL + final url = HostManager().getUriHostByPath("/user/login"); + debugPrint('url is: $url'); + + // 发送POST请求 + final response = await http.post( + url, + headers: {'Content-Type': 'application/json'}, + body: json.encode({'account': account, 'password': password}), + ); + + // 解析响应 + if (response.statusCode == 200) { + final result = json.decode(response.body); + // 打印后端返回的完整数据,确认code和状态 + debugPrint('登录响应:$result'); + if (result['success'] == true) { + debugPrint('登录成功,准备跳转'); + + // 保存token和用户信息到本地存储 + final prefs = await SharedPreferences.getInstance(); + await prefs.setString( + 'user_id', + result['data']['user_id'], + ); // 保存用户ID。 + debugPrint('保存的用户ID: ${result['data']['user_id']}'); + + // 延迟后再检查上下文是否仍有效 + if (mounted) { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const HomePage()), + ); + } + } else { + // 登录失败,显示错误信息 + _showDialog('登录失败', result['message'] ?? '未知错误'); + } + } else { + _showDialog('错误', '服务器响应异常: ${response.statusCode}'); + debugPrint('服务器响应异常: ${response.statusCode}'); + } + } catch (e) { + _showDialog('错误', '网络请求失败: $e'); + debugPrint('详细错误信息: ${e.toString()}'); + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + } + + // 显示对话框 + void _showDialog(String title, String content) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(title), + content: Text(content), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('确定'), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.surface, + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 60), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 60), + const Text( + '资产助手', + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 80), + + // 账号输入框 + TextField( + controller: _accountController, + style: const TextStyle(color: Colors.white), + decoration: const InputDecoration( + labelText: '账号', + hintText: '请输入账号', + prefixIcon: Icon(Icons.person, color: Color(0xFF94A3B8)), + ), + keyboardType: TextInputType.text, + ), + const SizedBox(height: 20), + + // 密码输入框 + TextField( + controller: _passwordController, + style: const TextStyle(color: Colors.white), + decoration: const InputDecoration( + labelText: '密码', + hintText: '请输入密码', + prefixIcon: Icon(Icons.lock, color: Color(0xFF94A3B8)), + suffixIcon: Icon( + Icons.visibility_off, + color: Color(0xFF94A3B8), + ), + ), + obscureText: true, + ), + const SizedBox(height: 40), + + // 登录按钮 + ElevatedButton( + onPressed: _isLoading ? null : _login, + child: _isLoading + ? const CircularProgressIndicator(color: Colors.white) + : const Text('登录'), + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} diff --git a/frontend/asset_assistant/lib/utils/host_manager.dart b/frontend/asset_assistant/lib/utils/host_manager.dart new file mode 100644 index 0000000..0e1ef0e --- /dev/null +++ b/frontend/asset_assistant/lib/utils/host_manager.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +class HostManager { + // 单例实例 + static final HostManager _instance = HostManager._internal(); + + // 工厂构造函数,确保保证只有一个实例 + factory HostManager() => _instance; + + // 私有构造函数 + HostManager._internal(); + + // 判断是否为开发环境 + bool get isDebug => const bool.fromEnvironment('dart.vm.product') == false; + + Uri getUriHostByPath(String path) { + final host = HostManager().getHostByPath(path); + final url = Uri.parse(host); + return url; + } + + // 根据传入的path返回对应的host + // 可根据实际需求扩展path与host的映射关系 + String getHostByPath(String path) { + debugPrint('path is:$path'); + String host = ""; + if (!isDebug) { + debugPrint('release model'); + host = "https://api.fishestlife.com"; + } else { + debugPrint('debug model'); + if (path.startsWith('/user')) { + host = "https://api.fishestlife.com"; + } else { + host = ""; + } + } + debugPrint('url is:$host$path'); + return host + path; + } +} diff --git a/frontend/asset_assistant/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/asset_assistant/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..724bb2a 100644 --- a/frontend/asset_assistant/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/asset_assistant/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/frontend/asset_assistant/pubspec.lock b/frontend/asset_assistant/pubspec.lock index ae0926d..adf6c68 100644 --- a/frontend/asset_assistant/pubspec.lock +++ b/frontend/asset_assistant/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -65,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + enum_to_string: + dependency: transitive + description: + name: enum_to_string + sha256: "93b75963d3b0c9f6a90c095b3af153e1feccb79f6f08282d3274ff8d9eea52bc" + url: "https://pub.dev" + source: hosted + version: "2.2.1" fake_async: dependency: transitive description: @@ -73,6 +89,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + flag: + dependency: "direct main" + description: + name: flag + sha256: "69e3e1d47453349ef72e2ebf4234b88024c0d57f9bcfaa7cc7facec49cd8561f" + url: "https://pub.dev" + source: hosted + version: "7.0.2" flutter: dependency: "direct main" description: flutter @@ -86,11 +126,32 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_svg: + dependency: transitive + description: + name: flutter_svg + sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" + url: "https://pub.dev" + source: hosted + version: "2.2.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" http_parser: dependency: transitive description: @@ -171,6 +232,118 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" + url: "https://pub.dev" + source: hosted + version: "2.4.15" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b" + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -232,6 +405,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + url: "https://pub.dev" + source: hosted + version: "1.1.19" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc + url: "https://pub.dev" + source: hosted + version: "1.1.19" vector_math: dependency: transitive description: @@ -249,13 +446,29 @@ packages: source: hosted version: "15.0.2" web: - dependency: transitive + dependency: "direct main" description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted version: "1.1.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" sdks: dart: ">=3.9.2 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.35.0" diff --git a/frontend/asset_assistant/pubspec.yaml b/frontend/asset_assistant/pubspec.yaml index 2ffdcf8..cbf405f 100644 --- a/frontend/asset_assistant/pubspec.yaml +++ b/frontend/asset_assistant/pubspec.yaml @@ -32,6 +32,10 @@ dependencies: sdk: flutter dio: ^5.3.3 # 网络请求库 + http: ^1.5.0 + shared_preferences: ^2.5.3 + web: ^1.1.1 + flag: ^7.0.2 cupertino_icons: ^1.0.8 dev_dependencies: