This commit is contained in:
wyf
2025-05-15 13:57:42 +08:00
parent fb5c3864a3
commit 75ddfca402
51 changed files with 2451 additions and 1233 deletions

View File

@@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
class SegmentData {
final Color color;
final double value;
SegmentData({required this.color, required this.value});
}
class SegmentedCirclePainter extends CustomPainter {
final List<SegmentData> segments;
final double strokeWidth;
final double gapAngle; // 每段之间的间隔角度(单位:度)
SegmentedCirclePainter({
required this.segments,
this.strokeWidth = 6.0,
this.gapAngle = 4.0,
});
@override
void paint(Canvas canvas, Size size) {
final double radius = size.width / 2;
final Offset center = Offset(size.width / 2, size.height / 2);
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.square;
final double totalValue = segments.fold(0, (sum, item) => sum + item.value);
final double totalGap = gapAngle * segments.length;
final double totalDrawAngle = 360.0 - totalGap;
double startAngle = -90.0; // 从顶部开始
for (var segment in segments) {
final double sweepAngle = (segment.value / totalValue) * totalDrawAngle;
paint.color = segment.color;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius - strokeWidth / 2),
radians(startAngle),
radians(sweepAngle),
false,
paint,
);
startAngle += sweepAngle + gapAngle;
}
}
double radians(double degrees) => degrees * 3.1415926 / 180;
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class SegmentedCircleWithCenterWidget extends StatelessWidget {
final List<SegmentData> segments;
final double strokeWidth;
final double gapAngle;
final Widget centerWidget;
const SegmentedCircleWithCenterWidget({
Key? key,
required this.segments,
this.strokeWidth = 6.0,
this.gapAngle = 4.0,
required this.centerWidget,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
CustomPaint(
size: Size(200, 200), // 设置圆的尺寸
painter: SegmentedCirclePainter(
segments: segments,
strokeWidth: strokeWidth,
gapAngle: gapAngle,
),
),
centerWidget, // 放置自定义的中心 Widget
Positioned(
right: 60.rpx, // 放置在右侧
child: SvgPicture.asset(
'assets/img/icon/add.svg',
width: 14.rpx,
height: 22.rpx,
color: themeController.currentColor.sc9,
),
),
],
);
}
}

View File

