QuoteViewController.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. //
  2. // QuoteViewController.swift
  3. // MTP2_iOS
  4. //
  5. // Created by zhongyuan on 2018/4/3.
  6. // Copyright © 2018年 zhongyuan.All rights reserved.
  7. //
  8. import UIKit
  9. import GTMRefresh
  10. import JXSegmentedView
  11. import WHToast
  12. /// 期货商品行情报价列表视图容器控制类
  13. class QuoteViewController: BaseTableViewController<QuoteModel>, QuoteSubscriptDelete {
  14. // MARK: - 属性列表
  15. /// 类目
  16. @IBOutlet weak var segmentedView: JXSegmentedView! {
  17. didSet {
  18. segmentedView.dataSource = subDataSource
  19. segmentedView.indicators = [backgroundIndicator]
  20. segmentedView.delegate = self
  21. }
  22. }
  23. /// 数据显示列表
  24. @IBOutlet weak var tableView: UITableView!
  25. /// 涨跌按钮
  26. @IBOutlet weak var riseButton: UIButton!
  27. /// 持仓量按钮
  28. @IBOutlet weak var volumnButton: UIButton!
  29. /// 无数据按钮
  30. @IBOutlet weak var dataButton: UIButton! {
  31. didSet {
  32. dataButton.set(image: UIImage.getImage(name: "noData-universal"), title: "没有查询记录", titlePosition: .bottom, additionalSpacing: 5.0, state: .normal)
  33. dataButton.tintColor = .hex999()
  34. dataButton.isHidden = true
  35. }
  36. }
  37. /// 所属市场
  38. var marketID: Int = Int.max {
  39. didSet {
  40. /// 构建行情列表
  41. self.buildQuoteModels()
  42. }
  43. }
  44. /// 外部交易市场
  45. var exchanges: [MoExternalExchange] = []
  46. // MARK: - 生命周期相关
  47. override func viewDidLoad() {
  48. super.viewDidLoad()
  49. /// loding...
  50. addLoadingView()
  51. /// cellReuseIdentifier
  52. cellReuseIdentifier = "Quote_Cell"
  53. /// 构建外部交易市场
  54. buildExternalExchanges()
  55. /// 添加下拉刷新控件
  56. tableView.gtm_addRefreshHeaderView(refreshHeader: DefaultGTMRefreshHeader()) { [weak self] in
  57. if self?.segmentedView.selectedIndex == 0 { /// 自选
  58. self?.buildMyFavoriteGoods()
  59. } else if self?.segmentedView.selectedIndex == 1 { /// 主力
  60. self?.buildGoodsSortByPosition()
  61. } else {
  62. /// 请求数据
  63. self?.buildQuoteModels()
  64. }
  65. }
  66. }
  67. override func viewWillDisappear(_ animated: Bool) {
  68. super.viewWillDisappear(animated)
  69. /// 清除广播侦听
  70. MTP2BusinessCore.shared.broadcastManager?.removeBroadcastListener(owner: self)
  71. }
  72. override func viewDidAppear(_ animated: Bool) {
  73. super.viewDidAppear(animated)
  74. /// 初始化广播侦听
  75. initListen()
  76. }
  77. /// 初始化广播侦听
  78. func initListen() {
  79. /// 侦听行情推送广播
  80. MTP2BusinessCore.shared.broadcastManager?.addBroadcastListener(owner: self, action: .ReceiveTradeQuote) { [weak self] in
  81. guard let quoteGoodsInfos = $0.object as? [(goodsHqCode: String, exchHqCode: String)],
  82. let goodsManager = MTP2BusinessCore.shared.goodsManager,
  83. let weakSelf = self else { return }
  84. /// 有数据
  85. if quoteGoodsInfos.count != 0 {
  86. /// 待刷新行
  87. for (index, quoteModel) in weakSelf.tableCellModels.enumerated() {
  88. if quoteGoodsInfos.first(where: { $0.goodsHqCode == quoteModel.outgoodscode }) != nil {
  89. let indexPath = IndexPath(row: index, section: 0)
  90. /// 更新显示行情数据
  91. guard let moGoodsInfo = goodsManager.goodsInfos?.first(where: { $0.goodscode == quoteModel.goodscode }) else { continue }
  92. if quoteModel.last != moGoodsInfo.moQuoteInfo?.last {
  93. quoteModel.update(moGoodsInfo: moGoodsInfo)
  94. DispatchQueue.main.async {
  95. /// 判断是否在可见区域
  96. if weakSelf.tableView.indexPathsForVisibleRows?.firstIndex(of: indexPath) != nil {
  97. weakSelf.tableView.reloadRows(at: [indexPath], with: .none)
  98. }
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. // MARK: - 数据初始化
  107. /// 构建外部交易市场
  108. fileprivate func buildExternalExchanges() {
  109. /// 加载资管外部交易市场
  110. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  111. let externals = goodsManager.exchanges?.filter({$0.goodsGroups.count != 0}),
  112. externals.count != 0 else {
  113. return
  114. }
  115. /// 外部交易市场
  116. exchanges = externals
  117. /// subDataSource
  118. subDataSource.titles = externals.map({ (obj) -> String in
  119. return obj.exexchangename
  120. })
  121. subDataSource.titles.insert(contentsOf: ["自选", "主力"], at: 0)
  122. /// 默认选中
  123. segmentedView.defaultSelectedIndex = 1
  124. segmentedView.reloadData()
  125. segmentedView.delegate?.segmentedView(segmentedView, didSelectedItemAt: 1)
  126. }
  127. // MARK: - 行情相关
  128. /// 构建行情
  129. fileprivate func buildQuoteModels() {
  130. /// 获取当前外部交易市场下的所有商品组的商品信息
  131. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  132. let exchange = goodsManager.getMoExternalExchange(autoID: marketID) else { return }
  133. /// 显示外部交易所下面商品组的商品信息
  134. var showGoodsInfos: [MoGoodsInfo] = []
  135. if let goodsInfos = goodsManager.goodsInfos {
  136. for obj in exchange.goodsGroups {
  137. let desGoodsInfos = goodsInfos.filter { $0.goodsgroupid == obj.goodsgroupid }
  138. showGoodsInfos.append(contentsOf: desGoodsInfos)
  139. }
  140. /// endRefreshing
  141. tableView.endRefreshing()
  142. /// 移出所有数据
  143. tableCellModels.removeAll()
  144. /// 构建数据
  145. showGoodsInfos.forEach {
  146. tableCellModels.append(QuoteModel(moGoodsInfo: $0, rise: false, volumn: 0))
  147. }
  148. /// 刷新数据
  149. tableView.reloadData()
  150. /// 无数据按钮
  151. dataButton.isHidden = tableCellModels.count != 0
  152. if tableCellModels.count > 0 {
  153. /// 等待UI操作完成,也就是tableView刷新完之后执行
  154. DispatchQueue.main.async { [weak self] in
  155. if self?.visibleGoodsCodes.count != 0 {
  156. self?.shouldSubscriptGoodsCodes = self?.visibleGoodsCodes
  157. }
  158. }
  159. }
  160. }
  161. }
  162. /// 构建我的自选商品信息
  163. fileprivate func buildMyFavoriteGoods() {
  164. /// 获取当前外部交易市场下的所有商品组的商品信息
  165. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  166. let goodsInfos = goodsManager.goodsInfos else { return }
  167. /// startAnimating
  168. _anim?.startAnimating()
  169. /// 查询我得收藏商品
  170. goodsManager.queryUserFavoriteGoodses { (isSuccess, error, ids) in
  171. DispatchQueue.main.async {
  172. /// endRefreshing
  173. self.tableView.endRefreshing()
  174. /// stopAnimating
  175. self._anim?.stopAnimating()
  176. /// 查询失败
  177. if !isSuccess {
  178. /// 为空
  179. self.tableCellModels = []
  180. /// 刷新数据
  181. self.tableView.reloadData()
  182. /// 无数据按钮
  183. self.dataButton.isHidden = self.tableCellModels.count != 0
  184. /// show error
  185. WHToast.showMessage("自选获取失败", duration: ToastTimer, finishHandler: {})
  186. return
  187. }
  188. /// 行情显示数据
  189. var objs: [QuoteModel] = []
  190. if let goodsids = ids, goodsids.count != 0 {
  191. goodsids.forEach { (id) in
  192. if let goodsInfo = goodsInfos.first(where: {$0.goodsid == id}) {
  193. objs.append(QuoteModel(moGoodsInfo: goodsInfo, rise: false, volumn: 0))
  194. }
  195. }
  196. }
  197. self.tableCellModels = objs
  198. /// 刷新数据
  199. self.tableView.reloadData()
  200. /// 无数据按钮
  201. self.dataButton.isHidden = self.tableCellModels.count != 0
  202. /// 等待UI操作完成,也就是tableView刷新完之后执行
  203. if self.tableCellModels.count > 0 {
  204. DispatchQueue.main.async { [weak self] in
  205. if self?.visibleGoodsCodes.count != 0 { self?.shouldSubscriptGoodsCodes = self?.visibleGoodsCodes }
  206. }
  207. }
  208. }
  209. }
  210. }
  211. /// 获取主力合约商品信息
  212. fileprivate func buildGoodsSortByPosition() {
  213. /// 获取当前外部交易市场下的所有商品组的商品信息
  214. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  215. let goodsInfos = goodsManager.goodsInfos else { return }
  216. /// endRefreshing
  217. tableView.endRefreshing()
  218. /// 行情显示数据
  219. var objs: [QuoteModel] = []
  220. let infos = goodsManager.sortByPositions
  221. if infos.count != 0 {
  222. infos.forEach { (id) in
  223. if let goodsInfo = goodsInfos.first(where: {$0.goodscode == id.goodscode}) {
  224. goodsInfo.ismian = true
  225. objs.append(QuoteModel(moGoodsInfo: goodsInfo, rise: false, volumn: 0))
  226. }
  227. }
  228. }
  229. tableCellModels = objs
  230. /// 刷新数据
  231. tableView.reloadData()
  232. /// 无数据按钮
  233. dataButton.isHidden = self.tableCellModels.count != 0
  234. /// 等待UI操作完成,也就是tableView刷新完之后执行
  235. if tableCellModels.count > 0 {
  236. DispatchQueue.main.async { [weak self] in
  237. if self?.visibleGoodsCodes.count != 0 {
  238. self?.shouldSubscriptGoodsCodes = self?.visibleGoodsCodes
  239. }
  240. }
  241. }
  242. }
  243. // MARK: - 交互相关
  244. /// onButtonPressed
  245. /// - Parameter sender: sender
  246. @IBAction func onButtonPressed(_ sender: UIButton) {}
  247. // MARK: - 订阅行情相关
  248. var uuid: String = UUID().uuidString
  249. var shouldSubscriptGoodsCodes: Set<String>? {
  250. didSet {
  251. guard let quoteSubscriptManager = MTP2BusinessCore.shared.quoteSubscriptManager else { return }
  252. quoteSubscriptManager.updateQuoteSubcriptGoods(uuid: uuid, goodsCode: shouldSubscriptGoodsCodes ?? [])
  253. }
  254. }
  255. var visibleGoodsCodes: Set<String> {
  256. var goodsCodes = Set<String>()
  257. self.tableView.visibleCells.forEach { (cell) in
  258. if let quoteCell = cell as? QuoteCell,
  259. let goodsCode = quoteCell.model?.outgoodscode {
  260. goodsCodes.insert(goodsCode)
  261. }
  262. }
  263. return goodsCodes
  264. }
  265. // MARK: - UIScrollViewDelegate
  266. override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  267. if visibleGoodsCodes.count != 0 {
  268. shouldSubscriptGoodsCodes = visibleGoodsCodes
  269. }
  270. }
  271. override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  272. return 60.0
  273. }
  274. // MARK: - Navigation
  275. // In a storyboard-based application, you will often want to do a little preparation before navigation
  276. override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  277. // Get the new view controller using segue.destination.
  278. // Pass the selected object to the new view controller.
  279. if segue.identifier == "ShowATPTrade", let t = segue.destination as? ATPTradeViewController, let goodsid = (sender as? QuoteCell)?.model?.goodsid { /// 交易下单
  280. t.moGoodsInfo = MTP2BusinessCore.shared.goodsManager?.goodsInfos?.first(where: {$0.goodsid == goodsid})
  281. } else if segue.identifier == "ShowChart", let c = segue.destination as? ChartViewController, let goodsid = (sender as? QuoteCell)?.model?.goodsid { /// 图表
  282. c.moGoodsInfo = MTP2BusinessCore.shared.goodsManager?.goodsInfos?.first(where: {$0.goodsid == goodsid})
  283. }
  284. }
  285. }
  286. // MARK: - JXSegmentedViewDelegate
  287. extension QuoteViewController: JXSegmentedViewDelegate {
  288. func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) {
  289. if index == 0 { /// 自选
  290. buildMyFavoriteGoods()
  291. } else if index == 1 { /// 主力
  292. buildGoodsSortByPosition()
  293. } else {
  294. marketID = exchanges[index-2].autoid
  295. }
  296. }
  297. }
  298. // MARK: - 行情报价牌Cell
  299. class QuoteCell: BaseTableViewCell<QuoteModel> {
  300. /// 商品名称
  301. @IBOutlet weak var goodsName: UILabel!
  302. /// 商品代码
  303. @IBOutlet weak var goodsCode: UILabel!
  304. /// 最新价
  305. @IBOutlet weak var last: UILabel!
  306. /// 涨跌
  307. @IBOutlet weak var increase: UILabel!
  308. /// 最低
  309. @IBOutlet weak var lowest: UILabel!
  310. /// 交易所标识
  311. @IBOutlet weak var flag: UILabel!
  312. /// 自选按钮
  313. @IBOutlet weak var favorit: UIButton!
  314. /// 最新价视图
  315. @IBOutlet weak var lastBgView: UIView!
  316. /// 是否是自选
  317. fileprivate var isSelfGoods = false {
  318. didSet {
  319. /// 图标
  320. favorit.setImage(UIImage(named: isSelfGoods ? "favorited" : "unfavorited"), for: .normal)
  321. }
  322. }
  323. override var model: QuoteModel? {
  324. didSet {
  325. if let obj = model {
  326. /// 商品名称
  327. goodsName.text = obj.goodsname
  328. /// 商品代码
  329. goodsCode.text = obj.outgoodscode
  330. /// 最新价
  331. last.text = (obj.last != nil && obj.last != 0.0) ? String(format: "%.\(obj.decimalplace)f", obj.last!) : "--"
  332. /// 自选商品
  333. isSelfGoods = MTP2BusinessCore.shared.goodsManager?.favGoodsIds?.contains(where: {$0 == obj.goodsid}) ?? false
  334. /// 涨跌值
  335. increase.text = obj.last == 0.0 ? "--" : "\(((obj.last ?? 0.0 )-obj.preSettle).toUpString(place: obj.decimalplace))"
  336. /// 持仓量 最低 最高 为0 则显示--
  337. let high = Double(obj.highest ?? "0.0") == 0.0 ? "--" : obj.highest?.isBlank()
  338. let low = Double(obj.lowest ?? "0.0") == 0.0 ? "--" : obj.lowest?.isBlank()
  339. let totalVolumn = obj.totalVolumn ?? "--"
  340. lowest.text = obj.volumn == 0 ? totalVolumn : (obj.volumn == 1 ? high : low)
  341. /// 昨结颜色
  342. if obj.preColorFlag == 1 {
  343. last.textColor = Color_Quote_Up
  344. increase.textColor = Color_Quote_Up
  345. lowest.textColor = Color_Quote_Up
  346. } else if obj.preColorFlag == -1 {
  347. last.textColor = Color_Quote_Down
  348. increase.textColor = Color_Quote_Down
  349. lowest.textColor = Color_Quote_Up
  350. } else {
  351. last.textColor = Color_Quote_Normal
  352. increase.textColor = Color_Quote_Normal
  353. lowest.textColor = Color_Quote_Up
  354. }
  355. /// 涨跌颜色
  356. if obj.colorFlag == 1 {
  357. lastBgView.backgroundColor = .fromHex(rgbValue: 0xFFEAEC)
  358. } else if obj.colorFlag == -1 {
  359. lastBgView.backgroundColor = .fromHex(rgbValue: 0xDDFFF4)
  360. } else {
  361. lastBgView.backgroundColor = .white
  362. }
  363. /// 获取标志
  364. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  365. let goodsGroup = goodsManager.goodsGroups?.first(where: {$0.goodsgroupid == obj.goodsgroupid}),
  366. let exchange = goodsManager.exchanges?.first(where: {$0.autoid == goodsGroup.exexchangeid}) else {
  367. return
  368. }
  369. flag.text = exchange.exexchangecode.getExchangeCodeVerb()
  370. }
  371. }
  372. }
  373. @IBAction fileprivate func onButtonPressed(_ sender: UIButton) {
  374. switch sender {
  375. case favorit:
  376. /// 异常
  377. guard let goodsManager = MTP2BusinessCore.shared.goodsManager,
  378. let goodsid = model?.goodsid else {
  379. return
  380. }
  381. /// 判断是否为自选
  382. if isSelfGoods {
  383. /// 删自选
  384. goodsManager.requestRemoveUserFavoriteGoods(goodsID: goodsid, callback: { (isSuccess, _) in
  385. DispatchQueue.main.async {
  386. self.isSelfGoods = !isSuccess
  387. }
  388. })
  389. } else { /// 加自选
  390. goodsManager.requestAddUserFavoriteGoods(goodsID: goodsid, callback: { (isSuccess, _) in
  391. DispatchQueue.main.async {
  392. self.isSelfGoods = isSuccess
  393. }
  394. })
  395. }
  396. default: break
  397. }
  398. }
  399. }
  400. // MARK: - 行情显示模型
  401. class QuoteModel {
  402. /// 商品ID
  403. var goodsid: Int = 0
  404. /// 商品名称
  405. var goodsname: String
  406. /// 商品代码
  407. var goodscode: String
  408. /// 外部商品代码
  409. var outgoodscode: String
  410. /// 最新价
  411. var last: Double?
  412. /// 成交量
  413. var totalVolumn: String?
  414. /// 最低
  415. var lowest: String?
  416. /// 最高
  417. var highest: String?
  418. /// 小数位
  419. var decimalplace: Int = 0
  420. /// 颜色标识
  421. var colorFlag: Int = 0
  422. /// 昨收颜色标识
  423. var preColorFlag: Int = 0
  424. /// 商品组ID
  425. var goodsgroupid: Int = 0
  426. /// 涨跌变量
  427. var rise: Bool = false
  428. /// 持仓量变量
  429. var volumn: Int = 0
  430. /// 昨结算
  431. var preSettle: Double = 0.0
  432. init(goodsid: Int = 0,
  433. goodscode: String,
  434. goodsname: String,
  435. outgoodscode: String) {
  436. self.goodscode = goodscode
  437. self.goodsname = goodsname
  438. self.goodsid = goodsid
  439. self.outgoodscode = outgoodscode
  440. }
  441. init(moGoodsInfo: MoGoodsInfo) {
  442. self.goodscode = moGoodsInfo.goodscode
  443. self.goodsname = moGoodsInfo.goodsname
  444. self.decimalplace = moGoodsInfo.decimalplace
  445. self.goodsgroupid = moGoodsInfo.goodsgroupid
  446. self.goodsid = moGoodsInfo.goodsid
  447. self.outgoodscode = moGoodsInfo.outgoodscode
  448. /// 更新行情信息
  449. self.update(moGoodsInfo: moGoodsInfo)
  450. }
  451. init(moGoodsInfo: MoGoodsInfo,
  452. rise: Bool = false,
  453. volumn: Int = 0) {
  454. self.goodscode = moGoodsInfo.goodscode
  455. self.goodsname = moGoodsInfo.goodsname
  456. self.decimalplace = moGoodsInfo.decimalplace
  457. self.goodsgroupid = moGoodsInfo.goodsgroupid
  458. self.rise = rise
  459. self.volumn = volumn
  460. self.goodsid = moGoodsInfo.goodsid
  461. self.outgoodscode = moGoodsInfo.outgoodscode
  462. /// 更新行情信息
  463. self.update(moGoodsInfo: moGoodsInfo)
  464. }
  465. func update(moGoodsInfo: MoGoodsInfo) {
  466. /// 行情相关字段赋值
  467. if let quote = moGoodsInfo.moQuoteInfo {
  468. /// 最新价
  469. self.last = quote.last
  470. /// 成交量
  471. self.totalVolumn = Double(moGoodsInfo.moQuoteInfo?.totalVolume ?? 0).getTotalTurnoverString(reserve: 2).isBlank()
  472. /// 颜色
  473. let preColorFlag = quote.last.compare(settle: moGoodsInfo.getPreClose())
  474. /// 昨收颜色标识
  475. self.preColorFlag = preColorFlag
  476. /// 昨结算
  477. self.preSettle = quote.presettle
  478. }
  479. /// 颜色值
  480. self.colorFlag = moGoodsInfo.colorFlag
  481. /// 最低价
  482. self.lowest = moGoodsInfo.moQuoteInfo?.lowest.toUpString(place: moGoodsInfo.decimalplace).isBlank()
  483. /// 最高价
  484. self.highest = moGoodsInfo.moQuoteInfo?.highest.toUpString(place: moGoodsInfo.decimalplace).isBlank()
  485. }
  486. }