li.shaoyi 2 éve
szülő
commit
1006f8d76f
25 módosított fájl, 853 hozzáadás és 40 törlés
  1. 1 1
      src/constants/chart.ts
  2. 6 2
      src/hooks/component/index.ts
  3. 7 7
      src/packages/pc/assets/themes/default/variable.less
  4. 0 25
      src/packages/pc/components/layouts/view/index.less
  5. 1 1
      src/packages/pc/components/layouts/view/index.vue
  6. 10 0
      src/packages/pc/components/modules/quote/chart/index.less
  7. 42 0
      src/packages/pc/components/modules/quote/chart/index.vue
  8. 44 0
      src/packages/pc/components/modules/quote/chart/kline/index.less
  9. 115 0
      src/packages/pc/components/modules/quote/chart/kline/index.vue
  10. 34 0
      src/packages/pc/components/modules/quote/chart/timeline/index.less
  11. 36 0
      src/packages/pc/components/modules/quote/chart/timeline/index.vue
  12. 30 0
      src/packages/pc/components/modules/quote/forex/index.less
  13. 90 0
      src/packages/pc/components/modules/quote/forex/index.vue
  14. 33 0
      src/packages/pc/components/modules/quote/index.vue
  15. 75 0
      src/packages/pc/components/modules/quote/price/index.less
  16. 80 0
      src/packages/pc/components/modules/quote/price/index.vue
  17. 11 0
      src/packages/pc/components/modules/quote/tik/index.less
  18. 87 0
      src/packages/pc/components/modules/quote/tik/index.vue
  19. 36 0
      src/packages/pc/views/market/trade/goods/detail/components/chart/index.vue
  20. 7 0
      src/packages/pc/views/market/trade/goods/detail/components/order/index.less
  21. 27 0
      src/packages/pc/views/market/trade/goods/detail/components/order/index.vue
  22. 26 0
      src/packages/pc/views/market/trade/goods/detail/index.less
  23. 35 0
      src/packages/pc/views/market/trade/goods/detail/index.vue
  24. 15 3
      src/packages/pc/views/market/trade/goods/index.vue
  25. 5 1
      src/packages/pc/views/market/trade/spot/index.vue

+ 1 - 1
src/constants/chart.ts

@@ -36,7 +36,7 @@ export function getChartCycleTypeList() {
         { label: '5分钟', value: ChartCycleType.Minutes5 },
         { label: '30分钟', value: ChartCycleType.Minutes30 },
         { label: '60分钟', value: ChartCycleType.Minutes60 },
-        { label: '2小时', value: ChartCycleType.Hours2 },
+        //{ label: '2小时', value: ChartCycleType.Hours2 },
         { label: '4小时', value: ChartCycleType.Hours4 },
         { label: '日线', value: ChartCycleType.Day },
     ]

+ 6 - 2
src/hooks/component/index.ts

@@ -6,10 +6,12 @@ import { v4 } from 'uuid'
 const componentInstanceMap = new Map<string, Ref>()
 
 /**
+ * 
  * @param callback 组件关闭时的回调
+ * @param routeListener 是否监听路由离开
  * @returns 
  */
