import 'package:autosos_flutter/api/user_api.dart'; import 'package:autosos_flutter/provider/providers.dart'; import 'package:autosos_flutter/router/app_router.dart'; import 'package:autosos_flutter/service/push_service.dart'; import 'package:autosos_flutter/util/toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; /// 登录页(升级版:手机号 + 验证码 + 密码 + 微信登录) class BindPhonePage extends ConsumerStatefulWidget { const BindPhonePage({super.key}); @override ConsumerState createState() => _BindPhonePageState(); } class _BindPhonePageState extends ConsumerState with SingleTickerProviderStateMixin { late TabController _tab; final _phoneCtrl = TextEditingController(); final _codeCtrl = TextEditingController(); final _pwdCtrl = TextEditingController(); bool _sending = false; int _countdown = 0; @override void initState() { super.initState(); _tab = TabController(length: 2, vsync: this); _phoneCtrl.text = '14725803690'; _pwdCtrl.text = '123456'; } @override void dispose() { _tab.dispose(); _phoneCtrl.dispose(); _codeCtrl.dispose(); _pwdCtrl.dispose(); super.dispose(); } Future _sendCode() async { final phone = _phoneCtrl.text.trim(); if (phone.length != 11) { Toast.show('请输入正确的手机号'); return; } setState(() => _sending = true); try { final r = await UserApi.instance.sendSms(phone); if (r.isSuccess) { Toast.show('验证码已发送'); _startCountdown(r.data?.cooldown ?? 60); } else { Toast.show(r.message ?? '发送失败'); } } catch (e) { Toast.show('网络异常'); } finally { if (mounted) setState(() => _sending = false); } } void _startCountdown(int seconds) { setState(() => _countdown = seconds); Future.doWhile(() async { await Future.delayed(const Duration(seconds: 1)); if (!mounted) return false; setState(() => _countdown = _countdown - 1); return _countdown > 0; }); } Future _loginByPassword() async { final phone = _phoneCtrl.text.trim(); final pwd = _pwdCtrl.text; if (phone.length != 11 || pwd.isEmpty) { Toast.show('请输入手机号和密码'); return; } try { final cid = PushService.instance.clientId; final r = await UserApi.instance.login( username: phone, password: pwd, cid: cid, ); if (r.isSuccess && r.data != null) { await _onLoginSuccess(r.data!.accessToken); } else { Toast.show(r.message ?? '登录失败'); } } catch (e) { Toast.show('网络异常'); } } Future _loginBySms() async { final phone = _phoneCtrl.text.trim(); final code = _codeCtrl.text.trim(); if (phone.length != 11 || code.isEmpty) { Toast.show('请输入手机号和验证码'); return; } try { final cid = PushService.instance.clientId; final r = await UserApi.instance.loginBySms(mobile: phone, code: code, cid: cid); if (r.isSuccess && r.data != null) { await _onLoginSuccess(r.data!.accessToken); } else { Toast.show(r.message ?? '登录失败'); } } catch (e) { Toast.show('网络异常'); } } Future _onLoginSuccess(String token) async { // 写入 SP await Future.wait([ // 持久化 token ]); ref.read(accessTokenProvider.notifier).state = token; Toast.show('登录成功'); if (!mounted) return; context.go(Routes.home); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('登录')), body: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( children: [ Image.asset('images/1.5x/ic_launcher.png', width: 72, height: 72), const SizedBox(height: 12), const Text('欢迎登录啾啾救援', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), const Text('宁波易到互联科技有限公司', style: TextStyle(fontSize: 13, color: Colors.grey)), const SizedBox(height: 24), TabBar( controller: _tab, tabs: const [Tab(text: '账号密码'), Tab(text: '短信登录')], ), const SizedBox(height: 16), SizedBox( height: 220, child: TabBarView( controller: _tab, children: [ _buildPasswordForm(), _buildSmsForm(), ], ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('其他登录方式:'), IconButton( icon: const Icon(Icons.wechat, color: Colors.green, size: 32), onPressed: () => context.push(Routes.bindWx), ), ], ), ], ), ), ); } Widget _buildPhoneField() { return Row( children: [ const Text('+86', style: TextStyle(fontSize: 16)), const SizedBox(width: 12), Container(width: 1, height: 24, color: Colors.grey), const SizedBox(width: 12), Expanded( child: TextField( controller: _phoneCtrl, keyboardType: TextInputType.phone, decoration: const InputDecoration( hintText: '请输入手机号', border: InputBorder.none, ), ), ), ], ); } Widget _buildPasswordForm() { return Column( children: [ _buildPhoneField(), const Divider(), TextField( controller: _pwdCtrl, obscureText: true, decoration: const InputDecoration( hintText: '请输入密码', border: InputBorder.none, ), ), const Divider(), const SizedBox(height: 24), SizedBox( width: double.infinity, height: 48, child: ElevatedButton( onPressed: _loginByPassword, child: const Text('登录'), ), ), ], ); } Widget _buildSmsForm() { return Column( children: [ _buildPhoneField(), const Divider(), Row( children: [ Expanded( child: TextField( controller: _codeCtrl, keyboardType: TextInputType.number, decoration: const InputDecoration( hintText: '请输入验证码', border: InputBorder.none, ), ), ), TextButton( onPressed: (_sending || _countdown > 0) ? null : _sendCode, child: Text( _countdown > 0 ? '$_countdown秒后重试' : '获取验证码', style: const TextStyle(fontSize: 14), ), ), ], ), const Divider(), const SizedBox(height: 24), SizedBox( width: double.infinity, height: 48, child: ElevatedButton( onPressed: _loginBySms, child: const Text('登录'), ), ), ], ); } }