index.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <template>
  2. <div ref="quoteRef" class="quote">
  3. <div class="quote-header">
  4. <div class="quote-header__logo">
  5. <img :src="'./logo/logo-horizontal.png'" />
  6. </div>
  7. <div class="quote-header__time">
  8. <span>
  9. {{ serverTime?.format('YYYY/MM/DD HH:mm:ss') }}
  10. </span>
  11. <span v-if="market">
  12. {{ [2, 6].includes(market.runstatus) ? getRunStatusName(market.runstatus) : '未开盘' }}
  13. </span>
  14. </div>
  15. </div>
  16. <table class="quote-table" cellspacing="0" cellpadding="0">
  17. <thead>
  18. <tr>
  19. <th>商品</th>
  20. <th>回购</th>
  21. <th>销售</th>
  22. <th>高</th>
  23. <th>低</th>
  24. </tr>
  25. </thead>
  26. <tbody>
  27. <tr v-for="(item, index) in touristTradeGoodsList" :key="index">
  28. <td>{{ item.goodsname }}</td>
  29. <td :class="item.bidColor">
  30. {{ handleNumberValue(formatDecimal(item.bid, item.decimalplace)) }}
  31. </td>
  32. <td :class="item.askColor">
  33. {{ handleNumberValue(formatDecimal(item.ask, item.decimalplace)) }}
  34. </td>
  35. <td :class="item.highestColor">
  36. {{ handleNumberValue(formatDecimal(item.highest, item.decimalplace)) }}
  37. </td>
  38. <td :class="item.lowestColor">
  39. {{ handleNumberValue(formatDecimal(item.lowest, item.decimalplace)) }}
  40. </td>
  41. </tr>
  42. </tbody>
  43. <thead>
  44. <tr>
  45. <th class="title" colspan="5">参考商品</th>
  46. </tr>
  47. </thead>
  48. <tbody>
  49. <tr v-for="(item, index) in touristRefGoodsList" :key="index">
  50. <td>{{ item.goodsname }}</td>
  51. <td :class="item.bidColor">
  52. {{ handleNumberValue(formatDecimal(item.bid, item.decimalplace)) }}
  53. </td>
  54. <td :class="item.askColor">
  55. {{ handleNumberValue(formatDecimal(item.ask, item.decimalplace)) }}
  56. </td>
  57. <td :class="item.highestColor">
  58. {{ handleNumberValue(formatDecimal(item.highest, item.decimalplace)) }}
  59. </td>
  60. <td :class="item.lowestColor">
  61. {{ handleNumberValue(formatDecimal(item.lowest, item.decimalplace)) }}
  62. </td>
  63. </tr>
  64. </tbody>
  65. </table>
  66. </div>
  67. </template>
  68. <script lang="ts" setup>
  69. import { shallowRef, computed, onActivated, onDeactivated, ref, onMounted, onUnmounted, nextTick } from 'vue'
  70. import moment, { Moment } from 'moment'
  71. import { timerInterceptor } from '@/utils/timer'
  72. import { getServerTime } from '@/services/api/common'
  73. import { handleNumberValue, formatDecimal } from '@/filters'
  74. import { queryTouristGoods, queryTouristQuoteDay } from '@/services/api/goods'
  75. import { useEnumStore, useFuturesStore } from '@/stores'
  76. import { timerTask } from '@/utils/timer'
  77. import { useRequest } from '@/hooks/request'
  78. import { getRunStatusName } from '@/constants/market'
  79. import { queryMarketRun } from '@/services/api/market'
  80. import quoteSocket from '@/services/websocket/quote'
  81. import service from '@/services'
  82. const enumStore = useEnumStore()
  83. const futuresStore = useFuturesStore()
  84. const subscribe = quoteSocket.createSubscribe()
  85. const quoteRef = shallowRef<HTMLDivElement>()
  86. const maxQty = 8 // 显示最多商品数量
  87. // 订阅的商品代码
  88. const subscribeData = shallowRef<string[]>([])
  89. // 系统时间
  90. const serverTime = ref<Moment>()
  91. service.onReady().then(() => {
  92. enumStore.getAllEnumList()
  93. // 获取游客商品列表
  94. queryTouristGoods().then((res) => {
  95. futuresStore.goodsList = res.data
  96. subscribeData.value = res.data.map((e) => e.goodscode)
  97. // 获取游客商品盘面
  98. queryTouristQuoteDay({
  99. data: {
  100. goodsCodes: subscribeData.value.join(',')
  101. }
  102. }).then((res) => {
  103. subscribeData.value.forEach((goodscode) => {
  104. const item = res.data.find((e) => e.goodscode === goodscode)
  105. futuresStore.updateQuotation(item ?? { goodscode })
  106. })
  107. subscribe.start(...subscribeData.value)
  108. const [firstMarket] = touristTradeGoodsList.value
  109. if (firstMarket) {
  110. /// 获取市场运行情况
  111. marketRun({
  112. marketID: firstMarket.marketid
  113. })
  114. }
  115. nextTick(() => autoHeight())
  116. })
  117. })
  118. // 校验服务器时间
  119. const checkServerTime = () => {
  120. getServerTime().then((res) => {
  121. serverTime.value = moment.parseZone(res.data)
  122. // 每1分钟同步一次服务器时间
  123. timerTask.setTimeout(() => {
  124. checkServerTime()
  125. }, 60 * 1000, 'getServerTime')
  126. })
  127. }
  128. checkServerTime()
  129. })
  130. const { data: market, run: marketRun } = useRequest(queryMarketRun, {
  131. manual: true,
  132. onSuccess: (res) => {
  133. market.value = res.data[0]
  134. // 每1分钟轮询刷新
  135. timerTask.setTimeout(() => marketRun(), 60 * 1000, 'getMarketRun')
  136. }
  137. })
  138. // 构建游客交易商品
  139. const touristTradeGoodsList = computed(() => {
  140. const list = futuresStore.getGoodsListByTradeMode(52)
  141. return list.sort((a, b) => a.goodsorder.localeCompare(b.goodsorder))
  142. })
  143. // 构建游客参考行情商品
  144. const touristRefGoodsList = computed(() => {
  145. const list = futuresStore.getGoodsListByTradeMode(99, 97)
  146. return list.sort((a, b) => a.goodsorder.localeCompare(b.goodsorder))
  147. })
  148. // 自适应高度
  149. const autoHeight = () => {
  150. const el = quoteRef.value
  151. const header = el?.querySelector('.quote-header')
  152. const table = el?.querySelector('.quote-table')
  153. if (header && table) {
  154. let height = document.body.clientHeight - header.clientHeight
  155. table.querySelectorAll('thead').forEach((e) => {
  156. height -= e.clientHeight
  157. })
  158. const tableRows = table.querySelectorAll('tbody tr') // 获取行数
  159. table.querySelectorAll('td').forEach((e) => {
  160. const n = tableRows.length < maxQty ? tableRows.length : maxQty
  161. e.style.setProperty('height', (height / n) + 'px')
  162. })
  163. }
  164. }
  165. onMounted(() => {
  166. serverTime.value = moment(new Date())
  167. timerTask.setInterval(() => {
  168. serverTime.value = moment(serverTime.value).add(1000, 'ms')
  169. }, 1000, 'refreshTime')
  170. // 监听窗口大小变化
  171. window.addEventListener('resize', timerInterceptor.setDebounce(() => autoHeight()))
  172. })
  173. onActivated(() => subscribeData.value.length && subscribe.start(...subscribeData.value))
  174. onDeactivated(() => subscribe.stop())
  175. onUnmounted(() => {
  176. timerTask.clearInterval('refreshTime')
  177. timerTask.clearTimeout('getServerTime')
  178. timerTask.clearTimeout('getMarketRun')
  179. })
  180. </script>
  181. <style lang="less">
  182. @import './index.less';
  183. </style>