field_control.dart 12 KB

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