fix: 修改客服

This commit is contained in:
speakeloudest 2026-01-09 01:36:24 -08:00
parent eb4fad64cb
commit ac65aaa227
12 changed files with 435 additions and 191 deletions

View File

@ -91,6 +91,8 @@ class HIHelpController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
//
KRCommonUtil.kr_warmUpCustomerService();
// //
// kr_getMessageList(); // kr_getMessageList();
} }

View File

@ -15,6 +15,7 @@ import 'package:kaer_with_panels/app/modules/hi_menu/widgets/hi_menu_list_item.d
import '../../../routes/app_pages.dart'; import '../../../routes/app_pages.dart';
import 'package:kaer_with_panels/app/widgets/swipe/has_swipe_config.dart'; import 'package:kaer_with_panels/app/widgets/swipe/has_swipe_config.dart';
import 'package:kaer_with_panels/app/widgets/swipe/swipe_config.dart'; import 'package:kaer_with_panels/app/widgets/swipe/swipe_config.dart';
import '../../../utils/kr_common_util.dart';
class HIHelpView extends GetView<HIHelpController> implements HasSwipeConfig { class HIHelpView extends GetView<HIHelpController> implements HasSwipeConfig {
const HIHelpView({super.key}); const HIHelpView({super.key});
@ -84,7 +85,7 @@ class HIHelpView extends GetView<HIHelpController> implements HasSwipeConfig {
title: '在线客服', title: '在线客服',
// 3. 使 onTap // 3. 使 onTap
onTap: () { onTap: () {
Get.toNamed(Routes.KR_CRISP); KRCommonUtil.kr_openCustomerService();
}, },
), ),
), ),

View File

@ -18,6 +18,8 @@ class HIMenuController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
//
KRCommonUtil.kr_warmUpCustomerService();
_kr_getVersion(); _kr_getVersion();
} }

View File

@ -17,6 +17,7 @@ import 'package:kaer_with_panels/app/modules/kr_home/views/hi_subscription_corne
import 'package:kaer_with_panels/app/common/app_run_data.dart'; import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/modules/hi_menu/widgets/hi_menu_list_item.dart'; import 'package:kaer_with_panels/app/modules/hi_menu/widgets/hi_menu_list_item.dart';
import 'package:kaer_with_panels/app/modules/hi_menu/widgets/user_info_card.dart'; import 'package:kaer_with_panels/app/modules/hi_menu/widgets/user_info_card.dart';
import '../../../utils/kr_common_util.dart';
class HIMenuView extends GetView<HIMenuController> implements HasSwipeConfig { class HIMenuView extends GetView<HIMenuController> implements HasSwipeConfig {
const HIMenuView({super.key}); const HIMenuView({super.key});
@ -29,6 +30,34 @@ class HIMenuView extends GetView<HIMenuController> implements HasSwipeConfig {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<MenuItem> menuItems = [
const MenuItem(
iconName: 'icon-1',
title: '节点列表',
route: Routes.HI_NODE_LIST,
),
const MenuItem(
iconName: 'icon-2',
title: '消息中心',
route: Routes.KR_MESSAGE,
),
const MenuItem(
iconName: 'icon-3',
title: '常见问题',
route: Routes.HI_HELP,
),
const MenuItem(
iconName: 'icon-4',
title: '邀请好友',
route: Routes.KR_INVITE,
),
MenuItem(
iconName: 'icon-5',
title: '在线客服',
onTap: () => KRCommonUtil.kr_openCustomerService(),
),
];
return HIBaseScaffold( return HIBaseScaffold(
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
@ -58,17 +87,17 @@ class HIMenuView extends GetView<HIMenuController> implements HasSwipeConfig {
); );
}), }),
SizedBox(height: 10.h), // SizedBox(height: 10.h), // and
// ListView.separated Padding // ListView.separated Padding
ListView.separated( ListView.separated(
shrinkWrap: true, // ListView shrinkWrap: true, // ListView
physics: physics:
const NeverScrollableScrollPhysics(), // Stack const NeverScrollableScrollPhysics(), // Stack
itemCount: _menuItems.length, itemCount: menuItems.length,
// //
itemBuilder: (context, index) { itemBuilder: (context, index) {
return MenuListItem(item: _menuItems[index]); return MenuListItem(item: menuItems[index]);
}, },
// //
separatorBuilder: (context, index) { separatorBuilder: (context, index) {
@ -104,31 +133,3 @@ class HIMenuView extends GetView<HIMenuController> implements HasSwipeConfig {
); );
} }
} }
const List<MenuItem> _menuItems = [
MenuItem(
iconName: 'icon-1',
title: '节点列表',
route: Routes.HI_NODE_LIST,
),
MenuItem(
iconName: 'icon-2',
title: '消息中心',
route: Routes.KR_MESSAGE,
),
MenuItem(
iconName: 'icon-3',
title: '常见问题',
route: Routes.HI_HELP,
),
MenuItem(
iconName: 'icon-4',
title: '邀请好友',
route: Routes.KR_INVITE,
),
MenuItem(
iconName: 'icon-5',
title: '在线客服',
route: Routes.KR_CRISP,
),
];

