import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:url_launcher/url_launcher.dart'; import '../widgets/kr_toast.dart'; import '../common/app_config.dart'; import '../routes/app_pages.dart'; import '../widgets/dialogs/hi_dialog.dart'; import '../modules/kr_crisp_chat/controllers/kr_crisp_controller.dart'; class KRCommonUtil { /// 是否正在打开客服(防重复点击) static bool _kr_isOpeningCustomerService = false; /// 预热客服(提前初始化控制器) static void kr_warmUpCustomerService() { if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) { if (!Get.isRegistered()) { Get.put(KRCrispController(), permanent: true); debugPrint('客服系统预热中(持久模式)...'); } } } /// 打开客服 static Future kr_openCustomerService() async { // 🔧 防重复点击 if (_kr_isOpeningCustomerService) { return; } final websiteId = AppConfig.getInstance().kr_website_id; if (websiteId.isEmpty) { HIDialog.show( title: '提示', message: '加载失败', ); return; } // 🔧 Windows 端直接跳转外部浏览器 if (Platform.isWindows) { _kr_isOpeningCustomerService = true; // 显示加载提示 kr_showToast('userInfo.openingCustomerService'.tr); // 直接使用 zh final Uri url = Uri.parse( 'https://go.crisp.chat/chat/embed/?website_id=$websiteId&locale=zh', ); try { if (await canLaunchUrl(url)) { await launchUrl(url, mode: LaunchMode.externalApplication); } else { kr_showToast("common.cannotOpenBrowser".tr); } } catch (e) { kr_showToast("common.openLinkFailed".tr); } finally { // 延迟重置状态 Future.delayed(const Duration(seconds: 2), () { _kr_isOpeningCustomerService = false; }); } return; } // 移动平台 (Android/iOS):直接调用原生 SDK,避免页面跳转闪烁 if (Platform.isAndroid || Platform.isIOS) { _kr_isOpeningCustomerService = true; // 预先显示加载提示(可选,原生 SDK 启动很快) // kr_showToast('userInfo.openingCustomerService'.tr); try { // 优先使用已注册的控制器 KRCrispController crispController; if (Get.isRegistered()) { crispController = Get.find(); } else { crispController = Get.put(KRCrispController(), permanent: true); } // 直接调用原生打开方法 await crispController.kr_openNativeCrispChat(); } catch (e) { debugPrint('打开原生客服失败: $e'); // 如果原生失败,可以考虑降级到内置页面或报错 Get.toNamed(Routes.KR_CRISP); } finally { Future.delayed(const Duration(seconds: 2), () { _kr_isOpeningCustomerService = false; }); } return; } // macOS/桌面平台:跳转内置页面 (WebView 实现) _kr_isOpeningCustomerService = true; Get.toNamed(Routes.KR_CRISP); // 延迟重置状态 Future.delayed(const Duration(seconds: 2), () { _kr_isOpeningCustomerService = false; }); } /// 提示 meesage: 提示内容, toastPosition: 提示显示的位置, timeout: 显示时间(毫秒) static kr_showToast(String message, {KRToastPosition toastPosition = KRToastPosition.center, int timeout = 2500}) { KRToast.kr_showToast( message, position: toastPosition, duration: Duration(milliseconds: timeout), ); } /// 显示加载动画 static kr_showLoading({String? message}) { KRToast.kr_showLoading(message: message); } /// 隐藏加载动画 static kr_hideLoading() { KRToast.kr_hideLoading(); } /// 格式化字节数为可读字符串 /// [bytes] 字节数 static String kr_formatBytes(int bytes) { if (bytes <= 0) return "0 B"; const List suffixes = ["B", "KB", "MB", "GB", "TB"]; int i = 0; double value = bytes.toDouble(); while (value >= 1024 && i < suffixes.length - 1) { value /= 1024; i++; } return "${value.toStringAsFixed(2)} ${suffixes[i]}"; } }