Forráskód Böngészése

[new] 首页优惠卷入口 埋点

zhujieshan 2 hónapja
szülő
commit
e0dfdbc7a8

+ 8 - 4
QuickSearchLocation.xcodeproj/project.pbxproj

@@ -58,6 +58,8 @@
 		04F33BEE2BC78B91003E2111 /* QSLUserModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F33BED2BC78B91003E2111 /* QSLUserModel.swift */; };
 		04F33BF02BC78D54003E2111 /* QSLMapTrackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F33BEF2BC78D54003E2111 /* QSLMapTrackModel.swift */; };
 		0E4DD66B97F41870AE6C6531 /* Pods_QuickSearchLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 192C4A9CA4C1E078845B9C07 /* Pods_QuickSearchLocation.framework */; };
+		FE3046E22E838ECA00BC44E2 /* QSLCountdownManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3046E12E838ECA00BC44E2 /* QSLCountdownManager.swift */; };
+		FE3046E42E838F0200BC44E2 /* QSLCountdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3046E32E838F0200BC44E2 /* QSLCountdownView.swift */; };
 		FE3B5E9A2E2A3B92002616CD /* QSQuickLogonHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3B5E992E2A3B92002616CD /* QSQuickLogonHandle.swift */; };
 		FE5B42422CF4612E000AACCB /* QSLSizeFit.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5B42412CF4612E000AACCB /* QSLSizeFit.swift */; };
 		FE5B42442CF485B9000AACCB /* QSLHomeFriendFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5B42432CF485A0000AACCB /* QSLHomeFriendFooterView.swift */; };
@@ -121,7 +123,6 @@
 		FE9139C92DE6B018001A8E42 /* QSLVipMostGoodCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE9139C82DE6B018001A8E42 /* QSLVipMostGoodCell.swift */; };
 		FE94B4F42D23F09100D2B001 /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE94B4F32D23F09100D2B001 /* LoadingViewController.swift */; };
 		FEC010E72D9E8B06008B8B0A /* QSLSubscriptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC010E62D9E8AFE008B8B0A /* QSLSubscriptionModel.swift */; };
-		FEC3D9542E8274CF007164A9 /* QSLActivityVipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC3D9532E8274CF007164A9 /* QSLActivityVipController.swift */; };
 		FEC3D9562E8276CB007164A9 /* QSLActivityVipVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC3D9552E8276CB007164A9 /* QSLActivityVipVC.swift */; };
 		FEC3D9582E82973C007164A9 /* QSLActivityVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC3D9572E82973C007164A9 /* QSLActivityVipCell.swift */; };
 		FED298C42E37267100F1E0F0 /* QSLVipAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED298C32E37267100F1E0F0 /* QSLVipAlertView.swift */; };
@@ -195,6 +196,8 @@
 		0900052B4EECA6E1E78B9DDB /* Pods-QuickSearchLocation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickSearchLocation.release.xcconfig"; path = "Target Support Files/Pods-QuickSearchLocation/Pods-QuickSearchLocation.release.xcconfig"; sourceTree = "<group>"; };
 		192C4A9CA4C1E078845B9C07 /* Pods_QuickSearchLocation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QuickSearchLocation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		A7769595966E4BEA107F93A2 /* Pods-QuickSearchLocation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickSearchLocation.debug.xcconfig"; path = "Target Support Files/Pods-QuickSearchLocation/Pods-QuickSearchLocation.debug.xcconfig"; sourceTree = "<group>"; };
+		FE3046E12E838ECA00BC44E2 /* QSLCountdownManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLCountdownManager.swift; sourceTree = "<group>"; };
+		FE3046E32E838F0200BC44E2 /* QSLCountdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLCountdownView.swift; sourceTree = "<group>"; };
 		FE3B5E992E2A3B92002616CD /* QSQuickLogonHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSQuickLogonHandle.swift; sourceTree = "<group>"; };
 		FE5B42412CF4612E000AACCB /* QSLSizeFit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLSizeFit.swift; sourceTree = "<group>"; };
 		FE5B42432CF485A0000AACCB /* QSLHomeFriendFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLHomeFriendFooterView.swift; sourceTree = "<group>"; };
@@ -260,7 +263,6 @@
 		FE9139C82DE6B018001A8E42 /* QSLVipMostGoodCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLVipMostGoodCell.swift; sourceTree = "<group>"; };
 		FE94B4F32D23F09100D2B001 /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = "<group>"; };
 		FEC010E62D9E8AFE008B8B0A /* QSLSubscriptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLSubscriptionModel.swift; sourceTree = "<group>"; };
-		FEC3D9532E8274CF007164A9 /* QSLActivityVipController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLActivityVipController.swift; sourceTree = "<group>"; };
 		FEC3D9552E8276CB007164A9 /* QSLActivityVipVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLActivityVipVC.swift; sourceTree = "<group>"; };
 		FEC3D9572E82973C007164A9 /* QSLActivityVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLActivityVipCell.swift; sourceTree = "<group>"; };
 		FED298C32E37267100F1E0F0 /* QSLVipAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSLVipAlertView.swift; sourceTree = "<group>"; };
@@ -799,7 +801,6 @@
 			children = (
 				FE8360802CF723FA00978E03 /* QSLVipController.swift */,
 				FEC3D9552E8276CB007164A9 /* QSLActivityVipVC.swift */,
-				FEC3D9532E8274CF007164A9 /* QSLActivityVipController.swift */,
 			);
 			path = Controller;
 			sourceTree = "<group>";
@@ -854,6 +855,7 @@
 		FE83608E2CFFFACD00978E03 /* View */ = {
 			isa = PBXGroup;
 			children = (
+				FE3046E32E838F0200BC44E2 /* QSLCountdownView.swift */,
 				FE83608F2CFFFAD600978E03 /* QSLAlertView.swift */,
 				FE83609F2D00813400978E03 /* QSLPopView.swift */,
 				FE8360A12D00814000978E03 /* QSLPopViewCell.swift */,
@@ -873,6 +875,7 @@
 		FE8360962D0041E000978E03 /* Tool */ = {
 			isa = PBXGroup;
 			children = (
+				FE3046E12E838ECA00BC44E2 /* QSLCountdownManager.swift */,
 				FE8360972D0041F300978E03 /* QSLCacheManager.swift */,
 				FE8360B22D02939600978E03 /* QSLSocketManager.swift */,
 				FE638A9F2D082FC400858121 /* QSLLoading.swift */,
@@ -1087,6 +1090,7 @@
 				FED298D42E3B176500F1E0F0 /* QSLGuideIsTriggeredModel.swift in Sources */,
 				04F33BCD2BC67486003E2111 /* UIView+Extension.swift in Sources */,
 				FE638AB02D0A8FF700858121 /* QSLContactAddAlertView.swift in Sources */,
+				FE3046E22E838ECA00BC44E2 /* QSLCountdownManager.swift in Sources */,
 				FE8360932D003AA300978E03 /* QSLNetwork.swift in Sources */,
 				FED298C42E37267100F1E0F0 /* QSLVipAlertView.swift in Sources */,
 				FE8360B12D01BFE500978E03 /* QSLFriendRemarkAlertView.swift in Sources */,
@@ -1099,6 +1103,7 @@
 				04F33BCF2BC675C5003E2111 /* UIApplication+Extension.swift in Sources */,
 				04F33BF02BC78D54003E2111 /* QSLMapTrackModel.swift in Sources */,
 				04F33BEB2BC6A657003E2111 /* UIImage+Extension.swift in Sources */,
+				FE3046E42E838F0200BC44E2 /* QSLCountdownView.swift in Sources */,
 				FE8360752CF707FE00978E03 /* QSLMessageTableViewCell.swift in Sources */,
 				04B666CF2BC92B3A0020BFBD /* UITableView+Extension.swift in Sources */,
 				04F33BC52BC64566003E2111 /* CustomTabBarController.swift in Sources */,
@@ -1110,7 +1115,6 @@
 				FE9139C92DE6B018001A8E42 /* QSLVipMostGoodCell.swift in Sources */,
 				FE638AA92D09858C00858121 /* QSLContactController.swift in Sources */,
 				04B6B52D2BCE2C9200777EB4 /* QSLMineViewModel.swift in Sources */,
-				FEC3D9542E8274CF007164A9 /* QSLActivityVipController.swift in Sources */,
 				04B666CB2BC922120020BFBD /* QSLHomeFriendView.swift in Sources */,
 				FE638AAE2D099B0F00858121 /* QSLContactCell.swift in Sources */,
 				04B666C32BC7E3760020BFBD /* CAGradientLayer+Extension.swift in Sources */,

+ 56 - 0
QuickSearchLocation/Classes/Common/Tool/QSLCountdownManager.swift

@@ -0,0 +1,56 @@
+//
+//  CountDownManager.swift
+//  QuickSearchLocation
+//
+//  Created by Destiny on 2025/9/24.
+//
+
+import UIKit
+
+
+class QSLCountdownManager {
+    static let shared = QSLCountdownManager()
+    private var timer: Timer?
+    var selectGood: QSLGoodModel?
+    var remainingSeconds: Int = 900 // 15分钟 = 900秒
+    var updateHandler: ((String) -> Void)?
+    var updateHandler1: ((String) -> Void)?
+    
+    var finishHandler: (() -> Void)?
+    
+    func startCountdown() {
+        timer?.invalidate()
+        timer = nil
+        if(selectGood == nil){
+            return
+        }
+        remainingSeconds = 900
+        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true)
+        RunLoop.current.add(timer!, forMode: .common)
+    }
+    
+    func stopCountdown() {
+        finishHandler?()
+        timer?.invalidate()
+        timer = nil
+    }
+    
+    @objc private func updateCountdown() {
+        remainingSeconds -= 1
+        if remainingSeconds <= 0 {
+            stopCountdown()
+        }
+        
+        let hours = remainingSeconds / 3600
+        let minutes = (remainingSeconds % 3600) / 60
+        let seconds = remainingSeconds % 60
+        
+        let timeString = String(format: "%02d : %02d : %02d", hours, minutes, seconds)
+        updateHandler?(timeString)
+        updateHandler1?(timeString)
+    }
+    
+    deinit {
+        stopCountdown()
+    }
+}

+ 3 - 1
QuickSearchLocation/Classes/Common/Tool/QSLGuideusersToCommentManager.swift

@@ -182,7 +182,7 @@ extension QSLGuideusersToCommentManager {
     }
     
     ///非会员的好评弹窗
-    @MainActor func noMemberPositiveReviewPopWindow() {
+    @MainActor func noMemberPositiveReviewPopWindow(closeBtnClosure: @escaping () -> () = {}) {
         // 创建深灰色文本样式
         let darkGrayAttributes: [NSAttributedString.Key: Any] = [
             .foregroundColor: UIColor(red: 51/255.0, green: 51/255.0, blue: 51/255.0, alpha: 1.0),
@@ -209,6 +209,8 @@ extension QSLGuideusersToCommentManager {
                     self?.requestForReceivingGoodReviewGuidance()
                 }*/
                 //self?.requestAddFri()
+            },closeBtnClosure: {
+                closeBtnClosure()
             })
         }
     }

+ 62 - 0
QuickSearchLocation/Classes/Common/Tool/QSLJumpManager.swift

@@ -6,6 +6,7 @@
 //
 
 import Foundation
+import UIKit
 
 enum JumpType: Int {
     case createInspration = 0   // 灵感创作
@@ -31,6 +32,67 @@ extension QSLJumpManager {
         let vc = QSLVipController()
         vc.type = type
         self.rootViewController()?.pushVC(vc: vc)
+        vc.finishHandler = {[weak self] isCancel in
+            if isCancel {
+                return
+            }
+            if QSLBaseManager.shared.isVip() {
+                return
+            }
+            if let currentWindow = UIApplication.keyWindow {
+                gravityInstance?.track(QSLGravityConst.new_vip_retention_show, properties: ["trigger_type":"close_vip_center"])
+                QSLRetainPopUpAlertView.alert(view: currentWindow, isOneBtn: true, oneBtnText: "继续支付", oneBtnClosure:  { [weak self] in
+                    self?.unlockBtnAction()
+                    gravityInstance?.track(QSLGravityConst.new_vip_retention_click, properties: ["button":"continue_payment"])
+                },secondBtnClosure: {
+                    gravityInstance?.track(QSLGravityConst.new_vip_retention_click, properties: ["button":"close"])
+                })
+            }
+        }
+    }
+    
+    func unlockBtnAction(){
+        if let selectGood = QSLCountdownManager.shared.selectGood{
+            
+            let memberModel = QSLBaseManager.shared.userModel.memberModel
+            
+            if let subscriptionExpired = memberModel.subscriptionExpired, !subscriptionExpired {
+                UIApplication.keyWindow?.toast(text: "你已经订阅了")
+                return
+            }
+            
+            QSLLoading.show()
+            QSLVipManager.shared.startPay(goods: selectGood) { status, outTradeNo in
+                QSLVipManager.shared.isPaying = false
+                if status == .success {
+                    QSLLoading.success(text: "支付成功")
+                    
+                    gravityInstance?.track(QSLGravityConst.new_vip_result, properties: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "success","pay_amount":Int32(selectGood.amount)])
+                    //支付成功埋点
+                    gravityInstance?.track(QSLGravityConst.vip_submit_success, properties: ["id": 01001])
+                    
+                    //弹出是否好评的弹窗
+                    QSLGuideusersToCommentManager.commentShare.manageWhetherTriggerPopUpWindow(QSLGuideusersToCommentType.member)
+                    
+                    // 引力传递支付事件
+                    gravityInstance?.trackPayEvent(withAmount: Int32(selectGood.amount), withPayType: "CNY", withOrderId: outTradeNo, withPayReason: selectGood.name, withPayMethod: "apple")
+                    
+                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+                        NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil)
+                    }
+                    
+                } else if status == .cancel {
+                    gravityInstance?.track(QSLGravityConst.new_vip_result, properties: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "cancel","pay_amount":Int32(selectGood.amount)])
+                    QSLLoading.error(text: "支付取消")
+                } else if status == .fail {
+                    gravityInstance?.track(QSLGravityConst.new_vip_result, properties: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "fail","pay_amount":Int32(selectGood.amount)])
+                    gravityInstance?.track(QSLGravityConst.vip_fail)
+                    QSLLoading.error(text: "支付失败")
+                } else if status == .searchFail {
+                    QSLLoading.error(text: "查询订单失败,请稍后重试")
+                }
+            }
+        }
     }
     
     // 跳转到登录页面

