WanPai.xcodeproj/project.pbxproj
@@ -47,6 +47,7 @@ 133A618D2A4E7AF90066C4E6 /* CustomerContentTCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 133A618B2A4E7AF90066C4E6 /* CustomerContentTCell.xib */; }; 13489E0A2A4C41A400155744 /* ProfileVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13489E082A4C41A400155744 /* ProfileVC.swift */; }; 13489E0B2A4C41A400155744 /* ProfileVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13489E092A4C41A400155744 /* ProfileVC.xib */; }; 134A750A2A5D0D64006D14AE /* RefreshModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134A75092A5D0D64006D14AE /* RefreshModel.swift */; }; 1353D5752A56CA0A00539FCA /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1353D5732A56CA0A00539FCA /* Services.swift */; }; 1353D5762A56CA0A00539FCA /* NetworkRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1353D5742A56CA0A00539FCA /* NetworkRequest.swift */; }; 1355ABFA2A4BE9FF002B25E4 /* WelfareCouponsSubListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1355ABF92A4BE9FF002B25E4 /* WelfareCouponsSubListVC.swift */; }; @@ -292,6 +293,7 @@ 133A618B2A4E7AF90066C4E6 /* CustomerContentTCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CustomerContentTCell.xib; sourceTree = "<group>"; }; 13489E082A4C41A400155744 /* ProfileVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileVC.swift; sourceTree = "<group>"; }; 13489E092A4C41A400155744 /* ProfileVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProfileVC.xib; sourceTree = "<group>"; }; 134A75092A5D0D64006D14AE /* RefreshModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshModel.swift; sourceTree = "<group>"; }; 1353D5732A56CA0A00539FCA /* Services.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = "<group>"; }; 1353D5742A56CA0A00539FCA /* NetworkRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRequest.swift; sourceTree = "<group>"; }; 1355ABF92A4BE9FF002B25E4 /* WelfareCouponsSubListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelfareCouponsSubListVC.swift; sourceTree = "<group>"; }; @@ -586,6 +588,7 @@ isa = PBXGroup; children = ( 1375463F2A57F545001FA77A /* UserModel.swift */, 134A75092A5D0D64006D14AE /* RefreshModel.swift */, ); path = ViewModel; sourceTree = "<group>"; @@ -1523,6 +1526,7 @@ 139A39FA2A413C6500737AFB /* WelfareWeeklyTCell.swift in Sources */, 8DA51DF72A32BE270085F4BA /* Enums.swift in Sources */, 130E402A2A4EC33C003A3D75 /* SearchStoreDetailHeadView.swift in Sources */, 134A750A2A5D0D64006D14AE /* RefreshModel.swift in Sources */, 1304B8582A4EAEBA000D4F2E /* CommonBannerView.swift in Sources */, 8D86DE852A3307A400A754EF /* CourseSubTypeView.swift in Sources */, 8D79A5932A395BF40029874B /* ActivityStudentListVC.swift in Sources */, WanPai/Config/Enums.swift
@@ -1,19 +1,19 @@ // // Enums.swift // WanPai // // Created by 杨锴 on 2023/6/9. // // // Enums.swift // WanPai // // Created by 杨锴 on 2023/6/9. // import Foundation import HandyJSON /// 登录类型 /// 登录类型 enum LoginType{ case pwd,smsCode } /// 支付方式 /// 支付方式 enum PayType:Int{ case aliPay = 0 case wechat = 1 @@ -26,7 +26,7 @@ case activity } /// 获取短信验证码类型 /// 获取短信验证码类型 enum GetSMSCodeType:Int{ case login = 1 case register = 2 @@ -73,7 +73,7 @@ enum HomeItemType:Int,HandyJSONEnum{ //1=报名玩湃课程,2=预约场地,3=报名赛事及活动,4=免费福利,5=线上课程积分,6=购买优惠门票,7=看视频得奖励,8=智慧球场) //1=报名玩湃课程,2=预约场地,3=报名赛事及活动,4=免费福利,5=线上课程积分,6=购买优惠门票,7=看视频得奖励,8=智慧球场) case none = 0 case course = 1 case booking = 2 @@ -146,3 +146,13 @@ case cash = 1 //现金 case coin = 2 //玩湃币 } enum CouponType:Int,HandyJSONEnum{ case none = 0 ///满减券 case discount = 1 /// 代金券 case voucher = 2 /// 体验券 case experience = 3 } WanPai/Model/CommonModels.swift
@@ -43,15 +43,28 @@ struct CourseDetailListModel:HandyJSON { var classHours: Int = 0 var id: Int = 0 var originalPrice: Double = 0 var paymentPrice: Double = 0 var payType: Int = 0 var playPaiCoin: Int = 0 var vipPrice: Double = 0 var originalPrice:Double? var paymentPrice:Double = 0 var payType: PaymentType = .cash var playPaiCoin:Int? var vipPrice:Double? } struct CourseDetailStudentModel:HandyJSON { var age: Int = 0 var id: Int = 0 var name = "" var phone: String = "" } struct CouponInfoModel:HandyJSON{ ///有效时间 var effectiveTime: String = "" /// 优惠内容 var favorable: String = "" var id: Int = 0 var name: String = "" var type:CouponType = .none /// 使用条件 var useCondition: String = "" } WanPai/Network/NetworkRequest.swift
@@ -137,6 +137,10 @@ let content = paramsArray.joined(separator: "&") params += ["sign": "\(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))"] #if DEBUG LogInfo("签名:\(content) ----- \(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))") #endif return self.params } WanPai/Network/Services.swift
@@ -23,8 +23,8 @@ class func homeInfo()->Observable<BaseResponse<HomeStoreModel>>{ let params = ParamsAppender.build(url: All_Url) .interface(url: "/account/api/appUser/queryJoinPlayPai") .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude) .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude.string) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude.string) return NetworkRequest.request(params: params, method: .post, progress: false) } @@ -94,8 +94,8 @@ class func queryCourseList(typeId:Int? = nil,distanceSort:SortType? = nil,salesRanking:SortType? = nil,search:String? = nil,storeId:Int? = nil)->Observable<BaseResponse<[CourseItemModel]>>{ let params = ParamsAppender.build(url: All_Url) .interface(url: "/course/api/course/queryCourseList") .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude) .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude.string) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude.string) .append(key: "coursePackageTypeId", value: typeId) .append(key: "distanceSort", value: distanceSort?.rawValue) .append(key: "salesRanking", value: salesRanking?.rawValue) @@ -113,8 +113,8 @@ class func queryStoreList()->Observable<BaseResponse<[NormalSimpleModel]>>{ let params = ParamsAppender.build(url: All_Url) .interface(url: "/other/base/store/queryStoreLists") .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude) .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude.string) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude.string) return NetworkRequest.request(params: params, method: .post, progress: false) } @@ -122,10 +122,20 @@ let params = ParamsAppender.build(url: All_Url) .interface(url: "/course/api/course/queryCourseInfo") .append(key: "id", value: id) .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude) .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude.string) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude.string) return NetworkRequest.request(params: params, method: .post, progress: true) } class func queryAvaiableCopons(id:Int,price:Double)->Observable<BaseResponse<[CouponInfoModel]>>{ let params = ParamsAppender.build(url: All_Url) .interface(url: "/activity/api/coupon/queryAvailableCouponList") .append(key: "coursePackageId", value: id) .append(key: "lat", value: locationTool.currentLocation?.coordinate.latitude.string) .append(key: "lon", value: locationTool.currentLocation?.coordinate.longitude.string) .append(key: "price", value: "\(price)") return NetworkRequest.request(params: params, method: .post, progress: false) } } // MARK: -- 其他 WanPai/Root/Course/VC/CourseDetailApplyVC.swift
@@ -1,9 +1,9 @@ // // CourseDetailApplyVC.swift // WanPai // // Created by 杨锴 on 2023/6/9. // // // CourseDetailApplyVC.swift // WanPai // // Created by 杨锴 on 2023/6/9. // import UIKit import JQTools @@ -15,20 +15,58 @@ @IBOutlet weak var cons_collectHei: NSLayoutConstraint! @IBOutlet weak var btn_addStudent: QMUIButton! @IBOutlet weak var tableView: UITableView! @IBOutlet weak var cons_collHei: NSLayoutConstraint! @IBOutlet weak var cons_tableHei: NSLayoutConstraint! @IBOutlet weak var btn_coupon: TapBtn! @IBOutlet weak var img_cover: UIImageView! @IBOutlet weak var label_title: UILabel! @IBOutlet weak var label_listenWeek: UILabel! @IBOutlet weak var label_listenTime: UILabel! @IBOutlet weak var label_store: UILabel! @IBOutlet weak var label_address: UILabel! @IBOutlet weak var label_price: UILabel! @IBOutlet weak var label_originPrice: UILabel! @IBOutlet weak var label_vipPrice: UILabel! @IBOutlet weak var label_coin: UILabel! @IBOutlet weak var btn_hasCoupon: TapBtn! @IBOutlet weak var studentTableView: UITableView! var CellW:Double! var CellH:Double! private var detailModel:CourseDetailModel? private var selectClassIndex:Int = 0 private var CellW:Double! private var CellH:Double! private var studentModels = [CourseDetailStudentModel]() private var couponModels = [CouponInfoModel]() override func viewDidLoad() { super.viewDidLoad() title = "课程详情" if let m = detailModel{ img_cover.sd_setImage(with: URL(string: m.coverDrawing)) label_title.text = m.name label_listenWeek.text = m.weeks.joined(separator: "、") label_listenTime.text = m.times label_store.text = m.storeName label_address.text = m.storeAddress changePrice(selectClassIndex) if let stu = m.student{studentModels.append(stu)} cons_collHei.constant = ceil(Double(m.list.count) / 3.0) * CellH + floor(Double(m.list.count) / 3.0) * 21.0 collectionView.reloadData() cons_tableHei.constant = CGFloat(studentModels.count * 87) tableView.reloadData() } } override func setUI() { btn_hasCoupon.isHidden = true CellW = (JQ_ScreenW - 155) / 3.0 CellH = CellW * 0.439 @@ -44,8 +82,22 @@ btn_addStudent.imagePosition = .right btn_addStudent.spacingBetweenImageAndTitle = 3 } init(detailModel:CourseDetailModel) { super.init(nibName: nil, bundle: nil) self.detailModel = detailModel } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @IBAction func couponAction(_ sender: TapBtn) { guard couponModels.count != 0 else { alertError(msg: "暂无优惠券");return } CouponChooseView.show() } @@ -56,7 +108,6 @@ let vc = AddStudentVC() self?.push(vc: vc) } } @IBAction func paymentAction(_ sender: UIButton) { @@ -67,21 +118,85 @@ self.present(vc, animated: true) } } private func changePrice(_ index:Int){ if let subM = detailModel?.list[index]{ label_price.text = subM.paymentPrice.currency() label_originPrice.isHidden = subM.originalPrice == nil label_coin.isHidden = subM.playPaiCoin == nil label_vipPrice.isHidden = subM.vipPrice == nil //原价 if let originPrice = subM.originalPrice{ let attribute = AttributedStringbuilder.build().add(string: originPrice.currency(), withFont: UIFont.systemFont(ofSize: 16, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F").withAlphaComponent(0.58)).underLine(color: UIColor(hexStr: "#3F3F3F").withAlphaComponent(0.58)) label_originPrice.attributedText = attribute.mutableAttributedString } //玩湃币 if let paiCoin = subM.playPaiCoin{ let coinAttribute = AttributedStringbuilder.build() .add(string: "玩湃币:", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F")) .add(string: "\(paiCoin)币", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#F21313")) label_coin.attributedText = coinAttribute.mutableAttributedString } //会员价 if let vipPrice = subM.vipPrice{ let vipAttribute = AttributedStringbuilder.build() .add(string: "会员价:", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F")) .add(string: vipPrice.currency(), withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#F21313")) label_vipPrice.attributedText = vipAttribute.mutableAttributedString } } queryCouponInfo() } private func queryCouponInfo(){ if let subM = detailModel?.list[selectClassIndex]{ var price:Double? switch subM.payType{ case .cash: price = subM.originalPrice == nil ? subM.vipPrice : subM.originalPrice case .coin: if let coin = subM.playPaiCoin{price = Double(coin)} } guard price != nil else { LogError("会员优惠价格出现问题:nil");return } Services.queryAvaiableCopons(id: subM.id, price: price!).subscribe(onNext: { [weak self] data in self?.btn_hasCoupon.isHidden = (data.data?.count ?? 0) == 0 self?.couponModels = data.data ?? [] }).disposed(by: disposeBag) } } } extension CourseDetailApplyVC:UICollectionViewDelegate{ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { selectClassIndex = indexPath.row collectionView.reloadData() changePrice(selectClassIndex) } } extension CourseDetailApplyVC:UICollectionViewDataSource{ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_Common_1_CCell", for: indexPath) as! Common_1_CCell let m = detailModel!.list[indexPath.row] let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_Common_1_CCell", for: indexPath) as! Common_1_CCell cell.isSelected = indexPath.row == selectClassIndex cell.courseDetailListModel = m return cell } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 3 return detailModel?.list.count ?? 0 } } @@ -102,12 +217,12 @@ extension CourseDetailApplyVC:UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 return studentModels.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "_StudentInfoTCell") as! StudentInfoTCell cell.studentModel = studentModels[indexPath.row] return cell } WanPai/Root/Course/VC/CourseDetailApplyVC.xib
@@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina6_12" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/> <capability name="Named colors" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/> @@ -14,9 +14,21 @@ <connections> <outlet property="btn_addStudent" destination="kv0-E8-XGD" id="H56-eE-BCc"/> <outlet property="btn_coupon" destination="XNI-Vj-heV" id="es4-Cw-MCW"/> <outlet property="btn_hasCoupon" destination="XNI-Vj-heV" id="680-zV-BEX"/> <outlet property="collectionView" destination="ZtR-nH-Ly1" id="Snf-0R-x4b"/> <outlet property="cons_collHei" destination="wOm-8j-9fG" id="gZo-vk-a95"/> <outlet property="cons_collectHei" destination="wOm-8j-9fG" id="ycB-BH-9OQ"/> <outlet property="cons_tableHei" destination="YrT-fE-DXP" id="wAi-wu-Afa"/> <outlet property="img_cover" destination="SMj-2r-hml" id="eCI-j3-X6o"/> <outlet property="label_address" destination="dq4-Ry-HOa" id="TTi-yc-w1v"/> <outlet property="label_coin" destination="AuS-Gc-geQ" id="rBY-uI-jPU"/> <outlet property="label_listenTime" destination="gAj-GU-kf7" id="F9v-Rl-tLX"/> <outlet property="label_listenWeek" destination="z0j-zm-BuW" id="6Sj-GF-NyT"/> <outlet property="label_originPrice" destination="hAD-m3-5Ns" id="mBy-fz-n85"/> <outlet property="label_price" destination="O3i-VB-ddE" id="4xG-PL-jeC"/> <outlet property="label_store" destination="OPI-Cq-tpZ" id="Yo2-ua-F9q"/> <outlet property="label_title" destination="FDE-aj-7d9" id="var-Cz-Fnt"/> <outlet property="label_vipPrice" destination="55J-SL-KdW" id="xZ1-NL-dkq"/> <outlet property="studentTableView" destination="CMD-gX-7vo" id="Xu0-eU-5SD"/> <outlet property="tableView" destination="CMD-gX-7vo" id="dC7-Q4-Es3"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> @@ -381,7 +393,7 @@ <nil key="highlightedColor"/> </label> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="8n8-uF-nCb"> <rect key="frame" x="52.666666666666657" y="19.666666666666629" width="57.333333333333343" height="36"/> <rect key="frame" x="52.666666666666657" y="10" width="57.333333333333343" height="55"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="会员价:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hAD-m3-5Ns"> <rect key="frame" x="0.0" y="0.0" width="57.333333333333336" height="17"/> @@ -389,8 +401,14 @@ <color key="textColor" red="0.2470588235" green="0.2470588235" blue="0.2470588235" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="玩湃币:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AuS-Gc-geQ"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="会员价:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="55J-SL-KdW"> <rect key="frame" x="0.0" y="19" width="57.333333333333336" height="17"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/> <color key="textColor" red="0.2470588235" green="0.2470588235" blue="0.2470588235" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="玩湃币:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AuS-Gc-geQ"> <rect key="frame" x="0.0" y="38" width="57.333333333333336" height="17"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/> <color key="textColor" red="0.2470588235" green="0.2470588235" blue="0.2470588235" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> @@ -457,7 +475,7 @@ <resources> <image name="btn_add_1" width="16" height="16"/> <namedColor name="FE6E0D"> <color red="0.99599999189376831" green="0.4309999942779541" blue="0.050999999046325684" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.99199998378753662" green="0.53299999237060547" blue="0.0080000003799796104" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> <systemColor name="systemBackgroundColor"> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> WanPai/Root/Course/VC/CourseDetailVC.swift
@@ -49,28 +49,33 @@ } label_price.text = detailModel.list.first!.paymentPrice.currency() if let subM = detailModel.list.first { label_originPrice.isHidden = subM.originalPrice == 0 label_coin.isHidden = subM.playPaiCoin == 0 label_vipPrice.isHidden = subM.vipPrice == 0 label_originPrice.isHidden = subM.originalPrice == nil label_coin.isHidden = subM.playPaiCoin == nil label_vipPrice.isHidden = subM.vipPrice == nil //原价 let attribute = AttributedStringbuilder.build().add(string: subM.originalPrice.currency(), withFont: UIFont.systemFont(ofSize: 16, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F").withAlphaComponent(0.58)).underLine(color: UIColor(hexStr: "#3F3F3F").withAlphaComponent(0.58)) label_originPrice.attributedText = attribute.mutableAttributedString //原价 if let originPrice = subM.originalPrice{ let attribute = AttributedStringbuilder.build().add(string: originPrice.currency(), withFont: UIFont.systemFont(ofSize: 16, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F").withAlphaComponent(0.58)).underLine(color: UIColor(hexStr: "#3F3F3F").withAlphaComponent(0.58)) label_originPrice.attributedText = attribute.mutableAttributedString } //玩湃币 let coinAttribute = AttributedStringbuilder.build() .add(string: "玩湃币:", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F")) .add(string: "\(subM.playPaiCoin)币", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#F21313")) label_coin.attributedText = coinAttribute.mutableAttributedString //玩湃币 if let paiCoin = subM.playPaiCoin{ let coinAttribute = AttributedStringbuilder.build() .add(string: "玩湃币:", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F")) .add(string: "\(paiCoin)币", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#F21313")) label_coin.attributedText = coinAttribute.mutableAttributedString } //会员价 let vipAttribute = AttributedStringbuilder.build() .add(string: "会员价:", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F")) .add(string: subM.vipPrice.currency(), withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#F21313")) label_vipPrice.attributedText = vipAttribute.mutableAttributedString if let vipPrice = subM.vipPrice{ let vipAttribute = AttributedStringbuilder.build() .add(string: "会员价:", withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#3F3F3F")) .add(string: vipPrice.currency(), withFont: UIFont.systemFont(ofSize: 14, weight: .semibold), withColor: UIColor(hexStr: "#F21313")) label_vipPrice.attributedText = vipAttribute.mutableAttributedString } } @@ -103,7 +108,7 @@ } @IBAction func applyAction(_ sender: UIButton) { let vc = CourseDetailApplyVC() let vc = CourseDetailApplyVC(detailModel: detailModel) push(vc: vc) } WanPai/Root/Course/VC/StudentCourseDetailVC.swift
@@ -30,7 +30,7 @@ title = "课时详情" headView.renewalClouse = { [weak self] () in let vc = CourseDetailApplyVC() let vc = CourseDetailApplyVC(detailModel: CourseDetailModel()) self?.push(vc: vc) } } WanPai/Root/Home/VC/HomeVC.swift
@@ -8,13 +8,22 @@ import UIKit import JQTools import RxSwift import RxCocoa import SDWebImage //http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 //http://vjs.zencdn.net/v/oceans.mp4 //https://media.w3.org/2010/05/sintel/trailer.mp4 class HomeVC: BaseVC,Refreshable{ class HomeViewModel:RefreshModel<HomeStoreConfigModel>{ let storeId = BehaviorRelay<Int>(value: 0) override func api() -> (Observable<BaseResponse<[HomeStoreConfigModel]>>)? { return Services.homeStoreConfig(storeId: 1) } } class HomeVC: BaseVC{ @IBOutlet weak var collectionView: BaseCollectionView! @IBOutlet weak var label_vipInfo: UILabel! @@ -22,9 +31,11 @@ @IBOutlet weak var label_store: UILabel! private var items = Array<HomeStoreConfigModel>() private var storeModel:HomeStoreModel? var viewModel = HomeViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.configure(collectionView,needMore: false) locationTool.startLocation { [weak self] local in locationTool.stopLocation() @@ -35,35 +46,40 @@ self?.getStoreInfo() } refreshStatusBind(to: collectionView,header: { [weak self] () in guard let weakSelf = self,weakSelf.storeModel != nil else { self?.refreshStatus.onNext(.endHeaderRefresh);return } Services.homeStoreConfig(storeId: weakSelf.storeModel!.storeId).subscribe(onNext: {data in if let models = data.data{ self?.items = models let group = DispatchGroup() viewModel.dataSource.subscribe(onNext: {[weak self] data in guard let weakSelf = self else { return } guard data.count != 0 else {return} weakSelf.items = data let group = DispatchGroup() for m in models{ let queue = DispatchQueue(label: "1") queue.async(group: group) { group.enter() SDWebImageDownloader.shared.downloadImage(with: URL(string: m.backgroundImage)) { image, data, error, status in if let i = image{ m.radio = i.size.width / i.size.height } group.leave() } for m in weakSelf.items{ let queue = DispatchQueue(label: "1") queue.async(group: group) { group.enter() SDWebImageDownloader.shared.downloadImage(with: URL(string: m.backgroundImage),options: .scaleDownLargeImages,progress: nil) { image, data, error, status in if let i = image{ m.radio = i.size.width / i.size.height } } group.notify(queue: .main){ self?.refreshStatus.onNext(.endHeaderRefresh) self?.collectionView.reloadData() group.leave() } } }).disposed(by: weakSelf.disposeBag) }) } group.notify(queue: .main){ //重置Layout,不然不更新 self!.layout = WaterFallFlowLayout() self!.layout.cols = 2 self!.layout.sectionInset = UIEdgeInsets(top: 14, left: 34, bottom: 14, right: 34) self!.layout.delegate = self self!.collectionView.collectionViewLayout = self!.layout self!.collectionView.reloadData() } }).disposed(by: disposeBag) } override func setRx() { } override func setUI() { @@ -90,8 +106,8 @@ self?.label_vipInfo.text = text if m.storeId != 0{ self?.storeModel = m self?.refreshStatus.onNext(.beingHeaderRefresh) self?.viewModel.storeId.accept(m.storeId) self?.viewModel.beginRefresh() }else{ self?.defaultData() } WanPai/Root/Other/CCell/Common_1_CCell.swift
@@ -10,9 +10,22 @@ class Common_1_CCell: UICollectionViewCell { @IBOutlet weak var label_content: UILabel! var courseDetailListModel:CourseDetailListModel?{ didSet{ if let m = courseDetailListModel{ label_content.text = "\(m.classHours)课时" label_content.backgroundColor = isSelected ? Def_ThemeColor : .white label_content.textColor = isSelected ? .white : UIColor(hexStr: "#898989") label_content.borderWidth = isSelected ? 0:1 } } } override func awakeFromNib() { super.awakeFromNib() label_content.borderColor = UIColor(hexStr: "#898989") label_content.cornerRadius = 4 } } WanPai/Root/Other/TCell/StudentInfoTCell.swift
@@ -9,6 +9,17 @@ class StudentInfoTCell: UITableViewCell { var studentModel:CourseDetailStudentModel?{ didSet{ label_name.text = studentModel?.name ?? "--" label_phone.text = studentModel?.phone ?? "--" label_age.text = "\(studentModel?.age ?? 0)岁" } } @IBOutlet weak var label_name: UILabel! @IBOutlet weak var label_phone: UILabel! @IBOutlet weak var label_age: UILabel! override func awakeFromNib() { super.awakeFromNib() selectionStyle = .none WanPai/Root/Other/TCell/StudentInfoTCell.xib
@@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina6_12" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -109,6 +109,11 @@ <constraint firstItem="wux-IQ-y3O" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="7" id="d2J-Ta-zpG"/> </constraints> </tableViewCellContentView> <connections> <outlet property="label_age" destination="o0m-fp-glP" id="KJW-vV-H6Z"/> <outlet property="label_name" destination="0TW-3R-Kd5" id="Rxm-mg-OvM"/> <outlet property="label_phone" destination="A9b-WE-sPt" id="obG-aM-y0V"/> </connections> <point key="canvasLocation" x="74.809160305343511" y="45.422535211267608"/> </tableViewCell> </objects> WanPai/Root/Welfare/VC/WelfareWeeklyDetailVC.swift
@@ -15,7 +15,7 @@ } @IBAction func applyAction(_ sender: UIButton) { let vc = CourseDetailApplyVC() let vc = CourseDetailApplyVC(detailModel: CourseDetailModel()) push(vc: vc) } } WanPai/ViewModel/RefreshModel.swift
New file @@ -0,0 +1,188 @@ // // RefreshModel.swift // WanPai // // Created by 无故事王国 on 2023/7/11. // import UIKit import MJRefresh import RxSwift import HandyJSON import RxRelay enum RefreshState { case refreshing case completedRefresh case moreLoading case completedLoad case completedLoadWithNoMoreData } protocol RefreshModelProctol { associatedtype T:HandyJSON func api()->(Observable<BaseResponse<[T]>>)? } class RefreshModel<T:HandyJSON>:RefreshModelProctol{ func api() -> (RxSwift.Observable<BaseResponse<[T]>>)? { return nil } let disposeBag = DisposeBag() enum RefreshType { case refresh,load } private var handle:UIScrollView! lazy var refreshSubject = PublishSubject<RefreshState>() var page:Int = 0 var pageSize:Int = 20 lazy var dataSource = BehaviorRelay<[T]>(value: []) func configure(_ scrollView:UITableView,needMore:Bool = true){ scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) if needMore{ scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) } refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) handle = scrollView } func configure(_ scrollView:UICollectionView,needMore:Bool = true){ scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) if needMore{ scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) } refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) handle = scrollView } func beginRefresh(){ handle.mj_header?.beginRefreshing() } func refreshData() ->(()->Void) { return {self.request(status: .refresh)} } func loadMoreData() ->(()->Void) { return {self.request(status: .load)} } func request(status:RefreshType){ switch status { case .refresh: self.page = 1 self.refreshSubject.onNext(.refreshing) case .load: self.page += 1 self.refreshSubject.onNext(.moreLoading) } api()?.subscribe(onNext: { data in if let datas = data.data,datas.count > 0{ switch status{ case .refresh: self.dataSource.accept(datas) self.refreshSubject.onNext(.completedRefresh) case .load: self.dataSource.accept(self.dataSource.value + datas) self.refreshSubject.onNext(.completedLoad) } }else{ self.refreshSubject.onNext(.completedLoadWithNoMoreData) } }, onError: { error in self.refreshSubject.onNext(.completedLoad) }).disposed(by: disposeBag) } } class CustomRefreshHeaer:MJRefreshNormalHeader{ static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshNormalHeader? { let refreshHeader = MJRefreshNormalHeader(refreshingBlock: refreshingBlock) return refreshHeader } } class CustomRefreshFooter:MJRefreshAutoNormalFooter{ static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshAutoNormalFooter? { let refrehFooter = MJRefreshAutoNormalFooter(refreshingBlock: refreshingBlock) return refrehFooter } } extension Reactive where Base : UITableView { func handlestatus() -> Binder<RefreshState> { return Binder(self.base) { (tableView, status) in switch status { case .moreLoading: self.base.mj_footer?.beginRefreshing() case .refreshing: self.base.reloadData() self.base.mj_footer?.resetNoMoreData() self.base.mj_header?.endRefreshing() case .completedLoadWithNoMoreData: DispatchQueue.main.async { self.base.reloadData() } self.base.mj_footer?.endRefreshingWithNoMoreData() case .completedLoad: DispatchQueue.main.async { self.base.reloadData() } self.base.mj_footer?.endRefreshing() default: DispatchQueue.main.async { self.base.reloadData() } self.base.mj_header?.endRefreshing() self.base.mj_footer?.endRefreshing() } } } } extension Reactive where Base : UICollectionView { func handlestatus() -> Binder<RefreshState> { return Binder(self.base) { (tableView, status) in switch status { case .moreLoading: self.base.mj_footer?.beginRefreshing() case .refreshing: self.base.reloadData() self.base.mj_footer?.resetNoMoreData() self.base.mj_header?.endRefreshing() case .completedLoadWithNoMoreData: DispatchQueue.main.async { self.base.reloadData() } self.base.mj_footer?.endRefreshingWithNoMoreData() case .completedLoad: DispatchQueue.main.async { self.base.reloadData() } self.base.mj_footer?.endRefreshing() default: DispatchQueue.main.async { self.base.reloadData() } self.base.mj_header?.endRefreshing() self.base.mj_footer?.endRefreshing() } } } }