OOAttendanceCheckInNewController.swift 18 KB

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