+ 118 - 0
QuickSearchLocation/Classes/Common/View/QSLCountdownView.swift

@@ -0,0 +1,118 @@
+//
+//  CountDownView.swift
+//  QuickSearchLocation
+//
+//  Created by Destiny on 2025/9/24.
+//
+
+import UIKit
+import YYText
+
+class QSLCountdownView: UIView {
+    private let label = YYLabel()
+    private var countdownType: Int  // 1 :首页优惠券 2:引导页
+        
+    // 自定义初始化方法,传入Int参数
+    init(frame: CGRect, type: Int) {
+        self.countdownType = type  // 存储传入的type
+        super.init(frame: frame)
+        setupUI()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    
+    private func setupUI() {
+        addSubview(label)
+        label.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+    
+    func updateCountdownText(_ timeString: String) {
+        
+        if (self.countdownType == 1){
+            let fullText = " \(timeString)   限时特惠"
+            let attributedText = NSMutableAttributedString(string: fullText)
+            
+            // 设置整体样式
+            attributedText.yy_font = UIFont.textM(10)
+            attributedText.yy_color = UIColor.white
+            
+            // 查找时间部分的range
+            if let timeRange = fullText.range(of: timeString) {
+                let nsRange = NSRange(timeRange, in: fullText)
+                let timeAttributedString = NSMutableAttributedString(string: String(fullText[timeRange]))
+                
+                // 设置数字背景
+                let bgColor = UIColor.white
+                let bgBorder = YYTextBorder(fill: bgColor, cornerRadius: 2)
+                bgBorder.insets = UIEdgeInsets(top: -3, left: -2.5, bottom: -2, right: -2)
+                
+                let bgBorder1 = YYTextBorder(fill: UIColor.clear, cornerRadius: 0)
+                bgBorder1.insets = UIEdgeInsets(top: -3, left: -2, bottom: -2, right: -2)
+                
+                // 遍历每个字符
+                for (index, char) in timeAttributedString.string.enumerated() {
+                    let range = NSRange(location: index, length: 1)
+                    if char.isNumber {
+                        timeAttributedString.yy_setTextBackgroundBorder(bgBorder, range: range)
+                        timeAttributedString.yy_setColor(UIColor.hexStringColor(hexString: "#0E5662"), range: range)
+                    } else {
+                        timeAttributedString.yy_setTextBackgroundBorder(bgBorder1, range: range)
+                        timeAttributedString.yy_setColor(bgColor, range: range)
+                        timeAttributedString.yy_setKern(NSNumber(value: 1), range: range)
+                    }
+                }
+                
+                // 替换原始文本中的时间部分
+                attributedText.replaceCharacters(in: nsRange, with: timeAttributedString)
+            }
+            
+            label.attributedText = attributedText
+        }else{
+            let fullText = "优惠活动倒计时   \(timeString)   限时特惠"
+            let attributedText = NSMutableAttributedString(string: fullText)
+            
+            // 设置整体样式
+            attributedText.yy_font = UIFont.systemFont(ofSize: 11)
+            attributedText.yy_color = UIColor.hexStringColor(hexString: "#FF5656")
+            
+            // 查找时间部分的range
+            if let timeRange = fullText.range(of: timeString) {
+                let nsRange = NSRange(timeRange, in: fullText)
+                let timeAttributedString = NSMutableAttributedString(string: String(fullText[timeRange]))
+                
+                // 设置数字背景
+                let bgColor = UIColor.hexStringColor(hexString: "#FF5656")
+                let bgBorder = YYTextBorder(fill: bgColor, cornerRadius: 2)
+                bgBorder.insets = UIEdgeInsets(top: -3, left: -2.5, bottom: -2, right: -2)
+                
+                let bgBorder1 = YYTextBorder(fill: UIColor.clear, cornerRadius: 0)
+                bgBorder1.insets = UIEdgeInsets(top: -3, left: -2, bottom: -2, right: -2)
+                
+                // 遍历每个字符
+                for (index, char) in timeAttributedString.string.enumerated() {
+                    let range = NSRange(location: index, length: 1)
+                    if char.isNumber {
+                        timeAttributedString.yy_setTextBackgroundBorder(bgBorder, range: range)
+                        timeAttributedString.yy_setColor(UIColor.white, range: range)
+                    } else {
+                        timeAttributedString.yy_setTextBackgroundBorder(bgBorder1, range: range)
+                        timeAttributedString.yy_setColor(bgColor, range: range)
+                        timeAttributedString.yy_setKern(NSNumber(value: 1), range: range)
+                    }
+                }
+                
+                // 替换原始文本中的时间部分
+                attributedText.replaceCharacters(in: nsRange, with: timeAttributedString)
+            }
+            
+            label.attributedText = attributedText
+            label.textAlignment = .center
+        }
+
+    }
+}

+ 14 - 0
QuickSearchLocation/Classes/Main/QSLBaseManager.swift

@@ -65,9 +65,23 @@ extension QSLBaseManager {
         
         if time != 0 {
             self.userModel.vipEndTimestamp = Date(timeIntervalSince1970: TimeInterval(time / 1000))
+            NotificationCenter.default.post(
+                name: Notification.Name("QSLHomeUpdateCouponViewNoti"),
+                object: nil,
+                userInfo: ["showCoupon": false]
+            )
         } else {
             self.userModel.vipEndTimestamp = nil
+            if let selectgood = QSLCountdownManager.shared.selectGood{
+                NotificationCenter.default.post(
+                    name: Notification.Name("QSLHomeUpdateCouponViewNoti"),
+                    object: nil,
+                    userInfo: ["showCoupon": true]
+                )
+            }
+            
         }
+        
     }
     
     // 是否登录状态

+ 20 - 8
QuickSearchLocation/Classes/Pages/QSLHome/Controller/QSLHomeController.swift

@@ -43,14 +43,26 @@ class QSLHomeController: QSLBaseController {
         //处理第一次进入首页定位
         firstEnterLocationHomepage()
         
-//        if let currentWindow = UIApplication.keyWindow {
-//            QSLHomeAddFriendView.alert(view: currentWindow) {
-//                QSLJumpManager.shared.pushToAdd(type: .homealert)
-//            }
-//        }
-        
-        let vc = QSLActivityVipVC()
-        self.rootViewController()?.pushVC(vc: vc)
+        if QSLBaseManager.shared.isVip() {
+            if let currentWindow = UIApplication.keyWindow {
+                QSLHomeAddFriendView.alert(view: currentWindow) {
+                    QSLJumpManager.shared.pushToAdd(type: .homealert)
+                }
+            }
+        }else{
+            let vc = QSLActivityVipVC()
+            vc.modalPresentationStyle = .fullScreen
+            self.present(vc, animated: false)
+            vc.dismissHandler = {[weak self] isCancel in
+                QSLGuideusersToCommentManager.commentShare.noMemberPositiveReviewPopWindow {
+                    if let currentWindow = UIApplication.keyWindow {
+                        QSLHomeAddFriendView.alert(view: currentWindow) {
+                            QSLJumpManager.shared.pushToAdd(type: .homealert)
+                        }
+                    }
+                }
+            }
+        }
         
     }
     

+ 2 - 2
QuickSearchLocation/Classes/Pages/QSLHome/View/QSLHomeAddFriendAlertView.swift

@@ -145,14 +145,14 @@ extension QSLHomeAddFriendView {
         hideButton.snp.makeConstraints { make in
             make.size.equalTo(CGSize(width: 60.rpx, height: 28.rpx))
             make.centerX.equalToSuperview()
-            make.bottom.equalTo(-20.rpx)
+            make.bottom.equalTo(-18.rpx)
         }
         
         contentView.addSubview(oneButton)
         oneButton.snp.makeConstraints { make in
             make.size.equalTo(CGSize(width: 250.rpx, height: 44.rpx))
             make.centerX.equalToSuperview()
-            make.bottom.equalTo(-56.rpx)
+            make.bottom.equalTo(-54.rpx)
         }
     }
 }

+ 161 - 5
QuickSearchLocation/Classes/Pages/QSLHome/View/QSLHomeFriendView.swift

@@ -6,6 +6,8 @@
 //
 
 import UIKit
+import YYText
+import SnapKit
 
 enum FriendViewLocation {
     case ScrollTop
@@ -31,6 +33,9 @@ protocol QSLHomeFriendViewDelegate: NSObjectProtocol {
 class QSLHomeFriendView: UIView {
     
     weak var delegate: QSLHomeFriendViewDelegate?
+    // 动态约束处理
+    var couponViewTopConstraint: Constraint? = nil
+    private var friBgViewTopConstraint: Constraint? // 新增:保存friBgView的顶部约束
     
     var viewModel:QSLHomeViewModel? {
         didSet {
@@ -72,6 +77,37 @@ class QSLHomeFriendView: UIView {
         button.addTarget(self, action: #selector(refreshBtnAction), for: .touchUpInside)
         return button
     }()
+
+    lazy var couponView: UIImageView = {
+        let view = UIImageView()
+        view.image = UIImage(named: "home_activity_bg")
+        view.isUserInteractionEnabled = true
+        view.isHidden = true
+        let tapG = UITapGestureRecognizer.init(target: self, action: #selector(unlockBtnAction))
+        view.addGestureRecognizer(tapG)
+        return view
+    }()
+    
+    lazy var couponLabel: YYLabel = {
+        let label = YYLabel()
+        return label
+    }()
+    
+    lazy var countdownLabel: QSLCountdownView = {
+        let label = QSLCountdownView(frame: CGRectMake(0, 0, QSLConst.qsl_kScreenW - 24.rpx, 17.rpx),type: 1)
+        return label
+    }()
+    
+    lazy var couponBtn: UILabel = {
+        let label = UILabel()
+        label.gradientBackgroundColor(color1: UIColor.hexStringColor(hexString: "#FFFEF3", alpha: 1), color2: UIColor.hexStringColor(hexString: "#FFE6C0", alpha: 1), width: 65.rpx, height: 28.rpx, direction: .horizontal)
+        label.text("去使用")
+        label.textAlignment = .center
+        label.font = UIFont.textM(14)
+        label.textColor = (UIColor.hexStringColor(hexString: "#3A331C"))
+        label.addRadius(radius: 14.rpx)
+        return label
+    }()
     
     lazy var friBgView: UIView = {
         
@@ -139,6 +175,13 @@ class QSLHomeFriendView: UIView {
         self.scrollBottomHeight = qsl_kScreenH - qsl_kTabbarFrameH - 56 - 40
         self.setupUI()
         self.addPanAction()
+        
+        NotificationCenter.default.addObserver(
+                    self,
+                    selector: #selector(handleCouponNotification(_:)),
+                    name: Notification.Name("QSLHomeUpdateCouponViewNoti"),
+                    object: nil
+                )
     }
     
     required init?(coder: NSCoder) {
@@ -156,7 +199,11 @@ class QSLHomeFriendView: UIView {
 extension QSLHomeFriendView {
     
     func setupUI() {
-        
+        addSubview(couponView)
+        couponView.addSubview(couponBtn)
+        couponView.addSubview(couponLabel)
+        couponView.addSubview(countdownLabel)
+ 
         addSubview(friBgView)
         addSubview(friTableView)
         
@@ -164,7 +211,7 @@ extension QSLHomeFriendView {
         addSubview(friExpandButton)
         addSubview(refreshButton)
         
-        addSubview(friHeaderView)
+        friBgView.addSubview(friHeaderView)
         friHeaderView.addSubview(friHeaderTitleIcon)
         friHeaderView.addSubview(friHeaderAddBtn)
         
@@ -179,10 +226,39 @@ extension QSLHomeFriendView {
             make.top.equalTo(friMapLogoImageView.snp.bottom).offset(8)
             make.centerX.equalTo(snp.centerX)
         }
+
+        // couponView 的约束(初始时设为隐藏)
+        couponView.snp.makeConstraints { make in
+            make.left.equalTo(8.rpx)
+            make.right.equalTo(-8.rpx)
+            make.height.equalTo(58.rpx)
+            couponViewTopConstraint = make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx).constraint
+        }
+        
+        couponBtn.snp.makeConstraints { make in
+            make.height.equalTo(28.rpx)
+            make.width.equalTo(65.rpx)
+            make.right.equalTo(-12.rpx)
+            make.centerY.equalToSuperview()
+        }
         
+        couponLabel.snp.makeConstraints { make in
+            make.height.equalTo(22.rpx)
+            make.top.equalTo(10.rpx)
+            make.left.equalTo(60.rpx)
+            make.right.equalTo(couponBtn.snp.left).offset(-10.rpx)
+        }
+        
+        countdownLabel.snp.makeConstraints { make in
+            make.height.equalTo(17.rpx)
+            make.top.equalTo(couponLabel.snp.bottom).offset(0)
+            make.left.equalTo(60.rpx)
+            make.right.equalTo(couponBtn.snp.left).offset(-10.rpx)
+        }
+
         friBgView.snp.makeConstraints { make in
-            make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx)
-            make.left.bottom.right.equalTo(0)
+            friBgViewTopConstraint = make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx).constraint
+            make.left.bottom.right.equalToSuperview()
         }
         
         refreshButton.snp.makeConstraints { make in
@@ -194,7 +270,7 @@ extension QSLHomeFriendView {
         friHeaderView.snp.makeConstraints { make in
             make.left.right.equalTo(0)
             make.height.equalTo(61.rpx)
-            make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx)
+            make.top.equalTo(0)
         }
         
         friHeaderTitleIcon.snp.makeConstraints { make in
@@ -220,6 +296,10 @@ extension QSLHomeFriendView {
 
 // MARK: - 点击事件
 extension QSLHomeFriendView {
+    @objc func unlockBtnAction(){
+        gravityInstance?.track(QSLGravityConst.home_coupon_click, properties: [:])
+        QSLJumpManager.shared.unlockBtnAction()
+    }
     
     @objc func homeFriPhoneViewAction() {
         delegate?.homeFriPhoneViewClick()
@@ -239,6 +319,82 @@ extension QSLHomeFriendView: QSLHomeFriendTableViewCellDelegate {
     func locateBtnAction(model: QSLUserModel) {
         delegate?.locateBtnAction(model: model)
     }
+    
+    @objc private func handleCouponNotification(_ notification: Notification) {
+        guard let shouldShow = notification.userInfo?["showCoupon"] as? Bool else { return }
+        
+        updateCouponViewVisibility(show: shouldShow)
+    }
+    
+    private func updateCouponViewVisibility(show: Bool) {
+        if(couponView.isHidden == !show){
+            return
+        }
+        couponView.isHidden = !show
+        
+        if show {
+            if let model = QSLCountdownManager.shared.selectGood {
+                let amount : Int = Int((model.originalAmount - model.amount) / 100)
+                
+                let attr = NSMutableAttributedString()
+                
+                let firstText = "您有一订单未支付专属优惠券 "
+                let firstAttr = NSMutableAttributedString(string: firstText)
+                firstAttr.yy_font = UIFont.textM(10)
+                firstAttr.yy_color = UIColor.white
+                attr.append(firstAttr)
+                
+                if amount > 0 {
+                    let secondText = "-¥\(amount)"
+                    let secondAttr = NSMutableAttributedString(string: secondText)
+                    secondAttr.yy_font = UIFont.textB(15)
+                    secondAttr.yy_color = UIColor.hexStringColor(hexString: "#E1E3A3")
+                    attr.append(secondAttr)
+                }
+                
+                self.couponLabel.attributedText = attr
+                
+                QSLCountdownManager.shared.updateHandler = { [weak self] timeString in
+                    self?.countdownLabel.updateCountdownText(timeString)
+                }
+                QSLCountdownManager.shared.finishHandler  = { [weak self] in
+                    self?.updateCouponViewVisibility(show: false)
+                }
+                QSLCountdownManager.shared.startCountdown()
+                
+            }
+            
+            couponViewTopConstraint?.update(offset: 8.rpx)
+            friBgViewTopConstraint?.update(offset: 8.rpx) // 修改这里
+            friBgView.snp.remakeConstraints { make in
+                make.top.equalTo(couponView.snp.bottom).offset(0)
+                make.left.bottom.right.equalToSuperview()
+            }
+            
+            refreshButton.snp.remakeConstraints { make in
+                make.size.equalTo(CGSize(width: 40.rpx, height: 40.rpx))
+                make.right.equalTo(-8.rpx)
+                make.bottom.equalTo(couponView.snp.top).offset(-12.rpx)
+            }
+            
+        } else {
+            couponViewTopConstraint?.update(offset: 0)
+            friBgView.snp.remakeConstraints { make in
+                make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx)
+                make.left.bottom.right.equalToSuperview()
+            }
+            
+            refreshButton.snp.remakeConstraints { make in
+                make.size.equalTo(CGSize(width: 40.rpx, height: 40.rpx))
+                make.right.equalTo(-8.rpx)
+                make.bottom.equalTo(friBgView.snp.top).offset(-12.rpx)
+            }
+        }
+        
+        UIView.animate(withDuration: 0.3) {
+            self.layoutIfNeeded()
+        }
+    }
 }
 
 // MARK: - 添加滑动逻辑

+ 19 - 19
QuickSearchLocation/Classes/Pages/QSLVip/Cell/QSLActivityVipCell.swift

@@ -122,12 +122,28 @@ class QSLActivityVipCell: UICollectionViewCell {
         
         self.goodModel = model
         self.type = type
-        
-        
-        let text = model.content
+
+        var text = model.content
         self.goodDailyPriceLabel.text = text
         self.goodNameLabel.text = model.name
         
+        var priceText = ""
+        if model.amount.truncatingRemainder(dividingBy: 100) == 0 {
+            priceText = "¥\(Int(model.amount / 100))"
+        } else {
+            priceText = String(format: "¥%.2lf", model.amount / 100 )
+        }
+        self.priceLabel.text = priceText
+        self.priceLabel.setSpecificTextColorFont("¥", color: .hexStringColor(hexString: "#404040"), font: UIFont.systemFont(ofSize: 14, weight: .medium))
+
+        var orinalPriceText = ""
+        if model.originalAmount.truncatingRemainder(dividingBy: 100) == 0 {
+            orinalPriceText = "¥\(Int(model.originalAmount / 100))"
+        } else {
+            orinalPriceText = String(format: "¥%.2lf", model.originalAmount / 100 )
+        }
+        self.originPriceLabel.text = orinalPriceText
+        
         if(type == .top){
             self.bgView.layer.cornerRadius = 0
             self.bgView.layer.masksToBounds = true
@@ -197,22 +213,6 @@ class QSLActivityVipCell: UICollectionViewCell {
             }
         }
 
-        var priceText = ""
-        if model.amount.truncatingRemainder(dividingBy: 100) == 0 {
-            priceText = "¥\(Int(model.amount / 100))"
-        } else {
-            priceText = String(format: "¥%.2lf", model.amount / 100 )
-        }
-        self.priceLabel.text = priceText
-        self.priceLabel.setSpecificTextColorFont("¥", color: .hexStringColor(hexString: "#404040"), font: UIFont.systemFont(ofSize: 14, weight: .medium))
-
-        var orinalPriceText = ""
-        if model.originalAmount.truncatingRemainder(dividingBy: 100) == 0 {
-            orinalPriceText = "¥\(Int(model.originalAmount / 100))"
-        } else {
-            orinalPriceText = String(format: "¥%.2lf", model.originalAmount / 100 )
-        }
-        self.originPriceLabel.text = orinalPriceText
     }
 }
 

+ 0 - 898
QuickSearchLocation/Classes/Pages/QSLVip/Controller/QSLActivityVipController.swift

@@ -1,898 +0,0 @@
-//
-//  QSLActivityVipController.swift
-//  QuickSearchLocation
-//
-//  Created by Destiny on 2025/9/23.
-//
-
-import UIKit
-
-class QSLActivityVipController: QSLBaseController {
-    
-    // 购买人数
-    static let record_non_member_clicks_to_claim = "RECORD_NON_MEMBER_CLIKS_CLAIM"
-    
-    struct UX {
-        static let mostCellHeight = 113.0.rpx
-        static let collectionRowHeight = 137.0.rpx
-        static let goodsBgHeight = 54.rpx + UX.mostCellHeight + 20.rpx
-    }
-    
-    var type: QSLVipJumpType?
-    
-    var goodList: [QSLGoodModel] = [QSLGoodModel]()
-    
-    ///是否是订阅的产品
-    //var is
-    
-    var selectGood: QSLGoodModel? {
-        didSet {
-            updateSelectGoodUI()
-        }
-    }
-    
-    lazy var vipBg: UIImageView = {
-       
-        let imageView = UIImageView()
-        imageView.image = UIImage(named: "vip_bg")
-        return imageView
-    }()
-    
-    lazy var backButton: UIButton = {
-       
-        let button = UIButton()
-        button.image(UIImage(named: "public_back_btn_white"))
-        button.title("会员中心")
-        button.mediumFont(17)
-        button.textColor(.white)
-        button.setImageTitleLayout(.imgLeft, spacing: 4.rpx)
-        button.addTarget(self, action: #selector(backBtnAction), for: .touchUpInside)
-        return button
-    }()
-    
-    lazy var peopleCountView: UIView = {
-       
-        let view = UIView()
-        view.layer.cornerRadius = 12.rpx
-        view.layer.borderColor = UIColor.white.withAlphaComponent(0.16).cgColor
-        view.layer.borderWidth = 1.rpx
-        return view
-    }()
-    
-    lazy var peopleAvatarImageView: UIImageView = {
-       
-        let imageView = UIImageView()
-        imageView.image = UIImage(named: "vip_avatars_icon")
-        return imageView
-    }()
-    
-    lazy var peopleCountLabel: UILabel = {
-       
-        let label = UILabel()
-        label.text("1456488人 已开通VIP")
-        label.color("#FFE6C0")
-        label.font(12)
-        return label
-    }()
-    
-    lazy var scrollView: UIScrollView = {
-       
-        let scrollView = UIScrollView()
-//        scrollView.delegate = self
-        scrollView.bounces = false
-        scrollView.showsVerticalScrollIndicator = false
-        scrollView.contentInsetAdjustmentBehavior = .never
-        return scrollView
-    }()
-    
-    lazy var cardBannerImageView: UIImageView = {
-       
-        let imageView = UIImageView()
-        imageView.image = UIImage(named: "vip_banner_card")
-        return imageView
-    }()
-    
-    lazy var vipTimeLabel: UILabel = {
-       
-        let label = UILabel()
-        label.text("升级VIP会员,解锁全部功能")
-        label.font(13)
-        label.textColor = .hexStringColor(hexString: "#AF7655")
-        return label
-    }()
-    
-    lazy var funcBannerImageView: UIImageView = {
-       
-        let imageView = UIImageView()
-        imageView.image = UIImage(named: "vip_banner_func")
-        return imageView
-    }()
-    
-    lazy var mainView: UIView = {
-       
-        let view = UIView()
-        view.backgroundColor = QSLColor.backGroundColor
-        view.addRadius(radius: 12.rpx)
-        return view
-    }()
-    
-    lazy var goodsBgView: UIView = {
-       
-        let view = UIView()
-        view.gradientBackgroundColor(color1: .hexStringColor(hexString: "#FFF8F2"), color2: .hexStringColor(hexString: "#FFFFFF"), width: QSLConst.qsl_kScreenW, height: UX.goodsBgHeight, direction: .vertical)
-        return view
-    }()
-    
-    lazy var vipGoodsTitleIcon: UIImageView = {
-       
-        let imageView = UIImageView()
-        imageView.image = UIImage(named: "vip_title_icon")
-        return imageView
-    }()
-    
-    lazy var resumeBtn: UIButton = {
-       
-        let button = UIButton()
-        button.title("恢复购买")
-        button.setTitleColor(.hexStringColor(hexString: "#000000").withAlphaComponent(0.5), for: .normal)
-        button.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .medium)
-        button.image(UIImage(named: "vip_resume_btn"))
-        button.setImageTitleLayout(.imgLeft, spacing: 2.rpx)
-        button.addTarget(self, action: #selector(resumeBtnAction), for: .touchUpInside)
-        return button
-    }()
-    
-    lazy var mostCell: QSLVipMostGoodCell = {
-        
-        let cell = QSLVipMostGoodCell()
-        
-        cell.isUserInteractionEnabled = true
-        let tap = UITapGestureRecognizer(target: self, action: #selector(firstCellClickAction))
-        cell.addGestureRecognizer(tap)
-        
-        return cell
-    }()
-    
-    lazy var goodsCollectionView: UICollectionView = {
-            
-        let layout = UICollectionViewFlowLayout()
-        layout.minimumLineSpacing = 0
-        layout.scrollDirection = .vertical
-        
-        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
-        collectionView.backgroundColor = .clear
-        
-        collectionView.dataSource = self
-        collectionView.delegate = self
-        
-        collectionView.showsHorizontalScrollIndicator = false
-        collectionView.bounces = false
-        
-        collectionView.register(cellClass: QSLVipGoodCollectionViewCell.self)
-        
-        return collectionView
-    }()
-    
-    lazy var selectBtn: UIButton = {
-       
-        let btn = UIButton()
-        btn.image(UIImage(named: "public_select_btn_false"), .normal)
-        btn.image(UIImage(named: "public_select_btn_true"), .selected)
-        btn.addTarget(self, action: #selector(selectBtnAction), for: .touchUpInside)
-        return btn
-    }()
-    
-    lazy var bottomContentView: UIView = {
-        let view = UIView()
-        return view
-    }()
-    
-    lazy var bottomTopView: UIView = {
-        let view = UIView()
-        view.isHidden = true
-        view.backgroundColor = .hexStringColor(hexString: "#FFFED8")
-        view.addRadius(radius: 30.rpx)
-        return view
-    }()
-    
-    
-    lazy var bottomTopLabel: UILabel = {
-        let label = UILabel()
-        label.text = ""
-        return label
-    }()
-    
-    func bottomTopLabelInfo(_ goodInfo : QSLGoodModel) {
-        let darkGrayAttributes: [NSAttributedString.Key: Any] = [
-            .foregroundColor: UIColor.hexStringColor(hexString: "#404040"),
-            .font: UIFont.systemFont(ofSize: 11),
-            .paragraphStyle: {
-                let paragraphStyle = NSMutableParagraphStyle()
-                paragraphStyle.alignment = .center
-                return paragraphStyle
-            }(),
-            .kern: -1
-        ]
-
-        // 创建绿色文本样式
-        let greenAttributes: [NSAttributedString.Key: Any] = [
-            .foregroundColor: UIColor.hexStringColor(hexString: "#EE6C1C"),
-            .font:  UIFont.systemFont(ofSize: 15, weight: .black),
-            .paragraphStyle: {
-                let paragraphStyle = NSMutableParagraphStyle()
-                paragraphStyle.alignment = .center
-                return paragraphStyle
-            }(),
-            .kern: -1
-        ]
-        
-        let textArray = goodInfo.text.components(separatedBy: goodInfo.keys.key)
-        if textArray.count > 0 && goodInfo.text.count > 0{
-            bottomTopView.isHidden = false
-            let inputtr = NSMutableAttributedString()
-            let titleOneAttr = NSAttributedString(string: textArray.first ?? "", attributes: darkGrayAttributes)
-            let titleTwoAttr = NSAttributedString(string: " \(goodInfo.keys.value) ", attributes: greenAttributes)
-            inputtr.append(titleOneAttr)
-            inputtr.append(titleTwoAttr)
-            if textArray.count > 1 {
-                let titleThreeAttr = NSAttributedString(string: textArray[1], attributes: darkGrayAttributes)
-                inputtr.append(titleThreeAttr)
-            }
-            bottomTopLabel.attributedText = inputtr
-        } else {
-            bottomTopView.isHidden = true
-        }
-
-    }
-    
-    lazy var bottomView: UIView = {
-       
-        let view = UIView()
-        view.gradientBackgroundColor(color1: .hexStringColor(hexString: "#00434E"), color2: .hexStringColor(hexString: "#0E5E61"), width: QSLConst.qsl_kScreenW - 24.rpx, height: 50.rpx, direction: .horizontal)
-        view.addRadius(radius: 25.rpx)
-        return view
-    }()
-    
-    lazy var unlockBtn: UIButton = {
-       
-        let btn = UIButton()
-        btn.setBackgroundImage(UIImage(named: "vip_unlock_btn_bg"), for: .normal)
-        btn.title("立即解锁")
-        btn.textColor(.hexStringColor(hexString: "#9B3800"))
-        btn.mediumFont(18)
-        btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 30.rpx, bottom: 0, right: 0)
-        btn.addTarget(self, action: #selector(unlockBtnAction), for: .touchUpInside)
-        return btn
-    }()
-    
-    lazy var priceIconLabel: UILabel = {
-       
-        let label = UILabel()
-        label.text("¥")
-        label.textColor = .hexStringColor(hexString: "#FFF8EF")
-        label.font(14)
-        return label
-    }()
-    
-    lazy var priceLabel: UILabel = {
-        
-        let label = UILabel()
-        label.text("168")
-        label.textColor = .hexStringColor(hexString: "#FFF8EF")
-        label.font(24)
-        return label
-    }()
-    
-    lazy var goodTypeLabel: UILabel = {
-        
-        let label = UILabel()
-        label.text("/")
-        label.textColor = .hexStringColor(hexString: "#FFF8EF")
-        label.font(12)
-        return label
-    }()
-    
-    lazy var goodOriginalPriceLabel: UILabel = {
-        
-        let label = UILabel()
-        label.text("原价228")
-        label.textColor = .hexStringColor(hexString: "#FFF8EF")
-        label.font(12)
-        label.centerLineText(lineValue: 1, underlineColor: .hexStringColor(hexString: "#FFF8EF"))
-        return label
-    }()
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        initializeView()
-        
-        updateUI()
-        
-        requestItemList()
-        
-        calculateBuyCount()
-        
-        if let type = self.type {
-            switch type {
-            case .homeRoad:
-                gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1001])
-            case .add:
-                gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1002])
-            case .friendRoad:
-                gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1003])
-            case .contact:
-                gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1004])
-            case .mine:
-                gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1006])
-            case .guideComments:
-                print("")
-            }
-        }
-    }
-}
-
-extension QSLActivityVipController {
-    
-    @objc func privacyAction() {
-        
-        let vc = QSLWebViewController()
-        vc.webUrl = QSLConfig.AppPrivacyAgreementLink
-        vc.title = "隐私政策"
-        self.navigationController?.pushViewController(vc, animated: true)
-    }
-    
-    @objc func serviceAction() {
-        
-        let vc = QSLWebViewController()
-        vc.webUrl = QSLConfig.AppServiceAgreementLink
-        vc.title = "服务协议"
-        self.navigationController?.pushViewController(vc, animated: true)
-    }
-    
-    @objc func subscibeAction() {
-        
-        let vc = QSLWebViewController()
-        vc.webUrl = QSLConfig.AppSubscibeAgreementLink
-        vc.title = "续订说明"
-        self.navigationController?.pushViewController(vc, animated: true)
-    }
-    
-    @objc func selectBtnAction() {
-        
-        selectBtn.isSelected = !selectBtn.isSelected
-    }
-    
-    // 恢复按钮点击
-    @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 unlockBtnAction() {
-        
-        switch self.selectGood?.level {
-        case 100 :
-            gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1006])
-            break
-        case 700:
-            gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1005])
-            break;
-        case 3100:
-            gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1004])
-            break;
-        case 9200:
-            gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1003])
-            break;
-        case 36600:
-            gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1002])
-            break;
-        case 3660000:
-            gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1001])
-            break;
-        default:
-            break;
-        }
-        
-//        if !selectBtn.isSelected {
-//            self.view.toast(text: "请先同意《隐私权政策》和《用户协议》")
-//            return
-//        }
-        
-        let memberModel = QSLBaseManager.shared.userModel.memberModel
-        
-        if let subscriptionExpired = memberModel.subscriptionExpired, !subscriptionExpired {
-            self.view.toast(text: "你已经订阅了")
-            return
-        }
-        
-        if goodList.count > 0, let selectGood = self.selectGood {
-            QSLLoading.show()
-            QSLVipManager.shared.startPay(goods: selectGood) { [self] status, outTradeNo in
-                QSLVipManager.shared.isPaying = false
-                if status == .success {
-                    QSLLoading.success(text: "支付成功")
-                    
-                    //支付成功埋点
-                    gravityInstance?.track(QSLGravityConst.vip_submit_success, properties: ["id": 01001])
-                    
-                    //弹出是否好评的弹窗
-                    QSLGuideusersToCommentManager.commentShare.manageWhetherTriggerPopUpWindow(QSLGuideusersToCommentType.member)
-                    
-                    // 引力传递支付事件
-                    if let selectGood = self.selectGood {
-                        gravityInstance?.trackPayEvent(withAmount: Int32(selectGood.amount), withPayType: "CNY", withOrderId: outTradeNo, withPayReason: selectGood.name, withPayMethod: "apple")
-                    }
-                    
-                    #if DEBUG
-                    #else
-                    //苹果广告奇异果传递支付事件
-                    QSWikiHandle.shared.addEventResultAttribution(eventDict: ["event_name": "pay", "event_val": selectGood.amount])
-                    #endif
-                    
-                    if let type = self.type {
-                        switch type {
-                        case .homeRoad:
-                            gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1001])
-                        case .add:
-                            gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1002])
-                        case .friendRoad:
-                            gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1003])
-                        case .contact:
-                            gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1004])
-                        case .mine:
-                            gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1006])
-                        case .guideComments:
-                            print("")
-                        }
-                    }
-
-                    switch self.selectGood?.level {
-                    case 100 :
-                        gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 1])
-                        break
-                    case 700:
-                        gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 5])
-                        break;
-                    case 3100:
-                        gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 9])
-                        break;
-                    case 9200:
-                        gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 13])
-                        break;
-                    case 36600:
-                        gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 17])
-                        break;
-                    case 3660000:
-                        gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 21])
-                        break;
-                    default:
-                        break;
-                    }
-
-                    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
-                        self.navigationController?.popViewController(animated: true)
-                        NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil)
-                    }
-                    
-                } else if status == .cancel {
-                    QSLLoading.error(text: "支付取消")
-                    payFailAlertTip()
-                } else if status == .fail {
-                    gravityInstance?.track(QSLGravityConst.vip_fail)
-                    QSLLoading.error(text: "支付失败")
-                    payFailAlertTip()
-                } else if status == .searchFail {
-                    QSLLoading.error(text: "查询订单失败,请稍后重试")
-                }
-            }
-        }
-    }
-    
-    func payFailAlertTip() {
-        if let currentWindow = UIApplication.keyWindow {
-            QSLRetainPopUpAlertView.alert(view: currentWindow, isOneBtn: true, oneBtnText: "继续支付", oneBtnClosure:  { [weak self] in
-                self?.unlockBtnAction()
-            })
-        }
-    }
-    
-    @objc func firstCellClickAction() {
-        
-        for i in 0..<self.goodList.count {
-            self.goodList[i].isSelect = false
-        }
-        
-        if self.goodList.count > 0 {
-            self.goodList[0].isSelect = true
-            self.selectGood = self.goodList[0]
-            self.mostCell.config(model: self.goodList[0])
-            //选中立即支付
-            unlockBtnAction()
-        }
-        
-        switch self.selectGood?.level {
-        case 100 :
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
-            break
-        case 700:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
-            break;
-        case 3100:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
-            break;
-        case 9200:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
-            break;
-        case 36600:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
-            break;
-        case 3660000:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
-            break;
-        default:
-            break;
-        }
-        
-        self.goodsCollectionView.reloadData()
-    }
-}
-
-extension QSLActivityVipController {
-    
-    // 请求商品列表
-    func requestItemList() {
-        // 定义并赋值 itemListDict(根据条件变化)
-        var itemListDict: [String: Any] = ["itemListType": 2]
-        if UserDefaults.standard.string(forKey: QSLActivityVipController.record_non_member_clicks_to_claim) == nil && self.type == .guideComments {
-            itemListDict = ["itemListType": 2, "showExtraText": true]
-            // 记录非会员点击领取
-            UserDefaults.standard.set("YES", forKey: QSLActivityVipController.record_non_member_clicks_to_claim)
-            UserDefaults.standard.synchronize()
-        }
-        
-        // 网络请求时使用 itemListDict 作为参数(关键修复)
-        QSLNetwork().request(.vipItemList(dict: itemListDict)) { response in
-            // 后续逻辑不变...
-            let list = response.mapArray(QSLGoodModel.self, modelKey: "data>list")
-            self.goodList = list
-            
-            if self.goodList.count > 0 {
-                self.goodList[0].isSelect = true
-                self.selectGood = self.goodList[0]
-                self.mostCell.config(model: self.goodList[0])
-        
-                let row = ceil(Double(self.goodList.count - 1) / 3.0)
-                let height = UX.goodsBgHeight + (row * UX.collectionRowHeight)
-                self.goodsBgView.gradientBackgroundColor(color1: .hexStringColor(hexString: "#FFF8F2"), color2: .hexStringColor(hexString: "#FFFFFF"), width: QSLConst.qsl_kScreenW, height: height, direction: .vertical)
-                
-                self.goodsBgView.snp.updateConstraints { make in
-                    make.height.equalTo(height)
-                }
-                
-                self.scrollView.snp.makeConstraints { make in
-                    make.bottom.equalTo(self.mainView)
-                }
-                
-                self.checkRenewalOfProducts()
-            }
-            self.goodsCollectionView.reloadData()
-        } fail: { code, error in
-            self.view.toast(text: "加载商品列表失败")
-        }
-    }
-    
-    ///检查有没有续订的产品
-    func checkRenewalOfProducts() {
-        let hasSubscribable = self.goodList.contains(where: { model in
-            return model.subscribable == 1
-        })
-    }
-    
-    // 计算购买人数
-    func calculateBuyCount() {
-        
-        var num = 145883
-        if let localNum = UserDefaults.standard.value(forKey: QSLConfig.user_default_local_buy_count) as? Int {
-            num = localNum
-        }
-        
-        let randomAddCount = Int.random(in: 1...10)
-        num = num + randomAddCount
-        UserDefaults.standard.setValue(num, forKey: QSLConfig.user_default_local_buy_count)
-        self.peopleCountLabel.text("\(num)人 已开通VIP")
-    }
-}
-
-// MARK: - 设置 CollectionView
-extension QSLActivityVipController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
-    
-    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        return goodList.count - 1
-    }
-    
-    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-        
-        let cell = collectionView.dequeueReusableCell(cellType: QSLVipGoodCollectionViewCell.self, cellForRowAt: indexPath)
-        let model = self.goodList[indexPath.row + 1]
-        
-        if self.goodList.count - 1 == 2 {
-            cell.config(model: model, type: .big)
-        } else {
-            cell.config(model: model, type: .small)
-        }
-        
-        return cell
-    }
-    
-    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-        
-        for i in 0..<self.goodList.count {
-            self.goodList[i].isSelect = false
-        }
-        
-        if self.goodList.count > 0 {
-            self.goodList[indexPath.row + 1].isSelect = true
-            self.selectGood = self.goodList[indexPath.row + 1]
-            self.mostCell.config(model: self.goodList[0])
-            //选中立即支付
-            unlockBtnAction()
-        }
-        
-        switch self.selectGood?.level {
-        case 100 :
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
-            break
-        case 700:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
-            break;
-        case 3100:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
-            break;
-        case 9200:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
-            break;
-        case 36600:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
-            break;
-        case 3660000:
-            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
-            break;
-        default:
-            break;
-        }
-        
-        self.goodsCollectionView.reloadData()
-    }
-    
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
-        
-        var width = (QSLConst.qsl_kScreenW - 42.0.rpx) / 3.0
-        
-        if self.goodList.count - 1 == 2 {
-            width = (QSLConst.qsl_kScreenW - 36.0.rpx) / 2.0
-        }
-        
-        return CGSize(width: width, height: 134.0.rpx)
-    }
-    
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
-        return 6.rpx
-    }
-    
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
-        return 3.rpx
-    }
-}
-
-extension QSLActivityVipController {
-    
-    func updateUI() {
-        
-        if QSLBaseManager.shared.isLogin() && QSLBaseManager.shared.isVip() {
-            let model = QSLBaseManager.shared.userModel.memberModel
-            if model.permanent {
-                
-                self.vipTimeLabel.text("您已是尊贵的永久会员")
-                self.bottomContentView.isHidden = true
-            } else {
-                
-                let level = model.memberLevelString()
-                let endTime = model.endTimestampString()
-                self.vipTimeLabel.text("\(level):\(endTime)到期")
-            }
-        } else {
-            
-            self.vipTimeLabel.text("升级VIP会员,解锁全部功能")
-        }
-    }
-    
-    func updateSelectGoodUI() {
-        var priceText = ""
-        var originalPriceText = ""
-        if let selectGood = self.selectGood {
-            if selectGood.amount.truncatingRemainder(dividingBy: 100) == 0 {
-                priceText = "\(Int(selectGood.amount / 100))"
-            } else {
-                priceText = String(format: "%.2lf", selectGood.amount / 100 )
-            }
-            
-            if selectGood.originalAmount.truncatingRemainder(dividingBy: 100) == 0 {
-                originalPriceText = "\(Int(selectGood.originalAmount / 100))"
-            } else {
-                originalPriceText = String(format: "%.2lf", selectGood.originalAmount / 100 )
-            }
-        }
-        self.priceLabel.text(priceText)
-//        self.goodTypeLabel.text("/ \(self.selectGood?.name ?? "")")
-        self.goodOriginalPriceLabel.text("原价\(originalPriceText)")
-        
-        ///底部内容
-        bottomTopLabelInfo(self.selectGood ?? QSLGoodModel())
-    }
-    
-    func initializeView() {
-        
-        self.view.addSubview(vipBg)
-        vipBg.snp.makeConstraints { make in
-            make.left.top.right.equalTo(0)
-        }
-        
-        self.view.addSubview(backButton)
-        backButton.snp.makeConstraints { make in
-            make.size.equalTo(100.rpx)
-            make.height.equalTo(25.rpx)
-            make.left.equalTo(12.rpx)
-            make.top.equalTo(QSLConst.qsl_kStatusBarFrameH)
-        }
-        
-        self.view.addSubview(peopleCountView)
-        peopleCountView.snp.makeConstraints { make in
-            make.size.equalTo(CGSize(width: 212.rpx, height: 24.rpx))
-            make.right.equalTo(-13.rpx)
-            make.centerY.equalTo(backButton.snp.centerY)
-        }
-        
-        peopleCountView.addSubview(peopleAvatarImageView)
-        peopleAvatarImageView.snp.makeConstraints { make in
-            make.size.equalTo(CGSize(width: 72.rpx, height: 16.6.rpx))
-            make.left.equalTo(3.rpx)
-            make.centerY.equalTo(peopleCountView.snp.centerY)
-        }
-        
-        peopleCountView.addSubview(peopleCountLabel)
-        peopleCountLabel.snp.makeConstraints { make in
-            make.left.equalTo(peopleAvatarImageView.snp.right).offset(7.rpx)
-            make.centerY.equalTo(peopleCountView.snp.centerY)
-        }
-        
-        self.view.addSubview(scrollView)
-        scrollView.snp.makeConstraints { make in
-            make.left.right.equalTo(0)
-            make.top.equalTo(QSLConst.qsl_kNavFrameH)
-            make.bottom.equalTo(0)
-        }
-        
-        scrollView.addSubview(cardBannerImageView)
-        cardBannerImageView.snp.makeConstraints { make in
-            make.top.equalTo(0)
-            make.centerX.equalToSuperview()
-            make.width.equalTo(336.rpx)
-            make.height.equalTo(108.rpx)
-        }
-        
-        cardBannerImageView.addSubview(vipTimeLabel)
-        vipTimeLabel.snp.makeConstraints { make in
-            make.left.equalTo(23.rpx)
-            make.bottom.equalTo(-19.rpx)
-        }
-        
-        scrollView.addSubview(funcBannerImageView)
-        funcBannerImageView.snp.makeConstraints { make in
-            make.size.equalTo(CGSize(width: 336.rpx, height: 172.rpx))
-            make.centerX.equalToSuperview()
-            make.top.equalTo(cardBannerImageView.snp.bottom).offset(14.rpx)
-        }
-        
-        scrollView.addSubview(mainView)
-        mainView.snp.makeConstraints { make in
-            make.left.right.equalTo(0)
-            make.top.equalTo(funcBannerImageView.snp.bottom).offset(-24.rpx)
-            make.width.equalTo(QSLConst.qsl_kScreenW)
-            //            make.height.equalTo(800.rpx)
-        }
-        
-        let goodsBgHeight = 54.rpx + UX.mostCellHeight
-        mainView.addSubview(goodsBgView)
-        goodsBgView.snp.makeConstraints { make in
-            make.top.left.right.equalTo(0)
-            make.width.equalTo(QSLConst.qsl_kScreenW)
-            make.height.equalTo(goodsBgHeight)
-        }
-        
-        goodsBgView.addSubview(vipGoodsTitleIcon)
-        vipGoodsTitleIcon.snp.makeConstraints { make in
-            make.size.equalTo(CGSize(width: 117.rpx, height: 26.rpx))
-            make.left.equalTo(20.rpx)
-            make.top.equalTo(16.rpx)
-        }
-        
-        goodsBgView.addSubview(resumeBtn)
-        resumeBtn.snp.makeConstraints { make in
-            make.size.equalTo(CGSize(width: 75.rpx, height: 16.rpx))
-            make.right.equalTo(-18.rpx)
-            make.centerY.equalTo(vipGoodsTitleIcon.snp.centerY)
-        }
-        
-        goodsBgView.addSubview(mostCell)
-        mostCell.snp.makeConstraints { make in
-            make.left.equalTo(12.rpx)
-            make.right.equalTo(-16.rpx)
-            make.height.equalTo(113.rpx)
-            make.top.equalTo(vipGoodsTitleIcon.snp.bottom).offset(10.rpx)
-        }
-        
-        goodsBgView.addSubview(goodsCollectionView)
-        goodsCollectionView.snp.makeConstraints { make in
-            make.left.equalTo(12.rpx)
-            make.right.equalTo(-18.rpx)
-            make.bottom.equalTo(-20.rpx)
-            make.top.equalTo(mostCell.snp.bottom).offset(3.rpx)
-        }
-        
-        scrollView.snp.makeConstraints { make in
-            make.bottom.equalTo(mainView)
-        }
-        
-        self.view.addSubview(bottomContentView)
-        bottomContentView.snp.makeConstraints { make in
-            make.left.equalTo(12.rpx)
-            make.right.equalTo(-12.rpx)
-            make.bottom.equalTo(-QSLConst.qsl_kTabbarBottom)
-            make.height.equalTo(72.rpx)
-        }
-    }
-        
-}

