QSLVipController.swift 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. //
  2. // QSLVipController.swift
  3. // QuickSearchLocation
  4. //
  5. // Created by Destiny on 2024/11/27.
  6. //
  7. import UIKit
  8. import YYText
  9. enum QSLVipJumpType: Int {
  10. case homeRoad // 定位查看轨迹
  11. case add // 添加好友
  12. case friendRoad // 好友列表查看轨迹
  13. case contact // 添加紧急联系人
  14. case mine //
  15. case guideComments // 引导评论
  16. }
  17. class QSLVipController: QSLBaseController {
  18. // 购买人数
  19. static let record_non_member_clicks_to_claim = "RECORD_NON_MEMBER_CLIKS_CLAIM"
  20. struct UX {
  21. static let mostCellHeight = 113.0.rpx
  22. static let collectionRowHeight = 137.0.rpx
  23. static let goodsBgHeight = 54.rpx + UX.mostCellHeight + 20.rpx
  24. }
  25. var type: QSLVipJumpType?
  26. var goodList: [QSLGoodModel] = [QSLGoodModel]()
  27. ///是否是订阅的产品
  28. //var is
  29. var selectGood: QSLGoodModel? {
  30. didSet {
  31. updateSelectGoodUI()
  32. }
  33. }
  34. lazy var vipBg: UIImageView = {
  35. let imageView = UIImageView()
  36. imageView.image = UIImage(named: "vip_bg")
  37. return imageView
  38. }()
  39. lazy var backButton: UIButton = {
  40. let button = UIButton()
  41. button.image(UIImage(named: "public_back_btn_white"))
  42. button.title("会员中心")
  43. button.mediumFont(17)
  44. button.textColor(.white)
  45. button.setImageTitleLayout(.imgLeft, spacing: 4.rpx)
  46. button.addTarget(self, action: #selector(backBtnAction), for: .touchUpInside)
  47. return button
  48. }()
  49. lazy var peopleCountView: UIView = {
  50. let view = UIView()
  51. view.layer.cornerRadius = 12.rpx
  52. view.layer.borderColor = UIColor.white.withAlphaComponent(0.16).cgColor
  53. view.layer.borderWidth = 1.rpx
  54. return view
  55. }()
  56. lazy var peopleAvatarImageView: UIImageView = {
  57. let imageView = UIImageView()
  58. imageView.image = UIImage(named: "vip_avatars_icon")
  59. return imageView
  60. }()
  61. lazy var peopleCountLabel: UILabel = {
  62. let label = UILabel()
  63. label.text("1456488人 已开通VIP")
  64. label.color("#FFE6C0")
  65. label.font(12)
  66. return label
  67. }()
  68. lazy var scrollView: UIScrollView = {
  69. let scrollView = UIScrollView()
  70. // scrollView.delegate = self
  71. scrollView.bounces = false
  72. scrollView.showsVerticalScrollIndicator = false
  73. scrollView.contentInsetAdjustmentBehavior = .never
  74. return scrollView
  75. }()
  76. lazy var cardBannerImageView: UIImageView = {
  77. let imageView = UIImageView()
  78. imageView.image = UIImage(named: "vip_banner_card")
  79. return imageView
  80. }()
  81. lazy var vipTimeLabel: UILabel = {
  82. let label = UILabel()
  83. label.text("升级VIP会员,解锁全部功能")
  84. label.font(13)
  85. label.textColor = .hexStringColor(hexString: "#AF7655")
  86. return label
  87. }()
  88. lazy var funcBannerImageView: UIImageView = {
  89. let imageView = UIImageView()
  90. imageView.image = UIImage(named: "vip_banner_func")
  91. return imageView
  92. }()
  93. lazy var mainView: UIView = {
  94. let view = UIView()
  95. view.backgroundColor = QSLColor.backGroundColor
  96. view.addRadius(radius: 12.rpx)
  97. return view
  98. }()
  99. lazy var goodsBgView: UIView = {
  100. let view = UIView()
  101. view.gradientBackgroundColor(color1: .hexStringColor(hexString: "#FFF8F2"), color2: .hexStringColor(hexString: "#FFFFFF"), width: QSLConst.qsl_kScreenW, height: UX.goodsBgHeight, direction: .vertical)
  102. return view
  103. }()
  104. lazy var vipGoodsTitleIcon: UIImageView = {
  105. let imageView = UIImageView()
  106. imageView.image = UIImage(named: "vip_title_icon")
  107. return imageView
  108. }()
  109. lazy var resumeBtn: UIButton = {
  110. let button = UIButton()
  111. button.title("恢复购买")
  112. button.setTitleColor(.hexStringColor(hexString: "#000000").withAlphaComponent(0.5), for: .normal)
  113. button.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .medium)
  114. button.image(UIImage(named: "vip_resume_btn"))
  115. button.setImageTitleLayout(.imgLeft, spacing: 2.rpx)
  116. button.addTarget(self, action: #selector(resumeBtnAction), for: .touchUpInside)
  117. return button
  118. }()
  119. lazy var mostCell: QSLVipMostGoodCell = {
  120. let cell = QSLVipMostGoodCell()
  121. cell.isUserInteractionEnabled = true
  122. let tap = UITapGestureRecognizer(target: self, action: #selector(firstCellClickAction))
  123. cell.addGestureRecognizer(tap)
  124. return cell
  125. }()
  126. lazy var goodsCollectionView: UICollectionView = {
  127. let layout = UICollectionViewFlowLayout()
  128. layout.minimumLineSpacing = 0
  129. layout.scrollDirection = .vertical
  130. let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
  131. collectionView.backgroundColor = .clear
  132. collectionView.dataSource = self
  133. collectionView.delegate = self
  134. collectionView.showsHorizontalScrollIndicator = false
  135. collectionView.bounces = false
  136. collectionView.register(cellClass: QSLVipGoodCollectionViewCell.self)
  137. return collectionView
  138. }()
  139. lazy var selectBtn: UIButton = {
  140. let btn = UIButton()
  141. btn.image(UIImage(named: "public_select_btn_false"), .normal)
  142. btn.image(UIImage(named: "public_select_btn_true"), .selected)
  143. btn.addTarget(self, action: #selector(selectBtnAction), for: .touchUpInside)
  144. return btn
  145. }()
  146. lazy var serviceLabel: YYLabel = {
  147. let label = YYLabel()
  148. //updateServiceLabelText(showSubscribe: true) // 默认显示所有内容
  149. return label
  150. }()
  151. // 更新服务条款文本内容
  152. func updateServiceLabelText(showSubscribe: Bool) {
  153. let attr = NSMutableAttributedString()
  154. // 固定部分:购买即同意
  155. let firstAttr = NSMutableAttributedString(string: "购买即同意")
  156. firstAttr.font(10)
  157. firstAttr.color(.hexStringColor(hexString: "#A7A7A7"))
  158. attr.append(firstAttr)
  159. let blankAttr = NSMutableAttributedString(string: " ")
  160. blankAttr.font(10)
  161. attr.append(blankAttr)
  162. // 《隐私权政策》
  163. let privacyHL = YYTextHighlight()
  164. let privacyStr = "《隐私权政策》"
  165. let privacyText = NSMutableAttributedString(string: privacyStr)
  166. privacyText.font(10)
  167. privacyText.color(.hexStringColor(hexString: "#E7B983"))
  168. privacyText.yy_setTextHighlight(privacyHL, range: NSRange(location: 0, length: privacyStr.count))
  169. privacyHL.tapAction = { [weak self] containerView, text, range, rect in
  170. self?.privacyAction()
  171. }
  172. attr.append(privacyText)
  173. attr.append(blankAttr)
  174. // 《用户协议》
  175. let serviceHL = YYTextHighlight()
  176. let serviceStr = "《用户协议》"
  177. let serviceText = NSMutableAttributedString(string: serviceStr)
  178. serviceText.font(10)
  179. serviceText.color(.hexStringColor(hexString: "#E7B983"))
  180. serviceText.yy_setTextHighlight(serviceHL, range: NSRange(location: 0, length: serviceStr.count))
  181. serviceHL.tapAction = { [weak self] containerView, text, range, rect in
  182. self?.serviceAction()
  183. }
  184. attr.append(serviceText)
  185. // 根据条件决定是否添加《续订说明》相关内容
  186. if showSubscribe {
  187. attr.append(blankAttr)
  188. let andAttr = NSMutableAttributedString(string: "和")
  189. andAttr.font(10)
  190. andAttr.color(.hexStringColor(hexString: "#A7A7A7"))
  191. attr.append(andAttr)
  192. attr.append(blankAttr)
  193. let subcribeHL = YYTextHighlight()
  194. let subcribeStr = "《续订说明》"
  195. let subcribeText = NSMutableAttributedString(string: subcribeStr)
  196. subcribeText.font(10)
  197. subcribeText.color(.hexStringColor(hexString: "#E7B983"))
  198. subcribeText.yy_setTextHighlight(subcribeHL, range: NSRange(location: 0, length: subcribeStr.count))
  199. subcribeHL.tapAction = { [weak self] containerView, text, range, rect in
  200. self?.subscibeAction()
  201. }
  202. attr.append(subcribeText)
  203. }
  204. serviceLabel.attributedText = attr
  205. }
  206. // 在需要隐藏《续订说明》时调用此方法
  207. func hideSubscribeDescription() {
  208. updateServiceLabelText(showSubscribe: false)
  209. }
  210. // 在需要显示《续订说明》时调用此方法
  211. func showSubscribeDescription() {
  212. updateServiceLabelText(showSubscribe: true)
  213. }
  214. lazy var commentView: UIView = {
  215. let view = UIView()
  216. view.backgroundColor = .white
  217. return view
  218. }()
  219. lazy var commentTitleIcon: UIImageView = {
  220. let imageView = UIImageView()
  221. imageView.image = UIImage(named: "vip_comment_title_icon")
  222. return imageView
  223. }()
  224. lazy var comment1: QSLVipCommentCellView = {
  225. let view = QSLVipCommentCellView()
  226. view.config(name: "用户189****7913", comment: "上班没时间,远程遛娃,非常方便很好用。")
  227. return view
  228. }()
  229. lazy var comment2: QSLVipCommentCellView = {
  230. let view = QSLVipCommentCellView()
  231. view.config(name: "用户189****7913", comment: "用了之后,才发现真的可以找到他。")
  232. return view
  233. }()
  234. lazy var comment3: QSLVipCommentCellView = {
  235. let view = QSLVipCommentCellView()
  236. view.config(name: "用户189****7913", comment: "轨迹很准,一目了然,奶奶出门遇到危险直接一键报警,我就收到信息了")
  237. return view
  238. }()
  239. lazy var tipTitleLabel: UILabel = {
  240. let label = UILabel()
  241. label.text("温馨提示")
  242. label.font(12)
  243. label.textColor = .hexStringColor(hexString: "#000000").withAlphaComponent(0.8)
  244. return label
  245. }()
  246. lazy var tipContentLabel: UILabel = {
  247. let label = UILabel()
  248. label.numberOfLines = 0
  249. label.text("1 订阅后会从您的iTunes账户中扣除相关费用。如需取消续订,请在当前订阅周期结束前,至少24小时内通过手机“账户设置”的订阅管理关闭自动续费功能。\n2 未成年人请在监护者的陪同下进行购买\n3 购买后不支持退款,请仔细审阅《隐私权政策》《用户协议》和《续订说明》")
  250. label.font(10)
  251. label.textColor = .hexStringColor(hexString: "#000000").withAlphaComponent(0.6)
  252. label.changeLineSpace(space: 5)
  253. return label
  254. }()
  255. lazy var bottomContentView: UIView = {
  256. let view = UIView()
  257. return view
  258. }()
  259. lazy var bottomTopView: UIView = {
  260. let view = UIView()
  261. view.isHidden = true
  262. view.backgroundColor = .hexStringColor(hexString: "#FFFED8")
  263. view.addRadius(radius: 30.rpx)
  264. return view
  265. }()
  266. lazy var bottomTopLabel: UILabel = {
  267. let label = UILabel()
  268. label.text = ""
  269. return label
  270. }()
  271. func bottomTopLabelInfo(_ goodInfo : QSLGoodModel) {
  272. let darkGrayAttributes: [NSAttributedString.Key: Any] = [
  273. .foregroundColor: UIColor.hexStringColor(hexString: "#404040"),
  274. .font: UIFont.systemFont(ofSize: 11),
  275. .paragraphStyle: {
  276. let paragraphStyle = NSMutableParagraphStyle()
  277. paragraphStyle.alignment = .center
  278. return paragraphStyle
  279. }(),
  280. .kern: -1
  281. ]
  282. // 创建绿色文本样式
  283. let greenAttributes: [NSAttributedString.Key: Any] = [
  284. .foregroundColor: UIColor.hexStringColor(hexString: "#EE6C1C"),
  285. .font: UIFont.systemFont(ofSize: 15, weight: .black),
  286. .paragraphStyle: {
  287. let paragraphStyle = NSMutableParagraphStyle()
  288. paragraphStyle.alignment = .center
  289. return paragraphStyle
  290. }(),
  291. .kern: -1
  292. ]
  293. let textArray = goodInfo.text.components(separatedBy: goodInfo.keys.key)
  294. if textArray.count > 0 && goodInfo.text.count > 0{
  295. bottomTopView.isHidden = false
  296. let inputtr = NSMutableAttributedString()
  297. let titleOneAttr = NSAttributedString(string: textArray.first ?? "", attributes: darkGrayAttributes)
  298. let titleTwoAttr = NSAttributedString(string: " \(goodInfo.keys.value) ", attributes: greenAttributes)
  299. inputtr.append(titleOneAttr)
  300. inputtr.append(titleTwoAttr)
  301. if textArray.count > 1 {
  302. let titleThreeAttr = NSAttributedString(string: textArray[1], attributes: darkGrayAttributes)
  303. inputtr.append(titleThreeAttr)
  304. }
  305. bottomTopLabel.attributedText = inputtr
  306. } else {
  307. bottomTopView.isHidden = true
  308. }
  309. }
  310. lazy var bottomView: UIView = {
  311. let view = UIView()
  312. view.gradientBackgroundColor(color1: .hexStringColor(hexString: "#00434E"), color2: .hexStringColor(hexString: "#0E5E61"), width: QSLConst.qsl_kScreenW - 24.rpx, height: 50.rpx, direction: .horizontal)
  313. view.addRadius(radius: 25.rpx)
  314. return view
  315. }()
  316. lazy var unlockBtn: UIButton = {
  317. let btn = UIButton()
  318. btn.setBackgroundImage(UIImage(named: "vip_unlock_btn_bg"), for: .normal)
  319. btn.title("立即解锁")
  320. btn.textColor(.hexStringColor(hexString: "#9B3800"))
  321. btn.mediumFont(18)
  322. btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 30.rpx, bottom: 0, right: 0)
  323. btn.addTarget(self, action: #selector(unlockBtnAction), for: .touchUpInside)
  324. return btn
  325. }()
  326. lazy var priceIconLabel: UILabel = {
  327. let label = UILabel()
  328. label.text("¥")
  329. label.textColor = .hexStringColor(hexString: "#FFF8EF")
  330. label.font(14)
  331. return label
  332. }()
  333. lazy var priceLabel: UILabel = {
  334. let label = UILabel()
  335. label.text("168")
  336. label.textColor = .hexStringColor(hexString: "#FFF8EF")
  337. label.font(24)
  338. return label
  339. }()
  340. lazy var goodTypeLabel: UILabel = {
  341. let label = UILabel()
  342. label.text("/")
  343. label.textColor = .hexStringColor(hexString: "#FFF8EF")
  344. label.font(12)
  345. return label
  346. }()
  347. lazy var goodOriginalPriceLabel: UILabel = {
  348. let label = UILabel()
  349. label.text("原价228")
  350. label.textColor = .hexStringColor(hexString: "#FFF8EF")
  351. label.font(12)
  352. label.centerLineText(lineValue: 1, underlineColor: .hexStringColor(hexString: "#FFF8EF"))
  353. return label
  354. }()
  355. override func viewDidLoad() {
  356. super.viewDidLoad()
  357. initializeView()
  358. updateUI()
  359. requestItemList()
  360. calculateBuyCount()
  361. if let type = self.type {
  362. switch type {
  363. case .homeRoad:
  364. gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1001])
  365. case .add:
  366. gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1002])
  367. case .friendRoad:
  368. gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1003])
  369. case .contact:
  370. gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1004])
  371. case .mine:
  372. gravityInstance?.track(QSLGravityConst.vip_show, properties: ["id": 1006])
  373. case .guideComments:
  374. print("")
  375. }
  376. }
  377. }
  378. }
  379. extension QSLVipController {
  380. @objc func privacyAction() {
  381. let vc = QSLWebViewController()
  382. vc.webUrl = QSLConfig.AppPrivacyAgreementLink
  383. vc.title = "隐私政策"
  384. self.navigationController?.pushViewController(vc, animated: true)
  385. }
  386. @objc func serviceAction() {
  387. let vc = QSLWebViewController()
  388. vc.webUrl = QSLConfig.AppServiceAgreementLink
  389. vc.title = "服务协议"
  390. self.navigationController?.pushViewController(vc, animated: true)
  391. }
  392. @objc func subscibeAction() {
  393. let vc = QSLWebViewController()
  394. vc.webUrl = QSLConfig.AppSubscibeAgreementLink
  395. vc.title = "续订说明"
  396. self.navigationController?.pushViewController(vc, animated: true)
  397. }
  398. @objc func selectBtnAction() {
  399. selectBtn.isSelected = !selectBtn.isSelected
  400. }
  401. // 恢复按钮点击
  402. @objc func resumeBtnAction() {
  403. let memberModel = QSLBaseManager.shared.userModel.memberModel
  404. let expired = memberModel.expired
  405. if !expired {
  406. QSLLoading.success(text: "您已经在订阅中,无需恢复")
  407. return
  408. }
  409. QSLLoading.show()
  410. QSLVipManager.shared.restoreAction { isSuccess in
  411. QSLVipManager.shared.isPaying = false
  412. if isSuccess {
  413. QSLNetwork().request(.userMember(dict: [:])) { response in
  414. let model = response.mapObject(QSLMemberModel.self, modelKey: "data")
  415. QSLBaseManager.shared.userModel.memberModel = model
  416. if model.expired {
  417. QSLBaseManager.shared.saveVipExpiredTime(time: 0)
  418. } else {
  419. QSLBaseManager.shared.saveVipExpiredTime(time: model.endTimestamp)
  420. }
  421. QSLBaseManager.shared.saveUserId(id: model.userId)
  422. if model.endTimestamp > memberModel.endTimestamp {
  423. QSLLoading.success(text: "恢复成功")
  424. // 支付成功通知
  425. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
  426. self.navigationController?.popViewController(animated: true)
  427. }
  428. } else {
  429. QSLLoading.error(text: "没有可供恢复的订阅")
  430. }
  431. } fail: { code, error in
  432. QSLLoading.error(text: "没有可供恢复的订阅")
  433. }
  434. } else {
  435. QSLLoading.error(text: "没有可供恢复的订阅")
  436. }
  437. }
  438. }
  439. // 支付按钮点击
  440. @objc func unlockBtnAction() {
  441. switch self.selectGood?.level {
  442. case 100 :
  443. gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1006])
  444. break
  445. case 700:
  446. gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1005])
  447. break;
  448. case 3100:
  449. gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1004])
  450. break;
  451. case 9200:
  452. gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1003])
  453. break;
  454. case 36600:
  455. gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1002])
  456. break;
  457. case 3660000:
  458. gravityInstance?.track(QSLGravityConst.vip_buy_click, properties: ["id": 1001])
  459. break;
  460. default:
  461. break;
  462. }
  463. // if !selectBtn.isSelected {
  464. // self.view.toast(text: "请先同意《隐私权政策》和《用户协议》")
  465. // return
  466. // }
  467. let memberModel = QSLBaseManager.shared.userModel.memberModel
  468. if let subscriptionExpired = memberModel.subscriptionExpired, !subscriptionExpired {
  469. self.view.toast(text: "你已经订阅了")
  470. return
  471. }
  472. if goodList.count > 0, let selectGood = self.selectGood {
  473. QSLLoading.show()
  474. QSLVipManager.shared.startPay(goods: selectGood) { [self] status, outTradeNo in
  475. QSLVipManager.shared.isPaying = false
  476. if status == .success {
  477. QSLLoading.success(text: "支付成功")
  478. //支付成功埋点
  479. gravityInstance?.track(QSLGravityConst.vip_submit_success, properties: ["id": 01001])
  480. //弹出是否好评的弹窗
  481. QSLGuideusersToCommentManager.commentShare.manageWhetherTriggerPopUpWindow(QSLGuideusersToCommentType.member)
  482. // 引力传递支付事件
  483. if let selectGood = self.selectGood {
  484. gravityInstance?.trackPayEvent(withAmount: Int32(selectGood.amount), withPayType: "CNY", withOrderId: outTradeNo, withPayReason: selectGood.name, withPayMethod: "apple")
  485. }
  486. #if DEBUG
  487. #else
  488. //苹果广告奇异果传递支付事件
  489. QSWikiHandle.shared.addEventResultAttribution(eventDict: ["event_name": "pay", "event_val": selectGood.amount])
  490. #endif
  491. if let type = self.type {
  492. switch type {
  493. case .homeRoad:
  494. gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1001])
  495. case .add:
  496. gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1002])
  497. case .friendRoad:
  498. gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1003])
  499. case .contact:
  500. gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1004])
  501. case .mine:
  502. gravityInstance?.track(QSLGravityConst.vip_success_page, properties: ["id": 1006])
  503. case .guideComments:
  504. print("")
  505. }
  506. }
  507. switch self.selectGood?.level {
  508. case 100 :
  509. gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 1])
  510. break
  511. case 700:
  512. gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 5])
  513. break;
  514. case 3100:
  515. gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 9])
  516. break;
  517. case 9200:
  518. gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 13])
  519. break;
  520. case 36600:
  521. gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 17])
  522. break;
  523. case 3660000:
  524. gravityInstance?.track(QSLGravityConst.vip_success_good, properties: ["id": 21])
  525. break;
  526. default:
  527. break;
  528. }
  529. DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
  530. self.navigationController?.popViewController(animated: true)
  531. NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil)
  532. }
  533. } else if status == .cancel {
  534. QSLLoading.error(text: "支付取消")
  535. payFailAlertTip()
  536. } else if status == .fail {
  537. gravityInstance?.track(QSLGravityConst.vip_fail)
  538. QSLLoading.error(text: "支付失败")
  539. payFailAlertTip()
  540. } else if status == .searchFail {
  541. QSLLoading.error(text: "查询订单失败,请稍后重试")
  542. }
  543. }
  544. }
  545. }
  546. func payFailAlertTip() {
  547. if let currentWindow = UIApplication.keyWindow {
  548. QSLRetainPopUpAlertView.alert(view: currentWindow, isOneBtn: true, oneBtnText: "继续支付", oneBtnClosure: { [weak self] in
  549. self?.unlockBtnAction()
  550. })
  551. }
  552. }
  553. @objc func firstCellClickAction() {
  554. for i in 0..<self.goodList.count {
  555. self.goodList[i].isSelect = false
  556. }
  557. if self.goodList.count > 0 {
  558. self.goodList[0].isSelect = true
  559. self.selectGood = self.goodList[0]
  560. self.mostCell.config(model: self.goodList[0])
  561. }
  562. switch self.selectGood?.level {
  563. case 100 :
  564. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
  565. break
  566. case 700:
  567. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
  568. break;
  569. case 3100:
  570. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
  571. break;
  572. case 9200:
  573. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
  574. break;
  575. case 36600:
  576. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
  577. break;
  578. case 3660000:
  579. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
  580. break;
  581. default:
  582. break;
  583. }
  584. self.goodsCollectionView.reloadData()
  585. }
  586. }
  587. extension QSLVipController {
  588. // 请求商品列表
  589. func requestItemList() {
  590. // 定义并赋值 itemListDict(根据条件变化)
  591. var itemListDict: [String: Any] = ["itemListType": 2]
  592. if UserDefaults.standard.string(forKey: QSLVipController.record_non_member_clicks_to_claim) == nil && self.type == .guideComments {
  593. itemListDict = ["itemListType": 2, "showExtraText": true]
  594. // 记录非会员点击领取
  595. UserDefaults.standard.set("YES", forKey: QSLVipController.record_non_member_clicks_to_claim)
  596. UserDefaults.standard.synchronize()
  597. }
  598. // 网络请求时使用 itemListDict 作为参数(关键修复)
  599. QSLNetwork().request(.vipItemList(dict: itemListDict)) { response in
  600. // 后续逻辑不变...
  601. let list = response.mapArray(QSLGoodModel.self, modelKey: "data>list")
  602. self.goodList = list
  603. if self.goodList.count > 0 {
  604. self.goodList[0].isSelect = true
  605. self.selectGood = self.goodList[0]
  606. self.mostCell.config(model: self.goodList[0])
  607. let row = ceil(Double(self.goodList.count - 1) / 3.0)
  608. let height = UX.goodsBgHeight + (row * UX.collectionRowHeight)
  609. self.goodsBgView.gradientBackgroundColor(color1: .hexStringColor(hexString: "#FFF8F2"), color2: .hexStringColor(hexString: "#FFFFFF"), width: QSLConst.qsl_kScreenW, height: height, direction: .vertical)
  610. self.goodsBgView.snp.updateConstraints { make in
  611. make.height.equalTo(height)
  612. }
  613. self.scrollView.snp.makeConstraints { make in
  614. make.bottom.equalTo(self.mainView)
  615. }
  616. self.checkRenewalOfProducts()
  617. }
  618. self.goodsCollectionView.reloadData()
  619. } fail: { code, error in
  620. self.view.toast(text: "加载商品列表失败")
  621. }
  622. }
  623. ///检查有没有续订的产品
  624. func checkRenewalOfProducts() {
  625. let hasSubscribable = self.goodList.contains(where: { model in
  626. return model.subscribable == 1
  627. })
  628. if hasSubscribable == false {
  629. tipContentLabel.isHidden = true
  630. hideSubscribeDescription()
  631. serviceLabel.snp.removeConstraints()
  632. serviceLabel.snp.makeConstraints { make in
  633. make.left.equalTo(16)
  634. make.top.equalTo(comment3.snp.bottom).offset(28.rpx)
  635. }
  636. } else {
  637. tipContentLabel.isHidden = false
  638. showSubscribeDescription()
  639. }
  640. }
  641. // 计算购买人数
  642. func calculateBuyCount() {
  643. var num = 145883
  644. if let localNum = UserDefaults.standard.value(forKey: QSLConfig.user_default_local_buy_count) as? Int {
  645. num = localNum
  646. }
  647. let randomAddCount = Int.random(in: 1...10)
  648. num = num + randomAddCount
  649. UserDefaults.standard.setValue(num, forKey: QSLConfig.user_default_local_buy_count)
  650. self.peopleCountLabel.text("\(num)人 已开通VIP")
  651. }
  652. }
  653. // MARK: - 设置 CollectionView
  654. extension QSLVipController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  655. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  656. return goodList.count - 1
  657. }
  658. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  659. let cell = collectionView.dequeueReusableCell(cellType: QSLVipGoodCollectionViewCell.self, cellForRowAt: indexPath)
  660. let model = self.goodList[indexPath.row + 1]
  661. if self.goodList.count - 1 == 2 {
  662. cell.config(model: model, type: .big)
  663. } else {
  664. cell.config(model: model, type: .small)
  665. }
  666. return cell
  667. }
  668. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  669. for i in 0..<self.goodList.count {
  670. self.goodList[i].isSelect = false
  671. }
  672. if self.goodList.count > 0 {
  673. self.goodList[indexPath.row + 1].isSelect = true
  674. self.selectGood = self.goodList[indexPath.row + 1]
  675. self.mostCell.config(model: self.goodList[0])
  676. }
  677. switch self.selectGood?.level {
  678. case 100 :
  679. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
  680. break
  681. case 700:
  682. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
  683. break;
  684. case 3100:
  685. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
  686. break;
  687. case 9200:
  688. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
  689. break;
  690. case 36600:
  691. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
  692. break;
  693. case 3660000:
  694. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
  695. break;
  696. default:
  697. break;
  698. }
  699. self.goodsCollectionView.reloadData()
  700. }
  701. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
  702. var width = (QSLConst.qsl_kScreenW - 42.0.rpx) / 3.0
  703. if self.goodList.count - 1 == 2 {
  704. width = (QSLConst.qsl_kScreenW - 36.0.rpx) / 2.0
  705. }
  706. return CGSize(width: width, height: 134.0.rpx)
  707. }
  708. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
  709. return 6.rpx
  710. }
  711. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
  712. return 3.rpx
  713. }
  714. }
  715. extension QSLVipController {
  716. func updateUI() {
  717. if QSLBaseManager.shared.isLogin() && QSLBaseManager.shared.isVip() {
  718. let model = QSLBaseManager.shared.userModel.memberModel
  719. if model.permanent {
  720. self.vipTimeLabel.text("您已是尊贵的永久会员")
  721. self.bottomContentView.isHidden = true
  722. } else {
  723. let level = model.memberLevelString()
  724. let endTime = model.endTimestampString()
  725. self.vipTimeLabel.text("\(level):\(endTime)到期")
  726. }
  727. } else {
  728. self.vipTimeLabel.text("升级VIP会员,解锁全部功能")
  729. }
  730. }
  731. func updateSelectGoodUI() {
  732. var priceText = ""
  733. var originalPriceText = ""
  734. if let selectGood = self.selectGood {
  735. if selectGood.amount.truncatingRemainder(dividingBy: 100) == 0 {
  736. priceText = "\(Int(selectGood.amount / 100))"
  737. } else {
  738. priceText = String(format: "%.2lf", selectGood.amount / 100 )
  739. }
  740. if selectGood.originalAmount.truncatingRemainder(dividingBy: 100) == 0 {
  741. originalPriceText = "\(Int(selectGood.originalAmount / 100))"
  742. } else {
  743. originalPriceText = String(format: "%.2lf", selectGood.originalAmount / 100 )
  744. }
  745. }
  746. self.priceLabel.text(priceText)
  747. // self.goodTypeLabel.text("/ \(self.selectGood?.name ?? "")")
  748. self.goodOriginalPriceLabel.text("原价\(originalPriceText)")
  749. ///底部内容
  750. bottomTopLabelInfo(self.selectGood ?? QSLGoodModel())
  751. }
  752. func initializeView() {
  753. self.view.addSubview(vipBg)
  754. vipBg.snp.makeConstraints { make in
  755. make.left.top.right.equalTo(0)
  756. }
  757. self.view.addSubview(backButton)
  758. backButton.snp.makeConstraints { make in
  759. make.size.equalTo(100.rpx)
  760. make.height.equalTo(25.rpx)
  761. make.left.equalTo(12.rpx)
  762. make.top.equalTo(QSLConst.qsl_kStatusBarFrameH)
  763. }
  764. self.view.addSubview(peopleCountView)
  765. peopleCountView.snp.makeConstraints { make in
  766. make.size.equalTo(CGSize(width: 212.rpx, height: 24.rpx))
  767. make.right.equalTo(-13.rpx)
  768. make.centerY.equalTo(backButton.snp.centerY)
  769. }
  770. peopleCountView.addSubview(peopleAvatarImageView)
  771. peopleAvatarImageView.snp.makeConstraints { make in
  772. make.size.equalTo(CGSize(width: 72.rpx, height: 16.6.rpx))
  773. make.left.equalTo(3.rpx)
  774. make.centerY.equalTo(peopleCountView.snp.centerY)
  775. }
  776. peopleCountView.addSubview(peopleCountLabel)
  777. peopleCountLabel.snp.makeConstraints { make in
  778. make.left.equalTo(peopleAvatarImageView.snp.right).offset(7.rpx)
  779. make.centerY.equalTo(peopleCountView.snp.centerY)
  780. }
  781. self.view.addSubview(scrollView)
  782. scrollView.snp.makeConstraints { make in
  783. make.left.right.equalTo(0)
  784. make.top.equalTo(QSLConst.qsl_kNavFrameH)
  785. make.bottom.equalTo(0)
  786. }
  787. scrollView.addSubview(cardBannerImageView)
  788. cardBannerImageView.snp.makeConstraints { make in
  789. make.top.equalTo(0)
  790. make.centerX.equalToSuperview()
  791. make.width.equalTo(336.rpx)
  792. make.height.equalTo(108.rpx)
  793. }
  794. cardBannerImageView.addSubview(vipTimeLabel)
  795. vipTimeLabel.snp.makeConstraints { make in
  796. make.left.equalTo(23.rpx)
  797. make.bottom.equalTo(-19.rpx)
  798. }
  799. scrollView.addSubview(funcBannerImageView)
  800. funcBannerImageView.snp.makeConstraints { make in
  801. make.size.equalTo(CGSize(width: 336.rpx, height: 172.rpx))
  802. make.centerX.equalToSuperview()
  803. make.top.equalTo(cardBannerImageView.snp.bottom).offset(14.rpx)
  804. }
  805. scrollView.addSubview(mainView)
  806. mainView.snp.makeConstraints { make in
  807. make.left.right.equalTo(0)
  808. make.top.equalTo(funcBannerImageView.snp.bottom).offset(-24.rpx)
  809. make.width.equalTo(QSLConst.qsl_kScreenW)
  810. // make.height.equalTo(800.rpx)
  811. }
  812. let goodsBgHeight = 54.rpx + UX.mostCellHeight
  813. mainView.addSubview(goodsBgView)
  814. goodsBgView.snp.makeConstraints { make in
  815. make.top.left.right.equalTo(0)
  816. make.width.equalTo(QSLConst.qsl_kScreenW)
  817. make.height.equalTo(goodsBgHeight)
  818. }
  819. goodsBgView.addSubview(vipGoodsTitleIcon)
  820. vipGoodsTitleIcon.snp.makeConstraints { make in
  821. make.size.equalTo(CGSize(width: 117.rpx, height: 26.rpx))
  822. make.left.equalTo(20.rpx)
  823. make.top.equalTo(16.rpx)
  824. }
  825. goodsBgView.addSubview(resumeBtn)
  826. resumeBtn.snp.makeConstraints { make in
  827. make.size.equalTo(CGSize(width: 75.rpx, height: 16.rpx))
  828. make.right.equalTo(-18.rpx)
  829. make.centerY.equalTo(vipGoodsTitleIcon.snp.centerY)
  830. }
  831. goodsBgView.addSubview(mostCell)
  832. mostCell.snp.makeConstraints { make in
  833. make.left.equalTo(12.rpx)
  834. make.right.equalTo(-16.rpx)
  835. make.height.equalTo(113.rpx)
  836. make.top.equalTo(vipGoodsTitleIcon.snp.bottom).offset(10.rpx)
  837. }
  838. goodsBgView.addSubview(goodsCollectionView)
  839. goodsCollectionView.snp.makeConstraints { make in
  840. make.left.equalTo(12.rpx)
  841. make.right.equalTo(-18.rpx)
  842. make.bottom.equalTo(-20.rpx)
  843. make.top.equalTo(mostCell.snp.bottom).offset(3.rpx)
  844. }
  845. // goodsBgView.addSubview(selectBtn)
  846. // selectBtn.snp.makeConstraints { make in
  847. // make.size.equalTo(CGSize(width: 12.rpx, height: 12.rpx))
  848. // make.left.equalTo(23.rpx)
  849. // make.top.equalTo(goodsCollectionView.snp.bottom).offset(16.rpx)
  850. // }
  851. //
  852. // goodsBgView.addSubview(serviceLabel)
  853. // serviceLabel.snp.makeConstraints { make in
  854. // make.left.equalTo(selectBtn.snp.right).offset(2.rpx)
  855. // make.centerY.equalTo(selectBtn.snp.centerY)
  856. // }
  857. mainView.addSubview(commentView)
  858. commentView.snp.makeConstraints { make in
  859. make.left.right.equalTo(0)
  860. make.top.equalTo(goodsBgView.snp.bottom).offset(8.rpx)
  861. make.height.equalTo(525.rpx + QSLConst.qsl_kTabbarBottom)
  862. make.bottom.equalTo(0)
  863. }
  864. commentView.addSubview(commentTitleIcon)
  865. commentTitleIcon.snp.makeConstraints { make in
  866. make.size.equalTo(CGSize(width: 83.rpx, height: 26.rpx))
  867. make.top.equalTo(16.rpx)
  868. make.left.equalTo(12.rpx)
  869. }
  870. let commentHeight1 = "上班没时间,远程遛娃,非常方便很好用。".heightAccording(width: QSLConst.qsl_kScreenW - 52.rpx - 12.rpx, font: .textF(15), lineSpacing: 2) + 35.rpx
  871. commentView.addSubview(comment1)
  872. comment1.snp.makeConstraints { make in
  873. make.left.right.equalTo(0)
  874. make.top.equalTo(commentTitleIcon.snp.bottom).offset(16.rpx)
  875. make.height.equalTo(commentHeight1)
  876. }
  877. let commentHeight2 = "用了之后,才发现真的可以找到他。".heightAccording(width: QSLConst.qsl_kScreenW - 52.rpx - 12.rpx, font: .textF(15), lineSpacing: 2) + 35.rpx
  878. commentView.addSubview(comment2)
  879. comment2.snp.makeConstraints { make in
  880. make.left.right.equalTo(0)
  881. make.top.equalTo(comment1.snp.bottom).offset(30.rpx)
  882. make.height.equalTo(commentHeight2)
  883. }
  884. let commentHeight3 = "轨迹很准,一目了然,奶奶出门遇到危险直接一键报警,我就收到信息了".heightAccording(width: QSLConst.qsl_kScreenW - 52.rpx - 12.rpx, font: .textF(15), lineSpacing: 2) + 35.rpx
  885. commentView.addSubview(comment3)
  886. comment3.snp.makeConstraints { make in
  887. make.left.right.equalTo(0)
  888. make.top.equalTo(comment2.snp.bottom).offset(30.rpx)
  889. make.height.equalTo(commentHeight3)
  890. }
  891. // commentView.addSubview(tipTitleLabel)
  892. // tipTitleLabel.snp.makeConstraints { make in
  893. // make.left.equalTo(16)
  894. // make.top.equalTo(comment3.snp.bottom).offset(28)
  895. // }
  896. commentView.addSubview(tipContentLabel)
  897. tipContentLabel.snp.makeConstraints { make in
  898. make.left.equalTo(16)
  899. make.right.equalTo(-16)
  900. make.top.equalTo(comment3.snp.bottom).offset(11)
  901. }
  902. commentView.addSubview(serviceLabel)
  903. serviceLabel.snp.makeConstraints { make in
  904. make.left.equalTo(16)
  905. make.top.equalTo(tipContentLabel.snp.bottom).offset(28.rpx)
  906. }
  907. scrollView.snp.makeConstraints { make in
  908. make.bottom.equalTo(mainView)
  909. }
  910. self.view.addSubview(bottomContentView)
  911. bottomContentView.snp.makeConstraints { make in
  912. make.left.equalTo(12.rpx)
  913. make.right.equalTo(-12.rpx)
  914. make.bottom.equalTo(-QSLConst.qsl_kTabbarBottom)
  915. make.height.equalTo(72.rpx)
  916. }
  917. bottomContentView.addSubview(bottomTopView)
  918. bottomTopView.snp.makeConstraints { make in
  919. make.left.equalToSuperview()
  920. make.right.equalToSuperview()
  921. make.top.equalToSuperview()
  922. make.height.equalTo(60.rpx)
  923. }
  924. bottomTopView.addSubview(bottomTopLabel)
  925. bottomTopLabel.snp.makeConstraints { make in
  926. make.left.equalToSuperview().offset(22)
  927. make.top.equalToSuperview()
  928. make.height.equalTo(21.rpx)
  929. }
  930. bottomContentView.addSubview(bottomView)
  931. bottomView.snp.makeConstraints { make in
  932. make.left.equalToSuperview()
  933. make.right.equalToSuperview()
  934. make.bottom.equalToSuperview()
  935. make.height.equalTo(50.rpx)
  936. }
  937. bottomView.addSubview(unlockBtn)
  938. unlockBtn.snp.makeConstraints { make in
  939. make.right.top.bottom.equalTo(0)
  940. make.width.equalTo(165.rpx)
  941. }
  942. bottomView.addSubview(priceIconLabel)
  943. priceIconLabel.snp.makeConstraints { make in
  944. make.left.equalTo(20.rpx)
  945. make.bottom.equalTo(-12.rpx)
  946. }
  947. bottomView.addSubview(priceLabel)
  948. priceLabel.snp.makeConstraints { make in
  949. make.left.equalTo(priceIconLabel.snp.right).offset(2.rpx)
  950. make.bottom.equalTo(-10.rpx)
  951. }
  952. bottomView.addSubview(goodTypeLabel)
  953. goodTypeLabel.snp.makeConstraints { make in
  954. make.left.equalTo(priceLabel.snp.right).offset(2.rpx)
  955. make.bottom.equalTo(-14.rpx)
  956. }
  957. bottomView.addSubview(goodOriginalPriceLabel)
  958. goodOriginalPriceLabel.snp.makeConstraints { make in
  959. make.left.equalTo(goodTypeLabel.snp.right).offset(2.rpx)
  960. make.centerY.equalTo(goodTypeLabel.snp.centerY)
  961. }
  962. }
  963. }