-export function useComponent(callback?: (componentName?: string) => void) {
+export function useComponent(callback?: (componentName?: string) => void, routeListener = true) {
     const uuid = v4()
     const components = new Set<string>() // 已打开的组件列表
     const componentId = ref<string>() // 当前显示的组件
@@ -31,7 +33,9 @@ export function useComponent(callback?: (componentName?: string) => void) {
      * @param componentName 
      */
     const openComponent = (componentName: string) => {
-        components.add(componentName)
+        if (routeListener) {
+            components.add(componentName)
+        }
         componentId.value = componentName
     }
 

+ 7 - 7
src/packages/pc/assets/themes/default/variable.less

@@ -153,19 +153,19 @@
                 display: flex;
                 justify-content: center;
                 align-items: center;
-                color: #626675;
+                color: #7a8a94;
                 cursor: pointer;
-                border-radius: 4px;
-                background-color: #f0f0f1;
-                padding: 8px 16px;
+                height: 22px;
+                border: 1px solid #22292c;
+                padding: 0 16px;
 
                 &:not(:first-child) {
-                    margin-left: 10px;
+                    border-left: 0;
                 }
 
                 &.is-active {
-                    color: #222;
-                    font-weight: bold;
+                    color: #0866b8;
+                    background-color: #0e2f4c;
                 }
             }
         }

+ 0 - 25
src/packages/pc/components/layouts/view/index.less

@@ -1,26 +1,4 @@
 .app-view {
-    flex: 1;
-    overflow: auto;
-    -webkit-overflow-scrolling: touch;
-
-    &:after {
-        content: '';
-        flex-shrink: 0;
-        display: block;
-        height: 20px;
-    }
-
-    &__header,
-    &__footer,
-    &__main {
-        padding: 0 20px;
-        margin-top: 15px;
-    }
-
-    &__header {
-        margin-top: 20px;
-    }
-
     &__header &__container {
         display: flex;
         flex-wrap: wrap;
@@ -28,9 +6,6 @@
     }
 
     &__container {
-        border-radius: 4px;
-        padding: 20px;
-
         &:empty {
             display: none;
         }

+ 1 - 1
src/packages/pc/components/layouts/view/index.vue

@@ -29,7 +29,7 @@ const scrollMap = new Map<string, number>();
 defineProps({
   flex: {
     type: Boolean,
-    default: false,
+    default: true,
   }
 })
 

+ 10 - 0
src/packages/pc/components/modules/quote/chart/index.less

@@ -0,0 +1,10 @@
+.app-quote-chart {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    padding: 10px;
+
+    &__body {
+        flex: 1;
+    }
+}

+ 42 - 0
src/packages/pc/components/modules/quote/chart/index.vue

@@ -0,0 +1,42 @@
+<template>
+    <div class="app-quote-chart">
+        <div class="app-quote-chart__header">
+            <app-tabs class="app-tabs--info" :data-list="dataList" @change="onTabChange" />
+        </div>
+        <TimeLine class="app-quote-chart__body" v-bind="{ goodsCode }" @ready="onReady"
+            v-if="cycleType === ChartCycleType.Time" />
+        <KLine class="app-quote-chart__body" v-bind="{ goodsCode, cycleType }" v-else />
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { ChartCycleType, getChartCycleTypeList } from '@/constants/chart'
+import AppTabs from '@/components/base/tabs/index.vue'
+import TimeLine from './timeline/index.vue'
+import KLine from './kline/index.vue'
+
+defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    }
+})
+
+const emit = defineEmits(['ready'])
+const dataList = getChartCycleTypeList()
+const cycleType = shallowRef(ChartCycleType.Time) // 图表周期类型
+
+// 切换图表
+const onTabChange = (index: number) => {
+    cycleType.value = dataList[index].value
+}
+
+const onReady = (startTime: string, endTime: string) => {
+    emit('ready', startTime, endTime)
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 44 - 0
src/packages/pc/components/modules/quote/chart/kline/index.less

@@ -0,0 +1,44 @@
+.app-echats-kline {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+
+    &__tip {
+        color: #fff;
+        margin: auto;
+    }
+
+    &__container {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+
+        .app-echarts {
+            flex: 1;
+        }
+
+        .legend {
+            display: flex;
+            color: #7a8a94;
+            font-size: 12px;
+            padding: 4px 4%;
+            margin: 0;
+
+            &-item {
+                &:not(:first-child) {
+                    margin-left: 10px;
+                }
+            }
+        }
+    }
+
+    .app-tabs {
+        height: 30%;
+
+        .tabs {
+            &-item {
+                font-size: 12px;
+            }
+        }
+    }
+}

+ 115 - 0
src/packages/pc/components/modules/quote/chart/kline/index.vue

@@ -0,0 +1,115 @@
+<template>
+    <div class="app-echats-kline">
+        <div class="app-echats-kline__tip" v-if="loading">正在加载...</div>
+        <div class="app-echats-kline__tip" v-else-if="isEmpty">暂无数据</div>
+        <template v-else>
+            <div class="app-echats-kline__container">
+                <ul class="legend">
+                    <li class="legend-item">开: {{ selectedItem.open }}</li>
+                    <li class="legend-item">收: {{ selectedItem.close }}</li>
+                    <li class="legend-item">高: {{ selectedItem.highest }}</li>
+                    <li class="legend-item">低: {{ selectedItem.lowest }}</li>
+                    <li class="legend-item">MA5: {{ selectedItem.ma5 }}</li>
+                    <li class="legend-item">MA10: {{ selectedItem.ma10 }}</li>
+                    <li class="legend-item">MA15: {{ selectedItem.ma15 }}</li>
+                </ul>
+                <app-echarts :option="options.candlestick" v-model:dataIndex="dataIndex" @ready="mainReady" />
+            </div>
+            <app-tabs class="app-tabs--info" direction="bottom" :data-list="chartSeriesTypeList" @change="tabChange"
+                v-if="showIndicator">
+                <!-- MACD -->
+                <div class="app-echats-kline__container" v-if="activeSeriesType === ChartSeriesType.MACD">
+                    <ul class="legend">
+                        <li class="legend-item">MACD: {{ selectedItem.macd }}</li>
+                        <li class="legend-item">DIF: {{ selectedItem.dif }}</li>
+                        <li class="legend-item">DEA: {{ selectedItem.dea }}</li>
+                    </ul>
+                    <app-echarts :option="options.macd" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
+                </div>
+                <!-- VOL -->
+                <div class="app-echats-kline__container" v-if="activeSeriesType === ChartSeriesType.VOL">
+                    <ul class="legend">
+                        <li class="legend-item">VOL: {{ selectedItem.vol }}</li>
+                    </ul>
+                    <app-echarts :option="options.vol" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
+                </div>
+                <!-- KDJ -->
+                <div class="app-echats-kline__container" v-if="activeSeriesType === ChartSeriesType.KDJ">
+                    <ul class="legend">
+                        <li class="legend-item">K: {{ selectedItem.k }}</li>
+                        <li class="legend-item">D: {{ selectedItem.d }}</li>
+                        <li class="legend-item">J: {{ selectedItem.j }}</li>
+                    </ul>
+                    <app-echarts :option="options.kdj" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
+                </div>
+                <!-- CCI -->
+                <div class="app-echats-kline__container" v-if="activeSeriesType === ChartSeriesType.CCI">
+                    <ul class="legend">
+                        <li class="legend-item">CCI: {{ selectedItem.cci }}</li>
+                    </ul>
+                    <app-echarts :option="options.cci" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
+                </div>
+            </app-tabs>
+        </template>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { PropType, watch, shallowRef } from 'vue'
+import { echarts } from '@/components/base/echarts/core'
+import { ChartCycleType, ChartSeriesType, getChartSeriesTypeList } from '@/constants/chart'
+import { useCandlestickChart } from '@/hooks/echarts/candlestick'
+import AppEcharts from '@/components/base/echarts/index.vue'
+import AppTabs from '@/components/base/tabs/index.vue'
+
+const props = defineProps({
+    goodsCode: {
+        type: String,
+        default: '',
+    },
+    // 周期类型
+    cycleType: {
+        type: Number as PropType<ChartCycleType>,
+        default: ChartCycleType.Minutes,
+    },
+    // 是否显示指标
+    showIndicator: {
+        type: Boolean,
+        default: true,
+    },
+})
+
+const { loading, dataIndex, isEmpty, options, selectedItem, initData, initOptions } = useCandlestickChart(props.goodsCode);
+const activeSeriesType = shallowRef(ChartSeriesType.MACD); // 当前选中的指标
+const chartGroup = new Map<string, echarts.ECharts>(); // 图表联动实例组
+const chartSeriesTypeList = getChartSeriesTypeList();
+
+// 指标切换
+const tabChange = (index: number) => {
+    activeSeriesType.value = chartSeriesTypeList[index].value;
+    setTimeout(() => {
+        initOptions();
+    }, 0);
+}
+
+const mainReady = (chart: echarts.ECharts) => {
+    chartGroup.set('main', chart);
+    initOptions();
+}
+
+const indicatorReady = (chart: echarts.ECharts) => {
+    chartGroup.set('indicator', chart);
+    echarts.connect([...chartGroup.values()]); // 图表联动
+}
+
+// 监听周期选择变化
+watch(() => props.cycleType, (val) => {
+    initData(val);
+}, {
+    immediate: true
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 34 - 0
src/packages/pc/components/modules/quote/chart/timeline/index.less

@@ -0,0 +1,34 @@
+.app-echats-timeline {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+
+    &__tip {
+        color: #fff;
+        margin: auto;
+    }
+
+    &__container {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+
+        .app-echarts {
+            flex: 1;
+        }
+
+        .legend {
+            display: flex;
+            color: #7a8a94;
+            font-size: 12px;
+            padding: 4px 4%;
+            margin: 0;
+
+            &-item {
+                &:not(:first-child) {
+                    margin-left: 10px;
+                }
+            }
+        }
+    }
+}

+ 36 - 0
src/packages/pc/components/modules/quote/chart/timeline/index.vue

@@ -0,0 +1,36 @@
+<template>
+    <div class="app-echats-timeline">
+        <template v-if="loading">
+            <div class="app-echats-timeline__tip">正在加载...</div>
+        </template>
+        <template v-else-if="isEmpty">
+            <div class="app-echats-timeline__tip">暂无数据</div>
+        </template>
+        <template v-else>
+            <div class="app-echats-timeline__container">
+                <ul class="legend" v-if="false">
+                    <li class="legend-item">MA5: {{ selectedItem.ma5 }}</li>
+                </ul>
+                <app-echarts :option="options.timeline" v-model:dataIndex="dataIndex" @ready="initOptions" />
+            </div>
+        </template>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { useTimelineChart } from '@/hooks/echarts/timeline'
+import AppEcharts from '@/components/base/echarts/index.vue'
+
+const props = defineProps({
+    goodsCode: {
+        type: String,
+        default: '',
+    },
+})
+
+const { loading, dataIndex, isEmpty, options, selectedItem, initOptions } = useTimelineChart(props.goodsCode)
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 30 - 0
src/packages/pc/components/modules/quote/forex/index.less

@@ -0,0 +1,30 @@
+.app-quote-forex {
+    color: #fff;
+
+    ul {
+        padding: .24rem;
+
+        +ul {
+            border-top: 1px solid #666;
+        }
+
+        li {
+            display: flex;
+            font-size: .24rem;
+            text-align: center;
+            padding: .1rem 0;
+
+            span {
+                flex: 1;
+
+                &:first-child {
+                    text-align: left;
+                }
+
+                &:last-child {
+                    text-align: right;
+                }
+            }
+        }
+    }
+}

+ 90 - 0
src/packages/pc/components/modules/quote/forex/index.vue

@@ -0,0 +1,90 @@
+<template>
+    <div class="app-quote-forex">
+        <ul>
+            <li @click="onSellClick(1)">
+                <span>卖一</span>
+                <span :class="quote?.askColor">{{ handleNumberValue(quote?.ask) }}</span>
+                <span>{{ handleNumberValue(quote?.askvolume) }}</span>
+            </li>
+            <li @click="onSellClick(2)">
+                <span>卖二</span>
+                <span :class="quote?.ask2Color">{{ handleNumberValue(quote?.ask2) }}</span>
+                <span>{{ handleNumberValue(quote?.askvolume2) }}</span>
+            </li>
+            <li @click="onSellClick(3)">
+                <span>卖三</span>
+                <span :class="quote?.ask3Color">{{ handleNumberValue(quote?.ask3) }}</span>
+                <span>{{ handleNumberValue(quote?.askvolume3) }}</span>
+            </li>
+            <li @click="onSellClick(4)">
+                <span>卖四</span>
+                <span :class="quote?.ask4Color">{{ handleNumberValue(quote?.ask4) }}</span>
+                <span>{{ handleNumberValue(quote?.askvolume4) }}</span>
+            </li>
+            <li @click="onSellClick(5)">
+                <span>卖五</span>
+                <span :class="quote?.ask5Color">{{ handleNumberValue(quote?.ask5) }}</span>
+                <span>{{ handleNumberValue(quote?.askvolume5) }}</span>
+            </li>
+        </ul>
+        <ul>
+            <li @click="onBuyClick(1)">
+                <span>买一</span>
+                <span :class="quote?.bidColor">{{ handleNumberValue(quote?.bid) }}</span>
+                <span>{{ handleNumberValue(quote?.bidvolume) }}</span>
+            </li>
+            <li @click="onBuyClick(2)">
+                <span>买二</span>
+                <span :class="quote?.bid2Color">{{ handleNumberValue(quote?.bid2) }}</span>
+                <span>{{ handleNumberValue(quote?.bidvolume2) }}</span>
+            </li>
+            <li @click="onBuyClick(3)">
+                <span>买三</span>
+                <span :class="quote?.bid3Color">{{ handleNumberValue(quote?.bid3) }}</span>
+                <span>{{ handleNumberValue(quote?.bidvolume3) }}</span>
+            </li>
+            <li @click="onBuyClick(4)">
+                <span>买四</span>
+                <span :class="quote?.bid4Color">{{ handleNumberValue(quote?.bid4) }}</span>
+                <span>{{ handleNumberValue(quote?.bidvolume4) }}</span>
+            </li>
+            <li @click="onBuyClick(5)">
+                <span>买五</span>
+                <span :class="quote?.bid5Color">{{ handleNumberValue(quote?.bid5) }}</span>
+                <span>{{ handleNumberValue(quote?.bidvolume5) }}</span>
+            </li>
+        </ul>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { handleNumberValue } from '@/filters'
+import { useFuturesStore } from '@/stores'
+
+const props = defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    },
+    showMore: {
+        type: Boolean,
+        default: true
+    }
+})
+
+const emit = defineEmits(['sellclick', 'buyclick'])
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getQuoteInfo(props.goodsCode)
+
+const onBuyClick = (index: number) => {
+    emit('buyclick', index)
+}
+
+const onSellClick = (index: number) => {
+    emit('sellclick', index)
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 33 - 0
src/packages/pc/components/modules/quote/index.vue

@@ -0,0 +1,33 @@
+<template>
+    <div class="app-quote">
+        <Price v-bind="{ goodsCode }" />
+        <Chart v-bind="{ goodsCode }" @ready="onReady" />
+        <Forex v-bind="{ goodsCode }" v-if="showForex" />
+        <Tik v-bind="{ goodsCode, startTime, endTime }" />
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { formatDate } from '@/filters'
+import Price from './price/index.vue'
+import Chart from './chart/index.vue'
+import Forex from './forex/index.vue'
+import Tik from './tik/index.vue'
+
+defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    },
+    showForex: Boolean,
+})
+
+const startTime = shallowRef<string>()
+const endTime = shallowRef<string>()
+
+const onReady = (start: string, end: string) => {
+    startTime.value = formatDate(start)
+    endTime.value = formatDate(end)
+}
+</script>

