OOAttendanceCheckInNewController.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. //
  2. // OOAttendanceCheckInNewController.swift
  3. // O2Platform
  4. //
  5. // Created by FancyLou on 2020/7/21.
  6. // Copyright © 2020 zoneland. All rights reserved.
  7. //
  8. import UIKit
  9. import CocoaLumberjack
  10. class OOAttendanceCheckInNewController: UIViewController {
  11. @IBOutlet weak var checkInBtnTimeLabel: UILabel!
  12. @IBOutlet weak var checkInBtnLable: UILabel!
  13. @IBOutlet weak var checkInBtnView: UIView!
  14. @IBOutlet weak var locationCheckImageView: UIImageView!
  15. @IBOutlet weak var locationLabel: UILabel!
  16. @IBOutlet weak var schedulesView: UICollectionView!
  17. fileprivate let itemNumberInRow = 2
  18. //定位
  19. private var userLocation: BMKUserLocation!
  20. private var locService: BMKLocationManager!
  21. private var searchAddress: BMKGeoCodeSearch!
  22. private var workPlaces: [OOAttandanceWorkPlace] = []
  23. //定时器
  24. private var timer: Timer?
  25. private lazy var viewModel: OOAttandanceViewModel = {
  26. return OOAttandanceViewModel()
  27. }()
  28. //打卡和班次
  29. private var schedules: [OOAttandanceMobileScheduleInfo] = []
  30. private var lastRecord: OOAttandanceMobileDetail? = nil
  31. private var needCheckIn = false
  32. private var isInWorkPlace = false
  33. private var bmkResult: BMKReverseGeoCodeSearchResult? = nil
  34. //打卡对象
  35. var checkinForm: OOAttandanceMobileCheckinForm = OOAttandanceMobileCheckinForm()
  36. override func viewDidLoad() {
  37. super.viewDidLoad()
  38. self.schedulesView.delegate = self
  39. self.schedulesView.dataSource = self
  40. self.schedulesView.register(UINib(nibName: "OOAttendanceScheduleViewCell", bundle: nil), forCellWithReuseIdentifier: "OOAttendanceScheduleViewCell")
  41. self.checkInBtnView.addTapGesture { (tap) in
  42. self.postCheckinButton(nil)
  43. }
  44. //初始化定时器
  45. self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timeTick), userInfo: nil, repeats: true)
  46. //获取数据
  47. self.loadMyRecords()
  48. //工作地址
  49. self.loadWorkPlace()
  50. }
  51. override func viewWillAppear(_ animated: Bool) {
  52. self.timer?.fire()
  53. self.startLocationService()
  54. }
  55. override func viewWillDisappear(_ animated: Bool) {
  56. self.timer?.invalidate()
  57. self.stopLocationService()
  58. }
  59. ///打卡
  60. private func postCheckinButton(_ scheduleInfo: OOAttandanceMobileScheduleInfo?) {
  61. if !self.needCheckIn {
  62. self.showError(title: "当前不需要打卡!")
  63. return
  64. }
  65. if !self.isInWorkPlace {
  66. self.showError(title: "不在打卡范围内!")
  67. return
  68. }
  69. if let info = scheduleInfo, info.recordId != nil { //更新打卡
  70. self.showDefaultConfirm(title: "更新打卡", message: "确定要更新这条打卡数据吗?") { (action) in
  71. self.showLoading()
  72. self.checkinForm.id = info.recordId
  73. self.checkinForm.recordAddress = self.bmkResult?.address
  74. self.checkinForm.desc = self.bmkResult?.sematicDescription
  75. self.checkinForm.longitude = String(self.bmkResult?.location.longitude ?? 0.0)
  76. self.checkinForm.latitude = String(self.bmkResult?.location.latitude ?? 0.0)
  77. self.checkinForm.empNo = O2AuthSDK.shared.myInfo()?.employee
  78. self.checkinForm.empName = O2AuthSDK.shared.myInfo()?.name
  79. let currenDate = Date()
  80. self.checkinForm.recordDateString = currenDate.toString("yyyy-MM-dd")
  81. self.checkinForm.signTime = currenDate.toString("HH:mm:ss")
  82. self.checkinForm.optMachineType = UIDevice.deviceModelReadable()
  83. self.checkinForm.optSystemName = "\(UIDevice.systemName()) \(UIDevice.systemVersion())"
  84. self.checkinForm.checkin_type = info.checkinType
  85. self.viewModel.postMyCheckin(self.checkinForm) { (result) in
  86. DispatchQueue.main.async {
  87. self.hideLoading()
  88. }
  89. switch result {
  90. case .ok(_):
  91. self.loadMyRecords()
  92. break
  93. case .fail(let errorMessage):
  94. DDLogError(errorMessage)
  95. break
  96. default:
  97. break
  98. }
  99. }
  100. }
  101. } else {
  102. let reversed = self.schedules.reversed()
  103. var newList: [OOAttandanceMobileScheduleInfo] = []
  104. reversed.forEach { (info) in
  105. if info.checkinStatus == "未打卡" {
  106. newList.append(info)
  107. }
  108. }
  109. let checkType = newList.count > 0 ? newList.last!.checkinType : ""
  110. self.showLoading()
  111. checkinForm.id = nil
  112. checkinForm.recordAddress = self.bmkResult?.address
  113. checkinForm.desc = self.bmkResult?.sematicDescription
  114. checkinForm.longitude = String(self.bmkResult?.location.longitude ?? 0.0)
  115. checkinForm.latitude = String(self.bmkResult?.location.latitude ?? 0.0)
  116. checkinForm.empNo = O2AuthSDK.shared.myInfo()?.employee
  117. checkinForm.empName = O2AuthSDK.shared.myInfo()?.name
  118. let currenDate = Date()
  119. checkinForm.recordDateString = currenDate.toString("yyyy-MM-dd")
  120. checkinForm.signTime = currenDate.toString("HH:mm:ss")
  121. checkinForm.optMachineType = UIDevice.deviceModelReadable()
  122. checkinForm.optSystemName = "\(UIDevice.systemName()) \(UIDevice.systemVersion())"
  123. checkinForm.checkin_type = checkType
  124. viewModel.postMyCheckin(checkinForm) { (result) in
  125. DispatchQueue.main.async {
  126. self.hideLoading()
  127. }
  128. switch result {
  129. case .ok(_):
  130. self.loadMyRecords()
  131. break
  132. case .fail(let errorMessage):
  133. DDLogError(errorMessage)
  134. break
  135. default:
  136. break
  137. }
  138. }
  139. }
  140. }
  141. ///接收到位置信息
  142. private func locationReceive(bmkResult: BMKReverseGeoCodeSearchResult, isIn: Bool, workPlace: OOAttandanceWorkPlace?) {
  143. self.isInWorkPlace = isIn
  144. self.bmkResult = bmkResult
  145. if isIn {
  146. // 打卡按钮启用
  147. self.locationLabel.text = workPlace?.placeName
  148. self.locationCheckImageView.image = UIImage(named: "icon__ok2_click")
  149. } else {
  150. //打卡按钮禁用
  151. self.locationLabel.text = bmkResult.address
  152. self.locationCheckImageView.image = UIImage(named: "icon_delete_1")
  153. }
  154. }
  155. ///定位
  156. private func startLocationService() {
  157. locService = BMKLocationManager()
  158. locService.desiredAccuracy = kCLLocationAccuracyBest
  159. //设置返回位置的坐标系类型
  160. locService.coordinateType = .BMK09LL
  161. //设置距离过滤参数
  162. locService.distanceFilter = kCLDistanceFilterNone;
  163. //设置预期精度参数
  164. locService.desiredAccuracy = kCLLocationAccuracyBest;
  165. //设置应用位置类型
  166. locService.activityType = .automotiveNavigation
  167. //设置是否自动停止位置更新
  168. locService.pausesLocationUpdatesAutomatically = false
  169. locService.delegate = self
  170. locService.startUpdatingLocation()
  171. searchAddress = BMKGeoCodeSearch()
  172. searchAddress.delegate = self
  173. }
  174. ///结束定位
  175. private func stopLocationService() {
  176. locService.stopUpdatingLocation()
  177. locService.delegate = nil
  178. searchAddress.delegate = nil
  179. }
  180. ///查询工作地址
  181. private func loadWorkPlace() {
  182. self.viewModel.getLocationWorkPlace { (myResult) in
  183. switch myResult {
  184. case .ok(let result):
  185. if let model = result as? [OOAttandanceWorkPlace] {
  186. DispatchQueue.main.async {
  187. self.workPlaces = model
  188. }
  189. }
  190. break
  191. case .fail(let s):
  192. self.showError(title: "错误:\n\(s)")
  193. break
  194. default:
  195. break
  196. }
  197. }
  198. }
  199. ///获取打卡记录和班次数据
  200. private func loadMyRecords() {
  201. self.viewModel.listMyRecords { (result) in
  202. switch result {
  203. case .ok(let record):
  204. let model = record as? OOMyAttandanceRecords
  205. let records = model?.records ?? []
  206. self.lastRecord = records.last
  207. var unCheckNumber = 0
  208. self.schedules.removeAll()
  209. model?.scheduleInfos?.forEach({ (f) in
  210. let info = OOAttandanceMobileScheduleInfo()
  211. info.signTime = f.signTime
  212. info.signDate = f.signDate
  213. info.checkinType = f.checkinType
  214. info.signSeq = f.signSeq
  215. let anyRecord = records.first { (detail) -> Bool in
  216. detail.checkin_type == f.checkinType
  217. }
  218. if anyRecord != nil {
  219. info.checkinStatus = "已打卡"
  220. info.recordId = anyRecord?.id
  221. info.checkinTime = anyRecord?.signTime
  222. unCheckNumber = 0
  223. } else {
  224. info.checkinStatus = "未打卡"
  225. unCheckNumber += 1
  226. }
  227. self.schedules.append(info)
  228. })
  229. DispatchQueue.main.async {
  230. if unCheckNumber > 0 {
  231. self.needCheckIn = true
  232. self.setCheckInBtnEnableStyle()
  233. } else {
  234. self.needCheckIn = false
  235. self.setCheckInBtnDisableStyle()
  236. }
  237. self.schedulesView.reloadData()
  238. }
  239. break
  240. case .fail(let err):
  241. DDLogError(err)
  242. DispatchQueue.main.async {
  243. self.needCheckIn = false
  244. self.setCheckInBtnDisableStyle()
  245. }
  246. break
  247. default:
  248. DDLogError("default ..................")
  249. break
  250. }
  251. }
  252. }
  253. private func setCheckInBtnDisableStyle() {
  254. self.checkInBtnView.backgroundColor = UIColor(hex: "#cccccc")
  255. }
  256. private func setCheckInBtnEnableStyle() {
  257. self.checkInBtnView.backgroundColor = base_color
  258. }
  259. ///刷新按钮时间
  260. @objc private func timeTick() {
  261. let now = Date().toString("HH:mm:ss")
  262. self.checkInBtnTimeLabel.text = now
  263. }
  264. }
  265. extension OOAttendanceCheckInNewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  266. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  267. return self.schedules.count
  268. }
  269. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  270. if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OOAttendanceScheduleViewCell", for: indexPath) as? OOAttendanceScheduleViewCell {
  271. let s = self.schedules[indexPath.row]
  272. let isLastRecord = s.recordId == self.lastRecord?.id
  273. cell.setData(info: s, isLastRecord: isLastRecord)
  274. return cell
  275. }
  276. return UICollectionViewCell()
  277. }
  278. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  279. let s = self.schedules[indexPath.row]
  280. if let last = self.lastRecord, last.id == s.recordId {
  281. self.postCheckinButton(s)
  282. }
  283. }
  284. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
  285. return CGSize(width: ((SCREEN_WIDTH - 52) / 2), height: 64)
  286. }
  287. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
  288. return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
  289. }
  290. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
  291. return 5.0
  292. }
  293. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
  294. return 0.0
  295. }
  296. }
  297. extension OOAttendanceCheckInNewController: BMKLocationManagerDelegate {
  298. func bmkLocationManager(_ manager: BMKLocationManager, didUpdate location: BMKLocation?, orError error: Error?) {
  299. if let loc = location?.location {
  300. DDLogDebug("当前位置,\(loc.coordinate.latitude),\(loc.coordinate.longitude)")
  301. //搜索到指定的地点
  302. let re = BMKReverseGeoCodeSearchOption()
  303. re.location = CLLocationCoordinate2D(latitude: loc.coordinate.latitude, longitude: loc.coordinate.longitude)
  304. let _ = searchAddress.reverseGeoCode(re)
  305. } else {
  306. DDLogError("没有获取到定位信息!!!!!")
  307. }
  308. }
  309. }
  310. extension OOAttendanceCheckInNewController: BMKGeoCodeSearchDelegate {
  311. /// 计算所有位置是否有一个位置在误差范围内
  312. func calcErrorRange(_ checkinLocation: CLLocationCoordinate2D) -> (Bool, OOAttandanceWorkPlace?) {
  313. var result = false
  314. for item in self.workPlaces {
  315. let longitude = Double((item.longitude)!)
  316. let latitude = Double((item.latitude)!)
  317. let eRange = item.errorRange!
  318. let theLocation = CLLocationCoordinate2DMake(latitude!, longitude!)
  319. result = BMKCircleContainsCoordinate(theLocation, checkinLocation, Double(eRange))
  320. if result == true {
  321. return (true, item)
  322. }
  323. }
  324. return (false, nil)
  325. }
  326. func onGetReverseGeoCodeResult(_ searcher: BMKGeoCodeSearch?, result: BMKReverseGeoCodeSearchResult?, errorCode error: BMKSearchErrorCode) {
  327. //发送定位的实时位置及名称信息
  328. if let location = result?.location {
  329. let calResult = calcErrorRange(location)
  330. self.locationReceive(bmkResult: result!, isIn: calResult.0, workPlace: calResult.1)
  331. }
  332. }
  333. func onGetGeoCodeResult(_ searcher: BMKGeoCodeSearch!, result: BMKGeoCodeSearchResult!, errorCode error: BMKSearchErrorCode) {
  334. if Int(error.rawValue) == 0 {
  335. DDLogDebug("result \(String(describing: result))")
  336. } else {
  337. DDLogDebug("result error errorCode = \(Int(error.rawValue))")
  338. }
  339. }
  340. }