view.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. import 'package:fis_jsonrpc/rpc.dart';
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:get/get.dart';
  5. import 'package:intl/intl.dart';
  6. import 'package:vitalapp/architecture/utils/prompt_box.dart';
  7. import 'package:vitalapp/components/alert_dialog.dart';
  8. import 'package:vitalapp/components/appbar.dart';
  9. import 'package:vitalapp/components/button.dart';
  10. import 'package:vitalapp/components/qr_code_with_logo/qr_code_with_logo.dart';
  11. import 'package:vitalapp/consts/styles.dart';
  12. import 'package:vitalapp/database/entities/defines.dart';
  13. import 'package:vitalapp/pages/check/follow_up/widgets/follow_up_from.dart';
  14. import 'package:vitalapp/pages/check/follow_up_record/controller.dart';
  15. import 'package:vitalapp/pages/check/maternal_health_management/widgets/delete_follow_up_record_button.dart';
  16. import 'package:vitalapp/pages/check/prescription/prescription_form_keys.dart';
  17. import 'package:vitalapp/pages/check/widgets/configurable_card.dart';
  18. import 'package:vitalapp/pages/form/form_info.dart';
  19. import 'package:vitalapp/pages/medical/controller.dart';
  20. import 'package:vitalapp/pages/patient/list/widgets/status.dart';
  21. import 'package:vitalapp/pages/widgets/record_common_item.dart';
  22. class FollowUpRecordPage extends GetView<FollowUpRecordController> {
  23. const FollowUpRecordPage({
  24. Key? key,
  25. required this.followUpType,
  26. }) : super(key: key);
  27. final String followUpType;
  28. @override
  29. Widget build(BuildContext context) {
  30. return GetBuilder(
  31. init: FollowUpRecordController(
  32. followUpType: followUpType,
  33. ),
  34. id: "FollowUpRecord",
  35. builder: (_) {
  36. return Scaffold(
  37. backgroundColor: const Color.fromRGBO(238, 238, 238, 1),
  38. appBar: VAppBar(
  39. titleWidget: Text(controller.getFollowUpValueByKey(followUpType)),
  40. actions: [
  41. IconButton(
  42. onPressed: () {
  43. _changePage(followUpType);
  44. },
  45. icon: Icon(
  46. Icons.add,
  47. size: 48,
  48. ),
  49. ),
  50. SizedBox(
  51. width: 8,
  52. )
  53. ],
  54. ),
  55. body: Stack(
  56. children: [
  57. Row(
  58. mainAxisAlignment: MainAxisAlignment.start,
  59. crossAxisAlignment: CrossAxisAlignment.start,
  60. children: [
  61. _buildDiagram(),
  62. _buildListView(),
  63. ],
  64. )
  65. ],
  66. ),
  67. );
  68. });
  69. }
  70. void _changePage(String key) async {
  71. FormInfo.instance.formValue.clear();
  72. /// TODO BAKA 急需求 后面改掉
  73. await Get.put(MedicalController());
  74. controller.followUpController.state.followUpTime = DateTime.now();
  75. controller.followUpController.state.nextFollowUpTime = null;
  76. controller.followUpController.state.followUpMode =
  77. FollowUpModeEnum.Outpatient;
  78. controller.followUpController.state.followUpPhoto = '';
  79. await Get.to(
  80. ConfigurableCard(
  81. cardKey: key,
  82. callBack: (key, templateCode, data, prescriptionKey) async {
  83. final result = await controller.followUpController.createFollowUp(
  84. key,
  85. templateCode,
  86. data,
  87. prescriptionKey.toString(),
  88. );
  89. return result;
  90. },
  91. followUpWidget: FollowUpFrom(
  92. cardKey: key,
  93. ),
  94. ),
  95. transition: Transition.rightToLeft,
  96. );
  97. await controller.getFollowUpRecordList();
  98. await Get.find<MedicalController>().initRecordDataState();
  99. await Get.delete<MedicalController>();
  100. }
  101. Widget _buildDiagram() {
  102. return Expanded(
  103. flex: 1,
  104. child: Padding(
  105. padding: const EdgeInsets.all(16.0).copyWith(right: 0),
  106. child: Container(
  107. // color: Colors.white,
  108. padding: const EdgeInsets.all(16),
  109. decoration: BoxDecoration(
  110. color: Colors.white,
  111. border: Border.all(
  112. color: Colors.white,
  113. ),
  114. borderRadius: GlobalStyles.borderRadius,
  115. ),
  116. child: Image.asset(
  117. 'assets/images/exam/normalMeasurementChart.png',
  118. height: double.infinity,
  119. fit: BoxFit.fitWidth,
  120. ),
  121. ),
  122. ),
  123. );
  124. }
  125. Widget _buildListView() {
  126. return Expanded(
  127. flex: 2,
  128. child: Padding(
  129. padding: const EdgeInsets.all(16),
  130. child: RefreshIndicator(
  131. child: Obx(
  132. () {
  133. final list = controller.state.followUpDTOList;
  134. final children = <Widget>[];
  135. for (var i = 0; i < list.length; i++) {
  136. final dto = list[i];
  137. final offlineSyncArr = controller.offlineSyncTemp[i];
  138. final records = dto.followUpRecordDatas;
  139. if (records == null) {
  140. continue;
  141. }
  142. for (var j = 0; j < records.length; j++) {
  143. final data = records[j];
  144. OfflineDataSyncState? offlineSyncState;
  145. offlineSyncState = kIsWeb ? null : offlineSyncArr[j];
  146. children.add(
  147. _followUpRecordCard(
  148. index: j,
  149. dto: dto,
  150. dataDto: data,
  151. syncState: offlineSyncState,
  152. ),
  153. );
  154. }
  155. }
  156. return list.isEmpty
  157. ? Container(
  158. margin: const EdgeInsets.only(top: 80),
  159. child: Column(
  160. children: [
  161. Center(
  162. child: Image.asset(
  163. "assets/images/no_data.png",
  164. width: 300,
  165. height: 300,
  166. fit: BoxFit.cover,
  167. ),
  168. ),
  169. const Text(
  170. "暂无数据,先看看别的吧",
  171. style: TextStyle(fontSize: 18),
  172. ),
  173. ],
  174. ),
  175. )
  176. : GridView(
  177. gridDelegate:
  178. const SliverGridDelegateWithFixedCrossAxisCount(
  179. crossAxisCount: 1,
  180. mainAxisSpacing: 16,
  181. crossAxisSpacing: 20,
  182. childAspectRatio: 900 / 180,
  183. ),
  184. children: children,
  185. );
  186. },
  187. ),
  188. onRefresh: () async {}),
  189. ),
  190. );
  191. }
  192. }
  193. // ignore: camel_case_types
  194. class _followUpRecordCard extends StatelessWidget {
  195. final FollowUpRecordDTO dto;
  196. final FollowUpRecordDataDTO dataDto;
  197. final int index;
  198. final OfflineDataSyncState? syncState; // TODO temp
  199. _followUpRecordCard({
  200. required this.dto,
  201. required this.dataDto,
  202. required this.index,
  203. this.syncState,
  204. });
  205. final controller = Get.find<FollowUpRecordController>();
  206. @override
  207. Widget build(BuildContext context) {
  208. final body = Stack(
  209. children: [
  210. Row(
  211. children: [
  212. Expanded(
  213. flex: 10,
  214. child: Container(
  215. padding: const EdgeInsets.symmetric(
  216. horizontal: 30,
  217. vertical: 12,
  218. ),
  219. child: Column(
  220. crossAxisAlignment: CrossAxisAlignment.start,
  221. children: [
  222. const SizedBox(
  223. height: 8,
  224. ),
  225. LayoutBuilder(builder: (context, c) {
  226. final width = c.maxWidth - 100;
  227. return SizedBox(
  228. width: width,
  229. child: _buildBaseInfoRow(),
  230. );
  231. }),
  232. const SizedBox(
  233. height: 20,
  234. ),
  235. Wrap(
  236. alignment: WrapAlignment.start,
  237. spacing: 20,
  238. runSpacing: 8,
  239. children: [
  240. SizedBox(
  241. width: 250,
  242. child: RecordCommonItem(
  243. itemName: '姓名',
  244. itemValue: dto.patientName ?? "",
  245. fontSize: 18,
  246. ),
  247. ),
  248. RecordCommonItem(
  249. itemName: '随访类型',
  250. itemValue:
  251. controller.getFollowUpMode(dataDto.followUpMode),
  252. fontSize: 18,
  253. ),
  254. ],
  255. ),
  256. const SizedBox(
  257. height: 20,
  258. ),
  259. Wrap(
  260. alignment: WrapAlignment.start,
  261. spacing: 20,
  262. runSpacing: 8,
  263. children: [
  264. SizedBox(
  265. width: 250,
  266. child: RecordCommonItem(
  267. itemName: '随访医生',
  268. itemValue: dataDto.followUpDoctor ?? "",
  269. fontSize: 18,
  270. ),
  271. ),
  272. RecordCommonItem(
  273. itemName: '随访时间',
  274. itemValue: dataDto.followUpTime != null
  275. ? DateFormat("yyyy-MM-dd")
  276. .format(dataDto.followUpTime!.toLocal())
  277. : "",
  278. fontSize: 18,
  279. ),
  280. ],
  281. )
  282. ],
  283. ),
  284. ),
  285. ),
  286. // Expanded(
  287. // child: IconButton(
  288. // onPressed: () {
  289. // controller.toCheckPage(dataDto, isCreateFromOldDto: true);
  290. // },
  291. // icon: Icon(
  292. // Icons.add,
  293. // size: 56,
  294. // color: Colors.grey.shade400,
  295. // ),
  296. // ),
  297. // )
  298. ],
  299. ),
  300. Positioned(
  301. bottom: 16,
  302. right: 160,
  303. child: _buildShareButton(),
  304. ),
  305. Positioned(
  306. bottom: 16,
  307. right: 40,
  308. child: _buildCopyAddButton(dataDto),
  309. ),
  310. Positioned(
  311. top: 16,
  312. right: 0,
  313. child: _FollowUpRecordSignStatusTag(
  314. dataDto: dataDto,
  315. ),
  316. ),
  317. // if(dataDto)
  318. Positioned(
  319. top: 16,
  320. right: 100,
  321. child: _OfflineSyncTag(syncState: syncState),
  322. ),
  323. // Positioned(
  324. // bottom: 16,
  325. // right: 12,
  326. // child: IconButton(
  327. // icon: Icon(
  328. // Icons.edit,
  329. // size: 26,
  330. // color: Theme.of(context).primaryColor,
  331. // ),
  332. // onPressed: () {
  333. // controller.toCheckPage(dataDto); //跳转到随访页面
  334. // },
  335. // ),
  336. // ),
  337. Positioned(
  338. bottom: 0,
  339. right: 0,
  340. child: DeleteFollowUpRecordButton(dataDto.code!, dataDto.key!, () {
  341. controller.getFollowUpRecordList();
  342. }),
  343. ),
  344. ],
  345. );
  346. return Material(
  347. borderRadius: GlobalStyles.borderRadius,
  348. child: Ink(
  349. decoration: BoxDecoration(
  350. color: Colors.white,
  351. borderRadius: GlobalStyles.borderRadius,
  352. ),
  353. child: InkWell(
  354. borderRadius: GlobalStyles.borderRadius,
  355. onTap: () {
  356. // controller.toFollowUpDetailPage(index, dto);
  357. controller.toCheckPage(dataDto); //跳转到随访页面
  358. },
  359. child: body,
  360. ),
  361. ),
  362. );
  363. }
  364. Widget _buildBaseInfoRow() {
  365. return SizedBox(
  366. child: RecordCommonItem(
  367. itemName: '随访病症',
  368. itemValue: controller.getFollowUpValueByKey(dataDto.key ?? ""),
  369. fontSize: 20,
  370. ),
  371. );
  372. }
  373. Widget _buildShareButton() {
  374. return Container(
  375. width: 120,
  376. height: 50,
  377. alignment: Alignment.bottomRight,
  378. child: VButton(
  379. onTap: () async {
  380. // 1111
  381. Map<String, dynamic> preview =
  382. await controller.sharePrescription(dataDto);
  383. const designWidth = 1280.0; // 设计尺寸宽度:1280
  384. final width = Get.width;
  385. final scale = width / designWidth; // 计算缩放比例
  386. if (preview["previewUrl"] == "" || preview["previewUrl"] == null) {
  387. PromptBox.toast("暂无处方");
  388. return;
  389. }
  390. Get.dialog(
  391. VAlertDialog(
  392. title: "分享处方",
  393. width: width * 0.4 / scale,
  394. content: _buildQrcode(preview),
  395. ),
  396. );
  397. },
  398. label: "分享",
  399. ),
  400. );
  401. }
  402. Widget _buildCopyAddButton(FollowUpRecordDataDTO dataDto) {
  403. return Container(
  404. width: 100,
  405. height: 50,
  406. alignment: Alignment.bottomRight,
  407. child: VButton(
  408. onTap: () {
  409. controller.toCheckPage(dataDto, isCreateFromOldDto: true);
  410. },
  411. label: "复制新增",
  412. ),
  413. );
  414. }
  415. ///二维码
  416. Widget _buildQrcode(Map<String, dynamic> qr) {
  417. return Container(
  418. child: VQRCodeWithLogo(
  419. qr["previewUrl"],
  420. codeStatement: PrescriptionFormKeys.AllFormKeys[qr["key"]] ?? '',
  421. operationStatement: "复制链接",
  422. size: 200,
  423. operationSuccessCallback: () {
  424. PromptBox.toast("复制成功");
  425. },
  426. ),
  427. );
  428. }
  429. }
  430. // ignore: camel_case_types
  431. class _FollowUpRecordSignStatusTag extends StatelessWidget {
  432. final FollowUpRecordDataDTO dataDto;
  433. _FollowUpRecordSignStatusTag({required this.dataDto});
  434. final controller = Get.find<FollowUpRecordController>();
  435. @override
  436. Widget build(BuildContext context) {
  437. return Container(
  438. alignment: Alignment.centerRight,
  439. width: 120,
  440. child: StatusLabel(
  441. title: controller.followUpStateTransition(dataDto.followUpState),
  442. color: controller.followUpStateColors(dataDto.followUpState),
  443. ),
  444. );
  445. }
  446. }
  447. class _OfflineSyncTag extends StatelessWidget {
  448. final OfflineDataSyncState? syncState;
  449. const _OfflineSyncTag({required this.syncState});
  450. @override
  451. Widget build(BuildContext context) {
  452. if (syncState == null || syncState == OfflineDataSyncState.success) {
  453. return const SizedBox();
  454. }
  455. return Container(
  456. height: 30,
  457. alignment: Alignment.center,
  458. padding: const EdgeInsets.symmetric(horizontal: 16),
  459. decoration: BoxDecoration(
  460. borderRadius: BorderRadius.circular(16),
  461. color: Colors.red,
  462. ),
  463. child: Text(
  464. syncState!.getDescription(),
  465. style: const TextStyle(color: Colors.white, fontSize: 14),
  466. ),
  467. );
  468. }
  469. }