sample_task_list_page.dart 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import 'package:dio/dio.dart';
  2. import 'package:easy_refresh/easy_refresh.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:lszlgl/base/base_lifecycle_state.dart';
  5. import 'package:lszlgl/base/base_state.dart';
  6. import 'package:lszlgl/base/base_vm.dart';
  7. import 'package:lszlgl/config/colors.dart';
  8. import 'package:lszlgl/main.dart';
  9. import 'package:lszlgl/model/rsp/sample_task_rsp.dart';
  10. import 'package:lszlgl/network/api.dart';
  11. import 'package:lszlgl/page/reap_step/sample_task_list_tab_page.dart';
  12. import 'package:lszlgl/service/dict_service.dart';
  13. import 'package:lszlgl/widget/button.dart';
  14. import 'package:lszlgl/widget/page_widget.dart';
  15. /// 扦样环节列表
  16. class SampleTaskListPage extends StatefulWidget {
  17. final StepType type;
  18. final bool complete;
  19. const SampleTaskListPage({
  20. super.key,
  21. required this.type,
  22. this.complete = false,
  23. });
  24. @override
  25. State<SampleTaskListPage> createState() => _SampleTaskListPageState();
  26. }
  27. class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage> with AutomaticKeepAliveClientMixin {
  28. late EasyRefreshController refreshCtrl;
  29. int pageNo = 1;
  30. int pageSize = 10;
  31. final pageState = DataStatusModel<List<SampleTaskItem>>().notifier<DataStatusModel<List<SampleTaskItem>>>();
  32. /// 详情
  33. void startDetail() {
  34. if (!widget.complete) return;
  35. switch (widget.type) {
  36. case StepType.reap:
  37. MyRouter.startReapSampleTask(detail: true);
  38. break;
  39. case StepType.stock:
  40. break;
  41. }
  42. }
  43. /// 扦样
  44. void startSample() {
  45. switch (widget.type) {
  46. case StepType.reap:
  47. MyRouter.startReapSampleTask();
  48. break;
  49. case StepType.stock:
  50. break;
  51. }
  52. }
  53. void getData({bool first = false, bool refresh = true}) async {
  54. if (refresh) {
  55. pageNo = 1;
  56. }
  57. try {
  58. var value = await Api().sampleTaskList(
  59. pageNo,
  60. pageSize,
  61. deliveryStatus: widget.complete ? 1 : 0,
  62. rwlx: widget.type.value,
  63. );
  64. List<SampleTaskItem> data = value.data?.list ?? [];
  65. var list = pageState.value.data ?? [];
  66. if (refresh) {
  67. list = data;
  68. refreshCtrl.finishRefresh(IndicatorResult.success, true);
  69. refreshCtrl.resetFooter();
  70. } else {
  71. list.addAll(data);
  72. }
  73. pageState.update(pageState.value.success(data: list));
  74. if (data.isEmpty) {
  75. refreshCtrl.finishLoad(IndicatorResult.noMore, true);
  76. } else {
  77. refreshCtrl.finishLoad(IndicatorResult.success, true);
  78. }
  79. pageNo++;
  80. } on DioException catch (err) {
  81. logger.e('${err.message}');
  82. if (pageNo == 1) pageState.update(pageState.value.error());
  83. if (refresh) {
  84. refreshCtrl.finishRefresh(IndicatorResult.fail, true);
  85. } else {
  86. refreshCtrl.finishLoad(IndicatorResult.fail, true);
  87. }
  88. }
  89. }
  90. @override
  91. void onInit() {
  92. refreshCtrl = EasyRefreshController(
  93. controlFinishRefresh: true,
  94. controlFinishLoad: true,
  95. );
  96. }
  97. @override
  98. void onFirstShow(Duration timeStamp) {
  99. refreshCtrl.callRefresh(overOffset: 200);
  100. }
  101. @override
  102. void onDestroy() {
  103. refreshCtrl.dispose();
  104. }
  105. @override
  106. Widget build(BuildContext context) {
  107. super.build(context);
  108. return Container(
  109. clipBehavior: Clip.hardEdge,
  110. decoration: const BoxDecoration(
  111. color: Colors.white,
  112. borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  113. ),
  114. alignment: Alignment.center,
  115. child: buildBody(),
  116. );
  117. }
  118. Widget buildBody() {
  119. return EasyRefresh.builder(
  120. controller: refreshCtrl,
  121. onRefresh: () => getData(refresh: true),
  122. onLoad: () => getData(refresh: false),
  123. childBuilder: (_, physics) => buildList(physics),
  124. );
  125. }
  126. Widget buildList(ScrollPhysics physics) {
  127. var sliver = pageState.builder((v) {
  128. var list = v.data;
  129. if (v.status == DataStatus.error) {
  130. // 加载失败
  131. return SliverToBoxAdapter(child: PageLoadingWidget.error(onTap: () => refreshCtrl.callRefresh()));
  132. } else if (list == null || list.isEmpty) {
  133. // 无数据
  134. return const SliverToBoxAdapter(child: PageLoadingWidget.empty());
  135. } else {
  136. return SliverList.builder(
  137. itemCount: list.length,
  138. itemBuilder: (_, index) => buildItem(index, list[index]),
  139. );
  140. }
  141. });
  142. return CustomScrollView(
  143. physics: physics,
  144. slivers: [sliver],
  145. );
  146. }
  147. Widget buildItem(int index, SampleTaskItem item) {
  148. return GestureDetector(
  149. behavior: HitTestBehavior.opaque,
  150. onTap: () => startDetail(),
  151. child: Container(
  152. margin: const EdgeInsets.only(left: 12, right: 12, top: 12),
  153. decoration: const BoxDecoration(
  154. color: Color(0xFFF5FFFD),
  155. borderRadius: BorderRadius.all(Radius.circular(8)),
  156. ),
  157. child: Column(
  158. crossAxisAlignment: CrossAxisAlignment.start,
  159. children: [
  160. const SizedBox(height: 15),
  161. buildTop(item.getDeliveryStatusText()),
  162. buildNumber(item.qyrwdh ?? ''),
  163. buildGrid(item),
  164. buildBottom(item),
  165. const SizedBox(height: 15),
  166. ],
  167. ),
  168. ),
  169. );
  170. }
  171. Widget buildTop(String state) {
  172. return Row(
  173. children: [
  174. buildVLine(),
  175. const Expanded(
  176. child: Text(
  177. '扦样任务单号',
  178. style: TextStyle(color: MyColor.c_333333, fontSize: 18, fontWeight: FontWeight.w500),
  179. ),
  180. ),
  181. buildState(state),
  182. ],
  183. );
  184. }
  185. Widget buildVLine() {
  186. return Container(
  187. width: 4,
  188. height: 20,
  189. margin: const EdgeInsets.only(right: 8),
  190. decoration: const BoxDecoration(
  191. color: MyColor.c_25A6EE,
  192. borderRadius: BorderRadius.all(Radius.circular(2)),
  193. ),
  194. );
  195. }
  196. Widget buildState(String state) {
  197. return Container(
  198. padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 4),
  199. decoration: BoxDecoration(
  200. color: MyColor.c_25A6EE.withOpacity(0.1),
  201. borderRadius: const BorderRadius.horizontal(left: Radius.circular(100)),
  202. ),
  203. child: Text(
  204. '状态:$state',
  205. style: const TextStyle(color: MyColor.c_1383C2, fontSize: 16),
  206. ),
  207. );
  208. }
  209. Widget buildNumber(String number) {
  210. return Padding(
  211. padding: const EdgeInsets.only(left: 12, top: 4, bottom: 6),
  212. child: Text(
  213. number,
  214. style: const TextStyle(color: MyColor.c_333333, fontSize: 18, fontWeight: FontWeight.w500),
  215. ),
  216. );
  217. }
  218. Widget buildGrid(SampleTaskItem item) {
  219. List<Map<String, String>> infoList;
  220. var jclb = DictService.get().getLabel(DictType.jclb, value: item.jclb)?.label;
  221. if (widget.complete) {
  222. infoList = [
  223. {'采样品种': item.cypzName ?? ''},
  224. {'粮食品类': item.lspz ?? ''},
  225. {'监测类别': jclb ?? ''},
  226. {'扦样数量(kg)': '${item.qysl ?? ''}'},
  227. {'扦样人员': item.name ?? ''},
  228. {'扦样时间': item.qysj ?? ''},
  229. ];
  230. } else {
  231. infoList = [
  232. {'采样品种': item.cypzName ?? ''},
  233. {'报送截止时间': item.bsjzsj ?? ''},
  234. {'监测类别': jclb ?? ''},
  235. {'扦样人员': item.name ?? ''},
  236. ];
  237. }
  238. return Padding(
  239. padding: const EdgeInsets.symmetric(horizontal: 12),
  240. child: LayoutBuilder(builder: (context, constraints) {
  241. return Wrap(
  242. spacing: 4,
  243. runSpacing: 4,
  244. children: infoList.map((item) {
  245. return SizedBox(
  246. width: constraints.maxWidth / 2 - 2,
  247. child: Text(
  248. '${item.keys.first}:${item.values.first}',
  249. style: const TextStyle(fontSize: 16, color: MyColor.c_666666),
  250. ),
  251. );
  252. }).toList(),
  253. );
  254. }),
  255. );
  256. }
  257. Widget buildBottom(SampleTaskItem item) {
  258. if (widget.complete) return const SizedBox.shrink();
  259. return Column(
  260. children: [
  261. buildDivider(),
  262. Row(
  263. children: [
  264. const SizedBox(width: 12),
  265. Expanded(
  266. child: Text(
  267. '[${item.qyryName ?? ''}]创建于${item.createTime ?? ''}',
  268. style: const TextStyle(fontSize: 16, color: MyColor.c_666666),
  269. ),
  270. ),
  271. const SizedBox(width: 4),
  272. MyButton(
  273. '开始扦样',
  274. onTap: () => startSample(),
  275. fountSize: 16,
  276. alignment: null,
  277. padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
  278. ),
  279. const SizedBox(width: 12),
  280. ],
  281. ),
  282. ],
  283. );
  284. }
  285. Widget buildDivider() {
  286. return Container(
  287. width: double.infinity,
  288. height: 1,
  289. color: MyColor.c_3BD2E5.withOpacity(0.15),
  290. margin: const EdgeInsets.symmetric(vertical: 10),
  291. );
  292. }
  293. @override
  294. bool get wantKeepAlive => true;
  295. }