| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- //
- // QSLVipTrialVC.swift
- // QuickSearchLocation
- //
- // Created by Destiny on 2025/10/23.
- //
- import UIKit
- import YYText
- import GKCycleScrollView
- enum QSLTrialVipJumpType: Int {
-
- case notiPush
- case shortcut
- }
- class QSLVipTrialVC: QSLBaseController {
- var type: QSLTrialVipJumpType?
- var selectGood: QSLGoodModel?
- var bannerList : [String] = []
-
- override func viewDidLoad() {
- super.viewDidLoad()
- QSEventHandle.eventPush(eventName: QSLGravityConst.activity_exposure_page, eventProps: ["page_type":"activity_page"])
- QSEventHandle.eventPush(eventName: QSLGravityConst.activity_exposure_page, eventProps: ["page_type":"coupon_popup"])
- 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"]
- )
- }
-
- }
- }
-
- let bannerModel = response.mapObject(QSLWakeupBannerModel.self, modelKey: "data")
- self?.bannerList = bannerModel.banners
- if(bannerModel.banners.count > 0){
- self?.bgCenterView.isHidden = true
- self?.cycleScrollView.isHidden = false
- self?.cycleScrollView.reloadData()
- }else{
- self?.bgCenterView.isHidden = false
- self?.cycleScrollView.isHidden = true
- }
-
- } fail: { code, error in
- self.view.toast(text: "加载商品列表失败")
- self.bgCenterView.isHidden = false
- self.cycleScrollView.isHidden = true
- }
- }
-
- // 恢复按钮点击
- @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(){
- QSEventHandle.eventPush(eventName: QSLGravityConst.activity_page_click, eventProps: ["page_type":"activity_page"])
-
- if(!QSLBaseManager.shared.isLogin()){
- if(QSLBaseManager.shared.isVip()){
- QSLJumpManager.shared.pushToLogin(type: .member)
- return
- }
-
- if(QSLConfig.loginPayEnable){
- QSLJumpManager.shared.pushToLogin(type: .member)
- return
- }
- }
-
- 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.activity_purchase_result, eventProps: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "success","pay_amount":Int32(currentGood.amount),"page_type":"activity_page"])
- DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
- NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil)
- self.backBtnAction()
- if(!QSLBaseManager.shared.isLogin()){
- QSLJumpManager.shared.pushToLogin(type: .member)
- }
- }
-
-
- } else if status == .cancel {
- QSEventHandle.eventPush(eventName: QSLGravityConst.activity_purchase_result, eventProps: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "cancel","pay_amount":Int32(currentGood.amount),"page_type":"activity_page"])
- QSLLoading.error(text: "支付取消")
- } else if status == .fail {
- QSEventHandle.eventPush(eventName: QSLGravityConst.activity_purchase_result, eventProps: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "fail","pay_amount":Int32(currentGood.amount),"page_type":"activity_page"])
- 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)
- }
-
- bgCenterView.isHidden = true
- bgImageView.addSubview(bgCenterView)
- bgCenterView.snp.makeConstraints { make in
- make.centerX.equalTo(bgImageView.snp.centerX)
- make.bottom.equalTo(0)
- }
-
- bgImageView.addSubview(cycleScrollView)
- cycleScrollView.snp.makeConstraints { make in
- make.centerX.equalTo(bgImageView.snp.centerX)
- make.width.equalTo(290.rpx)
- make.height.equalTo(344.rpx)
- make.bottom.equalTo(30.rpx)
- }
-
- 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
- item.isUserInteractionEnabled = 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
- }()
-
- lazy var cycleScrollView: GKCycleScrollView = {
-
- let cycleScrollView = GKCycleScrollView()
- cycleScrollView.dataSource = self
- cycleScrollView.delegate = self
- cycleScrollView.isAutoScroll = true
- cycleScrollView.isInfiniteLoop = true
- cycleScrollView.isChangeAlpha = false
- cycleScrollView.leftRightMargin = 0
- cycleScrollView.topBottomMargin = 0
-
- return cycleScrollView
- }()
- }
- extension QSLVipTrialVC : GKCycleScrollViewDelegate, GKCycleScrollViewDataSource{
- func cycleScrollView(_ cycleScrollView: GKCycleScrollView!, cellForViewAt index: Int) -> GKCycleScrollViewCell! {
- if let cell = cycleScrollView.dequeueReusableCell() {
- cell.imageView.kf.setImage(with: URL(string: self.bannerList[index]))
- cell.layer.masksToBounds = true
- cell.contentMode = .scaleAspectFit
- return cell
- }
-
- let cell = GKCycleScrollViewCell()
- cell.imageView.kf.setImage(with: URL(string: self.bannerList[index]))
- cell.layer.masksToBounds = true
- cell.contentMode = .scaleAspectFit
- return cell
- }
-
- func numberOfCells(in cycleScrollView: GKCycleScrollView!) -> Int {
- return self.bannerList.count
- }
-
- func sizeForCell(in cycleScrollView: GKCycleScrollView!) -> CGSize {
- return CGSizeMake(290.rpx, 344.rpx);
- }
-
-
- }
- 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) {
- }
- }
|