flutter_integrated_whb/lib/pages/login_page.dart

389 lines
14 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:qhd_prevention/tools/StorageService.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../http/ApiService.dart';
import 'package:pointycastle/asymmetric/api.dart' show RSAPublicKey;
import '../http/HttpManager.dart';
import '../tools/tools.dart';
import 'main_tab.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '登录页面',
theme: ThemeData(
primarySwatch: Colors.blue,
inputDecorationTheme: const InputDecorationTheme(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 8),
),
),
home: const LoginPage(),
debugShowCheckedModeBanner: false,
);
}
}
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _phoneController = TextEditingController(text: '13293211008');
final TextEditingController _passwordController = TextEditingController(text: 'Zsaq@123456');
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _errorMessage = '';
bool _isLoading = false;
bool _obscurePassword = true;
bool _agreed = false;
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg-login.png'),
fit: BoxFit.cover,
),
),
child: Form(
key: _formKey,
child: Stack(
children: [
Positioned.fill(
child: Image.asset(
'assets/images/bg-login.png',
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: 150),
Row(
children: [
Image.asset('assets/image/logo.png', height: 50),
const SizedBox(width: 15),
const Expanded(
child: Text(
'欢迎使用,\n智守安全云平台!',
style: TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 50),
_buildInputSection(
label: "手机号码",
controller: _phoneController,
hintText: "请输入手机号...",
keyboardType: TextInputType.phone,
validator: (value) {
if (value == null || value.isEmpty)
return '请输入手机号';
// if (!RegExp(r'^1[3-9]\d{9}\$').hasMatch(value))
// return '请输入有效的手机号';
return null;
},
),
const Divider(
height: 1,
thickness: 1,
color: Colors.white70,
),
_buildInputSection(
label: "密码",
controller: _passwordController,
hintText: "请输入密码...",
obscureText: _obscurePassword,
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility_off
: Icons.visibility,
color: Colors.white,
),
onPressed:
() =>
setState(
() => _obscurePassword = !_obscurePassword,
),
),
validator: (value) {
if (value == null || value.isEmpty)
return '请输入密码';
return null;
},
),
const Divider(
height: 1,
thickness: 1,
color: Colors.white70,
),
const SizedBox(height: 30),
if (_errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Text(
_errorMessage,
style: const TextStyle(color: Colors.blue),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24),
child: SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed:
(!_isLoading && _agreed) ? _handleLogin : null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child:
const Text(
'登录',
style: TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
Padding(
padding: const EdgeInsets.all(0),
child: Row(
children: [
Checkbox(
value: _agreed,
activeColor: Colors.white,
checkColor: Colors.blueAccent,
side: const BorderSide(color: Colors.white),
onChanged:
(v) => setState(() => _agreed = v ?? false),
),
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: '我已阅读并同意',
style: const TextStyle(
color: Colors.white,
),
),
TextSpan(
text: '《用户协议》',
style: const TextStyle(
color: Color(0xFF0D1D8C),
),
recognizer:
TapGestureRecognizer()
..onTap = () {
// 打开用户协议
},
),
TextSpan(
text: '',
style: const TextStyle(
color: Colors.white,
),
),
TextSpan(
text: '《隐私政策》',
style: const TextStyle(
color: Color(0xFF0D1D8C),
),
recognizer:
TapGestureRecognizer()
..onTap = () {
// 打开隐私政策
},
),
],
),
),
),
],
),
),
],
),
),
),
],
),
),
),
);
}
Widget _buildInputSection({
required String label,
required TextEditingController controller,
required String hintText,
bool obscureText = false,
TextInputType keyboardType = TextInputType.text,
Widget? suffixIcon,
String? Function(String?)? validator,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 15),
TextFormField(
controller: controller,
obscureText: obscureText,
keyboardType: keyboardType,
validator: validator,
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
hintText: hintText,
hintStyle: const TextStyle(color: Colors.white70),
suffixIcon: suffixIcon,
isDense: true,
contentPadding: EdgeInsets.zero,
),
style: const TextStyle(color: Colors.white),
),
],
),
);
}
Future<void> _handleLogin() async {
// 表单校验
if (!(_formKey.currentState?.validate() ?? false)) return;
final userName = _phoneController.text.trim();
final userPwd = _passwordController.text;
// RSA 加密encrypt 包的用法
final parser = encrypt.RSAKeyParser();
final pub = parser.parse(ApiService.publicKey) as RSAPublicKey;
final encrypter = encrypt.Encrypter(encrypt.RSA(publicKey: pub));
final plain = 'zcloudchina$userName,zy,$userPwd';
String keydataVal;
try {
keydataVal = encrypter.encrypt(plain).base64;
} catch (e) {
Fluttertoast.showToast(msg: '加密失败:$e', toastLength: Toast.LENGTH_LONG);
return;
}
setState(() => _isLoading = true);
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const Center(child: CircularProgressIndicator()),
);
try {
final data = await ApiService.loginCheck(keydataVal);
final result = data['result'] as String? ?? '';
if (result == 'success') {
// 存储用户信息
final prefs = await SharedPreferences.getInstance();
await prefs.setString('USER', json.encode(data));
await prefs.setStringList('remember', [userName, userPwd]);
SessionService.instance
..setLoginUserId(data['USER_ID'] as String)
..setCorpinfoId(data['CORPINFO_ID'] as String)
..setDeptId(data['DEPARTMENT_ID'] as String)
..setDeptLevel(data['DEPARTMENT_LEVEL'] as String)
..setIsRest(data['ISREST'] as String)
..setUsername(data['NAME'] as String)
..setLoginUser(data); // 这里 data 保存整个用户 JSON
final weak = data['WEAK_PASSWORD'] == '1';
final longTerm = data['LONG_TERM_PASSWORD_NOT_CHANGED'] == '1';
Navigator.of(context).pop(); // 关 loading
setState(() => _isLoading = false);
if (weak) {
// uni.redirectTo({
// url: '/pages/login/forget/forget-reset?canBack=1'
// });
} else if (longTerm) {
// uni.redirectTo({
// url: '/pages/login/forget/forget-reset?canBack=2'
// });
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const MainPage()),
);
}
} else {
// 理论上不会走这里,非 'success' 会抛 ApiException
}
} on ApiException catch (e) {
// 业务错误:
Navigator.of(context).pop();
setState(() => _isLoading = false);
String tip = e.message;
Fluttertoast.showToast(msg: tip, toastLength: Toast.LENGTH_LONG);
} catch (e) {
// 网络或其它未预期错误
Navigator.of(context).pop();
setState(() => _isLoading = false);
Fluttertoast.showToast(
msg: '服务器正在升级,请稍后再试。\n${e.toString()}',
toastLength: Toast.LENGTH_LONG,
);
}
}
}