@@ -0,0 +1,178 @@
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/pages/sleep_report/component/SegmentedCirclePainter.dart';
class SleepScoreWidget extends StatefulWidget {
const SleepScoreWidget({super.key});
@override
State<SleepScoreWidget> createState() => _SleepScoreWidgetState();
}
class _SleepScoreWidgetState extends State<SleepScoreWidget> {
@override
void setState(VoidCallback callback) {
super.setState(callback);
}
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
List<Map<String, dynamic>> showLabel = [
{"level": 1, "name": "优秀(≥85)", "color": Color(0xFF4CAF50)},
{"level": 2, "name": "良好(75~84)", "color": Color(0xFF8BC34A)},
{"level": 3, "name": "合格(60~74)", "color": Color(0xFFFFC107)},
{"level": 4, "name": "注意(60)", "color": Color(0xFFF44336)},
];
return Container(
width: double.infinity,
// decoration: BoxDecoration(color: Colors.green),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
decoration: BoxDecoration(),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'30天平均分',
style: TextStyle(
color: Color(0xFFD3D3D3),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Text(
'79',
style: TextStyle(
color: Colors.white,
fontSize: 48.rpx,
letterSpacing: 0.0,
),
),
].divide(SizedBox(height: 56.rpx)),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(),
child: SegmentedCircleWithCenterWidget(
segments: [
SegmentData(color: Colors.red, value: 30),
SegmentData(color: Colors.green, value: 20),
SegmentData(color: Colors.blue, value: 40),
SegmentData(color: Colors.orange, value: 10),
],
strokeWidth: 8,
gapAngle: 8,
centerWidget: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"睡眠评分",
style: TextStyle(
color: stringToColor("#FFFFFF"),
fontSize:
AppConstants().normal_text_fontSize),
),
Text(
"65",
style: TextStyle(
color: stringToColor("#FF9F66"),
fontSize: 100.rpx),
),
],
),
),
),
),
),
Container(
decoration: BoxDecoration(),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'睡眠等级',
style: TextStyle(
color: Color(0xFFD3D3D3),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Text(
'合格',
style: TextStyle(
color: stringToColor("#FF9F66"),
fontSize: 48.rpx,
letterSpacing: 0.0,
),
),
].divide(SizedBox(height: 56.rpx)),
),
),
].divide(SizedBox(
width: 33.rpx,
)),
),
),
Wrap(
spacing: 32.rpx,
runSpacing: 20.rpx,
children: showLabel.map<Widget>((item) {
return Container(
padding: EdgeInsets.all(5.rpx),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 20.rpx,
height: 20.rpx,
decoration: BoxDecoration(
color: item["color"],
borderRadius: BorderRadius.circular(10.rpx),
),
),
SizedBox(width: 17.rpx),
Text(
item["name"],
style: TextStyle(
color: Colors.white,
fontSize: 24.rpx,
),
),
],
),
);
}).toList(),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,631 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/controller/date/CalendarController.dart';
import 'package:vbvs_app/controller/sleep/sleep_report_controller.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
import 'package:vbvs_app/pages/sleep_report/component/SleepScoreWidget.dart';
class NewSleepReportPage extends StatefulWidget {
var date;
NewSleepReportPage({super.key, required this.date});
@override
State<NewSleepReportPage> createState() => _NewSleepReportPageState();
}
class _NewSleepReportPageState extends State<NewSleepReportPage> {
SleepReportController sleepReportController = Get.find();
CalendarController calendarController = Get.find();
@override
void initState() {
if (widget == null) {
widget.date = DateTime.now();
}
calendarController.selectedDate.value =
DateTime.fromMillisecondsSinceEpoch(widget.date);
sleepReportController.selectedDate.value =
DateTime.fromMillisecondsSinceEpoch(widget.date);
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, bodySize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent, // 背景透明
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
titleSpacing: 0,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
/// 居中标题
Text(
'健康报告'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
/// 左边返回按钮
Positioned(
left: 0,
child: returnIconButtom,
),
],
),
),
),
body: SafeArea(
top: true,
child: SingleChildScrollView(
child: Column(
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0),
child: Container(
width: double.infinity,
constraints: BoxConstraints(
minHeight: 90.rpx,
),
decoration: BoxDecoration(
color: themeController.currentColor.sc5),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 15.rpx, 30.rpx, 15.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Stack(
alignment: Alignment.bottomLeft,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Obx(() {
return ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc3,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () async {
sleepReportController.model.type =
1;
sleepReportController.updateAll();
},
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 115.rpx, // 固定宽度为 160.rpx
alignment:
Alignment.center, // 文字居中
child: Text(
'日报'.tr,
style: FlutterFlowTheme.of(
context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
sleepReportController
.model
.type ==
1
? themeController
.currentColor
.sc2
: themeController
.currentColor
.sc3,
),
),
),
SizedBox(height: 10.rpx),
],
),
);
}),
Obx(() {
return ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc3,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () async {
sleepReportController.model.type =
2;
sleepReportController.updateAll();
},
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 115.rpx, // 固定宽度为 160.rpx
alignment:
Alignment.center, // 文字居中
child: Text(
'周报'.tr,
style: FlutterFlowTheme.of(
context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
sleepReportController
.model
.type ==
2
? themeController
.currentColor
.sc2
: themeController
.currentColor
.sc3,
),
),
),
SizedBox(height: 10.rpx),
],
),
);
}),
Obx(() {
return ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc3,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () async {
sleepReportController.model.type =
3;
sleepReportController.updateAll();
},
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 115.rpx, // 固定宽度为 160.rpx
alignment:
Alignment.center, // 文字居中
child: Text(
'月报'.tr,
style: FlutterFlowTheme.of(
context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
sleepReportController
.model
.type ==
3
? themeController
.currentColor
.sc2
: themeController
.currentColor
.sc3,
),
),
),
SizedBox(height: 10.rpx),
],
),
);
}),
],
),
Obx(() {
double lineWidth = 115.rpx;
return AnimatedPositioned(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: 0,
left: sleepReportController.model.type ==
1
? 0
: sleepReportController.model.type ==
2
? 115.rpx
: 230.rpx,
child: Container(
width: lineWidth,
height: 4.rpx,
decoration: BoxDecoration(
color:
themeController.currentColor.sc2,
borderRadius:
BorderRadius.circular(2.rpx),
),
),
);
}),
],
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, 0.rpx, 0.rpx, 0),
child: Container(
width: 28.rpx,
height: 28.rpx,
// width: double.infinity,
decoration: BoxDecoration(),
child: SvgPicture.asset(
'assets/img/icon/share.svg',
fit: BoxFit.cover,
color: themeController.currentColor.sc3,
),
),
),
],
),
),
),
),
Container(
width: double.infinity,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 57.rpx, 30.rpx, 57.rpx),
child: Obx(() {
var date = sleepReportController.selectedDate;
return getTimeWidget();
}),
),
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 0.rpx, 30.rpx, 58.rpx),
child: ClickableContainer(
backgroundColor: themeController.currentColor.sc5,
highlightColor:
themeController.currentColor.sc5, // 或你希望的点击水波纹颜色
borderRadius: AppConstants()
.normal_container_radius, // 如果你想加圆角可以设置 eg. 12.rpx
padding: EdgeInsets.zero,
onTap: () {
print('点击了体征卡片');
},
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
Text(
'实时体征.姓名'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc4,
),
),
Text(
'实时体征.年龄'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc4,
),
),
].divide(SizedBox(height: 34.rpx)),
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'传感器1号',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
),
Text(
'2022-02-02',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
),
].divide(SizedBox(height: 34.rpx)),
),
]
.divide(SizedBox(width: 33.rpx))
.addToStart(SizedBox(width: 37.rpx)),
),
]
.addToStart(SizedBox(height: 36.rpx))
.addToEnd(SizedBox(height: 36.rpx)),
),
),
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
Text(
'实时体征.设备ID'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc4,
),
),
Text(
'实时体征.体重'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc4,
),
),
].divide(SizedBox(height: 34.rpx)),
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'1231212',
// "D11250300003",
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
),
Text(
'55kg',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
),
].divide(SizedBox(height: 34.rpx)),
),
]
.divide(SizedBox(width: 33.rpx))
.addToStart(SizedBox(width: 37.rpx)),
),
]
.addToStart(SizedBox(height: 36.rpx))
.addToEnd(SizedBox(height: 36.rpx)),
),
),
],
),
),
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 0.rpx, 30.rpx, 0),
child: Container(
width: double.infinity,
child: SleepScoreWidget(),
),
),
],
),
),
),
),
),
),
);
}
Widget getTimeWidget() {
if (sleepReportController.model.type == 1) {
//日报
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 28.rpx,
height: 28.rpx,
// width: double.infinity,
decoration: BoxDecoration(),
),
Container(
child: Row(
children: [
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc3,
padding: EdgeInsets.all(10.rpx), // 增加点击热区
borderRadius: 8.rpx,
onTap: () {
sleepReportController.selectedDate.value =
sleepReportController.selectedDate.value!
.subtract(const Duration(days: 1));
calendarController.selectedDate.value =
sleepReportController.selectedDate.value;
sleepReportController.updateAll();
calendarController.updateAll();
},
child: SizedBox(
width: 9.rpx,
height: 14.rpx,
child: SvgPicture.asset(
'assets/img/icon/arrow_left.svg',
color: themeController.currentColor.sc3,
),
),
),
Container(
child: Text(
MyUtils.getFormatChineseTime(sleepReportController
.selectedDate.value!.millisecondsSinceEpoch),
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
),
),
),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc3,
padding: EdgeInsets.all(10.rpx), // 增加点击热区
borderRadius: 8.rpx,
onTap: () {
sleepReportController.selectedDate.value =
sleepReportController.selectedDate.value!
.subtract(const Duration(days: -1));
calendarController.selectedDate.value =
sleepReportController.selectedDate.value;
sleepReportController.updateAll();
calendarController.updateAll();
},
child: SizedBox(
width: 9.rpx,
height: 14.rpx,
child: SvgPicture.asset(
'assets/img/icon/arrow_right.svg',
color: themeController.currentColor.sc3,
),
),
),
].divide(SizedBox(
width: 26.rpx,
)),
),
),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc3,
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx,
0.rpx,
0.rpx,
0.rpx,
),
borderRadius: 0,
onTap: () {
showSleepCalendarBottomSheet(
timestamp: sleepReportController
.selectedDate.value!.millisecondsSinceEpoch,
context: context,
onDateSelected: (selectedDate) {
print("选中日期:");
print(selectedDate);
sleepReportController.selectedDate.value = selectedDate;
calendarController.selectedDate.value = selectedDate;
sleepReportController.updateAll();
calendarController.updateAll();
});
},
child: SizedBox(
width: 28.rpx,
height: 28.rpx,
child: SvgPicture.asset(
'assets/img/icon/share.svg',
fit: BoxFit.cover,
color: themeController.currentColor.sc3,
),
),
)
],
);
}
if (sleepReportController.model.type == 2) {
//周报
}
if (sleepReportController.model.type == 3) {
//月报
}
return Container();
}
}

View File

@@ -3,8 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart';
import 'package:vbvs_app/controller/device/device_type_controller.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
@@ -26,7 +24,6 @@ class _SleepReportPageState extends State<SleepReportPage> {
ThemeController themeController = Get.find();
DeviceTypeController deviceTypeController = Get.find();
// 使用 ValueNotifier 来管理加载状态
ValueNotifier<bool> isPageLoading = ValueNotifier<bool>(true);
@override
@@ -121,56 +118,5 @@ class _SleepReportPageState extends State<SleepReportPage> {
);
}
Widget _buildDeviceCard(BuildContext context,
{required String title, required String imageUrl, required double type}) {
return CustomCard(
borderRadius: 20.rpx, // 圆角大小
onTap: () {
if (type != null) {
if (type == 1) {
Get.toNamed("/bodyDevice");
}
if (type == 2) {
TopSlideNotification.show(
context,
text: "待开发.提示".tr,
textColor: themeController.currentColor.sc2,
);
}
}
},
colors: [themeController.currentColor.sc17], // 背景色
child: Container(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.135,
constraints: BoxConstraints(
minHeight: 220.rpx,
),
padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFC2CED7),
fontSize: 30.rpx,
letterSpacing: 0.0,
),
),
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Image.network(
imageUrl,
width: 212.rpx,
height: 168.rpx,
),
),
],
),
),
);
}
}