// // QSLVipTrialVC.swift // QuickSearchLocation // // Created by Destiny on 2025/10/23. // import UIKit import YYText enum QSLTrialVipJumpType: Int { case notiPush case shortcut } class QSLVipTrialVC: QSLBaseController { var type: QSLTrialVipJumpType? var selectGood: QSLGoodModel? override func viewDidLoad() { super.viewDidLoad() QSLCountdownManager.shared.addDelegate(self) setUI() requestNetwork() } func startTimer(){ // 开始首页商品倒计时 QSLCountdownManager.shared.startCountdown(type: .trial, seconds: 900000) {[weak self] remainingSeconds in } finishCallback: { } } //倒计时 // func requestNetwork() { // 网络请求时使用 itemListDict 作为参数(关键修复) QSLNetwork().request(.wakeupTrialList(dict: ["itemListType": 2])) {[weak self] response in // 后续逻辑不变... let list = response.mapArray(QSLGoodModel.self, modelKey: "data>list") if list.count > 0 { self?.selectGood = list[0] self?.desLabel.text = self?.selectGood?.content ?? "" if(QSLCountdownManager.shared.trialGood == nil){ QSLCountdownManager.shared.trialGood = list[0] self?.startTimer() NotificationCenter.default.post( name: Notification.Name("QSLHomeUpdateCouponViewNoti"), object: nil, userInfo: ["showCoupon": true, "couponType":"1"] ) }else{ QSLCountdownManager.shared.trialGood = list[0] if(!QSLCountdownManager.shared.isCountdownActive(for: .trial)){ self?.countdownLabel.updateCountdownText("00 : 00 : 00") }else{ NotificationCenter.default.post( name: Notification.Name("QSLHomeUpdateCouponViewNoti"), object: nil, userInfo: ["showCoupon": true, "couponType":"1"] ) } } } } fail: { code, error in self.view.toast(text: "加载商品列表失败") } } // 恢复按钮点击 @objc func resumeBtnAction() { let memberModel = QSLBaseManager.shared.userModel.memberModel let expired = memberModel.expired if !expired { QSLLoading.success(text: "您已经在订阅中,无需恢复") return } QSLLoading.show() QSLVipManager.shared.restoreAction { isSuccess in QSLVipManager.shared.isPaying = false if isSuccess { QSLNetwork().request(.userMember(dict: [:])) { response in let model = response.mapObject(QSLMemberModel.self, modelKey: "data") QSLBaseManager.shared.userModel.memberModel = model if model.expired { QSLBaseManager.shared.saveVipExpiredTime(time: 0) } else { QSLBaseManager.shared.saveVipExpiredTime(time: model.endTimestamp) } QSLBaseManager.shared.saveUserId(id: model.userId) if model.endTimestamp > memberModel.endTimestamp { QSLLoading.success(text: "恢复成功") // 支付成功通知 DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) { self.navigationController?.popViewController(animated: true) } } else { QSLLoading.error(text: "没有可供恢复的订阅") } } fail: { code, error in QSLLoading.error(text: "没有可供恢复的订阅") } } else { QSLLoading.error(text: "没有可供恢复的订阅") } } } @objc func payBtnAction(){ let memberModel = QSLBaseManager.shared.userModel.memberModel if let subscriptionExpired = memberModel.subscriptionExpired, !subscriptionExpired { UIApplication.keyWindow?.toast(text: "你已经订阅了") return } guard let currentGood = selectGood else { return } QSLLoading.show() QSLVipManager.shared.startPay(goods: currentGood) { status, outTradeNo in QSLVipManager.shared.isPaying = false if status == .success { QSLLoading.success(text: "支付成功") // 引力传递支付事件 gravityInstance?.trackPayEvent(withAmount: Int32(currentGood.amount), withPayType: "CNY", withOrderId: outTradeNo, withPayReason: currentGood.name, withPayMethod: "apple") QSEventHandle.eventPush(eventName: QSLGravityConst.new_vip_result, eventProps: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "success","pay_amount":Int32(currentGood.amount)]) QSEventHandle.gravityPush(eventName: QSLGravityConst.vip_submit_success, eventProps: ["id": 01001]) DispatchQueue.main.asyncAfter(deadline: .now() + 1) { NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil) if(!QSLBaseManager.shared.isLogin()){ QSLJumpManager.shared.pushToLogin(type: .member) } } } else if status == .cancel { QSEventHandle.eventPush(eventName: QSLGravityConst.new_vip_result, eventProps: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "cancel","pay_amount":Int32(currentGood.amount)]) QSLLoading.error(text: "支付取消") } else if status == .fail { QSEventHandle.eventPush(eventName: QSLGravityConst.new_vip_result, eventProps: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "fail","pay_amount":Int32(currentGood.amount)]) QSLLoading.error(text: "支付失败") } } } @objc func privacyAction() { let vc = QSLWebViewController() vc.webUrl = QSLConfig.AppPrivacyAgreementLink vc.title = "隐私政策" self.present(vc, animated: true) } @objc func serviceAction() { let vc = QSLWebViewController() vc.webUrl = QSLConfig.AppServiceAgreementLink vc.title = "服务协议" self.present(vc, animated: true) } func setUI(){ self.view.addSubview(bottomSpaceView) bottomSpaceView.snp.makeConstraints { make in make.left.right.bottom.equalTo(0) make.height.equalTo(QSLConst.qsl_kTabbarBottom/2+1) } self.view.addSubview(bottomBgView) bottomBgView.snp.makeConstraints { make in make.left.right.equalTo(0) make.bottom.equalTo(bottomSpaceView.snp.top).offset(0) make.height.equalTo(QSLConst.qsl_kScreenW/1.4) } self.view.addSubview(bgImageView) bgImageView.snp.makeConstraints { make in make.left.top.right.equalTo(0) make.bottom.equalTo(bottomBgView.snp.top).offset(50.rpx) } bgImageView.addSubview(bgTopTit1View) bgTopTit1View.snp.makeConstraints { make in make.centerX.equalTo(bgImageView.snp.centerX) make.top.equalTo(QSLConst.qsl_kNavFrameH+20.rpx) } bgImageView.addSubview(bgTopTit2View) bgTopTit2View.snp.makeConstraints { make in make.centerX.equalTo(bgImageView.snp.centerX) make.top.equalTo(bgTopTit1View.snp.bottom).offset(1) } bgImageView.addSubview(desLabel) desLabel.snp.makeConstraints { make in make.right.left.equalTo(0) make.height.equalTo(17.rpx) make.top.equalTo(bgTopTit2View.snp.bottom).offset(6.rpx) } bgImageView.addSubview(bgCenterView) bgCenterView.snp.makeConstraints { make in make.centerX.equalTo(bgImageView.snp.centerX) make.bottom.equalTo(0) } self.view.addSubview(vipNaviView) vipNaviView.snp.makeConstraints { make in make.top.left.right.equalTo(0) make.height.equalTo(QSLConst.qsl_kNavFrameH) } vipNaviView.addSubview(backButton) backButton.snp.makeConstraints { make in make.width.equalTo(44.rpx) make.height.equalTo(44.rpx) make.left.equalTo(10.rpx) make.top.equalTo(QSLConst.qsl_kStatusBarFrameH) } vipNaviView.addSubview(naviTitleIcon) naviTitleIcon.snp.makeConstraints { make in make.centerX.equalToSuperview() make.width.equalTo(100.rpx) make.height.equalTo(20.rpx) make.centerY.equalTo(backButton.snp.centerY) } vipNaviView.addSubview(naviResumeBtn) naviResumeBtn.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 70.rpx, height: 18.rpx)) make.right.equalTo(-8.rpx) make.centerY.equalTo(backButton.snp.centerY) } self.bottomBgView.addSubview(bottomTitleView) bottomTitleView.snp.makeConstraints { make in make.top.equalTo(50.rpx) make.left.equalTo(32.rpx) make.right.equalTo(-32.rpx) } self.bottomBgView.addSubview(bottomBtnView) bottomBtnView.snp.makeConstraints { make in make.top.equalTo(bottomTitleView.snp.bottom).offset(20.rpx) make.left.right.bottom.equalTo(0) } bottomBtnView.addSubview(countdownView) countdownView.snp.makeConstraints { make in make.top.equalTo(0) make.left.equalTo(12.rpx) make.right.equalTo(-12.rpx) make.height.equalTo(47.rpx) } countdownView.addSubview(countdownLabel) countdownLabel.snp.makeConstraints { make in make.left.right.equalTo(0) make.top.equalTo(5.rpx) make.height.equalTo(17.rpx) } bottomBtnView.addSubview(unlockBtn) unlockBtn.snp.makeConstraints { make in make.left.equalTo(12.rpx) make.right.equalTo(-12.rpx) make.height.equalTo(50.rpx) make.top.equalTo(25.rpx) } bottomBtnView.addSubview(serviceLabel) serviceLabel.snp.makeConstraints { make in make.right.left.equalTo(0) make.height.equalTo(17.rpx) make.top.equalTo(unlockBtn.snp.bottom).offset(8.rpx) } updateServiceLabelText() self.view.bringSubviewToFront(bottomBgView) } // 更新服务条款文本内容 func updateServiceLabelText() { let attr = NSMutableAttributedString() // 固定部分:购买即同意 let firstAttr = NSMutableAttributedString(string: "购买前请先阅读") firstAttr.font(11) firstAttr.color(UIColor.init(white: 0, alpha: 0.4)) attr.append(firstAttr) // 《隐私权政策》 let privacyHL = YYTextHighlight() let privacyStr = "隐私政策" let privacyText = NSMutableAttributedString(string: privacyStr) privacyText.font(11) privacyText.color(UIColor.init(white: 0, alpha: 0.7)) privacyText.yy_setTextHighlight(privacyHL, range: NSRange(location: 0, length: privacyStr.count)) privacyHL.tapAction = { [weak self] containerView, text, range, rect in self?.privacyAction() } let underline1 = YYTextDecoration(style: .single, width: 1, color: UIColor(white: 0, alpha: 0.7)) privacyText.yy_textUnderline = underline1 attr.append(privacyText) let blankAttr = NSMutableAttributedString(string: "&") blankAttr.color(UIColor.init(white: 0, alpha: 0.4)) blankAttr.font(11) attr.append(blankAttr) // 《用户协议》 let serviceHL = YYTextHighlight() let serviceStr = "服务条款" let serviceText = NSMutableAttributedString(string: serviceStr) serviceText.font(11) serviceText.color(UIColor.init(white: 0, alpha: 0.7)) serviceText.yy_setTextHighlight(serviceHL, range: NSRange(location: 0, length: serviceStr.count)) serviceHL.tapAction = { [weak self] containerView, text, range, rect in self?.serviceAction() } let underline = YYTextDecoration(style: .single, width: 1, color: UIColor(white: 0, alpha: 0.7)) serviceText.yy_textUnderline = underline attr.append(serviceText) serviceLabel.attributedText = attr serviceLabel.textAlignment = .center } lazy var vipNaviView: UIView = { let view = UIView(frame: CGRectMake(0, 0, QSLConst.qsl_kScreenW, QSLConst.qsl_kScreenH)) view.addCorner(conrners: [.topLeft,.topRight], radius: 12.rpx) view.backgroundColor = UIColor.clear return view }() lazy var naviTitleIcon: UILabel = { let label = UILabel() label.text("超值福利") label.textColor = .white label.mediumFont(17) label.textAlignment = .center return label }() lazy var naviResumeBtn: UIButton = { let button = UIButton() button.title("恢复订阅") button.setTitleColor(.white, for: .normal) button.titleLabel?.font = UIFont.systemFont(ofSize: 11, weight: .medium) button.image(UIImage(named: "vip_resume_btn")?.withTintColor(UIColor.white)) button.setImageTitleLayout(.imgLeft, spacing: 2.rpx) button.addTarget(self, action: #selector(resumeBtnAction), for: .touchUpInside) return button }() lazy var backButton: UIButton = { let button = UIButton() button.image(UIImage(named: "vip_pay_failure_close")) button.addTarget(self, action: #selector(backBtnAction), for: .touchUpInside) return button }() lazy var bgImageView: UIImageView = { let item = UIImageView() item.contentMode = .scaleAspectFill item.image = UIImage(named: "vip_trial_top") item.layer.masksToBounds = true return item }() lazy var bgTopTit1View: UIImageView = { let item = UIImageView() item.contentMode = .scaleAspectFit item.image = UIImage(named: "vip_trial_top_title_1") item.layer.masksToBounds = true return item }() lazy var bgTopTit2View: UIImageView = { let item = UIImageView() item.contentMode = .scaleAspectFit item.image = UIImage(named: "vip_trial_top_title_2") item.layer.masksToBounds = true return item }() lazy var bgCenterView: UIImageView = { let item = UIImageView() item.contentMode = .scaleAspectFit item.image = UIImage(named: "vip_trial_center") item.layer.masksToBounds = true return item }() lazy var bottomSpaceView: UIView = { let item = UIView() item.backgroundColor = .white return item }() lazy var bottomBgView: UIImageView = { let item = UIImageView() item.contentMode = .scaleAspectFill item.image = UIImage(named: "vip_trial_bottom_bg") item.layer.masksToBounds = true return item }() lazy var bottomTitleView: UIImageView = { let item = UIImageView() item.image = UIImage(named: "vip_trial_bottom_title") return item }() lazy var bottomBtnView: UIView = { let item = UIView() return item }() lazy var bottomView: UIView = { let view = UIView() view.backgroundColor = UIColor.white return view }() lazy var countdownView: UIView = { let view = UIView(frame: CGRectMake(0, 0, QSLConst.qsl_kScreenW - 24.rpx, 47.rpx)) view.addCorner(conrners: [.topLeft,.topRight], radius: 30.rpx) view.backgroundColor = .hexStringColor(hexString: "#FFFED8") return view }() lazy var countdownLabel: QSLCountdownView = { let label = QSLCountdownView(frame: CGRectMake(0, 0, QSLConst.qsl_kScreenW - 24.rpx, 17.rpx),type: 2) return label }() lazy var unlockBtn: UIButton = { let btn = UIButton() btn.gradientBackgroundColor(color1: .hexStringColor(hexString: "#0E5E61"), color2: .hexStringColor(hexString: "#00434E"), width: QSLConst.qsl_kScreenW - 24.rpx, height: 50.rpx, direction: .horizontal) btn.addRadius(radius: 25.rpx) btn.title("立即领取") btn.textColor(.hexStringColor(hexString: "#FFF8EF")) btn.mediumFont(18) btn.addTarget(self, action: #selector(payBtnAction), for: .touchUpInside) return btn }() lazy var serviceLabel: YYLabel = { let label = YYLabel() label.textAlignment = .center return label }() lazy var desLabel: YYLabel = { let label = YYLabel() label.textAlignment = .center label.textColor = UIColor.init(white: 1, alpha: 0.4) label.font = UIFont.systemFont(ofSize: 12) return label }() } extension QSLVipTrialVC : QSLCountdownManagerDelegate{ func countdownManager(_ manager: QSLCountdownManager, didUpdateCountdown type: QSLCountdownType, remainingSeconds: Int) { // 处理倒计时更新 if type == .trial { let totalSeconds = remainingSeconds / 1000 let minutes = totalSeconds / 60 let seconds = totalSeconds % 60 var milliseconds = remainingSeconds % 1000 if(milliseconds <= 1){ milliseconds = 0 } // 格式:MM:SS:SSS(例如 "14:59:500") let timeString = String(format: "%02d : %02d : %03d", minutes, seconds, milliseconds) self.countdownLabel.updateCountdownText(timeString) } } func countdownManager(_ manager: QSLCountdownManager, didFinishCountdown type: QSLCountdownType) { } }