View File

@ -500,10 +500,12 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
confirmText: KRAppRunData.getInstance().isDeviceLogin() ? '前往' : null, confirmText: KRAppRunData.getInstance().isDeviceLogin() ? '前往' : null,
cancelText: KRAppRunData.getInstance().isDeviceLogin() ? '取消' : null, cancelText: KRAppRunData.getInstance().isDeviceLogin() ? '取消' : null,
onConfirm: () { onConfirm: () {
if(KRAppRunData.getInstance().isDeviceLogin()) {
Get.toNamed( Get.toNamed(
Routes.MR_LOGIN, Routes.MR_LOGIN,
arguments: {'entry': 'bind_email'}, arguments: {'entry': 'bind_email'},
); );
}
}, },
); );
}, },

View File

@ -1,33 +1,45 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:crisp_sdk/crisp_sdk.dart';
import 'package:kaer_with_panels/app/common/app_config.dart'; import 'package:kaer_with_panels/app/common/app_config.dart';
import 'package:kaer_with_panels/app/common/app_run_data.dart'; import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart'; import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
import 'dart:async'; import 'dart:async';
import '../../../services/kr_device_info_service.dart'; import '../../../services/kr_device_info_service.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
/// Crisp // 使 SDK
import 'package:crisp_chat/crisp_chat.dart' as native_crisp;
// 使 WebView SDK
import 'package:crisp_sdk/crisp_sdk.dart' as webview_crisp;
/// 🔧 P15 : Crisp
/// - (Android/iOS): 使 crisp_chat SDK WebView
/// - (macOS/Windows): 使 crisp_sdk WebView
class KRCrispController extends GetxController { class KRCrispController extends GetxController {
// Crisp // ========== ( SDK) ==========
CrispController? crispController; native_crisp.CrispConfig? _nativeConfig;
// // ========== (WebView SDK) ==========
final RxBool kr_isLoading = true.obs; webview_crisp.CrispController? crispController;
//
final RxBool kr_isInitialized = false.obs;
//
Completer<void>? _kr_initializationCompleter; Completer<void>? _kr_initializationCompleter;
bool _kr_isDisposed = false; bool _kr_isDisposed = false;
// ========== ==========
final RxBool kr_isLoading = true.obs;
final RxBool kr_isInitialized = false.obs;
///
bool get _isMobilePlatform => Platform.isAndroid || Platform.isIOS;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
_kr_prepareInitialization(); if (_isMobilePlatform) {
_kr_prepareMobileConfig();
} else {
_kr_prepareDesktopInitialization();
}
} }
@override @override
@ -35,21 +47,104 @@ class KRCrispController extends GetxController {
super.onReady(); super.onReady();
} }
/// // ==========================================
Future<void> _kr_prepareInitialization() async { // (Android/iOS - SDK)
// ==========================================
/// Crisp
void _kr_prepareMobileConfig() {
try {
kr_isLoading.value = false; // SDK
final appData = KRAppRunData();
final userEmail = appData.kr_account.value ?? '';
final deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
final identifier = userEmail.isNotEmpty ? userEmail : deviceId;
// SDK
_nativeConfig = native_crisp.CrispConfig(
websiteID: AppConfig.getInstance().kr_website_id,
user: native_crisp.User(
email: identifier,
nickName: identifier,
),
);
kr_isInitialized.value = true;
if (kDebugMode) {
print('[P15-Mobile] Crisp 原生 SDK 配置已准备');
}
} catch (e) {
if (kDebugMode) {
print('[P15-Mobile] 准备 Crisp 配置时出错: $e');
}
kr_isInitialized.value = false;
}
}
/// Crisp ()
Future<void> kr_openNativeCrispChat() async {
if (_nativeConfig == null) {
if (kDebugMode) {
print('[P15-Mobile] Crisp 配置未准备好');
}
return;
}
try {
if (kDebugMode) {
print('[P15-Mobile] 正在打开 Crisp 原生聊天界面...');
}
await native_crisp.FlutterCrispChat.openCrispChat(config: _nativeConfig!);
//
final currentLanguage = KRLanguageUtils.getCurrentLanguageCode();
final deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
native_crisp.FlutterCrispChat.setSessionString(
key: 'platform',
value: Platform.isAndroid ? 'android' : 'ios',
);
native_crisp.FlutterCrispChat.setSessionString(
key: 'language',
value: currentLanguage,
);
native_crisp.FlutterCrispChat.setSessionString(
key: 'device_id',
value: deviceId,
);
if (kDebugMode) {
print('[P15-Mobile] Crisp 聊天界面已打开');
}
} catch (e) {
if (kDebugMode) {
print('[P15-Mobile] 打开 Crisp 聊天时出错: $e');
}
}
}
// ==========================================
// (macOS/Windows - WebView SDK)
// ==========================================
///
Future<void> _kr_prepareDesktopInitialization() async {
if (_kr_isDisposed) return; if (_kr_isDisposed) return;
_kr_initializationCompleter = Completer<void>(); _kr_initializationCompleter = Completer<void>();
try { try {
kr_isLoading.value = true; kr_isLoading.value = true;
await kr_initializeCrisp(); await kr_initializeDesktopCrisp();
if (!_kr_isDisposed) { if (!_kr_isDisposed) {
kr_isInitialized.value = true; kr_isInitialized.value = true;
} }
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('初始化 Crisp 时出错: $e'); print('[P15-Desktop] 初始化 Crisp 时出错: $e');
} }
if (!_kr_isDisposed) { if (!_kr_isDisposed) {
kr_isInitialized.value = false; kr_isInitialized.value = false;
@ -62,34 +157,23 @@ class KRCrispController extends GetxController {
} }
} }
/// Crisp /// Crisp (WebView)
Future<void> kr_initializeCrisp() async { Future<void> kr_initializeDesktopCrisp() async {
if (_kr_isDisposed) return; if (_kr_isDisposed) return;
try { try {
final appData = KRAppRunData(); final appData = KRAppRunData();
final currentLanguage = KRLanguageUtils.getCurrentLanguageCode(); final currentLanguage = KRLanguageUtils.getCurrentLanguageCode();
final userEmail = appData.kr_account.value ?? ''; final userEmail = appData.kr_account.value ?? '';
// ID
final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; final deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
final identifier = userEmail.isNotEmpty ? userEmail : deviceId; final identifier = userEmail.isNotEmpty ? userEmail : deviceId;
// Crisp locale
// Crisp https://docs.crisp.chat/guides/chatbox/languages/
String locale = _getLocaleForCrisp(currentLanguage); String locale = _getLocaleForCrisp(currentLanguage);
if (_kr_isDisposed) return; if (_kr_isDisposed) return;
final websiteId = AppConfig.getInstance().kr_website_id; // WebView Crisp
if (websiteId.isEmpty) { crispController = webview_crisp.CrispController(
//
print('Crisp 初始化失败website_id 未配置');
return;
}
// Crisp
crispController = CrispController(
websiteId: AppConfig.getInstance().kr_website_id, websiteId: AppConfig.getInstance().kr_website_id,
locale: locale, locale: locale,
); );
@ -101,7 +185,7 @@ class KRCrispController extends GetxController {
// //
crispController?.register( crispController?.register(
user: CrispUser( user: webview_crisp.CrispUser(
email: identifier, email: identifier,
nickname: identifier, nickname: identifier,
), ),
@ -114,82 +198,100 @@ class KRCrispController extends GetxController {
// //
crispController?.setSessionData({ crispController?.setSessionData({
'platform': Platform.isAndroid 'platform': Platform.isWindows ? 'windows' : 'macos',
? 'android'
: Platform.isIOS
? 'ios'
: Platform.isWindows
? 'windows'
: Platform.isMacOS
? 'macos'
: 'unknown',
'language': currentLanguage, 'language': currentLanguage,
'app_version': '1.0.0', 'app_version': '1.0.0',
'device_id': deviceId, 'device_id': deviceId,
}); });
if (kDebugMode) { if (kDebugMode) {
print('Crisp 初始化完成'); print('[P15-Desktop] Crisp WebView 初始化完成');
} }
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('初始化 Crisp 时出错: $e'); print('[P15-Desktop] 初始化 Crisp 时出错: $e');
} }
crispController = null; crispController = null;
rethrow; rethrow;
} }
} }
@override
void onClose() {
_kr_isDisposed = true;
kr_cleanupResources();
super.onClose();
}
/// Crisp
Future<void> kr_cleanupResources() async {
try {
//
if (_kr_initializationCompleter != null && !_kr_initializationCompleter!.isCompleted) {
await _kr_initializationCompleter!.future;
}
if (kr_isInitialized.value) {
// Crisp
crispController = null;
kr_isInitialized.value = false;
kr_isLoading.value = false;
}
} catch (e) {
if (kDebugMode) {
print('清理 Crisp 资源时出错: $e');
}
}
}
/// Crisp locale /// Crisp locale
/// 西
String _getLocaleForCrisp(String languageCode) { String _getLocaleForCrisp(String languageCode) {
// Crisp locale
switch (languageCode) { switch (languageCode) {
case 'zh_CN': case 'zh_CN':
case 'zh': case 'zh':
return 'zh'; // return 'zh';
case 'zh_TW': case 'zh_TW':
case 'zhHant': case 'zhHant':
return 'zh-tw'; // return 'zh-tw';
case 'es': case 'es':
return 'es'; // 西 return 'es';
case 'ja': case 'ja':
return 'ja'; // return 'ja';
case 'ru': case 'ru':
return 'ru'; // return 'ru';
case 'et': case 'et':
return 'et'; // return 'et';
case 'en': case 'en':
default: default:
return 'en'; // return 'en';
}
}
// ==========================================
//
// ==========================================
@override
void onClose() {
_kr_isDisposed = true;
if (_isMobilePlatform) {
// SDK
if (kDebugMode) {
print('[P15-Mobile] onClose - 原生 SDK 无需手动清理');
}
} else {
// WebView
_kr_cleanupDesktopResources();
}
super.onClose();
}
///
Future<void> _kr_cleanupDesktopResources() async {
try {
if (_kr_initializationCompleter != null &&
!_kr_initializationCompleter!.isCompleted) {
try {
await _kr_initializationCompleter!.future.timeout(
const Duration(milliseconds: 500),
onTimeout: () {
if (!_kr_initializationCompleter!.isCompleted) {
_kr_initializationCompleter!.complete();
}
},
);
} catch (e) {
//
}
}
if (kr_isInitialized.value || kr_isLoading.value) {
crispController = null;
kr_isInitialized.value = false;
kr_isLoading.value = false;
if (kDebugMode) {
print('[P15-Desktop] Crisp WebView 资源已清理');
}
}
} catch (e) {
if (kDebugMode) {
print('[P15-Desktop] 清理资源时出错: $e');
}
} }
} }
} }

View File

@ -1,66 +1,80 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:crisp_sdk/crisp_sdk.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:io' show Platform;
import '../controllers/kr_crisp_controller.dart'; import '../controllers/kr_crisp_controller.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart'; import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart'; import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../../../widgets/kr_simple_loading.dart'; import '../../../widgets/kr_simple_loading.dart';
import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
// 使 WebView SDK
import 'package:crisp_sdk/crisp_sdk.dart' as webview_crisp;
/// Crisp /// 🔧 P15 : Crisp
/// - (Android/iOS):
/// - (macOS/Windows): WebView
class KRCrispView extends GetView<KRCrispController> { class KRCrispView extends GetView<KRCrispController> {
const KRCrispView({Key? key}) : super(key: key); const KRCrispView({Key? key}) : super(key: key);
///
bool get _isMobilePlatform => Platform.isAndroid || Platform.isIOS;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopScope( return Scaffold(
onPopInvokedWithResult: (didPop, result) async {
if (didPop) {
await controller.kr_cleanupResources();
}
},
child: Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor, backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: _kr_buildAppBar(context), appBar: _kr_buildAppBar(context),
body: _kr_buildBody(context), body: _kr_buildBody(context),
),
); );
} }
/// ///
PreferredSizeWidget _kr_buildAppBar(BuildContext context) { PreferredSizeWidget _kr_buildAppBar(BuildContext context) {
return AppBar( return AppBar(
backgroundColor: Colors.transparent, backgroundColor: Theme.of(context).cardColor,
toolbarHeight: 0.0,
elevation: 0, elevation: 0,
automaticallyImplyLeading: false, // 1. leading title: Text(
// title: Text( AppTranslations.kr_userInfo.customerService,
// AppTranslations.kr_userInfo.customerService, style: KrAppTextStyle(
// style: KrAppTextStyle( color: Theme.of(context).textTheme.bodyMedium?.color,
// color: Theme.of(context).textTheme.bodyMedium?.color, fontSize: 16,
// fontSize: 16, fontWeight: FontWeight.w500,
// fontWeight: FontWeight.w500, ),
// ), ),
// ), leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 20.sp,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
onPressed: () => Get.back(),
),
); );
} }
/// ///
Widget _kr_buildBody(BuildContext context) { Widget _kr_buildBody(BuildContext context) {
return Stack( return Container(
children: [ color: Theme.of(context).scaffoldBackgroundColor,
// --- WebView --- child: Obx(() {
// Obx CrispView // ========== ==========
Obx(() { if (_isMobilePlatform) {
if (!controller.kr_isInitialized.value) {
return _kr_buildErrorView(context);
}
//
return _kr_buildLoadingView(context, '正在加载客服');
}
// ========== ==========
if (controller.kr_isLoading.value) { if (controller.kr_isLoading.value) {
return _kr_buildLoadingView(context, '正在初始化客服系统...'); return _kr_buildLoadingView(context, '正在初始化客服系统');
} }
if (controller.kr_isInitialized.value && controller.crispController != null) { if (controller.kr_isInitialized.value && controller.crispController != null) {
return CrispView( // WebView
return webview_crisp.CrispView(
crispController: controller.crispController!, crispController: controller.crispController!,
clearCache: true, clearCache: true,
onSessionIdReceived: _kr_onSessionIdReceived, onSessionIdReceived: _kr_onSessionIdReceived,
@ -69,27 +83,32 @@ class KRCrispView extends GetView<KRCrispController> {
return _kr_buildErrorView(context); return _kr_buildErrorView(context);
}), }),
// --- ---
Positioned(
// 4. 使 MediaQuery UI重叠
top: 40.w,
left: 20.w,
child: GestureDetector(
onTap: () => _kr_handleBack(),
behavior: HitTestBehavior.translucent,
child: KrLocalImage(
imageName: 'hi-back-icon',
width: 48.w,
height: 48.w,
imageType: ImageType.svg,
),
),
),
],
); );
} }
/// Crisp ()
void _kr_openNativeCrispChat(BuildContext context) async {
if (!controller.kr_isInitialized.value) {
if (kDebugMode) {
print('[P15-Mobile] Crisp 未初始化,无法打开聊天');
}
return;
}
try {
if (kDebugMode) {
print('[P15-Mobile] 正在打开原生 Crisp 聊天界面...');
}
//
await controller.kr_openNativeCrispChat();
} catch (e) {
if (kDebugMode) {
print('[P15-Mobile] 打开 Crisp 聊天时出错: $e');
}
}
}
/// ///
Widget _kr_buildLoadingView(BuildContext context, String message) { Widget _kr_buildLoadingView(BuildContext context, String message) {
return Center( return Center(
@ -97,7 +116,7 @@ class KRCrispView extends GetView<KRCrispController> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
KRSimpleLoading( KRSimpleLoading(
color: Theme.of(context).primaryColor, color: Colors.blue,
size: 50.0, size: 50.0,
), ),
if (message.isNotEmpty) SizedBox(height: 16.sp), if (message.isNotEmpty) SizedBox(height: 16.sp),
@ -127,7 +146,7 @@ class KRCrispView extends GetView<KRCrispController> {
), ),
SizedBox(height: 16.sp), SizedBox(height: 16.sp),
Text( Text(
'客服系统初始化失败', 'crisp.initFailed'.tr,
style: KrAppTextStyle( style: KrAppTextStyle(
fontSize: 16, fontSize: 16,
color: Colors.red, color: Colors.red,
@ -136,23 +155,21 @@ class KRCrispView extends GetView<KRCrispController> {
SizedBox(height: 24.sp), SizedBox(height: 24.sp),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
controller.kr_initializeCrisp(); if (_isMobilePlatform) {
_kr_openNativeCrispChat(context);
} else {
controller.kr_initializeDesktopCrisp();
}
}, },
child: Text('重试'), child: Text('common.retry'.tr),
), ),
], ],
), ),
); );
} }
/// /// ID ()
Future<void> _kr_handleBack() async {
await controller.kr_cleanupResources();
Get.back();
}
/// ID
void _kr_onSessionIdReceived(String sessionId) { void _kr_onSessionIdReceived(String sessionId) {
debugPrint('Crisp 会话 ID: $sessionId'); debugPrint('[P15-Desktop] Crisp 会话 ID: $sessionId');
} }
} }

