import { reactive, computed, toRefs } from 'vue' import { timerInterceptor } from '@/utils/timer' import { queryErmcpGoods, queryQuoteDay } from '@/services/api/goods' import { defineStore } from '../store' import { timerTask } from '@/utils/timer' import eventBus from '@/services/bus' import moment from 'moment' /** * 期货存储对象 * @returns */ export const useFuturesStore = defineStore(() => { const state = reactive({ loading: false, quotes: [], // 行情数据 goodsList: [], // 商品列表 quoteDayList: [], // 盘面列表 }) // 行情列表 const quoteList = computed(() => { return state.goodsList.reduce((res, cur) => { const { goodsid, goodscode, goodsname, goodsgroupid, marketid, decimalplace } = cur const quoteDayInfo = state.quoteDayList.find((e) => e.goodscode.toUpperCase() === goodscode.toUpperCase()) const { last = 0, bid = 0, ask = 0, bidvolume = 0, askvolume = 0, totalvolume = 0, lastvolume = 0, holdvolume = 0, holdincrement = 0, presettle = 0, totalturnover = 0, opened = 0, highest = 0, lowest = 0, lasttime = '', } = quoteDayInfo ?? {} const rise = last ? last - presettle : 0 // 涨跌额/涨跌: 最新价 - 昨结价 const change = presettle ? rise / presettle : 0 // 涨跌幅/幅度: (最新价 - 昨结价) / 昨结价 const amplitude = last ? (highest - lowest) / last : 0 // 振幅: (最高价 - 最低价 ) / 最新价 // 处理行情价格颜色 const handleColor = (value: number) => { if (value === presettle) { return '' } else if (value > presettle) { return 'g-up-color' } else { return 'g-down-color' } } const item: Model.Futures = { marketid, goodsgroupid, goodsid, goodscode, goodsname, decimalplace, last, lasttime, rise, change, amplitude, bid, ask, bidvolume, askvolume, totalvolume, lastvolume, holdvolume, holdincrement, presettle, totalturnover, opened, highest, lowest, bidColor: handleColor(bid), askColor: handleColor(ask), lastColor: handleColor(last), bidvolumeColor: handleColor(bidvolume), askvolumeColor: handleColor(askvolume), openedColor: handleColor(opened), highestColor: handleColor(highest), lowestColor: handleColor(lowest), } res.push(item) return res }, [] as Model.Futures[]) }) // 获取商品列表 const getGoodsList = async () => { state.loading = true try { const res = await queryErmcpGoods() const codes = res.data.map((e) => e.goodscode) state.goodsList = res.data // 获取商品盘面信息 const getQuoteDay = async () => { try { const res_1 = await queryQuoteDay({ data: { goodsCodes: codes.join(',') } }) state.quoteDayList = res_1.data } finally { // 每5分钟获取一次盘面 timerTask.setTimeout(() => getQuoteDay(), 5 * 60 * 1000, 'quoteDay') } } if (codes.length) { getQuoteDay() } } finally { state.loading = false } } // 通过 goodscode 获取实时行情信息 const getQuoteInfo = (goodscode: string) => { return computed(() => quoteList.value.find((e) => e.goodscode.toUpperCase() === goodscode.toUpperCase())) } // 通过 goodscode 获取实时行情报价 const getQuotePrice = (goodscode: string) => { return computed(() => { const quote = getQuoteInfo(goodscode) return quote.value?.last ?? 0 }) } // 处理行情数据 const handleQuote = timerInterceptor.setThrottle(() => { state.quotes.forEach((item) => { const goods = state.goodsList.find((e) => e.goodscode.toUpperCase() === item.goodscode?.toUpperCase()) const quote: Model.QuoteDayRsp | undefined = state.quoteDayList.find((e) => e.goodscode.toUpperCase() === item.goodscode?.toUpperCase()) const lasttime = (item.date && item.time) ? moment(item.date + item.time, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss') : '' // 处理报价小数位 const handleDeimalplace = (value = 0) => { const decimal = goods ? Math.pow(10, goods.decimalplace) : 0 if (decimal) { return value / decimal } return value } const quoteItem: Model.QuoteDayRsp = { ask: handleDeimalplace(item.ask), ask2: handleDeimalplace(item.ask2), ask3: handleDeimalplace(item.ask3), ask4: handleDeimalplace(item.ask4), ask5: handleDeimalplace(item.ask5), ask6: 0, ask7: 0, ask8: 0, ask9: 0, ask10: 0, askorderid: 0, askorderid2: 0, askorderid3: 0, askorderid4: 0, askorderid5: 0, askordervolume: 0, askordervolume2: 0, askordervolume3: 0, askordervolume4: 0, askordervolume5: 0, askordervolume6: 0, askordervolume7: 0, askordervolume8: 0, askordervolume9: 0, askordervolume10: 0, askqueueinfo: '', askvolume: handleDeimalplace(item.askvolume), askvolume2: handleDeimalplace(item.askvolume2), askvolume3: handleDeimalplace(item.askvolume3), askvolume4: handleDeimalplace(item.askvolume4), askvolume5: handleDeimalplace(item.askvolume5), askvolume6: 0, askvolume7: 0, askvolume8: 0, askvolume9: 0, askvolume10: 0, averageprice: 0, bid: handleDeimalplace(item.bid), bid2: handleDeimalplace(item.bid2), bid3: handleDeimalplace(item.bid3), bid4: handleDeimalplace(item.bid4), bid5: handleDeimalplace(item.bid5), bid6: 0, bid7: 0, bid8: 0, bid9: 0, bid10: 0, bidorderid: 0, bidorderid2: 0, bidorderid3: 0, bidorderid4: 0, bidorderid5: 0, bidordervolume: 0, bidordervolume2: 0, bidordervolume3: 0, bidordervolume4: 0, bidordervolume5: 0, bidordervolume6: 0, bidordervolume7: 0, bidordervolume8: 0, bidordervolume9: 0, bidordervolume10: 0, bidqueueinfo: "", bidvolume: handleDeimalplace(item.bidvolume), bidvolume2: handleDeimalplace(item.bidvolume2), bidvolume3: handleDeimalplace(item.bidvolume3), bidvolume4: handleDeimalplace(item.bidvolume4), bidvolume5: handleDeimalplace(item.bidvolume5), bidvolume6: 0, bidvolume7: 0, bidvolume8: 0, bidvolume9: 0, bidvolume10: 0, calloptionpremiums: handleDeimalplace(item.calloptionpremiums), calloptionpremiums2: handleDeimalplace(item.calloptionpremiums2), calloptionpremiums3: handleDeimalplace(item.calloptionpremiums3), calloptionpremiums4: handleDeimalplace(item.calloptionpremiums4), calloptionpremiums5: handleDeimalplace(item.calloptionpremiums5), cleartime: 0, exchangecode: handleDeimalplace(item.exchangecode), exchangedate: handleDeimalplace(item.exchangedate), goodscode: item.goodscode ?? '', grepmarketprice: 0, highest: handleDeimalplace(item.highest), holdincrement: 0, holdvolume: handleDeimalplace(item.holdvolume), iep: 0, iev: 0, inventory: handleDeimalplace(item.inventory), iscleared: 0, issettled: 0, last: handleDeimalplace(item.last), lastlot: 0, lasttime, Lastturnover: 0, lastvolume: handleDeimalplace(item.lastvolume), limitdown: handleDeimalplace(item.limitlow), limitup: handleDeimalplace(item.limithigh), lowest: handleDeimalplace(item.lowest), nontotalholdervolume: 0, nontotallot: 0, nontotalturnover: 0, nontotalvolume: 0, opened: handleDeimalplace(item.opened), opentime: '', orderid: 0, preclose: handleDeimalplace(item.preclose), preholdvolume: handleDeimalplace(item.preholdvolume), presettle: handleDeimalplace(item.presettle), publictradetype: '', putoptionpremiums: handleDeimalplace(item.putoptionpremiums), putoptionpremiums2: handleDeimalplace(item.putoptionpremiums2), putoptionpremiums3: handleDeimalplace(item.putoptionpremiums3), putoptionpremiums4: handleDeimalplace(item.putoptionpremiums4), putoptionpremiums5: handleDeimalplace(item.putoptionpremiums5), settle: handleDeimalplace(item.settle), strikeprice: 0, totalaskvolume: 0, totalbidvolume: 0, totallot: 0, totalturnover: handleDeimalplace(item.totalturnover), totalvolume: handleDeimalplace(item.totalvolume), utclasttime: '' } if (quote) { Object.keys(item).forEach((key) => { // 只更新存在的属性 if (Reflect.has(quote, key)) { ((prop: K) => { quote[prop] = quoteItem[prop] })(key as keyof Model.QuoteDayRsp) } }) // 处理最高最低价 if (quote.last) { if (quote.last > quote.highest) { quote.highest = quote.last } if (quote.last < quote.lowest) { quote.lowest = quote.last } } // 处理最新时间 if (lasttime) { quote.lasttime = lasttime } } else { console.warn('行情推送的商品 ' + item.goodscode + ' 缺少盘面信息') state.quoteDayList.push(quoteItem) } }) }, 200) // 接收行情推送通知 const quotePushNotify = eventBus.$on('QuotePushNotify', (res) => { const data = res as Proto.Quote[] data.forEach((item) => { const index = state.quotes.findIndex((e) => e.goodscode === item.goodscode) if (index > -1) { state.quotes[index] = item } else { state.quotes.push(item) } }) handleQuote() }) return { ...toRefs(state), quoteList, getGoodsList, getQuoteInfo, getQuotePrice, quotePushNotify, } })