sample_task_list_page.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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/print/print_page.dart';
  10. import 'package:lszlgl/page/sample_task/sample_list_vm.dart';
  11. import 'package:lszlgl/page/sample_task/sample_task_list_tab_page.dart';
  12. import 'package:lszlgl/service/dict_service.dart';
  13. import 'package:lszlgl/service/print_service.dart';
  14. import 'package:lszlgl/utils/date_time_utils.dart';
  15. import 'package:lszlgl/utils/inject.dart';
  16. import 'package:lszlgl/widget/button.dart';
  17. import 'package:lszlgl/widget/page_widget.dart';
  18. import 'package:lszlgl/widget/print_checkbox_widget.dart';
  19. import '../../main.dart';
  20. import 'reap_sample_detail/reap_sample_task_page.dart';
  21. import 'stock_sample_detail/stock_sample_task_page.dart';
  22. /// 扦样环节列表
  23. class SampleTaskListPage extends StatefulWidget {
  24. final StepType type;
  25. final int tabIndex;
  26. const SampleTaskListPage({
  27. super.key,
  28. required this.type,
  29. required this.tabIndex,
  30. });
  31. @override
  32. State<SampleTaskListPage> createState() => _SampleTaskListPageState();
  33. }
  34. class _SampleTaskListPageState extends BaseLifecycleState<SampleTaskListPage>
  35. with AutomaticKeepAliveClientMixin {
  36. late SampleListVM vm;
  37. List<num> selectList = [];
  38. /// 详情
  39. void startTaskDetail(bool detail, SampleTaskItem data, {num? deliveryStatus}) async {
  40. bool? success;
  41. switch (widget.type) {
  42. case StepType.reap:
  43. success = await MyRouter.startReapSampleTask(
  44. args: ReapSampleTaskPageArgs(
  45. detail: detail, id: data.id, deliveryStatus: deliveryStatus));
  46. break;
  47. case StepType.stock:
  48. success = await MyRouter.startStockSampleTask(
  49. args: StockSampleTaskPageArgs(detail: detail, id: data.id));
  50. break;
  51. }
  52. if (success ?? false) {
  53. vm.refreshAll();
  54. }
  55. }
  56. /// 领取扦样任务
  57. void receiveTask(SampleTaskItem data) async {
  58. var delete = await MyNavigator.showDialog(
  59. builder: (_) => AlertDialog(
  60. title: const Text('系统提示'),
  61. content: const Text('任务认领后不可撤销,请确认是否认领任务!'),
  62. actions: [
  63. MyButton(
  64. '领取',
  65. alignment: null,
  66. backgroundColor: const Color(0xFFCE615A),
  67. onTap: () => MyNavigator.dismiss(status: SmartStatus.dialog, result: true),
  68. ),
  69. MyButton('取消', alignment: null, onTap: () => MyNavigator.dismiss()),
  70. ],
  71. ),
  72. );
  73. if (!(delete ?? false)) return;
  74. MyNavigator.showLoading();
  75. try {
  76. var rsp = await MyApi.get().receiveSampleTaskSgjc(data.id ?? 0);
  77. MyNavigator.dismissLoading();
  78. if (rsp.data == 1) {
  79. MyNavigator.showToast('领取成功');
  80. vm.refreshAll();
  81. }
  82. } catch (e) {
  83. logger.e(e);
  84. MyNavigator.dismissLoading();
  85. }
  86. }
  87. @override
  88. void onInit() {
  89. vm = Inject.get<SampleListVM>()!;
  90. }
  91. @override
  92. void onFirstShow(Duration timeStamp) {
  93. vm.refresh(widget.tabIndex);
  94. }
  95. @override
  96. Widget build(BuildContext context) {
  97. super.build(context);
  98. return Stack(
  99. children: [
  100. buildBody(),
  101. buildPrintBtn(),
  102. ],
  103. );
  104. // Container(
  105. // margin: const EdgeInsets.symmetric(horizontal: 8),
  106. // clipBehavior: Clip.hardEdge,
  107. // decoration: const BoxDecoration(
  108. // color: Colors.white,
  109. // borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
  110. // ),
  111. // alignment: Alignment.center,
  112. // child: buildBody(),
  113. // );
  114. }
  115. // 批量打印前 获取数据
  116. getPrintData() async{
  117. if(selectList.isEmpty){
  118. MyNavigator.showToast('请选择打印数据');
  119. return;
  120. }
  121. MyNavigator.showLoading(msg: '数据处理中...');
  122. var dataList = await Future.wait(selectList.map((id) => MyApi.get().sampleTaskDetailSgjc(id)));
  123. List<SampleTaskItem?> data = dataList.map((e) => e.data).toList();
  124. if(data.isEmpty){
  125. MyNavigator.dismissLoading();
  126. MyNavigator.showToast('打印数据获取有误');
  127. return;
  128. }
  129. // List<Uint8List?> imgList = await Future.wait(data.map((item)=>imageToBytes(item!.ewmfilePictureList!.first.url!)));
  130. List<PrintPageArgs> printList = [];
  131. for(int i = 0; i < data.length; i++){
  132. List<String> textList = PrintService.getPrintTextListWithSampleTaskItem(data[i]);
  133. PrintPageArgs arg = PrintPageArgs(
  134. urlCode:'${MyApi.globalUrl}/admin-api/zj/code-sampling-task-details-sgjc/getSamplingTaskDetails?id=${data[i]!.id}' ,
  135. numCode: data[i]!.ewmfilePictureList!.first.name,
  136. textList: textList,
  137. );
  138. printList.add(arg);
  139. }
  140. MyNavigator.dismissLoading();
  141. var args = PrintPageArgs(printList: printList);
  142. await MyRouter.startPrint(args);
  143. }
  144. // 通过地址获取图片内容
  145. // Future<Uint8List?> imageToBytes(String imageUrl) async {
  146. // try{
  147. // var response = await Dio().get(
  148. // imageUrl,
  149. // options: Options(responseType: ResponseType.bytes)
  150. // );
  151. // return Uint8List.fromList(response.data);
  152. // } catch(e){
  153. // logger.d(e);
  154. // return null;
  155. // }
  156. // }
  157. Widget buildPrintBtn() {
  158. if (widget.type == StepType.reap && widget.tabIndex == 2) {
  159. return ValueListenableBuilder(
  160. valueListenable: vm.showPrintBtn,
  161. builder: (context, show, child) {
  162. if (show) {
  163. return child!;
  164. } else {
  165. return const SizedBox.shrink();
  166. }
  167. },
  168. child: Positioned(
  169. bottom: 0,
  170. left: 8,
  171. right: 8,
  172. child: MyButton(
  173. '打 印',
  174. radius: 8,
  175. gradient: const LinearGradient(colors: [Color(0xFF3BD2E5), Color(0xFF247AF8)]),
  176. onTap: () {
  177. getPrintData();
  178. },
  179. )),
  180. );
  181. } else {
  182. return const SizedBox.shrink();
  183. }
  184. }
  185. Widget buildBody() {
  186. return EasyRefresh.builder(
  187. controller: vm.ctrlList[widget.tabIndex],
  188. onRefresh: () => vm.getData(widget.tabIndex, refresh: true),
  189. onLoad: () => vm.getData(widget.tabIndex, refresh: false),
  190. childBuilder: (_, physics) => buildList(physics),
  191. );
  192. }
  193. Widget buildList(ScrollPhysics physics) {
  194. var sliver = vm.pageStateList[widget.tabIndex].builder((v) {
  195. var list = v.data;
  196. if (v.status == DataStatus.error) {
  197. // 加载失败
  198. return SliverToBoxAdapter(
  199. child: PageLoadingWidget.error(onTap: () => vm.refresh(widget.tabIndex)));
  200. } else if (list == null || list.isEmpty) {
  201. // 无数据
  202. return const SliverToBoxAdapter(child: PageLoadingWidget.empty());
  203. } else {
  204. return SliverList.builder(
  205. itemCount: list.length,
  206. itemBuilder: (_, index) => buildItem(index, list[index]),
  207. );
  208. }
  209. });
  210. return CustomScrollView(
  211. physics: physics,
  212. slivers: [sliver],
  213. );
  214. }
  215. Widget buildItem(int index, SampleTaskItem item) {
  216. return GestureDetector(
  217. behavior: HitTestBehavior.opaque,
  218. onTap: () {
  219. if (item.deliveryStatus != 2) return;
  220. startTaskDetail(true, item);
  221. },
  222. child: Container(
  223. clipBehavior: Clip.hardEdge,
  224. margin: const EdgeInsets.only(left: 8, right: 8, top: 12),
  225. decoration: const BoxDecoration(
  226. color: Color(0xFFF9FEFE),
  227. borderRadius: BorderRadius.all(Radius.circular(8)),
  228. ),
  229. child: Stack(
  230. children: [
  231. Column(
  232. crossAxisAlignment: CrossAxisAlignment.start,
  233. children: [
  234. const SizedBox(height: 8),
  235. buildTop(),
  236. buildNumber(item.qyrwdh ?? ''),
  237. const Divider(thickness: 0.6, color: Color(0xFFEEEEEE), indent: 8),
  238. const SizedBox(height: 8),
  239. buildGrid(item),
  240. const SizedBox(height: 15),
  241. buildBottom(item),
  242. ],
  243. ),
  244. buildState(item.getDeliveryStatusText()),
  245. buildSelectBtn(item),
  246. ],
  247. ),
  248. ),
  249. );
  250. }
  251. buildSelectBtn(SampleTaskItem item){
  252. if(widget.type == StepType.reap && item.deliveryStatus==2){
  253. return ValueListenableBuilder(valueListenable: vm.showPrintBtn, builder: (_,show,__){
  254. if(show){
  255. return Positioned(
  256. right: 0,
  257. top: 60,
  258. child: PrintCheckboxWidget(
  259. list: selectList,
  260. itemId: item.id!,
  261. onChanged: (bool isSelect){
  262. item.isSelect = isSelect;
  263. if(isSelect){
  264. selectList.add(item.id!);
  265. }else{
  266. selectList.remove(item.id!);
  267. }
  268. },
  269. ),
  270. );
  271. }else{
  272. return const SizedBox.shrink();
  273. }
  274. });
  275. }else{
  276. return const SizedBox.shrink();
  277. }
  278. }
  279. Widget buildTop() {
  280. return Row(
  281. children: [
  282. Image.asset(
  283. imgListTitleIcon,
  284. width: 4,
  285. height: 20,
  286. fit: BoxFit.fill,
  287. ),
  288. const SizedBox(width: 4),
  289. const Text(
  290. '扦样任务单号',
  291. style: TextStyle(color: MyColor.c_333333, fontSize: 15, fontWeight: FontWeight.w500),
  292. ),
  293. ],
  294. );
  295. }
  296. Widget buildState(String state) {
  297. String img = imgQYStateOff;
  298. Color color = const Color(0xFF149723);
  299. if (state != '已扦样') {
  300. img = imgQYStateOn;
  301. color = const Color(0xFFDEA70A);
  302. }
  303. return Positioned(
  304. top: 0,
  305. right: 0,
  306. child: Container(
  307. padding: const EdgeInsets.fromLTRB(40, 4, 20, 16),
  308. decoration: BoxDecoration(image: DecorationImage(image: AssetImage(img), fit: BoxFit.fill)),
  309. child: Text(
  310. state,
  311. style: TextStyle(color: color, fontSize: 15),
  312. ),
  313. ),
  314. );
  315. }
  316. Widget buildNumber(String number) {
  317. return Padding(
  318. padding: const EdgeInsets.only(left: 8, bottom: 6),
  319. child: Text(
  320. number,
  321. style: const TextStyle(color: MyColor.c_333333, fontSize: 16, fontWeight: FontWeight.bold),
  322. ),
  323. );
  324. }
  325. Widget buildGrid(SampleTaskItem item) {
  326. List<ItemMsgModel> inList = [];
  327. if (item.deliveryStatus != 2) {
  328. // 未扦样
  329. inList.add(ItemMsgModel(tit: '采样品种', content: item.cypzName, img: imgIconCJPZ));
  330. if (item.jyzb != null) {
  331. inList.add(ItemMsgModel(tit: '检验指标', content: item.jyzb, img: imgIconJYZB));
  332. }
  333. if (item.ypdj != null) {
  334. inList.add(ItemMsgModel(
  335. tit: '样品层级',
  336. content: DictService.getDict(DictType.ypdj, value: item.ypdj)?.label,
  337. img: imgIconYPDJ));
  338. }
  339. inList.add(ItemMsgModel(tit: '扦样地区', content: item.qydq, img: imgIconQYDQ));
  340. } else {
  341. // 已扦样
  342. inList.add(ItemMsgModel(tit: '采样品种', content: item.cypzName, img: imgIconCJPZ));
  343. if (item.jyzb != null) {
  344. inList.add(ItemMsgModel(tit: '检验指标', content: item.jyzb, img: imgIconJYZB));
  345. }
  346. if (item.ypdj != null) {
  347. inList.add(ItemMsgModel(
  348. tit: '样品层级',
  349. content: DictService.getDict(DictType.ypdj, value: item.ypdj)?.label,
  350. img: imgIconYPDJ));
  351. }
  352. inList.add(ItemMsgModel(tit: '扦样地区', content: item.qydq, img: imgIconQYDQ));
  353. inList.add(ItemMsgModel(tit: '扦样人员', content: item.name, img: imgIconQYRY));
  354. inList.add(ItemMsgModel(tit: '扦样时间', content: item.qysj, img: imgIconQYSJ));
  355. }
  356. return Padding(
  357. padding: const EdgeInsets.symmetric(horizontal: 8),
  358. child: LayoutBuilder(builder: (context, constraints) {
  359. return Wrap(
  360. spacing: 4,
  361. runSpacing: 12,
  362. children: inList.map((item) {
  363. return SizedBox(
  364. width: constraints.maxWidth / 2 - 2,
  365. child: Row(
  366. crossAxisAlignment: CrossAxisAlignment.start,
  367. children: [
  368. Padding(
  369. padding: const EdgeInsets.fromLTRB(0, 4, 6, 0),
  370. child: Image.asset(item.img, width: 36),
  371. ),
  372. Expanded(
  373. child: Column(
  374. crossAxisAlignment: CrossAxisAlignment.start,
  375. children: [
  376. Text(item.tit, style: const TextStyle(color: MyColor.c_666666)),
  377. Text(
  378. item.content ?? '',
  379. style:
  380. const TextStyle(color: MyColor.c_666666, fontWeight: FontWeight.bold),
  381. )
  382. ],
  383. ),
  384. )
  385. ],
  386. ),
  387. );
  388. }).toList(),
  389. );
  390. }),
  391. );
  392. }
  393. Widget buildBottom(SampleTaskItem item) {
  394. Widget button = const SizedBox.shrink();
  395. if (widget.type == StepType.reap && item.deliveryStatus == 0) {
  396. button = MyButton(
  397. '任务领取',
  398. onTap: () => receiveTask(item),
  399. fountSize: 13,
  400. radius: 6,
  401. alignment: null,
  402. padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
  403. );
  404. } else if (widget.type == StepType.reap && item.deliveryStatus == 1) {
  405. button = MyButton(
  406. '开始扦样',
  407. onTap: () => startTaskDetail(false, item),
  408. fountSize: 13,
  409. radius: 6,
  410. alignment: null,
  411. padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
  412. );
  413. } else {
  414. button = MyButton(
  415. '修改信息',
  416. onTap: () => startTaskDetail(false, item, deliveryStatus: item.deliveryStatus),
  417. fountSize: 13,
  418. radius: 6,
  419. alignment: null,
  420. padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
  421. );
  422. }
  423. return Container(
  424. padding: const EdgeInsets.fromLTRB(8, 14, 12, 14),
  425. decoration: const BoxDecoration(
  426. gradient: LinearGradient(
  427. begin: Alignment.topCenter,
  428. end: Alignment.bottomCenter,
  429. colors: [Color(0xFFE4F5FC), Color(0xFFF5FDFD)])),
  430. child: Row(
  431. children: [
  432. Image.asset(
  433. imgIconEdit,
  434. width: 10,
  435. ),
  436. const SizedBox(width: 4),
  437. Expanded(
  438. child: item.deliveryStatus == 2
  439. ? Text(
  440. '扦样单据完成于${item.qysj ?? ''}',
  441. style: const TextStyle(fontSize: 14, color: MyColor.c_666666),
  442. )
  443. : Text(
  444. '扦样单据创建于${DateTimeUtils.yyyymmdd(timestamp: item.createTime) ?? ''}',
  445. style: const TextStyle(fontSize: 14, color: MyColor.c_666666),
  446. )),
  447. const SizedBox(width: 4),
  448. button,
  449. ],
  450. ),
  451. );
  452. }
  453. @override
  454. bool get wantKeepAlive => true;
  455. }
  456. class ItemMsgModel {
  457. final String tit;
  458. final String? content;
  459. final String img;
  460. ItemMsgModel({required this.tit, this.content, required this.img});
  461. }