view.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import 'package:fis_jsonrpc/rpc.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:get/get.dart';
  4. import 'package:vnoteapp/architecture/types/index.dart';
  5. import 'package:vnoteapp/architecture/utils/datetime.dart';
  6. import 'package:vnoteapp/components/button.dart';
  7. import 'package:vnoteapp/components/cell.dart';
  8. import 'package:vnoteapp/components/checkbox_button.dart';
  9. import 'package:vnoteapp/components/dialog_date.dart';
  10. import 'package:vnoteapp/components/dialog_input.dart';
  11. import 'package:vnoteapp/components/dialog_select.dart';
  12. import 'package:vnoteapp/components/dynamic_drawer.dart';
  13. import 'package:vnoteapp/consts/nations.dart';
  14. import 'package:vnoteapp/consts/rpc_enum_labels.dart';
  15. import 'package:vnoteapp/pages/controllers/crowd_labels.dart';
  16. import 'package:vnoteapp/pages/patient/create/controller.dart';
  17. class CreatePatientPage extends GetView<CreatePatientController> {
  18. const CreatePatientPage({super.key});
  19. @override
  20. Widget build(BuildContext context) {
  21. return Stack(
  22. children: [
  23. Scrollbar(
  24. thumbVisibility: true,
  25. child: SingleChildScrollView(
  26. child: Container(
  27. alignment: Alignment.topCenter,
  28. color: const Color.fromRGBO(238, 238, 238, 1),
  29. padding: const EdgeInsets.only(bottom: 64),
  30. child: SizedBox(
  31. width: 800,
  32. child: Column(
  33. children: [
  34. _AreaPanel(),
  35. const SizedBox(height: 12),
  36. _PatientInfoPanel(),
  37. const SizedBox(height: 12),
  38. _AddressPanel(),
  39. const SizedBox(height: 12),
  40. _CrowdLabelPanel(),
  41. const SizedBox(height: 24),
  42. ],
  43. ),
  44. ),
  45. ),
  46. ),
  47. ),
  48. Positioned(
  49. bottom: 8,
  50. left: 200,
  51. right: 200,
  52. child: Row(
  53. mainAxisAlignment: MainAxisAlignment.spaceAround,
  54. children: [
  55. VButton(
  56. label: "下一步",
  57. onTap: () {
  58. controller.gotoSignContract();
  59. },
  60. ),
  61. VButton(
  62. label: "存为档案",
  63. onTap: () {
  64. controller.gotoPatientDetail();
  65. },
  66. ),
  67. ],
  68. ),
  69. ),
  70. // Positioned(
  71. // top: 20,
  72. // right: 20,
  73. // child: SizedBox(
  74. // width: 90,
  75. // height: 90,
  76. // child: ElevatedButton(
  77. // style: ButtonStyle(
  78. // shape: MaterialStatePropertyAll(
  79. // RoundedRectangleBorder(
  80. // borderRadius: BorderRadius.circular(45),
  81. // side: BorderSide(color: Theme.of(context).primaryColor),
  82. // ),
  83. // ),
  84. // ),
  85. // onPressed: () {
  86. // controller.onReadCardClicked();
  87. // },
  88. // child: const Text(
  89. // "读卡",
  90. // style: TextStyle(fontWeight: FontWeight.bold),
  91. // ),
  92. // ),
  93. // ),
  94. // ),
  95. ],
  96. );
  97. }
  98. }
  99. class _PatientInfoPanel extends GetView<CreatePatientController> {
  100. @override
  101. Widget build(BuildContext context) {
  102. final state = controller.state;
  103. return VListFormCellGroup(
  104. children: [
  105. Obx(
  106. () => VListFormCell(
  107. label: "证件类型",
  108. content: RpcEnumLabels.cardType[state.cardType],
  109. onTap: () async {
  110. final result = await VDialogSelect<CardTypeEnum, CardTypeEnum>(
  111. title: "选择证件类型",
  112. source: CardTypeEnum.values,
  113. valueGetter: (data) => data,
  114. labelGetter: (data) => RpcEnumLabels.cardType[data] ?? "",
  115. initialValue: state.cardType,
  116. ).show();
  117. if (result != null) {
  118. controller.state.cardType = result;
  119. }
  120. },
  121. ),
  122. ),
  123. Obx(
  124. () => VListFormCell(
  125. label: "证件号",
  126. content: controller.state.cardNo,
  127. onTap: () async {
  128. final result = await VDialogInput(
  129. title: "证件号",
  130. initialValue: controller.state.cardNo,
  131. placeholder: "请填写证件号",
  132. ).show();
  133. if (result != null) {
  134. controller.state.cardNo = result;
  135. }
  136. },
  137. ),
  138. ),
  139. Obx(
  140. () => VListFormCell(
  141. label: "姓名",
  142. content: controller.state.name,
  143. onTap: () async {
  144. final result = await VDialogInput(
  145. title: "姓名",
  146. initialValue: controller.state.name,
  147. placeholder: "请填写姓名",
  148. ).show();
  149. if (result != null) {
  150. controller.state.name = result;
  151. }
  152. },
  153. ),
  154. ),
  155. Obx(
  156. () => VListFormCell(
  157. label: "性别",
  158. content: RpcEnumLabels.gender[state.gender],
  159. onTap: () async {
  160. final result = await VDialogSelect<GenderEnum, GenderEnum>(
  161. title: "选择性别",
  162. source: const [
  163. GenderEnum.Male,
  164. GenderEnum.Female,
  165. GenderEnum.Unknown,
  166. GenderEnum.Unspecified
  167. ],
  168. valueGetter: (data) => data,
  169. labelGetter: (data) => RpcEnumLabels.gender[data] ?? "",
  170. initialValue: state.gender,
  171. ).show();
  172. if (result != null) {
  173. controller.state.gender = result;
  174. }
  175. },
  176. ),
  177. ),
  178. Obx(
  179. () => VListFormCell(
  180. label: "民族",
  181. content: state.nation,
  182. onTap: () async {
  183. final result = await VDialogSelect<String, String>(
  184. title: "选择民族",
  185. source: Nations.china,
  186. valueGetter: (data) => data,
  187. labelGetter: (data) => data,
  188. initialValue: state.nation,
  189. ).show();
  190. if (result != null) {
  191. controller.state.nation = result;
  192. }
  193. },
  194. ),
  195. ),
  196. Obx(
  197. () => VListFormCell(
  198. label: "出生日期",
  199. content: DataTimeUtils.formatDateString(controller.state.birthday),
  200. onTap: () async {
  201. final result = await VDialogDate(
  202. title: "设置出生日期",
  203. initialValue: controller.state.birthday,
  204. ).show();
  205. if (result != null) {
  206. controller.state.birthday = result;
  207. }
  208. },
  209. ),
  210. ),
  211. Obx(
  212. () => VListFormCell(
  213. label: "年龄",
  214. content: controller.state.age.toString(),
  215. ),
  216. ),
  217. Obx(
  218. () => VListFormCell(
  219. label: "手机号码",
  220. content: controller.state.phoneNo,
  221. onTap: () async {
  222. final result = await VDialogInput(
  223. title: "手机号码",
  224. initialValue: controller.state.phoneNo,
  225. placeholder: "请填写手机号码",
  226. ).show();
  227. if (result != null) {
  228. controller.state.phoneNo = result;
  229. }
  230. },
  231. ),
  232. ),
  233. ],
  234. );
  235. }
  236. }
  237. class _AddressPanel extends GetView<CreatePatientController> {
  238. @override
  239. Widget build(BuildContext context) {
  240. return VListFormCellGroup(
  241. children: [
  242. VListFormCell(
  243. label: "同步户籍地址到现住地址",
  244. labelWidth: 250,
  245. contentWidget: Container(
  246. child: Obx(
  247. () => Switch(
  248. onChanged: (value) {
  249. controller.onSyncAddressCheckChanged(value);
  250. },
  251. value: controller.state.isSyncAddresses,
  252. ),
  253. ),
  254. ),
  255. ),
  256. Obx(
  257. () => VListFormCell(
  258. label: "户籍地址",
  259. content: controller.state.censusRegister,
  260. onTap: () async {
  261. final result = await VDialogInput(
  262. title: "户籍地址",
  263. initialValue: controller.state.censusRegister,
  264. placeholder: "请填写户籍地址",
  265. ).show();
  266. if (result != null) {
  267. controller.onCensusRegisterChanged(result);
  268. }
  269. },
  270. ),
  271. ),
  272. Obx(
  273. () => VListFormCell(
  274. label: "现住地址",
  275. content: controller.state.address,
  276. onTap: () async {
  277. final result = await VDialogInput(
  278. title: "现住地址",
  279. initialValue: controller.state.address,
  280. placeholder: "请填写现住地址",
  281. ).show();
  282. if (result != null) {
  283. controller.state.address = result;
  284. }
  285. },
  286. ),
  287. ),
  288. ],
  289. );
  290. }
  291. }
  292. class _AreaPanel extends GetView<CreatePatientController> {
  293. @override
  294. Widget build(BuildContext context) {
  295. return VListFormCellGroup(
  296. children: [
  297. VListFormCell(
  298. label: "签约机构",
  299. content: controller.state.organizationName,
  300. ),
  301. Obx(
  302. () {
  303. return VListFormCell(
  304. label: "签约区域",
  305. content: controller.state.villageName,
  306. onTap: () async {
  307. final result = await VDialogSelect<StringKVModel, String>(
  308. title: "选择签约区域",
  309. source: controller.state.villageOptions,
  310. valueGetter: (data) => data.key,
  311. labelGetter: (data) => data.value,
  312. initialValue: controller.state.villageCode,
  313. ).show();
  314. if (result != null) {
  315. controller.state.villageCode = result;
  316. }
  317. },
  318. );
  319. },
  320. ),
  321. ],
  322. );
  323. }
  324. }
  325. class _CrowdLabelPanel extends GetView<CrowdLabelsController> {
  326. final createController = Get.find<CreatePatientController>();
  327. @override
  328. Widget build(BuildContext context) {
  329. return VListFormCellGroup(
  330. children: [
  331. Obx(
  332. () => VListFormCell(
  333. label: "个人编号",
  334. content: createController.state.personalNo,
  335. onTap: () async {
  336. final result = await VDialogInput(
  337. title: "个人编号",
  338. initialValue: createController.state.personalNo,
  339. placeholder: "请填写个人编号",
  340. ).show();
  341. if (result != null) {
  342. createController.state.personalNo = result;
  343. }
  344. },
  345. ),
  346. ),
  347. VListFormCell(
  348. label: "人群分类",
  349. contentWidget: _buildContent(context),
  350. onTap: () {
  351. VDynamicDrawerWrapper.show(
  352. scaffoldKey: createController.homeScaffoldKey,
  353. builder: (_) => _buildDrawer(),
  354. );
  355. },
  356. ),
  357. ],
  358. );
  359. }
  360. Widget _buildDrawer() {
  361. final state = controller.state;
  362. List<String> selectedNormalCodes = state.selectedNormalCodes;
  363. List<String> selectedDiseaseCodes = state.selectedDiseaseCodes;
  364. List<String> selectedSpecialCareCodes = state.selectedSpecialCareCodes;
  365. final scrollController = ScrollController();
  366. return VDrawer(
  367. width: 600,
  368. title: "设置人群分类",
  369. scaffoldKey: createController.homeScaffoldKey,
  370. onConfirm: () {
  371. VDynamicDrawerWrapper.hide(
  372. scaffoldKey: createController.homeScaffoldKey,
  373. );
  374. state.selectedNormalCodes = selectedNormalCodes;
  375. state.selectedDiseaseCodes = selectedDiseaseCodes;
  376. state.selectedSpecialCareCodes = selectedSpecialCareCodes;
  377. },
  378. child: Scrollbar(
  379. controller: scrollController,
  380. thumbVisibility: true,
  381. child: SingleChildScrollView(
  382. controller: scrollController,
  383. child: Padding(
  384. padding: const EdgeInsets.symmetric(horizontal: 50),
  385. child: Column(
  386. children: [
  387. const SizedBox(height: 24),
  388. VCheckBoxButtonGroup<LabelDTO, String>(
  389. source: state.normalOptions,
  390. values: state.selectedNormalCodes,
  391. labelGetter: (LabelDTO data) => data.labelName!,
  392. valueGetter: (LabelDTO data) => data.code!,
  393. onChanged: (value) {
  394. selectedNormalCodes = value;
  395. },
  396. ),
  397. const SizedBox(height: 8),
  398. Divider(
  399. indent: 32,
  400. endIndent: 32,
  401. thickness: 1,
  402. color: Colors.grey.shade300,
  403. ),
  404. const SizedBox(height: 8),
  405. VCheckBoxButtonGroup<LabelDTO, String>(
  406. source: state.diseaseOptions,
  407. values: state.selectedDiseaseCodes,
  408. labelGetter: (LabelDTO data) => data.labelName!,
  409. valueGetter: (LabelDTO data) => data.code!,
  410. onChanged: (value) {
  411. selectedDiseaseCodes = value;
  412. },
  413. ),
  414. const SizedBox(height: 8),
  415. Divider(
  416. indent: 32,
  417. endIndent: 32,
  418. thickness: 1,
  419. color: Colors.grey.shade300,
  420. ),
  421. const SizedBox(height: 8),
  422. VCheckBoxButtonGroup<LabelDTO, String>(
  423. source: state.specialCareOptions,
  424. values: state.selectedSpecialCareCodes,
  425. labelGetter: (LabelDTO data) => data.labelName!,
  426. valueGetter: (LabelDTO data) => data.code!,
  427. onChanged: (value) {
  428. selectedSpecialCareCodes = value;
  429. },
  430. ),
  431. ],
  432. ),
  433. ),
  434. ),
  435. ),
  436. );
  437. }
  438. Widget _buildContent(BuildContext context) {
  439. return Obx(
  440. () {
  441. final themeData = Theme.of(context);
  442. final color = themeData.secondaryHeaderColor;
  443. final names = controller.state.selectedNames;
  444. const itemHeight = 32.0;
  445. const itemRadius = itemHeight / 2;
  446. final itemTextStyle = TextStyle(
  447. color: themeData.primaryColor,
  448. fontSize: 14,
  449. );
  450. return Wrap(
  451. spacing: 16,
  452. runSpacing: 16,
  453. children: names.map(
  454. (e) {
  455. return Container(
  456. height: itemHeight,
  457. alignment: Alignment.center,
  458. padding: const EdgeInsets.symmetric(horizontal: 12),
  459. decoration: BoxDecoration(
  460. color: color,
  461. border: Border.all(style: BorderStyle.none),
  462. borderRadius: BorderRadius.circular(itemRadius),
  463. ),
  464. child: Text(e, style: itemTextStyle),
  465. );
  466. },
  467. ).toList(),
  468. );
  469. },
  470. );
  471. }
  472. }