+ 75 - 0
src/packages/pc/components/modules/quote/price/index.less

@@ -0,0 +1,75 @@
+.app-quote-price {
+    color: #fff;
+    border-bottom: 1px solid #333;
+
+    &__main {
+        border-bottom: 1px solid #333;
+
+        .block {
+            &-top {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+
+                span {
+                    &:last-child {
+                        font-size: 20px;
+                        color: coral;
+                    }
+                }
+            }
+
+            &-bottom {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+
+                &-left {
+                    width: 80px;
+                    text-align: center;
+                }
+
+                &-right {
+                    display: flex;
+                    flex-direction: column;
+
+                    span {
+                        font-size: 12px;
+                    }
+                }
+            }
+        }
+    }
+
+    &__more {
+        ul {
+            display: flex;
+            flex-wrap: wrap;
+
+            li {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                width: 50%;
+
+                &:nth-child(even) {
+                    padding: 2px 10px;
+                    padding-right: 0;
+                }
+
+                &:nth-child(odd) {
+                    padding: 2px 10px;
+                    padding-left: 0;
+                }
+
+                span {
+                    font-size: 12px;
+
+                    &:first-child {
+                        color: #888;
+                    }
+                }
+            }
+        }
+    }
+}

+ 80 - 0
src/packages/pc/components/modules/quote/price/index.vue

