field_control.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import 'package:app_business/service/api.dart';
  2. import 'package:grpc/grpc.dart';
  3. import 'package:track_common/service/map_watch.dart';
  4. import 'package:track_common/track_common.dart';
  5. import 'package:track_common/view/home/field_control/field_control.dart';
  6. import 'package:track_common/view/home/field_control/field_control_controller.dart';
  7. import 'package:track_common/widget/prelude.dart';
  8. import 'dialog_event_register.dart';
  9. class FieldControlPageImpl extends FieldControlPage {
  10. const FieldControlPageImpl({super.key});
  11. @override
  12. Widget rightColumn(BuildContext context) {
  13. return Obx(() {
  14. final mapWatch = controller.mapWatch;
  15. debug('${identityHashCode(controller)}');
  16. return Container(
  17. width: 370,
  18. padding: const EdgeInsets.all(6.4),
  19. height: double.infinity,
  20. color: Colors.white,
  21. child: Column(
  22. children: [
  23. Container(
  24. padding: const EdgeInsets.only(bottom: 8),
  25. width: double.infinity,
  26. child: DarkButton(
  27. onPressed: mapWatch != null
  28. ? () => _onTapRegister(context, mapWatch)
  29. : null,
  30. child: const Text('注册比赛'))),
  31. const Expanded(child: EventInfoView())
  32. ],
  33. ));
  34. });
  35. }
  36. Future<void> _onTapRegister(BuildContext context, MapWatch mapWatch) async {
  37. // final r = await Get.dialog(const RegisterDialog(), arguments: mapWatch.id)
  38. // as RegisterInfo?;
  39. final mapId = mapWatch.id;
  40. final rl = await Get.find<ApiService>()
  41. .stub
  42. .toActivitySelectList(IdRequest()..id = Int64(mapId));
  43. final l = rl.list.map((e) => EventInfo()
  44. ..id = e.actId
  45. ..name = e.actName);
  46. final r = await showEventRegisterDialog(mapId, l);
  47. if (r != null) {
  48. Get.find<ApiService>()
  49. .stub
  50. .toMatchRegusterAdd(ToMatchRegusterAddRequest()
  51. ..actId = r.id
  52. ..regName = r.name
  53. ..startAt = r.eventStartAt.toPb()
  54. ..stopAt = r.eventStopAt.toPb()
  55. ..isAutoBegin = r.isAutoBegin
  56. ..bShowTime = r.showStartAt.toPb()
  57. ..eShowTime = r.showStopAt.toPb()
  58. ..isQueryPwd = r.passwordQuery != null
  59. ..queryPasswd = r.passwordQuery ?? ''
  60. ..matchPasswd = r.passwordEvent ?? '')
  61. .then((p0) {}, onError: (e) {
  62. if (e is GrpcError) {
  63. if (context.mounted) {
  64. ScaffoldMessenger.of(context)
  65. .showSnackBar(SnackBar(content: Text('注册失败:{${e.message}')));
  66. }
  67. }
  68. });
  69. }
  70. }
  71. }
  72. class EventInfo {
  73. int id = 0;
  74. String name = '';
  75. }
  76. class RegisterInfo {
  77. var id = 0;
  78. var name = '';
  79. var startAt = DateTime.now();
  80. var stopAt = DateTime.now();
  81. String? password;
  82. String? passwordQuery;
  83. }
  84. class FieldControlControllerImpl extends FieldControlController {
  85. @override
  86. void onInit() {
  87. debug('init');
  88. super.onInit();
  89. }
  90. @override
  91. void onClose() {
  92. super.onClose();
  93. debug('close');
  94. }
  95. }
  96. class RegisterDialogController extends GetxController {
  97. var registerName = '';
  98. final date = Rx<DateTime?>(null);
  99. final registerStartAt = Rx<TimeOfDay?>(null);
  100. final registerStopAt = Rx<TimeOfDay?>(null);
  101. final selected = Rx<EventInfo?>(null);
  102. final eventList = <EventInfo>[].obs;
  103. final hasPassword = false.obs;
  104. final hasPasswordQuery = false.obs;
  105. var password = '';
  106. var passwordQuery = '';
  107. late final int mapId;
  108. final api = Get.find<ApiService>();
  109. String? get dateString {
  110. final d = date.value;
  111. if (d != null) {
  112. return '${d.month}/${d.day}';
  113. }
  114. return null;
  115. }
  116. @override
  117. void onInit() {
  118. mapId = Get.arguments as int;
  119. api.stub.toActivitySelectList(IdRequest()..id = Int64(mapId)).then((r) {
  120. eventList.value = r.list
  121. .map((e) => EventInfo()
  122. ..id = e.actId
  123. ..name = e.actName)
  124. .toList();
  125. });
  126. super.onInit();
  127. }
  128. }
  129. class RegisterDialog extends GetView<RegisterDialogController> {
  130. const RegisterDialog({super.key});
  131. @override
  132. Widget build(BuildContext context) {
  133. return GetBuilder(
  134. init: RegisterDialogController(),
  135. builder: (c) {
  136. return AlertDialog(
  137. title: const Center(
  138. child: Text(
  139. '注册比赛',
  140. style: TextStyle(fontSize: 17),
  141. )),
  142. backgroundColor: Colors.white,
  143. shape: RoundedRectangleBorder(
  144. borderRadius: BorderRadius.circular(17.78)),
  145. content: SizedBox(
  146. width: 320,
  147. child: ListView(
  148. shrinkWrap: true,
  149. children: [
  150. Obx(() => SizedBox(
  151. child: DropdownMenu<EventInfo>(
  152. key: GlobalKey(),
  153. width: 320,
  154. hintText: '请选择活动',
  155. onSelected: (one) {
  156. controller.selected.value = one;
  157. },
  158. inputDecorationTheme: InputDecorationTheme(
  159. border: textBorder,
  160. isDense: true,
  161. ),
  162. dropdownMenuEntries: controller.eventList
  163. .map((e) => DropdownMenuEntry<EventInfo>(
  164. value: e, label: e.name))
  165. .toList()))),
  166. const SizedBox(height: 21.34),
  167. _TextField(
  168. hint: '请输入名称',
  169. onChanged: (v) {
  170. c.registerName = v;
  171. }),
  172. const SizedBox(height: 21.34),
  173. Row(children: [
  174. Expanded(
  175. child: Obx(() => _TextField(
  176. hint: '日期',
  177. readOnly: true,
  178. initText: c.dateString,
  179. onTap: () async {
  180. c.date.value = await _showDatePicker(
  181. context, c.date.value);
  182. }))),
  183. const SizedBox(width: 15.64),
  184. Expanded(
  185. child: Obx(() => _TextField(
  186. hint: '开始时间',
  187. readOnly: true,
  188. initText:
  189. c.registerStartAt.value?.format(context),
  190. onTap: () async {
  191. c.registerStartAt.value = await _showTimePicker(
  192. context, c.registerStartAt.value);
  193. }))),
  194. const SizedBox(width: 15.64),
  195. Expanded(
  196. child: Obx(() => _TextField(
  197. hint: '结束时间',
  198. readOnly: true,
  199. initText: c.registerStopAt.value?.format(context),
  200. onTap: () async {
  201. c.registerStopAt.value = await _showTimePicker(
  202. context, c.registerStopAt.value);
  203. }))),
  204. ]),
  205. const SizedBox(height: 21.34),
  206. Row(
  207. mainAxisSize: MainAxisSize.min,
  208. children: [
  209. Obx(() => Switch(
  210. value: c.hasPasswordQuery.value,
  211. onChanged: (v) {
  212. c.hasPasswordQuery.value = v;
  213. })),
  214. const Text('查询密码'),
  215. const SizedBox(width: 12),
  216. Obx(() => Expanded(
  217. child: Visibility(
  218. visible: c.hasPasswordQuery.value,
  219. child: _TextField(
  220. hint: '请输入密码',
  221. onChanged: (v) {
  222. c.passwordQuery = v;
  223. })))),
  224. ],
  225. ),
  226. const SizedBox(height: 6),
  227. Row(
  228. mainAxisSize: MainAxisSize.min,
  229. children: [
  230. Obx(() => Switch(
  231. value: c.hasPassword.value,
  232. onChanged: (v) {
  233. c.hasPassword.value = v;
  234. })),
  235. const Text('赛事密码'),
  236. const SizedBox(width: 12),
  237. Obx(() => Expanded(
  238. child: Visibility(
  239. visible: c.hasPassword.value,
  240. child: _TextField(
  241. hint: '请输入密码',
  242. onChanged: (v) {
  243. c.password = v;
  244. })))),
  245. ],
  246. ),
  247. const SizedBox(height: 21.34),
  248. SizedBox(
  249. width: double.infinity,
  250. child: DarkButton(
  251. onPressed: _onRegister, child: const Text('注 册')))
  252. ],
  253. )),
  254. );
  255. });
  256. }
  257. void _onRegister() {
  258. final date = controller.date.value;
  259. final timeStartAt = controller.registerStartAt.value;
  260. final timeStopAt = controller.registerStopAt.value;
  261. final selected = controller.selected.value;
  262. if (selected == null) {
  263. Get.snackbar('错误', '请选择一个活动');
  264. return;
  265. }
  266. if (controller.registerName.isEmpty) {
  267. Get.snackbar('错误', '输入名称');
  268. return;
  269. }
  270. if (date == null) {
  271. Get.snackbar('错误', '请选择日期');
  272. return;
  273. }
  274. if (timeStartAt == null) {
  275. Get.snackbar('错误', '请选择开始时间');
  276. return;
  277. }
  278. if (timeStopAt == null) {
  279. Get.snackbar('错误', '请选择结束时间');
  280. return;
  281. }
  282. final startAt =
  283. date.copyWith(hour: timeStartAt.hour, minute: timeStartAt.minute);
  284. final stopAt =
  285. date.copyWith(hour: timeStopAt.hour, minute: timeStopAt.minute);
  286. if (startAt.isAfter(stopAt)) {
  287. Get.snackbar('错误', '结束时间应晚于开始时间');
  288. return;
  289. }
  290. Get.back(
  291. result: RegisterInfo()
  292. ..id = selected.id
  293. ..name = controller.registerName
  294. ..startAt = startAt
  295. ..stopAt = stopAt
  296. ..password = controller.hasPassword.value ? controller.password : null
  297. ..passwordQuery = controller.hasPasswordQuery.value
  298. ? controller.passwordQuery
  299. : null);
  300. }
  301. Future<TimeOfDay?> _showTimePicker(
  302. BuildContext context, TimeOfDay? init) async {
  303. final TimeOfDay? time = await showTimePicker(
  304. context: context,
  305. initialTime: init ?? TimeOfDay.now(),
  306. );
  307. return time;
  308. }
  309. Future<DateTime?> _showDatePicker(
  310. BuildContext context, DateTime? init) async {
  311. final DateTime? time = await showDatePicker(
  312. context: context,
  313. initialDate: init ?? DateTime.now(),
  314. firstDate: DateTime.now(),
  315. lastDate: DateTime.now().add(365.days),
  316. );
  317. return time;
  318. }
  319. }
  320. final textBorder = OutlineInputBorder(
  321. borderSide: const BorderSide(width: 0.71, color: Color(0xff818181)),
  322. borderRadius: BorderRadius.circular(2.13),
  323. );
  324. class _TextField extends StatelessWidget {
  325. const _TextField(
  326. {required this.hint,
  327. this.onChanged,
  328. this.readOnly = false,
  329. this.onTap,
  330. this.initText});
  331. final String hint;
  332. final void Function(String)? onChanged;
  333. final bool readOnly;
  334. final void Function()? onTap;
  335. final String? initText;
  336. @override
  337. Widget build(BuildContext context) {
  338. return SizedBox(
  339. child: TextFormField(
  340. key: GlobalKey(),
  341. initialValue: initText,
  342. maxLines: 1,
  343. onChanged: onChanged,
  344. onTap: onTap,
  345. readOnly: readOnly,
  346. decoration: InputDecoration(
  347. hintText: hint,
  348. border: textBorder,
  349. isDense: true,
  350. // contentPadding: const EdgeInsets.all(8.53)
  351. )),
  352. );
  353. }
  354. }