Podfile
@@ -14,6 +14,7 @@ pod 'Alamofire' pod 'Lantern' pod 'SVProgressHUD' pod 'AliyunOSSiOS' post_install do |installer| installer.pods_project.targets.each do |target| WanPai.xcodeproj/project.pbxproj
@@ -48,6 +48,7 @@ 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 */; }; 134BD50A2A5FF72900786819 /* OBSUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134BD5092A5FF72800786819 /* OBSUploader.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 */; }; @@ -294,6 +295,7 @@ 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>"; }; 134BD5092A5FF72800786819 /* OBSUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OBSUploader.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>"; }; @@ -578,6 +580,7 @@ 1353D5722A56CA0A00539FCA /* Network */ = { isa = PBXGroup; children = ( 134BD5092A5FF72800786819 /* OBSUploader.swift */, 1353D5732A56CA0A00539FCA /* Services.swift */, 1353D5742A56CA0A00539FCA /* NetworkRequest.swift */, ); @@ -1461,6 +1464,7 @@ 1376A6A12A4A7E1E00D4C851 /* CourseChargeTCell.swift in Sources */, 13AFABC12A4AE3DC001FEA16 /* StudentRemarkListVC.swift in Sources */, 8D6D58D12A39906F0003CFE6 /* ActivitySignupListSubVC.swift in Sources */, 134BD50A2A5FF72900786819 /* OBSUploader.swift in Sources */, 1375464B2A581716001FA77A /* HomeModel.swift in Sources */, 8D70178D2A330E5700473C40 /* CourseDetailVC.swift in Sources */, 1355ABFD2A4C15C7002B25E4 /* RechargeRecordVC.swift in Sources */, @@ -1687,7 +1691,9 @@ INFOPLIST_FILE = WanPai/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "玩湃生活"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_NSCameraUsageDescription = "WanPai需要使用使用使用相机,来上传学员头像、评论图片"; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "WanPai需要使用使用定位,来推荐、搜索您附近的门店"; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "WanPai需要使用使用使用相册,来上传学员头像、评论图片"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; @@ -1727,7 +1733,9 @@ INFOPLIST_FILE = WanPai/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "玩湃生活"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_NSCameraUsageDescription = "WanPai需要使用使用使用相机,来上传学员头像、评论图片"; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "WanPai需要使用使用定位,来推荐、搜索您附近的门店"; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "WanPai需要使用使用使用相册,来上传学员头像、评论图片"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; WanPai/Config/Def.swift
@@ -63,3 +63,11 @@ func alertSuccess(msg:String){ SVProgressHUD.showSuccess(withStatus: msg) } func showHUD(_ text:String){ SVProgressHUD.show(withStatus:text) } func hiddenHUD(){ SVProgressHUD.dismiss() } WanPai/Config/Enums.swift
@@ -73,15 +73,22 @@ enum HomeItemType:Int,HandyJSONEnum{ //1=报名玩湃课程,2=预约场地,3=报名赛事及活动,4=免费福利,5=线上课程积分,6=购买优惠门票,7=看视频得奖励,8=智慧球场) case none = 0 ///报名玩湃课程 case course = 1 ///预约场地 case booking = 2 ///报名赛事及活动 case activity = 3 ///免费福利 case welfare = 4 ///线上课程积分 case coin = 5 ///购买优惠门票 case ticket = 6 /// 看视频得奖励 case video = 7 ///智慧球场 case wisdomCourt = 8 var defaultImg:UIImage{ @@ -142,6 +149,11 @@ } } enum GenderType:Int,HandyJSONEnum{ case man = 1 case woman = 2 } enum PaymentType:Int,HandyJSONEnum{ case cash = 1 //现金 case coin = 2 //玩湃币 WanPai/Model/CommonModels.swift
@@ -57,6 +57,17 @@ var phone: String = "" } struct StudentProfileModel:HandyJSON{ var birthday = "" var headImg = "" var height:Double = 0 var idCard:String? var name = "" var phone:String? var sex:GenderType = .man var weight:Double = 0 } struct CouponInfoModel:HandyJSON{ ///有效时间 var effectiveTime: String = "" WanPai/Network/OBSUploader.swift
New file @@ -0,0 +1,276 @@ // // OBSUploader.swift // QuanKeTong // // Created by alvin_y on 2019/7/31. // Copyright © 2019 yang-wang. All rights reserved. // import UIKit import RxSwift import RxCocoa import AliyunOSSiOS import JQTools import SwifterSwift public extension UIImage{ func uploadImgToService(name:String = String.jq_randomStr(len:10),scaleSize:CGSize? = nil)->Observable<String>{ return OBSUploader.shared.uploadImage(key: name, image: self, scaleSize: scaleSize) } } public extension URL{ func uploadVideoURLToService(name:String = String.jq_randomStr(len: 10))->Observable<String>{ return OBSUploader.shared.uploadVideo(key: name, data: self) } } public extension Sequence where Iterator.Element == UIImage{ func uploadImgToService(scaleSize:CGSize? = nil,needCompress:Bool? = nil)->Observable<[String]>{ var keys = [String]() var imgs = [UIImage]() for (_,item) in enumerated() { keys.append(String.jq_randomStr(len: 10)) imgs.append(item) } return OBSUploader.shared.uploadImage(keys: keys, image: imgs,scaleSize: scaleSize,needCompress:needCompress) } } public extension Sequence where Iterator.Element == URL{ func uploadVideoToService(progress:OSSNetworkingUploadProgressBlock? = nil)->Observable<[String]>{ var keys = [String]() var datas = [URL]() for (_,item) in enumerated() { keys.append(String.jq_randomStr(len: 10)) datas.append(item) } return OBSUploader.shared.uploadVideo(keys: keys, video: datas,progress: progress) } } /// OBS上传文件 class OBSUploader: NSObject { struct Config { static var oss_domain = "https://we-park-life.oss-cn-beijing.aliyuncs.com/img" static var accessKeyId = "LTAI47eyqWWhBPXM" static var accessKeySecret = "orsYX78NvgXS9KbN7wCwRja1wkUwPc" static var bucketName = "we-park-life" static var endpoint = "oss-cn-beijing.aliyuncs.com" } static let shared = OBSUploader() lazy var credential: OSSCustomSignerCredentialProvider = OSSCustomSignerCredentialProvider(implementedSigner: { sign, error in var sign = OSSUtil.calBase64Sha1(withData: sign, withSecret: Config.accessKeySecret) if sign == nil { fatalError() } return "OSS \(Config.accessKeyId):\(sign!)" })! lazy var ossClient: OSSClient = OSSClient(endpoint: Config.endpoint, credentialProvider: credential) func uploadImage(keys: [String], image: [UIImage],scaleSize:CGSize? = nil,needCompress:Bool? = nil) -> Observable<[String]> { if keys.count == 0 { return Observable.just([]) } var ob: [Observable<String>] = [] let count = keys.count for i in 0..<count { ob.append(createTask(key: keys[i], image: image[i],scaleSize: scaleSize,needCompress:needCompress)) } return Observable.zip(ob) } func uploadVideo(keys: [String], video: [URL],progress:OSSNetworkingUploadProgressBlock? = nil) -> Observable<[String]> { if keys.count == 0 { return Observable.just([]) } var ob: [Observable<String>] = [] let count = keys.count for i in 0..<count { ob.append(createTask(key: keys[i], url: video[i],progress: progress)) } return Observable.zip(ob) } func uploadVideo(key:String,data:URL,progress:OSSNetworkingUploadProgressBlock? = nil) -> Observable<String>{ if key.isEmpty {return Observable.just("")} var videoData:Data? do { videoData = try Data(contentsOf: data) } catch { return Observable.just("") } let client = ossClient return Observable<String>.create { ob in let put = OSSPutObjectRequest() put.bucketName = Config.bucketName put.objectKey = "\(key).mov" put.uploadingData = videoData! put.uploadProgress = { a,b,c in progress?(a,b,c) } let task = client.putObject(put) task.continue({ r in if r.error == nil { ob.onNext("\(Config.oss_domain)/\(key).mov") } else { ob.onError(VideoUploadError.Failed) } return nil }) return Disposables.create{} }.observe(on: MainScheduler.instance) } func uploadImage(key: String, image: UIImage,scaleSize:CGSize?) -> Observable<String> { if key.isEmpty { return Observable.just("") } else { let client = ossClient return Observable<String>.create{ ob in let put = OSSPutObjectRequest() put.bucketName = Config.bucketName put.objectKey = "\(key).png" if scaleSize != nil{ put.uploadingData = image.jq_scaled(to: scaleSize!).pngData()! }else{ put.uploadingData = image.pngData()! } let task = client.putObject(put) task.continue({ r in if r.error == nil { ob.onNext("\(Config.oss_domain)/\(key).png") } else { ob.onError(ImageUploadError.Failed) } return nil }) return Disposables.create{ } }.observe(on: MainScheduler.instance) } } /// 上传Data数据 /// - Parameters: /// - key: XXXX.png/Gif等 /// - data: 数据 func uploadData(key:String,data:Data)->Observable<String>{ if key.isEmpty{ return Observable.just("") }else{ let client = ossClient return Observable<String>.create{ ob in let put = OSSPutObjectRequest() put.bucketName = Config.bucketName put.objectKey = key put.uploadingData = data let task = client.putObject(put) task.continue({ r in if r.error == nil { ob.onNext("\(Config.oss_domain)/\(key)") } else { ob.onError(ImageUploadError.Failed) } return nil }) return Disposables.create{ } }.observe(on: MainScheduler.instance) } } func createTask(key: String, image: UIImage,scaleSize:CGSize? = nil,needCompress:Bool? = nil) -> Observable<String> { let client = ossClient return Observable<String>.create{ ob in let put = OSSPutObjectRequest() put.bucketName = Config.bucketName put.objectKey = "\(key).png" var img:UIImage? if scaleSize != nil{ img = image.jq_scaled(to: scaleSize!) }else if needCompress != nil && needCompress!{ img = image.jq_resizeImage() }else{ img = image } put.uploadingData = img!.pngData()! let task = client.putObject(put) task.continue({ r in if r.error == nil { ob.onNext("\(Config.oss_domain)/\(key).png") } else { ob.onError(ImageUploadError.Failed) } return nil }) return Disposables.create{ } }.observe(on: MainScheduler.instance) } func createTask(key: String, url: URL,progress:OSSNetworkingUploadProgressBlock? = nil) -> Observable<String> { if key.isEmpty{return Observable.just("")} var videoData:Data? do { videoData = try Data(contentsOf: url) } catch { return Observable.just("") } let client = ossClient return Observable<String>.create{ ob in let put = OSSPutObjectRequest() put.bucketName = Config.bucketName put.objectKey = "\(key).mov" put.uploadingData = videoData! put.uploadProgress = { a,b,c in progress?(a,b,c) } let task = client.putObject(put) task.continue({ r in if r.error == nil { ob.onNext("\(Config.oss_domain)/\(key).mov") } else { ob.onError(ImageUploadError.Failed) } return nil }) return Disposables.create{ } }.observe(on: MainScheduler.instance) } } enum ImageUploadError: Error { case Failed } enum VideoUploadError:Error{ case Failed } WanPai/Network/Services.swift
@@ -136,6 +136,29 @@ .append(key: "price", value: "\(price)") return NetworkRequest.request(params: params, method: .post, progress: false) } /// 获取学员列表 class func queryStudentList()->Observable<BaseResponse<[CourseDetailStudentModel]>>{ let params = ParamsAppender.build(url: All_Url) .interface(url: "/account/api/student/queryStudentList") return NetworkRequest.request(params: params, method: .post, progress: false) } /// 添加学员 class func addStudent(_ model:StudentProfileModel)->Observable<BaseResponse<SimpleModel>>{ let params = ParamsAppender.build(url: All_Url) .interface(url: "/account/api/startCource/addData") .append(key: "birthday", value: model.birthday) .append(key: "headImg", value: model.headImg) .append(key: "height", value: model.height.string) .append(key: "idCard", value: model.idCard) .append(key: "name", value: model.name) .append(key: "phone", value: model.phone) .append(key: "sex", value: model.sex.rawValue) .append(key: "weight", value: model.weight.string) return NetworkRequest.request(params: params, method: .post, progress: false) } } // MARK: -- 其他 WanPai/Root/Activity/VC/ActivityDetailVC.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"/> @@ -542,8 +542,8 @@ <constraint firstItem="fnl-2z-Ty3" firstAttribute="bottom" secondItem="b8k-c2-Sjg" secondAttribute="bottom" constant="48" id="ALd-Ad-n3a"/> <constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="b8k-c2-Sjg" secondAttribute="trailing" id="Rhl-P3-wPu"/> <constraint firstItem="lNv-6h-9fm" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="Rpd-eD-GWI"/> <constraint firstItem="b8k-c2-Sjg" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="Tak-Fe-kgU"/> <constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="lNv-6h-9fm" secondAttribute="trailing" id="U5X-Wh-bJU"/> <constraint firstItem="b8k-c2-Sjg" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="c3T-5o-vuv"/> <constraint firstItem="b8k-c2-Sjg" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="jmS-ku-wrt"/> </constraints> <point key="canvasLocation" x="136.64122137404578" y="20.422535211267608"/> @@ -552,7 +552,7 @@ <resources> <image name="icon_local_mini" width="11" 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/AddStudentVC.swift
@@ -6,11 +6,24 @@ // import UIKit import QMUIKit import JQTools class AddStudentVC: BaseVC { @IBOutlet weak var img_profile: UIImageView! @IBOutlet weak var tf_name: UITextField! @IBOutlet weak var tf_gender: UITextField! @IBOutlet weak var tf_idCard: UITextField! @IBOutlet weak var tf_height: QMUITextField! @IBOutlet weak var tf_weight: QMUITextField! @IBOutlet weak var tf_phone: QMUITextField! @IBOutlet weak var tf_birthday: UITextField! var profileImg:UIImage? var studentModel = StudentProfileModel() var verifyIdCard:Bool = false override func viewDidLoad() { @@ -22,11 +35,111 @@ self.tf_idCard.text = text } tf_idCard.inputView = idCardView let tap = UITapGestureRecognizer(target: self, action: #selector(userImgUploadAction)) img_profile.isUserInteractionEnabled = true img_profile.addGestureRecognizer(tap) } override func setUI() { tf_birthday.delegate = self tf_gender.delegate = self } @objc func userImgUploadAction(){ JQ_ImagePickerTool.getSharedInstance().singleImage({ [weak self] image in self?.img_profile.image = image self?.profileImg = image }, clipSize: CGSize(width: JQ_ScreenW, height: JQ_ScreenW)) } @IBAction func completeAction(_ sender: UIButton) { guard profileImg != nil else {alertError(msg: "请上传学员头像");return} guard !tf_name.text!.isEmpty else {alertError(msg: tf_name.placeholder!);return} guard !tf_birthday.text!.isEmpty else {alertError(msg: "请选择生日");return} guard !tf_height.text!.isEmpty else {alertError(msg: tf_height.placeholder!);return} guard !tf_weight.text!.isEmpty else {alertError(msg: tf_weight.placeholder!);return} guard tf_height.text != "0" else {alertError(msg: "请输入正确的身高");return} guard tf_weight.text != "0" else {alertError(msg: "请输入正确的体重");return} if !tf_phone.isEmpty{ #if DEBUG guard tf_phone.text!.jq_isPhone else {alertError(msg: "请输入正确的手机号");return} #endif } if !tf_idCard.isEmpty{ #if DEBUG guard tf_idCard.text!.jq_idCard() else {alertError(msg: "请输入正确的身份证号码");return} #endif } studentModel.birthday = tf_birthday.text! studentModel.name = tf_name.text! studentModel.height = tf_height.text!.toDouble studentModel.weight = tf_weight.text!.toDouble studentModel.phone = tf_phone.text studentModel.idCard = tf_idCard.text studentModel.name = tf_name.text! if studentModel.headImg.isEmpty{ showHUD("正在上传头像") profileImg!.uploadImgToService().subscribe(onNext: { [weak self] imgUrl in guard let weakSelf = self else { return } weakSelf.studentModel.headImg = imgUrl hiddenHUD() weakSelf.addStudent() }, onError: { error in hiddenHUD() alertError(msg: error.localizedDescription) }).disposed(by: disposeBag) }else{ addStudent() } } private func addStudent(){ Services.addStudent(studentModel).subscribe(onNext: { [weak self] data in guard let weakSelf = self else { return } alertSuccess(msg: "添加成功") DispatchQueue.main.asyncAfter(deadline: .now()+1) { weakSelf.navigationController?.popViewController() } }).disposed(by: disposeBag) } deinit{ JQ_ImagePickerTool.destroy() } } extension AddStudentVC:UITextFieldDelegate{ func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { if textField == tf_birthday{ CommonDatePickerView.show(before: 18, after: 0, type: .YMD) { year, month, day in textField.text = String(format: "%ld-%02ld-%02ld", year,month,day!) } return false } if textField == tf_gender{ let alertVC = UIAlertController(title: "性别", message: nil, preferredStyle: .actionSheet) alertVC.addAction(UIAlertAction(title: "男", style: .default) { _ in self.tf_gender.text = "男" }) alertVC.addAction(UIAlertAction(title: "女", style: .default) { _ in self.tf_gender.text = "女" }) alertVC.addAction(UIAlertAction(title: "取消", style: .cancel)) present(alertVC, animated: true) return false } return true } } WanPai/Root/Course/VC/AddStudentVC.xib
@@ -12,7 +12,14 @@ <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AddStudentVC" customModule="WanPai" customModuleProvider="target"> <connections> <outlet property="tf_idCard" destination="LD0-ej-DmH" id="cwL-wm-Ryg"/> <outlet property="img_profile" destination="5oh-CW-AiU" id="lde-3f-DOi"/> <outlet property="tf_birthday" destination="Gwh-N7-mRQ" id="Xsk-gB-nJj"/> <outlet property="tf_gender" destination="5l9-vJ-vil" id="Ch0-JK-GqD"/> <outlet property="tf_height" destination="Oj3-XX-8wG" id="K0c-bB-oPl"/> <outlet property="tf_idCard" destination="LD0-ej-DmH" id="Zob-2g-SgG"/> <outlet property="tf_name" destination="C9O-Cu-rp7" id="vcf-w1-Qts"/> <outlet property="tf_phone" destination="eT7-mO-3I8" id="1JO-Qe-MwA"/> <outlet property="tf_weight" destination="XDz-T3-HBc" id="POj-aw-Njj"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> </connections> </placeholder> @@ -372,6 +379,9 @@ <real key="value" value="20"/> </userDefinedRuntimeAttribute> </userDefinedRuntimeAttributes> <connections> <action selector="completeAction:" destination="-1" eventType="touchUpInside" id="xon-Wf-ldf"/> </connections> </button> </subviews> <viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/> WanPai/Root/Course/VC/CourseDetailApplyVC.swift
@@ -102,8 +102,8 @@ } @IBAction func studentAction(_ sender: QMUIButton) { StudentChooseView.show(itemType:.course) { StudentChooseView.show(itemType:.course,defaultStu: detailModel?.student) { } needAddClouse: { [weak self] () in let vc = AddStudentVC() self?.push(vc: vc) WanPai/Root/Course/VC/StudentCourseDetailVC.swift
@@ -131,7 +131,9 @@ } @objc func datetimePickerAction(){ CommonDatePickerView.show() CommonDatePickerView.show { year, month, day in } } required init?(coder: NSCoder) { WanPai/Root/Home/VC/HomeVC.swift
@@ -148,41 +148,40 @@ extension HomeVC:UICollectionViewDelegate{ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.row == 0{ let vc = CourseListVC() push(vc: vc) } if indexPath.row == 1{ let vc = ActivityListVC() push(vc: vc) } if indexPath.row == 2{ let vc = CourseOnlineListVC() vc.title = "看视频得奖励" push(vc: vc) } // let vc = JQ_CommonWebViewController(url: "https://whctw-qa.whitecoat.global/#/packageA/shares/index?booking_id=1b16387e-7dc4-4a0f-beb0-c919caf46fd2&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoibWVtYmVyIiwibmFtZWlkIjoicnlwL2ZIK3Urck81TnBrQ3VtUnl1OW10dUUxb1JibXExUnVEbytjaXVaQWcxbTNLZzZ0S2daM1R1VDRqK3BIcyIsInJmci10a24iOiIwMDA2MDEwNjEwMjA4Ny1EOTU4ODk2My00QTZBLTRCMDgtQTQ0Ri1GODg1RkZCQTkzQ0MiLCJyZXF1ZXN0X3VwZGF0ZV9jb250YWN0X2luZm8iOiJGYWxzZSIsInJlcXVlc3RfYWN0aXZhdGVfYWNjb3VudCI6IkZhbHNlIiwiZm9yX2NoaWxkX3RoYXRfdHVybmVkXzIxIjoiRmFsc2UiLCJkZXZpY2VfaWQiOiIiLCJkZXZpY2VfdHlwZSI6IjEiLCJpc3MiOiJodHRwczovL3doY2FwaS1xYS53aGl0ZWNvYXQuZ2xvYmFsLyIsImF1ZCI6IjQxNGUxOTI3YTM4ODRmNjhhYmM3OWY3MjgzODM3ZmQxIiwiZXhwIjoxNzIwNjA1ODE3LCJuYmYiOjE2ODkwNjk4MTd9.T5hMybqAV7W_FuqX9WlPkZjgFBzkjw_XP5RGxlN2aUQ&device_id=raBw19aP3NkWnm8g93LOPmZ7iSffnu%3Ftoken%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoibWVtYmVyIiwibmFtZWlkIjoicnlwL2ZIK3Urck81TnBrQ3VtUnl1OW10dUUxb1JibXExUnVEbytjaXVaQWcxbTNLZzZ0S2daM1R1VDRqK3BIcyIsInJmci10a24iOiIwMDA2MDEwNjEwMjA4Ny1EOTU4ODk2My00QTZBLTRCMDgtQTQ0Ri1GODg1RkZCQTkzQ0MiLCJyZXF1ZXN0X3VwZGF0ZV9jb250YWN0X2luZm8iOiJGYWxzZSIsInJlcXVlc3RfYWN0aXZhdGVfYWNjb3VudCI6IkZhbHNlIiwiZm9yX2NoaWxkX3RoYXRfdHVybmVkXzIxIjoiRmFsc2UiLCJkZXZpY2VfaWQiOiIiLCJkZXZpY2VfdHlwZSI6IjEiLCJpc3MiOiJodHRwczovL3doY2FwaS1xYS53aGl0ZWNvYXQuZ2xvYmFsLyIsImF1ZCI6IjQxNGUxOTI3YTM4ODRmNjhhYmM3OWY3MjgzODM3ZmQxIiwiZXhwIjoxNzIwNjA1ODE3LCJuYmYiOjE2ODkwNjk4MTd9.T5hMybqAV7W_FuqX9WlPkZjgFBzkjw_XP5RGxlN2aUQ&device_id=raBw19aP3NkWnm8g93LOPmZ7iSffnu") // vc.modalPresentationStyle = .fullScreen // present(vc, animated: true) // push(vc: vc) if indexPath.row == 3{ let vc = CourseOnlineListVC() vc.title = "线上课得积分" push(vc: vc) } let item = items[indexPath.row] if indexPath.row == 4{ let vc = YardListVC() push(vc: vc) } if indexPath.row == 5{ let vc = WelfareFreeVC() push(vc: vc) } if indexPath.row == 7{ let vc = WelfareWeeklyListVC() push(vc: vc) switch item.type{ case .course: let vc = CourseListVC() push(vc: vc) case .booking: let vc = YardListVC() push(vc: vc) case .activity: let vc = ActivityListVC() push(vc: vc) case .video: let vc = CourseOnlineListVC() vc.title = "看视频得奖励" push(vc: vc) case .coin: let vc = CourseOnlineListVC() vc.title = "线上课得积分" push(vc: vc) case .ticket:break case .wisdomCourt: let vc = WelfareWeeklyListVC() push(vc: vc) case .welfare: let vc = WelfareFreeVC() push(vc: vc) case .none:break } } WanPai/Root/Other/TCell/StudentInfoTCell.swift
@@ -19,6 +19,7 @@ @IBOutlet weak var label_name: UILabel! @IBOutlet weak var label_phone: UILabel! @IBOutlet weak var label_age: UILabel! @IBOutlet weak var btn_handle: UIButton! override func awakeFromNib() { super.awakeFromNib() WanPai/Root/Other/TCell/StudentInfoTCell.xib
@@ -110,6 +110,7 @@ </constraints> </tableViewCellContentView> <connections> <outlet property="btn_handle" destination="UXK-s8-fn9" id="icG-R5-OUD"/> <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"/> WanPai/Root/Other/View/CommonDatePickerView.swift
@@ -1,22 +1,28 @@ // // CommonDatePickerView.swift // WanPai // // Created by 无故事王国 on 2023/6/27. // // // CommonDatePickerView.swift // WanPai // // Created by 无故事王国 on 2023/6/27. // import UIKit import JQTools class CommonDatePickerView: UIView,JQNibView{ enum DatePickerType{ case YMD,YM } @IBOutlet weak var view_container: UIView! @IBOutlet weak var cons_bottom: NSLayoutConstraint! @IBOutlet weak var pickerView: UIPickerView! private var type:DatePickerType! private var years = [Int]() private var months = [1,2,3,4,5,6,7,8,9,10,11,12] private var days = [Int]() private var clouse:((Int,Int,Int?)->Void)? override func awakeFromNib() { super.awakeFromNib() @@ -25,16 +31,32 @@ layoutIfNeeded() pickerView.delegate = self pickerView.dataSource = self years.append(Date().jq_nowYear()) years.append(Date().jq_nowYear()-1) years.append(Date().jq_nowYear()-2) years.append(Date().jq_nowYear()-3) } static func show(){ /// 初始化 /// - Parameters: /// - before: 向上N年 /// - after: 向下N年 /// - type: 显示状态 static func show(before:Int = 0,after:Int = 0,type:DatePickerType = .YM,clouse:@escaping (Int,Int,Int?)->Void){ let pickerView = CommonDatePickerView.jq_loadNibView() pickerView.type = type pickerView.clouse = clouse if before > 0{ for i in 0...before{ pickerView.years.append(Date().jq_nowYear() - i) } } if after > 0{ for i in 0...after{ pickerView.years.append(Date().jq_nowYear() + i) } } pickerView.years = pickerView.years.sorted() pickerView.frame = screnDelegate?.window?.frame ?? .zero screnDelegate?.window?.addSubview(pickerView) @@ -43,6 +65,8 @@ pickerView.alpha = 1 pickerView.layoutIfNeeded() } pickerView.pickerView.reloadAllComponents() } @IBAction func hiddenAction(_ sender: UIButton) { @@ -55,6 +79,36 @@ } } @IBAction func completeAction(_ sender: UIButton) { var year:Int! var month:Int! var day:Int? switch type{ case .YMD: year = years[pickerView.selectedRow(inComponent: 0)] month = months[pickerView.selectedRow(inComponent: 1)] day = pickerView.selectedRow(inComponent: 2) + 1 case .YM: year = years[pickerView.selectedRow(inComponent: 0)] month = months[pickerView.selectedRow(inComponent: 1)] case .none:break } cons_bottom.constant = -(JQ_ScreenW * 0.6974) UIView.animate(withDuration: 0.4) { self.alpha = 0 self.layoutIfNeeded() } completion: { _ in self.removeFromSuperview() self.clouse?(year,month,day) } } override func layoutSubviews() { super.layoutSubviews() view_container.jq_addCorners(corner: [.topLeft,.topRight], radius: 20) @@ -65,6 +119,10 @@ func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { if component == 0{ pickerView.reloadComponent(1) if type == .YMD{pickerView.reloadComponent(2)} } if component == 1{ pickerView.reloadComponent(2) } } } @@ -72,21 +130,28 @@ extension CommonDatePickerView:UIPickerViewDataSource{ func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if component == 0{ return years.count } if component == 0{return years.count if component == 1 && pickerView.selectedRow(inComponent: 0) == 0{ return Date().jq_nowMonth() }else if component == 1{ if component == 1 && pickerView.selectedRow(inComponent: 0) == 0{ return Date().jq_nowMonth() } return months.count }else if component == 2{ let year = years[pickerView.selectedRow(inComponent: 0)] let month = months[pickerView.selectedRow(inComponent: 1)] return Date.jq_getDays(year, month) } return months.count return 0 } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 2 switch type{ case .YM:return 2 case .YMD:return 3 case .none:return 0 } } func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return 55 @@ -95,7 +160,10 @@ func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { if component == 0{ return "\(years[row])年" }else if component == 1{ return "\(months[row])月" }else{ return "\(row + 1)日" } return "\(months[row])月" } } WanPai/Root/Other/View/CommonDatePickerView.xib
@@ -34,6 +34,9 @@ <real key="value" value="20"/> </userDefinedRuntimeAttribute> </userDefinedRuntimeAttributes> <connections> <action selector="completeAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="O51-A6-ZdT"/> </connections> </button> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KFX-Q3-Kqk"> <rect key="frame" x="41" y="178" width="145.66666666666666" height="40"/> WanPai/Root/Other/View/StudentChooseView.swift
@@ -8,6 +8,14 @@ import UIKit import JQTools import QMUIKit import RxSwift import RxCocoa class StudentViewModel:RefreshModel<CourseDetailStudentModel>{ override func api() -> (Observable<BaseResponse<[CourseDetailStudentModel]>>)? { return Services.queryStudentList() } } class StudentChooseView: UIView,JQNibView{ @@ -19,6 +27,8 @@ private var clickClouse:(()->Void)! private var needAddClouse:(()->Void)! private var itemType:ItemType! private var selectStudents = [CourseDetailStudentModel]() private var viewModel = StudentViewModel() override func awakeFromNib() { super.awakeFromNib() @@ -30,10 +40,17 @@ tableView.separatorStyle = .none alpha = 0 layoutIfNeeded() viewModel.configure(tableView,needMore: false) viewModel.beginRefresh() } static func show(itemType:ItemType,clickClouse:@escaping ()->Void,needAddClouse:@escaping ()->Void){ static func show(itemType:ItemType,defaultStu:CourseDetailStudentModel? = nil,clickClouse:@escaping ()->Void,needAddClouse:@escaping ()->Void){ let studentChooseView = StudentChooseView.jq_loadNibView() if defaultStu != nil{ studentChooseView.selectStudents.append(defaultStu!) } if itemType == .course{ studentChooseView.tableView.register(UINib(nibName: "StudentInfoTCell", bundle: nil), forCellReuseIdentifier: "_StudentInfoTCell") }else if itemType == .activity{ @@ -55,15 +72,13 @@ } } @IBAction func closeAction(_ sender: UIButton) { closeAction() } @IBAction func addNewStudentAction(_ sender: QMUIButton) { self.cons_bottom.constant = -(JQ_ScreenW * 1.1) UIView.animate(withDuration: 0.4) { self.alpha = 0 } completion: { _ in self.removeFromSuperview() self.needAddClouse!() } needAddClouse!() closeAction() } @@ -73,33 +88,55 @@ self.view_container.jq_addCorners(corner: [.topLeft,.topRight], radius: 20) } } @IBAction func completeAction(_ sender: UIButton) { private func closeAction(){ self.cons_bottom.constant = -(JQ_ScreenW * 1.1) UIView.animate(withDuration: 0.4) { self.alpha = 0 self.layoutIfNeeded() } completion: { _ in self.removeFromSuperview() self.clickClouse!() } } @IBAction func completeAction(_ sender: UIButton) { clickClouse!() closeAction() } } extension StudentChooseView:UITableViewDelegate{ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let item = viewModel.dataSource.value[indexPath.row] if self.selectStudents.contains(where: {$0.id == item.id}){ if self.selectStudents.count == 1{ alert(msg: "至少选择一位学员");return } self.selectStudents.remove(at: indexPath.row) }else{ self.selectStudents.append(item) } tableView.reloadData() } } extension StudentChooseView:UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 5 return viewModel.dataSource.value.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if itemType == .course{ let item = viewModel.dataSource.value[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "_StudentInfoTCell") as! StudentInfoTCell if selectStudents.contains(where: {$0.id == item.id}){ cell.btn_handle.setImage(UIImage(named: "btn_choose_s"), for: .normal) }else{ cell.btn_handle.setImage(nil, for: .normal) } cell.studentModel = item return cell }else if itemType == .activity{ let cell = tableView.dequeueReusableCell(withIdentifier: "_StudentInfo_2_TCell") as! StudentInfo_2_TCell WanPai/Root/Other/View/StudentChooseView.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="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> @@ -74,12 +74,23 @@ <constraint firstItem="yxv-pI-vBX" firstAttribute="top" secondItem="7ap-MY-Ney" secondAttribute="top" constant="23" id="vTH-12-aze"/> </constraints> </view> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SpM-LP-wsu"> <rect key="frame" x="0.0" y="0.0" width="393" height="509"/> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <connections> <action selector="closeAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="yBy-d7-naf"/> </connections> </button> </subviews> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstItem="7ap-MY-Ney" firstAttribute="top" secondItem="SpM-LP-wsu" secondAttribute="bottom" id="9DJ-dZ-09M"/> <constraint firstItem="7ap-MY-Ney" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="9DX-KP-mJX"/> <constraint firstAttribute="trailing" secondItem="SpM-LP-wsu" secondAttribute="trailing" id="Iv2-Qs-iBg"/> <constraint firstAttribute="trailing" secondItem="7ap-MY-Ney" secondAttribute="trailing" id="Srr-Wv-dah"/> <constraint firstItem="SpM-LP-wsu" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="bKQ-hj-0tc"/> <constraint firstAttribute="bottom" secondItem="7ap-MY-Ney" secondAttribute="bottom" id="kSV-Gg-P7I"/> <constraint firstItem="SpM-LP-wsu" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="nXa-Iu-gYY"/> </constraints> <connections> <outlet property="btn_add" destination="yxv-pI-vBX" id="5Sm-2V-OfF"/> @@ -93,7 +104,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/Welfare/VC/RechargeRecordVC.swift
@@ -45,7 +45,9 @@ } @IBAction func datetimeAction(_ sender: UIButton) { CommonDatePickerView.show() CommonDatePickerView.show { year, month, day in } } WanPai/Root/Welfare/VC/WelfareBillListVC.swift
@@ -129,7 +129,9 @@ } @objc func datetimePickerAction(){ CommonDatePickerView.show() CommonDatePickerView.show{ year, month, day in } } required init?(coder: NSCoder) { WanPai/Root/Welfare/VC/WelfareFreeVC.swift
@@ -13,16 +13,4 @@ super.viewDidLoad() title = "免费福利" } /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destination. // Pass the selected object to the new view controller. } */ }