import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:vbvs_app/common/color/ServiceConstant.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/common/util/requestWithLog.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/controller/date/CalendarController.dart'; import 'SleepdateWidget.dart'; class SleepCalendarWidget extends StatefulWidget { final int? timestamp; final ValueChanged? onDateSelected; final int? type; // 新增参数,默认日历类型为日 final Color highlightColor; // ✅ 新增 final String? mac; const SleepCalendarWidget({ super.key, this.mac, this.timestamp, this.onDateSelected, this.type = 1, this.highlightColor = Colors.black, // ✅ 默认值 }); @override State createState() => _SleepCalendarWidgetState(); } class _SleepCalendarWidgetState extends State { CalendarController calendarController = Get.find(); RxMap sleepDate = {}.obs; RxList showLabel = [ {"level": 5, "name": "无报告".tr, "color": "#9E9E9E"} ].obs; // @override // void initState() { // super.initState(); // final initialDate = widget.timestamp != null // ? DateTime.fromMillisecondsSinceEpoch(widget.timestamp!) // : DateTime.now(); // calendarController.displayedMonth.value = initialDate; // calendarController.selectedDate.value = initialDate; // } @override void initState() { super.initState(); final initialDate = widget.timestamp != null ? DateTime.fromMillisecondsSinceEpoch(widget.timestamp!) : DateTime.now(); calendarController.displayedMonth.value = initialDate; calendarController.selectedDate.value = initialDate; // 初始化请求 fetchDate(initialDate); fetchSleepColor(); // 每当月份变化时,重新请求数据 ever(calendarController.displayedMonth, (DateTime newMonth) { fetchDate(newMonth); }); } Future fetchDate(DateTime timeStamp) async { final dateStr = timeStamp.toString().split(' ')[0]; String serviceAddress = ServiceConstant.service_address; String serviceName = ServiceConstant.server_service; String serviceApi = ServiceConstant.sleep_report; String queryUrl = "$serviceAddress$serviceName$serviceApi?mac=${widget.mac}&time=$dateStr&type=3&sleepType=2"; await requestWithLog( logTitle: "查询睡眠报告", method: MyHttpMethod.get, // queryUrl: // "https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${widget.mac}&time=$dateStr&type=3", queryUrl: queryUrl, onSuccess: (res) { sleepDate.value = res.data; }, onFailure: (res) { sleepDate.value = {}; }, ); } Future fetchSleepColor() async { await requestWithLog( logTitle: "查询睡眠报告", method: MyHttpMethod.get, queryUrl: "https://sleepdata.he-info.com/api/analysis/sleep/score/type", onSuccess: (res) { showLabel.value = [ ...res.data, {"level": 5, "name": "无报告".tr, "color": "#9E9E9E"}, // ✅ 注意拼写是 scoreList ]; }, onFailure: (res) { showLabel.value = [ {"level": 5, "name": "无报告".tr, "color": "#9E9E9E"}, ]; }, ); } @override Widget build(BuildContext context) { return Container( width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(20.rpx), topRight: Radius.circular(20.rpx), ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildHeader(), _buildCalendarBody(showLabel), ], ), ); } Widget _buildHeader() { return Container( width: double.infinity, constraints: BoxConstraints(minHeight: 90.rpx), decoration: BoxDecoration( color: const Color(0xFF313541), borderRadius: BorderRadius.only( topLeft: Radius.circular(20.rpx), topRight: Radius.circular(20.rpx), ), ), child: Padding( padding: EdgeInsets.symmetric(horizontal: 65.rpx), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsets.all(8.rpx), onTap: () => calendarController.previousMonth(), child: Icon( Icons.arrow_back_ios_new, color: const Color(0xFF6D6F73), size: 24.rpx, ), ), Obx(() => Text( '${calendarController.displayedMonth.value.year}/${calendarController.displayedMonth.value.month}', style: TextStyle( color: Colors.white, fontSize: 30.rpx, ), )), ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsets.all(8.rpx), onTap: () => calendarController.nextMonth(), child: Icon( Icons.arrow_forward_ios, color: const Color(0xFF6D6F73), size: 24.rpx, ), ), ], ), ), ); } Widget _buildCalendarBody(List showLabel) { return Container( width: double.infinity, constraints: BoxConstraints(minHeight: 720.rpx), decoration: const BoxDecoration(color: Color(0xFF242835)), child: Padding( // padding: EdgeInsetsDirectional.fromSTEB(65.rpx, 13.rpx, 65.rpx, 38.rpx), padding: EdgeInsetsDirectional.fromSTEB(65.rpx, 0.rpx, 65.rpx, 0.rpx), child: Obx(() { final daysInMonth = calendarController.getDaysInMonth(); final calendarRows = calendarController.getCalendarRows(daysInMonth); final selectedDate = calendarController.selectedDate.value; return Column( mainAxisSize: MainAxisSize.max, children: [ // 仅当为日历模式显示周标题 _buildWeekdayHeader(), // 日历日期格子,支持日和周的高亮逻辑sleep _buildCalendarGrid(calendarRows, selectedDate, sleepDate.value), // TODO: 你可以扩展 month 类型的展示 SizedBox(height: 55.rpx), _buildLegend(showLabel), ], ); }), ), ); } Widget _buildWeekdayHeader() { return Container( constraints: BoxConstraints(minHeight: 90.rpx), child: Row( children: ["一".tr, "二".tr, "三".tr, "四".tr, "五".tr, "六".tr, "日".tr].map((day) { return Expanded( child: Center( child: Text( day, style: TextStyle( color: stringToColor("#FFFFFF"), fontSize: AppConstants().normal_text_fontSize, ), ), ), ); }).toList(), ), ); } // Widget _buildCalendarGrid(List> calendarRows, // DateTime? selectedDate, Map sleepDate) { // final isMonthSelected = widget.type == 3; // Widget content = Column( // children: calendarRows.map((week) { // final isWeekSelected = widget.type == 2 && // selectedDate != null && // week.any((d) => // d.year == selectedDate.year && // d.month == selectedDate.month && // d.day == selectedDate.day); // final row = Row( // children: week.map((date) { // if (date.year == 0) { // return const Expanded(child: SizedBox.shrink()); // } // final isSelected = widget.type == 1 && // selectedDate != null && // date.year == selectedDate.year && // date.month == selectedDate.month && // date.day == selectedDate.day; // return Expanded( // child: SleepdateWidget( // highlightColor: widget.highlightColor, // ✅ 传入高亮颜色 // sleepDate: sleepDate, // date: date, // isSelected: isSelected, // onTap: () { // calendarController.selectDate(date); // widget.onDateSelected?.call(date); // Get.back(); // }, // ), // ); // }).toList(), // ); // if (isWeekSelected) { // return Container( // decoration: BoxDecoration( // color: widget.highlightColor, // borderRadius: // BorderRadius.circular(AppConstants().button_container_radius), // ), // padding: EdgeInsets.symmetric( // vertical: 6.rpx, horizontal: 12.rpx), // 增加左右padding // margin: EdgeInsets.symmetric(vertical: 6.rpx), // child: row, // ); // } else { // return row; // } // }).toList(), // ); // // 如果是月份模式,包一层黑色背景容器 // if (isMonthSelected && selectedDate != null) { // final displayedMonth = calendarController.displayedMonth.value; // final isSameMonth = displayedMonth.year == selectedDate.year && // displayedMonth.month == selectedDate.month; // if (isSameMonth) { // // ✅ 当前显示的月 == 当前选中的月 → 加高亮外框 // return Container( // decoration: BoxDecoration( // color: widget.highlightColor, // 背景淡色 // borderRadius: // BorderRadius.circular(AppConstants().normal_container_radius), // // border: Border.all( // // color: widget.highlightColor, // // width: 2.rpx, // // ), // ), // // padding: EdgeInsets.symmetric(vertical: 0.rpx, horizontal: 0.rpx), // // margin: EdgeInsets.symmetric(vertical: 0.rpx), // child: content, // ); // } else { // return content; // } // } else { // return content; // } // } Widget _buildCalendarGrid(List> calendarRows, DateTime? selectedDate, Map sleepDate) { final isMonthSelected = widget.type == 3; Widget content = Column( children: calendarRows.map((week) { final isWeekSelected = widget.type == 2 && selectedDate != null && week.any((d) => d.year == selectedDate.year && d.month == selectedDate.month && d.day == selectedDate.day); final row = Row( children: week.map((date) { if (date.year == 0) { return const Expanded(child: SizedBox.shrink()); } final isSelected = widget.type == 1 && selectedDate != null && date.year == selectedDate.year && date.month == selectedDate.month && date.day == selectedDate.day; return Expanded( child: SleepdateWidget( highlightColor: widget.highlightColor, sleepDate: sleepDate, date: date, isSelected: isSelected, onTap: () { calendarController.selectDate(date); widget.onDateSelected?.call(date); Get.back(); }, ), ); }).toList(), ); if (isWeekSelected) { return Container( decoration: BoxDecoration( color: widget.highlightColor, borderRadius: BorderRadius.circular(AppConstants().button_container_radius), ), // 把padding改成margin,避免Row宽度变化导致跳动 margin: EdgeInsets.symmetric(vertical: 6.rpx, horizontal: 12.rpx), child: row, ); } else { return row; } }).toList(), ); // 月视图高亮逻辑不变 if (isMonthSelected && selectedDate != null) { final displayedMonth = calendarController.displayedMonth.value; final isSameMonth = displayedMonth.year == selectedDate.year && displayedMonth.month == selectedDate.month; if (isSameMonth) { return Container( decoration: BoxDecoration( color: widget.highlightColor, borderRadius: BorderRadius.circular(AppConstants().normal_container_radius), ), child: content, ); } else { return content; } } else { return content; } } Widget _buildLegend(List showLabel) { return Wrap( spacing: 20.rpx, runSpacing: 20.rpx, children: showLabel.map((item) { return Container( padding: EdgeInsets.all(5.rpx), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 20.rpx, height: 20.rpx, decoration: BoxDecoration( color: stringToColor(item["color"]), borderRadius: BorderRadius.circular(10.rpx), ), ), SizedBox(width: 8.rpx), Text( item["name"], style: TextStyle( color: Colors.white, fontSize: 24.rpx, ), ), ], ), ); }).toList(), ); } }