view.dart 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:vitalapp/architecture/utils/advance_debounce.dart';
  4. import 'package:vitalapp/components/alert_dialog.dart';
  5. import 'package:vitalapp/pages/medical/controllers/waist.dart';
  6. import 'package:vitalapp/pages/medical/widgets/heart_rate.dart';
  7. import 'package:vitalapp/pages/medical/widgets/twelve_ecg.dart';
  8. import 'package:vitalapp/pages/medical/widgets/urinalysis.dart';
  9. import 'package:vitalapp/pages/medical/widgets/waist.dart';
  10. import 'package:vnote_device_plugin/consts/types.dart';
  11. import 'package:vitalapp/components/button.dart';
  12. import 'package:vitalapp/pages/medical/controller.dart';
  13. import 'package:vitalapp/pages/medical/widgets/blood_pressure.dart';
  14. import 'package:vitalapp/pages/medical/widgets/blood_sugar.dart';
  15. import 'package:vitalapp/pages/medical/widgets/body_temperature.dart';
  16. import 'package:vitalapp/pages/medical/widgets/body_bmi.dart';
  17. import 'package:vitalapp/pages/medical/widgets/bool_oxygen.dart';
  18. import 'package:vitalapp/store/store.dart';
  19. class MedicalPage extends GetView<MedicalController> {
  20. const MedicalPage({super.key});
  21. @override
  22. Widget build(BuildContext context) {
  23. return Scaffold(
  24. resizeToAvoidBottomInset: false,
  25. body: SizedBox(height: double.maxFinite, child: _buildMedical()),
  26. floatingActionButton: _buildSaveButton(context),
  27. );
  28. }
  29. Widget _buildMedical() {
  30. return Row(
  31. children: [
  32. _buildMedicalMenus(),
  33. Obx(
  34. () => _buildDeviceImage(controller.state.currentTab),
  35. ),
  36. Obx(
  37. () => _buildMedicalInput(controller.state.currentTab),
  38. ),
  39. ],
  40. );
  41. }
  42. Widget _buildMedicalMenus() {
  43. return Expanded(
  44. flex: 3,
  45. child: Obx(
  46. () => Column(
  47. mainAxisAlignment: MainAxisAlignment.center,
  48. children: [
  49. ListView(
  50. shrinkWrap: true,
  51. children: controller.state.medicalMenuList
  52. .map(
  53. (e) => Material(
  54. borderRadius: const BorderRadius.only(
  55. topRight: Radius.circular(30),
  56. bottomRight: Radius.circular(30),
  57. ),
  58. child: Ink(
  59. decoration: const BoxDecoration(
  60. borderRadius: BorderRadius.only(
  61. topRight: Radius.circular(30),
  62. bottomRight: Radius.circular(30),
  63. ),
  64. ),
  65. child: InkWell(
  66. borderRadius: const BorderRadius.only(
  67. topRight: Radius.circular(30),
  68. bottomRight: Radius.circular(30),
  69. ),
  70. onTap: () {
  71. controller.state.currentTab = e.key;
  72. },
  73. child: Obx(
  74. () => _SideBar(
  75. title: e.diagnosticItem,
  76. isActive: controller.state.currentTab == e.key,
  77. ),
  78. )),
  79. ),
  80. ),
  81. )
  82. .toList(),
  83. ),
  84. ],
  85. ),
  86. ),
  87. );
  88. }
  89. Widget _buildMedicalInput(String? currentTab) {
  90. return Expanded(
  91. flex: currentTab == DeviceTypes.TWELVEHEART ? 18 : 11,
  92. child: Stack(
  93. children: [
  94. Container(
  95. padding: const EdgeInsets.all(16),
  96. child: Column(
  97. children: [
  98. _buildContent(),
  99. ],
  100. ),
  101. ),
  102. ],
  103. ),
  104. );
  105. }
  106. String _deviceImageUrl(String? currentTab) {
  107. switch (currentTab) {
  108. case DeviceTypes.TEMP:
  109. return 'assets/images/healthCheck/temp.png';
  110. case DeviceTypes.SUGAR:
  111. return 'assets/images/healthCheck/sugar.png';
  112. case DeviceTypes.NIBP:
  113. return 'assets/images/healthCheck/nibp.png';
  114. case DeviceTypes.SPO2:
  115. return 'assets/images/healthCheck/spo2.png';
  116. case DeviceTypes.WEIGHT:
  117. return 'assets/images/healthCheck/bmi.png';
  118. case DeviceTypes.URINE:
  119. return 'assets/images/healthCheck/urine.png';
  120. case DeviceTypes.WAIST:
  121. return 'assets/images/healthCheck/whb.png';
  122. default:
  123. return 'assets/images/exam/normalMeasurementChart.png';
  124. }
  125. }
  126. Widget _buildDeviceImage(String? currentTab) {
  127. if (currentTab == DeviceTypes.TWELVEHEART) {
  128. return const SizedBox();
  129. }
  130. return Expanded(
  131. flex: 7,
  132. child: Container(
  133. alignment: Alignment.topCenter,
  134. margin: const EdgeInsets.all(16).copyWith(top: 10),
  135. child: Obx(
  136. () => ClipRect(
  137. child: Align(
  138. alignment: Alignment.bottomCenter,
  139. heightFactor: 0.8,
  140. child: controller.state.currentTab != null
  141. ? Image.asset(
  142. _deviceImageUrl(controller.state.currentTab),
  143. height: double.infinity,
  144. fit: BoxFit.contain, // 设置图像的适应方式
  145. )
  146. : Container(),
  147. ),
  148. ),
  149. ),
  150. ),
  151. );
  152. }
  153. Widget _buildGenerateReport() {
  154. return Positioned(
  155. bottom: 100,
  156. left: 16,
  157. child: VButton(
  158. onTap: () {
  159. Get.dialog(
  160. VAlertDialog(
  161. title: '提示',
  162. content: Container(
  163. margin: const EdgeInsets.only(bottom: 20),
  164. child: const Text(
  165. '当前检测是否已完成,请确定是否结束本次检测',
  166. style: TextStyle(fontSize: 20),
  167. textAlign: TextAlign.center,
  168. ),
  169. ),
  170. showCancel: true,
  171. onConfirm: () {
  172. controller.saveCachedAppDataId();
  173. },
  174. ),
  175. );
  176. },
  177. child: const Text(
  178. '生成检测',
  179. style: TextStyle(fontSize: 26),
  180. ),
  181. ),
  182. );
  183. }
  184. Widget _buildSaveButton(BuildContext context) {
  185. return Obx(() {
  186. if (Store.user.currentSelectPatientInfo == null) {
  187. return const SizedBox();
  188. }
  189. return FloatingActionButton.extended(
  190. backgroundColor: Theme.of(context).primaryColor,
  191. onPressed: () {
  192. advanceDebounce(controller.createDiagnosis, "createDiagnosis", 1500);
  193. },
  194. label: const SizedBox(
  195. width: 240,
  196. height: 60,
  197. child: Center(
  198. child: Text(
  199. '提交',
  200. style: TextStyle(
  201. fontSize: 26,
  202. color: Colors.white,
  203. ),
  204. ),
  205. ),
  206. ),
  207. );
  208. });
  209. }
  210. Widget _buildContent() {
  211. return Obx(() {
  212. switch (controller.state.currentTab) {
  213. case DeviceTypes.TEMP:
  214. return const BodyTemperature();
  215. case DeviceTypes.SUGAR:
  216. return const BloodSugar();
  217. case DeviceTypes.NIBP:
  218. return const BloodPressure();
  219. case DeviceTypes.SPO2:
  220. return const BloodOxygen();
  221. case DeviceTypes.WEIGHT:
  222. return const BodyWeight();
  223. case DeviceTypes.URINE:
  224. return const Urinalysis();
  225. case DeviceTypes.HEART:
  226. return const HeartRate();
  227. case DeviceTypes.TWELVEHEART:
  228. return const TwelveHeartRate();
  229. case DeviceTypes.WAIST:
  230. // TODO: 待统一各检测项
  231. return GetBuilder(
  232. init: WaistDeviceController(),
  233. builder: (_) => const WaistView(),
  234. );
  235. default:
  236. return const SizedBox();
  237. }
  238. });
  239. }
  240. }
  241. class _SideBar extends StatelessWidget {
  242. const _SideBar({
  243. required this.title,
  244. this.isActive,
  245. });
  246. final String title;
  247. final bool? isActive;
  248. @override
  249. Widget build(BuildContext context) {
  250. return Container(
  251. alignment: Alignment.centerLeft,
  252. width: 156,
  253. child: Container(
  254. margin: const EdgeInsets.only(bottom: 2),
  255. decoration: BoxDecoration(
  256. color: isActive!
  257. ? Theme.of(context).primaryColor
  258. : Theme.of(context).primaryColor.withOpacity(.2),
  259. borderRadius: const BorderRadius.only(
  260. topRight: Radius.circular(30),
  261. bottomRight: Radius.circular(30),
  262. ),
  263. ),
  264. alignment: Alignment.center,
  265. width: 156,
  266. height: 60,
  267. child: Text(
  268. title,
  269. style: TextStyle(
  270. fontSize: 26,
  271. color: isActive! ? Colors.white : Colors.black,
  272. ),
  273. ),
  274. ),
  275. );
  276. }
  277. }