@@ -0,0 +1,80 @@
+<template>
+    <div class="app-quote-price" v-if="quote">
+        <div class="app-quote-price__main">
+            <div class="block-top">
+                <span>{{ quote.goodscode }}</span>
+                <span>{{ quote.goodsname }}</span>
+            </div>
+            <div class="block-bottom">
+                <div class="block-bottom-left">
+                    <span :class="quote.lastColor">{{ handleNumberValue(quote.last) }}</span>
+                </div>
+                <div class="block-bottom-right">
+                    <span :class="quote.lastColor">{{ quote.rise.toFixed(quote.decimalplace) }}</span>
+                    <span :class="quote.lastColor">{{ parsePercent(quote.change) }}</span>
+                </div>
+            </div>
+        </div>
+        <div class="app-quote-price__more">
+            <ul>
+                <li>
+                    <span>开盘</span>
+                    <span>{{ handleNumberValue(quote.opened) }}</span>
+                </li>
+                <li>
+                    <span>昨结</span>
+                    <span>{{ handleNumberValue(quote.presettle) }}</span>
+                </li>
+                <li>
+                    <span>最高</span>
+                    <span :class="quote.highestColor">{{ handleNumberValue(quote.highest) }}</span>
+                </li>
+                <li>
+                    <span>最低</span>
+                    <span :class="quote.lowestColor">{{ handleNumberValue(quote.lowest) }}</span>
+                </li>
+                <li>
+                    <span>涨停</span>
+                    <span class="g-price-up">{{ handleNumberValue(quote.limitup) }}</span>
+                </li>
+                <li>
+                    <span>跌停</span>
+                    <span class="g-price-down">{{ handleNumberValue(quote.limitdown) }}</span>
+                </li>
+                <li>
+                    <span>均价</span>
+                    <span>{{ handleNumberValue(quote.averageprice) }}</span>
+                </li>
+                <li>
+                    <span>振幅</span>
+                    <span>{{ parsePercent(quote.amplitude) }}</span>
+                </li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, onUnmounted } from 'vue'
+import { parsePercent, handleNumberValue } from '@/filters'
+import { useFuturesStore } from '@/stores'
+import quoteSocket from '@/services/websocket/quote'
+
+const props = defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    }
+})
+
+const futuresStore = useFuturesStore()
+const subscribe = quoteSocket.createSubscribe()
+const quote = futuresStore.getQuoteInfo(props.goodsCode)
+
+onMounted(() => subscribe.start(props.goodsCode))
+onUnmounted(() => subscribe.stop())
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 11 - 0
src/packages/pc/components/modules/quote/tik/index.less