+ 340 - 45
QuickSearchLocation/Classes/Pages/QSLVip/Controller/QSLActivityVipVC.swift

@@ -7,29 +7,144 @@
 
 import UIKit
 import GKCycleScrollView
+import YYText
 
 class QSLActivityVipVC: QSLBaseController {
     var goodList: [QSLGoodModel] = [QSLGoodModel]()
-    var selectGood: QSLGoodModel? {
-        didSet {
-            updateSelectGoodUI()
-        }
-    }
+    var selectGood: QSLGoodModel? 
     var currentCell: QSLVipMostGoodCell?
+    var dismissHandler: ((Bool) -> Void)?
+    var isCancel = false
     
     override func viewDidLoad() {
         super.viewDidLoad()
         
-        //gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1001])
+        gravityInstance?.track(QSLGravityConst.activity_vip_show, properties: [:])
+        
         self.initializeView()
         
         self.requestNetwork()
         
+        self.updateServiceLabelText(showSubscribe: false)
+        
+        
+    }
+    
+    @objc func privacyAction() {
+        
+        let vc = QSLWebViewController()
+        vc.webUrl = QSLConfig.AppPrivacyAgreementLink
+        vc.title = "隐私政策"
+        self.navigationController?.pushViewController(vc, animated: true)
+    }
+    
+    @objc func serviceAction() {
         
+        let vc = QSLWebViewController()
+        vc.webUrl = QSLConfig.AppServiceAgreementLink
+        vc.title = "服务协议"
+        self.navigationController?.pushViewController(vc, animated: true)
+    }
+    
+    @objc func subscibeAction() {
+        
+        let vc = QSLWebViewController()
+        vc.webUrl = QSLConfig.AppSubscibeAgreementLink
+        vc.title = "续订说明"
+        self.navigationController?.pushViewController(vc, animated: true)
     }
     
     @objc func payBtnAction() {
         
+        switch self.selectGood?.level {
+        case 100 :
+            gravityInstance?.track(QSLGravityConst.activity_vip_click, properties: ["package_type": "day"])
+            break
+        case 700:
+            gravityInstance?.track(QSLGravityConst.activity_vip_click, properties: ["package_type": "weekly"])
+            break;
+        case 3100:
+            gravityInstance?.track(QSLGravityConst.activity_vip_click, properties: ["package_type": "monthly"])
+            break;
+        case 9200:
+            gravityInstance?.track(QSLGravityConst.activity_vip_click, properties: ["package_type": "quarterly"])
+            break;
+        case 36600:
+            gravityInstance?.track(QSLGravityConst.activity_vip_click, properties: ["package_type": "yearly"])
+            break;
+        case 3660000:
+            gravityInstance?.track(QSLGravityConst.activity_vip_click, properties: ["package_type": "lifetime"])
+            break;
+        default:
+            break;
+        }
+        
+        
+        let memberModel = QSLBaseManager.shared.userModel.memberModel
+        
+        if let subscriptionExpired = memberModel.subscriptionExpired, !subscriptionExpired {
+            self.view.toast(text: "你已经订阅了")
+            return
+        }
+        
+        if goodList.count > 0, let selectGood = self.selectGood {
+            QSLLoading.show()
+            QSLVipManager.shared.startPay(goods: selectGood) { [self] status, outTradeNo in
+                QSLVipManager.shared.isPaying = false
+                if status == .success {
+                    QSLLoading.success(text: "支付成功")
+                    
+                    //弹出是否好评的弹窗
+                    QSLGuideusersToCommentManager.commentShare.manageWhetherTriggerPopUpWindow(QSLGuideusersToCommentType.member)
+                    
+                    // 引力传递支付事件
+                    if let selectGood = self.selectGood {
+                        gravityInstance?.trackPayEvent(withAmount: Int32(selectGood.amount), withPayType: "CNY", withOrderId: outTradeNo, withPayReason: selectGood.name, withPayMethod: "apple")
+                    }
+                    
+                    #if DEBUG
+                    #else
+                    //苹果广告奇异果传递支付事件
+                    QSWikiHandle.shared.addEventResultAttribution(eventDict: ["event_name": "pay", "event_val": selectGood.amount])
+                    #endif
+                    
+                    gravityInstance?.track(QSLGravityConst.activity_vip_result, properties: ["purchase_result": "success","pay_amount":Int32(selectGood.amount)])
+
+                    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
+                        self.navigationController?.popViewController(animated: true)
+                        NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil)
+                    }
+                    
+                } else if status == .cancel {
+                    gravityInstance?.track(QSLGravityConst.activity_vip_result, properties: ["purchase_result": "cancel","pay_amount":Int32(selectGood.amount)])
+                    QSLLoading.error(text: "支付取消")
+//                    payFailAlertTip()
+                    self.isCancel = true
+                } else if status == .fail {
+                    gravityInstance?.track(QSLGravityConst.activity_vip_result, properties: ["purchase_result": "fail","pay_amount":Int32(selectGood.amount)])
+                    gravityInstance?.track(QSLGravityConst.vip_fail)
+                    QSLLoading.error(text: "支付失败")
+//                    payFailAlertTip()
+                    self.isCancel = true
+                } else if status == .searchFail {
+                    QSLLoading.error(text: "查询订单失败,请稍后重试")
+                }
+            }
+        }
+    }
+    
+    func payFailAlertTip() {
+//        gravityInstance?.track(QSLGravityConst.activity_vip_retention_show, properties: ["trigger_type":"cancel_payment"])
+        
+        if let currentWindow = UIApplication.keyWindow {
+            QSLRetainPopUpAlertView.alert(view: currentWindow, isOneBtn: true, oneBtnText: "继续支付", oneBtnClosure:  { [weak self] in
+                self?.payBtnAction()
+//                gravityInstance?.track(QSLGravityConst.activity_vip_retention_click, properties: ["button":"continue_payment"])
+            },secondBtnClosure: {
+//                gravityInstance?.track(QSLGravityConst.activity_vip_retention_click, properties: ["button":"close"])
+            })
+            
+        }
     }
     
     @objc func requestNetwork() {
@@ -41,9 +156,6 @@ class QSLActivityVipVC: QSLBaseController {
             // 后续逻辑不变...
             let list = response.mapArray(QSLGoodModel.self, modelKey: "data>list")
             self.goodList = list
-            //TODO: 测试
-            self.goodList += list
-            self.goodList += list
             
             if self.goodList.count > 0 {
                 self.goodList[0].isSelect = true
@@ -60,10 +172,21 @@ class QSLActivityVipVC: QSLBaseController {
                 }
                 
                 self.scrollView.snp.makeConstraints { make in
-                    make.bottom.equalTo(self.vipItemView).offset(100)
+                    make.bottom.equalTo(self.vipItemView).offset(0)
                 }
-//                
-//                self.checkRenewalOfProducts()
+                
+                QSLCountdownManager.shared.selectGood = self.goodList[0]
+
+                QSLCountdownManager.shared.updateHandler1 = { [weak self] timeString in
+                    self?.countdownLabel.updateCountdownText(timeString)
+                }
+                QSLCountdownManager.shared.startCountdown()
+
+                NotificationCenter.default.post(
+                    name: Notification.Name("QSLHomeUpdateCouponViewNoti"),
+                    object: nil,
+                    userInfo: ["showCoupon": true]
+                )
             }
             
             
@@ -73,8 +196,128 @@ class QSLActivityVipVC: QSLBaseController {
         }
     }
     
+    override func backBtnAction() {
+        self.dismissHandler?(self.isCancel)
+        super.backBtnAction()
+    }
+
+    // 恢复按钮点击
     @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: "没有可供恢复的订阅")
+            }
+        }
+    }
+    
+    // 更新服务条款文本内容
+    func updateServiceLabelText(showSubscribe: Bool) {
+        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)
+        
+        // 根据条件决定是否添加《续订说明》相关内容
+        if showSubscribe {
+            attr.append(blankAttr)
+            
+            let andAttr = NSMutableAttributedString(string: "和")
+            andAttr.font(10)
+            andAttr.color(.hexStringColor(hexString: "#A7A7A7"))
+            attr.append(andAttr)
+            
+            attr.append(blankAttr)
+            
+            let subcribeHL = YYTextHighlight()
+            let subcribeStr = "《续订说明》"
+            let subcribeText = NSMutableAttributedString(string: subcribeStr)
+            subcribeText.font(10)
+            subcribeText.color(.hexStringColor(hexString: "#E7B983"))
+            subcribeText.yy_setTextHighlight(subcribeHL, range: NSRange(location: 0, length: subcribeStr.count))
+            subcribeHL.tapAction = { [weak self] containerView, text, range, rect in
+                self?.subscibeAction()
+            }
+            attr.append(subcribeText)
+        }
+        
+        serviceLabel.attributedText = attr
+        serviceLabel.textAlignment = .center
     }
 
     lazy var scrollView: UIScrollView = {
@@ -90,7 +333,7 @@ class QSLActivityVipVC: QSLBaseController {
     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.white
+        view.backgroundColor = UIColor.clear
         return view
     }()
     
@@ -113,7 +356,6 @@ class QSLActivityVipVC: QSLBaseController {
         return button
     }()
     
-    
     lazy var backButton: UIButton = {
         let button = UIButton()
         button.image(UIImage(named: "vip_pay_failure_close"))
@@ -240,14 +482,45 @@ class QSLActivityVipVC: QSLBaseController {
         return collectionView
     }()
     
+    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
+    }()
+    
 }
 
 extension QSLActivityVipVC{
     
-    func updateSelectGoodUI(){
-        
-    }
-    
     func initializeView() {
         
         self.view.addSubview(scrollView)
@@ -258,7 +531,7 @@ extension QSLActivityVipVC{
         self.view.addSubview(vipNaviView)
         vipNaviView.snp.makeConstraints { make in
             make.top.left.right.equalTo(0)
-            make.top.equalTo(QSLConst.qsl_kNavFrameH)
+            make.height.equalTo(QSLConst.qsl_kNavFrameH)
         }
         
         vipNaviView.addSubview(backButton)
@@ -359,6 +632,41 @@ extension QSLActivityVipVC{
             make.left.right.top.bottom.equalTo(0)
         }
         
+        self.view.addSubview(bottomView)
+        bottomView.snp.makeConstraints { make in
+            make.right.equalTo(-12.rpx)
+            make.left.equalTo(12.rpx)
+            make.height.equalTo(100.rpx)
+            make.bottom.equalTo(-QSLConst.qsl_kTabbarBottom/2)
+        }
+        
+        bottomView.addSubview(countdownView)
+        countdownView.snp.makeConstraints { make in
+            make.right.left.top.equalTo(0)
+            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)
+        }
+        
+        bottomView.addSubview(unlockBtn)
+        unlockBtn.snp.makeConstraints { make in
+            make.right.left.equalTo(0)
+            make.height.equalTo(50.rpx)
+            make.top.equalTo(25.rpx)
+        }
+        
+        bottomView.addSubview(serviceLabel)
+        serviceLabel.snp.makeConstraints { make in
+            make.right.left.equalTo(0)
+            make.height.equalTo(17.rpx)
+            make.bottom.equalTo(0)
+        }
+        
     }
 }
 
