import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hgdj/hj_model/splash/ads_model.dart'; import 'package:hgdj/hj_page/main_page/provider/msg_provider.dart'; import 'package:hgdj/hj_page/pre_sale/pre_sale_provider.dart'; import 'package:hgdj/alert/vip_guide/guide_manager.dart'; import 'package:hgdj/alert/vip_guide/timed_popup_manager.dart'; import 'package:hgdj/tools_base/ad_manager.dart'; import '../../alert/splash/notice_image_dialog.dart'; import '../../alert/splash/notice_text_dialog.dart'; import '../../alert/vip_guide/guide_bottom_sheet.dart'; import '../../alert/vip_guide/guide_home_dialog.dart'; import '../../config/config.dart'; import '../../hj_model/home/plate_model.dart'; import '../../tools_base/debug_log.dart'; import '../../tools_base/module_sort_manager.dart'; import '../main_page/main_logic.dart'; import '../mine/mine_vip/mine_charge_vip_page.dart'; import '../mine/mine_vip/pay_order_source.dart'; import '../mine/widgets/gradient_text.dart'; import '../pre_sale/limit_time_provider.dart'; class HomeMainLogic extends GetxController { final bool isDarkStyle; final String? darkDefaultId; // 暗网跳转到指定的tab使用 HomeMainLogic({ this.isDarkStyle = false, this.darkDefaultId, }); //开屏弹窗链每次启动只跑一次 static bool _adShown = false; bool isLoading = true; int tabViewKey = 0; //排序变了就 +1 换掉 HomeTabView 的 key,强制重建 TabController int _initIndex = 0; // 注意⚠️:暗网进入,有可能通过内链指定亚模块进入,index通过defaultId 处理 //合并本地排序后的 tab 列表,_loadTabs 里算一次 List tabs = []; final scaffoldKey = GlobalKey(); //tabCtr 由 HomeTabView 在自身 vsync 下创建并 dispose,这里只持引用驱动 tab 切换;排序时置 null 触发重建 TabController? tabCtr; String get _sortKey => "_localSortKey_Tab_ID_cache_$isDarkStyle"; int get selectedTabIndex => tabCtr?.index ?? _initIndex; //服务端下发的原始顺序(抽屉「恢复默认」用) List get defaultList { if (isDarkStyle) return Config.plateModule?.deepWeb ?? []; // 固定最新模块 return [ ModuleData(id: "-110", moduleName: "最新", type: 1, showType: 1), ...?Config.plateModule?.homePage, ]; } @override void onInit() { super.onInit(); _loadTabs(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { limitTimeProvider.refresh(); //获取限时活动(放首帧后,避免 build 中 notifyListeners) _showAdChain(); }); } void openEndDrawer() => scaffoldKey.currentState?.openEndDrawer(); void gotoTabId(String tagDataId) { final index = tabs.indexWhere((e) => e.id == tagDataId); if (index >= 0) tabCtr?.index = index; } /// 应用抽屉里的新排序:顺序没变只切 tab,变了就落盘并换 key 重建 TabView Future resortTabs(List dataArr, ModuleData? tagData) async { final curTagData = tagData ?? tabs[selectedTabIndex]; bool isSameArr = true; int selectIndex = 0; //抽屉只重排不增删,长度必然一致;逐项比名字,顺带定位选中项的新位置 if (dataArr.length == tabs.length) { for (int i = 0; i < dataArr.length; i++) { if (dataArr[i].moduleName != tabs[i].moduleName) isSameArr = false; if (dataArr[i].moduleName == curTagData.moduleName) selectIndex = i; } } if (isSameArr) { tabCtr?.index = selectIndex; return; } await ModuleSortManager().saveLocalData(dataArr, _sortKey); tabViewKey++; tabs = dataArr; _initIndex = selectIndex; tabCtr = null; //旧 State 即将销毁,先断引用,由新 State 重新赋值 update(); } //合并本地排序与服务端 tab,顺带算好初始选中的 index Future _loadTabs() async { try { tabs = await ModuleSortManager().mergeLocalSortWithServer( serverTabs: defaultList, key: _sortKey, ); if (darkDefaultId?.isNotEmpty == true) { // 初始化指定模块(一般暗网模块用) final index = tabs.indexWhere((e) => e.id == darkDefaultId); if (index >= 0) _initIndex = index; } else if (!isDarkStyle) { //默认不显示最新模块,选中「热门」(index 1);但 tab 不足 2 个时回退到 0,避免越界 _initIndex = (tabs.first.moduleName == "最新" && tabs.length > 1) ? 1 : 0; } } catch (e) { debugLog(e); } isLoading = false; update(); } /// 首页开屏弹窗链:图片公告 → 文字公告 → 预售 → 支付分层 → VIP 引导。 /// 逐个 await 串行弹,每次启动只跑一次。 /// 九宫格应用广告不在这里:它跟着启动落地页弹,见 MainPageLogic.onReady Future _showAdChain() async { if (_adShown) return; _adShown = true; await MainPageLogic.appDialogFuture; //等落地页的九宫格弹窗关掉再接着弹,避免叠在一起 if (AdManager().showAbTestAd) { await _showImageDialog(); //图片公告 await _showWordDialog(); //文字公告 } await PreSaleProvider().showAlert(); //预售弹窗 await _showPayDialog(); //支付分层首页弹窗 await _showVipGuide(); //首页吸底 VIP 引导(全链优先级最后) } /// 支付分层首页弹窗:有 homePage 图就弹,倒计时由 lastDiscountTime 自行判断。 /// 点海报跳会员中心,这里 await 到用户从会员页返回,否则链条后面的吸底引导会弹在会员页上 Future _showPayDialog() async { final config = MineMsgProvider().payTier?.config; if (config == null || (config.homePage ?? '').isEmpty) return; final goVip = await Get.dialog(GuideHomeDialog(config: config)); if (goVip != true) return; await Get.to(() => MineChargeVipPage( vipID: config.vipCard, sourcePage: PaySourcePage.homeUserSegment)); } /// 首页吸底 VIP 引导:开屏弹窗链末尾弹(全链最低优先级),能不能弹由 HOME_NEW_USER / HOME_OLD_USER /// 两个场景开关决定(后端按分层下发,最多命中一个);文案优先用下发的,没配走默认。 /// 走 TimedPopupManager 与其它引导共用互斥;每次启动只弹一次(_showAdChain 由静态 _adShown 保证)。 Future _showVipGuide() async { final scene = GuideManager().homeScene; if (scene == null) return; final config = GuideManager().configOf(scene); await TimedPopupManager().trigger( canShow: () => GuideManager().canShow(scene), onShow: () => GuideBottomSheet.show( scene: scene, title: Text( config?.title?.isNotEmpty == true ? config!.title! : 'VIP影片免费试看', style: const TextStyle( color: Colors.white, fontSize: 19, fontWeight: FontWeight.w600), ), subtitle: GradientText( config?.description?.isNotEmpty == true ? config!.description! : '开通会员 查看完整影片', gradient: const LinearGradient(colors: [Colors.white, Color(0xffEFD394)]), style: const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500), ), ), ); } ///显示图片公告(上下双排,每次弹两张) Future _showImageDialog() async { final adsList = AdManager().adsByType(46); for (int i = 0; i < adsList.length; i += 2) { await Get.dialog( SystemImagesDialog(ads: adsList.skip(i).take(2).toList())); } } //文字公告 Future _showWordDialog() async { for (final wordBean in AdManager().announceList) { await Get.dialog( wordBean.type == 0 ? NoticeTextDialog( content: wordBean.content.toString(), wordBean: wordBean) : NoticeImageDialog( model: AdsInfoModel(cover: wordBean.cover, href: wordBean.href)), ); } } }