@@ -0,0 +1,11 @@
+.app-quote-tik {
+    color: #fff;
+
+    &__header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        background-color: #333;
+        padding: 5px;
+    }
+}

+ 87 - 0
src/packages/pc/components/modules/quote/tik/index.vue

@@ -0,0 +1,87 @@
+<template>
+    <div class="app-quote-tik">
+        <div class="app-quote-tik__header">
+            <h4>成交明细</h4>
+            <span>更多</span>
+        </div>
+        <div class="app-quote-tik__body">
+            <ul>
+                <li v-for="(item, index) in dataList" :key="index">
+                    <span>{{ item.TS }}</span>
+                    <span>{{ item.PE }}</span>
+                    <span>{{ item.Vol }}</span>
+                </li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, watchEffect, watch } from 'vue'
+import { formatDate } from '@/filters'
+import { useRequest } from '@/hooks/request'
+import { queryHistoryTikDatas } from '@/services/api/market'
+import { useFuturesStore } from '@/stores'
+
+const props = defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    },
+    startTime: String,
+    endTime: String,
+})
+
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getQuoteInfo(props.goodsCode)
+const dataList = ref<Model.HistoryTikDatasRsp[]>([])
+
+const { run } = useRequest(queryHistoryTikDatas, {
+    manual: true,
+    params: {
+        goodsCode: props.goodsCode,
+        count: 10
+    },
+    onSuccess: (res) => {
+        dataList.value = res.data
+    }
+})
+
+watchEffect(() => {
+    if (props.startTime && props.endTime) {
+        run({
+            startTime: formatDate(props.startTime),
+            endTime: formatDate(props.endTime),
+        })
+    }
+})
+
+watch(() => quote.value?.last, () => {
+    if (quote.value) {
+        const list = dataList.value
+        if (list.length > 9) {
+            // 移除列表最后一条记录
+            list.pop()
+        }
+        // 向列表开头添加新纪录
+        list.unshift({
+            AV: 0,
+            Ask: 0,
+            BV: 0,
+            Bid: 0,
+            HI: 0,
+            HV: 0,
+            PE: quote.value.last,
+            TDR: 0,
+            TK: 0,
+            TS: quote.value.lasttime,
+            TT: 0,
+            Vol: quote.value.lastvolume,
+        })
+    }
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 36 - 0
src/packages/pc/views/market/trade/goods/detail/components/chart/index.vue

@@ -0,0 +1,36 @@
+<template>
+    <div class="market-trade-goods-detail__container">
+        <div class="block-left">
+            <Chart v-bind="{ goodsCode }" @ready="onReady" />
+        </div>
+        <div class="block-right">
+            <Price v-bind="{ goodsCode }" />
+            <Forex v-bind="{ goodsCode }" />
+            <Tik v-bind="{ goodsCode, startTime, endTime }" />
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { formatDate } from '@/filters'
+import Price from '@pc/components/modules/quote/price/index.vue'
+import Chart from '@pc/components/modules/quote/chart/index.vue'
+import Forex from '@pc/components/modules/quote/forex/index.vue'
+import Tik from '@pc/components/modules/quote/tik/index.vue'
+
+defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    },
+})
+
+const startTime = shallowRef<string>()
+const endTime = shallowRef<string>()
+
+const onReady = (start: string, end: string) => {
+    startTime.value = formatDate(start)
+    endTime.value = formatDate(end)
+}
+</script>

