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 deviceStatus = AppDeviceStatusTool.status.value; final deviceProgress = AppDeviceStatusTool.progress.value; final availableDeviceCount = [ deviceStatus.printerAvailable, deviceStatus.steelyardAvailable, deviceStatus.cardReaderAvailable, deviceStatus.subScreenAvailable, ].where((available) => available).length; final items = <_SyncItem>[ _SyncItem( 'deviceStatus', '同步设备状态', deviceStatus.updatedAt, controller.syncDeviceStatus, icon: FAssets.icons.hardDrive, statusText: deviceProgress.running ? deviceProgress.message : '已连接 $availableDeviceCount/4 个设备', warning: availableDeviceCount < 4, forceSyncing: deviceProgress.running, ), _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: Container( key: const ValueKey('setting-sync-list'), clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: XfjDesignTokens.surface, border: Border.all(color: XfjDesignTokens.line), borderRadius: BorderRadius.circular(XfjDesignTokens.cardRadius), ), child: ListView.builder( padding: EdgeInsets.zero, itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; final itemSyncing = controller.syncingKey.value == item.key || item.forceSyncing; return _SyncRow( item: item, syncing: itemSyncing, failed: controller.syncFailedKeys.contains(item.key), enabled: controller.syncingKey.isEmpty || (item.key == 'deviceStatus' && (controller.syncingKey.value == 'deviceStatus' || item.forceSyncing)), showDivider: index != items.length - 1, ); }, ), ), ), SizedBox(height: XfjDesignTokens.cardGap.h), const SettingNotice( text: '网络接口可用不等于业务服务可用。同步失败时会保留本地数据,可在网络恢复后重新执行。', ), ], ); }); } class _SyncItem { const _SyncItem( this.key, this.title, this.time, this.action, { this.icon, this.statusText, this.warning = false, this.forceSyncing = false, }); final String key; final String title; final DateTime? time; final Future Function() action; final SvgAsset? icon; final String? statusText; final bool warning; final bool forceSyncing; } class _SyncRow extends StatelessWidget { const _SyncRow({ required this.item, required this.syncing, required this.failed, required this.enabled, required this.showDivider, }); final _SyncItem item; final bool syncing; final bool failed; final bool enabled; final bool showDivider; @override Widget build(BuildContext context) { final theme = FTheme.of(context); final statusColor = failed ? XfjDesignTokens.danger : syncing ? XfjDesignTokens.primary : item.warning ? XfjDesignTokens.warning : XfjDesignTokens.success; return Opacity( opacity: enabled || syncing ? 1 : 0.55, child: InkWell( onTap: enabled ? item.action : null, child: Container( constraints: BoxConstraints(minHeight: 76.h), padding: EdgeInsets.symmetric(horizontal: 18.w, vertical: 10.h), decoration: BoxDecoration( border: showDivider ? const Border( bottom: BorderSide(color: XfjDesignTokens.line), ) : null, ), child: Row( children: [ Container( width: 44.w, height: 44.w, alignment: Alignment.center, decoration: BoxDecoration( color: XfjDesignTokens.primarySoft, borderRadius: BorderRadius.circular( XfjDesignTokens.controlRadius, ), ), child: syncing ? SizedBox( width: 22.w, height: 22.w, child: const CircularProgressIndicator(strokeWidth: 2), ) : FIcon( item.icon ?? FAssets.icons.refreshCw, color: XfjDesignTokens.primary, size: 24.sp, ), ), SizedBox(width: 14.w), Expanded( child: Row( children: [ Expanded( child: Text( item.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.typography.base.copyWith( color: XfjDesignTokens.ink, fontWeight: FontWeight.w700, ), ), ), SizedBox(width: 20.w), SizedBox( width: 360.w, child: Text( '最后同步:${_formatTime(item.time)}', maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.right, style: theme.typography.sm.copyWith( color: XfjDesignTokens.muted, fontFeatures: const [FontFeature.tabularFigures()], ), ), ), SizedBox(width: 20.w), SizedBox( width: 320.w, child: Text( failed ? '同步失败,点击重试' : syncing ? item.statusText ?? '同步中...' : item.statusText ?? '状态正常', maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.right, style: theme.typography.sm.copyWith( color: statusColor, fontWeight: FontWeight.w600, ), ), ), ], ), ), SizedBox(width: 14.w), 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); }