import 'package:common_utils/common_utils.dart'; import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; import 'package:get/get.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; import '../controller.dart'; import 'setting_components.dart'; Widget settingSync(BuildContext context) { final controller = Get.find(); return Obx(() { final sys = controller.sysSetting.value; if (sys == null) return const SettingEmpty(message: '暂无同步信息'); final items = <_SyncItem>[ _SyncItem('config', '同步设备信息', sys.syncTime, controller.syncConfig), _SyncItem('mq', '同步长链接', sys.mqSyncTime, controller.syncMq), _SyncItem( 'classify', '同步分类', sys.sysType == SysType.CANTEEN ? sys.canteenClassifySyncTime : sys.supermarketClassifySyncTime, controller.syncClassify, ), _SyncItem( 'product', '同步商品', sys.sysType == SysType.CANTEEN ? sys.canteenProductSyncTime : sys.supermarketProductSyncTime, controller.syncProduct, ), _SyncItem( 'offlineOrder', '同步离线订单', sys.offlineOrderSyncTime, controller.syncOfflineOrder, icon: FAssets.icons.cloudDownload, ), ]; return Column( children: [ Expanded( child: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: XfjDesignTokens.cardGap.w, mainAxisSpacing: XfjDesignTokens.cardGap.h, childAspectRatio: 3.25, ), itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; return _SyncCard( item: item, syncing: controller.syncingKey.value == item.key, failed: controller.syncFailedKeys.contains(item.key), enabled: controller.syncingKey.isEmpty, ); }, ), ), SizedBox(height: XfjDesignTokens.cardGap.h), const SettingNotice( text: '网络接口可用不等于业务服务可用。同步失败时会保留本地数据,可在网络恢复后重新执行。', ), ], ); }); } class _SyncItem { const _SyncItem(this.key, this.title, this.time, this.action, {this.icon}); final String key; final String title; final DateTime? time; final Future Function() action; final SvgAsset? icon; } class _SyncCard extends StatelessWidget { const _SyncCard({ required this.item, required this.syncing, required this.failed, required this.enabled, }); final _SyncItem item; final bool syncing; final bool failed; final bool enabled; @override Widget build(BuildContext context) { final theme = FTheme.of(context); final statusColor = failed ? XfjDesignTokens.danger : syncing ? XfjDesignTokens.primary : XfjDesignTokens.success; return Opacity( opacity: enabled || syncing ? 1 : 0.55, child: InkWell( onTap: enabled ? item.action : null, borderRadius: BorderRadius.circular(XfjDesignTokens.cardRadius), child: Container( padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: XfjDesignTokens.surface, border: Border.all(color: XfjDesignTokens.line), borderRadius: BorderRadius.circular(XfjDesignTokens.cardRadius), boxShadow: XfjDesignTokens.surfaceShadow, ), child: Row( children: [ Container( width: 56.w, height: 56.w, alignment: Alignment.center, decoration: BoxDecoration( color: XfjDesignTokens.primarySoft, borderRadius: BorderRadius.circular( XfjDesignTokens.cardRadius, ), ), child: syncing ? SizedBox( width: 28.w, height: 28.w, child: const CircularProgressIndicator(strokeWidth: 3), ) : FIcon( item.icon ?? FAssets.icons.refreshCw, color: XfjDesignTokens.primary, size: 30.sp, ), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( item.title, style: theme.typography.lg.copyWith( color: XfjDesignTokens.ink, fontWeight: FontWeight.w800, ), ), SizedBox(height: 8.h), Text( '最后同步时间:${_formatTime(item.time)}', style: theme.typography.sm.copyWith( color: XfjDesignTokens.muted, ), ), SizedBox(height: 8.h), Text( failed ? '同步失败,点击重试' : syncing ? '同步中...' : '状态正常', style: theme.typography.sm.copyWith( color: statusColor, fontWeight: FontWeight.w600, ), ), ], ), ), FIcon( FAssets.icons.chevronRight, color: XfjDesignTokens.muted, size: 22.sp, ), ], ), ), ), ); } } String _formatTime(DateTime? time) { if (time == null) return '未同步'; return DateUtil.formatDate(time, format: DateFormats.full); }