view.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:intl/intl.dart';
  4. import 'package:vitalapp/architecture/utils/prompt_box.dart';
  5. import 'package:vitalapp/components/alert_dialog.dart';
  6. import 'package:vitalapp/components/appbar.dart';
  7. import 'package:vitalapp/components/dialog_table.dart';
  8. import 'package:vitalapp/consts/styles.dart';
  9. import 'package:vitalapp/global.dart';
  10. import 'package:vitalapp/managers/interfaces/models/diagnosis_aggregation_record_model.dart';
  11. import 'package:vitalapp/pages/medical/records/controller.dart';
  12. import 'package:vitalapp/pages/patient/list/widgets/status.dart';
  13. import 'package:vitalapp/pages/widgets/record_common_item.dart';
  14. class MedicalRecordsPage extends GetView<MedicalRecordsController> {
  15. const MedicalRecordsPage({super.key});
  16. @override
  17. Widget build(BuildContext context) {
  18. return GetBuilder(
  19. init: MedicalRecordsController(),
  20. id: "MedicalRecords",
  21. builder: (_) {
  22. return Scaffold(
  23. backgroundColor: const Color.fromRGBO(238, 238, 238, 1),
  24. appBar: VAppBar(
  25. titleWidget: const Text('健康检测记录'),
  26. ),
  27. body: Stack(
  28. children: [
  29. Row(
  30. mainAxisAlignment: MainAxisAlignment.start,
  31. crossAxisAlignment: CrossAxisAlignment.start,
  32. children: [
  33. _buildDiagram(),
  34. _buildListView(),
  35. ],
  36. )
  37. ],
  38. ),
  39. );
  40. },
  41. );
  42. }
  43. Widget _buildDiagram() {
  44. return Expanded(
  45. flex: 1,
  46. child: Padding(
  47. padding: const EdgeInsets.all(16.0).copyWith(right: 0),
  48. child: Container(
  49. // color: Colors.white,
  50. padding: const EdgeInsets.all(16),
  51. decoration: BoxDecoration(
  52. color: Colors.white,
  53. border: Border.all(
  54. color: Colors.white,
  55. ),
  56. borderRadius: GlobalStyles.borderRadius,
  57. ),
  58. child: Image.asset(
  59. 'assets/images/exam/normalMeasurementChart.png',
  60. height: double.infinity,
  61. fit: BoxFit.fitWidth,
  62. ),
  63. ),
  64. ),
  65. );
  66. }
  67. Widget _buildListView() {
  68. final scrollController = ScrollController();
  69. scrollController.addListener(
  70. () {
  71. // 如果滑动到底部
  72. try {
  73. if (scrollController.position.atEdge) {
  74. if (scrollController.position.pixels != 0) {
  75. if (controller.state.hasNextPage) {
  76. controller.loadNextPageList();
  77. }
  78. }
  79. }
  80. } catch (e) {
  81. // logger.e("listViewScrollController exception:", e);
  82. }
  83. },
  84. );
  85. return Expanded(
  86. flex: 2,
  87. child: Padding(
  88. padding: const EdgeInsets.all(16.0),
  89. child: RefreshIndicator(
  90. child: Obx(() {
  91. final children = <Widget>[];
  92. for (var element in controller.state.dataList) {
  93. children.add(_MedicalRecordCard(
  94. dto: element,
  95. ));
  96. }
  97. return controller.state.dataList.isEmpty
  98. ? Container(
  99. margin: const EdgeInsets.only(top: 80),
  100. child: Column(
  101. children: [
  102. Center(
  103. child: Image.asset(
  104. "assets/images/no_data.png",
  105. width: 300,
  106. height: 300,
  107. fit: BoxFit.cover,
  108. ),
  109. ),
  110. const Text(
  111. "暂无数据,先看看别的吧",
  112. style: TextStyle(fontSize: 18),
  113. ),
  114. ],
  115. ),
  116. )
  117. : Scrollbar(
  118. trackVisibility: true,
  119. controller: scrollController,
  120. child: GridView(
  121. shrinkWrap: true,
  122. controller: scrollController,
  123. gridDelegate:
  124. const SliverGridDelegateWithFixedCrossAxisCount(
  125. crossAxisCount: 1,
  126. mainAxisSpacing: 16,
  127. crossAxisSpacing: 20,
  128. childAspectRatio: 1080 / 180,
  129. ),
  130. children: children,
  131. ),
  132. );
  133. }),
  134. onRefresh: () async {}),
  135. ),
  136. );
  137. }
  138. }
  139. class _MedicalRecordCard extends GetView<MedicalRecordsController> {
  140. final DiagnosisAggregationRecordModel dto;
  141. const _MedicalRecordCard({required this.dto});
  142. @override
  143. Widget build(BuildContext context) {
  144. bool canDelete = kIsOnline || dto.isExistLocalData == true;
  145. final body = Stack(
  146. children: [
  147. Row(
  148. children: [
  149. Expanded(
  150. flex: 10,
  151. child: Container(
  152. padding: const EdgeInsets.symmetric(
  153. horizontal: 30,
  154. vertical: 12,
  155. ),
  156. child: Column(
  157. crossAxisAlignment: CrossAxisAlignment.start,
  158. children: [
  159. const SizedBox(
  160. height: 8,
  161. ),
  162. Wrap(
  163. alignment: WrapAlignment.start,
  164. spacing: 20,
  165. runSpacing: 8,
  166. children: [
  167. RecordCommonItem(
  168. itemName: '姓名',
  169. itemValue: dto.patientName ?? '',
  170. fontSize: 18,
  171. ),
  172. ],
  173. ),
  174. const SizedBox(
  175. height: 20,
  176. ),
  177. Wrap(
  178. alignment: WrapAlignment.start,
  179. spacing: 20,
  180. runSpacing: 8,
  181. children: [
  182. SizedBox(
  183. width: 250,
  184. child: RecordCommonItem(
  185. itemName: '检查医生',
  186. itemValue: dto.doctorName ?? '',
  187. fontSize: 18,
  188. ),
  189. ),
  190. RecordCommonItem(
  191. itemName: '诊断时间',
  192. itemValue: DateFormat("yyyy-MM-dd HH:mm:ss")
  193. .format(dto.diagnosisTime!.toLocal()),
  194. fontSize: 18,
  195. ),
  196. ],
  197. )
  198. ],
  199. ),
  200. ),
  201. ),
  202. Expanded(
  203. flex: 1,
  204. child: Icon(
  205. Icons.keyboard_arrow_right,
  206. size: 64,
  207. color: Colors.grey.shade400,
  208. ),
  209. ),
  210. ],
  211. ),
  212. Positioned(
  213. top: 16,
  214. right: 0,
  215. child: _diagnosisSignStatusTag(
  216. dto: dto,
  217. ),
  218. ),
  219. if (canDelete)
  220. Positioned(
  221. bottom: 0,
  222. right: 0,
  223. child: _DiagnosisRemoveMarkButton(dto: dto),
  224. ),
  225. if (dto.isExistLocalData == true)
  226. Positioned(
  227. top: 0,
  228. bottom: 0,
  229. right: 96,
  230. child: _DiagnosisEidtButton(dto: dto),
  231. ),
  232. ],
  233. );
  234. return Material(
  235. borderRadius: GlobalStyles.borderRadius,
  236. child: Ink(
  237. decoration: BoxDecoration(
  238. color: Colors.white,
  239. borderRadius: GlobalStyles.borderRadius,
  240. ),
  241. child: InkWell(
  242. borderRadius: GlobalStyles.borderRadius,
  243. onTap: () async {
  244. var result = await controller.getTableData(dto);
  245. await VDialogTable(
  246. isExistLocalData: dto.isExistLocalData,
  247. title: '数据展示',
  248. columnNames: const ['序号', '检测项目', '检测结果', '单位'],
  249. tableData: result,
  250. diagnosisTime: DateFormat("yyyy-MM-dd HH:mm:ss")
  251. .format(dto.diagnosisTime!.toLocal()),
  252. ).show();
  253. },
  254. child: body,
  255. ),
  256. ),
  257. );
  258. }
  259. }
  260. // ignore: camel_case_types
  261. class _diagnosisSignStatusTag extends StatelessWidget {
  262. final DiagnosisAggregationRecordModel dto;
  263. const _diagnosisSignStatusTag({required this.dto});
  264. @override
  265. Widget build(BuildContext context) {
  266. return Container(
  267. alignment: Alignment.centerRight,
  268. width: 120,
  269. child: StatusLabel(
  270. title: dto.isExistLocalData! ? "未上传" : "已上传",
  271. color: dto.isExistLocalData! ? Colors.red : Colors.green,
  272. ),
  273. );
  274. }
  275. }
  276. class _DiagnosisRemoveMarkButton extends StatelessWidget {
  277. final DiagnosisAggregationRecordModel dto;
  278. const _DiagnosisRemoveMarkButton({required this.dto});
  279. @override
  280. Widget build(BuildContext context) {
  281. return GestureDetector(
  282. onTap: () {
  283. Get.dialog(
  284. VAlertDialog(
  285. title: "提示",
  286. width: 260,
  287. content: Container(
  288. height: 32,
  289. padding: const EdgeInsets.symmetric(horizontal: 24),
  290. alignment: Alignment.center,
  291. child: Text(
  292. "确定删除此记录?",
  293. style: TextStyle(fontSize: 20),
  294. ),
  295. ),
  296. onConfirm: () async {
  297. Get.find<MedicalRecordsController>().removeRecord(dto.appDataId!);
  298. Get.back();
  299. },
  300. ),
  301. barrierDismissible: false,
  302. barrierColor: Colors.black.withOpacity(.4),
  303. );
  304. },
  305. child: Container(
  306. padding: EdgeInsets.only(top: 10, left: 10, right: 2, bottom: 2),
  307. decoration: BoxDecoration(
  308. color: Colors.red,
  309. borderRadius: BorderRadius.only(
  310. topLeft: Radius.circular(32),
  311. bottomRight: Radius.circular(8),
  312. ),
  313. ),
  314. child: Icon(
  315. Icons.delete_forever,
  316. size: 26,
  317. color: Colors.white,
  318. ),
  319. ),
  320. );
  321. }
  322. }
  323. class _DiagnosisEidtButton extends StatelessWidget {
  324. final DiagnosisAggregationRecordModel dto;
  325. const _DiagnosisEidtButton({required this.dto});
  326. @override
  327. Widget build(BuildContext context) {
  328. return Container(
  329. alignment: Alignment.center,
  330. child: IconButton(
  331. onPressed: () {
  332. Get.find<MedicalRecordsController>().editRecord(dto);
  333. },
  334. iconSize: 48,
  335. color: Colors.grey.shade400,
  336. icon: Icon(Icons.edit_note),
  337. ),
  338. );
  339. }
  340. }