feat(frontend): 前端时间显示统一转换为本地时间
- mobile-app: 新增 DateTimeUtils 工具类处理 UTC -> 本地时间转换 - mobile-app: 修改 ledger_detail_page 和 profile_page 使用本地时间 - admin-web: 添加 dayjs 自动转换注释说明 - mobile-upgrade: 优化 toLocaleString 格式化选项 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
7ae6af7841
commit
19bd804a21
|
|
@ -41,6 +41,7 @@ export function formatPercentage(value: number, decimals = 1): string {
|
|||
|
||||
/**
|
||||
* 格式化日期
|
||||
* dayjs 会自动将 UTC 时间转换为本地时间显示
|
||||
*/
|
||||
export function formatDate(date: string | Date, format = 'YYYY-MM-DD'): string {
|
||||
return dayjs(date).format(format);
|
||||
|
|
@ -48,6 +49,7 @@ export function formatDate(date: string | Date, format = 'YYYY-MM-DD'): string {
|
|||
|
||||
/**
|
||||
* 格式化日期时间
|
||||
* dayjs 会自动将 UTC 时间转换为本地时间显示
|
||||
*/
|
||||
export function formatDateTime(date: string | Date, format = 'YYYY-MM-DD HH:mm:ss'): string {
|
||||
return dayjs(date).format(format);
|
||||
|
|
@ -55,6 +57,7 @@ export function formatDateTime(date: string | Date, format = 'YYYY-MM-DD HH:mm:s
|
|||
|
||||
/**
|
||||
* 格式化相对时间 (如: 5分钟前, 2小时前)
|
||||
* dayjs 会自动将 UTC 时间转换为本地时间计算
|
||||
*/
|
||||
export function formatRelativeTime(date: string | Date): string {
|
||||
return dayjs(date).fromNow();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
import 'package:intl/intl.dart';
|
||||
|
||||
/// 日期时间工具类
|
||||
/// 处理 UTC 时间到本地时间的转换和格式化
|
||||
class DateTimeUtils {
|
||||
DateTimeUtils._();
|
||||
|
||||
/// 格式化日期时间(完整格式)
|
||||
/// 自动将 UTC 时间转换为本地时间
|
||||
static String formatDateTime(DateTime? dateTime) {
|
||||
if (dateTime == null) return '-';
|
||||
final localTime = dateTime.toLocal();
|
||||
return DateFormat('yyyy-MM-dd HH:mm').format(localTime);
|
||||
}
|
||||
|
||||
/// 格式化日期时间(带秒)
|
||||
static String formatDateTimeFull(DateTime? dateTime) {
|
||||
if (dateTime == null) return '-';
|
||||
final localTime = dateTime.toLocal();
|
||||
return DateFormat('yyyy-MM-dd HH:mm:ss').format(localTime);
|
||||
}
|
||||
|
||||
/// 格式化日期(仅日期)
|
||||
static String formatDate(DateTime? dateTime) {
|
||||
if (dateTime == null) return '-';
|
||||
final localTime = dateTime.toLocal();
|
||||
return DateFormat('yyyy-MM-dd').format(localTime);
|
||||
}
|
||||
|
||||
/// 格式化短日期(月/日)
|
||||
static String formatShortDate(DateTime? dateTime) {
|
||||
if (dateTime == null) return '-';
|
||||
final localTime = dateTime.toLocal();
|
||||
return DateFormat('MM/dd').format(localTime);
|
||||
}
|
||||
|
||||
/// 从字符串解析日期并格式化短日期
|
||||
static String formatShortDateFromString(String? dateStr) {
|
||||
if (dateStr == null || dateStr.isEmpty) return '-';
|
||||
try {
|
||||
final parsed = DateTime.parse(dateStr);
|
||||
return formatShortDate(parsed);
|
||||
} catch (e) {
|
||||
return dateStr;
|
||||
}
|
||||
}
|
||||
|
||||
/// 格式化时间(仅时间)
|
||||
static String formatTime(DateTime? dateTime) {
|
||||
if (dateTime == null) return '-';
|
||||
final localTime = dateTime.toLocal();
|
||||
return DateFormat('HH:mm').format(localTime);
|
||||
}
|
||||
|
||||
/// 格式化相对时间(如:刚刚、5分钟前、1小时前、昨天)
|
||||
static String formatRelativeTime(DateTime? dateTime) {
|
||||
if (dateTime == null) return '-';
|
||||
final localTime = dateTime.toLocal();
|
||||
final now = DateTime.now();
|
||||
final diff = now.difference(localTime);
|
||||
|
||||
if (diff.inSeconds < 60) {
|
||||
return '刚刚';
|
||||
} else if (diff.inMinutes < 60) {
|
||||
return '${diff.inMinutes}分钟前';
|
||||
} else if (diff.inHours < 24) {
|
||||
return '${diff.inHours}小时前';
|
||||
} else if (diff.inDays < 2) {
|
||||
return '昨天 ${formatTime(dateTime)}';
|
||||
} else if (diff.inDays < 7) {
|
||||
return '${diff.inDays}天前';
|
||||
} else {
|
||||
return formatDate(dateTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断是否是今天
|
||||
static bool isToday(DateTime? dateTime) {
|
||||
if (dateTime == null) return false;
|
||||
final localTime = dateTime.toLocal();
|
||||
final now = DateTime.now();
|
||||
return localTime.year == now.year &&
|
||||
localTime.month == now.month &&
|
||||
localTime.day == now.day;
|
||||
}
|
||||
|
||||
/// 判断是否是昨天
|
||||
static bool isYesterday(DateTime? dateTime) {
|
||||
if (dateTime == null) return false;
|
||||
final localTime = dateTime.toLocal();
|
||||
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||
return localTime.year == yesterday.year &&
|
||||
localTime.month == yesterday.month &&
|
||||
localTime.day == yesterday.day;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import '../../../../core/storage/storage_keys.dart';
|
|||
import '../../../../core/services/referral_service.dart';
|
||||
import '../../../../core/services/reward_service.dart';
|
||||
import '../../../../core/services/notification_service.dart';
|
||||
import '../../../../core/utils/date_utils.dart';
|
||||
import '../../../../routes/route_paths.dart';
|
||||
import '../../../../routes/app_router.dart';
|
||||
import '../../../auth/presentation/providers/auth_provider.dart';
|
||||
|
|
@ -2500,9 +2501,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
|
||||
/// 构建单条可结算奖励项
|
||||
Widget _buildSettleableRewardItem(SettleableRewardItem item) {
|
||||
// 格式化时间(优先使用 claimedAt,否则使用 createdAt)
|
||||
// 格式化时间(优先使用 claimedAt,否则使用 createdAt)- UTC -> 本地时间
|
||||
final displayDate = item.claimedAt ?? item.createdAt;
|
||||
final settledDate = '${displayDate.month}/${displayDate.day} ${displayDate.hour.toString().padLeft(2, '0')}:${displayDate.minute.toString().padLeft(2, '0')}';
|
||||
final localDate = displayDate.toLocal();
|
||||
final settledDate = '${localDate.month}/${localDate.day} ${localDate.hour.toString().padLeft(2, '0')}:${localDate.minute.toString().padLeft(2, '0')}';
|
||||
|
||||
// 构建金额显示文本
|
||||
final List<String> amountParts = [];
|
||||
|
|
@ -2585,9 +2587,10 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
|
||||
/// 构建堆叠卡片样式的可结算奖励项
|
||||
Widget _buildStackedSettleableRewardCard(SettleableRewardItem item, bool isSelected) {
|
||||
// 格式化时间(优先使用 claimedAt,否则使用 createdAt)
|
||||
// 格式化时间(优先使用 claimedAt,否则使用 createdAt)- UTC -> 本地时间
|
||||
final displayDate = item.claimedAt ?? item.createdAt;
|
||||
final settledDate = '${displayDate.month}/${displayDate.day} ${displayDate.hour.toString().padLeft(2, '0')}:${displayDate.minute.toString().padLeft(2, '0')}';
|
||||
final localDate = displayDate.toLocal();
|
||||
final settledDate = '${localDate.month}/${localDate.day} ${localDate.hour.toString().padLeft(2, '0')}:${localDate.minute.toString().padLeft(2, '0')}';
|
||||
|
||||
// 构建金额显示文本
|
||||
final List<String> amountParts = [];
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import '../../../../core/di/injection_container.dart';
|
||||
import '../../../../core/services/wallet_service.dart';
|
||||
import '../../../../core/utils/date_utils.dart';
|
||||
|
||||
/// 账本明细页面 - 显示用户的流水账、统计图表和筛选功能
|
||||
class LedgerDetailPage extends ConsumerStatefulWidget {
|
||||
|
|
@ -186,19 +187,14 @@ class _LedgerDetailPageState extends ConsumerState<LedgerDetailPage>
|
|||
return formatter.format(amount);
|
||||
}
|
||||
|
||||
/// 格式化日期
|
||||
/// 格式化日期 (UTC -> 本地时间)
|
||||
String _formatDate(DateTime date) {
|
||||
return DateFormat('yyyy-MM-dd HH:mm').format(date);
|
||||
return DateTimeUtils.formatDateTime(date);
|
||||
}
|
||||
|
||||
/// 格式化短日期
|
||||
/// 格式化短日期 (UTC -> 本地时间)
|
||||
String _formatShortDate(String date) {
|
||||
try {
|
||||
final parsed = DateTime.parse(date);
|
||||
return DateFormat('MM/dd').format(parsed);
|
||||
} catch (e) {
|
||||
return date;
|
||||
}
|
||||
return DateTimeUtils.formatShortDateFromString(date);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -23,9 +23,18 @@ export function VersionCard({ version, onEdit, onDelete, onToggle }: VersionCard
|
|||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
||||
}
|
||||
|
||||
// 格式化日期时间 - UTC 自动转换为本地时间
|
||||
const formatDate = (dateStr: string | null) => {
|
||||
if (!dateStr) return '-'
|
||||
return new Date(dateStr).toLocaleString('zh-CN')
|
||||
return new Date(dateStr).toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in New Issue