controller.dart 15 KB

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