+ 7 - 0
src/packages/pc/views/market/trade/goods/detail/components/order/index.less

@@ -0,0 +1,7 @@
+.market-trade-goods-detail-order {
+    display: flex;
+
+    .app-table {
+        flex: 1;
+    }
+}

+ 27 - 0
src/packages/pc/views/market/trade/goods/detail/components/order/index.vue

@@ -0,0 +1,27 @@
+<template>
+    <div class="market-trade-goods-detail-order">
+        <app-table v-model:columns="buyColumns"></app-table>
+        <app-table v-model:columns="sellColumns"></app-table>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import AppTable from '@pc/components/base/table/index.vue'
+
+const buyColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'goodscode', label: '销售方' },
+    { prop: 'buildtype', label: '卖量' },
+    { prop: 'orderprice', label: '卖价' },
+])
+
+const sellColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'goodscode', label: '买价' },
+    { prop: 'buildtype', label: '买量' },
+    { prop: 'orderprice', label: '购买方' },
+])
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 26 - 0
src/packages/pc/views/market/trade/goods/detail/index.less

@@ -0,0 +1,26 @@
+.market-trade-goods-detail {
+    .app-view__header {
+        background-color: #181e22;
+        border-bottom: 1px solid #3a87f7;
+    }
+
+    &__container {
+        display: flex;
+        height: 100%;
+
+        >.block-left {
+            flex: 1;
+        }
+
+        >.block-right {
+            display: flex;
+            flex-direction: column;
+            width: 240px;
+
+            .app-quote-tik {
+                flex: 1;
+                overflow-y: auto;
+            }
+        }
+    }
+}

