Commit f9141f05bb6fcc1e7e270c6dd25440b54b8c05f2

Authored by 常守达
1 parent 2267996c

feat(chart): 时间展示兼容

@@ -555,7 +555,7 @@ class _LatestHrvCard extends StatelessWidget { @@ -555,7 +555,7 @@ class _LatestHrvCard extends StatelessWidget {
555 return const SizedBox.shrink(); 555 return const SizedBox.shrink();
556 } 556 }
557 557
558 - final latestDataTimeText = DateFormat('HH:mm') 558 + final latestDataTimeText = DateFormat.jm()
559 .format(DateTime.fromMillisecondsSinceEpoch(latestDataTime * 1000)); 559 .format(DateTime.fromMillisecondsSinceEpoch(latestDataTime * 1000));
560 return Container( 560 return Container(
561 margin: EdgeInsets.only(bottom: 12), 561 margin: EdgeInsets.only(bottom: 12),
@@ -8,6 +8,7 @@ import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; @@ -8,6 +8,7 @@ import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
8 import 'package:fl_chart/fl_chart.dart'; 8 import 'package:fl_chart/fl_chart.dart';
9 import 'package:flutter/material.dart'; 9 import 'package:flutter/material.dart';
10 import 'package:get/get.dart'; 10 import 'package:get/get.dart';
  11 +import 'package:intl/intl.dart';
11 import 'package:lottie/lottie.dart'; 12 import 'package:lottie/lottie.dart';
12 13
13 import '../../controllers/today_controller.dart'; 14 import '../../controllers/today_controller.dart';
@@ -353,7 +354,16 @@ class TodayHrvChartCard extends StatelessWidget { @@ -353,7 +354,16 @@ class TodayHrvChartCard extends StatelessWidget {
353 if ((seconds - rounded * 3600).abs() > 30) return null; 354 if ((seconds - rounded * 3600).abs() > 30) return null;
354 if (rounded < 0 || rounded > 24) return null; 355 if (rounded < 0 || rounded > 24) return null;
355 if (rounded % 6 != 0) return null; 356 if (rounded % 6 != 0) return null;
356 - return '${rounded.toString().padLeft(2, '0')}:00'; 357 + final is24Hour = MediaQuery.of(Get.context!).alwaysUse24HourFormat;
  358 + if (is24Hour) {
  359 + return '${rounded.toString().padLeft(2, '0')}:00';
  360 + } else {
  361 + final now = DateTime.now();
  362 + final time = DateTime(now.year, now.month, now.day)
  363 + .add(Duration(seconds: seconds.round()));
  364 +
  365 + return DateFormat.j().format(time);
  366 + }
357 } 367 }
358 368
359 /// 按 state(1=过载/2=注意/3=正常/4=优秀)返回颜色 369 /// 按 state(1=过载/2=注意/3=正常/4=优秀)返回颜色
@@ -499,10 +509,18 @@ class TodayHrvChartCard extends StatelessWidget { @@ -499,10 +509,18 @@ class TodayHrvChartCard extends StatelessWidget {
499 509
500 /// 将距当天0点的秒数转为 HH:MM 格式(用于 tooltip) 510 /// 将距当天0点的秒数转为 HH:MM 格式(用于 tooltip)
501 String _formatHour(double seconds) { 511 String _formatHour(double seconds) {
502 - final total = seconds.round();  
503 - final h = (total ~/ 3600).clamp(0, 23).toString().padLeft(2, '0');  
504 - final m = ((total % 3600) ~/ 60).toString().padLeft(2, '0');  
505 - return '$h:$m'; 512 + final is24Hour = MediaQuery.of(Get.context!).alwaysUse24HourFormat;
  513 + if (is24Hour) {
  514 + final total = seconds.round();
  515 + final h = (total ~/ 3600).clamp(0, 23).toString().padLeft(2, '0');
  516 + final m = ((total % 3600) ~/ 60).toString().padLeft(2, '0');
  517 + return '$h:$m';
  518 + } else {
  519 + final now = DateTime.now();
  520 + final time = DateTime(now.year, now.month, now.day)
  521 + .add(Duration(seconds: seconds.round()));
  522 + return DateFormat.jm().format(time);
  523 + }
506 } 524 }
507 } 525 }
508 526
@@ -92,66 +92,66 @@ class PhoneLoginView extends GetView<LoginController> { @@ -92,66 +92,66 @@ class PhoneLoginView extends GetView<LoginController> {
92 crossAxisAlignment: CrossAxisAlignment.stretch, 92 crossAxisAlignment: CrossAxisAlignment.stretch,
93 children: [ 93 children: [
94 // 手机号输入框 94 // 手机号输入框
95 - Obx(() {  
96 - final phoneNotEmpty =  
97 - controller.phoneInput.value.isNotEmpty;  
98 - return TextField(  
99 - controller: controller.phoneController,  
100 - keyboardType: TextInputType.phone,  
101 - maxLength: 13,  
102 - contextMenuBuilder: (BuildContext context,  
103 - EditableTextState editableTextState) {  
104 - // 获取默认的菜单项列表  
105 - List<ContextMenuButtonItem> buttonItems =  
106 - editableTextState.contextMenuButtonItems; 95 + TextField(
  96 + controller: controller.phoneController,
  97 + keyboardType: TextInputType.phone,
  98 + maxLength: 13,
  99 + contextMenuBuilder: (BuildContext context,
  100 + EditableTextState editableTextState) {
  101 + // 获取默认的菜单项列表
  102 + List<ContextMenuButtonItem> buttonItems =
  103 + editableTextState.contextMenuButtonItems;
107 104
108 - // 例如:只保留“粘贴” (Paste)  
109 - buttonItems.removeWhere((item) =>  
110 - item.type != ContextMenuButtonType.paste);  
111 - return AdaptiveTextSelectionToolbar.buttonItems(  
112 - anchors: editableTextState.contextMenuAnchors,  
113 - buttonItems: buttonItems,  
114 - );  
115 - },  
116 - inputFormatters: [  
117 - FilteringTextInputFormatter.allow(  
118 - RegExp(r'[0-9\s]')),  
119 - _PhoneTextInputFormatter(),  
120 - ],  
121 - style: TextStyle(  
122 - color: context.colors.textPrimary, 105 + // 例如:只保留“粘贴” (Paste)
  106 + buttonItems.removeWhere((item) =>
  107 + item.type != ContextMenuButtonType.paste);
  108 + return AdaptiveTextSelectionToolbar.buttonItems(
  109 + anchors: editableTextState.contextMenuAnchors,
  110 + buttonItems: buttonItems,
  111 + );
  112 + },
  113 + inputFormatters: [
  114 + FilteringTextInputFormatter.allow(
  115 + RegExp(r'[0-9\s]')),
  116 + _PhoneTextInputFormatter(),
  117 + ],
  118 + style: TextStyle(
  119 + color: context.colors.textPrimary,
  120 + fontSize: 16,
  121 + fontWeight: FontWeight.w400,
  122 + ),
  123 + decoration: InputDecoration(
  124 + hintText: l10n.phoneLoginPhoneHint,
  125 + hintStyle: TextStyle(
  126 + color: context.colors.textTertiary,
123 fontSize: 16, 127 fontSize: 16,
124 - fontWeight: FontWeight.w400,  
125 ), 128 ),
126 - decoration: InputDecoration(  
127 - hintText: l10n.phoneLoginPhoneHint,  
128 - hintStyle: TextStyle(  
129 - color: context.colors.textTertiary,  
130 - fontSize: 16,  
131 - ),  
132 - counterText: '',  
133 - filled: true,  
134 - fillColor: Color(0xFFF3F3F3),  
135 - contentPadding: const EdgeInsets.symmetric(  
136 - horizontal: 20,  
137 - vertical: 16,  
138 - ),  
139 - border: OutlineInputBorder(  
140 - borderRadius: BorderRadius.circular(27),  
141 - borderSide: BorderSide.none,  
142 - ),  
143 - enabledBorder: OutlineInputBorder(  
144 - borderRadius: BorderRadius.circular(27),  
145 - borderSide: BorderSide.none,  
146 - ),  
147 - focusedBorder: OutlineInputBorder(  
148 - borderRadius: BorderRadius.circular(27),  
149 - borderSide: BorderSide(  
150 - color: context.colors.primary,  
151 - width: 1,  
152 - ), 129 + counterText: '',
  130 + filled: true,
  131 + fillColor: const Color(0xFFF3F3F3),
  132 + contentPadding: const EdgeInsets.symmetric(
  133 + horizontal: 20,
  134 + vertical: 16,
  135 + ),
  136 + border: OutlineInputBorder(
  137 + borderRadius: BorderRadius.circular(27),
  138 + borderSide: BorderSide.none,
  139 + ),
  140 + enabledBorder: OutlineInputBorder(
  141 + borderRadius: BorderRadius.circular(27),
  142 + borderSide: BorderSide.none,
  143 + ),
  144 + focusedBorder: OutlineInputBorder(
  145 + borderRadius: BorderRadius.circular(27),
  146 + borderSide: BorderSide(
  147 + color: context.colors.primary,
  148 + width: 1,
153 ), 149 ),
154 - suffixIcon: phoneNotEmpty 150 + ),
  151 + suffixIcon: Obx(() {
  152 + final phoneNotEmpty =
  153 + controller.phoneInput.value.isNotEmpty;
  154 + return phoneNotEmpty
155 ? GestureDetector( 155 ? GestureDetector(
156 onTap: controller.phoneController.clear, 156 onTap: controller.phoneController.clear,
157 child: Icon( 157 child: Icon(
@@ -160,82 +160,84 @@ class PhoneLoginView extends GetView<LoginController> { @@ -160,82 +160,84 @@ class PhoneLoginView extends GetView<LoginController> {
160 size: 18, 160 size: 18,
161 ), 161 ),
162 ) 162 )
163 - : null,  
164 - ),  
165 - );  
166 - }), 163 + : const SizedBox.shrink();
  164 + }),
  165 + ),
  166 + ),
167 167
168 const SizedBox(height: 16), 168 const SizedBox(height: 16),
169 169
170 // 验证码输入框(内嵌发送按钮) 170 // 验证码输入框(内嵌发送按钮)
171 - Obx(() {  
172 - final countdown = controller.countdownSeconds.value;  
173 - final isSending = controller.isSendingCode.value;  
174 - final isCounting = countdown > 0;  
175 - final hasSent = controller.hasSentCode.value;  
176 -  
177 - String btnText = hasSent  
178 - ? l10n.phoneLoginResend  
179 - : l10n.phoneLoginSendCode;  
180 - if (isSending) {  
181 - btnText = l10n.phoneLoginSending;  
182 - } else if (isCounting) {  
183 - btnText = l10n.phoneLoginSentCountdown(countdown);  
184 - }  
185 -  
186 - return TextField(  
187 - controller: controller.codeController,  
188 - focusNode: controller.codeFocusNode,  
189 - keyboardType: TextInputType.number,  
190 - maxLength: 6,  
191 - contextMenuBuilder: (BuildContext context,  
192 - EditableTextState editableTextState) {  
193 - // 获取默认的菜单项列表  
194 - List<ContextMenuButtonItem> buttonItems =  
195 - editableTextState.contextMenuButtonItems; 171 + TextField(
  172 + controller: controller.codeController,
  173 + focusNode: controller.codeFocusNode,
  174 + keyboardType: TextInputType.number,
  175 + maxLength: 6,
  176 + contextMenuBuilder: (BuildContext context,
  177 + EditableTextState editableTextState) {
  178 + // 获取默认的菜单项列表
  179 + List<ContextMenuButtonItem> buttonItems =
  180 + editableTextState.contextMenuButtonItems;
196 181
197 - // 例如:只保留“粘贴” (Paste)  
198 - buttonItems.removeWhere((item) =>  
199 - item.type != ContextMenuButtonType.paste);  
200 - return AdaptiveTextSelectionToolbar.buttonItems(  
201 - anchors: editableTextState.contextMenuAnchors,  
202 - buttonItems: buttonItems,  
203 - );  
204 - },  
205 - style: TextStyle(  
206 - color: context.colors.textPrimary, 182 + // 例如:只保留“粘贴” (Paste)
  183 + buttonItems.removeWhere((item) =>
  184 + item.type != ContextMenuButtonType.paste);
  185 + return AdaptiveTextSelectionToolbar.buttonItems(
  186 + anchors: editableTextState.contextMenuAnchors,
  187 + buttonItems: buttonItems,
  188 + );
  189 + },
  190 + style: TextStyle(
  191 + color: context.colors.textPrimary,
  192 + fontSize: 16,
  193 + fontWeight: FontWeight.w400,
  194 + ),
  195 + decoration: InputDecoration(
  196 + hintText: l10n.phoneLoginCodeHint,
  197 + hintStyle: TextStyle(
  198 + color: context.colors.textTertiary,
207 fontSize: 16, 199 fontSize: 16,
208 - fontWeight: FontWeight.w400,  
209 ), 200 ),
210 - decoration: InputDecoration(  
211 - hintText: l10n.phoneLoginCodeHint,  
212 - hintStyle: TextStyle(  
213 - color: context.colors.textTertiary,  
214 - fontSize: 16,  
215 - ),  
216 - counterText: '',  
217 - filled: true,  
218 - fillColor: Color(0xFFF3F3F3),  
219 - contentPadding: const EdgeInsets.symmetric(  
220 - horizontal: 20,  
221 - vertical: 16,  
222 - ),  
223 - border: OutlineInputBorder(  
224 - borderRadius: BorderRadius.circular(27),  
225 - borderSide: BorderSide.none,  
226 - ),  
227 - enabledBorder: OutlineInputBorder(  
228 - borderRadius: BorderRadius.circular(27),  
229 - borderSide: BorderSide.none,  
230 - ),  
231 - focusedBorder: OutlineInputBorder(  
232 - borderRadius: BorderRadius.circular(27),  
233 - borderSide: BorderSide(  
234 - color: context.colors.primary,  
235 - width: 1,  
236 - ), 201 + counterText: '',
  202 + filled: true,
  203 + fillColor: const Color(0xFFF3F3F3),
  204 + contentPadding: const EdgeInsets.symmetric(
  205 + horizontal: 20,
  206 + vertical: 16,
  207 + ),
  208 + border: OutlineInputBorder(
  209 + borderRadius: BorderRadius.circular(27),
  210 + borderSide: BorderSide.none,
  211 + ),
  212 + enabledBorder: OutlineInputBorder(
  213 + borderRadius: BorderRadius.circular(27),
  214 + borderSide: BorderSide.none,
  215 + ),
  216 + focusedBorder: OutlineInputBorder(
  217 + borderRadius: BorderRadius.circular(27),
  218 + borderSide: BorderSide(
  219 + color: context.colors.primary,
  220 + width: 1,
237 ), 221 ),
238 - suffixIcon: Padding( 222 + ),
  223 + suffixIcon: Obx(() {
  224 + final countdown =
  225 + controller.countdownSeconds.value;
  226 + final isSending = controller.isSendingCode.value;
  227 + final isCounting = countdown > 0;
  228 + final hasSent = controller.hasSentCode.value;
  229 +
  230 + String btnText = hasSent
  231 + ? l10n.phoneLoginResend
  232 + : l10n.phoneLoginSendCode;
  233 + if (isSending) {
  234 + btnText = l10n.phoneLoginSending;
  235 + } else if (isCounting) {
  236 + btnText =
  237 + l10n.phoneLoginSentCountdown(countdown);
  238 + }
  239 +
  240 + return Padding(
239 padding: const EdgeInsets.only(right: 12), 241 padding: const EdgeInsets.only(right: 12),
240 child: TextButton( 242 child: TextButton(
241 onPressed: controller.canRequestCode 243 onPressed: controller.canRequestCode
@@ -257,12 +259,12 @@ class PhoneLoginView extends GetView<LoginController> { @@ -257,12 +259,12 @@ class PhoneLoginView extends GetView<LoginController> {
257 ), 259 ),
258 child: Text(btnText), 260 child: Text(btnText),
259 ), 261 ),
260 - ),  
261 - suffixIconConstraints:  
262 - const BoxConstraints(minWidth: 0),  
263 - ),  
264 - );  
265 - }), 262 + );
  263 + }),
  264 + suffixIconConstraints:
  265 + const BoxConstraints(minWidth: 0),
  266 + ),
  267 + ),
266 268
267 const SizedBox(height: 56), 269 const SizedBox(height: 56),
268 270