event_manage.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. import 'package:app_business/service/api.dart';
  2. import 'package:app_business/view/home/dialog_event_register.dart';
  3. import 'package:pretty_qr_code/pretty_qr_code.dart';
  4. import 'package:track_common/model/event_state.dart';
  5. import 'package:track_common/widget.dart';
  6. import 'package:track_common/widget/prelude.dart';
  7. import 'event_manage_controller.dart';
  8. class EventManage extends GetView<EventManagerController> {
  9. const EventManage({super.key});
  10. @override
  11. Widget build(BuildContext context) {
  12. return Level2View(
  13. level1: level1(),
  14. level2: level2(),
  15. level1Title: '赛事列表',
  16. level1Action: wDate(context),
  17. level2Title: '用户列表',
  18. level2SubTitle: Row(
  19. children: [
  20. Obx(() => Text(
  21. controller.selected?.name != null
  22. ? '(${controller.selected!.name})'
  23. : '',
  24. style: const TextStyle(color: Colors.grey, fontSize: 14.22),
  25. )),
  26. const Spacer(),
  27. SizedBox(
  28. // height: 27.73,
  29. width: 360,
  30. child: wTopButtons(context),
  31. ),
  32. const SizedBox(width: 20)
  33. ],
  34. ));
  35. }
  36. Widget wTopButtons(BuildContext context) {
  37. return Obx(() {
  38. var enable = true;
  39. if (controller.selected?.state == EventState.idle) {
  40. enable = false;
  41. }
  42. return Row(
  43. mainAxisSize: MainAxisSize.min,
  44. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  45. children: [
  46. AppButton.outlined(
  47. color: Colors.orange,
  48. onPressed: enable ? controller.userRestartAll : null,
  49. child: const Text('一键重赛'),
  50. ),
  51. AppButton.outlined(
  52. color: Colors.blue,
  53. onPressed: enable ? controller.routeAllocAll : null,
  54. child: const Text('一键分发'),
  55. ),
  56. AppButton.outlined(
  57. color: Colors.green,
  58. onPressed: enable ? controller.userStartAll : null,
  59. child: const Text('一键开始'),
  60. ),
  61. AppButton.outlined(
  62. color: Colors.red,
  63. onPressed: enable ? controller.userDelAll : null,
  64. child: const Text('一键删除'),
  65. )
  66. ],
  67. );
  68. });
  69. }
  70. Widget wDate(BuildContext context) {
  71. return GestureDetector(
  72. onTap: () => _onTapDate(context),
  73. child: Obx(() => Container(
  74. height: 22.04,
  75. padding: const EdgeInsets.symmetric(horizontal: 8),
  76. decoration: BoxDecoration(
  77. border:
  78. Border.all(color: const Color(0xffe3e3e3), width: 0.71),
  79. borderRadius: BorderRadius.circular(2.13)),
  80. child: Text(controller.dateStr),
  81. )));
  82. }
  83. Future<void> _onTapDate(BuildContext context) async {
  84. final date = await showDatePicker(
  85. context: context,
  86. initialDate: controller.filterDate.value,
  87. firstDate: DateTime.now(),
  88. lastDate: DateTime.now().add(365.days));
  89. if (date != null) {
  90. controller.filterDate.value = date;
  91. controller.flushList();
  92. }
  93. }
  94. Widget level1() {
  95. return Obx(() => ListView(
  96. children: controller.eventList
  97. .map((e) => EventTitle(
  98. data: e,
  99. selected: controller.selectedId.value == e.id,
  100. onTap: () => controller.selectedId.value = e.id,
  101. ))
  102. .toList()));
  103. }
  104. Widget level2() {
  105. return Column(
  106. children: [
  107. Expanded(
  108. child: Obx(
  109. () => LineChart(titles: rightTitles(), children: rightUsers())))
  110. ],
  111. );
  112. }
  113. Iterable<LineChartTitle> rightTitles() {
  114. return [
  115. LineChartTitle(
  116. title: Checkbox(
  117. value: false,
  118. onChanged: (v) {
  119. controller.selectedUser.update((val) {
  120. final all = controller.userList.map((e) => e.checkId);
  121. val?.assignAll(all);
  122. });
  123. }),
  124. width: 32),
  125. const LineChartTitle(title: Text('序号'), width: 42),
  126. const LineChartTitle(title: Text('用户名'), width: 70),
  127. const LineChartTitle(title: Text('手机号'), width: 98),
  128. const LineChartTitle(title: Text('签到时间'), width: 78),
  129. const LineChartTitle(title: Text('手环'), width: 67),
  130. const LineChartTitle(title: Text('路线'), flex: 1),
  131. const LineChartTitle(title: Text('状态'), width: 67),
  132. const LineChartTitle(title: Text('操作'), width: 67),
  133. ];
  134. }
  135. Iterable<LineChartElem> rightUsers() {
  136. return controller.userList.indexed.map((e) {
  137. final (i, one) = e;
  138. var stateStr = '';
  139. var stateColor = Colors.white;
  140. var optStr = '删除';
  141. var optColor = Colors.red;
  142. VoidCallback? opt;
  143. if (one.isAllowDel) {
  144. opt = () => controller.deleteSignIn(one);
  145. }
  146. switch (one.state) {
  147. case UserState.idle:
  148. stateStr = '未分发';
  149. stateColor = Colors.blue;
  150. break;
  151. case UserState.isStart:
  152. stateStr = '已开始';
  153. stateColor = Colors.green;
  154. optStr = '结束';
  155. opt = () => controller.userStopGame(one);
  156. break;
  157. case UserState.isFinish:
  158. stateStr = '已结束';
  159. stateColor = Colors.orange;
  160. optStr = '重赛';
  161. optColor = Colors.orange;
  162. opt = () => controller.userRestartGame(one);
  163. break;
  164. default:
  165. }
  166. var snStr = '--';
  167. const n = 4;
  168. if (one.bandSN.length > n) {
  169. snStr = '-${one.bandSN.substring(one.bandSN.length - n)}';
  170. } else if (one.bandSN.isNotEmpty) {
  171. snStr = one.bandSN;
  172. }
  173. return LineChartElem([
  174. _CheckBox(one),
  175. Text('${i + 1}'),
  176. Text(one.name),
  177. Text(one.phone),
  178. Text(one.checkTime),
  179. Text(snStr),
  180. one.state == UserState.idle
  181. ? button(
  182. color: Colors.blue,
  183. onPressed: () => routeSelect(one),
  184. text: '分发')
  185. : Row(mainAxisSize: MainAxisSize.min, children: [
  186. Expanded(
  187. child: Text(
  188. one.routeName,
  189. maxLines: 1,
  190. style: const TextStyle(overflow: TextOverflow.ellipsis),
  191. )),
  192. SizedBox(
  193. width: 32,
  194. child: one.state == UserState.hasRoute
  195. ? GestureDetector(
  196. onTap: () => routeSelect(one),
  197. child: const Icon(Icons.mode_edit_outline))
  198. : const SizedBox())
  199. ]),
  200. one.state == UserState.hasRoute
  201. ? button(
  202. color: Colors.green,
  203. onPressed: () => controller.userStart(one),
  204. text: '开始')
  205. : Text(stateStr, style: TextStyle(color: stateColor)),
  206. button(text: optStr, color: optColor, isOutline: true, onPressed: opt)
  207. ]);
  208. });
  209. }
  210. Widget button(
  211. {Color? color,
  212. VoidCallback? onPressed,
  213. isOutline = false,
  214. required String text}) {
  215. return SizedBox(
  216. height: 22.78,
  217. width: 51.2,
  218. child: SmallButton(
  219. color: color,
  220. onPressed: onPressed,
  221. isOutline: isOutline,
  222. child: Text(text),
  223. ));
  224. }
  225. Future<void> routeSelect(UserInManage user) async {
  226. final list = await controller.routeList(user);
  227. await Get.dialog(_RouteSelectDialog(list: list));
  228. final route = controller.tmpSelectRoute;
  229. if (route != null) {
  230. await controller.routeAlloc(user, route);
  231. }
  232. }
  233. }
  234. class _CheckBox extends GetView<EventManagerController> {
  235. const _CheckBox(this.one);
  236. final UserInManage one;
  237. @override
  238. Widget build(BuildContext context) {
  239. return Obx(() => Checkbox(
  240. value: controller.selectedUser.value.contains(one.checkId),
  241. onChanged: (v) {
  242. controller.selectedUser.update((val) {
  243. if (v == true) {
  244. val?.add(one.checkId);
  245. } else {
  246. val?.remove(one.checkId);
  247. }
  248. });
  249. }));
  250. }
  251. }
  252. class _RouteSelectDialog extends GetView<EventManagerController> {
  253. const _RouteSelectDialog({required this.list});
  254. final Iterable<RouteInfo> list;
  255. @override
  256. Widget build(BuildContext context) {
  257. return AlertDialog(
  258. title: const Center(
  259. child: Text(
  260. '选择路线',
  261. style: TextStyle(fontSize: 17),
  262. )),
  263. backgroundColor: Colors.white,
  264. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(17.78)),
  265. content: Column(
  266. mainAxisSize: MainAxisSize.min,
  267. crossAxisAlignment: CrossAxisAlignment.center,
  268. children: [
  269. SizedBox(
  270. width: 303.64,
  271. child: DropdownMenu<RouteInfo>(
  272. key: GlobalKey(),
  273. width: 303,
  274. hintText: '请选择路线',
  275. onSelected: (one) {
  276. controller.tmpSelectRoute = one;
  277. },
  278. inputDecorationTheme: InputDecorationTheme(
  279. border: textBorder,
  280. isDense: true,
  281. ),
  282. dropdownMenuEntries: list
  283. .map((e) =>
  284. DropdownMenuEntry<RouteInfo>(value: e, label: e.name))
  285. .toList())),
  286. const SizedBox(height: 30),
  287. Row(
  288. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  289. children: [
  290. SizedBox(
  291. height: 38,
  292. width: 106.67,
  293. child: SmallButton(
  294. onPressed: () {
  295. Get.back();
  296. },
  297. color: Colors.orange,
  298. child: const Text('随机'))),
  299. SizedBox(
  300. height: 38,
  301. width: 106.67,
  302. child: SmallButton(
  303. onPressed: () {
  304. Get.back();
  305. },
  306. color: Colors.blue,
  307. child: const Text('确认'))),
  308. ],
  309. )
  310. ],
  311. ),
  312. );
  313. }
  314. }
  315. class EventTitle extends GetView<EventManagerController> {
  316. final bool selected;
  317. final EventInManage data;
  318. final VoidCallback onTap;
  319. const EventTitle(
  320. {super.key,
  321. required this.selected,
  322. required this.data,
  323. required this.onTap});
  324. @override
  325. Widget build(BuildContext context) {
  326. var children = <Widget>[
  327. AppTitleList(
  328. title: data.name,
  329. tail: Text('${data.userList.length}'),
  330. subtitle: Text.rich(TextSpan(
  331. text: '比赛时间:${data.startAt} - ${data.endAt} ',
  332. style: const TextStyle(
  333. fontSize: 9.9,
  334. fontWeight: FontWeight.w500,
  335. color: Color(0xff818181)),
  336. children: [
  337. TextSpan(
  338. text: data.state.toString(),
  339. style: TextStyle(color: data.state.toColor()))
  340. ])),
  341. isSelected: selected,
  342. onTap: onTap,
  343. )
  344. ];
  345. if (selected) {
  346. children.add(const SizedBox(height: 2));
  347. children.add(Container(
  348. decoration: const BoxDecoration(
  349. color: Color(0xfff1f1f1),
  350. borderRadius: BorderRadius.only(
  351. bottomLeft: Radius.circular(14.22),
  352. bottomRight: Radius.circular(14.22))),
  353. padding: const EdgeInsets.all(16.3),
  354. width: 221.87,
  355. child: Column(
  356. mainAxisSize: MainAxisSize.min,
  357. children: [
  358. Row(
  359. children: [
  360. DarkButton(
  361. onPressed: data.isAllowDel
  362. ? () => controller.deleteEvent(data)
  363. : null,
  364. child: const Text('删除')),
  365. const Spacer(),
  366. DarkButton(
  367. color: Colors.blue,
  368. onPressed: data.isAllowEdit
  369. ? () async {
  370. final r = await showEventEditDialog(
  371. controller.mapId!,
  372. EventRegisterInfo()..name = data.name);
  373. if (r != null) {
  374. controller.eventEdit(data.id, r);
  375. }
  376. }
  377. : null,
  378. child: const Text('修改')),
  379. ],
  380. ),
  381. const SizedBox(height: 24),
  382. SizedBox.square(
  383. dimension: 128, child: PrettyQrView.data(data: data.qrCode)),
  384. const SizedBox(height: 12),
  385. const Text(
  386. '用彩图奔跑APP扫码签到',
  387. style: TextStyle(color: Colors.red, fontSize: 14.22),
  388. )
  389. ],
  390. ),
  391. ));
  392. }
  393. return Column(
  394. mainAxisSize: MainAxisSize.min,
  395. children: children,
  396. );
  397. }
  398. }