|
|
@ -186,6 +186,10 @@
|
||||||
3062E8BC2FCEAC7100CEF511 /* CreateGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8BB2FCEAC7100CEF511 /* CreateGroupVC.swift */; };
|
3062E8BC2FCEAC7100CEF511 /* CreateGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8BB2FCEAC7100CEF511 /* CreateGroupVC.swift */; };
|
||||||
3062E8BE2FCEBD0E00CEF511 /* GroupIconListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8BD2FCEBD0E00CEF511 /* GroupIconListVC.swift */; };
|
3062E8BE2FCEBD0E00CEF511 /* GroupIconListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8BD2FCEBD0E00CEF511 /* GroupIconListVC.swift */; };
|
||||||
3062E8C02FCED7BB00CEF511 /* GroupIconListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8BF2FCED7BB00CEF511 /* GroupIconListView.swift */; };
|
3062E8C02FCED7BB00CEF511 /* GroupIconListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8BF2FCED7BB00CEF511 /* GroupIconListView.swift */; };
|
||||||
|
3062E8C22FCFB86800CEF511 /* CreateGroupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8C12FCFB86800CEF511 /* CreateGroupViewModel.swift */; };
|
||||||
|
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8C32FCFC90F00CEF511 /* CreateGroupVipPopView.swift */; };
|
||||||
|
3062E8C72FCFD02F00CEF511 /* VipRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8C62FCFD02F00CEF511 /* VipRechargeView.swift */; };
|
||||||
|
3062E8C92FCFD03B00CEF511 /* VipRechargeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3062E8C82FCFD03B00CEF511 /* VipRechargeVC.swift */; };
|
||||||
30A7A9112FCAEE3D00105780 /* GroupListPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */; };
|
30A7A9112FCAEE3D00105780 /* GroupListPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */; };
|
||||||
30BAB84D2FCD2FDE00C33B5C /* InviteJoinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB84C2FCD2FDE00C33B5C /* InviteJoinView.swift */; };
|
30BAB84D2FCD2FDE00C33B5C /* InviteJoinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB84C2FCD2FDE00C33B5C /* InviteJoinView.swift */; };
|
||||||
30BAB84F2FCD2FED00C33B5C /* InviteJoinVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB84E2FCD2FED00C33B5C /* InviteJoinVC.swift */; };
|
30BAB84F2FCD2FED00C33B5C /* InviteJoinVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB84E2FCD2FED00C33B5C /* InviteJoinVC.swift */; };
|
||||||
|
|
@ -195,6 +199,8 @@
|
||||||
30BAB8652FCD718A00C33B5C /* JoinGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB8642FCD718A00C33B5C /* JoinGroupView.swift */; };
|
30BAB8652FCD718A00C33B5C /* JoinGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB8642FCD718A00C33B5C /* JoinGroupView.swift */; };
|
||||||
30BAB8682FCD750E00C33B5C /* Mask_group@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30BAB8672FCD750E00C33B5C /* Mask_group@3x.png */; };
|
30BAB8682FCD750E00C33B5C /* Mask_group@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30BAB8672FCD750E00C33B5C /* Mask_group@3x.png */; };
|
||||||
30BAB8692FCD750E00C33B5C /* Mask_group@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30BAB8662FCD750E00C33B5C /* Mask_group@2x.png */; };
|
30BAB8692FCD750E00C33B5C /* Mask_group@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30BAB8662FCD750E00C33B5C /* Mask_group@2x.png */; };
|
||||||
|
30DC18522FD009CD0041DCD1 /* VipExpenseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */; };
|
||||||
|
30DC18542FD00C4A0041DCD1 /* VipRechargeVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */; };
|
||||||
C49B37352A45A02C28FF41BA /* Pods_QuickLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1C77B42994F352054070537 /* Pods_QuickLocation.framework */; };
|
C49B37352A45A02C28FF41BA /* Pods_QuickLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1C77B42994F352054070537 /* Pods_QuickLocation.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
|
@ -386,6 +392,10 @@
|
||||||
3062E8BB2FCEAC7100CEF511 /* CreateGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupVC.swift; sourceTree = "<group>"; };
|
3062E8BB2FCEAC7100CEF511 /* CreateGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupVC.swift; sourceTree = "<group>"; };
|
||||||
3062E8BD2FCEBD0E00CEF511 /* GroupIconListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupIconListVC.swift; sourceTree = "<group>"; };
|
3062E8BD2FCEBD0E00CEF511 /* GroupIconListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupIconListVC.swift; sourceTree = "<group>"; };
|
||||||
3062E8BF2FCED7BB00CEF511 /* GroupIconListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupIconListView.swift; sourceTree = "<group>"; };
|
3062E8BF2FCED7BB00CEF511 /* GroupIconListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupIconListView.swift; sourceTree = "<group>"; };
|
||||||
|
3062E8C12FCFB86800CEF511 /* CreateGroupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
3062E8C32FCFC90F00CEF511 /* CreateGroupVipPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupVipPopView.swift; sourceTree = "<group>"; };
|
||||||
|
3062E8C62FCFD02F00CEF511 /* VipRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipRechargeView.swift; sourceTree = "<group>"; };
|
||||||
|
3062E8C82FCFD03B00CEF511 /* VipRechargeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipRechargeVC.swift; sourceTree = "<group>"; };
|
||||||
30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupListPopView.swift; sourceTree = "<group>"; };
|
30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupListPopView.swift; sourceTree = "<group>"; };
|
||||||
30BAB84C2FCD2FDE00C33B5C /* InviteJoinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteJoinView.swift; sourceTree = "<group>"; };
|
30BAB84C2FCD2FDE00C33B5C /* InviteJoinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteJoinView.swift; sourceTree = "<group>"; };
|
||||||
30BAB84E2FCD2FED00C33B5C /* InviteJoinVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteJoinVC.swift; sourceTree = "<group>"; };
|
30BAB84E2FCD2FED00C33B5C /* InviteJoinVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteJoinVC.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -395,6 +405,8 @@
|
||||||
30BAB8642FCD718A00C33B5C /* JoinGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinGroupView.swift; sourceTree = "<group>"; };
|
30BAB8642FCD718A00C33B5C /* JoinGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinGroupView.swift; sourceTree = "<group>"; };
|
||||||
30BAB8662FCD750E00C33B5C /* Mask_group@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Mask_group@2x.png"; sourceTree = "<group>"; };
|
30BAB8662FCD750E00C33B5C /* Mask_group@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Mask_group@2x.png"; sourceTree = "<group>"; };
|
||||||
30BAB8672FCD750E00C33B5C /* Mask_group@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Mask_group@3x.png"; sourceTree = "<group>"; };
|
30BAB8672FCD750E00C33B5C /* Mask_group@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Mask_group@3x.png"; sourceTree = "<group>"; };
|
||||||
|
30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipExpenseModel.swift; sourceTree = "<group>"; };
|
||||||
|
30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipRechargeVM.swift; sourceTree = "<group>"; };
|
||||||
3E4359082FC48D26003470A5 /* QuickLocation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickLocation.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
3E4359082FC48D26003470A5 /* QuickLocation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickLocation.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
93647DF3683AA5E71EC2FB1A /* Pods-QuickLocation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickLocation.release.xcconfig"; path = "Target Support Files/Pods-QuickLocation/Pods-QuickLocation.release.xcconfig"; sourceTree = "<group>"; };
|
93647DF3683AA5E71EC2FB1A /* Pods-QuickLocation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickLocation.release.xcconfig"; path = "Target Support Files/Pods-QuickLocation/Pods-QuickLocation.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
D1C77B42994F352054070537 /* Pods_QuickLocation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QuickLocation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D1C77B42994F352054070537 /* Pods_QuickLocation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QuickLocation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
|
@ -851,6 +863,7 @@
|
||||||
305A76352FCA8C7000227D26 /* Map */,
|
305A76352FCA8C7000227D26 /* Map */,
|
||||||
305A76392FCA8C7000227D26 /* Mine */,
|
305A76392FCA8C7000227D26 /* Mine */,
|
||||||
305A798E2FCAC5F600227D26 /* InviteMember */,
|
305A798E2FCAC5F600227D26 /* InviteMember */,
|
||||||
|
3062E8C52FCFD01000CEF511 /* VipRecharge */,
|
||||||
3062E8B32FCE6BA400CEF511 /* Scan */,
|
3062E8B32FCE6BA400CEF511 /* Scan */,
|
||||||
);
|
);
|
||||||
path = Section;
|
path = Section;
|
||||||
|
|
@ -1032,12 +1045,25 @@
|
||||||
children = (
|
children = (
|
||||||
3062E8BB2FCEAC7100CEF511 /* CreateGroupVC.swift */,
|
3062E8BB2FCEAC7100CEF511 /* CreateGroupVC.swift */,
|
||||||
3062E8B92FCEAC6500CEF511 /* CreateGroupView.swift */,
|
3062E8B92FCEAC6500CEF511 /* CreateGroupView.swift */,
|
||||||
|
3062E8C12FCFB86800CEF511 /* CreateGroupViewModel.swift */,
|
||||||
3062E8BD2FCEBD0E00CEF511 /* GroupIconListVC.swift */,
|
3062E8BD2FCEBD0E00CEF511 /* GroupIconListVC.swift */,
|
||||||
3062E8BF2FCED7BB00CEF511 /* GroupIconListView.swift */,
|
3062E8BF2FCED7BB00CEF511 /* GroupIconListView.swift */,
|
||||||
|
3062E8C32FCFC90F00CEF511 /* CreateGroupVipPopView.swift */,
|
||||||
);
|
);
|
||||||
path = CreateGroup;
|
path = CreateGroup;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
3062E8C52FCFD01000CEF511 /* VipRecharge */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3062E8C82FCFD03B00CEF511 /* VipRechargeVC.swift */,
|
||||||
|
3062E8C62FCFD02F00CEF511 /* VipRechargeView.swift */,
|
||||||
|
30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */,
|
||||||
|
30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */,
|
||||||
|
);
|
||||||
|
path = VipRecharge;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
30BAB84B2FCD2FA400C33B5C /* InviteJoin */ = {
|
30BAB84B2FCD2FA400C33B5C /* InviteJoin */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -1257,6 +1283,7 @@
|
||||||
305A768A2FCA8C7000227D26 /* Single+Response.swift in Sources */,
|
305A768A2FCA8C7000227D26 /* Single+Response.swift in Sources */,
|
||||||
305A768B2FCA8C7000227D26 /* API.swift in Sources */,
|
305A768B2FCA8C7000227D26 /* API.swift in Sources */,
|
||||||
305A768C2FCA8C7000227D26 /* APIProvider.swift in Sources */,
|
305A768C2FCA8C7000227D26 /* APIProvider.swift in Sources */,
|
||||||
|
3062E8C92FCFD03B00CEF511 /* VipRechargeVC.swift in Sources */,
|
||||||
305A768D2FCA8C7000227D26 /* AppNetworkConfig.swift in Sources */,
|
305A768D2FCA8C7000227D26 /* AppNetworkConfig.swift in Sources */,
|
||||||
305A768E2FCA8C7000227D26 /* SignPlugin.swift in Sources */,
|
305A768E2FCA8C7000227D26 /* SignPlugin.swift in Sources */,
|
||||||
305A768F2FCA8C7000227D26 /* SystemAPI.swift in Sources */,
|
305A768F2FCA8C7000227D26 /* SystemAPI.swift in Sources */,
|
||||||
|
|
@ -1272,6 +1299,7 @@
|
||||||
305A76992FCA8C7000227D26 /* ImagePickerPopup.swift in Sources */,
|
305A76992FCA8C7000227D26 /* ImagePickerPopup.swift in Sources */,
|
||||||
305A769A2FCA8C7000227D26 /* PopupAnimator.swift in Sources */,
|
305A769A2FCA8C7000227D26 /* PopupAnimator.swift in Sources */,
|
||||||
3062E8BE2FCEBD0E00CEF511 /* GroupIconListVC.swift in Sources */,
|
3062E8BE2FCEBD0E00CEF511 /* GroupIconListVC.swift in Sources */,
|
||||||
|
3062E8C22FCFB86800CEF511 /* CreateGroupViewModel.swift in Sources */,
|
||||||
305A769B2FCA8C7000227D26 /* PopupAnimators.swift in Sources */,
|
305A769B2FCA8C7000227D26 /* PopupAnimators.swift in Sources */,
|
||||||
305A769C2FCA8C7000227D26 /* PopupViewController.swift in Sources */,
|
305A769C2FCA8C7000227D26 /* PopupViewController.swift in Sources */,
|
||||||
305A769D2FCA8C7000227D26 /* PopupViewController+Extension.swift in Sources */,
|
305A769D2FCA8C7000227D26 /* PopupViewController+Extension.swift in Sources */,
|
||||||
|
|
@ -1287,6 +1315,7 @@
|
||||||
305A76A62FCA8C7000227D26 /* Int+Extension.swift in Sources */,
|
305A76A62FCA8C7000227D26 /* Int+Extension.swift in Sources */,
|
||||||
30A7A9112FCAEE3D00105780 /* GroupListPopView.swift in Sources */,
|
30A7A9112FCAEE3D00105780 /* GroupListPopView.swift in Sources */,
|
||||||
305A76A72FCA8C7000227D26 /* NSAttributedString+Extension.swift in Sources */,
|
305A76A72FCA8C7000227D26 /* NSAttributedString+Extension.swift in Sources */,
|
||||||
|
30DC18542FD00C4A0041DCD1 /* VipRechargeVM.swift in Sources */,
|
||||||
305A76A82FCA8C7000227D26 /* ObjectMapper+Extension.swift in Sources */,
|
305A76A82FCA8C7000227D26 /* ObjectMapper+Extension.swift in Sources */,
|
||||||
305A76A92FCA8C7000227D26 /* Optional+Extension.swift in Sources */,
|
305A76A92FCA8C7000227D26 /* Optional+Extension.swift in Sources */,
|
||||||
305A76AA2FCA8C7000227D26 /* Response+ObjectMapper.swift in Sources */,
|
305A76AA2FCA8C7000227D26 /* Response+ObjectMapper.swift in Sources */,
|
||||||
|
|
@ -1327,6 +1356,7 @@
|
||||||
305A76CC2FCA8C7000227D26 /* FileTools.swift in Sources */,
|
305A76CC2FCA8C7000227D26 /* FileTools.swift in Sources */,
|
||||||
305A76CD2FCA8C7000227D26 /* Permission.swift in Sources */,
|
305A76CD2FCA8C7000227D26 /* Permission.swift in Sources */,
|
||||||
305A76CE2FCA8C7000227D26 /* RouterManager.swift in Sources */,
|
305A76CE2FCA8C7000227D26 /* RouterManager.swift in Sources */,
|
||||||
|
3062E8C72FCFD02F00CEF511 /* VipRechargeView.swift in Sources */,
|
||||||
305A76CF2FCA8C7000227D26 /* CountDownService.swift in Sources */,
|
305A76CF2FCA8C7000227D26 /* CountDownService.swift in Sources */,
|
||||||
305A76D02FCA8C7000227D26 /* MoneyFormatter.swift in Sources */,
|
305A76D02FCA8C7000227D26 /* MoneyFormatter.swift in Sources */,
|
||||||
305A76D12FCA8C7000227D26 /* TimeSpecificNotificationManager.swift in Sources */,
|
305A76D12FCA8C7000227D26 /* TimeSpecificNotificationManager.swift in Sources */,
|
||||||
|
|
@ -1371,6 +1401,7 @@
|
||||||
305A76EF2FCA8C7000227D26 /* MineViewController.swift in Sources */,
|
305A76EF2FCA8C7000227D26 /* MineViewController.swift in Sources */,
|
||||||
305A76F02FCA8C7000227D26 /* MineViewModel.swift in Sources */,
|
305A76F02FCA8C7000227D26 /* MineViewModel.swift in Sources */,
|
||||||
305A76F12FCA8C7000227D26 /* SystemService.swift in Sources */,
|
305A76F12FCA8C7000227D26 /* SystemService.swift in Sources */,
|
||||||
|
30DC18522FD009CD0041DCD1 /* VipExpenseModel.swift in Sources */,
|
||||||
305A76F22FCA8C7000227D26 /* UserService.swift in Sources */,
|
305A76F22FCA8C7000227D26 /* UserService.swift in Sources */,
|
||||||
305A76F32FCA8C7000227D26 /* AutoLayout+NSLayoutConstraint.swift in Sources */,
|
305A76F32FCA8C7000227D26 /* AutoLayout+NSLayoutConstraint.swift in Sources */,
|
||||||
305A76F42FCA8C7000227D26 /* AutoLayout+UIView.swift in Sources */,
|
305A76F42FCA8C7000227D26 /* AutoLayout+UIView.swift in Sources */,
|
||||||
|
|
@ -1395,6 +1426,7 @@
|
||||||
305A77062FCA8C7000227D26 /* MXScrollViewController.m in Sources */,
|
305A77062FCA8C7000227D26 /* MXScrollViewController.m in Sources */,
|
||||||
305A77072FCA8C7000227D26 /* Helper.swift in Sources */,
|
305A77072FCA8C7000227D26 /* Helper.swift in Sources */,
|
||||||
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
|
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
|
||||||
|
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */,
|
||||||
305A77092FCA8C7000227D26 /* PageContentView.swift in Sources */,
|
305A77092FCA8C7000227D26 /* PageContentView.swift in Sources */,
|
||||||
305A770A2FCA8C7000227D26 /* PageStyle.swift in Sources */,
|
305A770A2FCA8C7000227D26 /* PageStyle.swift in Sources */,
|
||||||
305A770B2FCA8C7000227D26 /* PageTitleView.swift in Sources */,
|
305A770B2FCA8C7000227D26 /* PageTitleView.swift in Sources */,
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ enum GatewayStatusCode: Int {
|
||||||
case noAuthority = 500
|
case noAuthority = 500
|
||||||
// 审核中
|
// 审核中
|
||||||
case review = 201
|
case review = 201
|
||||||
|
/** ============== 业务 ============== */
|
||||||
|
// 您创建的圈子个数已达上限,请升级会员等级
|
||||||
|
case groupLimit = 20009
|
||||||
|
|
||||||
/** ============== 未知错误 ============== */
|
/** ============== 未知错误 ============== */
|
||||||
case unknownError = -9999
|
case unknownError = -9999
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,10 @@ enum SystemAPI {
|
||||||
/// - phone: 手机号
|
/// - phone: 手机号
|
||||||
case sendCode(phone: String)
|
case sendCode(phone: String)
|
||||||
|
|
||||||
|
/// 充值内容
|
||||||
|
/// - Parameters:
|
||||||
|
/// - type: 类型 member
|
||||||
|
case rechargeInfo(type: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SystemAPI: MultiTargetProtocol {
|
extension SystemAPI: MultiTargetProtocol {
|
||||||
|
|
@ -29,12 +32,14 @@ extension SystemAPI: MultiTargetProtocol {
|
||||||
return "api/user/config"
|
return "api/user/config"
|
||||||
case .sendCode:
|
case .sendCode:
|
||||||
return "api/user/sms/code"
|
return "api/user/sms/code"
|
||||||
|
case .rechargeInfo:
|
||||||
|
return "api/order/goods"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var method: Moya.Method {
|
var method: Moya.Method {
|
||||||
switch self {
|
switch self {
|
||||||
case .userConfig:
|
case .userConfig, .rechargeInfo:
|
||||||
return .get
|
return .get
|
||||||
case .sendCode:
|
case .sendCode:
|
||||||
return .post
|
return .post
|
||||||
|
|
@ -50,6 +55,11 @@ extension SystemAPI: MultiTargetProtocol {
|
||||||
var params = Parameters()
|
var params = Parameters()
|
||||||
params["phone"] = phone
|
params["phone"] = phone
|
||||||
return .requestParameters(parameters: params, encoding: JSONEncoding())
|
return .requestParameters(parameters: params, encoding: JSONEncoding())
|
||||||
|
|
||||||
|
case let .rechargeInfo(type):
|
||||||
|
var params = Parameters()
|
||||||
|
params["type"] = type
|
||||||
|
return .requestParameters(parameters: params, encoding: URLEncoding())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Group_1545@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Group_1545@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "upgrade_bg@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "upgrade_bg@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Group_2396@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Group_2396@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 431 KiB |
|
After Width: | Height: | Size: 753 KiB |
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "alipay@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "alipay@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "tick-circle@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "tick-circle@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/checkbox.imageset/tick-circle@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/checkbox.imageset/tick-circle@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
22
QuickLocation/Assets.xcassets/VipRecharge/checkbox_on.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "icon_yindao_change@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "icon_yindao_change@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/checkbox_on.imageset/icon_yindao_change@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/checkbox_on.imageset/icon_yindao_change@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Rectangle 42116@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Rectangle 42116@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/count_bg.imageset/Rectangle 42116@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/count_bg.imageset/Rectangle 42116@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
22
QuickLocation/Assets.xcassets/VipRecharge/create_count.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "create_count@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "create_count@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/create_count.imageset/create_count@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/create_count.imageset/create_count@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "expense@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "expense@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "expense_on@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "expense_on@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/expense_on.imageset/expense_on@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/expense_on.imageset/expense_on@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
22
QuickLocation/Assets.xcassets/VipRecharge/expense_tips.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Rectangle 868@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Rectangle 868@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/expense_tips.imageset/Rectangle 868@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 918 B |
BIN
QuickLocation/Assets.xcassets/VipRecharge/expense_tips.imageset/Rectangle 868@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "header_bg@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "header_bg@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/header_bg.imageset/header_bg@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/header_bg.imageset/header_bg@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 572 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "join_count@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "join_count@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/join_count.imageset/join_count@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/join_count.imageset/join_count@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
22
QuickLocation/Assets.xcassets/VipRecharge/member_count.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "member_count@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "member_count@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/member_count.imageset/member_count@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
QuickLocation/Assets.xcassets/VipRecharge/member_count.imageset/member_count@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "组 47671@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "组 47671@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Rectangle 42118@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Rectangle 42118@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
QuickLocation/Assets.xcassets/VipRecharge/separator.imageset/Rectangle 42118@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 636 B |
BIN
QuickLocation/Assets.xcassets/VipRecharge/separator.imageset/Rectangle 42118@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 784 B |
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "wechat@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "wechat@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -101,10 +101,10 @@ extension ApiManager {
|
||||||
handlePopView(message)
|
handlePopView(message)
|
||||||
default:
|
default:
|
||||||
/// 统一提示错误
|
/// 统一提示错误
|
||||||
// let code = GatewayStatusCode(rawValue: code ?? -9999) ?? .unknownError
|
let code = GatewayStatusCode(rawValue: code ?? -9999) ?? .unknownError
|
||||||
// if code == .unknownError, handle {
|
if code == .unknownError, handle {
|
||||||
DLToast.show(text: combineMessage)
|
DLToast.show(text: combineMessage)
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
return .failure(handleError(with: code, domain: "Data Error", message: combineMessage, data: response))
|
return .failure(handleError(with: code, domain: "Data Error", message: combineMessage, data: response))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ enum Route: String {
|
||||||
case scan = "scan"
|
case scan = "scan"
|
||||||
/// 圈子图标
|
/// 圈子图标
|
||||||
case groupIconList = "groupIconList"
|
case groupIconList = "groupIconList"
|
||||||
|
/// 会员充值
|
||||||
|
case vipRecharge = "vipRecharge"
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Route: RouterTarget {
|
extension Route: RouterTarget {
|
||||||
|
|
@ -127,11 +129,16 @@ extension AppRouter: AppRouterProtocol {
|
||||||
ScanVC()
|
ScanVC()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 扫一扫
|
// MARK: - 圈子图标列表
|
||||||
AppRouter.register(Route.groupIconList) { url, parameters in
|
AppRouter.register(Route.groupIconList) { url, parameters in
|
||||||
let vc = GroupIconListVC(iconIndex: parameters["iconIndex"].safeString)
|
let vc = GroupIconListVC(iconIndex: parameters["iconIndex"].safeString)
|
||||||
return vc
|
return vc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - 充值会员
|
||||||
|
AppRouter.register(Route.vipRecharge) { url, parameters in
|
||||||
|
VipRechargeVC()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
class CreateGroupVC: BaseViewController {
|
class CreateGroupVC: BaseViewController {
|
||||||
|
|
||||||
|
|
@ -17,16 +18,57 @@ class CreateGroupVC: BaseViewController {
|
||||||
rootView = CreateGroupView(frame: UIScreen.main.bounds)
|
rootView = CreateGroupView(frame: UIScreen.main.bounds)
|
||||||
view = rootView
|
view = rootView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var viewModel = CreateGroupViewModel()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
bindViewModel()
|
||||||
|
reactiveAction()
|
||||||
|
|
||||||
|
viewModel.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reactiveAction() {
|
||||||
rootView.groupIconInputView.rx.tapGesture.subscribe { _ in
|
rootView.groupIconInputView.rx.tapGesture.subscribe { _ in
|
||||||
let vc = GroupIconListVC(iconIndex: "1")
|
let vc = GroupIconListVC(iconIndex: "1")
|
||||||
vc.onSelectIcon = { index in
|
vc.onSelectIcon = { index in
|
||||||
|
self.viewModel.iconIndex = index
|
||||||
|
self.rootView.groupIconImgView.image = UIImage(named: "GroupIcon/\(index)")
|
||||||
}
|
}
|
||||||
self.navigationController?.pushViewController(vc, animated: true)
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
}.disposed(by: disposeBag)
|
}.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
rootView.submitBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
|
self.viewModel.requestCreateGroup()
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func bindViewModel() {
|
||||||
|
rootView.groupNameTF.rx.text.orEmpty
|
||||||
|
.bind(to: viewModel.groupName)
|
||||||
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
rootView.groupContentTV.rx.text.orEmpty
|
||||||
|
.bind(to: viewModel.groupDesc)
|
||||||
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
viewModel.output.sectionedItems
|
||||||
|
.bind(to: rootView.tagView.rx.items(dataSource: dataSource))
|
||||||
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
rootView.tagView.rx.modelSelected(String.self)
|
||||||
|
.subscribe(viewModel.cellAction.inputs)
|
||||||
|
.disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - dataSource
|
||||||
|
private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<GroupTagListSectionModel> = {
|
||||||
|
RxCollectionViewSectionedReloadDataSource<GroupTagListSectionModel> { datasource, collectionView, indexPath, item in
|
||||||
|
let cell: TagCell = collectionView.dequeueReusableCell(for: indexPath)
|
||||||
|
cell.configure(item, isSelected: self.viewModel.isSelected(tag: item))
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ class CreateGroupView: UIView {
|
||||||
var disposeBag = DisposeBag()
|
var disposeBag = DisposeBag()
|
||||||
|
|
||||||
private let limitCount = 50
|
private let limitCount = 50
|
||||||
private let tagList = ["私密", "游戏", "运动", "美食",
|
|
||||||
"自驾", "聚会", "旅行", "学习"]
|
|
||||||
|
|
||||||
private func setupRx() {
|
private func setupRx() {
|
||||||
groupNameTF.rx.text.orEmpty
|
groupNameTF.rx.text.orEmpty
|
||||||
|
|
@ -385,9 +383,7 @@ class CreateGroupView: UIView {
|
||||||
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
cv.backgroundColor = .clear
|
cv.backgroundColor = .clear
|
||||||
cv.isScrollEnabled = false
|
cv.isScrollEnabled = false
|
||||||
cv.register(TagCell.self, forCellWithReuseIdentifier: TagCell.reuseId)
|
cv.register(TagCell.self)
|
||||||
cv.delegate = self
|
|
||||||
cv.dataSource = self
|
|
||||||
return cv
|
return cv
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -415,7 +411,6 @@ class CreateGroupView: UIView {
|
||||||
backgroundColor = .white
|
backgroundColor = .white
|
||||||
setupUI()
|
setupUI()
|
||||||
setupRx()
|
setupRx()
|
||||||
tagView.reloadData()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
|
@ -423,26 +418,6 @@ class CreateGroupView: UIView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UICollectionViewDelegate, UICollectionViewDataSource
|
|
||||||
extension CreateGroupView: UICollectionViewDelegate, UICollectionViewDataSource {
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
||||||
return tagList.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TagCell.reuseId, for: indexPath) as! TagCell
|
|
||||||
cell.configure(tagList[indexPath.item], isSelected: false)
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
||||||
if let cell = collectionView.cellForItem(at: indexPath) as? TagCell {
|
|
||||||
cell.toggleSelection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - TagCell
|
// MARK: - TagCell
|
||||||
final class TagCell: UICollectionViewCell {
|
final class TagCell: UICollectionViewCell {
|
||||||
static let reuseId = "TagCell"
|
static let reuseId = "TagCell"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// CreateGroupViewModel.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
import SwiftyUserDefaults
|
||||||
|
|
||||||
|
typealias GroupTagListSectionModel = SectionModel<String, String>
|
||||||
|
|
||||||
|
class CreateGroupViewModel {
|
||||||
|
struct Input {
|
||||||
|
|
||||||
|
}
|
||||||
|
struct Output {
|
||||||
|
var sectionedItems: Observable<[GroupTagListSectionModel]>
|
||||||
|
}
|
||||||
|
|
||||||
|
let input: Input
|
||||||
|
let output: Output
|
||||||
|
var disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
private let sectionedItems = PublishSubject<[GroupTagListSectionModel]>()
|
||||||
|
|
||||||
|
private let tagList = ["私密", "游戏", "运动", "美食",
|
||||||
|
"自驾", "聚会", "旅行", "学习"]
|
||||||
|
|
||||||
|
var selectedTagList: [String] = [] {
|
||||||
|
didSet {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var iconIndex = 1
|
||||||
|
let groupName = BehaviorRelay<String>(value: "")
|
||||||
|
let groupDesc = BehaviorRelay<String>(value: "")
|
||||||
|
|
||||||
|
// MARK: - Cell点击
|
||||||
|
lazy var cellAction: Action<String, Void> = { this in
|
||||||
|
return Action { tag in
|
||||||
|
if let idx = this.selectedTagList.firstIndex(of: tag) {
|
||||||
|
this.selectedTagList.remove(at: idx)
|
||||||
|
} else {
|
||||||
|
this.selectedTagList.append(tag)
|
||||||
|
}
|
||||||
|
return .empty()
|
||||||
|
}
|
||||||
|
}(self)
|
||||||
|
|
||||||
|
/// 当前tag是否选中
|
||||||
|
func isSelected(tag: String) -> Bool {
|
||||||
|
return selectedTagList.first(where: { tag == $0 }) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 加载数据
|
||||||
|
func loadData() {
|
||||||
|
sectionedItems.onNext(tagList.mapSection())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Request
|
||||||
|
func requestCreateGroup() {
|
||||||
|
let name = groupName.value.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard !name.isEmpty else {
|
||||||
|
DLToast.show(text: "请输入圈子名称")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let desc = groupDesc.value.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard !desc.isEmpty else {
|
||||||
|
DLToast.show(text: "请输入圈子描述")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DLToast.showLoading()
|
||||||
|
GroupService.operate(opType: "create",
|
||||||
|
requestData: ["group_name": name,
|
||||||
|
"icon_index": iconIndex,
|
||||||
|
"description": desc,
|
||||||
|
"labels": selectedTagList]).subscribe(onNext: { response in
|
||||||
|
DLToast.showSuccess(text: "创建成功") {
|
||||||
|
AppRouter.shared.popOrDismiss()
|
||||||
|
}
|
||||||
|
}, onError: { (error) in
|
||||||
|
guard let code = error.underlyingError?.code else { return }
|
||||||
|
if code == 20009 { // "您创建的圈子个数已达上限,请升级会员等级"
|
||||||
|
CreateGroupVipPopView.show()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DLToast.show(text: error.localizedDescription)
|
||||||
|
}
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - init
|
||||||
|
init() {
|
||||||
|
input = Input()
|
||||||
|
output = Output(
|
||||||
|
sectionedItems: sectionedItems.asObservable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
//
|
||||||
|
// CreateGroupVipPopView.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
|
||||||
|
class CreateGroupVipPopView: UIView {
|
||||||
|
|
||||||
|
private static let shared = CreateGroupVipPopView(frame: CGRect(origin: .zero, size: kScreenSize))
|
||||||
|
|
||||||
|
var disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
static func show() {
|
||||||
|
guard let superView = kKeyWindow else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if CreateGroupVipPopView.shared.superview != nil {
|
||||||
|
CreateGroupVipPopView.shared.removeFromSuperview()
|
||||||
|
CreateGroupVipPopView.shared.bgView.frame = .zero
|
||||||
|
}
|
||||||
|
CreateGroupVipPopView.shared.bgView.alpha = 1
|
||||||
|
CreateGroupVipPopView.shared.bgView.frame = CGRect(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight)
|
||||||
|
superView.addSubview(CreateGroupVipPopView.shared)
|
||||||
|
superView.bringSubviewToFront(CreateGroupVipPopView.shared)
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.25) {
|
||||||
|
CreateGroupVipPopView.shared.bgView.alpha = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关闭
|
||||||
|
static func dismiss() {
|
||||||
|
guard CreateGroupVipPopView.shared.superview != nil else { return }
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseIn]) {
|
||||||
|
CreateGroupVipPopView.shared.bgView.alpha = 0
|
||||||
|
} completion: { _ in
|
||||||
|
CreateGroupVipPopView.shared.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupRx() {
|
||||||
|
upgradedBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
|
CreateGroupVipPopView.dismiss()
|
||||||
|
AppRouter.push(Route.vipRecharge)
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
|
closeBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
|
CreateGroupVipPopView.dismiss()
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var bgView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .black.withAlphaComponent(0.5)
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var vipImgView: UIImageView = {
|
||||||
|
let view = UIImageView()
|
||||||
|
view.image = UIImage(named: "Group/vip_pop")
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var upgradedBtn: UIButton = {
|
||||||
|
let btn = UIButton(type: .custom)
|
||||||
|
btn.backgroundColor = .clear
|
||||||
|
btn.setBackgroundImage(UIImage(named: "Group/upgrade_bg"), for: .normal)
|
||||||
|
return btn
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var closeBtn: UIButton = {
|
||||||
|
let btn = UIButton(type: .custom)
|
||||||
|
btn.backgroundColor = .clear
|
||||||
|
btn.setBackgroundImage(UIImage(named: "Group/close"), for: .normal)
|
||||||
|
btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 100, bottom: 100, right: 100)
|
||||||
|
return btn
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
backgroundColor = .clear
|
||||||
|
addSubview(bgView)
|
||||||
|
bgView.addSubview(vipImgView)
|
||||||
|
bgView.addSubview(upgradedBtn)
|
||||||
|
bgView.addSubview(closeBtn)
|
||||||
|
|
||||||
|
vipImgView.layoutChain
|
||||||
|
.centerY()
|
||||||
|
.edgesHorzontal(25)
|
||||||
|
.heightToWidth(814/648)
|
||||||
|
|
||||||
|
upgradedBtn.layoutChain
|
||||||
|
.topToBottomOfView(vipImgView, offset: -20)
|
||||||
|
.centerX()
|
||||||
|
.width(240)
|
||||||
|
.height(60)
|
||||||
|
|
||||||
|
closeBtn.layoutChain
|
||||||
|
.topToBottomOfView(upgradedBtn, offset: 15)
|
||||||
|
.centerX()
|
||||||
|
.width(22)
|
||||||
|
.height(22)
|
||||||
|
|
||||||
|
setupRx()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
|
||||||
class GroupIconListVC: BaseViewController {
|
class GroupIconListVC: BaseViewController {
|
||||||
|
|
||||||
|
|
@ -23,6 +25,13 @@ class GroupIconListVC: BaseViewController {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
rootView.selectedIndex = iconIndex.integer
|
rootView.selectedIndex = iconIndex.integer
|
||||||
rootView.iconCollectionView.delegate = self
|
rootView.iconCollectionView.delegate = self
|
||||||
|
|
||||||
|
rootView.doneBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
|
if let onSelectIcon = self.onSelectIcon {
|
||||||
|
onSelectIcon(self.rootView.selectedIndex)
|
||||||
|
AppRouter.shared.popOrDismiss()
|
||||||
|
}
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class GroupIconListView: UIView {
|
||||||
addSubview(navBarView)
|
addSubview(navBarView)
|
||||||
navBarView.addSubview(navTitleLabel)
|
navBarView.addSubview(navTitleLabel)
|
||||||
navBarView.addSubview(backBtn)
|
navBarView.addSubview(backBtn)
|
||||||
|
navBarView.addSubview(doneBtn)
|
||||||
addSubview(selectedIconView)
|
addSubview(selectedIconView)
|
||||||
addSubview(titleLab)
|
addSubview(titleLab)
|
||||||
addSubview(iconCollectionView)
|
addSubview(iconCollectionView)
|
||||||
|
|
@ -57,6 +58,10 @@ class GroupIconListView: UIView {
|
||||||
.width(24)
|
.width(24)
|
||||||
.height(24)
|
.height(24)
|
||||||
|
|
||||||
|
doneBtn.layoutChain
|
||||||
|
.centerY(navTitleLabel)
|
||||||
|
.right(15)
|
||||||
|
|
||||||
selectedIconView.layoutChain
|
selectedIconView.layoutChain
|
||||||
.topToBottomOfView(navBarView, offset: 30)
|
.topToBottomOfView(navBarView, offset: 30)
|
||||||
.centerX()
|
.centerX()
|
||||||
|
|
@ -71,6 +76,8 @@ class GroupIconListView: UIView {
|
||||||
.topToBottomOfView(titleLab, offset: 13)
|
.topToBottomOfView(titleLab, offset: 13)
|
||||||
.edgesHorzontal(40)
|
.edgesHorzontal(40)
|
||||||
.bottom(kSafeBottomMargin + 10)
|
.bottom(kSafeBottomMargin + 10)
|
||||||
|
|
||||||
|
doneBtn.sizeToFit()
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy var navBgView: UIImageView = {
|
lazy var navBgView: UIImageView = {
|
||||||
|
|
@ -102,6 +109,15 @@ class GroupIconListView: UIView {
|
||||||
return btn
|
return btn
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
lazy var doneBtn: UIButton = {
|
||||||
|
let btn = UIButton(type: .custom)
|
||||||
|
btn.setTitle("完成", for: .normal)
|
||||||
|
btn.setTitleColor(ThemeManager.shared.color.titleAuxColor, for: .normal)
|
||||||
|
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||||
|
btn.extendEdgeInsets = UIEdgeInsets(top: 54, left: 100, bottom: 100, right: 15)
|
||||||
|
return btn
|
||||||
|
}()
|
||||||
|
|
||||||
lazy var selectedIconView: UIImageView = {
|
lazy var selectedIconView: UIImageView = {
|
||||||
let view = UIImageView()
|
let view = UIImageView()
|
||||||
view.cornerRadius = 40
|
view.cornerRadius = 40
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
//
|
||||||
|
// VipExpenseModel.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ObjectMapper
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
|
struct VipExpenseResponse: BaseModelProtocol {
|
||||||
|
// 状态码
|
||||||
|
var code: String?
|
||||||
|
// 消息
|
||||||
|
var message: String?
|
||||||
|
//
|
||||||
|
var list: [VipExpenseModel] = []
|
||||||
|
|
||||||
|
init?(map: Map) {}
|
||||||
|
|
||||||
|
mutating func mapping(map: Map) {
|
||||||
|
code <- map["code"]
|
||||||
|
message <- map["msg"]
|
||||||
|
list <- map["data"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VipExpenseModel: Mappable, Equatable {
|
||||||
|
///
|
||||||
|
var goods_id: String = ""
|
||||||
|
/// 资费名字
|
||||||
|
var goods_name: String = ""
|
||||||
|
/// 资费
|
||||||
|
var price: String = ""
|
||||||
|
/// 资费原价
|
||||||
|
var origin_price: String = ""
|
||||||
|
/// 资费标签
|
||||||
|
var tips: String = ""
|
||||||
|
/// 资费说明
|
||||||
|
var tips2: String = ""
|
||||||
|
/// 单位 月1m 年 12m 得换算
|
||||||
|
var value: String = ""
|
||||||
|
var unit: String {
|
||||||
|
guard value.hasSuffix("m") else { return "" }
|
||||||
|
let monthStr = value.replacingOccurrences(of: "m", with: "")
|
||||||
|
guard !monthStr.isEmpty, let month = Int(monthStr) else { return "" }
|
||||||
|
|
||||||
|
if month >= 12 {
|
||||||
|
let num = month / 12
|
||||||
|
return num > 1 ? "/\(num)年" : "/年"
|
||||||
|
} else if month % 3 == 0 {
|
||||||
|
let num = month / 3
|
||||||
|
return num > 1 ? "/\(num)季" : "/季"
|
||||||
|
} else {
|
||||||
|
return month > 1 ? "/\(month)月" : "/月"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 支付方式
|
||||||
|
var pay_type: String = ""
|
||||||
|
|
||||||
|
/// 选中状态
|
||||||
|
var checked: Bool = false
|
||||||
|
|
||||||
|
init?(map: Map) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func mapping(map: Map) {
|
||||||
|
goods_id <- map["goods_id"]
|
||||||
|
goods_name <- map["goods_name"]
|
||||||
|
price <- map["price"]
|
||||||
|
origin_price <- map["origin_price"]
|
||||||
|
tips <- map["tips"]
|
||||||
|
tips2 <- map["tips2"]
|
||||||
|
value <- map["value"]
|
||||||
|
checked <- map["checked"]
|
||||||
|
pay_type <- map["pay_type"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VipExpenseModel: IdentifiableType {
|
||||||
|
public typealias Identity = String
|
||||||
|
|
||||||
|
public var identity: String {
|
||||||
|
return goods_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// VipRechargeVC.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
|
class VipRechargeVC: BaseViewController {
|
||||||
|
|
||||||
|
fileprivate var rootView: VipRechargeView!
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
rootView = VipRechargeView(frame: UIScreen.main.bounds)
|
||||||
|
view = rootView
|
||||||
|
}
|
||||||
|
|
||||||
|
private var viewModel = VipRechargeVM()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
bindViewModel()
|
||||||
|
reactiveAction()
|
||||||
|
requestRechargeInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func bindViewModel() {
|
||||||
|
viewModel.output.sectionedItems
|
||||||
|
.bind(to: rootView.expenseCollectionView.rx.items(dataSource: dataSource))
|
||||||
|
.disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reactiveAction() {
|
||||||
|
Observable.zip(
|
||||||
|
rootView.expenseCollectionView.rx.itemSelected,
|
||||||
|
rootView.expenseCollectionView.rx.modelSelected(VipExpenseModel.self)
|
||||||
|
).subscribe(onNext: { indexPath, model in
|
||||||
|
self.viewModel.selectedIndex = indexPath.row
|
||||||
|
self.rootView.setupPayTypes(model.pay_type)
|
||||||
|
self.rootView.animatePrice(to: model.price)
|
||||||
|
self.rootView.discountLab.text = self.viewModel.discountPriceString
|
||||||
|
self.viewModel.refreshData()
|
||||||
|
})
|
||||||
|
.disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - dataSource
|
||||||
|
private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<ExpenseListSectionModel> = {
|
||||||
|
RxCollectionViewSectionedReloadDataSource<ExpenseListSectionModel> { datasource, collectionView, indexPath, model in
|
||||||
|
let cell: ExpenseCell = collectionView.dequeueReusableCell(for: indexPath)
|
||||||
|
cell.configure(model: model, isSelected: self.viewModel.selectedIndex == indexPath.row)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - API
|
||||||
|
private func requestRechargeInfo() {
|
||||||
|
DLToast.showLoading()
|
||||||
|
SystemService.rechargeInfo(type: "member").subscribe(onNext: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.viewModel.loadData(list: response.list)
|
||||||
|
self.rootView.setupPayTypes(self.viewModel.payType)
|
||||||
|
self.rootView.animatePrice(to: self.viewModel.price)
|
||||||
|
self.rootView.discountLab.text = self.viewModel.discountPriceString
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
//
|
||||||
|
// VipRechargeVM.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
import SwiftyUserDefaults
|
||||||
|
|
||||||
|
typealias ExpenseListSectionModel = SectionModel<String, VipExpenseModel>
|
||||||
|
|
||||||
|
class VipRechargeVM {
|
||||||
|
struct Input {
|
||||||
|
|
||||||
|
}
|
||||||
|
struct Output {
|
||||||
|
var sectionedItems: Observable<[ExpenseListSectionModel]>
|
||||||
|
}
|
||||||
|
|
||||||
|
let input: Input
|
||||||
|
let output: Output
|
||||||
|
var disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
private let sectionedItems = PublishSubject<[ExpenseListSectionModel]>()
|
||||||
|
|
||||||
|
var selectedIndex: Int = -1
|
||||||
|
var list: [VipExpenseModel] = []
|
||||||
|
var payType: String {
|
||||||
|
guard list.count > 0 else { return "" }
|
||||||
|
return list[selectedIndex].pay_type
|
||||||
|
}
|
||||||
|
|
||||||
|
var price: String {
|
||||||
|
guard list.count > 0 else { return "" }
|
||||||
|
return list[selectedIndex].price
|
||||||
|
}
|
||||||
|
|
||||||
|
var discountPriceString: String {
|
||||||
|
guard list.count > 0 else { return "已优惠0元" }
|
||||||
|
|
||||||
|
let price = list[selectedIndex].price
|
||||||
|
let oPrice = list[selectedIndex].origin_price
|
||||||
|
|
||||||
|
guard oPrice.int >= price.int else { return "已优惠0元" }
|
||||||
|
|
||||||
|
let diff = oPrice.double - price.double
|
||||||
|
let diffStr = diff.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", diff) : String(diff)
|
||||||
|
return "已优惠\(diffStr)元"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 加载数据
|
||||||
|
func loadData(list: [VipExpenseModel]) {
|
||||||
|
self.list = list
|
||||||
|
selectedIndex = list.firstIndex(where: { $0.checked == true }) ?? 0
|
||||||
|
sectionedItems.onNext(list.mapSection())
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshData() {
|
||||||
|
sectionedItems.onNext(list.mapSection())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - init
|
||||||
|
init() {
|
||||||
|
input = Input()
|
||||||
|
output = Output(
|
||||||
|
sectionedItems: sectionedItems.asObservable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,684 @@
|
||||||
|
//
|
||||||
|
// VipRechargeView.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
|
||||||
|
class VipRechargeView: UIView {
|
||||||
|
|
||||||
|
var disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
private func setupRx() {
|
||||||
|
backBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
|
AppRouter.shared.popOrDismiss()
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
|
agreementLab.rx.tapGesture.subscribe { _ in
|
||||||
|
// TODO: 打开会员服务协议
|
||||||
|
}.disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupUI() {
|
||||||
|
addSubview(scrollView)
|
||||||
|
scrollView.addSubview(scrollContentView)
|
||||||
|
scrollContentView.addSubview(headerBgImgView)
|
||||||
|
scrollContentView.addSubview(cornerView)
|
||||||
|
scrollContentView.addSubview(expenseCollectionView)
|
||||||
|
scrollContentView.addSubview(vipRightsView)
|
||||||
|
scrollContentView.addSubview(agreementLab)
|
||||||
|
scrollContentView.addSubview(tipsLab)
|
||||||
|
|
||||||
|
vipRightsView.addSubview(vipRightsTitleView)
|
||||||
|
|
||||||
|
addSubview(bottomView)
|
||||||
|
bottomView.addSubview(payTypeStackView)
|
||||||
|
bottomView.addSubview(payBtnView)
|
||||||
|
bottomView.addSubview(payPriceView)
|
||||||
|
|
||||||
|
addSubview(navBarView)
|
||||||
|
navBarView.addSubview(navTitleLabel)
|
||||||
|
addSubview(backBtn)
|
||||||
|
|
||||||
|
navBarView.layoutChain
|
||||||
|
.edges(excludingEdge: .bottom)
|
||||||
|
.height(kNaviHeight)
|
||||||
|
|
||||||
|
navTitleLabel.layoutChain
|
||||||
|
.top(kStatusBarHeight + 12)
|
||||||
|
.centerY(backBtn)
|
||||||
|
.centerX()
|
||||||
|
|
||||||
|
backBtn.layoutChain
|
||||||
|
.top(kStatusBarHeight + 12)
|
||||||
|
.left(15)
|
||||||
|
.width(24)
|
||||||
|
.height(24)
|
||||||
|
|
||||||
|
bottomView.layoutChain
|
||||||
|
.edgesHorzontal()
|
||||||
|
.heightToWidth(144/375)
|
||||||
|
.bottom()
|
||||||
|
|
||||||
|
payTypeStackView.layoutChain
|
||||||
|
.top(25)
|
||||||
|
.edgesHorzontal(16)
|
||||||
|
.centerX()
|
||||||
|
|
||||||
|
payBtnView.layoutChain
|
||||||
|
.edgesHorzontal(16)
|
||||||
|
.heightToWidth(50/343)
|
||||||
|
.centerY()
|
||||||
|
|
||||||
|
payPriceView.layoutChain
|
||||||
|
.left(32)
|
||||||
|
.centerY(payBtnView, offset: -7)
|
||||||
|
.height(30)
|
||||||
|
|
||||||
|
scrollView.layoutChain
|
||||||
|
.edges(excludingEdge: .bottom)
|
||||||
|
.bottomToTopOfView(bottomView)
|
||||||
|
|
||||||
|
scrollContentView.layoutChain
|
||||||
|
.edges()
|
||||||
|
.widthToView(scrollView)
|
||||||
|
|
||||||
|
headerBgImgView.layoutChain
|
||||||
|
.top(-kStatusBarHeight)
|
||||||
|
.edgesHorzontal()
|
||||||
|
// .edges(excludingEdge: .bottom)
|
||||||
|
.heightToWidth(267/375)
|
||||||
|
|
||||||
|
cornerView.layoutChain
|
||||||
|
.topToBottomOfView(headerBgImgView, offset: -20)
|
||||||
|
.edges(excludingEdge: .top)
|
||||||
|
|
||||||
|
let expenseCollectionViewHeight = (kScreenWidth - 32 - 32) / 3 * (144/110) + 10
|
||||||
|
expenseCollectionView.layoutChain
|
||||||
|
.topToView(cornerView, offset: -43)
|
||||||
|
.edgesHorzontal()
|
||||||
|
.height(expenseCollectionViewHeight * 1.1)
|
||||||
|
|
||||||
|
vipRightsView.layoutChain
|
||||||
|
.topToBottomOfView(expenseCollectionView, offset: 18)
|
||||||
|
.edgesHorzontal(16)
|
||||||
|
.height(302)
|
||||||
|
|
||||||
|
vipRightsTitleView.layoutChain
|
||||||
|
.top(14)
|
||||||
|
.centerX()
|
||||||
|
|
||||||
|
agreementLab.layoutChain
|
||||||
|
.topToBottomOfView(vipRightsView, offset: 6)
|
||||||
|
.leftToView(vipRightsView)
|
||||||
|
.rightToView(vipRightsView)
|
||||||
|
|
||||||
|
tipsLab.layoutChain
|
||||||
|
.topToBottomOfView(agreementLab, offset: 4)
|
||||||
|
.leftToView(vipRightsView)
|
||||||
|
.rightToView(vipRightsView)
|
||||||
|
.bottom(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy var navBarView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .white
|
||||||
|
view.alpha = 0
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var navTitleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.text = "升级会员"
|
||||||
|
label.font = .systemFont(ofSize: 18, weight: .medium)
|
||||||
|
label.textColor = ThemeManager.shared.color.titleAuxColor
|
||||||
|
label.textAlignment = .center
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var backBtn: UIButton = {
|
||||||
|
let btn = UIButton(type: .custom)
|
||||||
|
btn.setImage(UIImage(named: "Common/back"), for: .normal)
|
||||||
|
btn.extendEdgeInsets = UIEdgeInsets(top: 54, left: 15, bottom: 100, right: 100)
|
||||||
|
return btn
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var scrollView: UIScrollView = {
|
||||||
|
let view = UIScrollView()
|
||||||
|
view.backgroundColor = UIColor(hexStr: "#F5FBFB")
|
||||||
|
view.showsVerticalScrollIndicator = false
|
||||||
|
view.delegate = self
|
||||||
|
view.bounces = false
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var scrollContentView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var headerBgImgView: UIImageView = {
|
||||||
|
let view = UIImageView()
|
||||||
|
view.image = UIImage(named: "VipRecharge/header_bg")
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var cornerView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = UIColor(hexStr: "#F5FBFB")
|
||||||
|
view.clipsToBounds = false
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// 资费
|
||||||
|
lazy var expenseCollectionView: UICollectionView = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
let spacing: CGFloat = 16
|
||||||
|
let cvWidth = kScreenWidth - 32
|
||||||
|
let itemW = (cvWidth - spacing * 2) / 3
|
||||||
|
layout.itemSize = CGSize(width: itemW, height: itemW * (144/110) + 10)
|
||||||
|
layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
|
||||||
|
layout.minimumLineSpacing = spacing
|
||||||
|
layout.scrollDirection = .horizontal
|
||||||
|
|
||||||
|
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
cv.backgroundColor = .clear
|
||||||
|
cv.showsHorizontalScrollIndicator = false
|
||||||
|
cv.register(ExpenseCell.self)
|
||||||
|
return cv
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// 会员权益
|
||||||
|
lazy var vipRightsView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .white
|
||||||
|
view.cornerRadius = 10
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var vipRightsTitleView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
|
let titleLab = UILabel()
|
||||||
|
titleLab.text = "会员权益"
|
||||||
|
titleLab.font = .systemFont(ofSize: 14, weight: .bold)
|
||||||
|
titleLab.textColor = UIColor(hexStr: "#3D3D3D")
|
||||||
|
titleLab.textAlignment = .center
|
||||||
|
view.addSubview(titleLab)
|
||||||
|
titleLab.layoutChain
|
||||||
|
.edgesVertical()
|
||||||
|
.centerX()
|
||||||
|
|
||||||
|
let leftLine1 = UIView()
|
||||||
|
leftLine1.backgroundColor = UIColor(hexStr: "#B7F34E")
|
||||||
|
leftLine1.cornerRadius = 1
|
||||||
|
view.addSubview(leftLine1)
|
||||||
|
leftLine1.layoutChain
|
||||||
|
.left()
|
||||||
|
.width(2)
|
||||||
|
.height(10)
|
||||||
|
.centerY()
|
||||||
|
|
||||||
|
let leftLine2 = UIView()
|
||||||
|
leftLine2.backgroundColor = UIColor(hexStr: "#B7F34E")
|
||||||
|
leftLine2.cornerRadius = 1
|
||||||
|
view.addSubview(leftLine2)
|
||||||
|
leftLine2.layoutChain
|
||||||
|
.leftToRightOfView(leftLine1, offset: 4)
|
||||||
|
.width(2)
|
||||||
|
.edgesVertical()
|
||||||
|
.rightToLeftOfView(titleLab, offset: -10)
|
||||||
|
|
||||||
|
let leftLine3 = UIView()
|
||||||
|
leftLine3.backgroundColor = UIColor(hexStr: "#B7F34E")
|
||||||
|
leftLine3.cornerRadius = 1
|
||||||
|
view.addSubview(leftLine3)
|
||||||
|
leftLine3.layoutChain
|
||||||
|
.leftToRightOfView(titleLab, offset: 10)
|
||||||
|
.width(2)
|
||||||
|
.edgesVertical()
|
||||||
|
|
||||||
|
let leftLine4 = UIView()
|
||||||
|
leftLine4.backgroundColor = UIColor(hexStr: "#B7F34E")
|
||||||
|
leftLine4.cornerRadius = 1
|
||||||
|
view.addSubview(leftLine4)
|
||||||
|
leftLine4.layoutChain
|
||||||
|
.leftToRightOfView(leftLine3, offset: 4)
|
||||||
|
.width(2)
|
||||||
|
.height(10)
|
||||||
|
.centerY()
|
||||||
|
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var groupCountView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
|
// 创建圈子
|
||||||
|
let createCountView = UIView()
|
||||||
|
createCountView.backgroundColor = .clear
|
||||||
|
|
||||||
|
let createIcon = UIImageView(image: UIImage(named: "VipRecharge/create_count"))
|
||||||
|
|
||||||
|
let createBgImg = UIImageView(image: UIImage(named: "VipRecharge/count_bg"))
|
||||||
|
|
||||||
|
let createUnitLab = UILabel()
|
||||||
|
createUnitLab.text = "个"
|
||||||
|
createUnitLab.font = .systemFont(ofSize: 10, weight: .medium)
|
||||||
|
createUnitLab.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
|
||||||
|
let createTitleLab = UILabel()
|
||||||
|
createTitleLab.text = "创建圈子"
|
||||||
|
createTitleLab.font = .systemFont(ofSize: 12, weight: .medium)
|
||||||
|
createTitleLab.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
|
||||||
|
createCountView.addSubview(createIcon)
|
||||||
|
createCountView.addSubview(createBgImg)
|
||||||
|
createCountView.addSubview(createUnitLab)
|
||||||
|
createCountView.addSubview(createTitleLab)
|
||||||
|
createCountView.addSubview(createCountLab)
|
||||||
|
|
||||||
|
createIcon.layoutChain
|
||||||
|
.top()
|
||||||
|
.centerX()
|
||||||
|
.width(34).height(34)
|
||||||
|
|
||||||
|
createBgImg.layoutChain
|
||||||
|
.top(8)
|
||||||
|
|
||||||
|
createCountLab.layoutChain
|
||||||
|
.topToBottomOfView(createIcon)
|
||||||
|
|
||||||
|
// 分隔线
|
||||||
|
let separator1 = UIImageView(image: UIImage(named: "VipRecharge/separator"))
|
||||||
|
|
||||||
|
// 圈子人数
|
||||||
|
|
||||||
|
let separator2 = UIImageView(image: UIImage(named: "VipRecharge/separator"))
|
||||||
|
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var createCountLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 24, weight: .bold)
|
||||||
|
label.textColor = UIColor(hexStr: "#FF4F44")
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var agreementLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 10, weight: .medium)
|
||||||
|
label.textColor = UIColor(hexStr: "#767676")
|
||||||
|
label.isUserInteractionEnabled = true
|
||||||
|
|
||||||
|
let text = "*开通前请阅读《会员服务协议》"
|
||||||
|
let attr = NSMutableAttributedString(string: text)
|
||||||
|
let range = (text as NSString).range(of: "《会员服务协议》")
|
||||||
|
attr.addAttribute(.foregroundColor, value: UIColor(hexStr: "#2DBBFF"), range: range)
|
||||||
|
label.attributedText = attr
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var tipsLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.text = "*根据相关隐私保护的规定,本产品功能需双方下载并授权同意后再使用,请在自己的设备上使用,不得在未经过对方同意和授权的情况下使用,仅限家庭/亲人/朋友/情侣等熟人间使用。"
|
||||||
|
label.font = .systemFont(ofSize: 10, weight: .medium)
|
||||||
|
label.textColor = UIColor(hexStr: "#767676")
|
||||||
|
label.numberOfLines = 0
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var payTypeStackView: UIStackView = {
|
||||||
|
let stack = UIStackView()
|
||||||
|
stack.axis = .horizontal
|
||||||
|
stack.spacing = 30
|
||||||
|
// stack.distribution = .fillEqually
|
||||||
|
stack.alignment = .center
|
||||||
|
return stack
|
||||||
|
}()
|
||||||
|
|
||||||
|
var selectedPayTypeTag: Int = 0
|
||||||
|
|
||||||
|
/// 根据 pay_type 字符串动态构建支付方式 (格式: "alipay,weixin")
|
||||||
|
func setupPayTypes(_ payTypeStr: String) {
|
||||||
|
payTypeStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||||
|
let types = payTypeStr.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
|
||||||
|
let payTypeMap: [(String, String)] = types.compactMap {
|
||||||
|
if $0 == "weixin" { return ("wechat", "微信支付") }
|
||||||
|
if $0 == "alipay" { return ("alipay", "支付宝支付") }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard !payTypeMap.isEmpty else { return }
|
||||||
|
|
||||||
|
selectedPayTypeTag = 0
|
||||||
|
for (idx, (icon, name)) in payTypeMap.enumerated() {
|
||||||
|
let isSelected = idx == 0
|
||||||
|
let view = makePayTypeView(tag: idx, icon: icon, name: name, isSelected: isSelected)
|
||||||
|
payTypeStackView.addArrangedSubview(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makePayTypeView(tag: Int, icon: String, name: String, isSelected: Bool) -> UIView {
|
||||||
|
let view = UIView()
|
||||||
|
view.tag = tag
|
||||||
|
view.isUserInteractionEnabled = true
|
||||||
|
|
||||||
|
let checkbox = UIImageView(image: UIImage(named: isSelected ? "VipRecharge/checkbox_on" : "VipRecharge/checkbox"))
|
||||||
|
checkbox.contentMode = .scaleAspectFit
|
||||||
|
checkbox.tag = 100
|
||||||
|
view.addSubview(checkbox)
|
||||||
|
checkbox.layoutChain.left().centerY().width(18).height(18)
|
||||||
|
|
||||||
|
let payIcon = UIImageView(image: UIImage(named: "VipRecharge/\(icon)"))
|
||||||
|
payIcon.contentMode = .scaleAspectFit
|
||||||
|
view.addSubview(payIcon)
|
||||||
|
payIcon.layoutChain.leftToRightOfView(checkbox, offset: 8).centerY().width(22).height(22)
|
||||||
|
|
||||||
|
let nameLab = UILabel()
|
||||||
|
nameLab.text = name
|
||||||
|
nameLab.font = .systemFont(ofSize: 14, weight: .medium)
|
||||||
|
nameLab.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
view.addSubview(nameLab)
|
||||||
|
nameLab.layoutChain.leftToRightOfView(payIcon, offset: 6).centerY()
|
||||||
|
|
||||||
|
let tap = UITapGestureRecognizer(target: self, action: #selector(onPayTypeTap(_:)))
|
||||||
|
view.addGestureRecognizer(tap)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func onPayTypeTap(_ gesture: UITapGestureRecognizer) {
|
||||||
|
guard let tag = gesture.view?.tag, tag != selectedPayTypeTag else { return }
|
||||||
|
selectedPayTypeTag = tag
|
||||||
|
for view in payTypeStackView.arrangedSubviews {
|
||||||
|
let isSelected = view.tag == tag
|
||||||
|
if let checkbox = view.viewWithTag(100) as? UIImageView {
|
||||||
|
checkbox.image = UIImage(named: isSelected ? "VipRecharge/checkbox_on" : "VipRecharge/checkbox")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy var bottomView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .white
|
||||||
|
view.layer.shadowColor = UIColor.black.cgColor
|
||||||
|
view.layer.shadowOffset = CGSize(width: 0, height: -3)
|
||||||
|
view.layer.shadowOpacity = 0.06
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var payBtnView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
|
let bgImg = UIImageView()
|
||||||
|
bgImg.image = UIImage(named: "VipRecharge/pay_bg")
|
||||||
|
view.addSubview(bgImg)
|
||||||
|
bgImg.layoutChain.edges()
|
||||||
|
|
||||||
|
let unlockLab = UILabel()
|
||||||
|
unlockLab.text = "解锁会员"
|
||||||
|
unlockLab.font = .systemFont(ofSize: 16, weight: .heavy)
|
||||||
|
unlockLab.textColor = .white
|
||||||
|
view.addSubview(unlockLab)
|
||||||
|
unlockLab.layoutChain.right(21).centerY()
|
||||||
|
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var payPriceView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
|
||||||
|
let titleLab = UILabel()
|
||||||
|
titleLab.text = "合计"
|
||||||
|
titleLab.font = .systemFont(ofSize: 14, weight: .bold)
|
||||||
|
titleLab.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
view.addSubview(titleLab)
|
||||||
|
titleLab.layoutChain
|
||||||
|
.left()
|
||||||
|
.bottom()
|
||||||
|
|
||||||
|
let symbolLab = UILabel()
|
||||||
|
symbolLab.text = "¥"
|
||||||
|
symbolLab.font = .systemFont(ofSize: 12, weight: .heavy)
|
||||||
|
symbolLab.textColor = UIColor(hexStr: "#FF3B05")
|
||||||
|
view.addSubview(symbolLab)
|
||||||
|
symbolLab.layoutChain
|
||||||
|
.leftToRightOfView(titleLab)
|
||||||
|
.bottomToView(titleLab)
|
||||||
|
|
||||||
|
view.addSubview(priceLab)
|
||||||
|
priceLab.layoutChain
|
||||||
|
.leftToRightOfView(symbolLab)
|
||||||
|
.bottomToView(titleLab, offset: 3)
|
||||||
|
|
||||||
|
view.addSubview(discountLab)
|
||||||
|
discountLab.layoutChain
|
||||||
|
.leftToRightOfView(priceLab, offset: 5)
|
||||||
|
.bottomToView(titleLab)
|
||||||
|
.right()
|
||||||
|
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var priceLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.text = "0"
|
||||||
|
label.font = .systemFont(ofSize: 24, weight: .heavy)
|
||||||
|
label.textColor = UIColor(hexStr: "#FF3B05")
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// 数字翻滚动画 — 每位数字独立向上或向下滚动
|
||||||
|
func animatePrice(to newValue: String) {
|
||||||
|
guard let target = Double(newValue) else {
|
||||||
|
priceLab.text = newValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let current = Double(priceLab.text ?? "0") ?? 0
|
||||||
|
let diff = target - current
|
||||||
|
let duration = 0.6
|
||||||
|
let steps = 30
|
||||||
|
var step = 0
|
||||||
|
|
||||||
|
Timer.scheduledTimer(withTimeInterval: duration / Double(steps), repeats: true) { [weak self] t in
|
||||||
|
step += 1
|
||||||
|
guard let self = self else { t.invalidate(); return }
|
||||||
|
if step >= steps {
|
||||||
|
t.invalidate()
|
||||||
|
self.priceLab.text = newValue
|
||||||
|
} else {
|
||||||
|
let progress = Double(step) / Double(steps)
|
||||||
|
// ease out + overshoot on increase
|
||||||
|
let eased = diff > 0
|
||||||
|
? 1 - pow(1 - progress, 3)
|
||||||
|
: pow(progress, 0.5)
|
||||||
|
let value = current + diff * eased
|
||||||
|
self.priceLab.text = String(format: "%.0f", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy var discountLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 14, weight: .bold)
|
||||||
|
label.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
cornerView.setNeedsLayout()
|
||||||
|
cornerView.layoutIfNeeded()
|
||||||
|
cornerView.setCornerRadius(corners: [.topLeft, .topRight], withCornerRadii: CGSize(width: 10, height: 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
backgroundColor = .white
|
||||||
|
setupUI()
|
||||||
|
setupRx()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ExpenseCell
|
||||||
|
final class ExpenseCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
private let tagBadgeView: UIView = {
|
||||||
|
let iv = UIView()
|
||||||
|
// iv.image = UIImage(named: "VipRecharge/expense_tips")
|
||||||
|
// iv.contentMode = .scaleAspectFill
|
||||||
|
iv.backgroundColor = UIColor(hexStr: "#FF6643")
|
||||||
|
iv.isHidden = true
|
||||||
|
return iv
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let tagLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 10, weight: .bold)
|
||||||
|
label.textColor = .white
|
||||||
|
label.textAlignment = .center
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let bgImgView: UIImageView = {
|
||||||
|
let iv = UIImageView()
|
||||||
|
iv.image = UIImage(named: "VipRecharge/expense")
|
||||||
|
iv.contentMode = .scaleAspectFill
|
||||||
|
return iv
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let titleLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 14, weight: .bold)
|
||||||
|
label.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
label.textAlignment = .center
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var priceLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 12, weight: .bold)
|
||||||
|
label.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
label.textAlignment = .center
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var originPriceLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 12, weight: .bold)
|
||||||
|
label.textColor = UIColor(hexStr: "#767676")
|
||||||
|
label.textAlignment = .center
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var tipsLab: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .systemFont(ofSize: 10, weight: .bold)
|
||||||
|
label.textColor = UIColor(hexStr: "#1A1A1A")
|
||||||
|
label.textAlignment = .center
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
contentView.addSubview(bgImgView)
|
||||||
|
contentView.addSubview(tagBadgeView)
|
||||||
|
tagBadgeView.addSubview(tagLabel)
|
||||||
|
contentView.addSubview(titleLab)
|
||||||
|
contentView.addSubview(priceLab)
|
||||||
|
contentView.addSubview(originPriceLab)
|
||||||
|
contentView.addSubview(tipsLab)
|
||||||
|
|
||||||
|
tagBadgeView.layoutChain
|
||||||
|
.top().left()
|
||||||
|
|
||||||
|
tagLabel.layoutChain
|
||||||
|
.edgesHorzontal(10)
|
||||||
|
.edgesVertical(3)
|
||||||
|
|
||||||
|
bgImgView.layoutChain
|
||||||
|
.top(10)
|
||||||
|
.left().right().bottom()
|
||||||
|
|
||||||
|
titleLab.layoutChain
|
||||||
|
.centerX()
|
||||||
|
.top(30)
|
||||||
|
.edgesHorzontal(2)
|
||||||
|
|
||||||
|
priceLab.layoutChain
|
||||||
|
.centerY()
|
||||||
|
.edgesHorzontal(2)
|
||||||
|
|
||||||
|
originPriceLab.layoutChain
|
||||||
|
.topToBottomOfView(priceLab, offset: 5)
|
||||||
|
.edgesHorzontal(2)
|
||||||
|
|
||||||
|
tipsLab.layoutChain
|
||||||
|
.edgesHorzontal(2)
|
||||||
|
.bottom(7)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(model: VipExpenseModel, isSelected: Bool) {
|
||||||
|
titleLab.text = model.goods_name
|
||||||
|
tagBadgeView.isHidden = model.tips.isEmpty
|
||||||
|
tagLabel.text = model.tips
|
||||||
|
tipsLab.text = model.tips2
|
||||||
|
let priceAttr = NSMutableAttributedString(string: "¥", attributes: [.font: UIFont.systemFont(ofSize: 12, weight: .medium)])
|
||||||
|
priceAttr.append(NSAttributedString(string: model.price, attributes: [.font: UIFont.systemFont(ofSize: 30, weight: .medium)]))
|
||||||
|
priceAttr.append(NSAttributedString(string: model.unit, attributes: [.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
|
||||||
|
priceLab.attributedText = priceAttr
|
||||||
|
originPriceLab.text = "¥" + model.origin_price
|
||||||
|
originPriceLab.setupStrikethroughStyle()
|
||||||
|
setSelected(isSelected, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSelected(_ selected: Bool, animated: Bool) {
|
||||||
|
let scale: CGFloat = selected ? 1.1 : 1.0
|
||||||
|
let imageName = selected ? "VipRecharge/expense_on" : "VipRecharge/expense"
|
||||||
|
bgImgView.image = UIImage(named: imageName)
|
||||||
|
titleLab.textColor = selected ? UIColor(hexStr: "#16B3FF") : UIColor(hexStr: "#1A1A1A")
|
||||||
|
priceLab.textColor = selected ? UIColor(hexStr: "#FF4F44") : UIColor(hexStr: "#1A1A1A")
|
||||||
|
|
||||||
|
let animations = {
|
||||||
|
self.transform = CGAffineTransform(scaleX: scale, y: scale)
|
||||||
|
}
|
||||||
|
if animated {
|
||||||
|
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: animations)
|
||||||
|
} else {
|
||||||
|
animations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
tagBadgeView.setNeedsLayout()
|
||||||
|
tagBadgeView.layoutIfNeeded()
|
||||||
|
tagBadgeView.setCornerRadius(corners: [.topLeft, .bottomRight], withCornerRadii: CGSize(width: 10, height: 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VipRechargeView: UIScrollViewDelegate {
|
||||||
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
let maxY = scrollView.contentOffset.y
|
||||||
|
let alpha = maxY / kNaviHeight
|
||||||
|
navBarView.alpha = alpha < 0.0 ? 0.0 : alpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// VipRechargeViewModel.swift
|
||||||
|
// QuickLocation
|
||||||
|
//
|
||||||
|
// Created by 八条 on 2026/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
|
class VipRechargeViewModel {
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
var sectionedItems: Observable<[ExpenseSectionModel]>
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Output
|
||||||
|
private let sectionedItems = PublishSubject<[ExpenseSectionModel]>()
|
||||||
|
|
||||||
|
private let expenseList: [ExpenseItem] = [
|
||||||
|
ExpenseItem(title: "白银会员", tag: "推荐"),
|
||||||
|
ExpenseItem(title: "黄金会员", tag: "热门"),
|
||||||
|
ExpenseItem(title: "钻石会员", tag: "爆款"),
|
||||||
|
ExpenseItem(title: "永久会员", tag: "超值")
|
||||||
|
]
|
||||||
|
|
||||||
|
var selectedIndex: Int = 0 {
|
||||||
|
didSet {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadData() {
|
||||||
|
sectionedItems.onNext([SectionModel(model: "expense", items: expenseList)])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSelected(at index: Int) -> Bool {
|
||||||
|
return index == selectedIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
output = Output(
|
||||||
|
sectionedItems: sectionedItems.asObservable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,4 +25,14 @@ struct SystemService {
|
||||||
.map(SmsCodeResponse.self)
|
.map(SmsCodeResponse.self)
|
||||||
.asObservable()
|
.asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 充值内容
|
||||||
|
/// - Parameters:
|
||||||
|
/// - type: 类型 member
|
||||||
|
static func rechargeInfo(type: String) -> Observable<VipExpenseResponse> {
|
||||||
|
let api = SystemAPI.rechargeInfo(type: type).multiTarget
|
||||||
|
return APIProvider.request(token: api)
|
||||||
|
.map(VipExpenseResponse.self)
|
||||||
|
.asObservable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||