@@ -411,38 +719,25 @@ extension QSLActivityVipVC: UICollectionViewDelegate, UICollectionViewDataSource
     }
     
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        
+        var list = [QSLGoodModel]()
         for index in 0..<self.goodList.count{
             var submodel = self.goodList[index]
-            submodel.isSelect = false
+            if(indexPath.row == index){
+                submodel.isSelect = true
+                self.selectGood = submodel
+            }else{
+                submodel.isSelect = false
+            }
+            
+            list.append(submodel)
         }
         
-        var model = self.goodList[indexPath.row]
-        model.isSelect = !model.isSelect
-       
-//        switch self.selectGood?.level {
-//        case 100 :
-//            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
-//            break
-//        case 700:
-//            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
-//            break;
-//        case 3100:
-//            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
-//            break;
-//        case 9200:
-//            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
-//            break;
-//        case 36600:
-//            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
-//            break;
-//        case 3660000:
-//            gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
-//            break;
-//        default:
-//            break;
-//        }
+        self.goodList = list
         
         self.goodsCollectionView.reloadData()
+        
+        self.payBtnAction()
     }
     
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

+ 28 - 3
QuickSearchLocation/Classes/Pages/QSLVip/Controller/QSLVipController.swift

@@ -30,7 +30,8 @@ class QSLVipController: QSLBaseController {
     }
     
     var type: QSLVipJumpType?
-    
+    var isCancel = false
+    var finishHandler: ((Bool) -> Void)?
     var goodList: [QSLGoodModel] = [QSLGoodModel]()
     
     ///是否是订阅的产品
@@ -449,6 +450,10 @@ class QSLVipController: QSLBaseController {
         label.centerLineText(lineValue: 1, underlineColor: .hexStringColor(hexString: "#FFF8EF"))
         return label
     }()
+    
+    deinit {
+        self.finishHandler?(self.isCancel)
+    }
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -460,19 +465,24 @@ class QSLVipController: QSLBaseController {
         requestItemList()
         
         calculateBuyCount()
-        
+
         if let type = self.type {
             switch type {
             case .homeRoad:
                 gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1001])
+                gravityInstance?.track(QSLGravityConst.new_vip_show, properties: ["is_member":QSLBaseManager.shared.isVip(),"entry_point":"track"])
             case .add:
                 gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1002])
+                gravityInstance?.track(QSLGravityConst.new_vip_show, properties: ["is_member":QSLBaseManager.shared.isVip(),"entry_point":"find_friend"])
             case .friendRoad:
                 gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1003])
+                gravityInstance?.track(QSLGravityConst.new_vip_show, properties: ["is_member":QSLBaseManager.shared.isVip(),"entry_point":"find_track"])
             case .contact:
                 gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1004])
+                gravityInstance?.track(QSLGravityConst.new_vip_show, properties: ["is_member":QSLBaseManager.shared.isVip(),"entry_point":"contact"])
             case .mine:
                 gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1006])
+                gravityInstance?.track(QSLGravityConst.new_vip_show, properties: ["is_member":QSLBaseManager.shared.isVip(),"entry_point":"open_now"])
             case .guideComments:
                 print("")
             }
@@ -564,26 +574,31 @@ extension QSLVipController {
         switch self.selectGood?.level {
         case 100 :
             gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1006])
+            gravityInstance?.track(QSLGravityConst.new_vip_click, properties: ["is_member":QSLBaseManager.shared.isVip(),"package_type": "day"])
             break
         case 700:
             gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1005])
+            gravityInstance?.track(QSLGravityConst.new_vip_click, properties: ["is_member":QSLBaseManager.shared.isVip(),"package_type": "weekly"])
             break;
         case 3100:
             gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1004])
+            gravityInstance?.track(QSLGravityConst.new_vip_click, properties: ["is_member":QSLBaseManager.shared.isVip(),"package_type": "monthly"])
             break;
         case 9200:
             gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1003])
+            gravityInstance?.track(QSLGravityConst.new_vip_click, properties: ["is_member":QSLBaseManager.shared.isVip(),"package_type": "quarterly"])
             break;
         case 36600:
             gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1002])
