controller.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. import 'dart:async';
  2. import 'package:fis_jsonrpc/rpc.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:get/get.dart';
  5. import 'package:vitalapp/architecture/defines.dart';
  6. import 'package:vitalapp/architecture/network_connectivity.dart';
  7. import 'package:vitalapp/architecture/utils/prompt_box.dart';
  8. import 'package:vitalapp/components/appbar.dart';
  9. import 'package:vitalapp/global.dart';
  10. import 'package:vitalapp/helper/goto_helper.dart';
  11. import 'package:vitalapp/managers/interfaces/account.dart';
  12. import 'package:vitalapp/managers/interfaces/patient.dart';
  13. import 'package:vitalapp/pages/controllers/home_nav_mixin.dart';
  14. import 'package:vitalapp/pages/home/models/menu.dart';
  15. import 'package:vitalapp/pages/medical/widgets/twelve_ecg.dart';
  16. import 'package:vitalapp/store/store.dart';
  17. import 'package:vnote_device_plugin/consts/types.dart';
  18. import 'state.dart';
  19. class HomeController extends FControllerBase with HomeNavMixin {
  20. /// 仅支持在线模式的菜单路由
  21. static final _onlineOnlyMenuRouteNames = <String>[
  22. "/contract/package_list",
  23. "/check/form",
  24. ];
  25. static Widget _buildImgIcon(String assetName) {
  26. return ClipRect(
  27. child: SizedBox(
  28. height: 34,
  29. child: Image.asset(
  30. "assets/images/home/$assetName",
  31. width: 46,
  32. height: 46,
  33. fit: BoxFit.fitWidth,
  34. ),
  35. ),
  36. );
  37. }
  38. final _patientManager = Get.find<IPatientManager>();
  39. final state = HomeState();
  40. /// 当前选中路由
  41. int currentIndex = -1;
  42. List<HomeMenuItem> homeMenuItems = [
  43. HomeMenuItem(
  44. key: 'ZY',
  45. title: "主页",
  46. routeName: "/dashboard",
  47. iconData: Icons.home_outlined,
  48. ),
  49. HomeMenuItem(
  50. key: 'XJDA',
  51. title: "新建档案",
  52. routeName: "/patient/create",
  53. // TODO: 图addHealthRecord.png 不太合适,先层叠这样显示
  54. iconWidget: _PatientAddIconProxyWidget(
  55. baseIcon: _buildImgIcon('healthRecord.png'),
  56. ),
  57. ),
  58. HomeMenuItem(
  59. key: 'JKDA',
  60. title: "健康档案",
  61. routeName: "/patient/detail",
  62. iconWidget: _buildImgIcon('healthRecord.png'),
  63. ),
  64. HomeMenuItem(
  65. key: 'JMLB',
  66. title: "居民列表",
  67. routeName: "/patient/list",
  68. iconWidget: _buildImgIcon('patientList.png'),
  69. ),
  70. HomeMenuItem(
  71. key: 'YSQY',
  72. title: "医生签约",
  73. routeName: "/contract/package_list",
  74. iconWidget: _buildImgIcon('doctorSigning.png'),
  75. ),
  76. HomeMenuItem(
  77. key: 'JKTJ',
  78. title: "健康体检",
  79. routeName: "/check/form",
  80. iconWidget: _buildImgIcon('healthCheckup.png'),
  81. ),
  82. HomeMenuItem(
  83. key: 'RQSF',
  84. title: "人群随访",
  85. routeName: "/check/follow_up",
  86. iconWidget: _buildImgIcon('populationFollowUp.png'),
  87. ),
  88. HomeMenuItem(
  89. key: 'JKJC',
  90. title: "健康检测",
  91. routeName: "/medical",
  92. iconWidget: _buildImgIcon('diagnosisDisplay.png'),
  93. isSelected: true,
  94. ),
  95. HomeMenuItem(
  96. title: "中医体质",
  97. routeName: "/TraditionalChineseMedicineConstitution",
  98. key: "ZYTZ",
  99. iconWidget: _buildImgIcon("medicineConstitution.png"),
  100. isSelected: true,
  101. ),
  102. HomeMenuItem(
  103. key: 'TJDJ',
  104. title: "体检列表",
  105. routeName: "/registrationList",
  106. iconWidget: _PatientAddIconProxyWidget(
  107. baseIcon: _buildImgIcon('healthRecord.png'),
  108. ),
  109. ),
  110. HomeMenuItem(
  111. key: 'TJYY',
  112. title: "预约管理",
  113. routeName: "/appointment",
  114. iconWidget: _PatientAddIconProxyWidget(
  115. baseIcon: _buildImgIcon('diagnosisDisplay.png'),
  116. ),
  117. ),
  118. HomeMenuItem(
  119. key: 'TJJCJC',
  120. title: "基础检查",
  121. routeName: "/basicCheck",
  122. iconWidget: _PatientAddIconProxyWidget(
  123. baseIcon: _buildImgIcon('patientList.png'),
  124. ),
  125. ),
  126. HomeMenuItem(
  127. key: 'TJXD',
  128. title: "心电",
  129. routeName: "/electrocardiogram",
  130. iconWidget: _PatientAddIconProxyWidget(
  131. baseIcon: _buildImgIcon('populationFollowUp.png'),
  132. ),
  133. ),
  134. HomeMenuItem(
  135. key: 'TJNY',
  136. title: "尿常规",
  137. routeName: "/ncg",
  138. iconWidget: _PatientAddIconProxyWidget(
  139. baseIcon: _buildImgIcon('populationFollowUp.png'),
  140. ),
  141. ),
  142. HomeMenuItem(
  143. key: 'TJXCG',
  144. title: "血常规",
  145. routeName: "/bloodTest",
  146. iconWidget: _PatientAddIconProxyWidget(
  147. baseIcon: _buildImgIcon('populationFollowUp.png'),
  148. ),
  149. ),
  150. HomeMenuItem(
  151. key: 'TJSH',
  152. title: "生化",
  153. routeName: "/biochemistryTest",
  154. iconWidget: _PatientAddIconProxyWidget(
  155. baseIcon: _buildImgIcon('populationFollowUp.png'),
  156. ),
  157. ),
  158. // HomeMenuItem(
  159. // key: 'SZZX',
  160. // title: "设置中心",
  161. // routeName: "/settings",
  162. // iconData: Icons.settings,
  163. // ),
  164. HomeMenuItem(
  165. key: 'TJCS',
  166. routeName: '/remedicalRecordView',
  167. title: '超声',
  168. iconWidget: _PatientAddIconProxyWidget(
  169. baseIcon: _buildImgIcon('populationFollowUp.png'),
  170. ),
  171. ),
  172. // HomeMenuItem(
  173. // key: 'YCBG',
  174. // routeName: '/remedicalRecordView',
  175. // title: '异常报告',
  176. // iconWidget: _PatientAddIconProxyWidget(
  177. // baseIcon: _buildImgIcon('populationFollowUp.png'),
  178. // ),
  179. // ),
  180. ];
  181. /// 登出
  182. Future<void> logOut() async {
  183. final result = await Get.find<IAccountManager>().logout();
  184. if (result) {
  185. Get.offAllNamed("/login");
  186. }
  187. }
  188. // Future<void> changeMedical() async {
  189. // try {
  190. // Get.offAllNamed("/medical", id: 1001);
  191. // } catch (e) {}
  192. // }
  193. void onlineChanged(_, bool isOnline) {
  194. state.isOnline = isOnline;
  195. }
  196. @override
  197. void onInit() {
  198. initMenus();
  199. super.onInit();
  200. }
  201. initAddListener() {
  202. netChecker.onlineChangedEvent.addListener(onlineChanged);
  203. }
  204. netCheckerRemoveListener() {
  205. netChecker.onlineChangedEvent.removeListener(onlineChanged);
  206. }
  207. /// 切换活动菜单
  208. void switchActiveMenu(HomeMenuItem data) async {
  209. if (!kIsOnline) {
  210. // 无网络时,根据仅在线支持的菜单名单判断,进行阻断和提示
  211. if (_onlineOnlyMenuRouteNames.contains(data.routeName)) {
  212. PromptBox.toast("请检查网络连接");
  213. return;
  214. }
  215. }
  216. if (state.currentSelectMenu != data.routeName) {
  217. switchNavByName(data.routeName);
  218. }
  219. }
  220. void switchNavByName(String name, [Map? patientInfo]) {
  221. if (Store.user.currentSelectPatientInfo == null) {
  222. /// 有些路由跳转之前需要判断有没有选择居民
  223. if (["/patient/detail"].contains(name)) {
  224. PromptBox.toast("当前未选择居民,请先选择居民后再操作。");
  225. return;
  226. }
  227. }
  228. state.currentSelectMenu = name;
  229. NavGotoHelper.goto(name, patientInfo);
  230. }
  231. void initMenus() {
  232. if (Store.user.menuPermissionList?.isNotEmpty ?? false) {
  233. List<HomeMenuItem> menuItems = [];
  234. Store.user.menuPermissionList?.forEach((element) {
  235. for (var item in homeMenuItems) {
  236. if (item.key == element.code) {
  237. menuItems.add(item);
  238. }
  239. }
  240. });
  241. if (!menuItems.any((element) => element.key == "JKJC")) {
  242. state.currentSelectMenu = menuItems.first.routeName;
  243. }
  244. state.menuItems = menuItems;
  245. } else {
  246. // state.menuItems = homeMenuItems;
  247. state.menuItems = [
  248. HomeMenuItem(
  249. key: 'XJDA',
  250. title: "新建档案",
  251. routeName: "/patient/create",
  252. // TODO: 图addHealthRecord.png 不太合适,先层叠这样显示
  253. iconWidget: _PatientAddIconProxyWidget(
  254. baseIcon: _buildImgIcon('healthRecord.png'),
  255. ),
  256. ),
  257. HomeMenuItem(
  258. key: 'JMLB',
  259. title: "居民列表",
  260. routeName: "/patient/list",
  261. iconWidget: _buildImgIcon('patientList.png'),
  262. ),
  263. HomeMenuItem(
  264. key: 'JKDA',
  265. title: "健康档案",
  266. routeName: "/patient/detail",
  267. iconWidget: _buildImgIcon('healthRecord.png'),
  268. ),
  269. HomeMenuItem(
  270. key: 'JKJC',
  271. title: "健康检测",
  272. routeName: "/medical",
  273. iconWidget: _buildImgIcon('diagnosisDisplay.png'),
  274. ),
  275. ];
  276. }
  277. }
  278. void updateMenus() {
  279. if (Store.user.menuPermissionList?.isNotEmpty ?? false) {
  280. List<HomeMenuItem> menuItems = [];
  281. Store.user.menuPermissionList?.forEach((element) {
  282. for (var item in homeMenuItems) {
  283. if (item.key == element.code) {
  284. menuItems.add(item);
  285. }
  286. }
  287. });
  288. state.menuItems = menuItems;
  289. } else {
  290. state.menuItems = [
  291. HomeMenuItem(
  292. key: 'XJDA',
  293. title: "新建档案",
  294. routeName: "/patient/create",
  295. // TODO: 图addHealthRecord.png 不太合适,先层叠这样显示
  296. iconWidget: _PatientAddIconProxyWidget(
  297. baseIcon: _buildImgIcon('healthRecord.png'),
  298. ),
  299. ),
  300. HomeMenuItem(
  301. key: 'JMLB',
  302. title: "居民列表",
  303. routeName: "/patient/list",
  304. iconWidget: _buildImgIcon('patientList.png'),
  305. ),
  306. HomeMenuItem(
  307. key: 'JKDA',
  308. title: "健康档案",
  309. routeName: "/patient/detail",
  310. iconWidget: _buildImgIcon('healthRecord.png'),
  311. ),
  312. HomeMenuItem(
  313. key: 'JKJC',
  314. title: "健康检测",
  315. routeName: "/medical",
  316. iconWidget: _buildImgIcon('diagnosisDisplay.png'),
  317. ),
  318. // HomeMenuItem(
  319. // key: 'SZZX',
  320. // title: "设置中心",
  321. // routeName: "/settings",
  322. // iconData: Icons.settings,
  323. // ),
  324. ];
  325. }
  326. }
  327. void onScanData(String code) async {
  328. print('$code');
  329. RegisterPersonInfoDTO? registerPersonInfo =
  330. await _patientManager.getRegisterPersonInfoByPhysicalExamNumberAsync(
  331. physicalExamNumber: code,
  332. );
  333. PatientDTO? patientInfo = PatientDTO();
  334. if (registerPersonInfo != null &&
  335. registerPersonInfo.physicalExamNumber != null) {
  336. patientInfo.code = registerPersonInfo.code;
  337. patientInfo.patientName = registerPersonInfo.name;
  338. Store.user.currentSelectRegisterPersonInfo = registerPersonInfo;
  339. Store.user.currentSelectPatientInfo = patientInfo;
  340. }
  341. Get.back();
  342. onScanSwitchPage(state.currentSelectMenu);
  343. print(state.currentSelectMenu);
  344. }
  345. void onScanSwitchPage(String routeName) {
  346. switch (routeName) {
  347. case "/electrocardiogram":
  348. Get.dialog(_buildMedical());
  349. return;
  350. }
  351. }
  352. Widget _buildMedical() {
  353. return Scaffold(
  354. appBar: VAppBar(
  355. titleText: "体检心电",
  356. ),
  357. body: Container(
  358. color: Colors.white,
  359. child: Stack(
  360. children: [
  361. Row(
  362. children: [
  363. _buildDeviceImage(DeviceTypes.TWELVEHEART),
  364. _buildMedicalInput(DeviceTypes.TWELVEHEART),
  365. ],
  366. ),
  367. Positioned(
  368. right: 16,
  369. bottom: 16,
  370. child: _buildSaveButton(),
  371. ),
  372. ],
  373. ),
  374. ),
  375. );
  376. }
  377. Widget _buildMedicalInput(String? currentTab) {
  378. return Expanded(
  379. flex: currentTab == DeviceTypes.TWELVEHEART ? 18 : 11,
  380. child: Stack(
  381. children: [
  382. Container(
  383. padding: const EdgeInsets.all(16),
  384. child: Column(
  385. children: [
  386. _buildContent(),
  387. ],
  388. ),
  389. ),
  390. ],
  391. ),
  392. );
  393. }
  394. String _deviceImageUrl(String? currentTab) {
  395. switch (currentTab) {
  396. case DeviceTypes.TEMP:
  397. return 'assets/images/healthCheck/temp.png';
  398. case DeviceTypes.SUGAR:
  399. return 'assets/images/healthCheck/sugar.png';
  400. case DeviceTypes.NIBP:
  401. return 'assets/images/healthCheck/nibp.png';
  402. case DeviceTypes.SPO2:
  403. return 'assets/images/healthCheck/spo2.png';
  404. case DeviceTypes.WEIGHT:
  405. return 'assets/images/healthCheck/bmi.png';
  406. case DeviceTypes.URINE:
  407. return 'assets/images/healthCheck/urine.png';
  408. case DeviceTypes.WAIST:
  409. return 'assets/images/healthCheck/whb.png';
  410. default:
  411. return 'assets/images/exam/normalMeasurementChart.png';
  412. }
  413. }
  414. Widget _buildDeviceImage(String? currentTab) {
  415. if (currentTab == DeviceTypes.TWELVEHEART) {
  416. return const SizedBox();
  417. }
  418. return Expanded(
  419. flex: 7,
  420. child: Container(
  421. alignment: Alignment.topCenter,
  422. margin: const EdgeInsets.all(16).copyWith(top: 10),
  423. child: SizedBox(),
  424. ),
  425. );
  426. }
  427. Widget _buildSaveButton() {
  428. return Obx(() {
  429. if (Store.user.currentSelectRegisterPersonInfo == null) {
  430. return const SizedBox();
  431. }
  432. return FloatingActionButton.extended(
  433. // backgroundColor: Theme.of(context).primaryColor,
  434. onPressed: () {
  435. // advanceDebounce(
  436. // () => controller.createHeart(
  437. // Store.user.currentSelectRegisterPersonInfo?.physicalExamNumber ??
  438. // '',
  439. // 'HEIECG',
  440. // ),
  441. // "createCheckup",
  442. // 1500,
  443. // );
  444. },
  445. label: const SizedBox(
  446. width: 240,
  447. height: 60,
  448. child: Center(
  449. child: Text(
  450. '提交',
  451. style: TextStyle(
  452. fontSize: 26,
  453. color: Colors.white,
  454. ),
  455. ),
  456. ),
  457. ),
  458. );
  459. });
  460. }
  461. Widget _buildContent() {
  462. return const TwelveHeartRate();
  463. }
  464. }
  465. class _PatientAddIconProxyWidget extends StatelessWidget {
  466. final Widget baseIcon;
  467. const _PatientAddIconProxyWidget({
  468. Key? key,
  469. required this.baseIcon,
  470. }) : super(key: key);
  471. @override
  472. Widget build(BuildContext context) {
  473. return Stack(
  474. children: [
  475. baseIcon,
  476. const Positioned(
  477. right: 0,
  478. top: 0,
  479. child: Icon(
  480. Icons.add,
  481. size: 16,
  482. color: Colors.white,
  483. ),
  484. )
  485. ],
  486. );
  487. }
  488. }