2025-12-12 09:11:30 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
|
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/http/ApiService.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/mine/forgot_pwd_page.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/mine/mine_set_pwd_page.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/mine/webViewPage.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/user/CustomInput.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/user/choose_userFirm_page.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/user/full_userinfo_page.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/user/register_page.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/services/auth_service.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/tools/tools.dart';
|
|
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/pages/main_tab.dart';
|
|
|
|
|
|
import 'package:qhd_prevention/services/SessionService.dart';
|
|
|
|
|
|
|
|
|
|
|
|
class LoginPage extends StatefulWidget {
|
|
|
|
|
|
const LoginPage({super.key});
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
_LoginPageState createState() => _LoginPageState();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class _LoginPageState extends State<LoginPage> {
|
|
|
|
|
|
final TextEditingController _phoneController = TextEditingController();
|
|
|
|
|
|
final TextEditingController _passwordController = TextEditingController();
|
|
|
|
|
|
final TextEditingController _codeController = TextEditingController();
|
|
|
|
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
|
|
|
|
final FocusNode _phoneFocusNode = FocusNode();
|
|
|
|
|
|
final FocusNode _passwordFocusNode = FocusNode();
|
|
|
|
|
|
final FocusNode _codeFocusNode = FocusNode();
|
|
|
|
|
|
|
|
|
|
|
|
String _errorMessage = '';
|
|
|
|
|
|
bool _isLoading = false;
|
|
|
|
|
|
bool _obscurePassword = true;
|
|
|
|
|
|
bool _agreed = false;
|
|
|
|
|
|
String _captchaImageBase64 = '';
|
|
|
|
|
|
String _captchaIdentifier = '';
|
|
|
|
|
|
bool _rememberPassword = true;
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void initState() {
|
|
|
|
|
|
super.initState();
|
|
|
|
|
|
_phoneController.addListener(_onTextChanged);
|
|
|
|
|
|
_getData();
|
|
|
|
|
|
_getCaptcha();
|
|
|
|
|
|
_phoneController.text = SessionService.instance.loginPhone ?? "";
|
|
|
|
|
|
_passwordController.text = SessionService.instance.loginPass ?? "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
_phoneController.removeListener(_onTextChanged);
|
|
|
|
|
|
_phoneController.dispose();
|
|
|
|
|
|
_passwordController.dispose();
|
|
|
|
|
|
_codeController.dispose();
|
|
|
|
|
|
_phoneFocusNode.dispose();
|
|
|
|
|
|
_passwordFocusNode.dispose();
|
|
|
|
|
|
_codeFocusNode.dispose();
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _getCaptcha() async {
|
2025-12-24 16:07:53 +08:00
|
|
|
|
try{
|
|
|
|
|
|
final response = await AuthApi.getUserCaptcha();
|
|
|
|
|
|
if (response['success']) {
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
_captchaImageBase64 = response['data']['img'];
|
|
|
|
|
|
_captchaIdentifier = response['data']['captchaKey'];
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}catch(e){
|
|
|
|
|
|
print(e);
|
2025-12-12 09:11:30 +08:00
|
|
|
|
}
|
2025-12-24 16:07:53 +08:00
|
|
|
|
|
2025-12-12 09:11:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _getData() async {
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
_phoneController.text = prefs.getString('savePhone') ?? '';
|
|
|
|
|
|
_passwordController.text = prefs.getString('savePass') ?? '';
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _saveData(String phone, String pass) async {
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
await prefs.setString("savePhone", phone);
|
|
|
|
|
|
await prefs.setString("savePass", pass);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onTextChanged() {
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
|
final screenHeight = MediaQuery.of(context).size.height;
|
|
|
|
|
|
double height = 230.0;
|
|
|
|
|
|
return Scaffold(
|
|
|
|
|
|
backgroundColor: Colors.white,
|
|
|
|
|
|
resizeToAvoidBottomInset: true,
|
|
|
|
|
|
body: Stack(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
// 背景图:铺满屏幕
|
|
|
|
|
|
Positioned(
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
right: 0,
|
|
|
|
|
|
child: Image.asset(
|
|
|
|
|
|
'assets/images/loginbg.png',
|
|
|
|
|
|
fit: BoxFit.fitWidth,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
Positioned(
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
left: 20,
|
|
|
|
|
|
right: 0,
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
const SizedBox(height: 70), // 顶部间距
|
|
|
|
|
|
Image.asset(
|
2025-12-24 16:07:53 +08:00
|
|
|
|
"assets/images/g_logo.png",
|
2025-12-12 09:11:30 +08:00
|
|
|
|
width: 40,
|
|
|
|
|
|
height: 40,
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
|
const Text(
|
|
|
|
|
|
"欢迎使用",
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
fontSize: 23,
|
|
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
const Text(
|
|
|
|
|
|
"秦港-相关方安全管理平台",
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
fontSize: 23,
|
|
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
Positioned(
|
|
|
|
|
|
bottom: 0,
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
right: 0,
|
|
|
|
|
|
top: height,
|
|
|
|
|
|
child: GestureDetector(
|
|
|
|
|
|
onTap: () => FocusScope.of(context).unfocus(),
|
|
|
|
|
|
child: SafeArea(
|
|
|
|
|
|
child: Form(
|
|
|
|
|
|
key: _formKey,
|
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
|
|
// 让内容至少占满屏高,并且内容可以滚动
|
|
|
|
|
|
child: ConstrainedBox(
|
|
|
|
|
|
constraints: BoxConstraints(minHeight: screenHeight-height-50),
|
|
|
|
|
|
child: IntrinsicHeight(
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
// 中间可滚动表单区域(左右内边距)
|
|
|
|
|
|
Container(
|
|
|
|
|
|
// color: Colors.white,
|
|
|
|
|
|
child: Padding(
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
|
|
horizontal: 20,
|
|
|
|
|
|
),
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
// const SizedBox(height: 60),
|
|
|
|
|
|
CustomInput.buildInput(
|
|
|
|
|
|
_phoneController,
|
|
|
|
|
|
title: "手机号",
|
|
|
|
|
|
hint: "请输入您的手机号",
|
|
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
|
|
suffix:
|
|
|
|
|
|
_phoneController.text.isEmpty
|
|
|
|
|
|
? SizedBox()
|
|
|
|
|
|
: IconButton(
|
|
|
|
|
|
icon: const Icon(
|
|
|
|
|
|
Icons.cancel,
|
|
|
|
|
|
size: 20,
|
|
|
|
|
|
color: Colors.grey,
|
|
|
|
|
|
),
|
|
|
|
|
|
onPressed:
|
|
|
|
|
|
() => setState(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
_phoneController
|
|
|
|
|
|
.clear(),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
validator: (v) {
|
|
|
|
|
|
if (v == null || v.isEmpty)
|
|
|
|
|
|
return '请输入您的手机号';
|
|
|
|
|
|
return null;
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
|
|
|
|
|
|
CustomInput.buildInput(
|
|
|
|
|
|
title: "密码",
|
|
|
|
|
|
_passwordController,
|
|
|
|
|
|
hint: "请输入您的密码",
|
|
|
|
|
|
obscure: _obscurePassword,
|
|
|
|
|
|
suffix: IconButton(
|
|
|
|
|
|
icon: Icon(
|
|
|
|
|
|
_obscurePassword
|
|
|
|
|
|
? Icons.visibility_off
|
|
|
|
|
|
: Icons.visibility,
|
|
|
|
|
|
color: Colors.grey,
|
|
|
|
|
|
),
|
|
|
|
|
|
onPressed:
|
|
|
|
|
|
() => setState(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
_obscurePassword =
|
|
|
|
|
|
!_obscurePassword,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
validator: (v) {
|
|
|
|
|
|
if (v == null || v.isEmpty)
|
|
|
|
|
|
return '请输入密码';
|
|
|
|
|
|
return null;
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
Row(
|
|
|
|
|
|
mainAxisAlignment:
|
|
|
|
|
|
MainAxisAlignment.spaceBetween,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Text(
|
|
|
|
|
|
'验证码',
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
fontSize: 17,
|
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
// 验证码行
|
|
|
|
|
|
SizedBox(
|
|
|
|
|
|
height: 60,
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Expanded(
|
|
|
|
|
|
child: Padding(
|
|
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
right: 10,
|
|
|
|
|
|
),
|
|
|
|
|
|
child: TextFormField(
|
|
|
|
|
|
controller: _codeController,
|
|
|
|
|
|
focusNode: _codeFocusNode,
|
|
|
|
|
|
keyboardType:
|
|
|
|
|
|
TextInputType.number,
|
|
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
|
|
hintText: '请输入验证码',
|
|
|
|
|
|
hintStyle: TextStyle(
|
|
|
|
|
|
color: Colors.black26,
|
|
|
|
|
|
),
|
|
|
|
|
|
border: InputBorder.none,
|
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
|
),
|
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
|
color: Colors.black,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(
|
2025-12-24 16:07:53 +08:00
|
|
|
|
horizontal: 10,
|
2025-12-12 09:11:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
child: _buildCaptchaImage(),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
2025-12-24 16:07:53 +08:00
|
|
|
|
const Divider(),
|
2025-12-12 09:11:30 +08:00
|
|
|
|
|
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
|
|
|
|
|
|
|
if (_errorMessage.isNotEmpty)
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
|
|
horizontal: 25,
|
|
|
|
|
|
),
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
_errorMessage,
|
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
|
color: Colors.red,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
_remenbemberPWDAndRegister(),
|
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
|
|
|
|
|
|
|
CustomButton(
|
|
|
|
|
|
text: '登录',
|
|
|
|
|
|
backgroundColor: const Color(0xFF2A75F8),
|
|
|
|
|
|
height: 50,
|
|
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
|
),
|
|
|
|
|
|
borderRadius: 25,
|
|
|
|
|
|
onPressed: _handleLogin,
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
|
CustomButton(
|
|
|
|
|
|
text: '注册',
|
|
|
|
|
|
height: 50,
|
|
|
|
|
|
textColor: Colors.black87,
|
|
|
|
|
|
textStyle: const TextStyle(
|
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
|
),
|
|
|
|
|
|
backgroundColor: Color(0xFFF3F4F8),
|
|
|
|
|
|
borderRadius: 25,
|
|
|
|
|
|
onPressed: () {
|
|
|
|
|
|
pushPage(RegisterPage(), context);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
// 底部协议:固定在页面底部(不会被背景覆盖,因为在上层)
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
|
|
horizontal: 10,
|
|
|
|
|
|
vertical: 10,
|
|
|
|
|
|
),
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Checkbox(
|
|
|
|
|
|
value: _agreed,
|
|
|
|
|
|
activeColor: Colors.blue,
|
|
|
|
|
|
checkColor: Colors.white,
|
|
|
|
|
|
side: const BorderSide(color: Colors.grey),
|
|
|
|
|
|
onChanged: (value) {
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
_agreed = value ?? false;
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
Flexible(
|
|
|
|
|
|
child: RichText(
|
|
|
|
|
|
text: TextSpan(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
const TextSpan(
|
|
|
|
|
|
text: '我已阅读并同意',
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
color: Colors.black,
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
TextSpan(
|
|
|
|
|
|
text: '《服务协议》',
|
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
|
color: Colors.blue,
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
),
|
|
|
|
|
|
// 如果你用 recognizer,请替换为你之前的 recognizer 变量
|
|
|
|
|
|
recognizer:
|
|
|
|
|
|
TapGestureRecognizer()
|
|
|
|
|
|
..onTap = () {
|
|
|
|
|
|
pushPage(
|
|
|
|
|
|
const WebViewPage(
|
|
|
|
|
|
name: "用户服务协议",
|
|
|
|
|
|
url:
|
|
|
|
|
|
'http://47.92.102.56:7811/file/xieyi/zsyhxy.htm',
|
|
|
|
|
|
),
|
|
|
|
|
|
context,
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
const TextSpan(
|
|
|
|
|
|
text: '和',
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
color: Colors.black,
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
TextSpan(
|
|
|
|
|
|
text: '《隐私政策》',
|
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
|
color: Colors.blue,
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
),
|
|
|
|
|
|
recognizer:
|
|
|
|
|
|
TapGestureRecognizer()
|
|
|
|
|
|
..onTap = () {
|
|
|
|
|
|
pushPage(
|
|
|
|
|
|
const WebViewPage(
|
|
|
|
|
|
name: "隐私政策",
|
|
|
|
|
|
url:
|
|
|
|
|
|
'http://47.92.102.56:7811/file/xieyi/zsysq.htm',
|
|
|
|
|
|
),
|
|
|
|
|
|
context,
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
// 交互层:放在背景之上
|
|
|
|
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Widget _remenbemberPWDAndRegister() {
|
|
|
|
|
|
return Padding(
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
// 左侧:记住密码
|
|
|
|
|
|
Expanded(
|
|
|
|
|
|
child: InkWell(
|
|
|
|
|
|
onTap:
|
|
|
|
|
|
() => setState(() => _rememberPassword = !_rememberPassword),
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Checkbox(
|
|
|
|
|
|
value: _rememberPassword,
|
|
|
|
|
|
onChanged:
|
|
|
|
|
|
(v) => setState(() => _rememberPassword = v ?? false),
|
|
|
|
|
|
activeColor: const Color(0xFF2A75F8),
|
|
|
|
|
|
side: const BorderSide(color: Colors.grey),
|
|
|
|
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
|
|
|
|
),
|
|
|
|
|
|
const Text(
|
|
|
|
|
|
'记住密码',
|
|
|
|
|
|
style: TextStyle(fontSize: 14, color: Colors.black38),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
TextButton(
|
|
|
|
|
|
onPressed: () {
|
|
|
|
|
|
pushPage(ForgotPwdPage('0'), context);
|
|
|
|
|
|
},
|
|
|
|
|
|
child: const Text(
|
|
|
|
|
|
'忘记密码?',
|
|
|
|
|
|
style: TextStyle(fontSize: 14, color: Colors.black38),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 修改验证码图片构建方法
|
|
|
|
|
|
Widget _buildCaptchaImage() {
|
|
|
|
|
|
if (_captchaImageBase64.isEmpty) {
|
|
|
|
|
|
return Container(
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
height: 40,
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.black26,
|
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: const Center(
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
'加载中...',
|
|
|
|
|
|
style: TextStyle(color: Colors.grey, fontSize: 12),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
|
onTap: _getCaptcha,
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
height: 40,
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: ClipRRect(
|
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
|
child: Image.memory(
|
|
|
|
|
|
base64.decode(_captchaImageBase64),
|
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
|
errorBuilder: (context, error, stackTrace) {
|
|
|
|
|
|
return const Center(
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
'加载失败',
|
|
|
|
|
|
style: TextStyle(color: Colors.grey, fontSize: 12),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _handleLogin() async {
|
|
|
|
|
|
if (_isLoading) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!(_formKey.currentState?.validate() ?? false)) return;
|
|
|
|
|
|
if (_codeController.text.isEmpty) {
|
|
|
|
|
|
ToastUtil.showNormal(context, "请输入验证码");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!_agreed) {
|
|
|
|
|
|
ToastUtil.showNormal(context, "请先阅读并同意《服务协议》和《隐私政策》");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// _phoneController.text='18700000002';
|
|
|
|
|
|
// _passwordController.text='Aa@12345678';
|
|
|
|
|
|
// _phoneController.text='卓云企业1';
|
|
|
|
|
|
// _phoneController.text='ceshi36-220';
|
|
|
|
|
|
// _passwordController.text='Aa12345678';
|
|
|
|
|
|
final userName = _phoneController.text.trim();
|
|
|
|
|
|
final userPwd = _passwordController.text.trim();
|
|
|
|
|
|
|
|
|
|
|
|
_saveData(userName, userPwd);
|
|
|
|
|
|
|
|
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
|
|
|
|
|
|
|
|
Map params = {
|
|
|
|
|
|
'captchaCode': _codeController.text,
|
|
|
|
|
|
'captchaKey' : _captchaIdentifier,
|
|
|
|
|
|
};
|
|
|
|
|
|
LoadingDialogHelper.show();
|
|
|
|
|
|
try {
|
|
|
|
|
|
final data = await AuthService.login(params, userName, userPwd);
|
|
|
|
|
|
LoadingDialogHelper.hide();
|
|
|
|
|
|
_getCaptcha();
|
|
|
|
|
|
setState(() => _isLoading = false);
|
|
|
|
|
|
if (FormUtils.hasValue(data, 'success') &&
|
|
|
|
|
|
data['success']) { // 登录成功直接进入主页
|
|
|
|
|
|
Navigator.pushReplacement(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (_) => const MainPage(isChooseFirm: true,)),
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (FormUtils.hasValue(data, 'isInfoComplete') &&
|
|
|
|
|
|
data['isInfoComplete'] == false) { // 如果还没有用户信息。需要先完善
|
|
|
|
|
|
Navigator.pushReplacement(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (_) => const FullUserinfoPage(isEidt: true, isChooseFirm: false,)),
|
|
|
|
|
|
);
|
|
|
|
|
|
} else if (FormUtils.hasValue(data, 'isChooseFirm') &&
|
|
|
|
|
|
data['isChooseFirm'] == false) { // 多个企业,跳转选择
|
|
|
|
|
|
{ // 先不进行底座登录,选择企业
|
|
|
|
|
|
List firmList = data['firmList'];
|
|
|
|
|
|
if (firmList.isEmpty) {
|
|
|
|
|
|
Navigator.pushReplacement(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (_) => const MainPage(isChooseFirm: false,)),
|
|
|
|
|
|
);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}else{ // 跳转选择企业
|
|
|
|
|
|
Navigator.pushReplacement(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (_) => ChooseUserfirmPage(firms: firmList, userName: data['userName'], password: data['password'],)),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (data.isEmpty) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}catch (e) {
|
|
|
|
|
|
setState(() => _isLoading = false);
|
|
|
|
|
|
Fluttertoast.showToast(msg: '登录失败: $e');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|