ExposureViewController.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. //
  2. // ExposureViewController.swift
  3. // MTP2_iOS
  4. //
  5. // Created by Muchinfo on 2021/1/5.
  6. // Copyright © 2021 Muchinfo. All rights reserved.
  7. //
  8. import UIKit
  9. import JXSegmentedView
  10. import IBAnimatable
  11. import WHToast
  12. import GTMRefresh
  13. /// 敞口视图容器控制类
  14. class ExposureViewController: BaseCollectionViewController {
  15. // MARK: - 属性列表
  16. /// 主滚动视图
  17. @IBOutlet weak var mainScrollView: UIScrollView!
  18. /// 历史内容装载视图
  19. @IBOutlet weak var hisContetnView: UIView! {
  20. didSet {
  21. hisContetnView.isHidden = true
  22. }
  23. }
  24. /// 内容装载视图
  25. @IBOutlet weak var contetnView: UIStackView! {
  26. didSet {
  27. contetnView.isHidden = false
  28. }
  29. }
  30. /// 期货明细
  31. @IBOutlet weak var futures: UIButton!
  32. /// 现货明细
  33. @IBOutlet weak var spot: UIButton!
  34. /// 交易下单
  35. @IBOutlet weak var trade: UIButton!
  36. /// 实时敞口数据
  37. var exposures: [MoRealExposure] = [] {
  38. didSet {
  39. /// 左边数据
  40. leftDatas = exposures.map { (obj) -> String in
  41. /// 合同编号和采购销售名称
  42. return "\(obj.middleGoodsName)"+"\n\(obj.middleGoodsCode)"
  43. }
  44. /// 右边数据
  45. rightDatas = exposures.map { (obj) -> [String] in
  46. let e = obj.enumdicname.isBlank()
  47. return ["\(obj.hedgeqty.toUpString())\(e)\n\(obj.arbitrageqty.toUpString())\(e)",
  48. "\(obj.totalSpotQty.toUpString())\(e)\n\(obj.diffSpotQty.toUpString())\(e)",
  49. "\(obj.diffHedgeQty.toUpString())\(e)\n\(obj.diffArbitrageQty.toUpString())\(e)",
  50. "\((obj.middleGoodsHedgeRatio*100.0).toUpString())%\n\((obj.needarbitrageratio*100.0).toUpString())%",
  51. "\(obj.needhedgeqty.toUpString())\(e)\n\(obj.needarbitrageqty.toUpString())\(e)",
  52. "\(obj.totalNeedHedgeQty.toUpString())\(e)\n\(obj.diffSpotHedgeQty.toUpString())\(e)",
  53. "\(obj.totalFutureQty.toUpString())\(e)\n\(obj.diffFutuQty.toUpString())\(e)",
  54. "\(obj.needHedgeExposoure.toUpString())\(e)\n\(obj.diffExposoureQty.toUpString())\(e)",
  55. "\(obj.totalExposure.toUpString())\(e)\n\(obj.diffqty.toUpString())\(e)"]
  56. }
  57. /// 刷新右侧列表数据
  58. rightTableView.reloadData()
  59. /// 无数据按钮
  60. noDataButton.isHidden = exposures.count != 0
  61. }
  62. }
  63. /// 敞口现货头寸
  64. var exposureSpots: [MoExposureSpot] = [] {
  65. didSet {
  66. /// 左边数据
  67. leftDatas = exposureSpots.map { (obj) -> String in
  68. /// 合同编号和采购销售名称
  69. return "\(obj.wrstandardname)"
  70. }
  71. /// 右边数据
  72. rightDatas = exposureSpots.map { (obj) -> [String] in
  73. let e = obj.enumdicname.isBlank()
  74. return ["\(obj.oritoalspotqty.toUpString())\(e)",
  75. "\(obj.increaseqty.toUpString())\(e)\n\(obj.decreaseqty.toUpString())\(e)",
  76. "\(obj.totalspotqty.toUpString())\(e)"]
  77. }
  78. /// 刷新右侧列表数据
  79. rightTableView.reloadData()
  80. /// 无数据按钮
  81. noDataButton.isHidden = exposureSpots.count != 0
  82. }
  83. }
  84. /// 敞口期货头寸
  85. var hedgePositions: [MoExposureHedgePosition] = [] {
  86. didSet {
  87. /// 左边数据
  88. leftDatas = hedgePositions.map { (obj) -> String in
  89. /// 合同编号和采购销售名称
  90. return "\(obj.goodsname)"+"\n\(obj.goodscode)"
  91. }
  92. /// 右边数据
  93. rightDatas = hedgePositions.map { (obj) -> [String] in
  94. return ["\(obj.totalydqty.toUpString())手",
  95. "\(obj.increaseqty.toUpString())手\n\(obj.decreaseqty.toUpString())手",
  96. "\(obj.totalcurqty.toUpString())手"]
  97. }
  98. /// 刷新右侧列表数据
  99. rightTableView.reloadData()
  100. /// 无数据按钮
  101. noDataButton.isHidden = hedgePositions.count != 0
  102. }
  103. }
  104. /// 历史敞口视图
  105. lazy var hisExposure: HisExposuresViewController = {
  106. $0.view.frame = hisContetnView.bounds
  107. return $0
  108. } (self.viewController("Exposure", "HisExposures") as! HisExposuresViewController)
  109. /// AutoTimerID
  110. let AutoTimerID = "RealExposureTimer"
  111. /// 权限菜单
  112. var roleTitles: [String] {
  113. get {
  114. var titles: [String] = []
  115. /// 权限控制
  116. guard let commonManager = MTP2BusinessCore.shared.commonManager else {
  117. return titles
  118. }
  119. if commonManager.containMenu(key: "client_exposure_real") {
  120. titles.append("实时敞口")
  121. }
  122. if commonManager.containMenu(key: "client_exposure_spot") {
  123. titles.append("现货头寸")
  124. }
  125. if commonManager.containMenu(key: "client_exposure_future") {
  126. titles.append("期货头寸")
  127. }
  128. if commonManager.containMenu(key: "client_exposure_his") {
  129. titles.append("历史敞口")
  130. }
  131. return titles
  132. }
  133. }
  134. // MARK: - 生命周期
  135. override func viewDidLoad() {
  136. super.viewDidLoad()
  137. // Do any additional setup after loading the view.
  138. /// loding
  139. addLoadingView()
  140. /// 菜单
  141. dataSource.titles = roleTitles
  142. segmentedView.dataSource = dataSource
  143. segmentedView.reloadData()
  144. /// 判断其权限控制
  145. if let commonManager = MTP2BusinessCore.shared.commonManager {
  146. /// 交易下单
  147. trade.isHidden = !commonManager.containMenu(key: "client_exposure_trade")
  148. /// 期货明细
  149. futures.isHidden = !commonManager.containMenu(key: "client_exposure_futuresdetail")
  150. /// 现货明细
  151. spot.isHidden = !commonManager.containMenu(key: "client_exposure_spotdetail")
  152. }
  153. /// 分页回调
  154. segmentBlock = { (_ index: Int, _ segment: JXSegmentedView) in
  155. switch self.roleTitles[index] {
  156. case "实时敞口":
  157. /// 查询实时敞口
  158. self.reloadData(0)
  159. case "现货头寸":
  160. /// 查询现货头寸
  161. self.reloadData(1)
  162. case "期货头寸":
  163. /// 查询期货头寸
  164. self.reloadData(2)
  165. default: break
  166. }
  167. /// 内容视图
  168. self.contetnView.isHidden = self.roleTitles[index] == "历史敞口"
  169. /// 历史视图
  170. if self.roleTitles[index] == "历史敞口" {
  171. if !self.hisContetnView.subviews.contains(self.hisExposure.view) {
  172. self.hisContetnView.addSubview(self.hisExposure.view)
  173. self.addChild(self.hisExposure)
  174. self.hisExposure.didMove(toParent: self)
  175. self.noDataButton.isHidden = true
  176. }
  177. } else {
  178. /// 移出
  179. if self.hisContetnView.subviews.contains(self.hisExposure.view) {
  180. self.hisExposure.view.removeFromSuperview()
  181. self.hisExposure.removeFromParent()
  182. }
  183. }
  184. self.hisContetnView.isHidden = self.roleTitles[index] != "历史敞口"
  185. }
  186. /// 选中回调
  187. selectBlock = { (_ row: Int?) in
  188. if self.roleTitles[self.segmentedView.selectedIndex] != "实时敞口" {
  189. /// 头寸选中
  190. self.performSegue(withIdentifier: "ShowPositionSpotFutureDetail", sender: self.segmentedView.selectedIndex == 1 ? OperatorType.cashFlow : OperatorType.futuresFlow)
  191. } else{
  192. /// 判断选中位置
  193. if let index = self.selectedRow, let cell = self.rightTableView.cellForRow(at: IndexPath(row: index.row, section: 0)) as? RightCell {
  194. /// 判断Cell相对于view所处的位置
  195. let rectInTableView = self.rightTableView.rectForRow(at: IndexPath(row: index.row, section: 0))
  196. /// 获取
  197. let rectInSuperview = self.rightTableView.convert(rectInTableView, to: self.view)
  198. /// 数据显示
  199. /// 获取对应的位置
  200. self.operationView.top = rectInSuperview.origin.y + cell.collectionView.height + 5.0
  201. /// 是否隐藏
  202. self.operationView.isHidden = !index.isExpland
  203. }
  204. }
  205. }
  206. if roleTitles.count != 0 {
  207. /// 下拉刷新
  208. mainScrollView.gtm_addRefreshHeaderView(refreshHeader: DefaultGTMRefreshHeader()) {
  209. /// 查询实时数据
  210. self.reloadData(self.segmentedView.selectedIndex)
  211. }
  212. /// 默认选中第一个
  213. segmentedView.defaultSelectedIndex = 0
  214. segmentedView.delegate?.segmentedView(segmentedView, didSelectedItemAt: 0)
  215. }
  216. }
  217. override func viewDidAppear(_ animated: Bool) {
  218. super.viewDidAppear(animated)
  219. /// 重新请求数据
  220. reloadData(segmentedView.selectedIndex)
  221. }
  222. override func viewWillAppear(_ animated: Bool) {
  223. super.viewWillAppear(animated)
  224. /// 列头高度
  225. columnWidth = 100.0
  226. /// 开启定时器刷新
  227. startAutoTimer()
  228. }
  229. override func viewWillDisappear(_ animated: Bool) {
  230. super.viewWillDisappear(animated)
  231. /// 取消定时器刷新
  232. stopAutoTimer()
  233. }
  234. // MARK: - 交互相关
  235. /// onButtonPressed
  236. /// - Parameters:
  237. /// - sender: sender
  238. @IBAction fileprivate func onButtonPressed(_ sender: UIButton) {
  239. switch sender {
  240. case futures, spot: /// 实时敞口-现货期货明细
  241. self.performSegue(withIdentifier: "ShowExposureSpotFutureDetail", sender: sender == futures ? OperatorType.futuresFlow : OperatorType.cashFlow)
  242. case trade: /// 期货下单
  243. /// 跳转期货下单方法
  244. doFutureTrade()
  245. default: break
  246. }
  247. }
  248. /// 跳转期货下单方法
  249. fileprivate func doFutureTrade() {
  250. /// 异常
  251. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  252. let goodsInfos = goodsManager.goodsInfos,
  253. let row = selectedRow?.row else {
  254. return
  255. }
  256. /// 获取对应的主力合约商品信息
  257. goodsManager.queryExposureGoods(middlegoodsid: exposures[row].middleGoodsID, callback: { isSuccess, er, objs in
  258. DispatchQueue.main.async {
  259. /// 获取主力合约商品失败
  260. if !isSuccess {
  261. WHToast.showMessage("获取主力合约信息失败,原因:\(er?.retMsg ?? "查询超时")", duration: ToastTimer, finishHandler: {})
  262. return
  263. }
  264. /// 默认带主力合约数据
  265. if let obj = goodsInfos.first(where: {$0.goodsid == objs?.first?.goodsid}) {
  266. self.push("Futures", "ATPTrader", obj)
  267. } else {
  268. self.push("Futures", "ATPTrader", nil)
  269. }
  270. }
  271. })
  272. }
  273. // MARK: - 接口请求
  274. /// 刷新数据信息
  275. /// - Parameter type: 类型
  276. fileprivate func reloadData(_ type: Int) {
  277. /// 历史敞口不做处理
  278. if roleTitles[segmentedView.selectedIndex] == "历史敞口" {
  279. /// 结束下拉刷新
  280. mainScrollView.endRefreshing()
  281. return
  282. }
  283. /// 异常
  284. guard let commonManager = MTP2BusinessCore.shared.commonManager,
  285. let spotManager = MTP2BusinessCore.shared.spotManager else {
  286. return
  287. }
  288. /// startAnimating
  289. _anim?.startAnimating()
  290. /// group
  291. let group = DispatchGroup()
  292. /// 定义列头
  293. var key = ""
  294. /// group
  295. group.enter()
  296. /// 查询类型
  297. switch roleTitles[type] {
  298. case "实时敞口":
  299. /// 实时敞口
  300. key = "table_mobile_exposure_realtime"
  301. /// 允许选中
  302. canSelected = true
  303. /// 清空数据
  304. exposures = []
  305. /// 查询实时敞口数据
  306. spotManager.queryRealtimeExposure { (isSuccess, er, objs) in
  307. DispatchQueue.main.async {
  308. /// 查询失败
  309. if !isSuccess {
  310. self.exposures = []
  311. WHToast.showMessage("数据查询失败,原因:\(er?.retMsg ?? "查询超时")", duration: ToastTimer, finishHandler: {})
  312. return
  313. }
  314. /// 数据赋值
  315. self.exposures = objs ?? []
  316. }
  317. group.leave()
  318. }
  319. case "现货头寸":
  320. /// 现货头寸
  321. key = "table_mobile_exposure_spotposition"
  322. /// 不允许选中
  323. canSelected = false
  324. /// 清空数据
  325. exposureSpots = []
  326. /// 查询敞口现货头寸
  327. spotManager.queryExposureSpot { (isSuccess, er, objs) in
  328. DispatchQueue.main.async {
  329. /// 查询失败
  330. if !isSuccess {
  331. self.exposureSpots = []
  332. WHToast.showMessage("数据查询失败,原因:\(er?.retMsg ?? "查询超时")", duration: ToastTimer, finishHandler: {})
  333. return
  334. }
  335. /// 数据赋值
  336. self.exposureSpots = objs ?? []
  337. }
  338. group.leave()
  339. }
  340. default:
  341. /// 期货头寸
  342. key = "table_mobile_exposure_futuresposition"
  343. /// 不允许选中
  344. canSelected = false
  345. /// 清空数据
  346. hedgePositions = []
  347. /// queryExposureHedgePosition
  348. spotManager.queryExposureHedgePosition { (isSuccess, er, objs) in
  349. DispatchQueue.main.async {
  350. /// 查询失败
  351. if !isSuccess {
  352. self.hedgePositions = []
  353. WHToast.showMessage("数据查询失败,原因:\(er?.retMsg ?? "查询超时")", duration: ToastTimer, finishHandler: {})
  354. return
  355. }
  356. /// 数据赋值
  357. self.hedgePositions = objs ?? []
  358. }
  359. group.leave()
  360. }
  361. }
  362. group.enter()
  363. /// 查询列头信息
  364. commonManager.queryTableDefine(tableKey: key) { (isSuccess, er, objs) in
  365. DispatchQueue.main.async {
  366. /// 查询失败
  367. if !isSuccess {
  368. WHToast.showMessage("数据查询失败,原因:\(er?.retMsg ?? "查询超时")", duration: ToastTimer, finishHandler: {})
  369. return
  370. }
  371. /// 数据
  372. self.columnHeaders = objs?.first?.columns.sorted(by: { (obj1, obj2) -> Bool in
  373. return obj1.orderindex<obj2.orderindex
  374. }).map({ (obj) -> String in
  375. return obj.columntitle.replacingOccurrences(of: "\\n", with: "\n")
  376. }) ?? []
  377. }
  378. group.leave()
  379. }
  380. group.notify(queue: .main) {
  381. /// 结束下拉刷新
  382. self.mainScrollView.endRefreshing()
  383. /// stopAnimating
  384. self._anim?.stopAnimating()
  385. }
  386. }
  387. // MARK: - 定时器相关
  388. /// 定时器刷新
  389. fileprivate func startAutoTimer() {
  390. /// 每隔30秒自动刷新一次
  391. MCGCDTimer.shared.scheduledDispatchTimer(WithTimerName: AutoTimerID, timeInterval: 30, queue: .global(), repeats: true, isNowRun: false) {
  392. DispatchQueue.main.async {
  393. self.reloadData(self.segmentedView.selectedIndex)
  394. }
  395. }
  396. }
  397. /// 取消定时器刷新
  398. fileprivate func stopAutoTimer() {
  399. MCGCDTimer.shared.cancleTimer(WithTimerName: AutoTimerID)
  400. }
  401. // MARK: - Navigation
  402. // In a storyboard-based application, you will often want to do a little preparation before navigation
  403. override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  404. // Get the new view controller using segue.destination.
  405. // Pass the selected object to the new view controller.
  406. if segue.identifier == "ShowExposureSpotFutureDetail", let f = segue.destination as? ExposureSpotFutureDetailViewController, let row = selectedRow?.row, exposures.count != 0 {
  407. /// 实时敞口 - 期货现货明细
  408. f.realExposure = exposures[row]
  409. if let type = sender as? OperatorType {
  410. /// title
  411. f.navigationItem.title = "实时敞口-\(type == .cashFlow ? "现货" : "期货")明细"
  412. f.operatorType = type
  413. }
  414. } else if segue.identifier == "ShowPositionSpotFutureDetail", let p = segue.destination as? PositionSpotFutureDetailViewController,let row = selectedRow?.row {
  415. /// 头寸-期货现货明细
  416. if ((sender as! OperatorType) == .cashFlow) {
  417. /// 现货明细
  418. p.exposureSpot = exposureSpots[row]
  419. } else {
  420. /// 期货明细
  421. p.hedgePosition = hedgePositions[row]
  422. }
  423. if let type = sender as? OperatorType {
  424. /// title
  425. p.navigationItem.title = "\(type == .cashFlow ? "现货" : "期货")头寸-\(type == .cashFlow ? "现货" : "期货")明细"
  426. p.operatorType = type
  427. }
  428. }
  429. }
  430. }