diff --git a/lib/common/components/dialog_widgets.dart b/lib/common/components/dialog_widgets.dart index 5fc1bb7..dadfbef 100644 --- a/lib/common/components/dialog_widgets.dart +++ b/lib/common/components/dialog_widgets.dart @@ -37,42 +37,6 @@ buildCloseButton( ); } -/// 弹窗中的图片展示 -/// 目前在基础运动详情弹窗、动作详情弹窗、动作配置弹窗中可复用 -buildImageArea(BuildContext context, Exercise exercise) { - return Container( - // 预设的图片背景色一般是白色,所以这里也设置为白色,看起来一致 - // color: Colors.white, - // 2023-12-25 因为有设计深色模式,所以不能固定为白色 - color: Theme.of(context).canvasColor, - child: Center( - child: Padding( - padding: EdgeInsets.only(bottom: 10.sp), - child: GestureDetector( - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return Dialog( - child: Hero( - tag: 'imageTag', - child: buildExerciseImageCarouselSlider(exercise), - ), - ); - }, - ); - }, - child: Hero( - tag: 'imageTag', - // child: buildExerciseImage(exercise), - child: buildExerciseImageCarouselSlider(exercise), - ), - ), - ), - ), - ); -} - /// 弹窗中的标题部分 /// 主体是个ListTile,但有的地方其title只是显示文本,有的可能会是按钮,所以title部分保留传入部件 /// 目前在基础运动详情弹窗、动作详情弹窗、动作配置弹窗中可复用 @@ -117,50 +81,6 @@ Image buildExerciseImage(Exercise exercise) { ); } -// 锻炼的图片轮播图(后续如果食物的图片或者其他图片有类似功能,可能再抽一次) -buildExerciseImageCarouselSlider(Exercise exercise) { - List imageList = []; - // 先要排除image是个空字符串 - if (exercise.images != null && exercise.images!.trim().isNotEmpty) { - imageList = exercise.images!.split(","); - } - - return CarouselSlider( - options: CarouselOptions( - autoPlay: true, // 自动播放 - enlargeCenterPage: true, // 居中图片放大 - aspectRatio: 16 / 9, // 图片宽高比 - viewportFraction: 1, // 图片占屏幕宽度的比例 - // 只有一张图片时不滚动 - enableInfiniteScroll: imageList.length > 1, - ), - // 没有图片显示一张占位图片 - items: imageList.isEmpty - ? [Image.asset(placeholderImageUrl, fit: BoxFit.scaleDown)] - : imageList.map((imageUrl) { - return Builder( - builder: (BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - margin: const EdgeInsets.symmetric(horizontal: 5.0), - decoration: const BoxDecoration( - color: Colors.grey, - ), - child: Image.file( - File(imageUrl), - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Image.asset(placeholderImageUrl, - fit: BoxFit.scaleDown); - }, - ), - ); - }, - ); - }).toList(), - ); -} - // 图片轮播 buildImageCarouselSlider( List imageList, { @@ -271,7 +191,7 @@ _buildImageCarouselSliderType( imageProvider: FileImage(File(imageList[index])), ); }, - enableRotation: true, + // enableRotation: true, scrollPhysics: const BouncingScrollPhysics(), backgroundDecoration: const BoxDecoration( color: Colors.transparent, diff --git a/lib/common/utils/db_diary_helper.dart b/lib/common/utils/db_diary_helper.dart index 08f26dd..2480dbc 100644 --- a/lib/common/utils/db_diary_helper.dart +++ b/lib/common/utils/db_diary_helper.dart @@ -182,18 +182,28 @@ class DBDiaryHelper { required String keyword, required int pageSize, // 一次查询条数显示 required int page, // 一次查询的偏移量,用于分页 + // 2023-12-30 指定创建日期升序或者降序排序 + String? dateSort = "desc", }) async { Database db = await database; // 根据页数和页面获得偏移量 var offset = (page - 1) * pageSize; + var sort = dateSort?.toLowerCase(); + if (dateSort != null) { + // 如果有传入创建时间排序,不是传的降序一律升序 + sort = dateSort.toLowerCase() == 'desc' ? 'DESC' : 'ASC'; + } + try { // 查询指定关键字当前页的数据 List> maps = await db.query( DiaryDdl.tableNameOfDiary, - where: - '(title LIKE ? OR content LIKE ?) AND user_id = ? LIMIT ? OFFSET ?', - whereArgs: ['%$keyword%', '%$keyword%', userId, pageSize, offset], + where: '(title LIKE ? OR content LIKE ?) AND user_id = ?', + whereArgs: ['%$keyword%', '%$keyword%', userId], + limit: pageSize, + offset: offset, + orderBy: sort != null ? 'gmt_create $sort' : null, ); final list = maps.map((row) => Diary.fromMap(row)).toList(); @@ -220,6 +230,8 @@ class DBDiaryHelper { int userId, { String? startDate, String? endDate, + // 2023-12-30 指定创建日期升序或者降序排序 + String? dateSort = "desc", }) async { Database db = await database; @@ -239,12 +251,19 @@ class DBDiaryHelper { whereArgs.add(endDate); } + var sort = dateSort?.toLowerCase(); + if (dateSort != null) { + // 如果有传入创建时间排序,不是传的降序一律升序 + sort = dateSort.toLowerCase() == 'desc' ? 'DESC' : 'ASC'; + } + try { // 查询指定关键字当前页的数据 List> maps = await db.query( DiaryDdl.tableNameOfDiary, where: where.isNotEmpty ? where.join(' AND ') : null, whereArgs: whereArgs.isNotEmpty ? whereArgs : null, + orderBy: sort != null ? 'gmt_create $sort' : null, ); final list = maps.map((row) => Diary.fromMap(row)).toList(); diff --git a/lib/common/utils/db_training_helper.dart b/lib/common/utils/db_training_helper.dart index d457454..81e33e9 100644 --- a/lib/common/utils/db_training_helper.dart +++ b/lib/common/utils/db_training_helper.dart @@ -573,6 +573,16 @@ class DBTrainingHelper { whereArgs: [planId], ); + Future> queryTrainingPlanById(int planId) async { + return (await (await database).query( + TrainingDdl.tableNameOfPlan, + where: "plan_id =? ", + whereArgs: [planId], + )) + .map((row) => TrainingPlan.fromMap(row)) + .toList(); + } + /// ???查询指定计划以及其所有训练(3层嵌套,看怎么优化) // 计划支持条件查询,估计计划的数量不会多,就暂时不分页;同时关联的训练就全部带出。 Future> searchPlanWithGroups({ @@ -694,6 +704,12 @@ class DBTrainingHelper { Database db = await database; return await db.transaction((txn) async { + // 2023-12-30 一并更新该计划的周期为列表的长度 + await txn.rawUpdate( + 'UPDATE ${TrainingDdl.tableNameOfPlan} SET plan_period = ? WHERE plan_id = ?', + [phgList.length, planId], + ); + await txn.delete( TrainingDdl.tableNameOfPlanHasGroup, where: "plan_id = ? ", diff --git a/lib/common/utils/tool_widgets.dart b/lib/common/utils/tool_widgets.dart index 7caae6f..f476453 100644 --- a/lib/common/utils/tool_widgets.dart +++ b/lib/common/utils/tool_widgets.dart @@ -6,6 +6,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'dart:math' as math; +import '../../layout/themes/cus_font_size.dart'; import '../../models/cus_app_localizations.dart'; import '../global/constants.dart'; import 'tools.dart'; @@ -62,8 +63,14 @@ Widget buildLoader(bool isLoading) { // 表单使用是FormBuilderDropdown但要注意类型改为匹配的,或者不指定String List> genDropdownMenuItems( List options, { - double? textSize = 16, + double? textSize, }) { +// 2023-12-30 为了英文的时候输入框显示完整,字体小点(13),中午就16 + var fontSize = textSize ?? + (box.read('language') == "en" + ? CusFontSizes.pageSubContent + : CusFontSizes.pageSubTitle); + return options .map( (option) => DropdownMenuItem( @@ -71,7 +78,7 @@ List> genDropdownMenuItems( value: option.value, child: Text( showCusLable(option), - style: TextStyle(fontSize: textSize), + style: TextStyle(fontSize: fontSize), ), ), ) diff --git a/lib/common/utils/tools.dart b/lib/common/utils/tools.dart index e09b3c7..e8b7a39 100644 --- a/lib/common/utils/tools.dart +++ b/lib/common/utils/tools.dart @@ -7,6 +7,11 @@ import 'package:intl/intl.dart'; import '../global/constants.dart'; +/// 默认的日历显示范围,当前月的前后3个月 +final kToday = DateTime.now(); +final kFirstDay = DateTime(2023, 10, 12); +final kLastDay = DateTime(kToday.year, kToday.month + 1, 15); + // 10位的时间戳转字符串 String formatTimestampToString(int timestamp) { if (timestamp.toString().length == 10) { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 93e8d7b..bca9bb6 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -314,6 +314,7 @@ "richTextToolNote":" Expand Quill Toolbar", "lastModified":"Last Modified", + "gmtCreate":"Created Time", "confirmLabel":"Confirm", @@ -324,7 +325,7 @@ "moreDetail": "More Detail", "lessLabel":"less", "enterLabel":"Enter", - "skipLabel":"Next", + "skipLabel":"Not Now", "backLabel":"Back", "saveLabel":"Save", "eidtLabel":"Edit {name}", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index e27caf8..eddaa40 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -135,7 +135,7 @@ "groupDeleteAlert":"确认要删除该训练动作组: {workoutName} ? 删除后不可恢复!", "groupInUse":"该训练 {workoutName} 有被计划使用,暂不支持删除", - "modifyGroupLabels": "{num, select, 0{修改训练} 1{新建训练} 2{主要肌肉} 3{次要肌肉} 4{技术要点} 5{语音提示要点} 6{动作图片} 7{用户上传} other{其他} }", + "modifyGroupLabels": "{num, select, 0{新建训练} 1{修改训练} 2{主要肌肉} 3{次要肌肉} 4{技术要点} 5{语音提示要点} 6{动作图片} 7{用户上传} other{其他} }", "@modifyGroupLabels": { "description": "修改或新建训练动作组的标签" }, @@ -184,7 +184,7 @@ "planDeleteAlert":"确认要删除该训练计划: {planName} ? 删除后不可恢复!", "planInUse":"该训练计划 {planName} 存在跟练记录,暂不支持删除", - "modifyPlanLabels": "{num, select, 0{修改计划} 1{新建计划} 2{名称} 3{代号} 4{分类} 5{级别} 6{训练周期} 7{概述} other{其他} }", + "modifyPlanLabels": "{num, select, 0{新建计划} 1{修改计划} 2{名称} 3{代号} 4{分类} 5{级别} 6{训练周期} 7{概述} other{其他} }", "@modifyPlanLabels": { "description": "修改或新建 plan 的标签" }, @@ -321,6 +321,7 @@ "richTextToolNote":"展开富文本编辑工具栏", "lastModified":"上次修改时间", + "gmtCreate":"初始创建时间", "confirmLabel":"确定", "cancelLabel":"取消", @@ -329,8 +330,8 @@ "moreLabel":"更多", "moreDetail": "更多详情", "lessLabel":"收起", - "enterLabel":"进入", - "skipLabel":"下一步", + "enterLabel":"完成输入", + "skipLabel":"暂时忽略", "backLabel":"返回", "saveLabel":"保存", "eidtLabel":"修改{name}", diff --git a/lib/layout/home.dart b/lib/layout/home.dart index 754259b..209c78b 100644 --- a/lib/layout/home.dart +++ b/lib/layout/home.dart @@ -55,15 +55,15 @@ class _HomePageState extends State { actions: [ TextButton( onPressed: () { - Navigator.pop(context, true); + Navigator.pop(context, false); }, - child: Text(CusAL.of(context).confirmLabel), + child: Text(CusAL.of(context).cancelLabel), ), - ElevatedButton( + TextButton( onPressed: () { - Navigator.pop(context, false); + Navigator.pop(context, true); }, - child: Text(CusAL.of(context).cancelLabel), + child: Text(CusAL.of(context).confirmLabel), ), ], ); diff --git a/lib/layout/init_guide_page.dart b/lib/layout/init_guide_page.dart index 7fbc448..ef1780e 100644 --- a/lib/layout/init_guide_page.dart +++ b/lib/layout/init_guide_page.dart @@ -67,7 +67,7 @@ class _InitGuidePageState extends State { return Scaffold( resizeToAvoidBottomInset: false, body: Padding( - padding: const EdgeInsets.all(16.0), + padding: EdgeInsets.all(16.sp), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -78,12 +78,21 @@ class _InitGuidePageState extends State { controller: _usernameController, decoration: InputDecoration( labelText: CusAL.of(context).nameLabel, + // 设置透明底色 + filled: true, + fillColor: Colors.transparent, ), ), ), Padding( padding: EdgeInsets.all(10.sp), child: DropdownButtonFormField( + decoration: const InputDecoration( + isDense: true, + // 设置透明底色 + filled: true, + fillColor: Colors.transparent, + ), items: genderOptions.map((CusLabel gender) { return DropdownMenuItem( value: gender, diff --git a/lib/main.dart b/lib/main.dart index d764891..6614f38 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,8 +6,9 @@ import 'layout/app.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await GetStorage.init(); - await GetStorage().write('language', 'en'); + // await GetStorage().write('language', 'en'); // await GetStorage().write('language', 'cn'); + await GetStorage().write('language', 'system'); // await GetStorage().write('mode', 'dark'); // await GetStorage().write('mode', 'light'); await GetStorage().write('mode', 'system'); diff --git a/lib/models/training_state.dart b/lib/models/training_state.dart index 3509ee7..b35fb96 100644 --- a/lib/models/training_state.dart +++ b/lib/models/training_state.dart @@ -248,7 +248,7 @@ class TrainingPlan { planName: map['plan_name'] as String, planCategory: map['plan_category'] as String, planLevel: map['plan_level'] as String, - planPeriod: map['plan_period'] as int, + planPeriod: map['plan_period'] as int? ?? 0, description: map['description'] as String?, contributor: map['contributor'] as String?, gmtCreate: map['gmt_create'] as String?, diff --git a/lib/views/diary/diary_modify_rich_text.dart b/lib/views/diary/diary_modify_rich_text.dart index 86155e5..938205a 100644 --- a/lib/views/diary/diary_modify_rich_text.dart +++ b/lib/views/diary/diary_modify_rich_text.dart @@ -246,15 +246,15 @@ class _DiaryModifyRichTextState extends State { actions: [ TextButton( onPressed: () { - Navigator.pop(context, true); + Navigator.pop(context, false); }, - child: Text(CusAL.of(context).confirmLabel), + child: Text(CusAL.of(context).cancelLabel), ), - ElevatedButton( + TextButton( onPressed: () { - Navigator.pop(context, false); + Navigator.pop(context, true); }, - child: Text(CusAL.of(context).cancelLabel), + child: Text(CusAL.of(context).confirmLabel), ), ], ); diff --git a/lib/views/diary/index_table_calendar.dart b/lib/views/diary/index_table_calendar.dart index 47774e4..af96dbd 100644 --- a/lib/views/diary/index_table_calendar.dart +++ b/lib/views/diary/index_table_calendar.dart @@ -15,12 +15,6 @@ import '../../models/diary_state.dart'; import 'diary_modify_rich_text.dart'; import 'index_timeline.dart'; -/// 默认的日历显示范围,当前月的前后3个月 -/// ???实际手记的日历显示范围的话,就第一个手记的月份,到当前月份即可 -final kToday = DateTime.now(); -final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); -final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); - class DiaryTableCalendar extends StatefulWidget { const DiaryTableCalendar({super.key}); @@ -359,8 +353,15 @@ class _DiaryTableCalendarState extends State { ListTile( title: Text(diary.title), - subtitle: Text( - "${CusAL.of(context).lastModified}: ${diarys[index].gmtModified ?? diarys[index].gmtCreate}", + subtitle: Column( + children: [ + Text( + "${CusAL.of(context).lastModified}: ${diarys[index].gmtModified ?? diarys[index].gmtCreate}", + ), + Text( + "${CusAL.of(context).gmtCreate}: ${diarys[index].gmtCreate ?? unknownDateTimeString}", + ) + ], ), trailing: Icon( Icons.arrow_forward, diff --git a/lib/views/diary/index_timeline.dart b/lib/views/diary/index_timeline.dart index 89c6564..3ac7815 100644 --- a/lib/views/diary/index_timeline.dart +++ b/lib/views/diary/index_timeline.dart @@ -249,6 +249,9 @@ class _IndexTimelineState extends State { DateTime.parse(diaryItem.gmtCreate ?? unknownDateTimeString), ); + var createDate = DateFormat(constDateFormat).format( + DateTime.parse(diaryItem.gmtCreate ?? unknownDateTimeString), + ); return Container( // 内外边距 // padding: EdgeInsets.all(5.sp), @@ -260,7 +263,7 @@ class _IndexTimelineState extends State { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - diaryItem.date, + createDate, style: TextStyle(fontSize: CusFontSizes.pageSubContent), ), Text( diff --git a/lib/views/dietary/index.dart b/lib/views/dietary/index.dart index fd57373..47dd8cd 100644 --- a/lib/views/dietary/index.dart +++ b/lib/views/dietary/index.dart @@ -53,44 +53,52 @@ class _DietaryState extends State { // CusAL.of(context).dietaryReports, // ), // ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const DietaryReports(), - CusAL.of(context).dietaryReports, - CusAL.of(context).dietaryReportsSubtitle, - reportImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const DietaryReports(), + CusAL.of(context).dietaryReports, + CusAL.of(context).dietaryReportsSubtitle, + reportImageUrl, + ), ), ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const MealPhotoGallery(), - CusAL.of(context).mealGallery, - CusAL.of(context).mealGallerySubtitle, - dietaryMealImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const MealPhotoGallery(), + CusAL.of(context).mealGallery, + CusAL.of(context).mealGallerySubtitle, + dietaryMealImageUrl, + ), ), ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const DietaryFoods(), - CusAL.of(context).foodCompo, - CusAL.of(context).foodCompoSubtitle, - dietaryNutritionImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const DietaryFoods(), + CusAL.of(context).foodCompo, + CusAL.of(context).foodCompoSubtitle, + dietaryNutritionImageUrl, + ), ), ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const DietaryRecords(), - CusAL.of(context).dietaryRecords, - CusAL.of(context).dietaryRecordsSubtitle, - dietaryLogCoverImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const DietaryRecords(), + CusAL.of(context).dietaryRecords, + CusAL.of(context).dietaryRecordsSubtitle, + dietaryLogCoverImageUrl, + ), ), ), ], diff --git a/lib/views/dietary/records/report_calendar_summary.dart b/lib/views/dietary/records/report_calendar_summary.dart index ea054c1..7229bd4 100644 --- a/lib/views/dietary/records/report_calendar_summary.dart +++ b/lib/views/dietary/records/report_calendar_summary.dart @@ -14,12 +14,6 @@ import '../../../models/cus_app_localizations.dart'; import '../../../models/dietary_state.dart'; import 'format_tools.dart'; -/// 默认的日历显示范围,当前月的前后3个月 -/// ???实际手记的日历显示范围的话,就第一个手记的月份,到当前月份即可 -final kToday = DateTime.now(); -final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); -final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); - class ReportCalendarSummary extends StatefulWidget { const ReportCalendarSummary({super.key}); diff --git a/lib/views/me/_feature_mock_data/index.dart b/lib/views/me/_feature_mock_data/index.dart index fb324f7..3a16922 100644 --- a/lib/views/me/_feature_mock_data/index.dart +++ b/lib/views/me/_feature_mock_data/index.dart @@ -1,12 +1,11 @@ // ignore_for_file: avoid_print import 'package:flutter/material.dart'; -import 'package:free_fitness/common/global/constants.dart'; import '../../../common/utils/db_diary_helper.dart'; import '../../../common/utils/db_dietary_helper.dart'; import '../../../common/utils/db_training_helper.dart'; -import '../../../common/utils/db_user_helper.dart'; + import 'test_funcs.dart'; class FeatureMockDemo extends StatefulWidget { @@ -20,7 +19,9 @@ class _FeatureMockDemoState extends State { final DBTrainingHelper _trainingHelper = DBTrainingHelper(); final DBDietaryHelper _dietaryHelper = DBDietaryHelper(); final DBDiaryHelper _diaryHelper = DBDiaryHelper(); - final DBUserHelper _userHelper = DBUserHelper(); + // final DBUserHelper _userHelper = DBUserHelper(); + + bool isLoading = false; Future _showSimpleDialog(BuildContext context, String msg) async { return showDialog( @@ -49,146 +50,167 @@ class _FeatureMockDemoState extends State { title: const Text("生成测试数据"), ), // ??? 从上倒下预计是:个人信息、功能按钮、软件信息等区块 - body: SingleChildScrollView( - child: Center( - child: Column( - children: [ - /// ???在此处集中添加测试数据 - /// - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: () { - _dietaryHelper.deleteDB(); - _showSimpleDialog(context, "已删除 dietary db"); - }, - child: const Text("delete dietary db"), - ), - ElevatedButton( - onPressed: () { - _trainingHelper.deleteDB(); - _showSimpleDialog(context, "已删除 training db"); - }, - child: const Text("delete training db"), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: () { - _diaryHelper.deleteDB(); - _showSimpleDialog(context, "已删除 diary db"); - }, - child: const Text("delete diary db"), - ), - ElevatedButton( - onPressed: () { - _userHelper.deleteDB(); - CacheUser.clearUserId(); - _showSimpleDialog(context, "已删除 user db"); - }, - child: const Text("delete user db"), - ), - ], - ), + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : SingleChildScrollView( + child: Center( + child: Column( + children: [ + /// ???在此处集中添加测试数据 + /// + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // ElevatedButton( + // onPressed: () { + // _dietaryHelper.deleteDB(); + // _showSimpleDialog(context, "已删除 dietary db"); + // }, + // child: const Text("delete dietary db"), + // ), + // ElevatedButton( + // onPressed: () { + // _trainingHelper.deleteDB(); + // _showSimpleDialog(context, "已删除 training db"); + // }, + // child: const Text("delete training db"), + // ), + // ], + // ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // ElevatedButton( + // onPressed: () { + // _diaryHelper.deleteDB(); + // _showSimpleDialog(context, "已删除 diary db"); + // }, + // child: const Text("delete diary db"), + // ), + // ElevatedButton( + // onPressed: () { + // _userHelper.deleteDB(); + // CacheUser.clearUserId(); + // _showSimpleDialog(context, "已删除 user db"); + // }, + // child: const Text("delete user db"), + // ), + // ], + // ), - TextButton( - onPressed: () {}, - child: const Text("新增用户营养素目标(todo)"), - ), + // TextButton( + // onPressed: () {}, + // child: const Text("新增用户营养素目标(todo)"), + // ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: () async { - // 7种食物(对应7*3种单份营养素)、70条饮食日记条目、随机插入最近7天中 - // (一日四餐,每餐1个条目,7天都是7*4=28条数据) - await insertDailyLogDataDemo(7, 30, 7); - if (!mounted) return; - _showSimpleDialog(context, "已新增【饮食】模块示例"); - }, - child: const Text("新增饮食模块示例"), - ), - ElevatedButton( - onPressed: () async { - await insertOneRandomPlanHasGroup(); - if (!mounted) return; - _showSimpleDialog(context, "已新增【训练】模块示例"); - }, - child: const Text("新增训练模块示例"), - ), - ], - ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // ElevatedButton( + // onPressed: () async { + // // 7种食物(对应7*3种单份营养素)、70条饮食日记条目、随机插入最近7天中 + // // (一日四餐,每餐1个条目,7天都是7*4=28条数据) + // await insertDailyLogDataDemo(7, 30, 7); + // if (!mounted) return; + // _showSimpleDialog(context, "已新增【饮食】模块示例"); + // }, + // child: const Text("新增饮食模块示例"), + // ), + // ElevatedButton( + // onPressed: () async { + // await insertOneRandomPlanHasGroup(); + // if (!mounted) return; + // _showSimpleDialog(context, "已新增【训练】模块示例"); + // }, + // child: const Text("新增训练模块示例"), + // ), + // ], + // ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: () async { - for (var i = 0; i < 10; i++) { - await insertOneQuillDemo(); - } - if (!mounted) return; - _showSimpleDialog(context, "已新增10篇【手记】示例"); - }, - child: const Text("新增10篇手记"), - ), - ElevatedButton( - onPressed: () async { - await insertExtraUsers(); - if (!mounted) return; - _showSimpleDialog(context, "已新增额外用户"); - }, - child: const Text("新增额外两个用户"), - ), - ], - ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // ElevatedButton( + // onPressed: () async { + // for (var i = 0; i < 10; i++) { + // await insertOneQuillDemo(); + // } + // if (!mounted) return; + // _showSimpleDialog(context, "已新增10篇【手记】示例"); + // }, + // child: const Text("新增10篇手记"), + // ), + // ElevatedButton( + // onPressed: () async { + // await insertExtraUsers(); + // if (!mounted) return; + // _showSimpleDialog(context, "已新增额外用户"); + // }, + // child: const Text("新增额外两个用户"), + // ), + // ], + // ), - TextButton( - onPressed: () async { - await insertBMIDemo(); - if (!mounted) return; - _showSimpleDialog(context, "已新增10条篇【BMI】示例"); - }, - child: const Text("userId为1的新增10条随机BMI数据"), - ), + // TextButton( + // onPressed: () async { + // await insertBMIDemo(); + // if (!mounted) return; + // _showSimpleDialog(context, "已新增10条篇【BMI】示例"); + // }, + // child: const Text("userId为1的新增10条随机BMI数据"), + // ), - ElevatedButton( - onPressed: () async { - await _dietaryHelper.deleteDB(); - await _trainingHelper.deleteDB(); - await _diaryHelper.deleteDB(); + ElevatedButton( + onPressed: () async { + await _dietaryHelper.deleteDB(); + await _trainingHelper.deleteDB(); + await _diaryHelper.deleteDB(); - if (!mounted) return; - _showSimpleDialog(context, "已删除所有数据"); - }, - child: const Text("删除所有数据"), - ), - ElevatedButton( - onPressed: () async { - await insertDailyLogDataDemo(7, 30, 7); - await insertOneRandomPlanHasGroup(); - for (var i = 0; i < 10; i++) { - await insertOneQuillDemo(); - } - await insertExtraUsers(); - await insertBMIDemo(); - - await insertTrainingDetailLogDemo(); - - if (!mounted) return; - _showSimpleDialog(context, "已插入所有数据"); - }, - child: const Text("插入所有数据"), + if (!mounted) return; + _showSimpleDialog(context, "已删除所有测试数据"); + }, + child: const Text("删除测试数据"), + ), + ElevatedButton( + onPressed: () async { + if (isLoading) return; + setState(() { + isLoading = true; + }); + + // 饮食记录数据 + await insertDailyLogDataDemo(7, 30, 7); + + for (var i = 0; i < 10; i++) { + // 不加这个,所有的group都是被plan使用的,无法删除 + await insertOneRandomGroupAndAction(); + // 不加这个,所有exercise都被action或group使用,无法删除 + await insertOneRandomExercise(); + + // 锻炼模块数据(会新增group以及exercise,和上面的不关联) + await insertOneRandomPlanHasGroup(); + + // 手记数据 + await insertOneQuillDemo(); + // 训练日志数据 + await insertTrainingDetailLogDemo(); + } + await insertExtraUsers(); + await insertBMIDemo(); + + setState(() { + isLoading = false; + }); + + if (!mounted) return; + _showSimpleDialog(context, "已插入测试数据"); + }, + child: const Text("插入测试数据"), + ), + ], + ), ), - ], - ), - ), - ), + ), ); } } diff --git a/lib/views/me/_feature_mock_data/test_funcs.dart b/lib/views/me/_feature_mock_data/test_funcs.dart index 0c65020..e38fa4b 100644 --- a/lib/views/me/_feature_mock_data/test_funcs.dart +++ b/lib/views/me/_feature_mock_data/test_funcs.dart @@ -358,7 +358,7 @@ Future insertOneRandomPlanHasGroup() async { } // 2023-12-27 插入训练日志宽表数据,展示运动报告时不需要级联查询基础表 -insertTrainingDetailLogDemo({int? size = 10}) async { +insertTrainingDetailLogDemo() async { print("【【【 插入测试数据 start-->:insertTrainingDetailLogDemo "); /// 2023-12-27 正好是测试日志宽表,不会关联其他基础表,数据随意写就好了 diff --git a/lib/views/me/user_info/index.dart b/lib/views/me/user_info/index.dart index 00c27c7..0549276 100644 --- a/lib/views/me/user_info/index.dart +++ b/lib/views/me/user_info/index.dart @@ -57,19 +57,37 @@ class _UserInfoState extends State { appBar: AppBar( title: Text(CusAL.of(context).settingLabels("0")), actions: [ - TextButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ModifyUserPage(user: user), - ), - ).then((value) { - // 确认新增成功后重新加载当前日期的条目数据 - _queryLoginedUserInfo(); - }); - }, - child: Text(CusAL.of(context).eidtLabel(""))) + // TextButton( + // onPressed: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => ModifyUserPage(user: user), + // ), + // ).then((value) { + // // 确认新增成功后重新加载当前日期的条目数据 + // _queryLoginedUserInfo(); + // }); + // }, + // child: Text( + // CusAL.of(context).eidtLabel(""), + // style: const TextStyle(color: Colors.white), + // ), + // ), + IconButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ModifyUserPage(user: user), + ), + ).then((value) { + // 确认新增成功后重新加载当前日期的条目数据 + _queryLoginedUserInfo(); + }); + }, + icon: const Icon(Icons.edit), + ) ], ), body: isLoading diff --git a/lib/views/me/user_info/modify_user/index.dart b/lib/views/me/user_info/modify_user/index.dart index 48cd81c..6dee71d 100644 --- a/lib/views/me/user_info/modify_user/index.dart +++ b/lib/views/me/user_info/modify_user/index.dart @@ -107,12 +107,17 @@ class _ModifyUserPageState extends State { : CusAL.of(context).eidtLabel(CusAL.of(context).userInfo), ), actions: [ - TextButton( + // TextButton( + // onPressed: _saveUser, + // child: Text( + // CusAL.of(context).saveLabel, + // style: const TextStyle(color: Colors.white), + // ), + // ), + // 2023-12-30 appbar 的action都优先iconbutton + IconButton( onPressed: _saveUser, - child: Text( - CusAL.of(context).saveLabel, - style: const TextStyle(color: Colors.white), - ), + icon: const Icon(Icons.save), ), ], ), diff --git a/lib/views/me/weight_change_record/weight_change_line_chart.dart b/lib/views/me/weight_change_record/weight_change_line_chart.dart index 7ac5016..6413d9f 100644 --- a/lib/views/me/weight_change_record/weight_change_line_chart.dart +++ b/lib/views/me/weight_change_record/weight_change_line_chart.dart @@ -174,7 +174,7 @@ class _WeightChangeLineChartState extends State { // 找了很多问题,是Android9及之下,无法保存。 // 权限什么的都已经给了的,还是存不了,有时间找个Android10及其之上的设备试一下 - _saveChartImage() async { + saveChartImage() async { RenderRepaintBoundary boundary = _chartKey.currentContext!.findRenderObject() as RenderRepaintBoundary; ui.Image image = await boundary.toImage(pixelRatio: 2.sp); @@ -243,14 +243,15 @@ class _WeightChangeLineChartState extends State { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - if (isShowSaveButton) - IconButton( - onPressed: _saveChartImage, - icon: Icon( - Icons.download, - color: Theme.of(context).primaryColor, - ), - ), + // 2023-12-30 还没有优化,暂时不支持下载 + // if (isShowSaveButton) + // IconButton( + // onPressed: saveChartImage, + // icon: Icon( + // Icons.download, + // color: Theme.of(context).primaryColor, + // ), + // ), IconButton( onPressed: () { setState(() { diff --git a/lib/views/training/exercise/exercise_detail.dart b/lib/views/training/exercise/exercise_detail.dart index b2673dd..6e9c2ce 100644 --- a/lib/views/training/exercise/exercise_detail.dart +++ b/lib/views/training/exercise/exercise_detail.dart @@ -72,7 +72,7 @@ class _ExerciseDetailDialogState extends State { context, popValue: modifiedFlag, )), - Expanded(flex: 2, child: buildImageArea(context, _currentItem)), + Expanded(flex: 2, child: _buildExerciseImageArea(_currentItem)), Expanded( flex: 3, child: buildTitleAndDescription( @@ -88,6 +88,16 @@ class _ExerciseDetailDialogState extends State { ); } + // 动作的图片 + _buildExerciseImageArea(Exercise item) { + List imageList = []; + // 先要排除image是个空字符串 + if (item.images != null && item.images!.trim().isNotEmpty) { + imageList = item.images!.split(","); + } + return buildImageCarouselSlider(imageList); + } + // 更多和修改按钮 _buildMoreAndEditButton() { return SizedBox( diff --git a/lib/views/training/exercise/exercise_detail_more.dart b/lib/views/training/exercise/exercise_detail_more.dart index 65d87e6..35e0660 100644 --- a/lib/views/training/exercise/exercise_detail_more.dart +++ b/lib/views/training/exercise/exercise_detail_more.dart @@ -63,6 +63,13 @@ class _ExerciseDetailMoreState extends State { @override Widget build(BuildContext context) { + // 获取该动作的图片列表 + List imageList = []; + // 先要排除image是个空字符串 + if (_item.images != null && _item.images!.trim().isNotEmpty) { + imageList = _item.images!.split(","); + } + return Scaffold( appBar: AppBar( title: Text(CusAL.of(context).exerciseDetail), @@ -75,7 +82,7 @@ class _ExerciseDetailMoreState extends State { padding: EdgeInsets.all(10.sp), child: SizedBox( height: 0.3.sh, - child: buildExerciseImageCarouselSlider(_item), + child: buildImageCarouselSlider(imageList), ), ), Padding( @@ -158,16 +165,17 @@ class _ExerciseDetailMoreState extends State { CusAL.of(context).exerciseLabels('4'), _item.instructions ?? "", ), - _buildTableRow( - CusAL.of(context).exerciseLabels('5'), - _item.ttsNotes ?? "", - ), - _buildTableRow( - CusAL.of(context).exerciseLabels('7'), - (_item.isCustom != null && _item.isCustom == true) - ? CusAL.of(context).boolLabels('0') - : CusAL.of(context).boolLabels('1'), - ), + // 2023-12-30 这两个栏位目前没有实际含义,暂时不显示 + // _buildTableRow( + // CusAL.of(context).exerciseLabels('5'), + // _item.ttsNotes ?? "", + // ), + // _buildTableRow( + // CusAL.of(context).exerciseLabels('7'), + // (_item.isCustom != null && _item.isCustom == true) + // ? CusAL.of(context).boolLabels('0') + // : CusAL.of(context).boolLabels('1'), + // ), ], ), ), diff --git a/lib/views/training/exercise/exercise_modify.dart b/lib/views/training/exercise/exercise_modify.dart index 6b2b6d3..638bb71 100644 --- a/lib/views/training/exercise/exercise_modify.dart +++ b/lib/views/training/exercise/exercise_modify.dart @@ -127,8 +127,6 @@ class _ExerciseModifyState extends State { .toList() .join(",") : null, - - /// isCustom: true, contributor: CacheUser.userName, // 时间都存时间戳,显示的时候再格式化 @@ -345,13 +343,14 @@ class _ExerciseModifyState extends State { const SizedBox(height: 10), // 语音提醒文本 - cusFormBuilerTextField( - "tts_notes", - labelText: CusAL.of(context).exerciseLabels('5'), - initialValue: updateTarget?.ttsNotes, - maxLines: 5, - isOutline: true, - ), + // 2023-12-30 这个栏位目前无实际意义 + // cusFormBuilerTextField( + // "tts_notes", + // labelText: CusAL.of(context).exerciseLabels('5'), + // initialValue: updateTarget?.ttsNotes, + // maxLines: 5, + // isOutline: true, + // ), const SizedBox(height: 10), // 上传活动示例图片(静态图或者gif) @@ -424,8 +423,8 @@ class _ExerciseModifyState extends State { }) { // 把预设的基础活动选项列表转化为 MultiSelectDialogField 支持的列表 final muscleItems = musclesOptions - .map>((opt) => MultiSelectItem( - opt, box.read("language") == 'en' ? opt.enLabel : opt.cnLabel)) + .map>( + (opt) => MultiSelectItem(opt, showCusLable(opt))) .toList(); return Padding( @@ -454,9 +453,11 @@ class _ExerciseModifyState extends State { fontSize: CusFontSizes.pageContent, ), ), - searchable: true, + // searchable: true, validator: validator, onConfirm: onConfirm, + cancelText: Text(CusAL.of(context).cancelLabel), + confirmText: Text(CusAL.of(context).confirmLabel), ), ); } diff --git a/lib/views/training/exercise/index.dart b/lib/views/training/exercise/index.dart index 5265abe..ee4e06a 100644 --- a/lib/views/training/exercise/index.dart +++ b/lib/views/training/exercise/index.dart @@ -299,13 +299,13 @@ class _TrainingExerciseState extends State { .exerciseDeleteAlert(exerciseItem.exerciseName)), actions: [ TextButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text(CusAL.of(context).confirmLabel), - ), - ElevatedButton( onPressed: () => Navigator.of(context).pop(false), child: Text(CusAL.of(context).cancelLabel), ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text(CusAL.of(context).confirmLabel), + ), ], ); }, @@ -337,6 +337,11 @@ class _TrainingExerciseState extends State { _buildExerciseItemCard(int index) { var exerciseItem = exerciseItems[index]; + // 构建轮播图片列表 + List imageList = (exerciseItem.images?.trim().isNotEmpty == true) + ? exerciseItem.images!.split(",") + : []; + return Card( child: InkWell( onTap: () { @@ -367,7 +372,7 @@ class _TrainingExerciseState extends State { SizedBox( width: 0.4.sw, height: 0.3.sh, - child: buildExerciseImageCarouselSlider(exerciseItem), + child: buildImageCarouselSlider(imageList), ), Expanded( flex: 3, diff --git a/lib/views/training/index.dart b/lib/views/training/index.dart index 11af629..8140a91 100644 --- a/lib/views/training/index.dart +++ b/lib/views/training/index.dart @@ -66,44 +66,52 @@ class _TrainingState extends State { // CusAL.of(context).report, // ), // ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const TrainingReports(), - CusAL.of(context).trainingReports, - CusAL.of(context).trainingReportsSubtitle, - reportImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const TrainingReports(), + CusAL.of(context).trainingReports, + CusAL.of(context).trainingReportsSubtitle, + reportImageUrl, + ), ), ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const TrainingExercise(), - CusAL.of(context).exerciseLabel, - CusAL.of(context).exerciseSubtitle, - workoutWomanImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const TrainingExercise(), + CusAL.of(context).exerciseLabel, + CusAL.of(context).exerciseSubtitle, + workoutWomanImageUrl, + ), ), ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const TrainingWorkouts(), - CusAL.of(context).workout, - CusAL.of(context).workoutSubtitle, - workoutManImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const TrainingWorkouts(), + CusAL.of(context).workout, + CusAL.of(context).workoutSubtitle, + workoutManImageUrl, + ), ), ), - SizedBox( - height: screenHeight / 4, - child: buildCoverCard( - context, - const TrainingPlans(), - CusAL.of(context).plan, - CusAL.of(context).planSubtitle, - workoutCalendarImageUrl, + Expanded( + child: SizedBox( + height: screenHeight / 4, + child: buildCoverCard( + context, + const TrainingPlans(), + CusAL.of(context).plan, + CusAL.of(context).planSubtitle, + workoutCalendarImageUrl, + ), ), ), ], diff --git a/lib/views/training/plans/group_list.dart b/lib/views/training/plans/group_list.dart index aba3d87..663e503 100644 --- a/lib/views/training/plans/group_list.dart +++ b/lib/views/training/plans/group_list.dart @@ -43,6 +43,8 @@ class _GroupListState extends State { // 当前计划的每个训练日最后一次训练的日志map Map logMap = {}; + late TrainingPlan planItem; + // 是否在加载数据 bool isLoading = false; @@ -53,7 +55,10 @@ class _GroupListState extends State { void initState() { super.initState(); - _getGroupListByPlanId(); + setState(() { + planItem = widget.planItem; + _getGroupListByPlanId(); + }); } // 查询指定训练中的动作列表 @@ -69,13 +74,14 @@ class _GroupListState extends State { // ???正常来讲,这里一定只有1个结果,不会有多个,也不会没有(验证就暂时不做了) // 指定计划包含多个训练 var tempPWG = await _dbHelper.searchPlanWithGroups( - planId: widget.planItem.planId, + planId: planItem.planId, ); // 查询该训练计划的跟练日志信息,用于显示每个训练的最后一次跟练时间 - var tempLog = await _dbHelper.queryLastTrainingDetailLogByPlanName( - widget.planItem, - ); + var tempLog = + await _dbHelper.queryLastTrainingDetailLogByPlanName(planItem); + + print("动作组的跟练日志$tempLog"); // 设置查询结果 setState(() { @@ -97,7 +103,7 @@ class _GroupListState extends State { // 保存现有的动作列表到当前训练中 // 必须要把plan has group中对应plan的旧的plan has group id 删除 // 然后让数据库设定的自增生效,否则显示的结果默认以主键排序,和实际显示的结果可能不一致。 - var planId = widget.planItem.planId!; + var planId = planItem.planId!; List tempList = []; for (var i = 0; i < groupList.length; i++) { @@ -110,8 +116,16 @@ class _GroupListState extends State { } try { + // 2023-12-30 也一并修改计划的周期为目前group列表的长度 await _dbHelper.renewPlanWithGroupList(planId, tempList); await _getGroupListByPlanId(); + // 虽然更新了plan,则重新获取最新的plan + var plans = await _dbHelper.queryTrainingPlanById(planItem.planId!); + if (plans.isNotEmpty) { + setState(() { + planItem = plans.first; + }); + } } catch (e) { // 弹出报错提示框 if (!mounted) return; @@ -171,7 +185,7 @@ class _GroupListState extends State { text: TextSpan( children: [ TextSpan( - text: widget.planItem.planName, + text: planItem.planName, style: TextStyle(fontSize: CusFontSizes.pageTitle), ), TextSpan( @@ -352,7 +366,7 @@ class _GroupListState extends State { MaterialPageRoute( builder: (context) => ActionList( groupItem: groupItem, - planItem: widget.planItem, + planItem: planItem, dayNumber: index + 1, ), ), diff --git a/lib/views/training/plans/index.dart b/lib/views/training/plans/index.dart index 06d0d01..acf5def 100644 --- a/lib/views/training/plans/index.dart +++ b/lib/views/training/plans/index.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_print import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; @@ -368,7 +367,7 @@ class _TrainingPlansState extends State { }, child: Text(CusAL.of(context).cancelLabel), ), - ElevatedButton( + TextButton( onPressed: () { _clickPlanModifyButton(planItem); }, @@ -399,42 +398,35 @@ class _TrainingPlansState extends State { initialValue: planItem?.planCode, validator: FormBuilderValidators.required(), ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Flexible( - child: cusFormBuilerDropdown( - "plan_category", - categoryOptions, - labelText: '*${CusAL.of(context).modifyPlanLabels('4')}', - initialValue: planItem?.planCategory, - validator: FormBuilderValidators.required(), - ), - ), - Flexible( - child: cusFormBuilerDropdown( - "plan_level", - levelOptions, - labelText: '*${CusAL.of(context).modifyPlanLabels('5')}', - initialValue: planItem?.planLevel, - validator: FormBuilderValidators.required(), - ), - ), - ], + cusFormBuilerDropdown( + "plan_category", + categoryOptions, + labelText: '*${CusAL.of(context).modifyPlanLabels('4')}', + initialValue: planItem?.planCategory, + validator: FormBuilderValidators.required(), ), - cusFormBuilerTextField( - "plan_period", - labelText: '*${CusAL.of(context).modifyPlanLabels('6')}', - initialValue: planItem?.planPeriod.toString(), - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(), - ]), - // 正则来只允许输入数字 - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - keyboardType: TextInputType.number, + cusFormBuilerDropdown( + "plan_level", + levelOptions, + labelText: '*${CusAL.of(context).modifyPlanLabels('5')}', + initialValue: planItem?.planLevel, + validator: FormBuilderValidators.required(), ), + + // 2023-12-30 实际这个周期不需要用户手动输入,它就是该plan对应的group列表的长度 + // cusFormBuilerTextField( + // "plan_period", + // labelText: '*${CusAL.of(context).modifyPlanLabels('6')}', + // initialValue: planItem?.planPeriod.toString(), + // validator: FormBuilderValidators.compose([ + // FormBuilderValidators.required(), + // ]), + // // 正则来只允许输入数字 + // inputFormatters: [ + // FilteringTextInputFormatter.digitsOnly, + // ], + // keyboardType: TextInputType.number, + // ), cusFormBuilerTextField( "description", labelText: '*${CusAL.of(context).modifyPlanLabels('7')}', @@ -456,14 +448,18 @@ class _TrainingPlansState extends State { // 获取表单数值 Map formData = _addFormKey.currentState!.value; + print("修改的计划表单数据-----$formData"); // 对周期进行类型转换 - var planPeriod = int.parse(formData['plan_period']); - // 再放回去 - // 深拷贝表单数据的Map,修改拷贝后的(原始的那个好像是不可修改的,会报错) - var copiedFormData = Map.from(formData); - copiedFormData["plan_period"] = planPeriod; + // var planPeriod = int.parse(formData['plan_period']); + // // 再放回去 + // // 深拷贝表单数据的Map,修改拷贝后的(原始的那个好像是不可修改的,会报错) + // var copiedFormData = Map.from(formData); + // copiedFormData["plan_period"] = planPeriod; + + // var temp = TrainingPlan.fromMap(copiedFormData); - var temp = TrainingPlan.fromMap(copiedFormData); + // 2023-12-30 实际这个周期不需要用户手动输入,它就是该plan对应的group列表的长度 + var temp = TrainingPlan.fromMap(formData); try { // 如果是新增 diff --git a/lib/views/training/reports/index.dart b/lib/views/training/reports/index.dart index 71628cc..52a8f56 100644 --- a/lib/views/training/reports/index.dart +++ b/lib/views/training/reports/index.dart @@ -15,12 +15,6 @@ import '../../../models/cus_app_localizations.dart'; import '../../../models/training_state.dart'; import 'export/report_pdf_viewer.dart'; -/// 默认的日历显示范围,当前月的前后3个月 -/// ???实际手记的日历显示范围的话,就第一个手记的月份,到当前月份即可 -final kToday = DateTime.now(); -final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); -final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); - class TrainingReports extends StatefulWidget { const TrainingReports({super.key}); diff --git a/lib/views/training/workouts/action_config_dialog.dart b/lib/views/training/workouts/action_config_dialog.dart index 3d7254c..ed9360c 100644 --- a/lib/views/training/workouts/action_config_dialog.dart +++ b/lib/views/training/workouts/action_config_dialog.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import '../../../common/components/dialog_widgets.dart'; import '../../../common/global/constants.dart'; import '../../../common/utils/tools.dart'; import '../../../layout/themes/cus_font_size.dart'; @@ -26,6 +27,11 @@ void showConfigDialog( int count = ad.action.frequency ?? 10; double equipmentWeight = ad.action.equipmentWeight ?? 0; + // 基础动作的图片 + List imageList = (ad.exercise.images?.trim().isNotEmpty == true) + ? ad.exercise.images!.split(",") + : []; + // 其他需要输入的例如器械重量、action名称、描述之类的 final formKey = GlobalKey(); @@ -179,15 +185,9 @@ void showConfigDialog( Stack( children: [ // 图片部分 - Container( + SizedBox( height: 200.0, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage(placeholderImageUrl), - // ???这个不好弄,暂时与屏幕宽度对其就好,不知道会不会变形或者超出 - fit: BoxFit.fitWidth, - ), - ), + child: buildImageCarouselSlider(imageList), ), ], ), diff --git a/lib/views/training/workouts/action_detail.dart b/lib/views/training/workouts/action_detail.dart index 5742eb2..9af3cdd 100644 --- a/lib/views/training/workouts/action_detail.dart +++ b/lib/views/training/workouts/action_detail.dart @@ -52,7 +52,7 @@ class _ActionDetailDialogState extends State { SizedBox(height: 40.sp, child: buildCloseButton(context)), Expanded( flex: 2, - child: buildImageArea(context, _currentItem.exercise), + child: _buildExerciseImageArea(_currentItem.exercise), ), Expanded(flex: 1, child: _buildCountArea()), Expanded( @@ -82,6 +82,13 @@ class _ActionDetailDialogState extends State { ); } + // 动作的图片 + _buildExerciseImageArea(Exercise item) { + List imageList = + (item.images?.trim().isNotEmpty == true) ? item.images!.split(",") : []; + return buildImageCarouselSlider(imageList); + } + _buildCountArea() { // 两行 // return Padding( diff --git a/lib/views/training/workouts/action_follow_practice.dart b/lib/views/training/workouts/action_follow_practice.dart index 34990a2..9a8f1cc 100644 --- a/lib/views/training/workouts/action_follow_practice.dart +++ b/lib/views/training/workouts/action_follow_practice.dart @@ -71,7 +71,7 @@ class _ActionFollowPracticeWithTTSState int _currentIndex = -1; // 预设的休息时间(从用户配置表读取的,是不变的。每次休息完成之后都要重置为这个时间) - final _defaultCusRestTime = 10; + int _defaultCusRestTime = 10; // 当前休息的倒计时时间(每个跟练间隔的休息时间用户可以调整) int _cusRestTime = 10; @@ -175,7 +175,7 @@ class _ActionFollowPracticeWithTTSState var tempUser = (await _userHelper.queryUser(userId: CacheUser.userId))!; setState(() { - _cusRestTime = tempUser.actionRestTime ?? 10; + _defaultCusRestTime = tempUser.actionRestTime ?? 10; }); } @@ -245,6 +245,12 @@ class _ActionFollowPracticeWithTTSState }); } + // 从基础动作获取其图片列表(要排除images是个空字符串) + List _getExerciseImageList(Exercise exercise) => + (exercise.images?.trim().isNotEmpty == true) + ? exercise.images!.split(",") + : []; + /// /// TTS 相关的操作=============== /// @@ -516,7 +522,9 @@ class _ActionFollowPracticeWithTTSState Expanded( // 这里的盒子,只是单纯区分休息时显示下一个要小点,跟练时图片大点 flex: 5, - child: buildExerciseImageCarouselSlider(actions[0].exercise), + child: buildImageCarouselSlider( + _getExerciseImageList(actions[0].exercise), + ), ), Expanded( flex: 2, @@ -530,7 +538,7 @@ class _ActionFollowPracticeWithTTSState TextSpan( text: "${CusAL.of(context).workoutFollowLabel('1')}\n", style: TextStyle( - color: Colors.green, + color: Theme.of(context).primaryColor, fontSize: CusFontSizes.flagBig, fontWeight: FontWeight.bold, ), @@ -538,8 +546,9 @@ class _ActionFollowPracticeWithTTSState TextSpan( text: actions[_currentIndex + 1].exercise.exerciseName, style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: CusFontSizes.flagMedium, - fontWeight: FontWeight.w400, + fontWeight: FontWeight.bold, ), ), ], @@ -608,7 +617,10 @@ class _ActionFollowPracticeWithTTSState // 跳过预备时也停止语音 _stop(); }, - icon: const Icon(Icons.arrow_forward), + icon: Icon( + Icons.arrow_forward, + color: Theme.of(context).primaryColor, + ), ), ), ], @@ -623,8 +635,8 @@ class _ActionFollowPracticeWithTTSState Expanded( // 这里的盒子,只是单纯区分休息时显示下一个要小点,跟练时图片大点 flex: 4, - child: buildExerciseImageCarouselSlider( - actions[_currentIndex].exercise, + child: buildImageCarouselSlider( + _getExerciseImageList(actions[_currentIndex].exercise), ), ), // 在跟练页面显示当前训练占全部的进度条 @@ -654,7 +666,7 @@ class _ActionFollowPracticeWithTTSState TextSpan( text: actions[_currentIndex].exercise.exerciseName, style: TextStyle( - color: Colors.green, + color: Theme.of(context).primaryColor, fontSize: CusFontSizes.flagMedium, fontWeight: FontWeight.bold, ), @@ -1161,7 +1173,7 @@ class _ActionFollowPracticeWithTTSState // 索引是从0开始的,这里显示的序号,所以+1 text: ' ${_currentIndex + 1}/${actions.length}', style: TextStyle( - color: Colors.green, + color: Theme.of(context).primaryColor, fontSize: CusFontSizes.flagMedium, fontWeight: FontWeight.bold, ), @@ -1169,8 +1181,8 @@ class _ActionFollowPracticeWithTTSState TextSpan( text: '\n${actions[_currentIndex].exercise.exerciseName}', style: TextStyle( - color: Colors.green, - fontSize: CusFontSizes.flagBig, + color: Theme.of(context).primaryColor, + fontSize: CusFontSizes.flagMedium, fontWeight: FontWeight.bold, ), ), @@ -1195,8 +1207,8 @@ class _ActionFollowPracticeWithTTSState Expanded( flex: 3, - child: buildExerciseImageCarouselSlider( - actions[_currentIndex].exercise, + child: buildImageCarouselSlider( + _getExerciseImageList(actions[_currentIndex].exercise), ), ), ]; diff --git a/lib/views/training/workouts/action_list.dart b/lib/views/training/workouts/action_list.dart index 12a5ed0..907da0c 100644 --- a/lib/views/training/workouts/action_list.dart +++ b/lib/views/training/workouts/action_list.dart @@ -312,6 +312,12 @@ class _ActionListState extends State { .join(' + '); } + // 基础动作的图片 + List imageList = + (adItem.exercise.images?.trim().isNotEmpty == true) + ? adItem.exercise.images!.split(",") + : []; + return Card( elevation: 3, key: Key('$index'), @@ -366,7 +372,7 @@ class _ActionListState extends State { flex: 6, child: Padding( padding: EdgeInsets.all(_isEditing ? 5.sp : 10.sp), - child: buildExerciseImageCarouselSlider(adItem.exercise), + child: buildImageCarouselSlider(imageList), ), ), if (_isEditing) diff --git a/lib/views/training/workouts/index.dart b/lib/views/training/workouts/index.dart index 7944596..8583825 100644 --- a/lib/views/training/workouts/index.dart +++ b/lib/views/training/workouts/index.dart @@ -198,7 +198,6 @@ class _TrainingWorkoutsState extends State { "group_category", categoryOptions, labelText: CusAL.of(context).workoutQuerys('1'), - optionFontSize: CusFontSizes.itemContent, ), ), Expanded( @@ -206,7 +205,6 @@ class _TrainingWorkoutsState extends State { "group_level", levelOptions, labelText: CusAL.of(context).workoutQuerys('2'), - optionFontSize: CusFontSizes.itemContent, ), ), ], @@ -433,7 +431,7 @@ class _TrainingWorkoutsState extends State { Navigator.of(context).pop(); }, ), - ElevatedButton( + TextButton( child: Text(CusAL.of(context).confirmLabel), onPressed: () async { if (_groupFormKey.currentState!.saveAndValidate()) { diff --git a/lib/views/training/workouts/simple_exercise_list.dart b/lib/views/training/workouts/simple_exercise_list.dart index b70d854..822fa1f 100644 --- a/lib/views/training/workouts/simple_exercise_list.dart +++ b/lib/views/training/workouts/simple_exercise_list.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_print import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../../common/components/dialog_widgets.dart'; @@ -37,10 +38,10 @@ class _SimpleExerciseListState extends State { bool isLoading = false; ScrollController scrollController = ScrollController(); - // 查询条件输入框控制器 - final queryTextController = TextEditingController(); - // 输入的条件查询关键字 - String queryConditon = ""; + // 查询表单的key + final _queryFormKey = GlobalKey(); + // 可以筛选训练,条件用个map来存,初始化一个空map + Map conditionMap = {}; @override void initState() { @@ -53,7 +54,7 @@ class _SimpleExerciseListState extends State { void dispose() { scrollController.removeListener(_scrollListener); scrollController.dispose(); - queryTextController.dispose(); + super.dispose(); } @@ -78,12 +79,27 @@ class _SimpleExerciseListState extends State { isLoading = true; }); + print("查询数据中的条件$conditionMap"); + // 查询结果是个动态的list和该表的总数据,使用list要转型 - var temp = await _dbHelper.queryExerciseByKeyword( - pageSize: pageSize, - page: currentPage, - keyword: queryConditon, - ); + CusDataResult temp; + if (conditionMap.isEmpty) { + // 没有查询条件就默认查询所有 + temp = await _dbHelper.queryExerciseByKeyword( + pageSize: pageSize, + page: currentPage, + keyword: "", + ); + } else { + // 有其他条件,就条件查询 + temp = await _dbHelper.queryExercise( + pageSize: pageSize, + page: currentPage, + level: conditionMap["level"], + exerciseName: conditionMap["exercise_name"], + category: conditionMap["category"], + ); + } List newData = temp.data as List; @@ -106,21 +122,6 @@ class _SimpleExerciseListState extends State { }); } - // 定义查询回调函数,参数为查询条件的值 - void handleQuery(String query) { - // 处理查询条件的值 - print(query); // 示例:打印查询条件的值 - - // 有变动查询条件,则重新开始查询 - setState(() { - queryConditon = query; - exerciseItems.clear(); - exerciseCount = 0; - currentPage = 1; - _loadData(); - }); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -144,51 +145,115 @@ class _SimpleExerciseListState extends State { ), body: Column( children: [ - Card(elevation: 5, child: _buildQueryRow()), + FormBuilder( + key: _queryFormKey, + child: Card( + elevation: 5.sp, + child: Column( + children: [_buildQueryAreaRow(), SizedBox(height: 10.sp)], + ), + ), + ), Expanded(child: _buildListArea()), ], ), ); } - _buildQueryRow() { + _buildQueryAreaRow() { return Row( children: [ Expanded( - flex: 3, - child: Padding( - padding: EdgeInsets.all(10.sp), - child: TextField( - // 设置文本大小 - style: TextStyle(fontSize: CusFontSizes.searchInputMedium), - decoration: InputDecoration( - // 四周带上边框 - border: const OutlineInputBorder(), - // 设置输入框大小 - contentPadding: EdgeInsets.all(10.sp), - // 占位符文本 - hintText: CusAL.of(context).queryKeywordHintText( - CusAL.of(context).exercise, - ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: cusFormBuilerTextField( + "exercise_name", + labelText: CusAL.of(context).workoutQuerys('0'), + ), + ), + SizedBox( + width: 50.sp, + height: 36.sp, + child: TextButton( + onPressed: () { + setState(() { + _queryFormKey.currentState?.reset(); + // 2023-12-12 不知道为什么,reset对下拉选中的没有效,所以手动清除 + _queryFormKey.currentState?.fields['category'] + ?.didChange(null); + _queryFormKey.currentState?.fields['level'] + ?.didChange(null); + + // 重置后重新查询 + conditionMap = {}; + currentPage = 1; + exerciseItems.clear(); + exerciseCount = 0; + _loadData(); + }); + // 如果有键盘就收起键盘 + FocusScope.of(context).focusedChild?.unfocus(); + }, + child: Text( + CusAL.of(context).resetLabel, + style: TextStyle( + fontSize: CusFontSizes.pageAppendix, + color: Theme.of(context).primaryColor, + ), + ), + ), + ), + ], ), - controller: queryTextController, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: cusFormBuilerDropdown( + "category", + categoryOptions, + labelText: CusAL.of(context).workoutQuerys('1'), + ), + ), + Expanded( + child: cusFormBuilerDropdown( + "level", + levelOptions, + labelText: CusAL.of(context).workoutQuerys('2'), + ), + ), + ], + ), + ], ), ), - Expanded( - flex: 1, - child: Center( - child: ElevatedButton( - onPressed: () { - // 点击查询按钮时收起键盘 - FocusScope.of(context).unfocus(); - // 执行条件查询 - handleQuery(queryTextController.text); - }, - child: const Icon(Icons.search), - ), + Container( + width: 50.sp, + alignment: Alignment.center, + child: IconButton( + icon: Icon(Icons.search, color: Theme.of(context).primaryColor), + onPressed: () { + if (_queryFormKey.currentState!.saveAndValidate()) { + setState(() { + conditionMap = _queryFormKey.currentState!.value; + + // 重新查询的结果要全部替换掉之前的结果 + currentPage = 1; + exerciseItems.clear(); + exerciseCount = 0; + _loadData(); + }); + } + // 如果有键盘就收起键盘 + FocusScope.of(context).focusedChild?.unfocus(); + }, ), - ), + ) ], ); } @@ -202,6 +267,12 @@ class _SimpleExerciseListState extends State { return buildLoader(isLoading); } else { var exerciseItem = exerciseItems[index]; + + List imageList = + (exerciseItem.images?.trim().isNotEmpty == true) + ? exerciseItem.images!.split(",") + : []; + return Card( elevation: 10, child: GestureDetector( @@ -222,7 +293,7 @@ class _SimpleExerciseListState extends State { flex: 9, child: ListTile( title: Text( - "$index-${exerciseItem.exerciseName}", + "${index + 1}-${exerciseItem.exerciseName}", overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( @@ -247,7 +318,7 @@ class _SimpleExerciseListState extends State { height: 80.sp, child: Padding( padding: EdgeInsets.all(5.sp), - child: buildExerciseImageCarouselSlider(exerciseItem), + child: buildImageCarouselSlider(imageList), ), ), ), diff --git a/readme-merged.md b/readme-merged.md index 5aae25e..f15f546 100644 --- a/readme-merged.md +++ b/readme-merged.md @@ -165,3 +165,55 @@ - fix:统一动作、训练、计划的分类为同一个列表;修正训练的动作组为空时不显示“开始”按钮。 flutter build apk --split-per-abi + +### 2023-12-30 + +- bug 或需要修改的问题列表: + + - done 初次使用的输入框有背景色;且就算有输入数据还是使用的预设值(按钮文字含义不明确) + - done 首次输入的身高体重也应该放到体重趋势表 + - done 基本信息的修改文字按钮看不清,应该是用的 primaryColor + - done 基础动作的条件查询按钮和训练、计划不一致 + - (保留不一致,因为查询条件不一样、列表的栏位也不一样、删除方式也不一样,是故意的。) + - done 下拉选择框文字大小不一致,英文时又可能太长无法完整显示 + - 统一在组件内部限定,中文标签时 16.sp,英文时 13.sp + - done 基础动作的 ttsNote、用户上传等栏位没有意义,不要显示 + - done 基础动作修改的肌肉多选弹窗的按钮是英文,且搜索框有背景色 + - 数量不多,不启用搜索 + - done 基础动作详情弹窗的图片不能点击缩放预览 + - done 所有的地方的 appbar 处的按钮都应该一致,都改为图标,有的地方还是文字 + - 有的地方比如手记的新增保留了文字+图表的按钮。 + - done 新建训练和修改训练的标题文字反了 + - done 新建计划和修改计划的标题文字反了 + - done 修改训练组时,给它添加新的动作没有分类筛选的功能,只有关键字(应该和计划添加训练一致) + - ???跟练完查看报告后返回依旧是跟练的最后一个动作的页面 + - 等待复现 + - done 跟练的休息间隔时间一开始是 db 中的 30s,但不知道是不是点击了退出弹窗或者暂停按钮,下一个动作时变为了预设的 10s + - ??? 修改计划弹窗,点击确认之后,退出到上一层(不应该),且弹窗没有关闭 + - 突然又没有了,等待复现 + - done 修改计划弹窗中的“周期栏位”好像没意义,因为添加的训练组和输入周期不匹配没有问题,没做限制 + - 周期栏位不显示,仅存在理论作用,即根据该计划包含的训练数量为实际业务依据。 + - done 基础动作条件查询时,如果有弹出键盘,点击 appbar 返回按钮时会有像素溢出问题,(训练、计划模块也是如此,饮食模块中子模块返回亦同) + - 这个问题就是点击返回按钮时键盘没有收起来,所以即便加上了 expanded,还是有一瞬间看到被压缩的子模块的样子 + - 就算修改了 appbar 的 leading,点击时先收起键盘`FocusScope.of(context).unfocus();`再 pop,还是有一瞬间能看到压缩后的样子。 + - 切换中英文后,弹窗的文字没有变,要有其他动作(比如点击)之后才改变为目标文字 + - done 所有的 exercise 的轮播图和公共轮播组件不一致,没有点击缩放预览功能 + - done 训练新增动作时,动作配置页面的图片不对 + - done 不同地方的取消和确定按钮的样式不对(有点左边是确定,有的右边是确定,有的都是 TextButton,有的是 ElevatedButton) + - 所有的 AlertDialog 的按钮都是 TextButton,且一般都是左边取消右边确认 + +--- + +有好些 bug 有出现过,但又没法直接复现,但可能还是会出现。其中有部分我猜测是手机性能不够,我以为正常很快的结果并不是,尤其是页面 pop 或者 posh 是在异步函数中的时候。 + +--- + +- done 手记的时间线模式应该按照时间倒序 +- 有修改过的手记,在时间线模式的时间戳没显示时间 +- 编辑手记时分类和心情被选中的模式要改一下,不要前面的勾,因为被挤压变现严重 +- 饮食报告的 pdf,中文标题无法显示,正文的中文倒是正常(运动的 pdf 没有问题) +- 餐次相册,在饮食日记给对应餐次添加照片,好像全是当日的餐次,如果是切换到其他日期,也是今天的。 +- 此外假如正在预览昨天的饮食记录,看到显示的图片及其数量也是今天的。 +- 餐次相册模块的照片下方应该有点空余,不然不好看 +- 饮食报告的今天昨天本周上周的下拉框字体大小不是 16 和 12sp +- 食物列表应该按食物新增时间倒序排序,不管是食物成分模块还是新增饮食记录