sample_task_list_page.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. import 'package:easy_refresh/easy_refresh.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
  4. import 'package:lszlgl/base/base_lifecycle_state.dart';
  5. import 'package:lszlgl/base/base_vm.dart';
  6. import 'package:lszlgl/config/colors.dart';
  7. import 'package:lszlgl/model/rsp/sample_task_rsp.dart';
  8. import 'package:lszlgl/network/my_api.dart';
  9. import 'package:lszlgl/page/sample_task/sample_list_vm.dart';
  10. import 'package:lszlgl/page/sample_task/sample_task_list_tab_page.dart';
  11. import 'package:lszlgl/service/dict_service.dart';
  12. import 'package:lszlgl/utils/date_time_utils.dart';
  13. import 'package:lszlgl/utils/inject.dart';
  14. import 'package:lszlgl/widget/button.dart';
  15. import 'package:lszlgl/widget/page_widget.dart';
  16. import '../../main.dart';
  17. import 'reap_sample_detail/reap_sample_task_page.dart';
  18. import 'stock_sample_detail/stock_sample_task_page.dart';
  19. /// 扦样环节列表
  20. class SampleTaskListPage extends StatefulWidget {
  21. final StepType type;
  22. final int tabIndex;
  23. const SampleTaskListPage({
  24. super.key,
  25. required this.type,
  26. required this.tabIndex,
  27. });
  28. @override
  29. State<SampleTaskListPage> createState() => _SampleTaskListPageState();
  30. }
  31. class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage>
  32. with AutomaticKeepAliveClientMixin {
  33. late SampleListVM vm;
  34. /// 详情
  35. void startTaskDetail(bool detail, SampleTaskItem data, {num? deliveryStatus}) async {
  36. bool? success;
  37. switch (widget.type) {
  38. case StepType.reap:
  39. success = await MyRouter.startReapSampleTask(
  40. args: ReapSampleTaskPageArgs(detail: detail, id: data.id,deliveryStatus: deliveryStatus));
  41. break;
  42. case StepType.stock:
  43. success = await MyRouter.startStockSampleTask(
  44. args: StockSampleTaskPageArgs(detail: detail, id: data.id));
  45. break;
  46. }
  47. if (success ?? false) {
  48. vm.refreshAll();
  49. }
  50. }
  51. /// 领取扦样任务
  52. void receiveTask(SampleTaskItem data) async {
  53. var delete = await MyNavigator.showDialog(
  54. builder: (_) => AlertDialog(
  55. title: const Text('系统提示'),
  56. content: const Text('任务认领后不可撤销,请确认是否认领任务!'),
  57. actions: [
  58. MyButton(
  59. '领取',
  60. alignment: null,
  61. backgroundColor: const Color(0xFFCE615A),
  62. onTap: () => MyNavigator.dismiss(status: SmartStatus.dialog, result: true),
  63. ),
  64. MyButton('取消', alignment: null, onTap: () => MyNavigator.dismiss()),
  65. ],
  66. ),
  67. );
  68. if (!(delete ?? false)) return;
  69. MyNavigator.showLoading();
  70. try {
  71. var rsp = await MyApi.get().receiveSampleTaskSgjc(data.id ?? 0);
  72. MyNavigator.dismissLoading();
  73. if (rsp.data == 1) {
  74. MyNavigator.showToast('领取成功');
  75. vm.refreshAll();
  76. }
  77. } catch (e) {
  78. logger.e(e);
  79. MyNavigator.dismissLoading();
  80. }
  81. }
  82. @override
  83. void onInit() {
  84. vm = Inject.get<SampleListVM>()!;
  85. }
  86. @override
  87. void onFirstShow(Duration timeStamp) {
  88. vm.refresh(widget.tabIndex);
  89. }
  90. @override
  91. Widget build(BuildContext context) {
  92. super.build(context);
  93. return buildBody();
  94. // Container(
  95. // margin: const EdgeInsets.symmetric(horizontal: 8),
  96. // clipBehavior: Clip.hardEdge,
  97. // decoration: const BoxDecoration(
  98. // color: Colors.white,
  99. // borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
  100. // ),
  101. // alignment: Alignment.center,
  102. // child: buildBody(),
  103. // );
  104. }
  105. Widget buildBody() {
  106. return EasyRefresh.builder(
  107. controller: vm.ctrlList[widget.tabIndex],
  108. onRefresh: () => vm.getData(widget.tabIndex, refresh: true),
  109. onLoad: () => vm.getData(widget.tabIndex, refresh: false),
  110. childBuilder: (_, physics) => buildList(physics),
  111. );
  112. }
  113. Widget buildList(ScrollPhysics physics) {
  114. var sliver = vm.pageStateList[widget.tabIndex].builder((v) {
  115. var list = v.data;
  116. if (v.status == DataStatus.error) {
  117. // 加载失败
  118. return SliverToBoxAdapter(
  119. child: PageLoadingWidget.error(onTap: () => vm.refresh(widget.tabIndex)));
  120. } else if (list == null || list.isEmpty) {
  121. // 无数据
  122. return const SliverToBoxAdapter(child: PageLoadingWidget.empty());
  123. } else {
  124. return SliverList.builder(
  125. itemCount: list.length,
  126. itemBuilder: (_, index) => buildItem(index, list[index]),
  127. );
  128. }
  129. });
  130. return CustomScrollView(
  131. physics: physics,
  132. slivers: [sliver],
  133. );
  134. }
  135. Widget buildItem(int index, SampleTaskItem item) {
  136. return GestureDetector(
  137. behavior: HitTestBehavior.opaque,
  138. onTap: () {
  139. if (item.deliveryStatus != 2) return;
  140. startTaskDetail(true, item);
  141. },
  142. child: Container(
  143. clipBehavior: Clip.hardEdge,
  144. margin: const EdgeInsets.only(left: 8, right: 8, top: 12),
  145. decoration: const BoxDecoration(
  146. color: Color(0xFFF9FEFE),
  147. borderRadius: BorderRadius.all(Radius.circular(8)),
  148. ),
  149. child: Stack(
  150. alignment: Alignment.topRight,
  151. children: [
  152. Column(
  153. crossAxisAlignment: CrossAxisAlignment.start,
  154. children: [
  155. const SizedBox(height: 8),
  156. buildTop(),
  157. buildNumber(item.qyrwdh ?? ''),
  158. const Divider(thickness: 0.6, color: Color(0xFFEEEEEE), indent: 8),
  159. const SizedBox(height: 8),
  160. buildGrid(item),
  161. const SizedBox(height: 15),
  162. buildBottom(item),
  163. ],
  164. ),
  165. buildState(item.getDeliveryStatusText()),
  166. ],
  167. ),
  168. ),
  169. );
  170. }
  171. Widget buildTop() {
  172. return Row(
  173. children: [
  174. Image.asset(
  175. imgListTitleIcon,
  176. width: 4,
  177. height: 20,
  178. fit: BoxFit.fill,
  179. ),
  180. const SizedBox(width: 4),
  181. const Text(
  182. '扦样任务单号',
  183. style: TextStyle(color: MyColor.c_333333, fontSize: 15, fontWeight: FontWeight.w500),
  184. ),
  185. ],
  186. );
  187. }
  188. Widget buildState(String state) {
  189. String img = imgQYStateOff;
  190. Color color = const Color(0xFF149723);
  191. if (state != '已扦样') {
  192. img = imgQYStateOn;
  193. color = const Color(0xFFDEA70A);
  194. }
  195. return Container(
  196. padding: const EdgeInsets.fromLTRB(40, 4, 20, 16),
  197. decoration: BoxDecoration(image: DecorationImage(image: AssetImage(img), fit: BoxFit.fill)),
  198. child: Text(
  199. state,
  200. style: TextStyle(color: color, fontSize: 15),
  201. ),
  202. );
  203. }
  204. Widget buildNumber(String number) {
  205. return Padding(
  206. padding: const EdgeInsets.only(left: 8, bottom: 6),
  207. child: Text(
  208. number,
  209. style: const TextStyle(color: MyColor.c_333333, fontSize: 16, fontWeight: FontWeight.bold),
  210. ),
  211. );
  212. }
  213. Widget buildGrid(SampleTaskItem item) {
  214. List<ItemMsgModel> inList = [];
  215. if (item.deliveryStatus != 2) {
  216. // 未扦样
  217. inList.add(ItemMsgModel(tit: '采样品种', content: item.cypzName, img: imgIconCJPZ));
  218. if (item.jyzb != null) {
  219. inList.add(ItemMsgModel(tit: '检验指标', content: item.jyzb, img: imgIconJYZB));
  220. }
  221. if (item.ypdj != null) {
  222. inList.add(ItemMsgModel(
  223. tit: '样品层级',
  224. content: DictService.getDict(DictType.ypdj, value: item.ypdj)?.label,
  225. img: imgIconYPDJ));
  226. }
  227. inList.add(ItemMsgModel(tit: '扦样地区', content: item.qydq, img: imgIconQYDQ));
  228. } else {
  229. // 已扦样
  230. inList.add(ItemMsgModel(tit: '采样品种', content: item.cypzName, img: imgIconCJPZ));
  231. if (item.jyzb != null) {
  232. inList.add(ItemMsgModel(tit: '检验指标', content: item.jyzb, img: imgIconJYZB));
  233. }
  234. if (item.ypdj != null) {
  235. inList.add(ItemMsgModel(
  236. tit: '样品层级',
  237. content: DictService.getDict(DictType.ypdj, value: item.ypdj)?.label,
  238. img: imgIconYPDJ));
  239. }
  240. inList.add(ItemMsgModel(tit: '扦样地区', content: item.qydq, img: imgIconQYDQ));
  241. inList.add(ItemMsgModel(tit: '扦样人员', content: item.name, img: imgIconQYRY));
  242. inList.add(ItemMsgModel(tit: '扦样时间', content: item.qysj, img: imgIconQYSJ));
  243. }
  244. return Padding(
  245. padding: const EdgeInsets.symmetric(horizontal: 8),
  246. child: LayoutBuilder(builder: (context, constraints) {
  247. return Wrap(
  248. spacing: 4,
  249. runSpacing: 12,
  250. children: inList.map((item) {
  251. return SizedBox(
  252. width: constraints.maxWidth / 2 - 2,
  253. child: Row(
  254. crossAxisAlignment: CrossAxisAlignment.start,
  255. children: [
  256. Padding(
  257. padding: const EdgeInsets.fromLTRB(0, 4, 6, 0),
  258. child: Image.asset(item.img, width: 36),
  259. ),
  260. Expanded(
  261. child: Column(
  262. crossAxisAlignment: CrossAxisAlignment.start,
  263. children: [
  264. Text(item.tit, style: const TextStyle(color: MyColor.c_666666)),
  265. Text(
  266. item.content ?? '',
  267. style:
  268. const TextStyle(color: MyColor.c_666666, fontWeight: FontWeight.bold),
  269. )
  270. ],
  271. ),
  272. )
  273. ],
  274. ),
  275. );
  276. }).toList(),
  277. );
  278. }),
  279. );
  280. }
  281. Widget buildBottom(SampleTaskItem item) {
  282. Widget button = const SizedBox.shrink();
  283. if (widget.type == StepType.reap && item.deliveryStatus == 0) {
  284. button = MyButton(
  285. '任务领取',
  286. onTap: () => receiveTask(item),
  287. fountSize: 13,
  288. radius: 6,
  289. alignment: null,
  290. padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
  291. );
  292. } else if (widget.type == StepType.reap && item.deliveryStatus == 1) {
  293. button = MyButton(
  294. '开始扦样',
  295. onTap: () => startTaskDetail(false, item),
  296. fountSize: 13,
  297. radius: 6,
  298. alignment: null,
  299. padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
  300. );
  301. } else {
  302. button = MyButton(
  303. '修改信息',
  304. onTap: () => startTaskDetail(false, item,deliveryStatus: item.deliveryStatus),
  305. fountSize: 13,
  306. radius: 6,
  307. alignment: null,
  308. padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
  309. );
  310. }
  311. return Container(
  312. padding: const EdgeInsets.fromLTRB(8, 14, 12, 14),
  313. decoration: const BoxDecoration(
  314. gradient: LinearGradient(
  315. begin: Alignment.topCenter,
  316. end: Alignment.bottomCenter,
  317. colors: [Color(0xFFE4F5FC), Color(0xFFF5FDFD)])),
  318. child: Row(
  319. children: [
  320. Image.asset(
  321. imgIconEdit,
  322. width: 10,
  323. ),
  324. const SizedBox(width: 4),
  325. Expanded(
  326. child: item.deliveryStatus == 2
  327. ? Text(
  328. '扦样单据完成于${item.qysj ?? ''}',
  329. style: const TextStyle(fontSize: 14, color: MyColor.c_666666),
  330. )
  331. : Text(
  332. '扦样单据创建于${DateTimeUtils.yyyymmdd(timestamp: item.createTime) ?? ''}',
  333. style: const TextStyle(fontSize: 14, color: MyColor.c_666666),
  334. )),
  335. const SizedBox(width: 4),
  336. button,
  337. ],
  338. ),
  339. );
  340. }
  341. @override
  342. bool get wantKeepAlive => true;
  343. }
  344. class ItemMsgModel {
  345. final String tit;
  346. final String? content;
  347. final String img;
  348. ItemMsgModel({required this.tit, this.content, required this.img});
  349. }