login.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:eitc_erm_app/register.dart';
  4. import 'package:eitc_erm_app/utils/Component.dart';
  5. import 'package:eitc_erm_app/utils/Constants.dart';
  6. import 'package:eitc_erm_app/utils/Utils.dart';
  7. import 'package:eitc_erm_app/utils/logger.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter/services.dart';
  10. import 'package:http/http.dart' as http;
  11. import 'package:http/http.dart';
  12. import 'bean/login_entity.dart';
  13. import 'bean/normal_response.dart';
  14. import 'bottom_navigation.dart';
  15. import 'doctor_talk_list.dart';
  16. void main() {
  17. WidgetsFlutterBinding.ensureInitialized();
  18. runApp(Login());
  19. }
  20. class Login extends StatefulWidget {
  21. @override
  22. State<StatefulWidget> createState() => LoginState();
  23. }
  24. class LoginState extends State<Login> {
  25. @override
  26. Widget build(BuildContext context) {
  27. return Scaffold(
  28. appBar: AppBar(title: Text('登录'), centerTitle: true),
  29. body: Center(
  30. child: MyBody(),
  31. ),
  32. );
  33. }
  34. }
  35. class MyBody extends StatefulWidget {
  36. @override
  37. _MyBodyState createState() => _MyBodyState();
  38. }
  39. class _MyBodyState extends State<MyBody> {
  40. bool isButtonEnable = true; //按钮状态 是否可点击
  41. String buttonText = '发送验证码'; //初始文本
  42. int count = 60; //初始倒计时时间
  43. Timer? timer; //倒计时的计时器
  44. TextEditingController mController = TextEditingController();
  45. TextEditingController phoneController = TextEditingController();
  46. bool isPasswordLogin = false;
  47. String passText = "验证码";
  48. String loginText = "密码登录";
  49. String hintText = "请输入验证码";
  50. int patientOrDoctor = 1;
  51. void _buttonClickListen() {
  52. if (!Utils.isChinaPhoneLegal(phoneController.text)) {
  53. Component.toast("请输入正确手机号码!", 0);
  54. return null;
  55. }
  56. setState(() {
  57. sendCaptchaCode();
  58. if (isButtonEnable) {
  59. isButtonEnable = false; //按钮状态标记
  60. _initTimer();
  61. return null; //返回null按钮禁止点击
  62. } else {
  63. return null; //返回null按钮禁止点击
  64. }
  65. });
  66. }
  67. void _initTimer() {
  68. timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
  69. count--;
  70. setState(() {
  71. if (count == 0) {
  72. timer.cancel(); //倒计时结束取消定时器
  73. isButtonEnable = true; //按钮可点击
  74. count = 60; //重置时间
  75. buttonText = '发送验证码'; //重置按钮文本
  76. } else {
  77. buttonText = '重新发送($count)'; //更新文本内容
  78. }
  79. });
  80. });
  81. }
  82. @override
  83. void dispose() {
  84. timer?.cancel(); //销毁计时器
  85. super.dispose();
  86. }
  87. @override
  88. Widget build(BuildContext context) {
  89. return SingleChildScrollView(
  90. child: Container(
  91. padding: const EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
  92. child: Column(
  93. children: <Widget>[
  94. Container(
  95. color: Colors.white,
  96. padding: const EdgeInsets.only(left: 10, right: 10),
  97. child: Column(
  98. children: <Widget>[
  99. const Padding(
  100. padding: EdgeInsets.fromLTRB(0, 80, 0, 80),
  101. child: Center(
  102. child: Image(
  103. image: AssetImage('assets/images/logo.png'),
  104. ),
  105. ),
  106. ),
  107. Opacity(
  108. opacity: patientOrDoctor == 1 ? 1 : 0,
  109. child: const Align(
  110. alignment: Alignment.centerLeft,
  111. child: Text(
  112. '未注册的手机号验证后自动创建您的账号',
  113. style: TextStyle(color: Colors.blue, fontSize: 15),
  114. textAlign: TextAlign.left,
  115. overflow: TextOverflow.ellipsis,
  116. ),
  117. )),
  118. Row(
  119. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  120. crossAxisAlignment: CrossAxisAlignment.baseline,
  121. textBaseline: TextBaseline.alphabetic,
  122. children: <Widget>[
  123. SizedBox(
  124. width: 50,
  125. child: Text(
  126. patientOrDoctor == 1 ? "+86" : "用户名",
  127. style: const TextStyle(
  128. fontSize: 13, color: Color(0xff333333)),
  129. ),
  130. ),
  131. Expanded(
  132. child: Padding(
  133. padding:
  134. const EdgeInsets.only(left: 0, right: 15, top: 5),
  135. child: TextFormField(
  136. maxLines: 1,
  137. onSaved: (value) {},
  138. controller: phoneController,
  139. textAlign: TextAlign.left,
  140. inputFormatters: patientOrDoctor == 1
  141. ? [
  142. FilteringTextInputFormatter.digitsOnly,
  143. LengthLimitingTextInputFormatter(11)
  144. ]
  145. : [],
  146. decoration: InputDecoration(
  147. hintText:
  148. (patientOrDoctor == 1 ? '请填写手机号' : "请填写用户名"),
  149. hintStyle: const TextStyle(
  150. color: Color(0xff999999),
  151. fontSize: 13,
  152. ),
  153. alignLabelWithHint: true,
  154. border: const OutlineInputBorder(
  155. borderSide: BorderSide.none),
  156. ),
  157. ),
  158. ),
  159. ),
  160. ],
  161. ),
  162. Row(
  163. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  164. crossAxisAlignment: CrossAxisAlignment.baseline,
  165. textBaseline: TextBaseline.alphabetic,
  166. children: <Widget>[
  167. SizedBox(
  168. width: 50,
  169. child: Text(
  170. passText,
  171. style: TextStyle(
  172. fontSize: 13, color: Color(0xff333333)),
  173. )),
  174. Expanded(
  175. child: Padding(
  176. padding: EdgeInsets.only(left: 0, right: 15, top: 0),
  177. child: TextFormField(
  178. maxLines: 1,
  179. onSaved: (value) {},
  180. controller: mController,
  181. textAlign: TextAlign.left,
  182. obscureText: isPasswordLogin,
  183. inputFormatters: isPasswordLogin
  184. ? []
  185. : [
  186. FilteringTextInputFormatter.digitsOnly,
  187. LengthLimitingTextInputFormatter(6)
  188. ],
  189. decoration: InputDecoration(
  190. counterText: "",
  191. hintText: (hintText),
  192. hintStyle: TextStyle(
  193. color: Color(0xff999999),
  194. fontSize: 13,
  195. ),
  196. alignLabelWithHint: true,
  197. border: OutlineInputBorder(
  198. borderSide: BorderSide.none),
  199. ),
  200. ),
  201. ),
  202. ),
  203. Container(
  204. padding: const EdgeInsets.all(0),
  205. width: 120,
  206. child: Visibility(
  207. visible: !isPasswordLogin,
  208. child: ElevatedButton(
  209. style: ButtonStyle(
  210. backgroundColor:
  211. WidgetStateProperty.resolveWith<Color>(
  212. (states) {
  213. if (states.contains(WidgetState.disabled)) {
  214. return Colors.brown; // Disabled color
  215. }
  216. return Colors.blue; // Regular color
  217. }),
  218. foregroundColor:
  219. const WidgetStatePropertyAll(Colors.white),
  220. ),
  221. // backgroundColor : isButtonEnable
  222. // ? Color(0xff44c5fe)
  223. // : Colors.grey.withOpacity(0.1), //按钮的颜色
  224. // splashColor: isButtonEnable
  225. // ? Colors.white.withOpacity(0.1)
  226. // : Colors.transparent,
  227. // shape: StadiumBorder(side: BorderSide.none),
  228. onPressed: () {
  229. if (isButtonEnable) {
  230. setState(() {
  231. _buttonClickListen();
  232. });
  233. }
  234. },
  235. child: Text(
  236. buttonText,
  237. style: const TextStyle(
  238. fontSize: 12,
  239. ),
  240. ),
  241. ),
  242. ),
  243. ),
  244. ],
  245. ),
  246. ],
  247. ),
  248. ),
  249. Container(
  250. width: double.infinity,
  251. height: 45,
  252. margin: const EdgeInsets.only(top: 30, left: 10, right: 10),
  253. child: ElevatedButton(
  254. style: ButtonStyle(
  255. backgroundColor:
  256. MaterialStateProperty.resolveWith<Color>((states) {
  257. return Colors.blue; // Regular color
  258. }),
  259. ),
  260. onPressed: () {
  261. logd('手机号:${phoneController.text},验证码:${mController.text}');
  262. login();
  263. },
  264. // shape: StadiumBorder(side: BorderSide.none),
  265. // color: Color(0xff44c5fe),
  266. child: Text(
  267. '登录',
  268. style: TextStyle(color: Colors.white, fontSize: 15),
  269. ),
  270. ),
  271. ),
  272. Padding(
  273. padding: EdgeInsets.all(20),
  274. child: Row(
  275. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  276. children: [
  277. Row(
  278. children: <Widget>[
  279. Text("患者"),
  280. Radio(
  281. value: 1,
  282. onChanged: (value) {
  283. setState(() {
  284. this.patientOrDoctor = value!;
  285. });
  286. },
  287. groupValue: patientOrDoctor,
  288. ),
  289. SizedBox(
  290. width: 20,
  291. ),
  292. Text("医生"),
  293. Radio(
  294. value: 2,
  295. onChanged: (value) {
  296. setState(() {
  297. patientOrDoctor = value!;
  298. isPasswordLogin = true;
  299. _updateText();
  300. });
  301. },
  302. groupValue: patientOrDoctor,
  303. ),
  304. ],
  305. ),
  306. Visibility(
  307. visible: patientOrDoctor == 1,
  308. child: GestureDetector(
  309. onTap: () {
  310. isPasswordLogin = !isPasswordLogin;
  311. logd(isPasswordLogin);
  312. setState(() {
  313. _updateText();
  314. });
  315. },
  316. child: Text(
  317. loginText,
  318. style: const TextStyle(
  319. color: Colors.blue, fontSize: 15),
  320. textAlign: TextAlign.right,
  321. ),
  322. )),
  323. ]),
  324. )
  325. ],
  326. ),
  327. ),
  328. );
  329. }
  330. void _updateText() {
  331. if (isPasswordLogin) {
  332. passText = "密 码";
  333. loginText = "验证码登录";
  334. hintText = "请输入密码";
  335. } else {
  336. passText = "验证码";
  337. loginText = "密码登录";
  338. hintText = "请输入验证码";
  339. }
  340. }
  341. Future<void> launchAndroid() async {
  342. const platform = MethodChannel('com.example.xcx_dashan_app/channel');
  343. try {
  344. var result = await platform.invokeMethod('launchApp');
  345. // var result = await platform.invokeMethod('launchAppOne',{'param1': "这是一个参数"});
  346. // var result = await platform.invokeMethod('launchAppOne',{'param1': "这是一个参数",'param2': "这是一个参数"});
  347. logd("结果$result");
  348. } on PlatformException catch (e) {
  349. logd("Failed to invoke method: '${e.message}'.");
  350. }
  351. }
  352. Future<String?> sendCaptchaCode() async {
  353. var params = {
  354. 'phoneNumber': phoneController.text,
  355. };
  356. var response = await http.post(
  357. Uri.parse('${Global.BaseUrl}sendCaptchaCode'),
  358. headers: jsonHeaders(),
  359. body: encodeBody(params));
  360. logd("encoded=${encodeBody(params)}");
  361. if (response.statusCode == 200) {
  362. final json = decodeBodyToJson(response.bodyBytes);
  363. logd(json);
  364. NormalResponse mNormalResponse = NormalResponse.fromJson(json);
  365. if (mNormalResponse.code == Global.responseSuccessCode) {
  366. Component.toast("短信发送成功,请查收!", 2);
  367. } else {
  368. Component.toast(mNormalResponse.msg.toString(), 0);
  369. }
  370. return response.toString();
  371. } else {
  372. Component.toast("出错了,请稍后再试!", 0);
  373. return null;
  374. }
  375. }
  376. Future<String?> login() async {
  377. if (patientOrDoctor == 1) {
  378. if (!Utils.isChinaPhoneLegal(phoneController.text)) {
  379. Component.toast("请输入正确手机号码!", 0);
  380. return null;
  381. }
  382. } else {
  383. if (phoneController.text.isEmpty) {
  384. Component.toast("请输入用户名!", 0);
  385. return null;
  386. }
  387. }
  388. if (!isPasswordLogin && !Utils.isVerifyCode(mController.text)) {
  389. Component.toast("请输入正确验证码!", 0);
  390. return null;
  391. }
  392. //验证码登录
  393. if (!isPasswordLogin) {
  394. bool bo = await _checkCaptchaCode();
  395. if (!bo) {
  396. Component.toast("验证码错误!", 0);
  397. return null;
  398. }
  399. }
  400. Map<String, dynamic> params;
  401. if (isPasswordLogin) {
  402. params = {
  403. 'phoneNumber': phoneController.text,
  404. 'password': aesEncrypt(mController.text),
  405. 'type': patientOrDoctor,
  406. };
  407. } else {
  408. params = {
  409. 'phoneNumber': phoneController.text,
  410. 'captchaCode': mController.text,
  411. 'type': patientOrDoctor,
  412. };
  413. }
  414. String url = 'captcha/login';
  415. if (isPasswordLogin) {
  416. url = 'password/login';
  417. }
  418. logd("提交数据=$params");
  419. var response = await http.post(Uri.parse(Global.BaseUrl + url),
  420. body: encodeBody(params), headers: jsonHeaders());
  421. logd(response.body.toString());
  422. if (response.statusCode == 200) {
  423. final json = decodeBodyToJson(response.bodyBytes);
  424. logd(json);
  425. LoginEntity mLoginEntity = LoginEntity.fromJson(json);
  426. if (mLoginEntity.code == Global.responseSuccessCode) {
  427. Component.toast("登录成功!", 2);
  428. Global.loginPhoneNo = phoneController.text;
  429. Global.token = mLoginEntity.data!.token!;
  430. Global.user = mLoginEntity;
  431. if (patientOrDoctor == 2) {
  432. Utils.saveData("doctorToken", Global.token);
  433. Navigator.pushReplacement(
  434. context,
  435. MaterialPageRoute(builder: (context) => DoctorTalkList()),
  436. );
  437. } else {
  438. Utils.saveData("token", Global.token);
  439. Utils.saveData("userId", Global.user.data!.id.toString());
  440. Utils.saveData("userAvatar", mLoginEntity.data?.avatar ?? "");
  441. Navigator.pushReplacement(
  442. context,
  443. MaterialPageRoute(builder: (context) => BottomNavigationWidget()),
  444. );
  445. }
  446. } else {
  447. Component.toast(mLoginEntity.msg.toString(), 0);
  448. if (mLoginEntity.msg.toString().contains("用户不存在") &&
  449. patientOrDoctor == 1) {
  450. Navigator.push(
  451. context,
  452. MaterialPageRoute(builder: (context) => Register()),
  453. );
  454. }
  455. }
  456. return response.toString();
  457. } else {
  458. Component.toast("出错了,请稍后再试!", 0);
  459. return null;
  460. }
  461. }
  462. ///检查验证码
  463. Future<bool> _checkCaptchaCode() async {
  464. Map<String, dynamic> params = {
  465. "phoneNumber": phoneController.text,
  466. "captchaCode": mController.text
  467. };
  468. Uri uri = Uri.parse("${Global.BaseUrl}api/checkCaptchaCode");
  469. logd("检查验证码,uri=$uri");
  470. Response response = await http.post(uri, body: encodeBody(params), headers: jsonHeaders());
  471. if (response.statusCode == 200) {
  472. final json = decodeBodyToJson(response.bodyBytes);
  473. logd("检查验证码结果$json");
  474. NormalResponse mNormalResponse = NormalResponse.fromJson(json);
  475. return mNormalResponse.code == 200;
  476. } else {
  477. logd("检查验证码异常${response.body}");
  478. }
  479. return false;
  480. }
  481. }