import 'dart:convert'; import 'package:fis_common/index.dart'; import 'package:fis_common/logger/logger.dart'; import 'package:fis_jsonrpc/rpc.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:vitalapp/architecture/utils/compute_children_level.dart'; import 'package:vitalapp/architecture/utils/prompt_box.dart'; import 'package:vitalapp/components/button.dart'; import 'package:vitalapp/components/dialog_date.dart'; import 'package:vitalapp/components/dialog_gxy_medication.dart'; import 'package:vitalapp/components/dialog_input.dart'; import 'package:vitalapp/components/dialog_medication.dart'; import 'package:vitalapp/components/dialog_number.dart'; import 'package:vitalapp/components/dynamic_drawer.dart'; import 'package:vitalapp/managers/interfaces/cachedRecord.dart'; import 'package:vitalapp/managers/interfaces/follow_up.dart'; import 'package:vitalapp/managers/interfaces/template.dart'; import 'package:vitalapp/pages/check/models/form.dart'; import 'package:vitalapp/pages/check/prescription/prescription.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_child_anterior_fontanelle.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_blood_sugar.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_body_temperature.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_body_weight.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_boold_oxygen.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_check_box.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_checkbox_frequency.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_input.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_medication.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_number_input.dart'; import 'dart:math' as math; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_radio.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_radio_score.dart'; import 'package:vitalapp/pages/check/widgets/device_controller.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_table.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_toxic_substance.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_urinalys.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/follow_blood_pressure.dart'; import 'package:vitalapp/pages/check/widgets/exam_table/homecare_bed_history_from.dart'; import 'package:vitalapp/pages/check/widgets/exam_table/hospitalization_history_from.dart'; import 'package:vitalapp/pages/check/widgets/exam_table/inoculate_history_from.dart'; import 'package:vitalapp/pages/check/widgets/exam_table/main_medication_status_from.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_child_anterior_fontanelle_other.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_child_height_and_weight.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_child_radio_input.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_child_referral.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_medication.dart'; import 'package:vitalapp/pages/check/widgets/follow_up_configurable/follow_up_resident_detail.dart'; import 'package:vitalapp/pages/check/widgets/title_clip_path.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:vitalapp/pages/form/form_info.dart'; import 'package:vitalapp/store/store.dart'; class ConfigurableCard extends StatefulWidget { final String cardKey; final Future Function(String, String, dynamic, String?) callBack; final Widget? followUpWidget; final String? patientCode; final String? examData; final bool canPrescribe; final void Function(String, String, dynamic)? onClickPrescribe; const ConfigurableCard({ super.key, required this.cardKey, required this.callBack, this.followUpWidget, this.patientCode, this.examData, this.canPrescribe = false, this.onClickPrescribe, }); @override State createState() => _ConfigurableFormState(); } class _ConfigurableFormState extends State { /// 当前最新的模板的键值对 Map templateRelation = {}; String templateCode = ''; /// 当前模板数据 List currentTemplate = []; /// 当前title的下标 int currentTitleIndex = 0; /// 处方key String prescriptionKey = ''; Map formValue = {}; var scaffoldKey = GlobalKey(); final _templateManager = Get.find(); final _cachedRecordManager = Get.find(); final _followUpManager = Get.find(); // _followUpManager.onFollowMedicalData final arrowHeight = math.tan(120 / 180) * 19; List deviceList = ['Temp', 'GLU', 'NIBP', 'SpO2', 'BMI']; Map deviceCached = {}; Map currentTable = {}; @override void initState() { Get.put(DeviceController()); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { if (mounted) { initTemplate(); } }); _followUpManager.onFollowMedicalData.addListener(_setFollowUpData); super.initState(); switch (widget.cardKey) { case "TNB": prescriptionKey = "DiabetesPrescription"; break; case "GXY": prescriptionKey = "HypertensionPrescription"; break; case "FollowUpTuberculosisRecord": prescriptionKey = "TuberculosisPrescription"; break; case "YZJSZASFFW": prescriptionKey = "PsychiatricPrescription"; break; } } @override void dispose() { _followUpManager.onFollowMedicalData.removeListener(_setFollowUpData); super.dispose(); } Future initTemplate() async { Get.find().busy = true; await fetchTemplateIndex(); await fetchTemplate(widget.cardKey); await fetchTemplateData(); Get.find().busy = false; } /// 读取健康检测的缓存 Future readCachedRecord(String currentDevice) async { if (widget.patientCode == null) { return null; } String? value = await _cachedRecordManager.readCachedRecord( currentDevice, widget.patientCode!, 'ZLZS', ); return value; } /// 读取体检的缓存 Future readCachedCheck() async { if (widget.patientCode == null) { return null; } String? value = await _cachedRecordManager.readCachedRecord( widget.cardKey, widget.patientCode!, 'exam', ); return value; } Future readCached() async { for (var element in deviceList) { String? value = await readCachedRecord(element); if (value?.isNotEmpty ?? false) { deviceCached.addAll(jsonDecode(value!)); } } } Future fetchTemplateIndex() async { try { /// 获取模板的键值对 String? templates; templates = await _templateManager.readTemplateRelation('templateRelation'); templateRelation = jsonDecode(templates!); setState(() {}); } catch (error) { print('发生错误: $error'); } } Future loadJsonData() async { return await rootBundle.loadString('assets/data/3-6.json'); } Future fetchTemplateData() async { // / 这逻辑需要优化 if (widget.examData?.isNotEmpty ?? false) { formValue = jsonDecode(widget.examData!); return; } String? value = await readCachedCheck(); await readCached(); if (deviceCached.isNotEmpty) { formValue = deviceCached; setState(() {}); return; } if (value?.isNotEmpty ?? false) { formValue = jsonDecode(value!); } formValue.forEach( (key, value) { if (value is List) { formValue[key] = List.from(formValue[key]); } else if (value is List) { formValue[key] = List.from(formValue[key]); } }, ); setState(() {}); } Future loadJsonFromAssets(String filePath) async { String jsonString = await rootBundle.loadString(filePath); return jsonString; } Future fetchTemplateDebug() async { try { // if (templateRelation[key] == null) { // currentTemplate = []; // setState(() {}); // return; // } // var template = // await _templateManager.readTemplate(templateRelation[key]!); var template = await loadJsonData(); List> list = jsonDecode(template)["Content"].cast>(); for (var i in list) { if (i['children'] != null) { List currentChildren = []; for (var j in i['children']) { currentChildren.add(FormObject.fromJson(j)); } i['children'] = currentChildren; } var item = FormObject.fromJson(i); currentTemplate.add(item); } setState(() {}); } catch (error) { print('发生错误: $error'); } } Future fetchTemplate(String key) async { try { // String? templateContent = ""; // String? template = ""; // if (key == "FollowUpTuberculosisFirstRecord") { // templateContent = await loadJsonFromAssets('assets/${key}.json'); // } // if (templateRelation[key] == null && templateContent.isNullOrEmpty) { if (templateRelation[key] == null) { logger.i( "ConfigurableCard - fetchTemplate Template not exist key: $key."); currentTemplate = []; setState(() {}); if (!FPlatform.isWindows) { return; } } // if (templateContent.isNullOrEmpty) // template = await _templateManager.readTemplate(templateRelation[key]!); // if (templateContent.isNullOrEmpty) // templateContent = List> list = []; String? template; if (templateRelation.containsKey(key)) { templateCode = templateRelation[key]!; template = await _templateManager.readTemplate(templateCode); } if (template == null) { var json = await loadJsonFromAssets('assets/templates/${key}.json'); list = jsonDecode(json)["Content"].cast>(); } else { var templateContent = TemplateDTO.fromJson(jsonDecode(template)).templateContent!; list = jsonDecode(templateContent).cast>(); } for (var i in list) { if (i['children'] != null) { List currentChildren = []; for (var j in i['children']) { currentChildren.add(FormObject.fromJson(j)); } i['children'] = currentChildren; } var item = FormObject.fromJson(i); currentTemplate.add(item); } // if (widget.cardKey == 'LNRZYYJKGLFWJL') { // for (var element in storeTypeList) { // formValue[element] = calculatePhysicalFitnessScore( // element, // element == 'Ping_Score', // ); // } // } setState(() {}); } catch (error) { print('发生错误: $error'); } } @override Widget build(BuildContext context) { return Scaffold( key: scaffoldKey, endDrawer: VDynamicDrawerWrapper(scaffoldKey: scaffoldKey), resizeToAvoidBottomInset: false, body: Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox( width: 16, ), Container( margin: const EdgeInsets.only(top: 8), width: 130, height: 54, child: VButton( onTap: () { FormInfo.instance.formValue.clear(); Get.back(); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Icon(Icons.arrow_back_ios_new, size: 24), SizedBox( width: 8, ), Text("返回", style: TextStyle(fontSize: 20)), ], ), ), ), Expanded( child: Container( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 10), child: _buildTitleList(), ), ), if (widget.canPrescribe) Container( margin: const EdgeInsets.only(top: 8, right: 16), width: 130, height: 54, child: VButton( onTap: () { widget.onClickPrescribe?.call( widget.cardKey, templateCode, jsonEncode(formValue), ); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Icon(Icons.assignment_outlined, size: 24), SizedBox( width: 8, ), Text("处方", style: TextStyle(fontSize: 20)), ], ), ), ), Container( margin: const EdgeInsets.only(top: 8, right: 16), width: 130, height: 54, child: VButton( onTap: () async { if (["TNB", "GXY"].contains(widget.cardKey)) { for (var element in currentTemplate) { for (var child in element.children!) { if (child.required ?? false) { var value = formValue[child.key]; if (value == null || value.length <= 0) { PromptBox.toast("有必填项未填写,请检查"); return; } } } } } final result = await widget.callBack( widget.cardKey, templateCode, jsonEncode(formValue), prescriptionKey, ); if (result) { Get.back(); } }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Icon(Icons.save, size: 24), SizedBox( width: 8, ), Text("保存", style: TextStyle(fontSize: 20)), ], ), ), ), ], ), Expanded( child: Stack( children: [ Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildDiagram(), _buildContent(), ], ), if (currentTitleIndex != currentTemplate.length - 1) _buildPositionedButton( () async { currentTitleIndex++; setState(() {}); }, right: -30, ), ], ), ) ], ), ); } Widget buildSingleItem(Widget item, int span) { return FractionallySizedBox( widthFactor: span == 24 ? 1 : 0.5, child: item, ); } Widget buildWidget(FormObject? currentFormObject) { Map widgetMap = { 'checkbox': _buildCheckBox, 'numberInput': _buildNumberInput, 'input': _buildInput, 'radio': _buildRadio, 'radioScore': _buildRadioScore, 'radioInput': _buildRadioInput, 'bloodPressure': _buildBloodPressure, 'bodyTemperature': _buildBodyTemperature, 'weight': _buildBodyWeight, 'sugar': _buildBodySugar, 'bloodOxygen': _buildBloodOxygen, 'medicalHistory': _buildMedicalHistory, 'homecareBedHistor': _buildHomecareBedHistory, 'table': _buildMainMedicationHistory, 'inoculateHistory': _buildInoculateHistory, 'safetyPrecautions': _buildToxicSubstance, 'urinalys': _buildUrinalysis, 'date': _buildDate, 'checkBoxFrequency': _buildCheckBoxFrequency, 'radioHospitalization': _buildRadioHospitalization, 'radioReferral': _buildRadioReferral, 'medication': _buildMedication, 'gxyAndTnbMedication': _buildGxyAndTnbMedication, 'radioExtraDescription': _buildRadioExtraDescription, 'checkBoxIncludeOther': _buildCheckBoxIncludeOther, 'inputAndRadio': _buildChildHeightAndWeight, 'anteriorFontanelle': _buildAnteriorFontanelle, 'anteriorFontanelleOther': _buildAnteriorFontanelleOther, 'referral': _buildFollowUpChildReferral, 'prescriptionType': _buildPrescription, 'residentdetail': _buildResidentDetail, }; Widget Function(FormObject) builder = widgetMap[currentFormObject?.type] ?? _buildInput; return builder(currentFormObject!); } Widget flowCardList() { int itemCount = 0; bool currentTemplateOptionsIsNotEmpty = false; if (currentTemplate.isNotEmpty) { itemCount = currentTemplate[currentTitleIndex].children?.length ?? 0; currentTemplateOptionsIsNotEmpty = currentTemplate[currentTitleIndex].options?.isNotEmpty ?? false; } List items = List.generate(itemCount, (index) { FormObject? currentFormObject = currentTemplate[currentTitleIndex].children?[index]; int span = currentFormObject?.span ?? 12; //父结构的options不等于空或null if (true) { //子结构的options若是无值则取父类的options值 if (currentTemplateOptionsIsNotEmpty) { currentFormObject!.options = currentTemplate[currentTitleIndex].options; } } return buildSingleItem(buildWidget(currentFormObject), span); }); return Scrollbar( thumbVisibility: true, child: SingleChildScrollView( child: Container( alignment: Alignment.topCenter, padding: const EdgeInsets.all(15), child: Wrap( runSpacing: 20, // 纵向元素间距 alignment: WrapAlignment.start, children: items, ), ), ), ); } void _setFollowUpData(sender, e) { if (mounted) { print(e); Map followUpData = jsonDecode(e); followUpData.forEach((key, value) { if (key == "BMI") { formValue.addAll(value); } if (key == "NIBP") { /// 之前区分左右,后面重新设计 List bloodValue = []; if (value["Sbp"] != null && value["Dbp"] != null && value["Pulse_Beat"] != null) { bloodValue.addAll([value["Sbp"], value["Dbp"]]); Map nibpData = { "Blood": {"Blood": jsonEncode(bloodValue)}, "Heart_Rate": value["Pulse_Beat"], }; formValue.addAll(nibpData); } } if (key == "GLU") { Map sugar = { "Blood_Sugar": value["sugar"], }; formValue.addAll(sugar); } }); logger.i("当前表单数据:${formValue}"); setState(() {}); print(e); } } /// title标签 Widget _buildTitleList() { return Wrap( runSpacing: 10, // 设置子小部件之间的间距 spacing: -12, alignment: WrapAlignment.start, children: currentTemplate.asMap().entries.map( (e) { /// 处方的处理 if (FormInfo.instance.formValue.isNotEmpty) { formValue.remove("Prescription"); } /// TODO 这边需要改下 MaterialColor currentColors = Colors.grey; e.value.children?.forEach((element) { if (formValue.containsKey(element.key)) currentColors = Colors.green; }); return TitleClipRect( title: e.value.label ?? '', color: currentTitleIndex == e.key ? null : currentColors, arrowHeight: arrowHeight, clickTitle: () { currentTitleIndex = e.key; setState(() {}); }, ); }, ).toList(), ); } /// 示意图 Widget _buildDiagram() { if (widget.cardKey == 'ZYYYFMYGHYFJZS') { return const SizedBox(); } return Expanded( flex: 1, child: Stack( children: [ /// TODO BAKA-优化 _buildImageCard(), if (currentTitleIndex != 0) _buildPositionedButton( () async { if (currentTitleIndex == 0) { Get.back(); } else { currentTitleIndex--; setState(() {}); } }, left: -30, ), ], ), ); } Widget _buildImageCard() { // if (currentTemplate[currentTitleIndex].key == 'Temperature') { // return Container( // alignment: Alignment.topCenter, // margin: const EdgeInsets.all(16).copyWith(top: 0), // child: Image.asset( // 'assets/images/healthCheck/temp.png', // height: double.infinity, // fit: BoxFit.fitWidth, // 设置图像的适应方式 // ), // ); // } if ([ 'GXY', 'TNB', 'LNRZYYJKGLFWJL', 'YZJSZASFFW', ].contains(widget.cardKey) || widget.cardKey.contains("ET_") || widget.cardKey.contains("FollowUpTuberculosis")) { return widget.followUpWidget!; } else { return Container( alignment: Alignment.topCenter, margin: const EdgeInsets.all(16).copyWith(top: 0), child: Image.asset( 'assets/images/exam/normalMeasurementChart.png', height: double.infinity, fit: BoxFit.fitWidth, // 设置图像的适应方式 ), ); } } /// 前囟 Widget _buildAnteriorFontanelle(FormObject currentFormObject) { List