- 苹果支付、分享

This commit is contained in:
linshujie 2026-07-06 15:23:28 +08:00
parent 4269af8611
commit 6163dd1ca8
46 changed files with 1376 additions and 271 deletions

View File

@ -225,6 +225,8 @@
30C4C01D2FDBF557009215C1 /* RemoveMemberViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */; }; 30C4C01D2FDBF557009215C1 /* RemoveMemberViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */; };
30C4C0202FDC0EC5009215C1 /* GroupInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */; }; 30C4C0202FDC0EC5009215C1 /* GroupInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */; };
30C4C0222FDC0ED3009215C1 /* GroupInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */; }; 30C4C0222FDC0ED3009215C1 /* GroupInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */; };
30C6666C2FFB7C4000E62B25 /* IAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C6666B2FFB7C4000E62B25 /* IAPManager.swift */; };
30C6679E2FFB7FEF00E62B25 /* SharePopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C6679C2FFB7FEF00E62B25 /* SharePopView.swift */; };
30CCDE4E2FE26CEA00F5214A /* PayResultPopVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE4D2FE26CEA00F5214A /* PayResultPopVC.swift */; }; 30CCDE4E2FE26CEA00F5214A /* PayResultPopVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE4D2FE26CEA00F5214A /* PayResultPopVC.swift */; };
30CCDE512FE2785D00F5214A /* SignInVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE502FE2785D00F5214A /* SignInVC.swift */; }; 30CCDE512FE2785D00F5214A /* SignInVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE502FE2785D00F5214A /* SignInVC.swift */; };
30CCDE532FE2786600F5214A /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE522FE2786600F5214A /* SignInView.swift */; }; 30CCDE532FE2786600F5214A /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCDE522FE2786600F5214A /* SignInView.swift */; };
@ -524,6 +526,8 @@
30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveMemberViewModel.swift; sourceTree = "<group>"; }; 30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveMemberViewModel.swift; sourceTree = "<group>"; };
30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoView.swift; sourceTree = "<group>"; }; 30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoView.swift; sourceTree = "<group>"; };
30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoVC.swift; sourceTree = "<group>"; }; 30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoVC.swift; sourceTree = "<group>"; };
30C6666B2FFB7C4000E62B25 /* IAPManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAPManager.swift; sourceTree = "<group>"; };
30C6679C2FFB7FEF00E62B25 /* SharePopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePopView.swift; sourceTree = "<group>"; };
30CCDE4D2FE26CEA00F5214A /* PayResultPopVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayResultPopVC.swift; sourceTree = "<group>"; }; 30CCDE4D2FE26CEA00F5214A /* PayResultPopVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayResultPopVC.swift; sourceTree = "<group>"; };
30CCDE502FE2785D00F5214A /* SignInVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInVC.swift; sourceTree = "<group>"; }; 30CCDE502FE2785D00F5214A /* SignInVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInVC.swift; sourceTree = "<group>"; };
30CCDE522FE2786600F5214A /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; }; 30CCDE522FE2786600F5214A /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; };
@ -910,6 +914,7 @@
305A752A2FCA8C7000227D26 /* Theme */, 305A752A2FCA8C7000227D26 /* Theme */,
305A752C2FCA8C7000227D26 /* URL */, 305A752C2FCA8C7000227D26 /* URL */,
30D87CD52FDF9F1900E958FD /* MQTT */, 30D87CD52FDF9F1900E958FD /* MQTT */,
30C6666A2FFB7C3000E62B25 /* IAPManager */,
); );
path = Manager; path = Manager;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1072,6 +1077,7 @@
30DC18592FD11E7A0041DCD1 /* Web */, 30DC18592FD11E7A0041DCD1 /* Web */,
30ACD54A2FF6332200174861 /* PopupWindow */, 30ACD54A2FF6332200174861 /* PopupWindow */,
30EFF3AD2FD7FF1400EB35D4 /* TextInput */, 30EFF3AD2FD7FF1400EB35D4 /* TextInput */,
30C6679D2FFB7FEF00E62B25 /* Share */,
); );
path = Section; path = Section;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1436,6 +1442,22 @@
path = GroupInfo; path = GroupInfo;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
30C6666A2FFB7C3000E62B25 /* IAPManager */ = {
isa = PBXGroup;
children = (
30C6666B2FFB7C4000E62B25 /* IAPManager.swift */,
);
path = IAPManager;
sourceTree = "<group>";
};
30C6679D2FFB7FEF00E62B25 /* Share */ = {
isa = PBXGroup;
children = (
30C6679C2FFB7FEF00E62B25 /* SharePopView.swift */,
);
path = Share;
sourceTree = "<group>";
};
30CCDE4F2FE2782700F5214A /* SignIn */ = { 30CCDE4F2FE2782700F5214A /* SignIn */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1840,6 +1862,7 @@
305A769C2FCA8C7000227D26 /* PopupViewController.swift in Sources */, 305A769C2FCA8C7000227D26 /* PopupViewController.swift in Sources */,
305A769D2FCA8C7000227D26 /* PopupViewController+Extension.swift in Sources */, 305A769D2FCA8C7000227D26 /* PopupViewController+Extension.swift in Sources */,
30EFF3C42FDA431D00EB35D4 /* ChangePhoneVC.swift in Sources */, 30EFF3C42FDA431D00EB35D4 /* ChangePhoneVC.swift in Sources */,
30C6666C2FFB7C4000E62B25 /* IAPManager.swift in Sources */,
305A769E2FCA8C7000227D26 /* TextArrowCell.swift in Sources */, 305A769E2FCA8C7000227D26 /* TextArrowCell.swift in Sources */,
305A769F2FCA8C7000227D26 /* TextContentArrowCell.swift in Sources */, 305A769F2FCA8C7000227D26 /* TextContentArrowCell.swift in Sources */,
30D87D042FE1336300E958FD /* NavigationVC.swift in Sources */, 30D87D042FE1336300E958FD /* NavigationVC.swift in Sources */,
@ -1925,6 +1948,7 @@
305A76CA2FCA8C7000227D26 /* AppSettings.swift in Sources */, 305A76CA2FCA8C7000227D26 /* AppSettings.swift in Sources */,
305A76CB2FCA8C7000227D26 /* Authorize.swift in Sources */, 305A76CB2FCA8C7000227D26 /* Authorize.swift in Sources */,
30CCDE552FE2903100F5214A /* SignInModel.swift in Sources */, 30CCDE552FE2903100F5214A /* SignInModel.swift in Sources */,
30C6679E2FFB7FEF00E62B25 /* SharePopView.swift in Sources */,
305A76CC2FCA8C7000227D26 /* FileTools.swift in Sources */, 305A76CC2FCA8C7000227D26 /* FileTools.swift in Sources */,
305A76CD2FCA8C7000227D26 /* Permission.swift in Sources */, 305A76CD2FCA8C7000227D26 /* Permission.swift in Sources */,
30D74AB02FEA13ED0050EB2C /* ScheduleVC.swift in Sources */, 30D74AB02FEA13ED0050EB2C /* ScheduleVC.swift in Sources */,

View File

@ -15,7 +15,7 @@ enum OrderAPI {
/// - Parameters: /// - Parameters:
/// - type: member /// - type: member
case rechargeInfo(type: String) case rechargeInfo(type: String)
/// ///
/// - Parameters: /// - Parameters:
/// - goodsId: ID /// - goodsId: ID
@ -23,19 +23,24 @@ enum OrderAPI {
/// - source: center /// - source: center
/// - extra: /// - extra:
case orderPayParams(goodsId: String, payType: String, source: String, extra: [String:Any]=[:]) case orderPayParams(goodsId: String, payType: String, source: String, extra: [String:Any]=[:])
/// Apple
case appleNotify(receipt: String, productId: String, transactionId: String)
} }
extension OrderAPI: MultiTargetProtocol { extension OrderAPI: MultiTargetProtocol {
var path: String { var path: String {
switch self { switch self {
case .rechargeInfo: case .rechargeInfo:
return "api/order/goods" return "api/order/goods"
case .orderPayParams: case .orderPayParams:
return "api/order" return "api/order"
case .appleNotify:
return "api/order/notify/apple"
} }
} }
var method: Moya.Method { var method: Moya.Method {
switch self { switch self {
case .rechargeInfo: case .rechargeInfo:
@ -44,14 +49,14 @@ extension OrderAPI: MultiTargetProtocol {
return .post return .post
} }
} }
var task: Moya.Task { var task: Moya.Task {
switch self { switch self {
case let .rechargeInfo(type): case let .rechargeInfo(type):
var params = Parameters() var params = Parameters()
params["type"] = type params["type"] = type
return .requestParameters(parameters: params, encoding: URLEncoding()) return .requestParameters(parameters: params, encoding: URLEncoding())
case let .orderPayParams(goodsId, payType, source, extra): case let .orderPayParams(goodsId, payType, source, extra):
var params = Parameters() var params = Parameters()
params["goods_id"] = goodsId params["goods_id"] = goodsId
@ -60,6 +65,13 @@ extension OrderAPI: MultiTargetProtocol {
params["pay_source"] = "app" params["pay_source"] = "app"
params["extra"] = extra params["extra"] = extra
return .requestParameters(parameters: params, encoding: JSONEncoding()) return .requestParameters(parameters: params, encoding: JSONEncoding())
case let .appleNotify(receipt, productId, transactionId):
var params = Parameters()
params["receipt_data"] = receipt
params["product_id"] = productId
params["transaction_id"] = transactionId
return .requestParameters(parameters: params, encoding: JSONEncoding())
} }
} }
} }

View File

@ -18,25 +18,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
setupLocation() setupLocation()
ApiManager.shared.setup() ApiManager.shared.setup()
AppRouter.shared.setup() AppRouter.shared.setup()
return true return true
} }
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch. // Override point for customization after application launch.
IAPManager.shared.startObserving()
IQKeyboardManager.shared.isEnabled = true IQKeyboardManager.shared.isEnabled = true
IQKeyboardManager.shared.enableAutoToolbar = false IQKeyboardManager.shared.enableAutoToolbar = false
IQKeyboardManager.shared.resignOnTouchOutside = true IQKeyboardManager.shared.resignOnTouchOutside = true
// //
WXApi.registerApp(AppSettings.kAppsWXApiAppId, universalLink: AppSettings.kAppsUniversalLink) WXApi.registerApp(AppSettings.kAppsWXApiAppId, universalLink: AppSettings.kAppsUniversalLink)
// //
#if !targetEnvironment(simulator) #if !targetEnvironment(simulator)
AMapServices.shared().apiKey = AppSettings.kAppsAMapAppId AMapServices.shared().apiKey = AppSettings.kAppsAMapAppId
@ -53,19 +55,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
print("preGetToken: \(preDic)") print("preGetToken: \(preDic)")
} }
#endif #endif
// OpenIM // OpenIM
GroupIMService.shared.initSDK() GroupIMService.shared.initSDK()
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = .white window?.backgroundColor = .white
window?.overrideUserInterfaceStyle = .light window?.overrideUserInterfaceStyle = .light
window?.rootViewController = LaunchViewController() window?.rootViewController = LaunchViewController()
window?.makeKeyAndVisible() window?.makeKeyAndVisible()
return true return true
} }
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("📱 application open url: \(url.absoluteString) host: \(url.host ?? "")") print("📱 application open url: \(url.absoluteString) host: \(url.host ?? "")")
// host SDK // host SDK
@ -75,17 +77,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
guard let result = resultDic, guard let result = resultDic,
let resultStatus = result["resultStatus"] as? String, let resultStatus = result["resultStatus"] as? String,
resultStatus != "6001" else { return } resultStatus != "6001" else { return }
} }
return true return true
} }
else if url.host == "pay" { else if url.host == "pay" {
return WXApi.handleOpen(url, delegate: self) return WXApi.handleOpen(url, delegate: self)
} }
return true return true
} }
func setupLocation() { func setupLocation() {
let manager = AuthorizeManager.manager(type: .locationWhenInUse) let manager = AuthorizeManager.manager(type: .locationWhenInUse)
if manager?.authorizeStatus() != .authorized { if manager?.authorizeStatus() != .authorized {
@ -112,7 +114,7 @@ extension AppDelegate {
static var keyWindow: UIWindow? { static var keyWindow: UIWindow? {
UIApplication.shared.windows.filter { $0.isKeyWindow }.first UIApplication.shared.windows.filter { $0.isKeyWindow }.first
} }
static var rootViewController: UIViewController? { static var rootViewController: UIViewController? {
getRootViewController(keyWindow?.rootViewController) getRootViewController(keyWindow?.rootViewController)
} }
@ -141,7 +143,7 @@ extension AppDelegate: WXApiDelegate {
func onReq(_ req: BaseReq) { func onReq(_ req: BaseReq) {
print(req) print(req)
} }
func onResp(_ resp: BaseResp) { func onResp(_ resp: BaseResp) {
if resp.isKind(of: PayResp.self), if resp.isKind(of: PayResp.self),
let response: PayResp = resp as? PayResp { let response: PayResp = resp as? PayResp {
@ -150,7 +152,7 @@ extension AppDelegate: WXApiDelegate {
if response.errCode == 0 { // if response.errCode == 0 { //
DispatchQueue.global().async { DispatchQueue.global().async {
DispatchQueue.main.async { DispatchQueue.main.async {
} }
} }
} }

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2432@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2432@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "normal_on_bg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "normal_on_bg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "recommend_bg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "recommend_bg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2429@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2429@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "recommend_on_bg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "recommend_on_bg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_2428@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_2428@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "friend@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "friend@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "moments@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "moments@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "url@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "url@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "apple@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "apple@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -116,6 +116,8 @@ struct PopupWindowTimeModel: Mappable {
var pay_pop_time: Int = 0 var pay_pop_time: Int = 0
/// vip /// vip
var min_discount: Double = 0 var min_discount: Double = 0
/// 1 2
var pay_pop_type: Int = 0
init?(map: Map) { init?(map: Map) {
@ -127,5 +129,6 @@ struct PopupWindowTimeModel: Mappable {
signup_tip <- map["signup_tip"] signup_tip <- map["signup_tip"]
pay_pop_time <- map["pay_pop_time"] pay_pop_time <- map["pay_pop_time"]
min_discount <- map["min_discount"] min_discount <- map["min_discount"]
pay_pop_type <- map["pay_pop_type"]
} }
} }

View File

@ -0,0 +1,81 @@
//
// IAPManager.swift
// QuickLocation
//
// Created by on 2026/7/3.
//
import StoreKit
import RxSwift
class IAPManager: NSObject {
static let shared = IAPManager()
private let disposeBag = DisposeBag()
private var currentGoodsId: String = ""
private var purchaseCompletion: ((Bool, String?) -> Void)?
func startObserving() {
SKPaymentQueue.default().add(self)
}
func purchase(goodsId: String, completion: @escaping (Bool, String?) -> Void) {
currentGoodsId = goodsId
purchaseCompletion = completion
let request = SKProductsRequest(productIdentifiers: [goodsId])
request.delegate = self
request.start()
}
}
// MARK: - SKProductsRequestDelegate
extension IAPManager: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
guard let product = response.products.first else {
purchaseCompletion?(false, "商品不可用")
return
}
SKPaymentQueue.default().add(SKPayment(product: product))
}
}
// MARK: - SKPaymentTransactionObserver
extension IAPManager: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for tx in transactions {
switch tx.transactionState {
case .purchased:
verifyReceipt(transaction: tx)
case .failed:
queue.finishTransaction(tx)
let msg = tx.error?.localizedDescription ?? "支付失败"
purchaseCompletion?(false, msg)
default:
break
}
}
}
private func verifyReceipt(transaction: SKPaymentTransaction) {
guard let receiptURL = Bundle.main.appStoreReceiptURL,
let receiptData = try? Data(contentsOf: receiptURL) else {
SKPaymentQueue.default().finishTransaction(transaction)
purchaseCompletion?(false, "获取凭证失败")
return
}
let receipt = receiptData.base64EncodedString()
let txId = transaction.transactionIdentifier ?? ""
OrderService.appleNotify(
receipt: receipt,
productId: currentGoodsId,
transactionId: txId
).subscribe(onNext: { [weak self] _ in
SKPaymentQueue.default().finishTransaction(transaction)
self?.purchaseCompletion?(true, nil)
}, onError: { [weak self] error in
SKPaymentQueue.default().finishTransaction(transaction)
self?.purchaseCompletion?(false, error.localizedDescription)
}).disposed(by: disposeBag)
}
}

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import ObjectMapper import ObjectMapper
import RxSwift import RxSwift
import RxCocoa
class InviteJoinVC: BaseViewController { class InviteJoinVC: BaseViewController {
@ -25,8 +26,13 @@ class InviteJoinVC: BaseViewController {
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
guard let model = groupInModel else { return } //
rootView.shareAppBtn.rx.tap.subscribe(onNext: { _ in
let url = "https://smartdrive.zuom8.cn/jisu/share/user/"
SharePopView.show(shareURL: url)
}).disposed(by: disposeBag)
guard let model = groupInModel else { return }
rootView.groupIcon.image = model.groupIcon rootView.groupIcon.image = model.groupIcon
rootView.groupNameLab.text = model.name rootView.groupNameLab.text = model.name
requestOperateGroup(key: model.group_key) requestOperateGroup(key: model.group_key)

View File

@ -83,7 +83,7 @@ class HomeViewController: BaseViewController {
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
} }
// MARK: - MQTT // MARK: - MQTT
@ -246,6 +246,13 @@ class HomeViewController: BaseViewController {
guard let self = self else { return } guard let self = self else { return }
self.requestSendEmote(emoteIdx: emoteIdx) self.requestSendEmote(emoteIdx: emoteIdx)
} }
//
rootView.interactionView.onShare = { [weak self] in
guard let self = self, let member = self.rootView.interactionView.currentMember else { return }
let url = "https://smartdrive.zuom8.cn/jisu/share/user/\(member.id)"
SharePopView.show(shareURL: url)
}
} }
private func bindViewModel() { private func bindViewModel() {

View File

@ -14,17 +14,17 @@ import Lottie
class InteractionView: UIView { class InteractionView: UIView {
var disposeBag = DisposeBag() var disposeBag = DisposeBag()
private static let emojiCols = 4 private static let emojiCols = 4
private static let emojiRows = 3 private static let emojiRows = 3
private static let emojiPerPage = emojiCols * emojiRows private static let emojiPerPage = emojiCols * emojiRows
func configure(member: CircleMember) { func configure(member: CircleMember) {
self.currentMember = member self.currentMember = member
avaterImgView.image = UIImage(named: "UserIcon/\(member.avatar)") avaterImgView.image = UIImage(named: "UserIcon/\(member.avatar)")
nameLab.text = member.name nameLab.text = member.name
locationLab.text = member.address locationLab.text = member.address
// //
if AppContextManager.shared.vip > 1 { if AppContextManager.shared.vip > 1 {
batteryInfoView.isHidden = member.battery.int == 0 batteryInfoView.isHidden = member.battery.int == 0
@ -33,7 +33,7 @@ class InteractionView: UIView {
let batteryPercent = min(CGFloat(batteryInt), 100) let batteryPercent = min(CGFloat(batteryInt), 100)
batteryView.layoutChain.width(CGFloat(16 - 1) * batteryPercent / 100.0) batteryView.layoutChain.width(CGFloat(16 - 1) * batteryPercent / 100.0)
batteryLab.text = "\(member.battery)%" batteryLab.text = "\(member.battery)%"
// //
lockView.isHidden = true lockView.isHidden = true
} }
@ -68,6 +68,12 @@ class InteractionView: UIView {
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
shareBtn.rx.tap
.subscribe(onNext: { [weak self] _ in
self?.onShare?()
})
.disposed(by: disposeBag)
// collectionView // collectionView
emojiRelay emojiRelay
.map { [SectionModel(model: "", items: $0)] } .map { [SectionModel(model: "", items: $0)] }
@ -115,7 +121,7 @@ class InteractionView: UIView {
interestBg.isHidden = isFun interestBg.isHidden = isFun
emojiRelay.accept(isFun ? UIView.funEmojiFileNames : UIView.emojiFileNames) emojiRelay.accept(isFun ? UIView.funEmojiFileNames : UIView.emojiFileNames)
} }
private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>> = { private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>> = {
RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>> { _, collectionView, indexPath, name in RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>> { _, collectionView, indexPath, name in
let cell: EmojiPanelCell = collectionView.dequeueReusableCell(for: indexPath) let cell: EmojiPanelCell = collectionView.dequeueReusableCell(for: indexPath)
@ -130,7 +136,7 @@ class InteractionView: UIView {
(cell as? EmojiPanelCell)?.stopAnimation() (cell as? EmojiPanelCell)?.stopAnimation()
} }
} }
private func setupUI() { private func setupUI() {
addSubview(infoView) addSubview(infoView)
infoView.addSubview(headerBgView) infoView.addSubview(headerBgView)
@ -150,109 +156,109 @@ class InteractionView: UIView {
emojiView.addSubview(segmentView) emojiView.addSubview(segmentView)
emojiView.addSubview(emojiCollectionView) emojiView.addSubview(emojiCollectionView)
emojiView.addSubview(emojiPageControl) emojiView.addSubview(emojiPageControl)
infoView.layoutChain.edges() infoView.layoutChain.edges()
headerBgView.layoutChain headerBgView.layoutChain
.edges(excludingEdge: .bottom) .edges(excludingEdge: .bottom)
.height(134) .height(134)
lineView.layoutChain lineView.layoutChain
.top(13) .top(13)
.width(36) .width(36)
.height(4) .height(4)
.centerX() .centerX()
avaterImgView.layoutChain avaterImgView.layoutChain
.top(25) .top(25)
.left(25) .left(25)
.width(50) .width(50)
.height(50) .height(50)
batteryInfoView.layoutChain batteryInfoView.layoutChain
.leftToView(avaterImgView) .leftToView(avaterImgView)
.rightToView(avaterImgView) .rightToView(avaterImgView)
.bottomToView(avaterImgView) .bottomToView(avaterImgView)
.height(12) .height(12)
cornerView.layoutChain.edges() cornerView.layoutChain.edges()
batteryIcon.layoutChain batteryIcon.layoutChain
.left(7) .left(7)
.centerY() .centerY()
.width(16) .width(16)
.height(8) .height(8)
batteryView.layoutChain batteryView.layoutChain
.topToView(batteryIcon) .topToView(batteryIcon)
.leftToView(batteryIcon, offset: -1) .leftToView(batteryIcon, offset: -1)
.bottomToView(batteryIcon) .bottomToView(batteryIcon)
batteryLab.layoutChain batteryLab.layoutChain
.leftToRightOfView(batteryIcon, offset: 4) .leftToRightOfView(batteryIcon, offset: 4)
.right(5) .right(5)
.centerY() .centerY()
nameLab.layoutChain nameLab.layoutChain
.topToView(avaterImgView, offset: 8) .topToView(avaterImgView, offset: 8)
.leftToRightOfView(avaterImgView, offset: 15) .leftToRightOfView(avaterImgView, offset: 15)
locationLab.layoutChain locationLab.layoutChain
.topToBottomOfView(nameLab) .topToBottomOfView(nameLab)
.leftToView(nameLab) .leftToView(nameLab)
shareBtn.layoutChain shareBtn.layoutChain
.right(15) .right(15)
.centerY(avaterImgView) .centerY(avaterImgView)
.width(30) .width(30)
.height(30) .height(30)
navigateBtn.layoutChain navigateBtn.layoutChain
.rightToLeftOfView(shareBtn, offset: -10) .rightToLeftOfView(shareBtn, offset: -10)
.centerY(avaterImgView) .centerY(avaterImgView)
.width(30) .width(30)
.height(30) .height(30)
emojiView.layoutChain emojiView.layoutChain
.topToBottomOfView(batteryInfoView, offset: 21) .topToBottomOfView(batteryInfoView, offset: 21)
.edgesHorzontal(15) .edgesHorzontal(15)
.height(276) .height(276)
emojiBgView.layoutChain.edges() emojiBgView.layoutChain.edges()
segmentView.layoutChain segmentView.layoutChain
.edges(excludingEdge: .bottom) .edges(excludingEdge: .bottom)
.height(56) .height(56)
emojiPageControl.layoutChain emojiPageControl.layoutChain
.centerX() .centerX()
.height(28) .height(28)
.bottom(13) .bottom(13)
emojiCollectionView.layoutChain emojiCollectionView.layoutChain
.top(51) .top(51)
.edgesHorzontal() .edgesHorzontal()
.height(180) .height(180)
} }
lazy var infoView: UIView = { lazy var infoView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
return view return view
}() }()
lazy var headerBgView: UIImageView = { lazy var headerBgView: UIImageView = {
let view = UIImageView(image: UIImage(named: "Home/interaction_header")) let view = UIImageView(image: UIImage(named: "Home/interaction_header"))
return view return view
}() }()
lazy var lineView: UIView = { lazy var lineView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
view.cornerRadius = 2 view.cornerRadius = 2
return view return view
}() }()
lazy var avaterImgView: UIImageView = { lazy var avaterImgView: UIImageView = {
let view = UIImageView() let view = UIImageView()
view.backgroundColor = .lightGray view.backgroundColor = .lightGray
@ -260,7 +266,7 @@ class InteractionView: UIView {
view.cornerRadius = 25 view.cornerRadius = 25
return view return view
}() }()
lazy var batteryInfoView: UIView = { lazy var batteryInfoView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
@ -271,48 +277,48 @@ class InteractionView: UIView {
view.isHidden = true view.isHidden = true
return view return view
}() }()
lazy var cornerView: UIView = { lazy var cornerView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
view.cornerRadius = 6 view.cornerRadius = 6
return view return view
}() }()
lazy var batteryView: UIView = { lazy var batteryView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = UIColor(hexStr: "#75E582") view.backgroundColor = UIColor(hexStr: "#75E582")
return view return view
}() }()
lazy var batteryIcon: UIImageView = { lazy var batteryIcon: UIImageView = {
let view = UIImageView() let view = UIImageView()
view.backgroundColor = .clear view.backgroundColor = .clear
view.image = UIImage(named: "Home/battery") view.image = UIImage(named: "Home/battery")
return view return view
}() }()
lazy var batteryLab: UILabel = { lazy var batteryLab: UILabel = {
let label = UILabel() let label = UILabel()
label.textColor = UIColor(hexStr: "#D4D4D4") label.textColor = UIColor(hexStr: "#D4D4D4")
label.font = .systemFont(ofSize: 6, weight: .medium) label.font = .systemFont(ofSize: 6, weight: .medium)
return label return label
}() }()
lazy var nameLab: UILabel = { lazy var nameLab: UILabel = {
let label = UILabel() let label = UILabel()
label.textColor = UIColor(hexStr: "#0F2846") label.textColor = UIColor(hexStr: "#0F2846")
label.font = .systemFont(ofSize: 14, weight: .semibold) label.font = .systemFont(ofSize: 14, weight: .semibold)
return label return label
}() }()
lazy var locationLab: UILabel = { lazy var locationLab: UILabel = {
let label = UILabel() let label = UILabel()
label.textColor = UIColor(hexStr: "#8D8D8D") label.textColor = UIColor(hexStr: "#8D8D8D")
label.font = .systemFont(ofSize: 10, weight: .regular) label.font = .systemFont(ofSize: 10, weight: .regular)
return label return label
}() }()
lazy var navigateBtn: UIButton = { lazy var navigateBtn: UIButton = {
let btn = UIButton(type: .custom) let btn = UIButton(type: .custom)
btn.backgroundColor = .white btn.backgroundColor = .white
@ -321,7 +327,7 @@ class InteractionView: UIView {
btn.extendEdgeInsets = UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 0) btn.extendEdgeInsets = UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 0)
return btn return btn
}() }()
lazy var shareBtn: UIButton = { lazy var shareBtn: UIButton = {
let btn = UIButton(type: .custom) let btn = UIButton(type: .custom)
btn.backgroundColor = .white btn.backgroundColor = .white
@ -330,36 +336,36 @@ class InteractionView: UIView {
btn.extendEdgeInsets = UIEdgeInsets(top: 30, left: 0, bottom: 30, right: 15) btn.extendEdgeInsets = UIEdgeInsets(top: 30, left: 0, bottom: 30, right: 15)
return btn return btn
}() }()
lazy var emojiView: UIView = { lazy var emojiView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = UIColor(hexStr: "#E3F7FE")//.clear view.backgroundColor = UIColor(hexStr: "#E3F7FE")//.clear
view.cornerRadius = 15 view.cornerRadius = 15
return view return view
}() }()
lazy var emojiBgView: UIView = { lazy var emojiBgView: UIView = {
let view = UIImageView()//UIImageView(image: UIImage(named: "Home/emoji_bg")) let view = UIImageView()//UIImageView(image: UIImage(named: "Home/emoji_bg"))
view.contentMode = .scaleAspectFill view.contentMode = .scaleAspectFill
return view return view
}() }()
lazy var segmentView: UIView = { lazy var segmentView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = UIColor(hexStr: "#E3F7FE") view.backgroundColor = UIColor(hexStr: "#E3F7FE")
view.addSubview(normalBg) view.addSubview(normalBg)
view.addSubview(interestBg) view.addSubview(interestBg)
normalBg.layoutChain normalBg.layoutChain
.top().left() .top().left()
.height(56) .height(56)
.widthToHeight(220/56) .widthToHeight(220/56)
interestBg.layoutChain interestBg.layoutChain
.top().right() .top().right()
.height(56) .height(56)
.widthToHeight(220/56) .widthToHeight(220/56)
let normalLab = UILabel() let normalLab = UILabel()
normalLab.text = "常规表情" normalLab.text = "常规表情"
normalLab.font = .systemFont(ofSize: 12, weight: .semibold) normalLab.font = .systemFont(ofSize: 12, weight: .semibold)
@ -369,7 +375,7 @@ class InteractionView: UIView {
normalLab.layoutChain normalLab.layoutChain
.top(11) .top(11)
.left().rightToCenterXOfView(view) .left().rightToCenterXOfView(view)
let interesView = UIView() let interesView = UIView()
interesView.backgroundColor = .clear interesView.backgroundColor = .clear
view.addSubview(interesView) view.addSubview(interesView)
@ -377,81 +383,81 @@ class InteractionView: UIView {
.top(11) .top(11)
.right().leftToCenterXOfView(view) .right().leftToCenterXOfView(view)
.height(18) .height(18)
let interestLab = UILabel() let interestLab = UILabel()
interestLab.text = "趣味表情" interestLab.text = "趣味表情"
interestLab.font = .systemFont(ofSize: 12, weight: .semibold) interestLab.font = .systemFont(ofSize: 12, weight: .semibold)
interestLab.textColor = .black interestLab.textColor = .black
interestLab.textAlignment = .center interestLab.textAlignment = .center
interesView.addSubview(interestLab) interesView.addSubview(interestLab)
interestLab.layoutChain interestLab.layoutChain
.top() .top()
.centerX() .centerX()
interesView.addSubview(lockView) interesView.addSubview(lockView)
lockView.layoutChain lockView.layoutChain
.leftToRightOfView(interestLab, offset: 4) .leftToRightOfView(interestLab, offset: 4)
.centerY(interestLab) .centerY(interestLab)
.width(18) .width(18)
.height(18) .height(18)
view.addSubview(emojiNormalBtn) view.addSubview(emojiNormalBtn)
emojiNormalBtn.layoutChain emojiNormalBtn.layoutChain
.top() .top()
.left() .left()
.rightToCenterXOfView(view) .rightToCenterXOfView(view)
.height(40) .height(40)
view.addSubview(emojiFunBtn) view.addSubview(emojiFunBtn)
emojiFunBtn.layoutChain emojiFunBtn.layoutChain
.top() .top()
.right() .right()
.leftToCenterXOfView(view) .leftToCenterXOfView(view)
.height(40) .height(40)
return view return view
}() }()
lazy var normalBg: UIImageView = { lazy var normalBg: UIImageView = {
let view = UIImageView(image: UIImage(named: "Home/emoji_normal")) let view = UIImageView(image: UIImage(named: "Home/emoji_normal"))
view.contentMode = .scaleAspectFill view.contentMode = .scaleAspectFill
view.isHidden = true view.isHidden = true
return view return view
}() }()
lazy var interestBg: UIImageView = { lazy var interestBg: UIImageView = {
let view = UIImageView(image: UIImage(named: "Home/emoji_interest")) let view = UIImageView(image: UIImage(named: "Home/emoji_interest"))
view.contentMode = .scaleAspectFill view.contentMode = .scaleAspectFill
return view return view
}() }()
lazy var emojiNormalBtn: UIButton = { lazy var emojiNormalBtn: UIButton = {
let btn = UIButton() let btn = UIButton()
btn.backgroundColor = .clear btn.backgroundColor = .clear
return btn return btn
}() }()
lazy var emojiFunBtn: UIButton = { lazy var emojiFunBtn: UIButton = {
let btn = UIButton() let btn = UIButton()
btn.backgroundColor = .clear btn.backgroundColor = .clear
return btn return btn
}() }()
lazy var lockView: UIView = { lazy var lockView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
view.cornerRadius = 9 view.cornerRadius = 9
let icon = UIImageView(image: UIImage(named: "Home/lock")) let icon = UIImageView(image: UIImage(named: "Home/lock"))
view.addSubview(icon) view.addSubview(icon)
icon.layoutChain icon.layoutChain
.centerX() .centerX()
.centerY() .centerY()
return view return view
}() }()
lazy var emojiCollectionView: UICollectionView = { lazy var emojiCollectionView: UICollectionView = {
let layout = CollectionHFlowLayout() let layout = CollectionHFlowLayout()
let hSpacing: CGFloat = (kScreenWidth - 30 - CGFloat(InteractionView.emojiCols) * 50) / CGFloat(InteractionView.emojiCols + 1) let hSpacing: CGFloat = (kScreenWidth - 30 - CGFloat(InteractionView.emojiCols) * 50) / CGFloat(InteractionView.emojiCols + 1)
@ -471,9 +477,10 @@ class InteractionView: UIView {
cv.delegate = self cv.delegate = self
return cv return cv
}() }()
var onNavigate: (() -> Void)? var onNavigate: (() -> Void)?
var onSendEmote: ((Int) -> Void)? var onSendEmote: ((Int) -> Void)?
var onShare: (() -> Void)?
var currentMember: CircleMember? var currentMember: CircleMember?
lazy var emojiPageControl: UIPageControl = { lazy var emojiPageControl: UIPageControl = {
@ -483,7 +490,7 @@ class InteractionView: UIView {
pc.pageIndicatorTintColor = UIColor(hexStr: "#7AD6FF", alpha: 0.4) pc.pageIndicatorTintColor = UIColor(hexStr: "#7AD6FF", alpha: 0.4)
return pc return pc
}() }()
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
backgroundColor = .clear backgroundColor = .clear
@ -559,17 +566,17 @@ final class EmojiPanelCell: UICollectionViewCell {
v.loopMode = .loop v.loopMode = .loop
return v return v
}() }()
lazy var lockView: UIView = { lazy var lockView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
view.isHidden = true view.isHidden = true
let icon = UIImageView(image: UIImage(named: "Home/emoji_lock")) let icon = UIImageView(image: UIImage(named: "Home/emoji_lock"))
view.addSubview(icon) view.addSubview(icon)
icon.layoutChain icon.layoutChain
.edges() .edges()
return view return view
}() }()
@ -603,7 +610,7 @@ final class EmojiPanelCell: UICollectionViewCell {
self.lottieView.play() self.lottieView.play()
} }
} }
lockView.isHidden = AppContextManager.shared.vip > 1 lockView.isHidden = AppContextManager.shared.vip > 1
} }
@ -636,7 +643,7 @@ extension UIView {
.map { $0.replacingOccurrences(of: ".json", with: "") } .map { $0.replacingOccurrences(of: ".json", with: "") }
.sorted() .sorted()
}() }()
static var funEmojiFileNames: [String] = { static var funEmojiFileNames: [String] = {
let paths = Bundle.main.paths(forResourcesOfType: "json", inDirectory: nil) let paths = Bundle.main.paths(forResourcesOfType: "json", inDirectory: nil)
return paths return paths

View File

@ -10,10 +10,37 @@ import RxSwift
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
extension PromotionalActivitiesItem: Equatable {
static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.recommend(let a), .recommend(let b)): return a == b
case (.normal(let a), .normal(let b)): return a == b
default: return false
}
}
}
extension PromotionalActivitiesItem: IdentifiableType {
typealias Identity = String
var identity: String {
switch self {
case .recommend(let model): return model.goods_id
case .normal(let model): return model.goods_id
}
}
}
class PromotionalActivitiesVC: BaseViewController { class PromotionalActivitiesVC: BaseViewController {
fileprivate var rootView: PromotionalActivitiesView! fileprivate var rootView: PromotionalActivitiesView!
var onDismiss: (() -> Void)? var onDismiss: (() -> Void)?
private var selectedGoodsId: String = ""
private let itemsRelay = BehaviorRelay<[PromotionalActivitiesItem]>(value: [])
var items: [PromotionalActivitiesItem] {
get { itemsRelay.value }
set { itemsRelay.accept(newValue) }
}
override func loadView() { override func loadView() {
rootView = PromotionalActivitiesView(frame: UIScreen.main.bounds) rootView = PromotionalActivitiesView(frame: UIScreen.main.bounds)
@ -23,8 +50,50 @@ class PromotionalActivitiesVC: BaseViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. setupTableView()
requestRechargeInfo() requestRechargeInfo()
rootView.confirmBtn.rx.tap.subscribe(onNext: { _ in
self.requestOrderPayParams()
}).disposed(by: disposeBag)
}
private func setupTableView() {
typealias Section = SectionModel<String, PromotionalActivitiesItem>
let dataSource = RxTableViewSectionedReloadDataSource<Section> { _, tableView, indexPath, item in
let isSelected = item.identity == self.selectedGoodsId
switch item {
case .recommend(let model):
let cell: PromotionalRecommendCell = tableView.dequeueReusableCell(for: indexPath)
cell.configure(model: model, isSelected: isSelected)
return cell
case .normal(let model):
let cell: PromotionalNormalCell = tableView.dequeueReusableCell(for: indexPath)
cell.configure(model: model, isSelected: isSelected)
return cell
}
}
itemsRelay
.map { [SectionModel(model: "", items: $0)] }
.bind(to: rootView.tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
itemsRelay
.subscribe(onNext: { [weak self] items in
self?.rootView.tableView.layoutChain.height(CGFloat(items.count) * 106 + 5)
self?.rootView.scrollContentView.layoutIfNeeded()
})
.disposed(by: disposeBag)
rootView.tableView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
guard let self = self else { return }
let item = self.itemsRelay.value[indexPath.row]
self.selectedGoodsId = item.identity
self.rootView.tableView.reloadData()
})
.disposed(by: disposeBag)
} }
override func viewDidDisappear(_ animated: Bool) { override func viewDidDisappear(_ animated: Bool) {
@ -39,7 +108,103 @@ class PromotionalActivitiesVC: BaseViewController {
DLToast.showLoading() DLToast.showLoading()
OrderService.rechargeInfo(type: "member").subscribe(onNext: { [weak self] response in OrderService.rechargeInfo(type: "member").subscribe(onNext: { [weak self] response in
guard let self = self else { return } guard let self = self else { return }
self.rootView.startCountdown(endTime: 1769875199) if let checkedId = response.list.first(where: { $0.checked })?.goods_id {
self.selectedGoodsId = checkedId
}
let items = response.list.map { model in
model.goods_id == "20042"
? PromotionalActivitiesItem.recommend(model: model)
: PromotionalActivitiesItem.normal(model: model)
}
self.items = items
self.rootView.startCountdown()
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
} }
private func requestOrderPayParams() {
guard let systemConfig = AppContextManager.shared.systemConfig, let timeConfig = systemConfig.timeConfig else {
return
}
if timeConfig.pay_pop_type == 3 {
IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in
guard let self = self else { return }
if success {
self.showPayResultPop(showCloseBtn: false,
status: true,
title: "支付成功",
message: "恭喜您!成功开通!",
confirmText: "确定", confirmBlock: {
AppRouter.shared.popOrDismiss()
}, cancelBlock: { })
} else {
self.showPayResultPop(status: false,
title: "支付失败",
message: msg ?? "很抱歉,请您重新支付!",
confirmText: "重新支付", confirmBlock: { },
cancelText: "取消", cancelBlock: {
AppRouter.shared.popOrDismiss()
})
}
}
return
}
let payType = timeConfig.pay_pop_type == 1 ? "alipay" : "weixin"
dl.showLoading()
OrderService.orderPayParams(goodsId: selectedGoodsId,
payType: payType).subscribe(onNext: { response in
self.dl.dismiss()
guard let data = response.data else { return }
if payType == "weixin" { //
// WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink)
//
// let request: PayReq = PayReq()
// request.partnerId = model.partnerId
// request.prepayId = model.prepayId
// request.package = model.packageValue
// request.nonceStr = model.nonceStr
// request.timeStamp = UInt32(model.timeStamp) ?? 0
// request.sign = model.sign
// WXApi.send(request)
}
else if payType == "alipay" { //
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
AlipaySDK.defaultService().payOrder(payParam, fromScheme: "Alipay\(appId)") { resultDic in
print("支付宝callback -> \(resultDic)")
guard let result = resultDic,
let resultStatus = result["resultStatus"] as? String else { return }
/**
9000
8000
4000
5000
6001
6002
6004
*/
if resultStatus == "9000" {
self.showPayResultPop(showCloseBtn: false,
status: true,
title: "支付成功",
message: "恭喜您!成功开通\("")",
confirmText: "确定", confirmBlock: {
AppRouter.shared.popOrDismiss()
}, cancelBlock: { })
}
else {
self.showPayResultPop(status: false,
title: "支付失败",
message: "很抱歉,请您重新支付!",
confirmText: "重新支付", confirmBlock: { }, cancelText: "取消", cancelBlock: {
AppRouter.shared.popOrDismiss()
})
}
}
}
}
}, onError: { _ in }).disposed(by: disposeBag)
}
} }

View File

@ -9,6 +9,11 @@ import UIKit
import RxSwift import RxSwift
import RxCocoa import RxCocoa
enum PromotionalActivitiesItem {
case recommend(model: VipExpenseModel)
case normal(model: VipExpenseModel)
}
class PromotionalActivitiesView: UIView { class PromotionalActivitiesView: UIView {
var disposeBag = DisposeBag() var disposeBag = DisposeBag()
@ -17,7 +22,6 @@ class PromotionalActivitiesView: UIView {
backBtn.rx.tap.subscribe(onNext: { _ in backBtn.rx.tap.subscribe(onNext: { _ in
VipWaivePopView.show() VipWaivePopView.show()
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
} }
private func setupUI() { private func setupUI() {
@ -121,13 +125,18 @@ class PromotionalActivitiesView: UIView {
.leftToRightOfView(iconView, offset: 4) .leftToRightOfView(iconView, offset: 4)
.centerY(iconView) .centerY(iconView)
scrollContentView.addSubview(tableView)
tableView.layoutChain
.topToBottomOfView(titleLab, offset: 0)
.edgesHorzontal()
let agreementView = UIView() let agreementView = UIView()
infoView.addSubview(agreementView) infoView.addSubview(agreementView)
agreementView.layoutChain agreementView.layoutChain
.topToBottomOfView(titleLab, offset: 18) .topToBottomOfView(tableView, offset: 15)
.left(15) .left(15)
.right(15) .right(15)
.height(30) .height(16)
agreementView.addSubview(agreementCheckBtn) agreementView.addSubview(agreementCheckBtn)
agreementCheckBtn.layoutChain agreementCheckBtn.layoutChain
@ -138,7 +147,7 @@ class PromotionalActivitiesView: UIView {
agreementView.addSubview(agreementTextView) agreementView.addSubview(agreementTextView)
agreementTextView.layoutChain agreementTextView.layoutChain
.leftToRightOfView(agreementCheckBtn, offset: 6) .leftToRightOfView(agreementCheckBtn, offset: 3)
.right(0) .right(0)
.centerY() .centerY()
.bottom(0) .bottom(0)
@ -161,12 +170,24 @@ class PromotionalActivitiesView: UIView {
return view return view
}() }()
lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.estimatedRowHeight = 106
tableView.isScrollEnabled = false
tableView.showsVerticalScrollIndicator = false
tableView.register(PromotionalNormalCell.self)
tableView.register(PromotionalRecommendCell.self)
return tableView
}()
lazy var bottomView: UIView = { lazy var bottomView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
view.addSubview(countdownLabel) view.addSubview(countdownContainer)
countdownLabel.layoutChain countdownContainer.layoutChain
.top(11) .top(11)
.centerX() .centerX()
@ -186,6 +207,7 @@ class PromotionalActivitiesView: UIView {
btn.rx.tap.subscribe(onNext: { [weak btn] in btn.rx.tap.subscribe(onNext: { [weak btn] in
btn?.isSelected.toggle() btn?.isSelected.toggle()
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
btn.isSelected = true
return btn return btn
}() }()
@ -212,9 +234,59 @@ class PromotionalActivitiesView: UIView {
return tv return tv
}() }()
lazy var countdownLabel: UILabel = { lazy var countdownContainer: UIStackView = {
let stack = UIStackView(arrangedSubviews: [countdownPrefixLabel, countdownMinLabel, countdownColonLabel, countdownSecLabel, countdownSuffixLabel])
stack.axis = .horizontal
stack.alignment = .center
stack.spacing = 0
return stack
}()
lazy var countdownPrefixLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .systemFont(ofSize: 12)
label.textColor = UIColor(hexStr: "#FD501B")
label.text = "优惠活动倒计时 "
return label
}()
lazy var countdownMinLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12)
label.textColor = .white
label.backgroundColor = UIColor(hexStr: "#FD501B")
label.layer.cornerRadius = 2
label.clipsToBounds = true
label.textAlignment = .center label.textAlignment = .center
label.text = "15"
return label
}()
lazy var countdownColonLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12)
label.textColor = UIColor(hexStr: "#FD501B")
label.text = ":"
return label
}()
lazy var countdownSecLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12)
label.textColor = .white
label.backgroundColor = UIColor(hexStr: "#FD501B")
label.layer.cornerRadius = 2
label.clipsToBounds = true
label.textAlignment = .center
label.text = "00"
return label
}()
lazy var countdownSuffixLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12)
label.textColor = UIColor(hexStr: "#FD501B")
label.text = " 限时特惠"
return label return label
}() }()
@ -228,11 +300,11 @@ class PromotionalActivitiesView: UIView {
return btn return btn
}() }()
private var countdownEndTime: TimeInterval = 0 private var countdownRemaining: Int = 15 * 60
private var countdownTimer: Timer? private var countdownTimer: Timer?
func startCountdown(endTime: TimeInterval) { func startCountdown() {
countdownEndTime = endTime countdownRemaining = 15 * 60
updateCountdown() updateCountdown()
countdownTimer?.invalidate() countdownTimer?.invalidate()
countdownTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in countdownTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
@ -246,38 +318,17 @@ class PromotionalActivitiesView: UIView {
} }
private func updateCountdown() { private func updateCountdown() {
let remaining = max(0, countdownEndTime - Date().timeIntervalSince1970) if countdownRemaining > 0 {
let totalSeconds = Int(remaining) countdownRemaining -= 1
let days = totalSeconds / 86400
let hours = (totalSeconds % 86400) / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
let attr = NSMutableAttributedString()
let normalStyle: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor(hexStr: "#FD501B"),
.font: UIFont.systemFont(ofSize: 12)
]
let timeStyle: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.white,
.font: UIFont.systemFont(ofSize: 12),
.backgroundColor: UIColor(hexStr: "#FD501B")
]
attr.append(NSAttributedString(string: "优惠活动倒计时 ", attributes: normalStyle))
let timeStr: String
if days > 0 {
timeStr = String(format: "%d天%02d:%02d:%02d", days, hours, minutes, seconds)
} else {
timeStr = String(format: "%02d:%02d:%02d", hours, minutes, seconds)
} }
attr.append(NSAttributedString(string: timeStr, attributes: timeStyle)) else {
AppRouter.shared.popOrDismiss()
attr.append(NSAttributedString(string: " 限时特惠", attributes: normalStyle)) return
}
countdownLabel.attributedText = attr let min = countdownRemaining / 60
let sec = countdownRemaining % 60
countdownMinLabel.text = String(format: "%02d", min)
countdownSecLabel.text = String(format: "%02d", sec)
} }
override func layoutSubviews() { override func layoutSubviews() {
@ -319,3 +370,287 @@ extension PromotionalActivitiesView: UITextViewDelegate {
return false return false
} }
} }
// MARK: - PromotionalNormalCell
class PromotionalNormalCell: UITableViewCell {
func configure(model: VipExpenseModel, isSelected: Bool) {
priceLab.text = model.price
originPriceLab.text = "原价" + model.origin_price
originPriceLab.setupStrikethroughStyle()
goodsNameLab.text = model.goods_name
tipsLab.text = model.tips2
bgView.image = UIImage(named: isSelected ? "PromotionalActivities/normal_on_bg" : "PromotionalActivities/normal_bg")
priceLab.textColor = isSelected ? UIColor(hexStr: "#FF383C") : ThemeManager.shared.color.titleAuxColor
unitLab.textColor = isSelected ? UIColor(hexStr: "#FF383C") : ThemeManager.shared.color.titleAuxColor
}
override init(style: CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
setupSubviews()
}
private func setupSubviews() {
contentView.addSubview(bgView)
bgView.addSubview(unitLab)
bgView.addSubview(priceLab)
bgView.addSubview(originPriceLab)
bgView.addSubview(goodsNameLab)
bgView.addSubview(tipsLab)
bgView.layoutChain
.top(22)
.edgesHorzontal(15)
.bottom()
priceLab.layoutChain
.leftToRightOfView(unitLab, offset: 0)
.centerY()
unitLab.layoutChain
.left(32)
.centerY(bgView, offset: 9)
originPriceLab.layoutChain
.topToBottomOfView(priceLab, offset: -5)
.centerX(priceLab)
goodsNameLab.layoutChain
.leftToRightOfView(priceLab, offset: 27)
.bottomToCenterYOfView(bgView)
tipsLab.layoutChain
.leftToView(goodsNameLab)
.topToCenterYOfView(bgView, offset: 3)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
lazy var bgView: UIImageView = {
let view = UIImageView()
view.backgroundColor = .clear
view.contentMode = .scaleAspectFill
return view
}()
lazy var priceLab: UILabel = {
let label = UILabel()
label.textColor = UIColor(hexStr: "#FF383C")
label.font = .systemFont(ofSize: 40, weight: .bold)
return label
}()
lazy var unitLab: UILabel = {
let label = UILabel()
label.text = ""
label.textColor = UIColor(hexStr: "#FF383C")
label.font = .systemFont(ofSize: 12, weight: .regular)
return label
}()
lazy var originPriceLab: UILabel = {
let label = UILabel()
label.textColor = UIColor(hexStr: "#999999")
label.font = .systemFont(ofSize: 10, weight: .regular)
return label
}()
lazy var goodsNameLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.titleAuxColor
label.font = .systemFont(ofSize: 18, weight: .semibold)
return label
}()
lazy var tipsLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.contentColor
label.font = .systemFont(ofSize: 14, weight: .regular)
return label
}()
}
// MARK: - PromotionalRecommendCell
class PromotionalRecommendCell: UITableViewCell {
func configure(model: VipExpenseModel, isSelected: Bool) {
tipsTitleLab.text = model.tips
priceLab.text = model.price
originPriceLab.text = "原价" + model.origin_price
originPriceLab.setupStrikethroughStyle()
goodsNameLab.text = model.goods_name
tipsLab.text = model.tips2
bgView.image = UIImage(named: isSelected ? "PromotionalActivities/recommend_on_bg" : "PromotionalActivities/recommend_bg")
priceLab.textColor = isSelected ? UIColor(hexStr: "#FF383C") : ThemeManager.shared.color.titleAuxColor
unitLab.textColor = isSelected ? UIColor(hexStr: "#FF383C") : ThemeManager.shared.color.titleAuxColor
}
override init(style: CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
setupSubviews()
}
private func setupSubviews() {
contentView.addSubview(bgView)
bgView.addSubview(unitLab)
bgView.addSubview(priceLab)
bgView.addSubview(originPriceLab)
bgView.addSubview(goodsNameLab)
bgView.addSubview(tipsLab)
bgView.addSubview(recommendLab)
bgView.addSubview(recommendIcon)
bgView.addSubview(tipsTitleView)
tipsTitleView.addSubview(tipsTitleLab)
bgView.layoutChain
.top(22)
.edgesHorzontal(15)
.bottom()
tipsTitleView.layoutChain
.topToView(bgView, offset: -8)
.leftToView(bgView, offset: -6)
.width(94)
.height(35)
tipsTitleLab.layoutChain
.centerX()
.top(5)
priceLab.layoutChain
.leftToRightOfView(unitLab, offset: 0)
.centerY()
unitLab.layoutChain
.left(32)
.centerY(bgView, offset: 9)
originPriceLab.layoutChain
.topToBottomOfView(priceLab, offset: -5)
.centerX(priceLab)
goodsNameLab.layoutChain
.leftToRightOfView(priceLab, offset: 27)
.bottomToCenterYOfView(bgView, offset: -5)
tipsLab.layoutChain
.leftToView(goodsNameLab)
.topToCenterYOfView(bgView, offset: 8)
recommendLab.layoutChain
.top(8)
.right(24)
recommendIcon.layoutChain
.rightToLeftOfView(recommendLab, offset: -3)
.centerY(recommendLab)
.width(13)
.height(11)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
lazy var bgView: UIImageView = {
let view = UIImageView()
view.backgroundColor = .clear
view.contentMode = .scaleAspectFill
return view
}()
lazy var priceLab: UILabel = {
let label = UILabel()
label.textColor = UIColor(hexStr: "#FF383C")
label.font = .systemFont(ofSize: 40, weight: .bold)
return label
}()
lazy var unitLab: UILabel = {
let label = UILabel()
label.text = ""
label.textColor = UIColor(hexStr: "#FF383C")
label.font = .systemFont(ofSize: 12, weight: .regular)
return label
}()
lazy var originPriceLab: UILabel = {
let label = UILabel()
label.textColor = UIColor(hexStr: "#999999")
label.font = .systemFont(ofSize: 10, weight: .regular)
return label
}()
lazy var goodsNameLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.titleAuxColor
label.font = .systemFont(ofSize: 18, weight: .semibold)
return label
}()
lazy var tipsTitleView: UIImageView = {
let view = UIImageView()
view.backgroundColor = .clear
view.image = UIImage(named: "PromotionalActivities/tips_bg")
return view
}()
lazy var tipsTitleLab: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 12, weight: .semibold)
return label
}()
lazy var tipsLab: UILabel = {
let label = UILabel()
label.textColor = ThemeManager.shared.color.contentColor
label.font = .systemFont(ofSize: 14, weight: .regular)
return label
}()
lazy var recommendIcon: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
view.image = UIImage(named: "PromotionalActivities/recommend_icon")
return view
}()
lazy var recommendLab: UILabel = {
let label = UILabel()
label.text = "80%的用户选择"
label.textColor = .white
label.font = .systemFont(ofSize: 12, weight: .medium)
return label
}()
}

