Browse Source

扦样界面每个item比例调整。登录短信验证弹窗。个人中心更新内容。

liujq 1 month ago
parent
commit
f44f4d3307

+ 8 - 0
lib/network/api.dart

@@ -112,6 +112,14 @@ abstract class Api {
112 112
   @POST('/admin-api/system/auth/reset_password_send_sms_code')
113 113
   Future<ApiRsp<bool?>> postPhoneMsg(@Body() Map<String, dynamic> map);
114 114
 
115
+  /// 登录时手机验证码获取
116
+  @POST('/admin-api/system/auth/send-sms-code')
117
+  Future<ApiRsp<bool?>> loginWithPhoneMsg(@Body() Map<String, dynamic> map);
118
+
119
+  /// 手机验证码验证登录
120
+  @POST('/admin-api/system/auth/sms-login')
121
+  Future<ApiRsp<LoginRsp>> smsLogin(@Body() Map<String, dynamic> map);
122
+
115 123
   ///忘记密码
116 124
   @POST('/admin-api/system/auth/reset_password')
117 125
   Future<ApiRsp<bool?>> resetPassword(@Body() Map<String, dynamic> map);

+ 62 - 0
lib/network/api.g.dart

@@ -681,6 +681,68 @@ class _Api implements Api {
681 681
   }
682 682
 
683 683
   @override
684
+  Future<ApiRsp<bool?>> loginWithPhoneMsg(Map<String, dynamic> map) async {
685
+    const _extra = <String, dynamic>{};
686
+    final queryParameters = <String, dynamic>{};
687
+    final _headers = <String, dynamic>{};
688
+    final _data = <String, dynamic>{};
689
+    _data.addAll(map);
690
+    final _result = await _dio
691
+        .fetch<Map<String, dynamic>>(_setStreamType<ApiRsp<bool>>(Options(
692
+      method: 'POST',
693
+      headers: _headers,
694
+      extra: _extra,
695
+    )
696
+            .compose(
697
+              _dio.options,
698
+              '/admin-api/system/auth/send-sms-code',
699
+              queryParameters: queryParameters,
700
+              data: _data,
701
+            )
702
+            .copyWith(
703
+                baseUrl: _combineBaseUrls(
704
+              _dio.options.baseUrl,
705
+              baseUrl,
706
+            ))));
707
+    final value = ApiRsp<bool?>.fromJson(
708
+      _result.data!,
709
+      (json) => json as bool?,
710
+    );
711
+    return value;
712
+  }
713
+
714
+  @override
715
+  Future<ApiRsp<LoginRsp>> smsLogin(Map<String, dynamic> map) async {
716
+    const _extra = <String, dynamic>{};
717
+    final queryParameters = <String, dynamic>{};
718
+    final _headers = <String, dynamic>{};
719
+    final _data = <String, dynamic>{};
720
+    _data.addAll(map);
721
+    final _result = await _dio
722
+        .fetch<Map<String, dynamic>>(_setStreamType<ApiRsp<LoginRsp>>(Options(
723
+      method: 'POST',
724
+      headers: _headers,
725
+      extra: _extra,
726
+    )
727
+            .compose(
728
+              _dio.options,
729
+              '/admin-api/system/auth/sms-login',
730
+              queryParameters: queryParameters,
731
+              data: _data,
732
+            )
733
+            .copyWith(
734
+                baseUrl: _combineBaseUrls(
735
+              _dio.options.baseUrl,
736
+              baseUrl,
737
+            ))));
738
+    final value = ApiRsp<LoginRsp>.fromJson(
739
+      _result.data!,
740
+      (json) => LoginRsp.fromJson(json as Map<String, dynamic>),
741
+    );
742
+    return value;
743
+  }
744
+
745
+  @override
684 746
   Future<ApiRsp<bool?>> resetPassword(Map<String, dynamic> map) async {
685 747
     const _extra = <String, dynamic>{};
686 748
     final queryParameters = <String, dynamic>{};

+ 3 - 1
lib/network/my_api.dart

@@ -14,7 +14,9 @@ class MyApi {
14 14
 
15 15
   /// 测试url
16 16
   // static String testUrl = 'http://101.36.160.117:31070';
17
-  static String testUrl = 'http://121.36.17.6:9398';
17
+   static String testUrl = 'http://121.36.17.6:9398';
18
+
19
+
18 20
 
19 21
 
20 22
   static late String globalUrl;

+ 1 - 1
lib/page/home/home_page.dart

@@ -16,7 +16,7 @@ class HomePage extends StatefulWidget {
16 16
 class _HomePageState extends BaseState<HomePage> with AutomaticKeepAliveClientMixin {
17 17
   List<String> bannerList = [
18 18
     'http://121.36.17.6:19000/quality/app/zsys.png',
19
-    'https://gd-hbimg.huaban.com/82e60fd3c61530d52ec1d01f80cfda11526c42e4495cb-6JwN1d_fw658webp',
19
+    'https://gd-hbimg.huaban.com/c7a22fb15d70a0a976e20fb810c048ec11c76fc31ac08-hajElf_fw658webp',
20 20
   ];
21 21
 
22 22
   late List<ServiceModel> serviceList;

+ 46 - 23
lib/page/login/login_page.dart

@@ -11,7 +11,9 @@ import 'package:lszlgl/network/my_api.dart';
11 11
 import 'package:lszlgl/service/dict_service.dart';
12 12
 import 'package:lszlgl/service/upgrade_service.dart';
13 13
 import 'package:lszlgl/service/user_service.dart';
14
+import 'package:lszlgl/utils/string_utils.dart';
14 15
 import 'package:lszlgl/widget/button.dart';
16
+import 'package:lszlgl/widget/phone_verify_widget.dart';
15 17
 
16 18
 import '../../config/reresh_config.dart';
17 19
 import '../../network/base_dio.dart';
@@ -33,6 +35,25 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
33 35
   late ValueNotifier<bool> _showPwd;
34 36
   late ValueNotifier<Map<String, dynamic>> _account;
35 37
 
38
+  // 手机号验证码弹窗  软检用
39
+  Future<bool> verifyPhone(String phone) async {
40
+    bool canLogin = false;
41
+    if (StringUtils.isPhoneNum(phone)) {
42
+      MyNavigator.dismissLoading();
43
+      canLogin = await showDialog(
44
+          context: context,
45
+          barrierDismissible: false,
46
+          builder: (c) {
47
+            return PhoneVerifyWidget(phone: phone);
48
+          });
49
+    } else {
50
+      canLogin = true;
51
+    }
52
+
53
+    return canLogin;
54
+  }
55
+
56
+  // 验收时 新增验证码登录
36 57
   void onLogin() async {
37 58
     var account = accountCtrl.text;
38 59
     var pwd = pwdCtrl.text;
@@ -49,6 +70,10 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
49 70
         MyNavigator.dismissLoading();
50 71
         return;
51 72
       }
73
+
74
+      // bool canLogin = await verifyPhone(account);
75
+      // if (!canLogin) return;
76
+
52 77
       await UserService.get().saveLogin(login.data);
53 78
 
54 79
       // 存储 账号、密码
@@ -176,7 +201,7 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
176 201
                   ),
177 202
                   const SizedBox(height: 12),
178 203
                   Container(
179
-                    padding: const EdgeInsets.symmetric(horizontal: 10,vertical: 2),
204
+                    padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
180 205
                     decoration: const BoxDecoration(
181 206
                         color: Colors.white,
182 207
                         borderRadius: BorderRadius.only(
@@ -259,7 +284,11 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
259 284
                 child: GestureDetector(
260 285
                   child: Padding(
261 286
                     padding: const EdgeInsets.only(top: 4, right: 16),
262
-                    child: Image.asset(imgEyelash, width: 14,height: 14,),
287
+                    child: Image.asset(
288
+                      imgEyelash,
289
+                      width: 14,
290
+                      height: 14,
291
+                    ),
263 292
                   ),
264 293
                   onTap: () {
265 294
                     _showPwd.value = !_showPwd.value;
@@ -286,8 +315,8 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
286 315
 
287 316
   Widget buildEdit(
288 317
       {required TextEditingController ctrl,
289
-      required String hint, 
290
-        required String titleName,
318
+      required String hint,
319
+      required String titleName,
291 320
       required String topIcon,
292 321
       TextInputAction action = TextInputAction.done,
293 322
       bool obscure = false,
@@ -300,32 +329,26 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
300 329
         Padding(
301 330
           padding: const EdgeInsets.only(left: 8.0),
302 331
           child: Row(
303
-            children: [
304
-              Image.asset(topIcon, height: 18),
305
-              const SizedBox(width: 8),
306
-              Text(titleName)
307
-            ],
332
+            children: [Image.asset(topIcon, height: 18), const SizedBox(width: 8), Text(titleName)],
308 333
           ),
309 334
         ),
310 335
         const SizedBox(height: 6),
311 336
         TextField(
312 337
           controller: ctrl,
313 338
           decoration: InputDecoration(
314
-            border: const OutlineInputBorder(
339
+              border: const OutlineInputBorder(
340
+                  borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(10))),
341
+              enabledBorder: const OutlineInputBorder(
315 342
                 borderSide: BorderSide.none,
316
-                borderRadius: BorderRadius.all(Radius.circular(10))),
317
-            enabledBorder: const OutlineInputBorder(
318
-              borderSide: BorderSide.none,
319
-              borderRadius: BorderRadius.all(Radius.circular(10)),
320
-            ),
321
-            filled: true,
322
-            fillColor: const Color(0xffF6F6F8),
323
-            hintText: hint,
324
-            hintStyle: const TextStyle(color: Color(0xFFBBBBBB)),
325
-            isDense: true,
326
-            suffixIcon: rightIcon,
327
-            suffixIconConstraints: const BoxConstraints()
328
-          ),
343
+                borderRadius: BorderRadius.all(Radius.circular(10)),
344
+              ),
345
+              filled: true,
346
+              fillColor: const Color(0xffF6F6F8),
347
+              hintText: hint,
348
+              hintStyle: const TextStyle(color: Color(0xFFBBBBBB)),
349
+              isDense: true,
350
+              suffixIcon: rightIcon,
351
+              suffixIconConstraints: const BoxConstraints()),
329 352
           style: const TextStyle(fontSize: 14),
330 353
           textInputAction: action,
331 354
           obscureText: obscure,

+ 17 - 3
lib/page/sample_task/reap_sample_detail/reap_sample_basic_detail_page.dart

@@ -1,11 +1,11 @@
1 1
 import 'dart:async';
2 2
 import 'dart:convert';
3
-import 'dart:typed_data';
4 3
 import 'package:amap_flutter_location/amap_location_option.dart';
5 4
 import 'package:cached_network_image/cached_network_image.dart';
6 5
 import 'package:card_swiper/card_swiper.dart';
7 6
 import 'package:device_info_plus/device_info_plus.dart';
8 7
 import 'package:flutter/material.dart';
8
+import 'package:flutter/services.dart';
9 9
 import 'package:image_gallery_saver/image_gallery_saver.dart';
10 10
 import 'package:lszlgl/main.dart';
11 11
 import 'package:lszlgl/page/print/print_page.dart';
@@ -551,6 +551,13 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
551 551
     return true;
552 552
   }
553 553
 
554
+  String? getJWString(String? v){
555
+    if(v!=null && v.contains(',')){
556
+      v = v.replaceAll(',', '\n');
557
+    }
558
+    return v;
559
+  }
560
+
554 561
   @override
555 562
   bool get wantKeepAlive => true;
556 563
 
@@ -624,7 +631,12 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
624 631
           ),
625 632
           child: Column(
626 633
             children: [
627
-              CardItemWidget('样品单号', rightText: data?.qyrwdh),
634
+              CardItemWidget('样品单号', rightText: data?.qyrwdh,rightRatio: 3,
635
+                  onTap:() async{
636
+                    await Clipboard.setData(ClipboardData(text: data?.qyrwdh ?? '' ));
637
+                    MyNavigator.showToast('样品单号已复制');
638
+                  }
639
+              ),
628 640
               CardItemWidget('扦样人员', rightText: data?.name, bottomLine: true),
629 641
               otherpeople.builder((v) => CardItemWidget(
630 642
                 '陪同人员',
@@ -665,6 +677,7 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
665 677
                 '乡镇',
666 678
                 data?.xiangXzqhName?.toString(),
667 679
                 onChanged: (value) => data?.xiangXzqhName = value.isEmpty ? null : value,
680
+                rightRatio: 3
668 681
               ),
669 682
               CardWidgets.buildEdit(
670 683
                 isDetail,
@@ -672,11 +685,12 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
672 685
                 '村',
673 686
                 data?.cunXzqhName?.toString(),
674 687
                 onChanged: (value) => data?.cunXzqhName = value.isEmpty ? null : value,
688
+                rightRatio: 3
675 689
               ),
676 690
               qyddjwd.builder(
677 691
                     (v) => CardItemWidget(
678 692
                   '扦样地点经纬度',
679
-                  rightText: v,
693
+                  rightText: getJWString(v),
680 694
                   bottomLine: true,
681 695
                   trailing: isDetail || v != null ? null : const MyButton('获取经纬度', alignment: null),
682 696
                   onTap: isDetail || data?.deliveryStatus == 2 ? null : getLocation,

+ 8 - 0
lib/page/sample_task/reap_sample_detail/reap_sample_disaster_detail_page.dart

@@ -136,6 +136,7 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
136 136
         CardWidgets.buildMenu(
137 137
           isDetail,
138 138
           '种植土地周围是否有工厂排污',
139
+          leftRatio: 6,
139 140
           boolList,
140 141
           zztdzwsfygcpw,
141 142
           (_, sel) {
@@ -161,6 +162,7 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
161 162
         CardWidgets.buildMenu(
162 163
           isDetail,
163 164
           '种植土地周围使用水源、土壤是否有污染',
165
+          leftRatio: 6,
164 166
           boolList,
165 167
           zztdzwsysytrsfywr,
166 168
           (_, sel) {
@@ -186,6 +188,7 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
186 188
         CardWidgets.buildMenu(
187 189
           isDetail,
188 190
           '生产过程中是否发生比较严重的病虫害',
191
+          leftRatio: 6,
189 192
           boolList,
190 193
           scgczsffsbjyzdbch,
191 194
           (_, sel) {
@@ -206,12 +209,14 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
206 209
           '病虫害类型',
207 210
           bchlxList,
208 211
           bchlx,
212
+          rightRatio: 2,
209 213
           (_, sel) {
210 214
             data?.bchlx = sel.value;
211 215
           },
212 216
         ),
213 217
         CardWidgets.buildMenu(
214 218
           isDetail,
219
+          leftRatio: 6,
215 220
           '收获期间是否发生连阴雨天气',
216 221
           sfList,
217 222
           shqjsffslyytq,
@@ -219,6 +224,7 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
219 224
         ),
220 225
         CardWidgets.buildMenu(
221 226
           isDetail,
227
+          leftRatio: 6,
222 228
           '收获粮食水分是否偏高',
223 229
           sfList,
224 230
           shlssfsfpg,
@@ -226,6 +232,7 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
226 232
         ),
227 233
         CardWidgets.buildMenu(
228 234
           isDetail,
235
+          leftRatio: 6,
229 236
           '近年来本地是否发生真菌毒素污染',
230 237
           sfList,
231 238
           jnlbdsffszmwr,
@@ -251,6 +258,7 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
251 258
         ),
252 259
         CardWidgets.buildMenu(
253 260
           isDetail,
261
+          leftRatio: 6,
254 262
           '近年来本地是否发生重金属污染',
255 263
           sfList,
256 264
           jnlbdsfzjswr,

+ 1 - 0
lib/page/sample_task/reap_sample_detail/reap_sample_medicine_detail_page.dart

@@ -195,6 +195,7 @@ class _ReapSampleMedicineDetailPageState extends BaseLifecycleState<ReapSampleMe
195 195
                     CardWidgets.buildEdit(
196 196
                       isDetail,
197 197
                       '施药方法',
198
+                      rightRatio: 2,
198 199
                       item.syff,
199 200
                       backgroundColor: null,
200 201
                       onChanged: (value) => item.syff = value,

+ 1 - 0
lib/page/sample_task/reap_sample_detail/reap_sample_org_detail_page.dart

@@ -84,6 +84,7 @@ class _ReapSampleOrgDetailPageState extends BaseLifecycleState<ReapSampleOrgDeta
84 84
           '食品安全指标检验机构',
85 85
           rightText: aqjg,
86 86
           bottomLine: true,
87
+          leftRatio: aqjg==null ? 2:1,
87 88
         ),
88 89
         // CardItemWidget(
89 90
         //   '机构地址',

+ 4 - 0
lib/page/sample_task/reap_sample_detail/reap_sample_variety_detail_page.dart

@@ -259,6 +259,7 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
259 259
         CardWidgets.buildMenu(
260 260
           isDetail,
261 261
           '粮食品类',
262
+          rightRatio: 2,
262 263
           lspzList,
263 264
           lspz,
264 265
           (_, sel) => data?.lspz = sel.value?.toString(),
@@ -266,6 +267,7 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
266 267
         CardWidgets.buildEdit(
267 268
           isDetail,
268 269
           '种植品种',
270
+          rightRatio: 2,
269 271
           data?.jtpzmc,
270 272
           onChanged: (value) => data?.jtpzmc = value,
271 273
         ),
@@ -348,6 +350,7 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
348 350
               formatters: [XNumberTextInputFormatter()],
349 351
               onChanged: (value) => item.qysl = value.isEmpty ? null : num.parse(value),
350 352
               backgroundColor: null,
353
+              leftRatio: 2
351 354
             ),
352 355
             CardWidgets.buildEdit(
353 356
               isDetail,
@@ -357,6 +360,7 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
357 360
               formatters: [XNumberTextInputFormatter()],
358 361
               onChanged: (value) => item.qydbsl = value.isEmpty ? null : num.parse(value),
359 362
               backgroundColor: null,
363
+              leftRatio: 2
360 364
             ),
361 365
             CardWidgets.buildEdit(
362 366
               isDetail,

+ 14 - 3
lib/page/user_center/setting_page.dart

@@ -24,8 +24,8 @@ class _SettingPageState extends BaseState<SettingPage> {
24 24
     MyNavigator.pop();
25 25
   }
26 26
 
27
-  void onVersionTap() async {
28
-    UpgradeService.checkUpgrade(true);
27
+  void onVersionTap(bool show, {bool pop=false}) async {
28
+    UpgradeService.checkUpgrade(show, pop: pop);
29 29
   }
30 30
 
31 31
   @override
@@ -83,7 +83,18 @@ class _SettingPageState extends BaseState<SettingPage> {
83 83
             bottomLine: true,
84 84
           ),
85 85
           CardItemWidget('版本信息',
86
-              rightText: 'V${AppConfig.packageInfo.version}', onTap: onVersionTap),
86
+              rightText: 'V${AppConfig.packageInfo.version}',
87
+              onTap: (){
88
+                onVersionTap(true,pop: false);
89
+              },
90
+              bottomLine: true,
91
+          ),
92
+          CardItemWidget('更新内容',
93
+            rightChild: const Icon(Icons.keyboard_arrow_right,size: 24, color: Color(0xFF01B2C8)),
94
+            onTap: (){
95
+              onVersionTap(true,pop: true);
96
+            },
97
+          ),
87 98
         ],
88 99
       ),
89 100
     );

+ 50 - 3
lib/service/upgrade_service.dart

@@ -1,8 +1,10 @@
1 1
 import 'dart:io';
2 2
 
3 3
 import 'package:dio/dio.dart';
4
+import 'package:flutter/material.dart';
4 5
 import 'package:install_plugin/install_plugin.dart';
5 6
 import 'package:lszlgl/config/app_config.dart';
7
+import 'package:lszlgl/widget/button.dart';
6 8
 import 'package:path_provider/path_provider.dart';
7 9
 import 'package:permission_handler/permission_handler.dart';
8 10
 
@@ -54,10 +56,17 @@ class UpgradeService {
54 56
     return pathSplit[pathSplit.length - 1];
55 57
   }
56 58
 
59
+  static String? getContentsString(String? v) {
60
+    if (v != null && v.contains('*')) {
61
+      v = v.replaceAll('*', '\n');
62
+    }
63
+    return v;
64
+  }
65
+
57 66
   static bool checking = false;
58 67
 
59 68
   /// 检查版本更新
60
-  static Future<void> checkUpgrade(bool showLoading) async {
69
+  static Future<void> checkUpgrade(bool showLoading, {bool pop = false}) async {
61 70
     if (!Platform.isAndroid) return;
62 71
     if (checking) return;
63 72
     checking = true;
@@ -75,10 +84,48 @@ class UpgradeService {
75 84
         int localCode = int.parse(AppConfig.packageInfo.buildNumber);
76 85
         logger.d('versionUpgrade:localCode:$localCode onlineCode:$onlineCode');
77 86
         if (localCode >= onlineCode) {
78
-          if (showLoading) MyNavigator.showToast('已是最新版本');
87
+          if (showLoading && !pop) {
88
+            MyNavigator.showToast('已是最新版本');
89
+          }
90
+          if (showLoading && pop) {
91
+            MyNavigator.showDialog(
92
+              tag: 'pop',
93
+              builder: (ctx) {
94
+                return Container(
95
+                  margin: const EdgeInsets.symmetric(horizontal: 24),
96
+                  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
97
+                  decoration: const BoxDecoration(
98
+                    color: Colors.white,
99
+                    borderRadius: BorderRadius.all(Radius.circular(8)),
100
+                  ),
101
+                  child: Column(
102
+                    mainAxisSize: MainAxisSize.min,
103
+                    crossAxisAlignment: CrossAxisAlignment.stretch,
104
+                    children: [
105
+                      const Text(
106
+                        '当前版本更新内容:',
107
+                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
108
+                      ),
109
+                      const SizedBox(height: 12),
110
+                      Text(UpgradeService.getContentsString(contents) ?? ''),
111
+                      const SizedBox(height: 22),
112
+                      MyButton(
113
+                        '知道了',
114
+                        radius: 8,
115
+
116
+                        onTap: () {
117
+                          MyNavigator.dismiss(tag: 'pop');
118
+                        },
119
+                      )
120
+                    ],
121
+                  ),
122
+                );
123
+              },
124
+            );
125
+          }
79 126
         } else {
80 127
           // 版本更新
81
-          UpgradeDialog.showDialog(path,contents);
128
+          UpgradeDialog.showDialog(path, UpgradeService.getContentsString(contents) ?? '');
82 129
         }
83 130
       }
84 131
     } catch (e) {

+ 59 - 13
lib/widget/card_item.dart

@@ -33,6 +33,10 @@ class CardItemWidget extends StatelessWidget {
33 33
 
34 34
   final Color? backgroundColor;
35 35
 
36
+  // 左右两边 填充比例
37
+  final int leftRatio;
38
+  final int rightRatio;
39
+
36 40
   const CardItemWidget(
37 41
     this.title, {
38 42
     Key? key,
@@ -46,6 +50,8 @@ class CardItemWidget extends StatelessWidget {
46 50
     this.showTopLine = false,
47 51
     this.bottomLine = false,
48 52
     this.backgroundColor = Colors.white,
53
+    this.leftRatio = 1,
54
+    this.rightRatio = 1,
49 55
   }) : super(key: key);
50 56
 
51 57
   @override
@@ -56,7 +62,9 @@ class CardItemWidget extends StatelessWidget {
56 62
       child: Column(
57 63
         mainAxisSize: MainAxisSize.min,
58 64
         children: [
59
-          showTopLine ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1) : const SizedBox.shrink(),
65
+          showTopLine
66
+              ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1)
67
+              : const SizedBox.shrink(),
60 68
           Container(
61 69
             constraints: const BoxConstraints(minHeight: 48),
62 70
             padding: const EdgeInsets.all(12),
@@ -64,14 +72,16 @@ class CardItemWidget extends StatelessWidget {
64 72
             child: Row(
65 73
               children: [
66 74
                 buildLeading(),
67
-                SizedBox(width: (MediaQuery.of(context).size.width/2)-20,child: buildTitle()),
75
+                buildTitle(),
68 76
                 const SizedBox(width: 4),
69 77
                 buildRight(),
70 78
                 buildTrailing(),
71 79
               ],
72 80
             ),
73 81
           ),
74
-          bottomLine ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1) : const SizedBox.shrink(),
82
+          bottomLine
83
+              ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1)
84
+              : const SizedBox.shrink(),
75 85
         ],
76 86
       ),
77 87
     );
@@ -94,12 +104,15 @@ class CardItemWidget extends StatelessWidget {
94 104
   }
95 105
 
96 106
   Widget buildTitle() {
97
-    return Text(
98
-      title,
99
-      style: const TextStyle(
100
-        color: Color(0xFF515151),
101
-        fontSize: 14,
102
-        fontWeight: FontWeight.w300,
107
+    return Expanded(
108
+      flex: leftRatio,
109
+      child: Text(
110
+        title,
111
+        style: const TextStyle(
112
+          color: Color(0xFF515151),
113
+          fontSize: 14,
114
+          fontWeight: FontWeight.w300,
115
+        ),
103 116
       ),
104 117
     );
105 118
   }
@@ -120,6 +133,7 @@ class CardItemWidget extends StatelessWidget {
120 133
       );
121 134
     }
122 135
     return Expanded(
136
+      flex: rightRatio,
123 137
       child: Container(
124 138
         alignment: rightAlign,
125 139
         child: child,
@@ -145,6 +159,8 @@ class CardItemMenuWidget extends StatelessWidget {
145 159
 
146 160
   final Color? backgroundColor;
147 161
   final Color? menuColor;
162
+  final int leftRatio;
163
+  final int rightRatio;
148 164
 
149 165
   final ValueNotifier<List<CardMenuData>> listNotifier;
150 166
   final ValueNotifier<CardMenuData?> selNotifier;
@@ -166,6 +182,9 @@ class CardItemMenuWidget extends StatelessWidget {
166 182
     this.backgroundColor = Colors.white,
167 183
     this.menuColor = Colors.white,
168 184
     this.onSelectTap,
185
+        this.leftRatio = 1,
186
+        this.rightRatio = 1,
187
+
169 188
   }) : super(key: key);
170 189
 
171 190
   void clickMenu() {
@@ -190,6 +209,8 @@ class CardItemMenuWidget extends StatelessWidget {
190 209
       bottomLine: bottomLine,
191 210
       backgroundColor: backgroundColor,
192 211
       rightChild: buildMenu(),
212
+      leftRatio: leftRatio,
213
+      rightRatio: rightRatio,
193 214
     );
194 215
   }
195 216
 
@@ -239,7 +260,11 @@ class CardItemMenuWidget extends StatelessWidget {
239 260
           selData == null
240 261
               ? const Padding(
241 262
                   padding: EdgeInsets.only(left: 4),
242
-                  child: Icon(Icons.keyboard_arrow_down, size: 24, color: Color(0xFF01B2C8),),
263
+                  child: Icon(
264
+                    Icons.keyboard_arrow_down,
265
+                    size: 24,
266
+                    color: Color(0xFF01B2C8),
267
+                  ),
243 268
                 )
244 269
               : const SizedBox.shrink(),
245 270
         ],
@@ -259,7 +284,8 @@ class CardItemMenuWidget extends StatelessWidget {
259 284
         alignment: Alignment.center,
260 285
         child: Text(
261 286
           item.name ?? '',
262
-          style: TextStyle(color: item.value == selData?.value ? const Color(0xFF01B2C8) : MyColor.c_666666),
287
+          style: TextStyle(
288
+              color: item.value == selData?.value ? const Color(0xFF01B2C8) : MyColor.c_666666),
263 289
         ),
264 290
       ),
265 291
     );
@@ -297,6 +323,8 @@ class CardWidgets {
297 323
     TextEditingController? ctrl,
298 324
     int? maxLength,
299 325
     String? errorText,
326
+        int leftRatio = 1,
327
+        int rightRatio = 1,
300 328
   }) {
301 329
     if (isDetail) {
302 330
       return CardItemWidget(
@@ -304,6 +332,8 @@ class CardWidgets {
304 332
         rightText: value,
305 333
         bottomLine: bottomLine,
306 334
         backgroundColor: backgroundColor,
335
+        leftRatio: leftRatio,
336
+        rightRatio: rightRatio,
307 337
       );
308 338
     }
309 339
     return CardItemWidget(
@@ -330,6 +360,8 @@ class CardWidgets {
330 360
       ),
331 361
       backgroundColor: backgroundColor,
332 362
       bottomLine: bottomLine,
363
+      leftRatio: leftRatio,
364
+      rightRatio: rightRatio,
333 365
     );
334 366
   }
335 367
 
@@ -349,7 +381,11 @@ class CardWidgets {
349 381
         title,
350 382
         rightText: v,
351 383
         trailing: v == null
352
-            ? const Icon(Icons.keyboard_arrow_down, size: 24, color: Color(0xFF01B2C8),)
384
+            ? const Icon(
385
+                Icons.keyboard_arrow_down,
386
+                size: 24,
387
+                color: Color(0xFF01B2C8),
388
+              )
353 389
             : const SizedBox.shrink(),
354 390
         bottomLine: true,
355 391
         onTap: () async {
@@ -388,7 +424,11 @@ class CardWidgets {
388 424
       trailing: isDetail || value != null
389 425
           ? null
390 426
           : trailing ??
391
-           const Icon(Icons.keyboard_arrow_down, size: 24, color: Color(0xFF01B2C8),),
427
+              const Icon(
428
+                Icons.keyboard_arrow_down,
429
+                size: 24,
430
+                color: Color(0xFF01B2C8),
431
+              ),
392 432
       onTap: isDetail
393 433
           ? null
394 434
           : () async {
@@ -413,6 +453,8 @@ class CardWidgets {
413 453
     String? hint,
414 454
     bool showTopLine = false,
415 455
     bool bottomLine = true,
456
+        int leftRatio = 1,
457
+        int rightRatio = 1,
416 458
   }) {
417 459
     if (isDetail) {
418 460
       return selNotifier.builder(
@@ -422,6 +464,8 @@ class CardWidgets {
422 464
           bottomLine: bottomLine,
423 465
           hint: hint,
424 466
           showTopLine: showTopLine,
467
+          leftRatio: leftRatio,
468
+          rightRatio: rightRatio,
425 469
         ),
426 470
       );
427 471
     }
@@ -433,6 +477,8 @@ class CardWidgets {
433 477
       bottomLine: bottomLine,
434 478
       hint: hint,
435 479
       showTopLine: showTopLine,
480
+      leftRatio: leftRatio,
481
+      rightRatio: rightRatio,
436 482
     );
437 483
   }
438 484
 }

+ 136 - 0
lib/widget/phone_verify_widget.dart

@@ -0,0 +1,136 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:lszlgl/base/base_lifecycle_state.dart';
3
+import 'package:lszlgl/main.dart';
4
+import 'package:lszlgl/network/my_api.dart';
5
+import 'package:lszlgl/widget/button.dart';
6
+import 'package:pin_code_fields/pin_code_fields.dart';
7
+
8
+class PhoneVerifyWidget extends StatefulWidget {
9
+  final String phone;
10
+  const PhoneVerifyWidget({super.key, required this.phone});
11
+
12
+  @override
13
+  State<PhoneVerifyWidget> createState() => _PhoneVerifyWidgetState();
14
+}
15
+
16
+class _PhoneVerifyWidgetState extends State<PhoneVerifyWidget> {
17
+  String currentText = "";
18
+  String starPhone = '';
19
+
20
+  /// 获取验证码
21
+  Future<bool> getPhoneMsg() async {
22
+    try {
23
+      var res = await MyApi.get().loginWithPhoneMsg({
24
+        'mobile': widget.phone,
25
+        'scene': 21,
26
+      });
27
+      //print('$res.data');
28
+      if (res.data ?? false) {
29
+        MyNavigator.showToast('验证码已发送');
30
+        return true;
31
+      } else {
32
+        MyNavigator.showToast('验证码发送失败');
33
+        return false;
34
+      }
35
+    } catch (e) {
36
+      logger.e(e);
37
+      return false;
38
+    }
39
+  }
40
+
41
+  @override
42
+  void initState() {
43
+    super.initState();
44
+    getPhoneMsg();
45
+    starPhone = widget.phone.replaceFirst(RegExp(r'\d{4}'), '****', 3);
46
+  }
47
+
48
+  @override
49
+  Widget build(BuildContext context) {
50
+    return PopScope(
51
+      canPop: false,
52
+      onPopInvoked: (bool didPop) {
53
+        if (didPop) {
54
+          return;
55
+        } else {
56
+          Navigator.of(context).pop(false);
57
+        }
58
+      },
59
+      child: Dialog(
60
+        insetPadding: const EdgeInsets.symmetric(horizontal: 22),
61
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
62
+        child: Container(
63
+          padding: const EdgeInsets.fromLTRB(18, 24, 18, 18),
64
+          child: Column(
65
+            mainAxisSize: MainAxisSize.min,
66
+            children: [
67
+              const Text('短信验证码',style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16),),
68
+              const SizedBox(height: 8),
69
+              Text('已向$starPhone发送短信验证码', style: const TextStyle(color: Colors.grey),),
70
+              const SizedBox(height: 22),
71
+              PinCodeTextField(
72
+                length: 6,
73
+                appContext: context,
74
+                autoFocus: true,
75
+                keyboardType: TextInputType.number,
76
+                cursorColor: Colors.grey,
77
+                animationType: AnimationType.scale,
78
+                pinTheme: PinTheme(
79
+                    activeColor: const Color(0xFF25A6EE),
80
+                    selectedColor: const Color(0xFF25A6EE),
81
+                    inactiveColor: const Color(0xFF25A6EE)),
82
+                onChanged: (value) {
83
+                  setState(() {
84
+                    currentText = value;
85
+                  });
86
+                },
87
+              ),
88
+              const SizedBox(height: 16),
89
+              MyButton(
90
+                '登 录',
91
+                radius: 8,
92
+                fontWeight: FontWeight.bold,
93
+                gradient: const LinearGradient(colors: [Color(0xFF3BD2E5), Color(0xFF247AF8)]),
94
+                onTap: () async {
95
+                  if (currentText.length < 6) {
96
+                    MyNavigator.showToast('验证码错误');
97
+                    return;
98
+                  }
99
+
100
+                  MyNavigator.showLoading();
101
+                  try {
102
+                    var res = await MyApi.get().smsLogin({
103
+                      'mobile': widget.phone,
104
+                      'code': currentText,
105
+                    });
106
+                    if (res.data != null) {
107
+                      if (context.mounted) {
108
+                        Navigator.of(context).pop(true);
109
+                      }
110
+                    }
111
+                  } catch (e) {
112
+                    logger.e(e);
113
+                  }
114
+                  MyNavigator.dismissLoading();
115
+                },
116
+              ),
117
+              Row(
118
+                children: [
119
+                  const Spacer(),
120
+                  TextButton(
121
+                      onPressed: () {
122
+                        getPhoneMsg();
123
+                      },
124
+                      child: const Text(
125
+                        '重发验证码',
126
+                        style: TextStyle(fontSize: 12, color: Color(0xFF25A6EE)),
127
+                      ))
128
+                ],
129
+              )
130
+            ],
131
+          ),
132
+        ),
133
+      ),
134
+    );
135
+  }
136
+}

+ 7 - 7
lib/widget/upgrade_dialog.dart

@@ -82,26 +82,26 @@ class UpgradeDialog extends StatelessWidget {
82 82
         mainAxisSize: MainAxisSize.min,
83 83
         children: [
84 84
           Container(
85
-            alignment: Alignment.topLeft,
85
+            alignment: Alignment.topCenter,
86 86
             child: Text(
87 87
               '发现新版本 $versionName',
88
-              style: const TextStyle(color: Color(0xFF333333), fontSize: 16, fontWeight: FontWeight.w500),
88
+              style: const TextStyle(color: Color(0xFF333333), fontSize: 16, fontWeight: FontWeight.bold),
89 89
             ),
90 90
           ),
91
-
91
+          const SizedBox(height: 12),
92 92
           const SizedBox(
93 93
             width: double.infinity,
94 94
             child: Text(
95
-             '更新内容:',
96
-             style:  TextStyle(fontSize: 14,color: Color(0xFF333333)),
95
+             '更新内容',
96
+             style:  TextStyle(fontSize: 14,color: Color(0xFF333333),fontWeight: FontWeight.bold),
97 97
             ),
98 98
           ),
99
-
99
+          const SizedBox(height: 4),
100 100
           SizedBox(
101 101
             width: double.infinity,
102 102
             child: Text(
103 103
               contents,
104
-              style: const TextStyle(fontSize: 14,color: Color(0xFF333333),fontWeight: FontWeight.bold),
104
+              style: const TextStyle(fontSize: 14,color: Color(0xFF333333)),
105 105
             ),
106 106
           ),
107 107
 

+ 1 - 0
pubspec.yaml

@@ -99,6 +99,7 @@ dependencies:
99 99
   jpush_flutter: 3.0.3
100 100
   # 虚线边框
101 101
   dotted_border: ^2.1.0
102
+  pin_code_fields: ^8.0.1
102 103
 
103 104
 dev_dependencies:
104 105
   flutter_test: