sample_task_list_page.dart 16 KB

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