event_manage.dart 13 KB

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