+ 35 - 0
src/packages/pc/views/market/trade/goods/detail/index.vue

@@ -0,0 +1,35 @@
+<!-- 交易市场-订单交易-详情 -->
+<template>
+    <teleport to="#appPageTeleport">
+        <app-view class="market-trade-goods-detail">
+            <template #header>
+                <el-button type="primary" icon="ArrowLeftBold" @click="emit('closed')" />
+                <el-button type="primary" @click="active = false" v-if="active">买卖大厅</el-button>
+                <el-button type="primary" @click="active = true" v-else>图表</el-button>
+            </template>
+            <Chart v-bind="{ goodsCode }" v-if="active" />
+            <Order v-bind="{ goodsCode }" v-else />
+        </app-view>
+    </teleport>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, defineAsyncComponent } from 'vue'
+
+const Chart = defineAsyncComponent(() => import('./components/chart/index.vue'))
+const Order = defineAsyncComponent(() => import('./components/order/index.vue'))
+
+defineProps({
+    goodsCode: {
+        type: String,
+        required: true
+    },
+})
+
+const emit = defineEmits(['closed'])
+const active = shallowRef(true)
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 15 - 3
src/packages/pc/views/market/trade/goods/index.vue

@@ -1,22 +1,29 @@
 <!-- 交易市场-订单交易 -->
 <template>
