Bladeren bron

v0.0.5; 修改logo; 修改收购扦样列表; 修改收购扦样详情和编辑;

maqiang 1 jaar geleden
bovenliggende
commit
ba54ee9a63
33 gewijzigde bestanden met toevoegingen van 554 en 139 verwijderingen
  1. 10 0
      README.md
  2. 5 0
      android/app/build.gradle
  3. 51 14
      android/app/src/main/AndroidManifest.xml
  4. BIN
      android/app/src/main/ic_launcher-playstore.png
  5. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  6. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
  7. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  8. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
  9. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  10. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  11. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  12. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  13. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  14. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  15. BIN
      assets/images/login_title.webp
  16. 4 0
      lib/main.dart
  17. 15 2
      lib/model/rsp/sample_task_rsp.dart
  18. 10 0
      lib/model/rsp/sample_task_rsp.g.dart
  19. 4 0
      lib/network/api.dart
  20. 134 63
      lib/page/sample_task/reap_sample_detail/reap_sample_basic_detail_page.dart
  21. 8 9
      lib/page/sample_task/reap_sample_detail/reap_sample_disaster_detail_page.dart
  22. 4 1
      lib/page/sample_task/reap_sample_detail/reap_sample_task_page.dart
  23. 28 15
      lib/page/sample_task/reap_sample_detail/reap_sample_variety_detail_page.dart
  24. 2 2
      lib/page/sample_task/sample_list_vm.dart
  25. 49 22
      lib/page/sample_task/sample_task_list_page.dart
  26. 1 1
      lib/page/sample_task/sample_task_list_tab_page.dart
  27. 1 1
      lib/router/my_router.dart
  28. 4 0
      lib/service/dict_service.dart
  29. 131 0
      lib/utils/location_utils.dart
  30. 6 8
      lib/widget/card_item.dart
  31. 76 0
      lib/widget/menu_dialog.dart
  32. 8 0
      pubspec.lock
  33. 3 1
      pubspec.yaml

+ 10 - 0
README.md

@@ -14,3 +14,13 @@ A few resources to get you started if this is your first Flutter project:
14 14
 For help getting started with Flutter development, view the
15 15
 [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 16
 samples, guidance on mobile development, and a full API reference.
17
+
18
+### 2024.03.14 app改动点:
19
+1. 扦样任务单状态新增:待领取, 检测机构收到扦样任务通知时,所有扦样人员都能看到所有任务单据,在app待扦样页面进行任务领取.
20
+现在的开始扦样按钮改成任务领取,点击任务领取以后扦样任务单状态改成待扦样,列表页按钮变成开始扦样,点击扦样进行正常操作。√
21
+2. 扦样人员变成多选,后端提供人员接口,选择扦样人员;
22
+3. 温度、湿度字段去掉,新增字段:天气情况:晴天/阴天/雨天,单选;√
23
+4. 扦样时间默认给当前时间;√
24
+5. 灾害污染情况给默认值:否;√
25
+6. 点击经纬度获取,自动代入省市县乡镇村;√
26
+7. 列表页显示字段修改:扦样任务单号/状态/检验指标/样品等级/采样品种;√

+ 5 - 0
android/app/build.gradle

@@ -77,6 +77,11 @@ android {
77 77
             it.outputFileName = "lszlgl_${buildType}_${defaultConfig.versionName}_${defaultConfig.versionCode}.apk"
78 78
         }
79 79
     }
80
+
81
+    dependencies {
82
+        implementation fileTree(include: ['*.jar'], dir: 'libs')
83
+        implementation 'com.amap.api:location:5.6.0'
84
+    }
80 85
 }
81 86
 
82 87
 flutter {

+ 51 - 14
android/app/src/main/AndroidManifest.xml

@@ -1,32 +1,66 @@
1 1
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
-    <uses-permission android:name="android.permission.INTERNET"/>
3
-    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
4
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2
+    <!--允许访问网络,必选权限-->
3
+    <uses-permission android:name="android.permission.INTERNET" />
4
+
5
+    <!--允许获取精确位置,精准定位必选-->
6
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
7
+
8
+    <!--允许获取粗略位置,粗略定位必选-->
9
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
10
+
11
+    <!--允许获取设备和运营商信息,用于问题排查和网络定位(无gps情况下的定位),若需网络定位功能则必选-->
12
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
13
+
14
+    <!--允许获取网络状态,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
15
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
16
+
17
+    <!--允许获取wifi网络信息,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
18
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
19
+
20
+    <!--允许获取wifi状态改变,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
21
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
22
+
23
+    <!--后台获取位置信息,若需后台定位则必选-->
24
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
25
+
26
+    <!--用于申请调用A-GPS模块,卫星定位加速-->
27
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
28
+
29
+    <!--允许写设备缓存,用于问题排查-->
30
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
31
+
32
+    <!--允许写入扩展存储,用于写入缓存定位数据-->
5 33
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
34
+
35
+    <!--允许读设备等信息,用于问题排查-->
36
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
37
+
38
+    <!--允许安装应用-->
39
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
40
+
6 41
     <application
7
-        android:label="粮食质量管理"
8 42
         android:name="${applicationName}"
9
-        android:icon="@mipmap/ic_launcher">
43
+        android:icon="@mipmap/ic_launcher"
44
+        android:label="粮食质量管理">
10 45
         <activity
11 46
             android:name=".MainActivity"
47
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
12 48
             android:exported="true"
49
+            android:hardwareAccelerated="true"
13 50
             android:launchMode="singleTop"
51
+            android:networkSecurityConfig="@xml/network_security_config"
14 52
             android:theme="@style/LaunchTheme"
15
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
16
-            android:hardwareAccelerated="true"
17
-            android:windowSoftInputMode="adjustResize"
18
-            android:networkSecurityConfig="@xml/network_security_config">
53
+            android:windowSoftInputMode="adjustResize">
19 54
             <!-- Specifies an Android theme to apply to this Activity as soon as
20 55
                  the Android process has started. This theme is visible to the user
21 56
                  while the Flutter UI initializes. After that, this theme continues
22 57
                  to determine the Window background behind the Flutter UI. -->
23 58
             <meta-data
24
-              android:name="io.flutter.embedding.android.NormalTheme"
25
-              android:resource="@style/NormalTheme"
26
-              />
59
+                android:name="io.flutter.embedding.android.NormalTheme"
60
+                android:resource="@style/NormalTheme" />
27 61
             <intent-filter>
28
-                <action android:name="android.intent.action.MAIN"/>
29
-                <category android:name="android.intent.category.LAUNCHER"/>
62
+                <action android:name="android.intent.action.MAIN" />
63
+                <category android:name="android.intent.category.LAUNCHER" />
30 64
             </intent-filter>
31 65
         </activity>
32 66
         <!-- Don't delete the meta-data below.
@@ -34,5 +68,8 @@
34 68
         <meta-data
35 69
             android:name="flutterEmbedding"
36 70
             android:value="2" />
71
+
72
+        <!-- 配置定位Service -->
73
+        <service android:name="com.amap.api.location.APSService"/>
37 74
     </application>
38 75
 </manifest>

BIN
android/app/src/main/ic_launcher-playstore.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
assets/images/login_title.webp


+ 4 - 0
lib/main.dart

@@ -8,6 +8,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
8 8
 import 'package:logger/logger.dart';
9 9
 import 'package:lszlgl/network/base_dio.dart';
10 10
 import 'package:lszlgl/router/my_navigator.dart';
11
+import 'package:lszlgl/utils/location_utils.dart';
11 12
 import 'package:lszlgl/utils/sp_utils.dart';
12 13
 
13 14
 import 'config/reresh_config.dart';
@@ -25,6 +26,9 @@ void main() async {
25 26
     BaseDio.get().init();
26 27
     await SPUtils.getInstance().init();
27 28
     RefreshConfig.get().initDefault();
29
+    LocationUtils.updatePrivacyShow(true, true);
30
+    LocationUtils.updatePrivacyAgree(true);
31
+    LocationUtils.setApiKey('2c783509376e267b24d63b21681686fa', '');
28 32
     runApp(const MyApp());
29 33
   });