View File

@ -0,0 +1,199 @@
//
// SharePopView.swift
// QuickLocation
//
// Created by on 2026/7/3.
//
import UIKit
class SharePopView: UIView {
private static let shared = SharePopView(frame: CGRect(origin: .zero, size: kScreenSize))
private var shareURL: String = ""
private let contentHeight: CGFloat = 191 + kSafeBottomMargin
static func show(shareURL: String) {
guard let superView = kKeyWindow else { return }
if SharePopView.shared.superview != nil {
SharePopView.shared.dismiss(animated: false)
}
SharePopView.shared.shareURL = shareURL
SharePopView.shared.bgView.alpha = 0
superView.addSubview(SharePopView.shared)
superView.bringSubviewToFront(SharePopView.shared)
SharePopView.shared.contentView.transform = CGAffineTransform(translationX: 0, y: SharePopView.shared.contentHeight)
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut) {
SharePopView.shared.bgView.alpha = 1
SharePopView.shared.contentView.transform = .identity
}
}
private func dismiss(animated: Bool = true) {
if animated {
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) {
self.bgView.alpha = 0
self.contentView.transform = CGAffineTransform(translationX: 0, y: self.contentHeight)
} completion: { _ in
self.removeFromSuperview()
}
} else {
removeFromSuperview()
}
}
@objc private func handleBgTap() {
dismiss()
}
@objc private func handleWeChatTap() {
shareToWeChat(scene: 0)
}
@objc private func handleMomentsTap() {
shareToWeChat(scene: 1)
}
@objc private func handleLinkTap() {
UIPasteboard.general.string = shareURL
DLToast.showSuccess(text: "链接已复制")
dismiss()
}
private func shareToWeChat(scene: Int32) {
let message = WXMediaMessage()
message.title = kAppName
let webpage = WXWebpageObject()
webpage.webpageUrl = shareURL
message.mediaObject = webpage
let req = SendMessageToWXReq()
req.bText = false
req.message = message
req.scene = scene
WXApi.send(req)
dismiss()
}
// MARK: - UI
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
addSubview(bgView)
bgView.addSubview(contentView)
contentView.addSubview(titleLabel)
contentView.addSubview(shareStack)
contentView.addSubview(closeBtn)
bgView.layoutChain.edges()
contentView.layoutChain
.edges(excludingEdge: .top)
.height(contentHeight)
titleLabel.layoutChain
.top(20)
.centerX()
closeBtn.layoutChain
.centerY(titleLabel)
.right(20)
.width(20)
.height(20)
shareStack.layoutChain
.topToBottomOfView(titleLabel, offset: 30)
.centerX()
let tap = UITapGestureRecognizer(target: self, action: #selector(handleBgTap))
bgView.addGestureRecognizer(tap)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Lazy Views
private lazy var bgView: UIView = {
let view = UIView()
view.backgroundColor = .black.withAlphaComponent(0.5)
return view
}()
private lazy var contentView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 26
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.clipsToBounds = true
return view
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = "分享"
label.font = .systemFont(ofSize: 16, weight: .medium)
label.textColor = UIColor(hexStr: "#333333")
return label
}()
private lazy var shareStack: UIStackView = {
let stack = UIStackView(arrangedSubviews: [
makeShareItem(icon: "Share/friend", title: "微信", action: #selector(handleWeChatTap)),
makeShareItem(icon: "Share/moments", title: "朋友圈", action: #selector(handleMomentsTap)),
makeShareItem(icon: "Share/url", title: "链接", action: #selector(handleLinkTap))
])
stack.axis = .horizontal
stack.spacing = 50
stack.alignment = .center
return stack
}()
private lazy var closeBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "Group/close"), for: .normal)
btn.addTarget(self, action: #selector(handleBgTap), for: .touchUpInside)
btn.extendEdgeInsets = UIEdgeInsets(top: 15, left: 20, bottom: 20, right: 20)
return btn
}()
private func makeShareItem(icon: String, title: String, action: Selector) -> UIView {
let container = UIView()
let imgView = UIImageView(image: UIImage(named: icon))
imgView.contentMode = .scaleAspectFit
container.addSubview(imgView)
imgView.layoutChain
.top(0)
.centerX()
.width(50)
.height(50)
let label = UILabel()
label.text = title
label.font = .systemFont(ofSize: 12)
label.textColor = UIColor(hexStr: "#666666")
label.textAlignment = .center
container.addSubview(label)
label.layoutChain
.topToBottomOfView(imgView, offset: 8)
.centerX()
.bottom(0)
container.layoutChain.width(60).height(78)
let btn = UIButton(type: .custom)
container.addSubview(btn)
btn.layoutChain.edges()
btn.addTarget(self, action: action, for: .touchUpInside)
return container
}
}

View File

@ -18,7 +18,7 @@ class VipRechargeVC: BaseViewController {
rootView = VipRechargeView(frame: UIScreen.main.bounds) rootView = VipRechargeView(frame: UIScreen.main.bounds)
view = rootView view = rootView
} }
private var viewModel = VipRechargeVM() private var viewModel = VipRechargeVM()
override func viewDidLoad() { override func viewDidLoad() {
@ -33,19 +33,19 @@ class VipRechargeVC: BaseViewController {
viewModel.output.sectionedItems viewModel.output.sectionedItems
.bind(to: rootView.expenseCollectionView.rx.items(dataSource: dataSource)) .bind(to: rootView.expenseCollectionView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag) .disposed(by: disposeBag)
viewModel.output.vipRightItems viewModel.output.vipRightItems
.bind(to: rootView.vipRightsCollectionView.rx.items(dataSource: vipRightsDataSource)) .bind(to: rootView.vipRightsCollectionView.rx.items(dataSource: vipRightsDataSource))
.disposed(by: disposeBag) .disposed(by: disposeBag)
viewModel.createCount.asObservable() viewModel.createCount.asObservable()
.bind(to: rootView.createCountLab.rx.text) .bind(to: rootView.createCountLab.rx.text)
.disposed(by: disposeBag) .disposed(by: disposeBag)
viewModel.groupCount.asObservable() viewModel.groupCount.asObservable()
.bind(to: rootView.groupCountLab.rx.text) .bind(to: rootView.groupCountLab.rx.text)
.disposed(by: disposeBag) .disposed(by: disposeBag)
viewModel.joinCount.asObservable() viewModel.joinCount.asObservable()
.bind(to: rootView.joinCountLab.rx.text) .bind(to: rootView.joinCountLab.rx.text)
.disposed(by: disposeBag) .disposed(by: disposeBag)
@ -55,7 +55,7 @@ class VipRechargeVC: BaseViewController {
rootView.payBtnView.rx.tapGesture.subscribe(onNext: { _ in rootView.payBtnView.rx.tapGesture.subscribe(onNext: { _ in
self.requestOrderPayParams() self.requestOrderPayParams()
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
Observable.zip( Observable.zip(
rootView.expenseCollectionView.rx.itemSelected, rootView.expenseCollectionView.rx.itemSelected,
rootView.expenseCollectionView.rx.modelSelected(VipExpenseModel.self) rootView.expenseCollectionView.rx.modelSelected(VipExpenseModel.self)
@ -67,7 +67,7 @@ class VipRechargeVC: BaseViewController {
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
} }
// MARK: - dataSource // MARK: - dataSource
private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<ExpenseListSectionModel> = { private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<ExpenseListSectionModel> = {
RxCollectionViewSectionedReloadDataSource<ExpenseListSectionModel> { datasource, collectionView, indexPath, model in RxCollectionViewSectionedReloadDataSource<ExpenseListSectionModel> { datasource, collectionView, indexPath, model in
@ -76,7 +76,7 @@ class VipRechargeVC: BaseViewController {
return cell return cell
} }
}() }()
private lazy var vipRightsDataSource: RxCollectionViewSectionedReloadDataSource<VipRightsListSectionModel> = { private lazy var vipRightsDataSource: RxCollectionViewSectionedReloadDataSource<VipRightsListSectionModel> = {
RxCollectionViewSectionedReloadDataSource<VipRightsListSectionModel> { datasource, collectionView, indexPath, model in RxCollectionViewSectionedReloadDataSource<VipRightsListSectionModel> { datasource, collectionView, indexPath, model in
let cell: VipRightsCell = collectionView.dequeueReusableCell(for: indexPath) let cell: VipRightsCell = collectionView.dequeueReusableCell(for: indexPath)
@ -96,19 +96,45 @@ class VipRechargeVC: BaseViewController {
self.rootView.discountLab.text = self.viewModel.discountPriceString self.rootView.discountLab.text = self.viewModel.discountPriceString
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
} }
// MARK: - API // MARK: - API
private func requestOrderPayParams() { private func requestOrderPayParams() {
DLToast.showLoading()
let types = viewModel.payType.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) } let types = viewModel.payType.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
let payType = types[rootView.selectedPayTypeTag]
if payType == "apple" {
IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in
guard let self = self else { return }
if success {
self.showPayResultPop(showCloseBtn: false,
status: true,
title: "支付成功",
message: "恭喜您!成功开通\(self.viewModel.goodsName)",
confirmText: "确定", confirmBlock: {
AppRouter.shared.popOrDismiss()
}, cancelBlock: { })
} else {
self.showPayResultPop(status: false,
title: "支付失败",
message: msg ?? "很抱歉,请您重新支付!",
confirmText: "重新支付", confirmBlock: { },
cancelText: "取消", cancelBlock: {
AppRouter.shared.popOrDismiss()
})
}
}
return
}
DLToast.showLoading()
OrderService.orderPayParams(goodsId: viewModel.goodsId, OrderService.orderPayParams(goodsId: viewModel.goodsId,
payType: types[rootView.selectedPayTypeTag]).subscribe(onNext: { response in payType: types[rootView.selectedPayTypeTag]).subscribe(onNext: { response in
guard let data = response.data else { return } guard let data = response.data else { return }
let payType = types[self.rootView.selectedPayTypeTag] let payType = types[self.rootView.selectedPayTypeTag]
if payType == "weixin" { // if payType == "weixin" { //
// WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink) // WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink)
// //
// let request: PayReq = PayReq() // let request: PayReq = PayReq()
// request.partnerId = model.partnerId // request.partnerId = model.partnerId
// request.prepayId = model.prepayId // request.prepayId = model.prepayId

View File

@ -10,23 +10,23 @@ import RxSwift
import RxCocoa import RxCocoa
class VipRechargeView: UIView { class VipRechargeView: UIView {
var disposeBag = DisposeBag() var disposeBag = DisposeBag()
private func setupRx() { private func setupRx() {
backBtn.rx.tap.subscribe(onNext: { _ in backBtn.rx.tap.subscribe(onNext: { _ in
VipWaivePopView.show() VipWaivePopView.show()
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
agreementLab.rx.tapGesture.subscribe { _ in agreementLab.rx.tapGesture.subscribe { _ in
AppRouter.push(Route.web, userInfo: ["url": URLManager.shared.userAgreementUrl]) AppRouter.push(Route.web, userInfo: ["url": URLManager.shared.userAgreementUrl])
}.disposed(by: disposeBag) }.disposed(by: disposeBag)
vipRightsDetailView.rx.tapGesture.subscribe { _ in vipRightsDetailView.rx.tapGesture.subscribe { _ in
AppRouter.push(Route.vipRights) AppRouter.push(Route.vipRights)
}.disposed(by: disposeBag) }.disposed(by: disposeBag)
} }
private func setupUI() { private func setupUI() {
addSubview(scrollView) addSubview(scrollView)
scrollView.addSubview(scrollContentView) scrollView.addSubview(scrollContentView)
@ -37,119 +37,119 @@ class VipRechargeView: UIView {
scrollContentView.addSubview(vipRightsView) scrollContentView.addSubview(vipRightsView)
scrollContentView.addSubview(agreementLab) scrollContentView.addSubview(agreementLab)
scrollContentView.addSubview(tipsLab) scrollContentView.addSubview(tipsLab)
vipRightsView.addSubview(vipRightsTitleView) vipRightsView.addSubview(vipRightsTitleView)
vipRightsView.addSubview(groupCountView) vipRightsView.addSubview(groupCountView)
vipRightsView.addSubview(vipRightsCollectionView) vipRightsView.addSubview(vipRightsCollectionView)
addSubview(bottomView) addSubview(bottomView)
bottomView.addSubview(payTypeStackView) bottomView.addSubview(payTypeStackView)
bottomView.addSubview(payBtnView) bottomView.addSubview(payBtnView)
bottomView.addSubview(payPriceView) bottomView.addSubview(payPriceView)
addSubview(navBarView) addSubview(navBarView)
navBarView.addSubview(navTitleLabel) navBarView.addSubview(navTitleLabel)
addSubview(backBtn) addSubview(backBtn)
navBarView.layoutChain navBarView.layoutChain
.edges(excludingEdge: .bottom) .edges(excludingEdge: .bottom)
.height(kNaviHeight) .height(kNaviHeight)
navTitleLabel.layoutChain navTitleLabel.layoutChain
.top(kStatusBarHeight + 12) .top(kStatusBarHeight + 12)
.centerY(backBtn) .centerY(backBtn)
.centerX() .centerX()
backBtn.layoutChain backBtn.layoutChain
.top(kStatusBarHeight + 12) .top(kStatusBarHeight + 12)
.left(15) .left(15)
.width(24) .width(24)
.height(24) .height(24)
bottomView.layoutChain bottomView.layoutChain
.edgesHorzontal() .edgesHorzontal()
.heightToWidth(144/375) .heightToWidth(144/375)
.bottom() .bottom()
payBtnView.layoutChain payBtnView.layoutChain
.edgesHorzontal(16) .edgesHorzontal(16)
.heightToWidth(50/343) .heightToWidth(50/343)
.centerY() .centerY()
payTypeStackView.layoutChain payTypeStackView.layoutChain
.top(13) .top(13)
.edgesHorzontal(16) .edgesHorzontal(16)
.bottomToTopOfView(payBtnView) .bottomToTopOfView(payBtnView)
payPriceView.layoutChain payPriceView.layoutChain
.left(32) .left(32)
.centerY(payBtnView, offset: -7) .centerY(payBtnView, offset: -7)
.height(30) .height(30)
scrollView.layoutChain scrollView.layoutChain
.edges(excludingEdge: .bottom) .edges(excludingEdge: .bottom)
.bottomToTopOfView(bottomView) .bottomToTopOfView(bottomView)
scrollContentView.layoutChain scrollContentView.layoutChain
.edges() .edges()
.widthToView(scrollView) .widthToView(scrollView)
headerBgImgView.layoutChain headerBgImgView.layoutChain
.edges(excludingEdge: .bottom) .edges(excludingEdge: .bottom)
.heightToWidth(267/375) .heightToWidth(267/375)
vipRightsDetailView.layoutChain vipRightsDetailView.layoutChain
.top(36) .top(36)
.right() .right()
.height(24) .height(24)
cornerView.layoutChain cornerView.layoutChain
.topToBottomOfView(headerBgImgView, offset: -20) .topToBottomOfView(headerBgImgView, offset: -20)
.edges(excludingEdge: .top) .edges(excludingEdge: .top)
let expenseCollectionViewHeight = (kScreenWidth - 32 - 32) / 3 * (144/110) + 10 let expenseCollectionViewHeight = (kScreenWidth - 32 - 32) / 3 * (144/110) + 10
expenseCollectionView.layoutChain expenseCollectionView.layoutChain
.topToView(cornerView, offset: -43) .topToView(cornerView, offset: -43)
.edgesHorzontal() .edgesHorzontal()
.height(expenseCollectionViewHeight * 1.1) .height(expenseCollectionViewHeight * 1.1)
vipRightsView.layoutChain vipRightsView.layoutChain
.topToBottomOfView(expenseCollectionView, offset: 18) .topToBottomOfView(expenseCollectionView, offset: 18)
.edgesHorzontal(16) .edgesHorzontal(16)
vipRightsTitleView.layoutChain vipRightsTitleView.layoutChain
.top(14) .top(14)
.centerX() .centerX()
groupCountView.layoutChain groupCountView.layoutChain
.topToBottomOfView(vipRightsTitleView, offset: 9) .topToBottomOfView(vipRightsTitleView, offset: 9)
.edgesHorzontal() .edgesHorzontal()
vipRightsCollectionView.layoutChain vipRightsCollectionView.layoutChain
.topToBottomOfView(groupCountView, offset: 14) .topToBottomOfView(groupCountView, offset: 14)
.edgesHorzontal() .edgesHorzontal()
.bottom(14) .bottom(14)
.height(150) .height(150)
agreementLab.layoutChain agreementLab.layoutChain
.topToBottomOfView(vipRightsView, offset: 6) .topToBottomOfView(vipRightsView, offset: 6)
.leftToView(vipRightsView) .leftToView(vipRightsView)
.rightToView(vipRightsView) .rightToView(vipRightsView)
tipsLab.layoutChain tipsLab.layoutChain
.topToBottomOfView(agreementLab, offset: 4) .topToBottomOfView(agreementLab, offset: 4)
.leftToView(vipRightsView) .leftToView(vipRightsView)
.rightToView(vipRightsView) .rightToView(vipRightsView)
.bottom(10) .bottom(10)
} }
lazy var navBarView: UIView = { lazy var navBarView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
view.alpha = 0 view.alpha = 0
return view return view
}() }()
lazy var navTitleLabel: UILabel = { lazy var navTitleLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.text = "升级会员" label.text = "升级会员"
@ -158,14 +158,14 @@ class VipRechargeView: UIView {
label.textAlignment = .center label.textAlignment = .center
return label return label
}() }()
lazy var backBtn: UIButton = { lazy var backBtn: UIButton = {
let btn = UIButton(type: .custom) let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "Common/back"), for: .normal) btn.setImage(UIImage(named: "Common/back"), for: .normal)
btn.extendEdgeInsets = UIEdgeInsets(top: 54, left: 15, bottom: 15, right: 100) btn.extendEdgeInsets = UIEdgeInsets(top: 54, left: 15, bottom: 15, right: 100)
return btn return btn
}() }()
lazy var scrollView: UIScrollView = { lazy var scrollView: UIScrollView = {
let view = UIScrollView() let view = UIScrollView()
view.backgroundColor = UIColor(hexStr: "#F5FBFB") view.backgroundColor = UIColor(hexStr: "#F5FBFB")
@ -175,43 +175,43 @@ class VipRechargeView: UIView {
view.contentInsetAdjustmentBehavior = .never view.contentInsetAdjustmentBehavior = .never
return view return view
}() }()
lazy var scrollContentView: UIView = { lazy var scrollContentView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
return view return view
}() }()
lazy var headerBgImgView: UIImageView = { lazy var headerBgImgView: UIImageView = {
let view = UIImageView() let view = UIImageView()
view.image = UIImage(named: "VipRecharge/header_bg") view.image = UIImage(named: "VipRecharge/header_bg")
return view return view
}() }()
lazy var vipRightsDetailView: UIView = { lazy var vipRightsDetailView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = UIColor(hexStr: "#FFFFFF", alpha: 0.7) view.backgroundColor = UIColor(hexStr: "#FFFFFF", alpha: 0.7)
let label = UILabel() let label = UILabel()
label.text = "权益对比" label.text = "权益对比"
label.font = .systemFont(ofSize: 11, weight: .bold) label.font = .systemFont(ofSize: 11, weight: .bold)
label.textColor = UIColor(hexStr: "#224E92") label.textColor = UIColor(hexStr: "#224E92")
view.addSubview(label) view.addSubview(label)
label.layoutChain label.layoutChain
.edgesVertical(4) .edgesVertical(4)
.edgesHorzontal(8) .edgesHorzontal(8)
return view return view
}() }()
lazy var cornerView: UIView = { lazy var cornerView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = UIColor(hexStr: "#F5FBFB") view.backgroundColor = UIColor(hexStr: "#F5FBFB")
view.clipsToBounds = false view.clipsToBounds = false
return view return view
}() }()
/// ///
lazy var expenseCollectionView: UICollectionView = { lazy var expenseCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout() let layout = UICollectionViewFlowLayout()
@ -222,14 +222,14 @@ class VipRechargeView: UIView {
layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
layout.minimumLineSpacing = spacing layout.minimumLineSpacing = spacing
layout.scrollDirection = .horizontal layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear cv.backgroundColor = .clear
cv.showsHorizontalScrollIndicator = false cv.showsHorizontalScrollIndicator = false
cv.register(ExpenseCell.self) cv.register(ExpenseCell.self)
return cv return cv
}() }()
/// ///
lazy var vipRightsView: UIView = { lazy var vipRightsView: UIView = {
let view = UIView() let view = UIView()
@ -237,11 +237,11 @@ class VipRechargeView: UIView {
view.cornerRadius = 10 view.cornerRadius = 10
return view return view
}() }()
lazy var vipRightsTitleView: UIView = { lazy var vipRightsTitleView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
let titleLab = UILabel() let titleLab = UILabel()
titleLab.text = "会员权益" titleLab.text = "会员权益"
titleLab.font = .systemFont(ofSize: 14, weight: .bold) titleLab.font = .systemFont(ofSize: 14, weight: .bold)
@ -251,7 +251,7 @@ class VipRechargeView: UIView {
titleLab.layoutChain titleLab.layoutChain
.edgesVertical() .edgesVertical()
.centerX() .centerX()
let leftLine1 = UIView() let leftLine1 = UIView()
leftLine1.backgroundColor = UIColor(hexStr: "#B7F34E") leftLine1.backgroundColor = UIColor(hexStr: "#B7F34E")
leftLine1.cornerRadius = 1 leftLine1.cornerRadius = 1
@ -261,7 +261,7 @@ class VipRechargeView: UIView {
.width(2) .width(2)
.height(10) .height(10)
.centerY() .centerY()
let leftLine2 = UIView() let leftLine2 = UIView()
leftLine2.backgroundColor = UIColor(hexStr: "#B7F34E") leftLine2.backgroundColor = UIColor(hexStr: "#B7F34E")
leftLine2.cornerRadius = 1 leftLine2.cornerRadius = 1
@ -271,7 +271,7 @@ class VipRechargeView: UIView {
.width(2) .width(2)
.edgesVertical() .edgesVertical()
.rightToLeftOfView(titleLab, offset: -10) .rightToLeftOfView(titleLab, offset: -10)
let leftLine3 = UIView() let leftLine3 = UIView()
leftLine3.backgroundColor = UIColor(hexStr: "#B7F34E") leftLine3.backgroundColor = UIColor(hexStr: "#B7F34E")
leftLine3.cornerRadius = 1 leftLine3.cornerRadius = 1
@ -280,7 +280,7 @@ class VipRechargeView: UIView {
.leftToRightOfView(titleLab, offset: 10) .leftToRightOfView(titleLab, offset: 10)
.width(2) .width(2)
.edgesVertical() .edgesVertical()
let leftLine4 = UIView() let leftLine4 = UIView()
leftLine4.backgroundColor = UIColor(hexStr: "#B7F34E") leftLine4.backgroundColor = UIColor(hexStr: "#B7F34E")
leftLine4.cornerRadius = 1 leftLine4.cornerRadius = 1
@ -290,195 +290,195 @@ class VipRechargeView: UIView {
.width(2) .width(2)
.height(10) .height(10)
.centerY() .centerY()
return view return view
}() }()
lazy var groupCountView: UIView = { lazy var groupCountView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
// //
let createCountView = UIView() let createCountView = UIView()
createCountView.backgroundColor = .clear createCountView.backgroundColor = .clear
let createIcon = UIImageView(image: UIImage(named: "VipRecharge/create_count")) let createIcon = UIImageView(image: UIImage(named: "VipRecharge/create_count"))
let createBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg")) let createBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg"))
let createUnitLab = UILabel() let createUnitLab = UILabel()
createUnitLab.text = "" createUnitLab.text = ""
createUnitLab.font = .systemFont(ofSize: 10, weight: .medium) createUnitLab.font = .systemFont(ofSize: 10, weight: .medium)
createUnitLab.textColor = UIColor(hexStr: "#1A1A1A") createUnitLab.textColor = UIColor(hexStr: "#1A1A1A")
let createTitleLab = UILabel() let createTitleLab = UILabel()
createTitleLab.text = "创建圈子" createTitleLab.text = "创建圈子"
createTitleLab.font = .systemFont(ofSize: 12, weight: .medium) createTitleLab.font = .systemFont(ofSize: 12, weight: .medium)
createTitleLab.textColor = UIColor(hexStr: "#1A1A1A") createTitleLab.textColor = UIColor(hexStr: "#1A1A1A")
createCountView.addSubview(createIcon) createCountView.addSubview(createIcon)
createCountView.addSubview(createBgImg) createCountView.addSubview(createBgImg)
createCountView.addSubview(createUnitLab) createCountView.addSubview(createUnitLab)
createCountView.addSubview(createTitleLab) createCountView.addSubview(createTitleLab)
createCountView.addSubview(createCountLab) createCountView.addSubview(createCountLab)
createIcon.layoutChain createIcon.layoutChain
.top() .top()
.centerX() .centerX()
.width(34).height(34) .width(34).height(34)
createBgImg.layoutChain createBgImg.layoutChain
.top(8) .top(8)
.edges(excludingEdge: .top) .edges(excludingEdge: .top)
createCountLab.layoutChain createCountLab.layoutChain
.topToBottomOfView(createIcon, offset: 2) .topToBottomOfView(createIcon, offset: 2)
.centerX() .centerX()
createUnitLab.layoutChain createUnitLab.layoutChain
.leftToRightOfView(createCountLab) .leftToRightOfView(createCountLab)
.bottomToView(createCountLab, offset: 0) .bottomToView(createCountLab, offset: 0)
createTitleLab.layoutChain createTitleLab.layoutChain
.centerX() .centerX()
.bottom(4) .bottom(4)
// 线 // 线
let separator1 = UIImageView(image: UIImage(named: "VipRecharge/separator")) let separator1 = UIImageView(image: UIImage(named: "VipRecharge/separator"))
// //
let groupCountView = UIView() let groupCountView = UIView()
groupCountView.backgroundColor = .clear groupCountView.backgroundColor = .clear
let groupCountIcon = UIImageView(image: UIImage(named: "VipRecharge/member_count")) let groupCountIcon = UIImageView(image: UIImage(named: "VipRecharge/member_count"))
let groupCountBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg")) let groupCountBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg"))
let groupCountUnitLab = UILabel() let groupCountUnitLab = UILabel()
groupCountUnitLab.text = "" groupCountUnitLab.text = ""
groupCountUnitLab.font = .systemFont(ofSize: 10, weight: .medium) groupCountUnitLab.font = .systemFont(ofSize: 10, weight: .medium)
groupCountUnitLab.textColor = UIColor(hexStr: "#1A1A1A") groupCountUnitLab.textColor = UIColor(hexStr: "#1A1A1A")
let groupCountTitleLab = UILabel() let groupCountTitleLab = UILabel()
groupCountTitleLab.text = "圈子人数" groupCountTitleLab.text = "圈子人数"
groupCountTitleLab.font = .systemFont(ofSize: 12, weight: .medium) groupCountTitleLab.font = .systemFont(ofSize: 12, weight: .medium)
groupCountTitleLab.textColor = UIColor(hexStr: "#1A1A1A") groupCountTitleLab.textColor = UIColor(hexStr: "#1A1A1A")
groupCountView.addSubview(groupCountIcon) groupCountView.addSubview(groupCountIcon)
groupCountView.addSubview(groupCountBgImg) groupCountView.addSubview(groupCountBgImg)
groupCountView.addSubview(groupCountUnitLab) groupCountView.addSubview(groupCountUnitLab)
groupCountView.addSubview(groupCountTitleLab) groupCountView.addSubview(groupCountTitleLab)
groupCountView.addSubview(groupCountLab) groupCountView.addSubview(groupCountLab)
groupCountIcon.layoutChain groupCountIcon.layoutChain
.top() .top()
.centerX() .centerX()
.width(34).height(34) .width(34).height(34)
groupCountBgImg.layoutChain groupCountBgImg.layoutChain
.top(8) .top(8)
.edges(excludingEdge: .top) .edges(excludingEdge: .top)
groupCountLab.layoutChain groupCountLab.layoutChain
.topToBottomOfView(groupCountIcon, offset: 2) .topToBottomOfView(groupCountIcon, offset: 2)
.centerX() .centerX()
groupCountUnitLab.layoutChain groupCountUnitLab.layoutChain
.leftToRightOfView(groupCountLab) .leftToRightOfView(groupCountLab)
.bottomToView(groupCountLab, offset: 0) .bottomToView(groupCountLab, offset: 0)
groupCountTitleLab.layoutChain groupCountTitleLab.layoutChain
.centerX() .centerX()
.bottom(4) .bottom(4)
// 线 // 线
let separator2 = UIImageView(image: UIImage(named: "VipRecharge/separator")) let separator2 = UIImageView(image: UIImage(named: "VipRecharge/separator"))
// //
let joinCountView = UIView() let joinCountView = UIView()
joinCountView.backgroundColor = .clear joinCountView.backgroundColor = .clear
let joinCountIcon = UIImageView(image: UIImage(named: "VipRecharge/join_count")) let joinCountIcon = UIImageView(image: UIImage(named: "VipRecharge/join_count"))
let joinCountBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg")) let joinCountBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg"))
let joinCountUnitLab = UILabel() let joinCountUnitLab = UILabel()
joinCountUnitLab.text = "" joinCountUnitLab.text = ""
joinCountUnitLab.font = .systemFont(ofSize: 10, weight: .medium) joinCountUnitLab.font = .systemFont(ofSize: 10, weight: .medium)
joinCountUnitLab.textColor = UIColor(hexStr: "#1A1A1A") joinCountUnitLab.textColor = UIColor(hexStr: "#1A1A1A")
let joinCountTitleLab = UILabel() let joinCountTitleLab = UILabel()
joinCountTitleLab.text = "加入圈子" joinCountTitleLab.text = "加入圈子"
joinCountTitleLab.font = .systemFont(ofSize: 12, weight: .medium) joinCountTitleLab.font = .systemFont(ofSize: 12, weight: .medium)
joinCountTitleLab.textColor = UIColor(hexStr: "#1A1A1A") joinCountTitleLab.textColor = UIColor(hexStr: "#1A1A1A")
joinCountView.addSubview(joinCountIcon) joinCountView.addSubview(joinCountIcon)
joinCountView.addSubview(joinCountBgImg) joinCountView.addSubview(joinCountBgImg)
joinCountView.addSubview(joinCountUnitLab) joinCountView.addSubview(joinCountUnitLab)
joinCountView.addSubview(joinCountTitleLab) joinCountView.addSubview(joinCountTitleLab)
joinCountView.addSubview(joinCountLab) joinCountView.addSubview(joinCountLab)
joinCountIcon.layoutChain joinCountIcon.layoutChain
.top() .top()
.centerX() .centerX()
.width(34).height(34) .width(34).height(34)
joinCountBgImg.layoutChain joinCountBgImg.layoutChain
.top(8) .top(8)
.edges(excludingEdge: .top) .edges(excludingEdge: .top)
joinCountLab.layoutChain joinCountLab.layoutChain
.topToBottomOfView(joinCountIcon, offset: 2) .topToBottomOfView(joinCountIcon, offset: 2)
.centerX() .centerX()
joinCountUnitLab.layoutChain joinCountUnitLab.layoutChain
.leftToRightOfView(joinCountLab) .leftToRightOfView(joinCountLab)
.bottomToView(joinCountLab, offset: 0) .bottomToView(joinCountLab, offset: 0)
joinCountTitleLab.layoutChain joinCountTitleLab.layoutChain
.centerX() .centerX()
.bottom(4) .bottom(4)
view.addSubview(createCountView) view.addSubview(createCountView)
view.addSubview(separator1) view.addSubview(separator1)
view.addSubview(groupCountView) view.addSubview(groupCountView)
view.addSubview(separator2) view.addSubview(separator2)
view.addSubview(joinCountView) view.addSubview(joinCountView)
groupCountView.layoutChain groupCountView.layoutChain
.top() .top()
.centerX() .centerX()
.width(69) .width(69)
.height(90) .height(90)
.bottom() .bottom()
separator1.layoutChain separator1.layoutChain
.rightToLeftOfView(groupCountView, offset: -26) .rightToLeftOfView(groupCountView, offset: -26)
.centerY() .centerY()
.height(85) .height(85)
.width(1) .width(1)
createCountView.layoutChain createCountView.layoutChain
.topToView(groupCountView) .topToView(groupCountView)
.rightToLeftOfView(separator1, offset: -26) .rightToLeftOfView(separator1, offset: -26)
.width(69) .width(69)
.height(90) .height(90)
separator2.layoutChain separator2.layoutChain
.leftToRightOfView(groupCountView, offset: 26) .leftToRightOfView(groupCountView, offset: 26)
.centerY() .centerY()
.height(85) .height(85)
.width(1) .width(1)
joinCountView.layoutChain joinCountView.layoutChain
.topToView(groupCountView) .topToView(groupCountView)
.leftToRightOfView(separator2, offset: 26) .leftToRightOfView(separator2, offset: 26)
.width(69) .width(69)
.height(90) .height(90)
return view return view
}() }()
lazy var createCountLab: UILabel = { lazy var createCountLab: UILabel = {
let label = UILabel() let label = UILabel()
label.text = "0" label.text = "0"
@ -486,7 +486,7 @@ class VipRechargeView: UIView {
label.textColor = UIColor(hexStr: "#FF4F44") label.textColor = UIColor(hexStr: "#FF4F44")
return label return label
}() }()
lazy var groupCountLab: UILabel = { lazy var groupCountLab: UILabel = {
let label = UILabel() let label = UILabel()
label.text = "0" label.text = "0"
@ -494,7 +494,7 @@ class VipRechargeView: UIView {
label.textColor = UIColor(hexStr: "#FF4F44") label.textColor = UIColor(hexStr: "#FF4F44")
return label return label
}() }()
lazy var joinCountLab: UILabel = { lazy var joinCountLab: UILabel = {
let label = UILabel() let label = UILabel()
label.text = "0" label.text = "0"
@ -502,7 +502,7 @@ class VipRechargeView: UIView {
label.textColor = UIColor(hexStr: "#FF4F44") label.textColor = UIColor(hexStr: "#FF4F44")
return label return label
}() }()
lazy var vipRightsCollectionView: UICollectionView = { lazy var vipRightsCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout() let layout = UICollectionViewFlowLayout()
let spacing: CGFloat = 35 let spacing: CGFloat = 35
@ -513,20 +513,20 @@ class VipRechargeView: UIView {
layout.minimumLineSpacing = 20 layout.minimumLineSpacing = 20
layout.minimumInteritemSpacing = spacing layout.minimumInteritemSpacing = spacing
layout.scrollDirection = .vertical layout.scrollDirection = .vertical
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear cv.backgroundColor = .clear
cv.showsHorizontalScrollIndicator = false cv.showsHorizontalScrollIndicator = false
cv.register(VipRightsCell.self) cv.register(VipRightsCell.self)
return cv return cv
}() }()
lazy var agreementLab: UILabel = { lazy var agreementLab: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .systemFont(ofSize: 10, weight: .medium) label.font = .systemFont(ofSize: 10, weight: .medium)
label.textColor = UIColor(hexStr: "#767676") label.textColor = UIColor(hexStr: "#767676")
label.isUserInteractionEnabled = true label.isUserInteractionEnabled = true
let text = "*开通前请阅读《会员服务协议》" let text = "*开通前请阅读《会员服务协议》"
let attr = NSMutableAttributedString(string: text) let attr = NSMutableAttributedString(string: text)
let range = (text as NSString).range(of: "《会员服务协议》") let range = (text as NSString).range(of: "《会员服务协议》")
@ -534,7 +534,7 @@ class VipRechargeView: UIView {
label.attributedText = attr label.attributedText = attr
return label return label
}() }()
lazy var tipsLab: UILabel = { lazy var tipsLab: UILabel = {
let label = UILabel() let label = UILabel()
label.text = "*根据相关隐私保护的规定,本产品功能需双方下载并授权同意后再使用,请在自己的设备上使用,不得在未经过对方同意和授权的情况下使用,仅限家庭/亲人/朋友/情侣等熟人间使用。" label.text = "*根据相关隐私保护的规定,本产品功能需双方下载并授权同意后再使用,请在自己的设备上使用,不得在未经过对方同意和授权的情况下使用,仅限家庭/亲人/朋友/情侣等熟人间使用。"
@ -543,7 +543,7 @@ class VipRechargeView: UIView {
label.numberOfLines = 0 label.numberOfLines = 0
return label return label
}() }()
lazy var payTypeStackView: UIStackView = { lazy var payTypeStackView: UIStackView = {
let stack = UIStackView() let stack = UIStackView()
stack.axis = .horizontal stack.axis = .horizontal
@ -562,6 +562,7 @@ class VipRechargeView: UIView {
let payTypeMap: [(String, String)] = types.compactMap { let payTypeMap: [(String, String)] = types.compactMap {
if $0 == "weixin" { return ("wechat", "微信支付") } if $0 == "weixin" { return ("wechat", "微信支付") }
if $0 == "alipay" { return ("alipay", "支付宝支付") } if $0 == "alipay" { return ("alipay", "支付宝支付") }
if $0 == "apple" { return ("apple", "苹果支付") }
return nil return nil
} }
guard !payTypeMap.isEmpty else { return } guard !payTypeMap.isEmpty else { return }
@ -622,30 +623,30 @@ class VipRechargeView: UIView {
view.layer.shadowOpacity = 0.06 view.layer.shadowOpacity = 0.06
return view return view
}() }()
lazy var payBtnView: UIView = { lazy var payBtnView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
let bgImg = UIImageView() let bgImg = UIImageView()
bgImg.image = UIImage(named: "VipRecharge/pay_bg") bgImg.image = UIImage(named: "VipRecharge/pay_bg")
view.addSubview(bgImg) view.addSubview(bgImg)
bgImg.layoutChain.edges() bgImg.layoutChain.edges()
let unlockLab = UILabel() let unlockLab = UILabel()
unlockLab.text = "解锁会员" unlockLab.text = "解锁会员"
unlockLab.font = .systemFont(ofSize: 16, weight: .heavy) unlockLab.font = .systemFont(ofSize: 16, weight: .heavy)
unlockLab.textColor = .white unlockLab.textColor = .white
view.addSubview(unlockLab) view.addSubview(unlockLab)
unlockLab.layoutChain.right(21).centerY() unlockLab.layoutChain.right(21).centerY()
return view return view
}() }()
lazy var payPriceView: UIView = { lazy var payPriceView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = .clear view.backgroundColor = .clear
let titleLab = UILabel() let titleLab = UILabel()
titleLab.text = "合计" titleLab.text = "合计"
titleLab.font = .systemFont(ofSize: 14, weight: .bold) titleLab.font = .systemFont(ofSize: 14, weight: .bold)
@ -654,7 +655,7 @@ class VipRechargeView: UIView {
titleLab.layoutChain titleLab.layoutChain
.left() .left()
.bottom() .bottom()
let symbolLab = UILabel() let symbolLab = UILabel()
symbolLab.text = "" symbolLab.text = ""
symbolLab.font = .systemFont(ofSize: 12, weight: .heavy) symbolLab.font = .systemFont(ofSize: 12, weight: .heavy)
@ -663,21 +664,21 @@ class VipRechargeView: UIView {
symbolLab.layoutChain symbolLab.layoutChain
.leftToRightOfView(titleLab) .leftToRightOfView(titleLab)
.bottomToView(titleLab) .bottomToView(titleLab)
view.addSubview(priceLab) view.addSubview(priceLab)
priceLab.layoutChain priceLab.layoutChain
.leftToRightOfView(symbolLab) .leftToRightOfView(symbolLab)
.bottomToView(titleLab, offset: 3) .bottomToView(titleLab, offset: 3)
view.addSubview(discountLab) view.addSubview(discountLab)
discountLab.layoutChain discountLab.layoutChain
.leftToRightOfView(priceLab, offset: 5) .leftToRightOfView(priceLab, offset: 5)
.bottomToView(titleLab) .bottomToView(titleLab)
.right() .right()
return view return view
}() }()
lazy var priceLab: UILabel = { lazy var priceLab: UILabel = {
let label = UILabel() let label = UILabel()
label.text = "0" label.text = "0"
@ -685,7 +686,7 @@ class VipRechargeView: UIView {
label.textColor = UIColor(hexStr: "#FF3B05") label.textColor = UIColor(hexStr: "#FF3B05")
return label return label
}() }()
/// ///
func animatePrice(to newValue: String) { func animatePrice(to newValue: String) {
guard let target = Double(newValue) else { guard let target = Double(newValue) else {
@ -722,18 +723,18 @@ class VipRechargeView: UIView {
label.textColor = UIColor(hexStr: "#1A1A1A") label.textColor = UIColor(hexStr: "#1A1A1A")
return label return label
}() }()
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
cornerView.setNeedsLayout() cornerView.setNeedsLayout()
cornerView.layoutIfNeeded() cornerView.layoutIfNeeded()
cornerView.setCornerRadius(corners: [.topLeft, .topRight], withCornerRadii: CGSize(width: 10, height: 10)) cornerView.setCornerRadius(corners: [.topLeft, .topRight], withCornerRadii: CGSize(width: 10, height: 10))
vipRightsDetailView.setNeedsLayout() vipRightsDetailView.setNeedsLayout()
vipRightsDetailView.layoutIfNeeded() vipRightsDetailView.layoutIfNeeded()
vipRightsDetailView.setCornerRadius(corners: [.topLeft, .bottomLeft], withCornerRadii: CGSize(width: 6, height: 6)) vipRightsDetailView.setCornerRadius(corners: [.topLeft, .bottomLeft], withCornerRadii: CGSize(width: 6, height: 6))
} }
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
backgroundColor = .white backgroundColor = .white
@ -789,7 +790,7 @@ final class ExpenseCell: UICollectionViewCell {
label.textAlignment = .center label.textAlignment = .center
return label return label
}() }()
lazy var priceLab: UILabel = { lazy var priceLab: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .bold) label.font = .systemFont(ofSize: 12, weight: .bold)
@ -797,7 +798,7 @@ final class ExpenseCell: UICollectionViewCell {
label.textAlignment = .center label.textAlignment = .center
return label return label
}() }()
lazy var originPriceLab: UILabel = { lazy var originPriceLab: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .bold) label.font = .systemFont(ofSize: 12, weight: .bold)
@ -805,7 +806,7 @@ final class ExpenseCell: UICollectionViewCell {
label.textAlignment = .center label.textAlignment = .center
return label return label
}() }()
lazy var tipsLab: UILabel = { lazy var tipsLab: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .systemFont(ofSize: 10, weight: .bold) label.font = .systemFont(ofSize: 10, weight: .bold)
@ -839,15 +840,15 @@ final class ExpenseCell: UICollectionViewCell {
.centerX() .centerX()
.top(30) .top(30)
.edgesHorzontal(2) .edgesHorzontal(2)
priceLab.layoutChain priceLab.layoutChain
.centerY() .centerY()
.edgesHorzontal(2) .edgesHorzontal(2)
originPriceLab.layoutChain originPriceLab.layoutChain
.topToBottomOfView(priceLab, offset: 5) .topToBottomOfView(priceLab, offset: 5)
.edgesHorzontal(2) .edgesHorzontal(2)
tipsLab.layoutChain tipsLab.layoutChain
.edgesHorzontal(2) .edgesHorzontal(2)
.bottom(7) .bottom(7)
@ -887,7 +888,7 @@ final class ExpenseCell: UICollectionViewCell {
animations() animations()
} }
} }
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
tagBadgeView.setNeedsLayout() tagBadgeView.setNeedsLayout()
@ -898,12 +899,12 @@ final class ExpenseCell: UICollectionViewCell {
// MARK: - ExpenseCell // MARK: - ExpenseCell
final class VipRightsCell: UICollectionViewCell { final class VipRightsCell: UICollectionViewCell {
func configure(_ model: VipRightsModel) { func configure(_ model: VipRightsModel) {
iconView.image = UIImage(named: model.icon) iconView.image = UIImage(named: model.icon)
titleLabel.text = model.title titleLabel.text = model.title
} }
private let titleLabel: UILabel = { private let titleLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .medium) label.font = .systemFont(ofSize: 12, weight: .medium)
@ -917,7 +918,7 @@ final class VipRightsCell: UICollectionViewCell {
iv.contentMode = .scaleAspectFill iv.contentMode = .scaleAspectFill
return iv return iv
}() }()
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
contentView.addSubview(titleLabel) contentView.addSubview(titleLabel)
@ -927,7 +928,7 @@ final class VipRightsCell: UICollectionViewCell {
.top().centerX() .top().centerX()
.width(40) .width(40)
.height(40) .height(40)
titleLabel.layoutChain titleLabel.layoutChain
.edgesHorzontal() .edgesHorzontal()
.bottom() .bottom()

View File

@ -10,7 +10,7 @@ import Moya
struct OrderService { struct OrderService {
static let disposeBag = DisposeBag() static let disposeBag = DisposeBag()
/// ///
/// - Parameters: /// - Parameters:
/// - type: member /// - type: member
@ -20,7 +20,7 @@ struct OrderService {
.map(VipExpenseResponse.self) .map(VipExpenseResponse.self)
.asObservable() .asObservable()
} }
/// ///
/// - Parameters: /// - Parameters:
/// - goodsId: ID /// - goodsId: ID
@ -33,4 +33,12 @@ struct OrderService {
.map(ResponseModel.self) .map(ResponseModel.self)
.asObservable() .asObservable()
} }
/// Apple
static func appleNotify(receipt: String, productId: String, transactionId: String) -> Observable<Void> {
let api = OrderAPI.appleNotify(receipt: receipt, productId: productId, transactionId: transactionId).multiTarget
return APIProvider.request(token: api)
.map { _ in () }
.asObservable()
}
} }