-    <app-table :data="tableList" v-model:columns="tableColumns" :loading="loading" showIndex />
+    <app-table :data="tableList" v-model:columns="tableColumns" :loading="loading" @row-click="onRowClick" showIndex />
+    <component ref="componentRef" :is="Detail" v-bind="{ goodsCode }" @closed="closeComponent" v-if="componentId" />
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, computed, onUnmounted } from 'vue'
+import { shallowRef, computed, onUnmounted, defineAsyncComponent } from 'vue'
 import { parsePercent, handleNumberValue } from '@/filters'
 import { useRequest } from '@/hooks/request'
+import { useComponent } from '@/hooks/component'
 import { queryQuoteGoodsList } from '@/services/api/swap'
 import { useFuturesStore, useUserStore } from '@/stores'
 import quoteSocket from '@/services/websocket/quote'
 import AppTable from '@pc/components/base/table/index.vue'
 
+const Detail = defineAsyncComponent(() => import('./detail/index.vue')) // 详情
+
 const futuresStore = useFuturesStore()
 const userStore = useUserStore()
 const subscribe = quoteSocket.createSubscribe()
+const goodsCode = shallowRef('')
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => run(), false)
 
-const { dataList, loading } = useRequest(queryQuoteGoodsList, {
+const { dataList, loading, run } = useRequest(queryQuoteGoodsList, {
     params: {
         usertype: userStore.userType ?? 0,
         marketids: '50101'
@@ -78,5 +85,10 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'limitdown', label: '跌停价' },
 ])
 
+const onRowClick = (row: Model.OrderQuoteRsp) => {
+    goodsCode.value = row.goodscode
+    openComponent('detail')
+}
+
 onUnmounted(() => subscribe.stop())
 </script>

+ 5 - 1
src/packages/pc/views/market/trade/spot/index.vue

@@ -1,6 +1,6 @@
 <!-- 交易市场-仓单交易 -->
 <template>
-    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" showIndex>
+    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" @row-click="onRowClick" showIndex>
         <template #header>
             <app-filter :options="filterOptons" :loading="loading" />
         </template>
@@ -51,4 +51,8 @@ const onSearch = (clear = false) => {
         run(qs)
     }, clear)
 }
+
+const onRowClick = (row: Model.OrderQuoteRsp) => {
+    console.log(row)
+}
 </script>