view.dart 14 KB

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