|
@@ -0,0 +1,314 @@
|
|
|
+import 'dart:convert';
|
|
|
+
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter/services.dart';
|
|
|
+import 'package:get/get.dart';
|
|
|
+
|
|
|
+import 'package:vitalapp/components/alert_dialog.dart';
|
|
|
+import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_card.dart';
|
|
|
+import 'package:vitalapp/pages/medical/models/item.dart';
|
|
|
+import 'package:vnote_device_plugin/models/exams/nibp.dart';
|
|
|
+
|
|
|
+/// 小弹窗输入
|
|
|
+class VDialogBloodPressure extends StatelessWidget {
|
|
|
+ /// 标题
|
|
|
+ final String? title;
|
|
|
+
|
|
|
+ /// 描述
|
|
|
+ final String? description;
|
|
|
+
|
|
|
+ /// 输入占位符
|
|
|
+ final String? placeholder;
|
|
|
+
|
|
|
+ /// 初始值
|
|
|
+ final List<String>? initialValue;
|
|
|
+
|
|
|
+ const VDialogBloodPressure({
|
|
|
+ super.key,
|
|
|
+ this.title,
|
|
|
+ this.description,
|
|
|
+ this.placeholder,
|
|
|
+ this.initialValue,
|
|
|
+ });
|
|
|
+
|
|
|
+ Future<String?> show<String>() => VAlertDialog.showDialog<String>(this);
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ final controller1 = TextEditingController(text: initialValue?.first);
|
|
|
+ final controller2 = TextEditingController(text: initialValue?.last);
|
|
|
+ return VAlertDialog(
|
|
|
+ title: title,
|
|
|
+ width: 440,
|
|
|
+ contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
|
|
|
+ content: _buildContent(context, controller1, controller2),
|
|
|
+ showCancel: true,
|
|
|
+ onConfirm: () {
|
|
|
+ Get.back(result: json.encode([controller1.text, controller2.text]));
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildContent(
|
|
|
+ BuildContext context,
|
|
|
+ TextEditingController controller1,
|
|
|
+ TextEditingController controller2,
|
|
|
+ ) {
|
|
|
+ final children = <Widget>[];
|
|
|
+ if (description != null) {
|
|
|
+ children.add(
|
|
|
+ Padding(
|
|
|
+ padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
|
+ child: Text(
|
|
|
+ description!,
|
|
|
+ style: const TextStyle(color: Colors.black87, fontSize: 18),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ children.add(const SizedBox(height: 8));
|
|
|
+ } else {
|
|
|
+ children.add(const SizedBox(height: 12));
|
|
|
+ }
|
|
|
+ children.add(_buildInputWidget(context, controller1, controller2));
|
|
|
+ return SingleChildScrollView(
|
|
|
+ child: Column(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: children,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildInputWidget(
|
|
|
+ BuildContext context,
|
|
|
+ TextEditingController controller1,
|
|
|
+ TextEditingController controller2,
|
|
|
+ ) {
|
|
|
+ const fontSize = 20.0;
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ _buildItem(context, controller1, '高压'),
|
|
|
+ const Text(
|
|
|
+ '/',
|
|
|
+ style: TextStyle(fontSize: fontSize),
|
|
|
+ ),
|
|
|
+ _buildItem(context, controller2, '低压'),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildItem(
|
|
|
+ BuildContext context,
|
|
|
+ TextEditingController controller,
|
|
|
+ String hintText,
|
|
|
+ ) {
|
|
|
+ const fontSize = 20.0;
|
|
|
+ const height = 56.0;
|
|
|
+ return Expanded(
|
|
|
+ child: SizedBox(
|
|
|
+ height: height,
|
|
|
+ child: TextField(
|
|
|
+ controller: controller,
|
|
|
+ readOnly: false,
|
|
|
+ autofocus: true,
|
|
|
+ keyboardType: const TextInputType.numberWithOptions(
|
|
|
+ decimal: true), // 允许输入数字和小数点
|
|
|
+ inputFormatters: [
|
|
|
+ FilteringTextInputFormatter.allow(
|
|
|
+ RegExp(r'^\d+\.?\d{0,2}'),
|
|
|
+ ), // 只允许输入数字和小数点,俩位小数
|
|
|
+ ],
|
|
|
+ style: const TextStyle(fontSize: fontSize),
|
|
|
+ decoration: InputDecoration(
|
|
|
+ border: const UnderlineInputBorder(
|
|
|
+ borderRadius: BorderRadius.zero,
|
|
|
+ borderSide: BorderSide(),
|
|
|
+ ),
|
|
|
+ enabledBorder: const UnderlineInputBorder(
|
|
|
+ borderRadius: BorderRadius.zero,
|
|
|
+ borderSide: BorderSide(
|
|
|
+ color: Colors.black54,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ focusedBorder: UnderlineInputBorder(
|
|
|
+ borderRadius: BorderRadius.zero,
|
|
|
+ borderSide: BorderSide(
|
|
|
+ color: Theme.of(context).primaryColor.withOpacity(.4),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ filled: true,
|
|
|
+ fillColor: Colors.white,
|
|
|
+ contentPadding: const EdgeInsets.symmetric(
|
|
|
+ vertical: (height - fontSize * 1.2) / 2,
|
|
|
+ horizontal: 8,
|
|
|
+ ),
|
|
|
+ hintStyle: const TextStyle(fontSize: fontSize),
|
|
|
+ labelStyle: const TextStyle(fontSize: fontSize),
|
|
|
+ hintText: hintText,
|
|
|
+ isCollapsed: true,
|
|
|
+ ),
|
|
|
+ onSubmitted: (value) {
|
|
|
+ print(value);
|
|
|
+ Get.back(result: value);
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// TODO 优化血压组件,存储需要重新设计
|
|
|
+// ignore: must_be_immutable
|
|
|
+class FollowBloodPressure extends StatefulWidget {
|
|
|
+ FollowBloodPressure({
|
|
|
+ super.key,
|
|
|
+ required this.currentValue,
|
|
|
+ required this.bloodPressure,
|
|
|
+ });
|
|
|
+ Map currentValue;
|
|
|
+ Function(Map) bloodPressure;
|
|
|
+ @override
|
|
|
+ State<FollowBloodPressure> createState() => _FollowBloodPressureState();
|
|
|
+}
|
|
|
+
|
|
|
+class _FollowBloodPressureState extends State<FollowBloodPressure> {
|
|
|
+ PressureStatus pressureStatus = PressureStatus.left;
|
|
|
+
|
|
|
+ NibpExamValue? value;
|
|
|
+ NibpExamValue? nibpExamValue;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ if (widget.currentValue['Blood'] != null) {
|
|
|
+ String currentValue = widget.currentValue['Blood'];
|
|
|
+ nibpExamValue = NibpExamValue(
|
|
|
+ systolicPressure: int.parse(jsonDecode(currentValue).first.toString()),
|
|
|
+ diastolicPressure: int.parse(jsonDecode(currentValue).last.toString()),
|
|
|
+ pulse: 0,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ super.initState();
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void dispose() {
|
|
|
+ super.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Stack(
|
|
|
+ children: [
|
|
|
+ ExamCard(
|
|
|
+ content: Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ InkWell(
|
|
|
+ child: _SideBar(
|
|
|
+ title: '血压',
|
|
|
+ value: _buildResult(nibpExamValue),
|
|
|
+ unit: 'mmHg',
|
|
|
+ ),
|
|
|
+ onTap: () async {
|
|
|
+ String? result = await VDialogBloodPressure(
|
|
|
+ title: '血压',
|
|
|
+ initialValue: [
|
|
|
+ nibpExamValue?.systolicPressure.toString() ?? '',
|
|
|
+ nibpExamValue?.diastolicPressure.toString() ?? '',
|
|
|
+ ],
|
|
|
+ ).show();
|
|
|
+ if (result != null) {
|
|
|
+ nibpExamValue = NibpExamValue(
|
|
|
+ diastolicPressure: int.parse(jsonDecode(result).last),
|
|
|
+ systolicPressure: int.parse(jsonDecode(result).first),
|
|
|
+ pulse: 0,
|
|
|
+ );
|
|
|
+ widget.bloodPressure({
|
|
|
+ "Blood": result,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ setState(() {});
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildResult(NibpExamValue? nibpExamValue) {
|
|
|
+ const textStyle = TextStyle(fontSize: 26, color: Colors.black);
|
|
|
+
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Align(
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+ child: Text(
|
|
|
+ nibpExamValue?.systolicPressure.toString() ?? '',
|
|
|
+ style: textStyle,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Align(
|
|
|
+ alignment: Alignment.centerRight,
|
|
|
+ child: Text(
|
|
|
+ nibpExamValue?.diastolicPressure.toString() ?? '',
|
|
|
+ style: textStyle,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _SideBar extends StatelessWidget {
|
|
|
+ final String title;
|
|
|
+ final Widget value;
|
|
|
+ final String unit;
|
|
|
+
|
|
|
+ const _SideBar({
|
|
|
+ required this.title,
|
|
|
+ required this.value,
|
|
|
+ required this.unit,
|
|
|
+ });
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ // padding: const EdgeInsets.symmetric(horizontal: 30),
|
|
|
+ child: Text(
|
|
|
+ title,
|
|
|
+ style: const TextStyle(
|
|
|
+ fontSize: 26,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ alignment: Alignment.bottomRight,
|
|
|
+ padding: const EdgeInsets.only(
|
|
|
+ right: 30,
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.end,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
+ children: [
|
|
|
+ value,
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|