30 34
 }

+ 15 - 2
lib/model/rsp/sample_task_rsp.dart

@@ -26,7 +26,10 @@ class SampleTaskItem {
26 26
   String? fjrq; // 分解日期
27 27
   String? ypbh; // 样品编号
28 28
   String? jtpzmc; // 具体品种名称
29
-  num? deliveryStatus; // 任务单任务状态:0是待扦样,1已扦样
29
+  num? deliveryStatus; // 任务单任务状态:0是待领取,1是待扦样,2已扦样
30
+  num? ypdj; // 样品等级--字典获取
31
+  String? jyzb; // 检验指标:质量/品质/食安
32
+  String? qydq; // 扦样地区
30 33
   num? jhcysl; // 计划采样数量
31 34
   num? zljysl; // 质量检验数量
32 35
   num? pzjyjg; // 品质检验机构
@@ -74,6 +77,7 @@ class SampleTaskItem {
74 77
   num? createTime; // 创建时间
75 78
   num? state; // 0是待扦样,1已扦样,2已完成
76 79
   String? name; // 扦样人员姓名
80
+  String? dgryName; // 扦样多个人员名称: 张三,李四
77 81
   String? rwjssj; // 任务接收时间
78 82
   String? bsjzsj; // 报送截止时间
79 83
   num? rwlx; // 任务类型/监测环节
@@ -103,6 +107,7 @@ class SampleTaskItem {
103 107
   num? zjswrlx; // 重金属污染类型
104 108
   List<JyjgxxItem>? jyjgxxRespVOList; // 检验机构信息
105 109
   num? jypzStatus; // 检验品质按钮0显示1不显示
110
+  num? tqqk; // 天气情况
106 111
 
107 112
   SampleTaskItem({
108 113
     this.id,
@@ -111,6 +116,9 @@ class SampleTaskItem {
111 116
     this.ypbh,
112 117
     this.jtpzmc,
113 118
     this.deliveryStatus,
119
+    this.ypdj,
120
+    this.jyzb,
121
+    this.qydq,
114 122
     this.jhcysl,
115 123
     this.zljysl,
116 124
     this.pzjyjg,
@@ -158,6 +166,7 @@ class SampleTaskItem {
158 166
     this.createTime,
159 167
     this.state,
160 168
     this.name,
169
+    this.dgryName,
161 170
     this.rwjssj,
162 171
     this.bsjzsj,
163 172
     this.rwlx,
@@ -185,6 +194,7 @@ class SampleTaskItem {
185 194
     this.zjswrlx,
186 195
     this.jyjgxxRespVOList,
187 196
     this.jypzStatus,
197
+    this.tqqk,
188 198
   });
189 199
 
190 200
   factory SampleTaskItem.fromJson(Map<String, dynamic> json) => _$SampleTaskItemFromJson(json);
@@ -192,7 +202,8 @@ class SampleTaskItem {
192 202
   Map<String, dynamic> toJson() => _$SampleTaskItemToJson(this);
193 203
 
194 204
   String getDeliveryStatusText() {
195
-    return switch (deliveryStatus) { 0 => '待扦样', 1 => '已扦样', _ => '' };
205
+    // 任务单任务状态:0是待领取,1是待扦样,2已扦样
206
+    return switch (deliveryStatus) { 0 => '待领取', 1 => '待扦样', 2 => '已扦样', _ => '' };
196 207
   }
197 208
 
198 209
   Map<String, dynamic> getReqJson() {
@@ -233,6 +244,8 @@ class SampleTaskItem {
233 244
     map['jnlbdsfzjswr'] = jnlbdsfzjswr;
234 245
     map['zjswrlx'] = zjswrlx;
235 246
     map['codeUseMedicineList'] = codeUseMedicineList?.map((e) => e.toReqJson()).toList();
247
+    map['dgryName'] = dgryName;
248
+    map['tqqk'] = tqqk;
236 249
 
237 250
     return map;
238 251
   }

+ 10 - 0
lib/model/rsp/sample_task_rsp.g.dart

@@ -28,6 +28,9 @@ SampleTaskItem _$SampleTaskItemFromJson(Map<String, dynamic> json) =>
28 28
       ypbh: const StringConverter().fromJson(json['ypbh']),
29 29
       jtpzmc: const StringConverter().fromJson(json['jtpzmc']),
30 30
       deliveryStatus: const NumConverter().fromJson(json['deliveryStatus']),
31
+      ypdj: const NumConverter().fromJson(json['ypdj']),
32
+      jyzb: const StringConverter().fromJson(json['jyzb']),
33
+      qydq: const StringConverter().fromJson(json['qydq']),
31 34
       jhcysl: const NumConverter().fromJson(json['jhcysl']),
32 35
       zljysl: const NumConverter().fromJson(json['zljysl']),
33 36
       pzjyjg: const NumConverter().fromJson(json['pzjyjg']),
@@ -80,6 +83,7 @@ SampleTaskItem _$SampleTaskItemFromJson(Map<String, dynamic> json) =>
80 83
       createTime: const NumConverter().fromJson(json['createTime']),
81 84
       state: const NumConverter().fromJson(json['state']),
82 85
       name: const StringConverter().fromJson(json['name']),
86
+      dgryName: const StringConverter().fromJson(json['dgryName']),
83 87
       rwjssj: const StringConverter().fromJson(json['rwjssj']),
84 88
       bsjzsj: const StringConverter().fromJson(json['bsjzsj']),
85 89
       rwlx: const NumConverter().fromJson(json['rwlx']),
@@ -117,6 +121,7 @@ SampleTaskItem _$SampleTaskItemFromJson(Map<String, dynamic> json) =>
117 121
           ?.map((e) => JyjgxxItem.fromJson(e as Map<String, dynamic>))
118 122
           .toList(),
119 123
       jypzStatus: const NumConverter().fromJson(json['jypzStatus']),
124
+      tqqk: const NumConverter().fromJson(json['tqqk']),
120 125
     );
121 126
 
122 127
 Map<String, dynamic> _$SampleTaskItemToJson(SampleTaskItem instance) =>
@@ -127,6 +132,9 @@ Map<String, dynamic> _$SampleTaskItemToJson(SampleTaskItem instance) =>
127 132
       'ypbh': const StringConverter().toJson(instance.ypbh),
128 133
       'jtpzmc': const StringConverter().toJson(instance.jtpzmc),
129 134
       'deliveryStatus': const NumConverter().toJson(instance.deliveryStatus),
135
+      'ypdj': const NumConverter().toJson(instance.ypdj),
136
+      'jyzb': const StringConverter().toJson(instance.jyzb),
137
+      'qydq': const StringConverter().toJson(instance.qydq),
130 138
       'jhcysl': const NumConverter().toJson(instance.jhcysl),
131 139
       'zljysl': const NumConverter().toJson(instance.zljysl),
132 140
       'pzjyjg': const NumConverter().toJson(instance.pzjyjg),
@@ -179,6 +187,7 @@ Map<String, dynamic> _$SampleTaskItemToJson(SampleTaskItem instance) =>
179 187
       'createTime': const NumConverter().toJson(instance.createTime),
180 188
       'state': const NumConverter().toJson(instance.state),
181 189
       'name': const StringConverter().toJson(instance.name),
190
+      'dgryName': const StringConverter().toJson(instance.dgryName),
182 191
       'rwjssj': const StringConverter().toJson(instance.rwjssj),
183 192
       'bsjzsj': const StringConverter().toJson(instance.bsjzsj),
184 193
       'rwlx': const NumConverter().toJson(instance.rwlx),
@@ -207,6 +216,7 @@ Map<String, dynamic> _$SampleTaskItemToJson(SampleTaskItem instance) =>
207 216
       'zjswrlx': const NumConverter().toJson(instance.zjswrlx),
208 217
       'jyjgxxRespVOList': instance.jyjgxxRespVOList,
209 218
       'jypzStatus': const NumConverter().toJson(instance.jypzStatus),
219
+      'tqqk': const NumConverter().toJson(instance.tqqk),
210 220
     };
211 221
 
212 222
 EnterpriseItem _$EnterpriseItemFromJson(Map<String, dynamic> json) =>

+ 4 - 0
lib/network/api.dart

@@ -63,6 +63,10 @@ abstract class Api {
63 63
   @GET('/admin-api/zj/base-grain-information/lsxxfl')
64 64
   Future<ApiRsp<List<LsxxRsp>?>> lsxxList(@Query('zlmc') String zlmc);
65 65
 
66
+  /// 领取扦样任务单-收购
67
+  @GET('/admin-api/zj/code-sampling-task-details-sgjc/updateStatus')
68
+  Future<ApiRsp<num?>> receiveSampleTaskSgjc(@Query('id') num id);
69
+
66 70
   /// 省市县
67 71
   @GET('/admin-api/zj/base-administrative-division/list')
68 72
   Future<ApiRsp<List<DistrictRsp>?>> districtList(

+ 134 - 63
lib/page/sample_task/reap_sample_detail/reap_sample_basic_detail_page.dart

@@ -1,6 +1,12 @@
1
+import 'dart:async';
2
+import 'dart:convert';
3
+import 'package:amap_flutter_location/amap_location_option.dart';
1 4
 import 'package:flutter/material.dart';
2
-import 'package:flutter/services.dart';
5
+import 'package:lszlgl/main.dart';
3 6
 import 'package:lszlgl/utils/input_formatter.dart';
7
+import 'package:lszlgl/utils/location_utils.dart';
8
+import 'package:lszlgl/widget/button.dart';
9
+import 'package:lszlgl/widget/menu_dialog.dart';
4 10
 import '../../../base/base_lifecycle_state.dart';
5 11
 import '../../../model/rsp/dict_rsp.dart';
6 12
 import '../../../model/rsp/sample_task_rsp.dart';
@@ -23,14 +29,12 @@ class ReapSampleBasicDetailPage extends StatefulWidget {
23 29
   State<ReapSampleBasicDetailPage> createState() => _ReapSampleBasicDetailPageState();
24 30
 }
25 31
 
26
-class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasicDetailPage>
27
-    with AutomaticKeepAliveClientMixin {
32
+class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasicDetailPage> with AutomaticKeepAliveClientMixin {
28 33
   SampleTaskItem? data;
29 34
   late bool isDetail;
30 35
 
31
-  final shengList = <CardMenuData>[].notifier<List<CardMenuData>>();
32
-  final shiList = <CardMenuData>[].notifier<List<CardMenuData>>();
33
-  final quList = <CardMenuData>[].notifier<List<CardMenuData>>();
36
+  StreamSubscription? locationListener;
37
+
34 38
   final xianList = <CardMenuData>[].notifier<List<CardMenuData>>();
35 39
   final cunList = <CardMenuData>[].notifier<List<CardMenuData>>();
36 40
   final sheng = null.notifier<CardMenuData?>();
@@ -42,25 +46,26 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
42 46
   final trxxList = <CardMenuData>[].notifier<List<CardMenuData>>();
43 47
   final trxx = null.notifier<CardMenuData?>();
44 48
 
49
+  final qyddjwd = null.notifier<String?>();
50
+
45 51
   /// 获取行政区划列表
46
-  Future<void> getDistrictList(num level, {num? id}) async {
52
+  Future<List<CardMenuData>?> getDistrictList(num level, {num? id}) async {
47 53
     MyNavigator.showLoading();
48 54
     try {
49 55
       var rsp = await MyApi.get().districtList(level, id: id);
50 56
       List<CardMenuData> list = (rsp.data ?? []).map((e) => CardMenuData(e.uname, e.id)).toList();
51
-      if (level == 1) {
52
-        shengList.value = list;
53
-      } else if (level == 2) {
54
-        shiList.value = list;
55
-      } else if (level == 3) {
56
-        quList.value = list;
57
-      } else if (level == 4) {
57
+      if (level == 4) {
58 58
         xianList.value = list;
59 59
       } else if (level == 5) {
60 60
         cunList.value = list;
61 61
       }
62
-    } catch (e) {}
62
+      MyNavigator.dismissLoading();
63
+      return list;
64
+    } catch (e) {
65
+      logger.e(e);
66
+    }
63 67
     MyNavigator.dismissLoading();
68
+    return null;
64 69
   }
65 70
 
66 71
   /// 获取土壤信息列表
@@ -78,50 +83,16 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
78 83
         }
79 84
       }
80 85
       trxxList.value = list;
81
-    } catch (e) {}
86
+    } catch (e) {
87
+      logger.e(e);
88
+    }
82 89
     MyNavigator.dismissLoading();
83 90
   }
84 91
 
85 92
   /// 选中行政区划
86 93
   void onSelectXzqh(ValueNotifier<CardMenuData?> selNotifier, CardMenuData selData) {
87 94
     num level = 100;
88
-    if (selNotifier == sheng) {
89
-      level = 1;
90
-      data?.shengXzqh = selData.value;
91
-      data?.shiXzqh = null;
92
-      data?.quXzqh = null;
93
-      data?.xiangXzqh = null;
94
-      data?.cunXzqh = null;
95
-      shi.value = null;
96
-      qu.value = null;
97
-      xian.value = null;
98
-      cun.value = null;
99
-      shiList.value = [];
100
-      quList.value = [];
101
-      xianList.value = [];
102
-      cunList.value = [];
103
-    } else if (selNotifier == shi) {
104
-      level = 2;
105
-      data?.shiXzqh = selData.value;
106
-      data?.quXzqh = null;
107
-      data?.xiangXzqh = null;
108
-      data?.cunXzqh = null;
109
-      qu.value = null;
110
-      xian.value = null;
111
-      cun.value = null;
112
-      quList.value = [];
113
-      xianList.value = [];
114
-      cunList.value = [];
115
-    } else if (selNotifier == qu) {
116
-      level = 3;
117
-      data?.quXzqh = selData.value;
118
-      data?.xiangXzqh = null;
119
-      data?.cunXzqh = null;
120
-      xian.value = null;
121
-      cun.value = null;
122
-      xianList.value = [];
123
-      cunList.value = [];
124
-    } else if (selNotifier == xian) {
95
+    if (selNotifier == xian) {
125 96
       level = 4;
126 97
       data?.xiangXzqh = selData.value;
127 98
       data?.cunXzqh = null;
@@ -139,8 +110,6 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
139 110
 
140 111
   /// 获取编辑数据
141 112
   void getEditData() {
142
-    // 省
143
-    getDistrictList(1);
144 113
     // 市
145 114
     if (data?.shengXzqh != null) {
146 115
       getDistrictList(2, id: data?.shengXzqh);
@@ -159,6 +128,90 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
159 128
     }
160 129
   }
161 130
 
131
+  void getLocation() async {
132
+    bool granted = await LocationUtils.requestLocationPermission();
133
+    if (!granted) {
134
+      MyNavigator.showToast('请打开APP的定位权限');
135
+      return;
136
+    }
137
+    LocationUtils.setLocationOption(AMapLocationOption(onceLocation: true));
138
+    LocationUtils.startLocation();
139
+  }
140
+
141
+  void initLocation() {
142
+    locationListener = LocationUtils.onLocationChanged().listen((value) async {
143
+      logger.d('LocationChanged:${jsonEncode(value)}');
144
+      var lon = (value['longitude'] as double).toStringAsFixed(2);
145
+      var lat = (value['latitude'] as double).toStringAsFixed(2);
146
+      var province = value['province'];
147
+      var city = value['city'] == province ? '市辖区' : value['city'];
148
+      var district = value['district'];
149
+      // if (value['province'] != data?.sheng) {
150
+      //   MyNavigator.showToast('扦样地点有误,请检查扦样人员所在地点。');
151
+      //   return;
152
+      // }
153
+      // 市、区、县、村数据清除
154
+      data?.shiXzqh = null;
155
+      shi.value = null;
156
+
157
+      data?.quXzqh = null;
158
+      qu.value = null;
159
+
160
+      data?.xiangXzqh = null;
161
+      xian.value = null;
162
+      xianList.value = [];
163
+
164
+      data?.cunXzqh = null;
165
+      cun.value = null;
166
+      cunList.value = [];
167
+
168
+      bool stepSuccess = false;
169
+      // 省
170
+      var provinceList = await getDistrictList(1);
171
+      if (provinceList == null) return;
172
+      for (CardMenuData item in provinceList) {
173
+        if (item.name == province) {
174
+          data?.shengXzqh = item.value;
175
+          sheng.value = item;
176
+          stepSuccess = true;
177
+          break;
178
+        }
179
+      }
180
+      if (!stepSuccess) return;
181
+      stepSuccess = false;
182
+      // 市
183
+      var cityList = await getDistrictList(2, id: data?.shengXzqh);
184
+      if (cityList == null) return;
185
+      for (CardMenuData item in cityList) {
186
+        if (item.name == city) {
187
+          data?.shiXzqh = item.value;
188
+          shi.value = item;
189
+          stepSuccess = true;
190
+          break;
191
+        }
192
+      }
193
+      if (!stepSuccess) return;
194
+      stepSuccess = false;
195
+      // 区
196
+      var districtList = await getDistrictList(3, id: data?.shiXzqh);
197
+      if (districtList == null) return;
198
+      for (CardMenuData item in districtList) {
199
+        if (item.name == district) {
200
+          data?.quXzqh = item.value;
201
+          qu.value = item;
202
+          getDistrictList(4, id: item.value);
203
+          break;
204
+        }
205
+      }
206
+      String jwd = '$lon,$lat';
207
+      qyddjwd.value = jwd;
208
+      data?.qyddjwd = jwd;
209
+    });
210
+    if (!isDetail && data?.shiXzqh == null) {
211
+      getLocation();
212
+    }
213
+  }
214
+
162 215
   @override
163 216
   bool get wantKeepAlive => true;
164 217
 
@@ -174,13 +227,22 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
174 227
     if (data?.qu != null) qu.value = CardMenuData(data?.qu, data?.quXzqh);
175 228
     if (data?.xian != null) xian.value = CardMenuData(data?.xian, data?.xiangXzqh);
176 229
     if (data?.cun != null) cun.value = CardMenuData(data?.cun, data?.cunXzqh);
230
+    qyddjwd.value = data?.qyddjwd;
177 231
     // 编辑数据
178 232
     if (!isDetail) {
179 233
       getEditData();
234
+      initLocation();
180 235
     }
181 236
   }
182 237
 
183 238
   @override
239
+  void onDestroy() {
240
+    LocationUtils.stopLocation();
241
+    LocationUtils.destroy();
242
+    locationListener?.cancel();
243
+  }
244
+
245
+  @override
184 246
   Widget build(BuildContext context) {
185 247
     super.build(context);
186 248
     return SingleChildScrollView(child: buildList());
@@ -191,17 +253,26 @@ class _ReapSampleBasicDetailPageState extends BaseLifecycleState<ReapSampleBasic
191 253
       children: [
192 254
         CardItemWidget('扦样任务单号', rightText: data?.qyrwdh, bottomLine: true),
193 255
         CardItemWidget('扦样单位', rightText: data?.dwmc, bottomLine: true),
194
-        CardItemWidget('扦样人员', rightText: data?.name, bottomLine: true),
256
+        CardItemWidget(
257
+          '扦样人员',
258
+          rightText: data?.name,
259
+          bottomLine: true,
260
+          trailing: isDetail ? null : Image.asset(imgItemArrowDown, width: 20),
261
+          onTap: isDetail ? null : () => showMenuDialog(isScrollControlled: true),
262
+        ),
195 263
         CardItemWidget('监测类别', rightText: DictService.getLabel(DictType.jclb, value: data?.jclb), bottomLine: true),
196
-        CardWidgets.buildMenu(isDetail, '省份', shengList, sheng, onSelectXzqh),
197
-        CardWidgets.buildMenu(isDetail, '市区', shiList, shi, onSelectXzqh),
198
-        CardWidgets.buildMenu(isDetail, '区县', quList, qu, onSelectXzqh),
264
+        sheng.builder((v) => CardItemWidget('省份', rightText: v?.name, bottomLine: true)),
265
+        shi.builder((v) => CardItemWidget('市区', rightText: v?.name, bottomLine: true)),
266
+        qu.builder((v) => CardItemWidget('区县', rightText: v?.name, bottomLine: true)),
199 267
         CardWidgets.buildMenu(isDetail, '乡镇', xianList, xian, onSelectXzqh),
200 268
         CardWidgets.buildMenu(isDetail, '村', cunList, cun, onSelectXzqh),
201
-        CardItemWidget(
202
-          '扦样地点经纬度',
203
-          rightText: data?.qyddjwd,
204
-          bottomLine: true,
269
+        qyddjwd.builder(
270
+          (v) => CardItemWidget(
271
+            '扦样地点经纬度',
272
+            rightText: v,
273
+            bottomLine: true,
274
+            trailing: isDetail ? null : MyButton('获取经纬度', onTap: getLocation, alignment: null),
275
+          ),
205 276
         ),
206 277
         CardWidgets.buildEdit(
207 278
           isDetail,

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

@@ -22,8 +22,7 @@ class ReapSampleDisasterDetailPage extends StatefulWidget {
22 22
   State<ReapSampleDisasterDetailPage> createState() => _ReapSampleDisasterDetailPageState();
23 23
 }
24 24
 
25
-class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDisasterDetailPage>
26
-    with AutomaticKeepAliveClientMixin {
25
+class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDisasterDetailPage> with AutomaticKeepAliveClientMixin {
27 26
   SampleTaskItem? data;
28 27
   late bool isDetail;
29 28
 
@@ -88,37 +87,37 @@ class _ReapSampleDisasterDetailPageState extends BaseLifecycleState<ReapSampleDi
88 87
     isDetail = widget.detail;
89 88
 
90 89
     // 工厂排污
91
-    setBoolMenuSelNotifier(zztdzwsfygcpw, data?.zztdzwsfygcpw);
90
+    setBoolMenuSelNotifier(zztdzwsfygcpw, data?.zztdzwsfygcpw ?? false);
92 91
 
93 92
     // 工厂排污类型
94 93
     setDictMenuNotifier(gcpwlxList, gcpwlx, DictType.gcpwlx, data?.gcpwlx);
95 94
 
96 95
     // 水源、土壤污染
97
-    setBoolMenuSelNotifier(zztdzwsysytrsfywr, data?.zztdzwsysytrsfywr);
96
+    setBoolMenuSelNotifier(zztdzwsysytrsfywr, data?.zztdzwsysytrsfywr ?? false);
98 97
 
99 98
     // 污染物类型
100 99
     setDictMenuNotifier(wrwlxList, wrwlx, DictType.wrwlx, data?.wrwlx);
101 100
 
102 101
     // 病虫害
103
-    setBoolMenuSelNotifier(scgczsffsbjyzdbch, data?.scgczsffsbjyzdbch);
102
+    setBoolMenuSelNotifier(scgczsffsbjyzdbch, data?.scgczsffsbjyzdbch ?? false);
104 103
 
105 104
     // 病虫害类型
106 105
     setDictMenuNotifier(bchlxList, bchlx, DictType.bchlx, data?.bchlx);
107 106
 
108 107
     // 收货期间是否发生连阴雨天气
109
-    setNumBoolMenuSelNotifier(shqjsffslyytq, data?.shqjsffslyytq);
108
+    setNumBoolMenuSelNotifier(shqjsffslyytq, data?.shqjsffslyytq ?? 1);
110 109
 
111 110
     // 收货粮食水分是否偏高
112
-    setNumBoolMenuSelNotifier(shlssfsfpg, data?.shlssfsfpg);
111
+    setNumBoolMenuSelNotifier(shlssfsfpg, data?.shlssfsfpg ?? 1);
113 112
 
114 113
     // 近年来本地是否发生真菌毒素污染
115
-    setNumBoolMenuSelNotifier(jnlbdsffszmwr, data?.jnlbdsffszmwr);
114
+    setNumBoolMenuSelNotifier(jnlbdsffszmwr, data?.jnlbdsffszmwr ?? 1);
116 115
 
117 116
     // 真菌毒素污染类型
118 117
     setDictMenuNotifier(zjdswrlxList, zjdswrlx, DictType.zjdswrlx, data?.zjdswrlx);
119 118
 
120 119
     // 近年来本地是否发生重金属污染
121
-    setNumBoolMenuSelNotifier(jnlbdsfzjswr, data?.jnlbdsfzjswr);
120
+    setNumBoolMenuSelNotifier(jnlbdsfzjswr, data?.jnlbdsfzjswr ?? 1);
122 121
 
123 122
     // 重金属污染类型
124 123
     setDictMenuNotifier(zjswrlxList, zjswrlx, DictType.zjswrlx, data?.zjswrlx);

+ 4 - 1
lib/page/sample_task/reap_sample_detail/reap_sample_task_page.dart

@@ -10,6 +10,7 @@ import 'package:lszlgl/page/sample_task/reap_sample_detail/reap_sample_org_detai
10 10
 import 'package:lszlgl/page/sample_task/reap_sample_detail/reap_sample_variety_detail_page.dart';
11 11
 
12 12
 import '../../../base/base_vm.dart';
13
+import '../../../main.dart';
13 14
 import '../../../network/my_api.dart';
14 15
 import '../../../widget/button.dart';
15 16
 import '../../../widget/page_widget.dart';
@@ -85,7 +86,9 @@ class _ReapSampleTaskPageState extends BaseLifecycleState<ReapSampleTaskPage> wi
85 86
       } else {
86 87
         MyNavigator.showToast('提交失败');
87 88
       }
88
-    } catch (e) {}
89
+    } catch (e) {
90
+      logger.e(e);
91
+    }
89 92
     MyNavigator.dismissLoading();
90 93
   }
91 94
 

+ 28 - 15
lib/page/sample_task/reap_sample_detail/reap_sample_variety_detail_page.dart

@@ -48,6 +48,10 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
48 48
   /// 扦样时间
49 49
   final qysj = null.notifier<String?>();
50 50
 
51
+  /// 天气情况
52
+  final tqqkList = <CardMenuData>[].notifier<List<CardMenuData>>();
53
+  final tqqk = null.notifier<CardMenuData?>();
54
+
51 55
   /// 获取粮食信息列表
52 56
   Future<void> getLsxxList() async {
53 57
     MyNavigator.showLoading();
@@ -102,7 +106,22 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
102 106
     // 优质品种菜单数据
103 107
     getYxpzList();
104 108
     shsj.value = data?.shsj;
109
+
110
+    // 扦样时间
105 111
     qysj.value = data?.qysj;
112
+    if (!isDetail && data?.qysj == null) {
113
+      // 默认当前时间
114
+      var date = DateTimeUtils.yyyymmdd(date: DateTime.now());
115
+      qysj.value = date;
116
+      data?.qysj = date;
117
+    }
118
+
119
+    // 天气情况
120
+    tqqkList.value = (DictService.getDictList(DictType.tqqk) ?? []).map((e) {
121
+      var menu = CardMenuData(e.label, num.parse(e.value ?? '0'));
122
+      if (menu.value == data?.tqqk) tqqk.value = menu;
123
+      return menu;
124
+    }).toList();
106 125
   }
107 126
 
108 127
   @override
@@ -118,9 +137,10 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
118 137
     return Column(
119 138
       children: [
120 139
         CardItemWidget('采样品种', rightText: data?.cypzName, bottomLine: true),
140
+        CardItemWidget('样品等级', rightText: DictService.getLabel(DictType.ypdj, value: data?.ypdj), bottomLine: true),
121 141
         CardWidgets.buildMenu(
122 142
           isDetail,
123
-          '采样品种',
143
+          '粮食品类',
124 144
           lspzList,
125 145
           lspz,
126 146
           (_, sel) => data?.lspz = sel.value?.toString(),
@@ -174,21 +194,14 @@ class _ReapSampleVarietyDetailPageState extends BaseLifecycleState<ReapSampleVar
174 194
           formatters: [XNumberTextInputFormatter()],
175 195
           onChanged: (value) => data?.qydbsl = value.isEmpty ? null : num.parse(value),
176 196
         ),
177
-        CardWidgets.buildEdit(
178
-          isDetail,
179
-          '温度(℃)',
180
-          data?.wendu?.toString(),
181
-          inputType: const TextInputType.numberWithOptions(decimal: true),
182
-          formatters: [XNumberTextInputFormatter()],
183
-          onChanged: (value) => data?.wendu = value.isEmpty ? null : num.parse(value),
184
-        ),
185
-        CardWidgets.buildEdit(
197
+        CardWidgets.buildMenu(
186 198
           isDetail,
187
-          '湿度(%)',
188
-          data?.shidu?.toString(),
189
-          inputType: const TextInputType.numberWithOptions(decimal: true),
190
-          formatters: [XNumberTextInputFormatter()],
191
-          onChanged: (value) => data?.shidu = value.isEmpty ? null : num.parse(value),
199
+          '天气情况',
200
+          tqqkList,
201
+          tqqk,
202
+          (_, sel) {
203
+            // data?.tqqk = sel.value?.toString();
204
+          },
192 205
         ),
193 206
       ],
194 207
     );

+ 2 - 2
lib/page/sample_task/sample_list_vm.dart

@@ -12,8 +12,8 @@ class SampleListVM extends BaseVM {
12 12
   SampleListVM() {
13 13
     _uncompletedCtrl = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true);
14 14
     _completeCtrl = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true);
15
-    uncompletedReq = SampleTaskListReq(deliveryStatus: 0);
16
-    completeReq = SampleTaskListReq(deliveryStatus: 1);
15
+    uncompletedReq = SampleTaskListReq(deliveryStatus: 1);
16
+    completeReq = SampleTaskListReq(deliveryStatus: 2);
17 17
   }
18 18
 
19 19
   /// 刷新控制

+ 49 - 22
lib/page/sample_task/sample_task_list_page.dart

@@ -5,6 +5,7 @@ import 'package:lszlgl/base/base_state.dart';
5 5
 import 'package:lszlgl/base/base_vm.dart';
6 6
 import 'package:lszlgl/config/colors.dart';
7 7
 import 'package:lszlgl/model/rsp/sample_task_rsp.dart';
8
+import 'package:lszlgl/network/my_api.dart';
8 9
 import 'package:lszlgl/page/sample_task/sample_list_vm.dart';
9 10
 import 'package:lszlgl/page/sample_task/sample_task_list_tab_page.dart';
10 11
 import 'package:lszlgl/service/dict_service.dart';
@@ -50,6 +51,21 @@ class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage> wi
50 51
     }
51 52
   }
52 53
 
54
+  /// 领取扦样任务
55
+  void receiveTask(SampleTaskItem data) async {
56
+    MyNavigator.showLoading();
57
+    try {
58
+      var rsp = await MyApi.get().receiveSampleTaskSgjc(data.id ?? 0);
59
+      MyNavigator.dismissLoading();
60
+      if (rsp.data == 1) {
61
+        MyNavigator.showToast('领取成功');
62
+        vm.refreshAll();
63
+      }
64
+    } catch (e) {
65
+      MyNavigator.dismissLoading();
66
+    }
67
+  }
68
+
53 69
   @override
54 70
   void onInit() {
55 71
     vm = Inject.get<SampleListVM>()!;
@@ -185,23 +201,22 @@ class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage> wi
185 201
   }
186 202
 
187 203
   Widget buildGrid(SampleTaskItem item) {
188
-    List<Map<String, String>> infoList;
189
-    var jclb = DictService.getDict(DictType.jclb, value: item.jclb)?.label;
190
-    if (widget.complete) {
204
+    List<Map<String, String?>> infoList;
205
+    if (!widget.complete) {
191 206
       infoList = [
192
-        {'采样品种': item.cypzName ?? ''},
193
-        {'粮食品类': item.lspz ?? ''},
194
-        {'监测类别': jclb ?? ''},
195
-        {'扦样数量(kg)': '${item.qysl ?? ''}'},
196
-        {'扦样人员': item.name ?? ''},
197
-        {'扦样时间': item.qysj ?? ''},
207
+        {'采样品种': item.cypzName},
208
+        {'检验指标': item.jyzb},
209
+        {'样品等级': DictService.getDict(DictType.ypdj, value: item.ypdj)?.label},
210
+        {'扦样地区': item.qydq},
198 211
       ];
199 212
     } else {
200 213
       infoList = [
201
-        {'采样品种': item.cypzName ?? ''},
202
-        {'报送截止时间': item.bsjzsj ?? ''},
203
-        {'监测类别': jclb ?? ''},
204
-        {'扦样人员': item.name ?? ''},
214
+        {'采样品种': item.cypzName},
215
+        {'检验指标': item.jyzb},
216
+        {'样品等级': DictService.getDict(DictType.ypdj, value: item.ypdj)?.label},
217
+        {'扦样地区': item.qydq},
218
+        {'扦样人员': item.qyryName},
219
+        {'扦样时间': item.qysj},
205 220
       ];
206 221
     }
207 222
     return Padding(
@@ -214,7 +229,7 @@ class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage> wi
214 229
             return SizedBox(
215 230
               width: constraints.maxWidth / 2 - 2,
216 231
               child: Text(
217
-                '${item.keys.first}:${item.values.first}',
232
+                '${item.keys.first}:${item.values.first ?? ''}',
218 233
                 style: const TextStyle(fontSize: 13, color: MyColor.c_666666),
219 234
               ),
220 235
             );
@@ -226,6 +241,24 @@ class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage> wi
226 241
 
227 242
   Widget buildBottom(SampleTaskItem item) {
228 243
     if (widget.complete) return const SizedBox.shrink();
244
+    Widget button;
245
+    if (widget.type == StepType.reap && item.deliveryStatus == 0) {
246
+      button = MyButton(
247
+        '任务领取',
248
+        onTap: () => receiveTask(item),
249
+        fountSize: 13,
250
+        alignment: null,
251
+        padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
252
+      );
253
+    } else {
254
+      button = MyButton(
255
+        '开始扦样',
256
+        onTap: () => startTaskDetail(false, item),
257
+        fountSize: 13,
258
+        alignment: null,
259
+        padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
260
+      );
261
+    }
229 262
     return Column(
230 263
       children: [
231 264
         buildDivider(),
@@ -234,18 +267,12 @@ class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage> wi
234 267
             const SizedBox(width: 8),
235 268
             Expanded(
236 269
               child: Text(
237
-                '[${item.qyryName ?? ''}]创建于${DateTimeUtils.yyyymmdd(timestamp: item.createTime) ?? ''}',
270
+                '扦样单据创建于${DateTimeUtils.yyyymmdd(timestamp: item.createTime) ?? ''}',
238 271
                 style: const TextStyle(fontSize: 13, color: MyColor.c_666666),
239 272
               ),
240 273
             ),
241 274
             const SizedBox(width: 4),
242
-            MyButton(
243
-              '开始扦样',
244
-              onTap: () => startTaskDetail(false, item),
245
-              fountSize: 13,
246
-              alignment: null,
247
-              padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
248
-            ),
275
+            button,
249 276
             const SizedBox(width: 12),
250 277
           ],
251 278
         ),

+ 1 - 1
lib/page/sample_task/sample_task_list_tab_page.dart

@@ -58,7 +58,7 @@ class _SampleTaskListTabPageState extends BaseLifecycleState<SampleTaskListTabPa
58 58
     tabCtrl = TabController(length: 2, vsync: this);
59 59
     pageCtrl = PageController();
60 60
 
61
-    tabTextList = ['待扦样', '扦样完成'];
61
+    tabTextList = ['未扦样', '已扦样'];
62 62
     pageList = [
63 63
       SampleTaskListPage(type: widget.args.type),
64 64
       SampleTaskListPage(type: widget.args.type, complete: true),

+ 1 - 1
lib/router/my_router.dart

@@ -80,7 +80,7 @@ class MyRouter {
80 80
   }
81 81
 
82 82
   /// 收获扦样任务
83
-  static Future<dynamic> startReapSampleTask({ReapSampleTaskPageArgs? args}) {
83
+  static Future<dynamic> startReapSampleTask({ReapSampleTaskPageArgs? args}) async {
84 84
     return MyNavigator.push(rReapSampleTaskPage, args: args ?? ReapSampleTaskPageArgs());
85 85
   }
86 86
 

+ 4 - 0
lib/service/dict_service.dart

@@ -1,8 +1,12 @@
1 1
 import 'package:lszlgl/model/rsp/dict_rsp.dart';
2 2
 
3 3
 enum DictType {
4
+  // 天气情况
5
+  tqqk('tqqk'),
4 6
   // 监测类别
5 7
   jclb('jclb'),
8
+  // 样品等级
9
+  ypdj('ypdj'),
6 10
   // 小麦品种优质类型
7 11
   xmpzyzlx('xmpzyzlx'),
8 12
   // 玉米品种优质类型

+ 131 - 0
lib/utils/location_utils.dart

@@ -0,0 +1,131 @@
1
+import 'package:amap_flutter_location/amap_flutter_location.dart';
2
+import 'package:amap_flutter_location/amap_location_option.dart';
3
+import 'package:permission_handler/permission_handler.dart';
4
+
5
+class LocationUtils {
6
+  LocationUtils._();
7
+
8
+  ///设置Android和iOS的apikey,建议在weigdet初始化时设置<br>
9
+  ///apiKey的申请请参考高德开放平台官网<br>
10
+  ///Android端: https://lbs.amap.com/api/android-location-sdk/guide/create-project/get-key<br>
11
+  ///iOS端: https://lbs.amap.com/api/ios-location-sdk/guide/create-project/get-key<br>
12
+  ///[androidKey] Android平台的key<br>
13
+  ///[iosKey] ios平台的key<br>
14
+  static void setApiKey(String androidKey, String iosKey) {
15
+    AMapFlutterLocation.setApiKey(androidKey, iosKey);
16
+  }
17
+
18
+  /// 设置是否已经包含高德隐私政策并弹窗展示显示用户查看,如果未包含或者没有弹窗展示,高德定位SDK将不会工作<br>
19
+  /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy<br>
20
+  /// <b>必须保证在调用定位功能之前调用, 建议首次启动App时弹出《隐私政策》并取得用户同意</b><br>
21
+  /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy
22
+  /// [hasContains] 隐私声明中是否包含高德隐私政策说明<br>
23
+  /// [hasShow] 隐私权政策是否弹窗展示告知用户<br>
24
+  static void updatePrivacyShow(bool hasContains, bool hasShow) {
25
+    AMapFlutterLocation.updatePrivacyShow(hasContains, hasShow);
26
+  }
27
+
28
+  /// 设置是否已经取得用户同意,如果未取得用户同意,高德定位SDK将不会工作<br>
29
+  /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy<br>
30
+  /// <b>必须保证在调用定位功能之前调用, 建议首次启动App时弹出《隐私政策》并取得用户同意</b><br>
31
+  /// [hasAgree] 隐私权政策是否已经取得用户同意<br>
32
+  static void updatePrivacyAgree(bool hasAgree) {
33
+    AMapFlutterLocation.updatePrivacyAgree(hasAgree);
34
+  }
35
+
36
+  static AMapFlutterLocation? _location;
37
+
38
+  static AMapFlutterLocation get location => _location ??= AMapFlutterLocation();
39
+
40
+  /// 设置定位参数
41
+  static AMapFlutterLocation setLocationOption(AMapLocationOption option) {
42
+    location.setLocationOption(option);
43
+    return location;
44
+  }
45
+
46
+  ///开始定位
47
+  static AMapFlutterLocation startLocation() {
48
+    location.startLocation();
49
+    return location;
50
+  }
51
+
52
+  ///停止定位
53
+  static AMapFlutterLocation stopLocation() {
54
+    location.stopLocation();
55
+    return location;
56
+  }
57
+
58
+  ///销毁定位
59
+  static void destroy() {
60
+    location.destroy();
61
+    _location = null;
62
+  }
63
+
64
+  ///定位结果回调
65
+  ///
66
+  ///定位结果以map的形式透出,其中包含的key已经含义如下:
67
+  ///
68
+  /// `callbackTime`:回调时间,格式为"yyyy-MM-dd HH:mm:ss"
69
+  ///
70
+  /// `locationTime`:定位时间, 格式为"yyyy-MM-dd HH:mm:ss"
71
+  ///
72
+  /// `locationType`:  定位类型, 具体类型可以参考https://lbs.amap.com/api/android-location-sdk/guide/utilities/location-type
73
+  ///
74
+  /// `latitude`:纬度
75
+  ///
76
+  /// `longitude`:精度
77
+  ///
78
+  /// `accuracy`:精确度
79
+  ///
80
+  /// `altitude`:海拔, android上只有locationType==1时才会有值
81
+  ///
82
+  /// `bearing`: 角度,android上只有locationType==1时才会有值
83
+  ///
84
+  /// `speed`:速度, android上只有locationType==1时才会有值
85
+  ///
86
+  /// `country`: 国家,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
87
+  ///
88
+  /// `province`: 省,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
89
+  ///
90
+  /// `city`: 城市,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
91
+  ///
92
+  /// `district`: 城镇(区),android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
93
+  ///
94
+  /// `street`: 街道,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
95
+  ///
96
+  /// `streetNumber`: 门牌号,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
97
+  ///
98
+  /// `cityCode`: 城市编码,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
99
+  ///
100
+  /// `adCode`: 区域编码, android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
101
+  ///
102
+  /// `address`: 地址信息, android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
103
+  ///
104
+  /// `description`: 位置语义, android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
105
+  ///
106
+  /// `errorCode`: 错误码,当定位失败时才会返回对应的错误码, 具体错误请参考:https://lbs.amap.com/api/android-location-sdk/guide/utilities/errorcode
107
+  ///
108
+  /// `errorInfo`: 错误信息, 当定位失败时才会返回
109
+  static Stream<Map<String, Object>> onLocationChanged() {
110
+    return location.onLocationChanged();
111
+  }
112
+
113
+  /// 申请定位权限
114
+  /// 授予定位权限返回true, 否则返回false
115
+  static Future<bool> requestLocationPermission() async {
116
+    //获取当前的权限
117
+    var status = await Permission.location.status;
118
+    if (status == PermissionStatus.granted) {
119
+      //已经授权
120
+      return true;
121
+    } else {
122
+      //未授权则发起一次申请
123
+      status = await Permission.location.request();
124
+      if (status == PermissionStatus.granted) {
125
+        return true;
126
+      } else {
127
+        return false;
128
+      }
129
+    }
130
+  }
131
+}

+ 6 - 8
lib/widget/card_item.dart

@@ -94,14 +94,12 @@ class CardItemWidget extends StatelessWidget {
94 94
   }
95 95
 
96 96
   Widget buildTitle() {
97
-    return Expanded(
98
-      child: Text(
99
-        title,
100
-        style: const TextStyle(
101
-          color: Color(0xFF333333),
102
-          fontSize: 14,
103
-          fontWeight: FontWeight.w500,
104
-        ),
97
+    return Text(
98
+      title,
99
+      style: const TextStyle(
100
+        color: Color(0xFF333333),
101
+        fontSize: 14,
102
+        fontWeight: FontWeight.w500,
105 103
       ),
106 104
     );
107 105
   }

+ 76 - 0
lib/widget/menu_dialog.dart

@@ -0,0 +1,76 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:lszlgl/base/base_lifecycle_state.dart';
3
+import 'package:lszlgl/widget/button.dart';
4
+
5
+Future<T?> showMenuDialog<T>({
6
+  BoxConstraints? constraints,
7
+  bool isScrollControlled = false,
8
+  bool isDismissible = true,
9
+  bool enableDrag = true,
10
+  bool useSafeArea = false,
11
+  RouteSettings? routeSettings,
12
+  AnimationController? animationCtrl,
13
+  Offset? anchorPoint,
14
+}) {
15
+  return showModalBottomSheet(
16
+    context: MyNavigator.navigator.currentState!.context,
17
+    constraints: constraints,
18
+    isScrollControlled: isScrollControlled,
19
+    isDismissible: isDismissible,
20
+    enableDrag: enableDrag,
21
+    showDragHandle: true,
22
+    useSafeArea: useSafeArea,
23
+    routeSettings: routeSettings,
24
+    transitionAnimationController: animationCtrl,
25
+    anchorPoint: anchorPoint,
26
+    builder: (_) => MenuDialog(),
27
+  );
28
+}
29
+
30
+class MenuDialog extends StatelessWidget {
31
+  const MenuDialog({super.key});
32
+
33
+  @override
34
+  Widget build(BuildContext context) {
35
+    return Column(
36
+      mainAxisSize: MainAxisSize.min,
37
+      crossAxisAlignment: CrossAxisAlignment.start,
38
+      children: [
39
+        buildTitle(),
40
+        buildButton(),
41
+      ],
42
+    );
43
+  }
44
+
45
+  Widget buildTitle() {
46
+    return const Padding(
47
+      padding: EdgeInsets.only(left: 24, top: 16, bottom: 8),
48
+      child: Text(
49
+        '请选择:',
50
+        style: TextStyle(color: Color(0xFF333333), fontSize: 16, fontWeight: FontWeight.w500),
51
+      ),
52
+    );
53
+  }
54
+
55
+  // Widget buildList() {
56
+  //   return ListView.builder(
57
+  //     padding: EdgeInsets.zero,
58
+  //     itemBuilder: (_, index) => buildItem(index),
59
+  //   );
60
+  // }
61
+  //
62
+  // Widget buildItem(int index) {}
63
+
64
+  Widget buildButton() {
65
+    return Padding(
66
+      padding: const EdgeInsets.only(bottom: 16, top: 8),
67
+      child: Row(
68
+        children: [
69
+          const Spacer(),
70
+          Expanded(flex: 2, child: MyButton('确认', onTap: () => MyNavigator.pop())),
71
+          const Spacer(),
72
+        ],
73
+      ),
74
+    );
75
+  }
76
+}

+ 8 - 0
pubspec.lock

@@ -9,6 +9,14 @@ packages:
9 9
       url: "https://pub.dev"
10 10
     source: hosted
11 11
     version: "64.0.0"
12
+  amap_flutter_location:
13
+    dependency: "direct main"
14
+    description:
15
+      name: amap_flutter_location
16
+      sha256: f35ff00e196d579608e0bc28ccbc1f6f53787644702f947de941f775769cc701
17
+      url: "https://pub.dev"
18
+    source: hosted
19
+    version: "3.0.0"
12 20
   analyzer:
13 21
     dependency: transitive
14 22
     description:

+ 3 - 1
pubspec.yaml

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
16 16
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 17
 # In Windows, build-name is used as the major, minor, and patch parts
18 18
 # of the product and file versions while build-number is used as the build suffix.
19
-version: 0.0.4+4
19
+version: 0.0.5+5
20 20
 
21 21
 environment:
22 22
   sdk: '>=3.1.5 <4.0.0'
@@ -78,6 +78,8 @@ dependencies:
78 78
   install_plugin: ^2.1.0
79 79
   # 权限管理
80 80
   permission_handler: ^11.3.0
81
+  # 高德定位
82
+  amap_flutter_location: ^3.0.0
81 83
 
82 84
 dev_dependencies:
83 85
   flutter_test: