event_manage.dart 13 KB

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