View File

@ -60,6 +60,8 @@ class KRPurchaseMembershipController extends GetxController {
@override @override
void onInit() async { void onInit() async {
super.onInit(); super.onInit();
//
KRCommonUtil.kr_warmUpCustomerService();
print( print(
'💳 [PurchaseMembership] ========== Controller.onInit 被调用 =========='); '💳 [PurchaseMembership] ========== Controller.onInit 被调用 ==========');
print('💳 [PurchaseMembership] 当前时间: ${DateTime.now()}'); print('💳 [PurchaseMembership] 当前时间: ${DateTime.now()}');

View File

@ -20,6 +20,7 @@ import 'dart:convert';
import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
import '../../../routes/app_pages.dart'; import '../../../routes/app_pages.dart';
import 'package:kaer_with_panels/app/services/iap/iap_service.dart'; import 'package:kaer_with_panels/app/services/iap/iap_service.dart';
import 'package:kaer_with_panels/app/utils/kr_common_util.dart';
@ -216,7 +217,7 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController>
barrierDismissible: false, barrierDismissible: false,
preventBackDismiss: true, preventBackDismiss: true,
onConfirm: () async { onConfirm: () async {
Get.toNamed(Routes.KR_CRISP); KRCommonUtil.kr_openCustomerService();
}, },
); );
}, },

