QSLVipController.swift 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  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. unlockBtnAction()
  563. }
  564. switch self.selectGood?.level {
  565. case 100 :
  566. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
  567. break
  568. case 700:
  569. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
  570. break;
  571. case 3100:
  572. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
  573. break;
  574. case 9200:
  575. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
  576. break;
  577. case 36600:
  578. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
  579. break;
  580. case 3660000:
  581. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
  582. break;
  583. default:
  584. break;
  585. }
  586. self.goodsCollectionView.reloadData()
  587. }
  588. }
  589. extension QSLVipController {
  590. // 请求商品列表
  591. func requestItemList() {
  592. // 定义并赋值 itemListDict(根据条件变化)
  593. var itemListDict: [String: Any] = ["itemListType": 2]
  594. if UserDefaults.standard.string(forKey: QSLVipController.record_non_member_clicks_to_claim) == nil && self.type == .guideComments {
  595. itemListDict = ["itemListType": 2, "showExtraText": true]
  596. // 记录非会员点击领取
  597. UserDefaults.standard.set("YES", forKey: QSLVipController.record_non_member_clicks_to_claim)
  598. UserDefaults.standard.synchronize()
  599. }
  600. // 网络请求时使用 itemListDict 作为参数(关键修复)
  601. QSLNetwork().request(.vipItemList(dict: itemListDict)) { response in
  602. // 后续逻辑不变...
  603. let list = response.mapArray(QSLGoodModel.self, modelKey: "data>list")
  604. self.goodList = list
  605. if self.goodList.count > 0 {
  606. self.goodList[0].isSelect = true
  607. self.selectGood = self.goodList[0]
  608. self.mostCell.config(model: self.goodList[0])
  609. let row = ceil(Double(self.goodList.count - 1) / 3.0)
  610. let height = UX.goodsBgHeight + (row * UX.collectionRowHeight)
  611. self.goodsBgView.gradientBackgroundColor(color1: .hexStringColor(hexString: "#FFF8F2"), color2: .hexStringColor(hexString: "#FFFFFF"), width: QSLConst.qsl_kScreenW, height: height, direction: .vertical)
  612. self.goodsBgView.snp.updateConstraints { make in
  613. make.height.equalTo(height)
  614. }
  615. self.scrollView.snp.makeConstraints { make in
  616. make.bottom.equalTo(self.mainView)
  617. }
  618. self.checkRenewalOfProducts()
  619. }
  620. self.goodsCollectionView.reloadData()
  621. } fail: { code, error in
  622. self.view.toast(text: "加载商品列表失败")
  623. }
  624. }
  625. ///检查有没有续订的产品
  626. func checkRenewalOfProducts() {
  627. let hasSubscribable = self.goodList.contains(where: { model in
  628. return model.subscribable == 1
  629. })
  630. if hasSubscribable == false {
  631. tipContentLabel.isHidden = true
  632. hideSubscribeDescription()
  633. serviceLabel.snp.removeConstraints()
  634. serviceLabel.snp.makeConstraints { make in
  635. make.left.equalTo(16)
  636. make.top.equalTo(comment3.snp.bottom).offset(28.rpx)
  637. }
  638. } else {
  639. tipContentLabel.isHidden = false
  640. showSubscribeDescription()
  641. }
  642. }
  643. // 计算购买人数
  644. func calculateBuyCount() {
  645. var num = 145883
  646. if let localNum = UserDefaults.standard.value(forKey: QSLConfig.user_default_local_buy_count) as? Int {
  647. num = localNum
  648. }
  649. let randomAddCount = Int.random(in: 1...10)
  650. num = num + randomAddCount
  651. UserDefaults.standard.setValue(num, forKey: QSLConfig.user_default_local_buy_count)
  652. self.peopleCountLabel.text("\(num)人 已开通VIP")
  653. }
  654. }
  655. // MARK: - 设置 CollectionView
  656. extension QSLVipController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  657. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  658. return goodList.count - 1
  659. }
  660. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  661. let cell = collectionView.dequeueReusableCell(cellType: QSLVipGoodCollectionViewCell.self, cellForRowAt: indexPath)
  662. let model = self.goodList[indexPath.row + 1]
  663. if self.goodList.count - 1 == 2 {
  664. cell.config(model: model, type: .big)
  665. } else {
  666. cell.config(model: model, type: .small)
  667. }
  668. return cell
  669. }
  670. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  671. for i in 0..<self.goodList.count {
  672. self.goodList[i].isSelect = false
  673. }
  674. if self.goodList.count > 0 {
  675. self.goodList[indexPath.row + 1].isSelect = true
  676. self.selectGood = self.goodList[indexPath.row + 1]
  677. self.mostCell.config(model: self.goodList[0])
  678. //选中立即支付
  679. unlockBtnAction()
  680. }
  681. switch self.selectGood?.level {
  682. case 100 :
  683. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1006])
  684. break
  685. case 700:
  686. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1005])
  687. break;
  688. case 3100:
  689. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1004])
  690. break;
  691. case 9200:
  692. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1003])
  693. break;
  694. case 36600:
  695. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1002])
  696. break;
  697. case 3660000:
  698. gravityInstance?.track(QSLGravityConst.vip_good_select, properties: ["id": 1001])
  699. break;
  700. default:
  701. break;
  702. }
  703. self.goodsCollectionView.reloadData()
  704. }
  705. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
  706. var width = (QSLConst.qsl_kScreenW - 42.0.rpx) / 3.0
  707. if self.goodList.count - 1 == 2 {
  708. width = (QSLConst.qsl_kScreenW - 36.0.rpx) / 2.0
  709. }
  710. return CGSize(width: width, height: 134.0.rpx)
  711. }
  712. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
  713. return 6.rpx
  714. }
  715. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
  716. return 3.rpx
  717. }
  718. }
  719. extension QSLVipController {
  720. func updateUI() {
  721. if QSLBaseManager.shared.isLogin() && QSLBaseManager.shared.isVip() {
  722. let model = QSLBaseManager.shared.userModel.memberModel
  723. if model.permanent {
  724. self.vipTimeLabel.text("您已是尊贵的永久会员")
  725. self.bottomContentView.isHidden = true
  726. } else {
  727. let level = model.memberLevelString()
  728. let endTime = model.endTimestampString()
  729. self.vipTimeLabel.text("\(level):\(endTime)到期")
  730. }
  731. } else {
  732. self.vipTimeLabel.text("升级VIP会员,解锁全部功能")
  733. }
  734. }
  735. func updateSelectGoodUI() {
  736. var priceText = ""
  737. var originalPriceText = ""
  738. if let selectGood = self.selectGood {
  739. if selectGood.amount.truncatingRemainder(dividingBy: 100) == 0 {
  740. priceText = "\(Int(selectGood.amount / 100))"
  741. } else {
  742. priceText = String(format: "%.2lf", selectGood.amount / 100 )
  743. }
  744. if selectGood.originalAmount.truncatingRemainder(dividingBy: 100) == 0 {
  745. originalPriceText = "\(Int(selectGood.originalAmount / 100))"
  746. } else {
  747. originalPriceText = String(format: "%.2lf", selectGood.originalAmount / 100 )
  748. }
  749. }
  750. self.priceLabel.text(priceText)
  751. // self.goodTypeLabel.text("/ \(self.selectGood?.name ?? "")")
  752. self.goodOriginalPriceLabel.text("原价\(originalPriceText)")
  753. ///底部内容
  754. bottomTopLabelInfo(self.selectGood ?? QSLGoodModel())
  755. }
  756. func initializeView() {
  757. self.view.addSubview(vipBg)
  758. vipBg.snp.makeConstraints { make in
  759. make.left.top.right.equalTo(0)
  760. }
  761. self.view.addSubview(backButton)
  762. backButton.snp.makeConstraints { make in
  763. make.size.equalTo(100.rpx)
  764. make.height.equalTo(25.rpx)
  765. make.left.equalTo(12.rpx)
  766. make.top.equalTo(QSLConst.qsl_kStatusBarFrameH)
  767. }
  768. self.view.addSubview(peopleCountView)
  769. peopleCountView.snp.makeConstraints { make in
  770. make.size.equalTo(CGSize(width: 212.rpx, height: 24.rpx))
  771. make.right.equalTo(-13.rpx)
  772. make.centerY.equalTo(backButton.snp.centerY)
  773. }
  774. peopleCountView.addSubview(peopleAvatarImageView)
  775. peopleAvatarImageView.snp.makeConstraints { make in
  776. make.size.equalTo(CGSize(width: 72.rpx, height: 16.6.rpx))
  777. make.left.equalTo(3.rpx)
  778. make.centerY.equalTo(peopleCountView.snp.centerY)
  779. }
  780. peopleCountView.addSubview(peopleCountLabel)
  781. peopleCountLabel.snp.makeConstraints { make in
  782. make.left.equalTo(peopleAvatarImageView.snp.right).offset(7.rpx)
  783. make.centerY.equalTo(peopleCountView.snp.centerY)
  784. }
  785. self.view.addSubview(scrollView)
  786. scrollView.snp.makeConstraints { make in
  787. make.left.right.equalTo(0)
  788. make.top.equalTo(QSLConst.qsl_kNavFrameH)
  789. make.bottom.equalTo(0)
  790. }
  791. scrollView.addSubview(cardBannerImageView)
  792. cardBannerImageView.snp.makeConstraints { make in
  793. make.top.equalTo(0)
  794. make.centerX.equalToSuperview()
  795. make.width.equalTo(336.rpx)
  796. make.height.equalTo(108.rpx)
  797. }
  798. cardBannerImageView.addSubview(vipTimeLabel)
  799. vipTimeLabel.snp.makeConstraints { make in
  800. make.left.equalTo(23.rpx)
  801. make.bottom.equalTo(-19.rpx)
  802. }
  803. scrollView.addSubview(funcBannerImageView)
  804. funcBannerImageView.snp.makeConstraints { make in
  805. make.size.equalTo(CGSize(width: 336.rpx, height: 172.rpx))
  806. make.centerX.equalToSuperview()
  807. make.top.equalTo(cardBannerImageView.snp.bottom).offset(14.rpx)
  808. }
  809. scrollView.addSubview(mainView)
  810. mainView.snp.makeConstraints { make in
  811. make.left.right.equalTo(0)
  812. make.top.equalTo(funcBannerImageView.snp.bottom).offset(-24.rpx)
  813. make.width.equalTo(QSLConst.qsl_kScreenW)
  814. // make.height.equalTo(800.rpx)
  815. }
  816. let goodsBgHeight = 54.rpx + UX.mostCellHeight
  817. mainView.addSubview(goodsBgView)
  818. goodsBgView.snp.makeConstraints { make in
  819. make.top.left.right.equalTo(0)
  820. make.width.equalTo(QSLConst.qsl_kScreenW)
  821. make.height.equalTo(goodsBgHeight)
  822. }
  823. goodsBgView.addSubview(vipGoodsTitleIcon)
  824. vipGoodsTitleIcon.snp.makeConstraints { make in
  825. make.size.equalTo(CGSize(width: 117.rpx, height: 26.rpx))
  826. make.left.equalTo(20.rpx)
  827. make.top.equalTo(16.rpx)
  828. }
  829. goodsBgView.addSubview(resumeBtn)
  830. resumeBtn.snp.makeConstraints { make in
  831. make.size.equalTo(CGSize(width: 75.rpx, height: 16.rpx))
  832. make.right.equalTo(-18.rpx)
  833. make.centerY.equalTo(vipGoodsTitleIcon.snp.centerY)
  834. }
  835. goodsBgView.addSubview(mostCell)
  836. mostCell.snp.makeConstraints { make in
  837. make.left.equalTo(12.rpx)
  838. make.right.equalTo(-16.rpx)
  839. make.height.equalTo(113.rpx)
  840. make.top.equalTo(vipGoodsTitleIcon.snp.bottom).offset(10.rpx)
  841. }
  842. goodsBgView.addSubview(goodsCollectionView)
  843. goodsCollectionView.snp.makeConstraints { make in
  844. make.left.equalTo(12.rpx)
  845. make.right.equalTo(-18.rpx)
  846. make.bottom.equalTo(-20.rpx)
  847. make.top.equalTo(mostCell.snp.bottom).offset(3.rpx)
  848. }
  849. // goodsBgView.addSubview(selectBtn)
  850. // selectBtn.snp.makeConstraints { make in
  851. // make.size.equalTo(CGSize(width: 12.rpx, height: 12.rpx))
  852. // make.left.equalTo(23.rpx)
  853. // make.top.equalTo(goodsCollectionView.snp.bottom).offset(16.rpx)
  854. // }
  855. //
  856. // goodsBgView.addSubview(serviceLabel)
  857. // serviceLabel.snp.makeConstraints { make in
  858. // make.left.equalTo(selectBtn.snp.right).offset(2.rpx)
  859. // make.centerY.equalTo(selectBtn.snp.centerY)
  860. // }
  861. mainView.addSubview(commentView)
  862. commentView.snp.makeConstraints { make in
  863. make.left.right.equalTo(0)
  864. make.top.equalTo(goodsBgView.snp.bottom).offset(8.rpx)
  865. make.height.equalTo(525.rpx + QSLConst.qsl_kTabbarBottom)
  866. make.bottom.equalTo(0)
  867. }
  868. commentView.addSubview(commentTitleIcon)
  869. commentTitleIcon.snp.makeConstraints { make in
  870. make.size.equalTo(CGSize(width: 83.rpx, height: 26.rpx))
  871. make.top.equalTo(16.rpx)
  872. make.left.equalTo(12.rpx)
  873. }
  874. let commentHeight1 = "上班没时间,远程遛娃,非常方便很好用。".heightAccording(width: QSLConst.qsl_kScreenW - 52.rpx - 12.rpx, font: .textF(15), lineSpacing: 2) + 35.rpx
  875. commentView.addSubview(comment1)
  876. comment1.snp.makeConstraints { make in
  877. make.left.right.equalTo(0)
  878. make.top.equalTo(commentTitleIcon.snp.bottom).offset(16.rpx)
  879. make.height.equalTo(commentHeight1)
  880. }
  881. let commentHeight2 = "用了之后,才发现真的可以找到他。".heightAccording(width: QSLConst.qsl_kScreenW - 52.rpx - 12.rpx, font: .textF(15), lineSpacing: 2) + 35.rpx
  882. commentView.addSubview(comment2)
  883. comment2.snp.makeConstraints { make in
  884. make.left.right.equalTo(0)
  885. make.top.equalTo(comment1.snp.bottom).offset(30.rpx)
  886. make.height.equalTo(commentHeight2)
  887. }
  888. let commentHeight3 = "轨迹很准,一目了然,奶奶出门遇到危险直接一键报警,我就收到信息了".heightAccording(width: QSLConst.qsl_kScreenW - 52.rpx - 12.rpx, font: .textF(15), lineSpacing: 2) + 35.rpx
  889. commentView.addSubview(comment3)
  890. comment3.snp.makeConstraints { make in
  891. make.left.right.equalTo(0)
  892. make.top.equalTo(comment2.snp.bottom).offset(30.rpx)
  893. make.height.equalTo(commentHeight3)
  894. }
  895. // commentView.addSubview(tipTitleLabel)
  896. // tipTitleLabel.snp.makeConstraints { make in
  897. // make.left.equalTo(16)
  898. // make.top.equalTo(comment3.snp.bottom).offset(28)
  899. // }
  900. commentView.addSubview(tipContentLabel)
  901. tipContentLabel.snp.makeConstraints { make in
  902. make.left.equalTo(16)
  903. make.right.equalTo(-16)
  904. make.top.equalTo(comment3.snp.bottom).offset(11)
  905. }
  906. commentView.addSubview(serviceLabel)
  907. serviceLabel.snp.makeConstraints { make in
  908. make.left.equalTo(16)
  909. make.top.equalTo(tipContentLabel.snp.bottom).offset(28.rpx)
  910. }
  911. scrollView.snp.makeConstraints { make in
  912. make.bottom.equalTo(mainView)
  913. }
  914. self.view.addSubview(bottomContentView)
  915. bottomContentView.snp.makeConstraints { make in
  916. make.left.equalTo(12.rpx)
  917. make.right.equalTo(-12.rpx)
  918. make.bottom.equalTo(-QSLConst.qsl_kTabbarBottom)
  919. make.height.equalTo(72.rpx)
  920. }
  921. bottomContentView.addSubview(bottomTopView)
  922. bottomTopView.snp.makeConstraints { make in
  923. make.left.equalToSuperview()
  924. make.right.equalToSuperview()
  925. make.top.equalToSuperview()
  926. make.height.equalTo(60.rpx)
  927. }
  928. bottomTopView.addSubview(bottomTopLabel)
  929. bottomTopLabel.snp.makeConstraints { make in
  930. make.left.equalToSuperview().offset(22)
  931. make.top.equalToSuperview()
  932. make.height.equalTo(21.rpx)
  933. }
  934. bottomContentView.addSubview(bottomView)
  935. bottomView.snp.makeConstraints { make in
  936. make.left.equalToSuperview()
  937. make.right.equalToSuperview()
  938. make.bottom.equalToSuperview()
  939. make.height.equalTo(50.rpx)
  940. }
  941. bottomView.addSubview(unlockBtn)
  942. unlockBtn.snp.makeConstraints { make in
  943. make.right.top.bottom.equalTo(0)
  944. make.width.equalTo(165.rpx)
  945. }
  946. bottomView.addSubview(priceIconLabel)
  947. priceIconLabel.snp.makeConstraints { make in
  948. make.left.equalTo(20.rpx)
  949. make.bottom.equalTo(-12.rpx)
  950. }
  951. bottomView.addSubview(priceLabel)
  952. priceLabel.snp.makeConstraints { make in
  953. make.left.equalTo(priceIconLabel.snp.right).offset(2.rpx)
  954. make.bottom.equalTo(-10.rpx)
  955. }
  956. bottomView.addSubview(goodTypeLabel)
  957. goodTypeLabel.snp.makeConstraints { make in
  958. make.left.equalTo(priceLabel.snp.right).offset(2.rpx)
  959. make.bottom.equalTo(-14.rpx)
  960. }
  961. bottomView.addSubview(goodOriginalPriceLabel)
  962. goodOriginalPriceLabel.snp.makeConstraints { make in
  963. make.left.equalTo(goodTypeLabel.snp.right).offset(2.rpx)
  964. make.centerY.equalTo(goodTypeLabel.snp.centerY)
  965. }
  966. }
  967. }