+            gravityInstance?.track(QSLGravityConst.new_vip_click, properties: ["is_member":QSLBaseManager.shared.isVip(),"package_type": "yearly"])
             break;
         case 3660000:
             gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1001])
+            gravityInstance?.track(QSLGravityConst.new_vip_click, properties: ["is_member":QSLBaseManager.shared.isVip(),"package_type": "lifetime"])
             break;
         default:
             break;
         }
-        
 //        if !selectBtn.isSelected {
 //            self.view.toast(text: "请先同意《隐私权政策》和《用户协议》")
 //            return
@@ -603,6 +618,7 @@ extension QSLVipController {
                 if status == .success {
                     QSLLoading.success(text: "支付成功")
                     
+                    gravityInstance?.track(QSLGravityConst.new_vip_result, properties: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "success","pay_amount":Int32(selectGood.amount)])
                     //支付成功埋点
                     gravityInstance?.track(QSLGravityConst.vip_submit_success, properties: ["id": 01001])
                     
@@ -666,12 +682,16 @@ extension QSLVipController {
                     }
                     
                 } else if status == .cancel {
+                    gravityInstance?.track(QSLGravityConst.new_vip_result, properties: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "cancel","pay_amount":Int32(selectGood.amount)])
                     QSLLoading.error(text: "支付取消")
                     payFailAlertTip()
+                    gravityInstance?.track(QSLGravityConst.new_vip_retention_show, properties: ["trigger_type":"cancel_payment"])
                 } else if status == .fail {
+                    gravityInstance?.track(QSLGravityConst.new_vip_result, properties: ["is_member":QSLBaseManager.shared.isVip(),"purchase_result": "fail","pay_amount":Int32(selectGood.amount)])
                     gravityInstance?.track(QSLGravityConst.vip_fail)
                     QSLLoading.error(text: "支付失败")
                     payFailAlertTip()
+                    gravityInstance?.track(QSLGravityConst.new_vip_retention_show, properties: ["trigger_type":"fail"])
                 } else if status == .searchFail {
                     QSLLoading.error(text: "查询订单失败,请稍后重试")
                 }
@@ -681,8 +701,12 @@ extension QSLVipController {
     
     func payFailAlertTip() {
         if let currentWindow = UIApplication.keyWindow {
+            self.isCancel = true
             QSLRetainPopUpAlertView.alert(view: currentWindow, isOneBtn: true, oneBtnText: "继续支付", oneBtnClosure:  { [weak self] in
                 self?.unlockBtnAction()
+                gravityInstance?.track(QSLGravityConst.new_vip_retention_click, properties: ["button":"continue_payment"])
+            },secondBtnClosure: {
+                gravityInstance?.track(QSLGravityConst.new_vip_retention_click, properties: ["button":"close"])
             })
         }
     }
@@ -750,6 +774,7 @@ extension QSLVipController {
             if self.goodList.count > 0 {
                 self.goodList[0].isSelect = true
                 self.selectGood = self.goodList[0]
+                QSLCountdownManager.shared.selectGood = self.goodList[0]
                 self.mostCell.config(model: self.goodList[0])
         
                 let row = ceil(Double(self.goodList.count - 1) / 3.0)

+ 3 - 4
QuickSearchLocation/Classes/Pages/QSLVip/View/QSLVipAlertView.swift

@@ -227,15 +227,14 @@ class QSLVipAlertView: UIView {
     
     // 关闭按钮点击事件
     @objc func closeBtnAction() {
-        if let closeBtnClosure = self.closeBtnClosure {
-            closeBtnClosure()
-        }
         removeView()
     }
     
     // 移除
     @objc func removeView() {
-        
+        if let closeBtnClosure = self.closeBtnClosure {
+            closeBtnClosure()
+        }
         UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.95, initialSpringVelocity: 0.05) { [weak self] in
             self?.backgroundColor = UIColor.init(white: 0, alpha: 0)
             self?.contentView.isHidden = true

+ 25 - 0
QuickSearchLocation/Macro/QSLGravityConst.swift

@@ -289,3 +289,28 @@ extension QSLGravityConst {
     // 退出账号-确认弹窗-取消
     static let mine_logout_cancel = "z8004006"
 }
+
+extension QSLGravityConst {
+    
+    // 引导页曝光
+    static let activity_vip_show = "homepage_exposure_guide"
+    // 引导页点击
+    static let activity_vip_click = "homepage_click_guide_package"
+    // 引导页购买
+    static let activity_vip_result = "homepage_purchase_result_guide"
+    // 首页优惠券入口
+    static let home_coupon_click = "homepage_click_guide_coupon"
+    
+    // 会员页曝光
+    static let new_vip_show = "vip_center_exposure"
+    // 会员页点击
+    static let new_vip_click = "vip_center_click_package"
+    // 会员页购买
+    static let new_vip_result = "vip_center_purchase_result"
+    //会员中心挽留弹窗曝光
+    static let new_vip_retention_show = "vip_center_exposure_retention_popup"
+    //会员中心挽留弹窗点击
+    static let new_vip_retention_click = "vip_center_click_retention_popup"
+    
+   
+}

+ 21 - 0
QuickSearchLocation/Resources/Assets.xcassets/Home/home_activity_bg.imageset/Contents.json

@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "顶部卡片.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
QuickSearchLocation/Resources/Assets.xcassets/Home/home_activity_bg.imageset/顶部卡片.png