View File

@ -1,7 +1,112 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher.dart';
import '../widgets/kr_toast.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 { class KRCommonUtil {
///
static bool _kr_isOpeningCustomerService = false;
///
static void kr_warmUpCustomerService() {
if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
if (!Get.isRegistered<KRCrispController>()) {
Get.put(KRCrispController(), permanent: true);
debugPrint('客服系统预热中(持久模式)...');
}
}
}
///
static Future<void> 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<KRCrispController>()) {
crispController = Get.find<KRCrispController>();
} 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: /// meesage: , toastPosition: , timeout:
static kr_showToast(String message, static kr_showToast(String message,
{KRToastPosition toastPosition = KRToastPosition.center, {KRToastPosition toastPosition = KRToastPosition.center,

View File

@ -241,6 +241,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.0" version: "3.2.0"
crisp_chat:
dependency: "direct main"
description:
name: crisp_chat
sha256: "63be64c416b5b44bb099bbd278b8de718714075e5ac9d444272dcf99e48eda3d"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
crisp_sdk: crisp_sdk:
dependency: "direct main" dependency: "direct main"
description: description:
@ -780,10 +788,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: http name: http
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.6.0"
http2: http2:
dependency: transitive dependency: transitive
description: description:

View File

@ -73,7 +73,8 @@ dependencies:
window_manager: ^0.4.3 window_manager: ^0.4.3
url_launcher: ^6.3.1 url_launcher: ^6.3.1
flutter_inappwebview: ^6.1.5 # 最新稳定版本 flutter_inappwebview: ^6.1.5 # 最新稳定版本
crisp_sdk: ^1.1.0 # 使用 crisp_sdk配合最新的 flutter_inappwebview crisp_chat: ^2.4.3 # 🔧 P15: 移动平台使用原生 SDK解决 WebView 黑屏问题
crisp_sdk: ^1.1.0 # 🔧 P15: 桌面平台继续使用 WebView 实现
protocol_handler_windows: ^0.2.0 protocol_handler_windows: ^0.2.0
share_plus: ^7.2.2 share_plus: ^7.2.2