Handy_Cao 2 роки тому
батько
коміт
e20b9783c8

+ 18 - 0
public/config/router.json

@@ -336,6 +336,24 @@
                         "url": "spot",
                         "urlType": 1,
                         "component": "views/market/trade/spot/index.vue"
+                    },
+                    {
+                        "authType": 1,
+                        "sort": 4,
+                        "title": "掉期市场",
+                        "code": "market_trade_swap",
+                        "url": "spot",
+                        "urlType": 1,
+                        "component": "views/market/trade/swap/index.vue"
+                    },
+                    {
+                        "authType": 1,
+                        "sort": 5,
+                        "title": "参考行情",
+                        "code": "market_trade_market",
+                        "url": "spot",
+                        "urlType": 1,
+                        "component": "views/market/trade/market/index.vue"
                     }
                 ]
             }

+ 1 - 1
src/packages/mobile/components/base/list/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="app-list">
+    <div class="app-list" v-if="dataList.length != 0">
         <table class="app-list__table" cellspacing="0" cellpadding="0">
             <!-- <colgroup>
                 <col :width="colWidth" v-for="i in columns.length" :key="i" />

+ 1 - 1
src/packages/mobile/views/order/list/components/swaporder/detail/Index.vue

@@ -51,7 +51,7 @@ const onCancelSumit = () => {
         showCancelButton: true,
     }).then(() => {
         /// 市场ID
-        formData.Header = { MarketID: props.selectedRow.marketid }
+        formData.Header = { MarketID: props.selectedRow.marketid, GoodsID:props.selectedRow.goodsid }
         formData.OldOrderId = handleRequestBigNumber(props.selectedRow.orderid)
 
         /// loding....

+ 1 - 1
src/packages/mobile/views/order/list/components/swaporder/list/Index.vue

@@ -97,7 +97,7 @@ const onCancelSumit = (item: Model.TradeOrderDetailRsp) => {
         message: '确认要撤销吗?',
         showCancelButton: true,
     }).then(() => {
-        formData.Header = { MarketID: item.marketid }
+        formData.Header = { MarketID: item.marketid, GoodsID: item.goodsid }
         formData.OldOrderId = handleRequestBigNumber(item.orderid)
 
         /// loding....

+ 13 - 5
src/packages/mobile/views/swap/detail/Index.vue

@@ -7,11 +7,11 @@
                         <span>挂牌</span>
                     </div>
                 </template>
-                <template #footer>
-                    <app-quote :goodsCode="item.refgoodscode" style="margin-bottom: .2rem;" />
-                </template>
             </app-navbar>
         </template>
+        <component :is="Price" v-bind="{ goodsCode }" />
+        <component :is="Chart" v-bind="{ goodsCode }" />
+        <component :is="Tik" v-bind="{ goodsCode }" />
         <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" v-model:error="error" v-model:pageIndex="pageIndex"
             :page-count="pageCount" @refresh="onRefresh">
             <Tabs v-model:active="tabIndex" @click="onTabChange">
@@ -37,22 +37,27 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, defineAsyncComponent } from 'vue'
+import { shallowRef, defineAsyncComponent, computed } from 'vue'
 import { Tab, Tabs, Button, showToast } from 'vant'
 import { useRequest } from '@/hooks/request'
 import { useNavigation } from '@mobile/router/navigation'
 import { useComponent } from '@/hooks/component'
 import { BuyOrSell } from '@/constants/order'
 import { queryTjmdTradeOrderDetail } from '@/services/api/swap'
+import { useFuturesStore } from '@/stores'
 import { useLoginStore } from '@/stores'
 import AppPullRefresh from '@mobile/components/base/pull-refresh/index.vue'
 import AppList from '@mobile/components/base/list/index.vue'
-import AppQuote from '@mobile/components/modules/quote/price/index.vue'
+
+const Price = defineAsyncComponent(() => import('@mobile/components/modules/quote/price/index.vue'))
+const Chart = defineAsyncComponent(() => import('@mobile/components/modules/quote/chart/index.vue'))
+const Tik = defineAsyncComponent(() => import('@mobile/components/modules/quote/tik/index.vue'))
 
 const componentMap = new Map<string, unknown>([
     ['delisting', defineAsyncComponent(() => import('./components/delisting/Index.vue'))],
     ['listing', defineAsyncComponent(() => import('./components/listing/Index.vue'))],
 ])
+
 const pullRefreshRef = shallowRef()
 const loginStore = useLoginStore()
 const { getGlobalUrlParams } = useNavigation()
@@ -61,6 +66,9 @@ const tabIndex = shallowRef(0)
 const selectedRow = shallowRef<Model.TjmdTradeOrderDetailRsp>()
 const error = shallowRef(false)
 const dataList = shallowRef<Model.TjmdTradeOrderDetailRsp[]>([])
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getGoodsQuote(item.refgoodsid)
+const goodsCode = computed(() => quote.value?.goodscode ?? '')
 
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => onTabChange())
 

+ 1 - 1
src/packages/pc/components/modules/goods-detail/index.vue

@@ -20,7 +20,7 @@
                             </span>
                         </template>
                         <slot name="headerRight"></slot>
-                        <template v-if="quote?.trademode === 16">
+                        <template v-if="![50, 99].includes(quote?.trademode ?? 0)">
                             <el-button type="primary" @click="active = false" v-if="active">买卖大厅</el-button>
                             <el-button type="primary" @click="active = true" v-else>图表</el-button>
                         </template>

+ 1 - 1
src/packages/pc/views/footer/swap/order/cancel/index.vue

@@ -35,7 +35,7 @@ const onCancel = (isRefresh = false) => {
 
 const onCancelSumit = () => {
     /// 市场ID
-    formData.Header = { MarketID: props.selectedRow.marketid }
+    formData.Header = { MarketID: props.selectedRow.marketid, GoodsID: props.selectedRow.goodsid }
     formData.OldOrderId = handleRequestBigNumber(props.selectedRow.orderid)
     /// 提交
     cancelSubmit().then(() => {

+ 2 - 6
src/packages/pc/views/footer/swap/order/index.vue

@@ -14,10 +14,6 @@
         <template #orderstatus="{ value }">
             {{ getWRTradeOrderStatusName(value) }}
         </template>
-        <!-- 委托状态 -->
-        <template #wrtradeorderstatus="{ value }">
-            {{ getWRTradeOrderStatusName(value) }}
-        </template>
         <!-- 委托时间 -->
         <template #ordertime="{ value }">
             {{ formatDate(value) }}
@@ -64,14 +60,14 @@ const { rowKey, expandKeys, rowClick } = useComposeTable<Model.TradeOrderDetailR
 const selectedRow = shallowRef<Model.TradeOrderDetailRsp>()
 
 const tableColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'orderid', label: '委托单号' },
     { prop: 'goodsname', label: '商品代码/名称' },
     { prop: 'buyorsell', label: '方向' },
     { prop: 'orderqty', label: '委托数量' },
     { prop: 'orderprice', label: '委托价格' },
     { prop: 'tradeqty', label: '成交数量' },
     { prop: 'orderstatus', label: '委托状态' },
-    { prop: 'ordertime', label: '委托时间' },
-    { prop: 'orderid', label: '挂牌单号' },
+    { prop: 'ordertime', label: '委托时间' }
 ])
 
 const showComponent = (componentName: string, row: Model.TradeOrderDetailRsp) => {

+ 134 - 0
src/packages/pc/views/footer/swap/position/close/index.vue

@@ -0,0 +1,134 @@
+<!-- 掉期市场-持仓汇总-平仓 -->
+<template>
+    <app-drawer title="平仓" v-model:show="show" :width="960" :loading="loading" :refresh="refresh">
+        <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey"
+        :expand-row-keys="expandKeys" @row-click="rowClick">
+            <!-- 商品代码/名称 -->
+            <template #goodsname="{ row }">
+                {{ row.goodscode }}/{{ row.goodsname }}
+            </template>
+            <!-- 方向 -->
+            <template #buyorsell="{ value }">
+                {{ getBuyOrSellName(value) }}
+            </template>
+            <!-- 最新价 -->
+            <template #holderamount="{ value }">
+                <span>
+                    {{ formatDecimal(value) }}
+                </span>
+            </template>
+            <!-- 可用数量 -->
+            <template #enableqty="{ row }">
+                {{ row.holderqty - row.freezeqty  }}
+            </template>
+            <!-- 到期日 -->
+            <template #expiredate="{ value }">
+                <span>{{ formatDate(value, 'YYYY/MM/DD') }}</span>
+            </template>
+            <!-- 展开行 -->
+            <template #expand="{ row }">
+                <div class="buttonbar">
+                    <el-button type="danger" size="small" @click="onCloseSubmit(row)">平仓</el-button>
+                </div>
+            </template>
+        </app-table>
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)">取消</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, ref, PropType } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { useComposeTable } from '@pc/components/base/table'
+import { useRequest } from '@/hooks/request'
+import { useHolderClose } from '@/business/trade'
+import { queryTradeHolderDetail } from '@/services/api/order'
+import { formatDate, formatDecimal, handleRequestBigNumber } from '@/filters'
+import { getBuyOrSellName } from '@/constants/order'
+import { ETradeMode } from '@/constants/client'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppTable from '@pc/components/base/table/index.vue'
+import quoteSocket from '@/services/websocket/quote'
+
+const { rowKey, expandKeys, rowClick } = useComposeTable<Model.TradeHolderDetailRsp>({ rowKey: 'tradeid' })
+
+const props = defineProps({
+    position: {
+        type: Object as PropType<Model.TradePositionRsp>,
+        required: true,
+    }
+})
+
+const show = ref(true)
+const refresh = ref(false)
+const dataList = shallowRef<Model.TradeHolderDetailRsp[]>([])
+const error = shallowRef(false)
+const subscribe = quoteSocket.createSubscribe()
+const { holderCloseSubmit, formData } = useHolderClose()
+
+const { loading, pageIndex } = useRequest(queryTradeHolderDetail, {
+    params: {
+        pagesize: 20,
+        /// 交易模式, 格式 1,2,3
+        trademodes: ETradeMode.TRADEMODE_TJMD.toString(),
+        /// marketid
+        marketids: props.position.marketid.toString(),
+        /// 商品id
+        goodsid: props.position.goodsid,
+        /// 买卖方向 0-买 1-卖
+        buyorsell: props.position.buyorsell
+    },
+    onSuccess: (res) => {
+        if (pageIndex.value === 1) {
+            dataList.value = []
+        }
+        dataList.value.push(...res.data)
+        /// 订阅行情
+        subscribe.start(props.position.refgoodscode)
+    },
+    onError: () => {
+        error.value = true
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'tradeid', label: '单号' },
+    { prop: 'goodsname', label: '商品代码/名称' },
+    { prop: 'buyorsell', label: '方向' },
+    { prop: 'holderqty', label: '持有数量' },
+    { prop: 'freezeqty', label: '冻结数量' },
+    { prop: 'enableqty', label: '可用数量' },
+    { prop: 'holderprice', label: '持仓价格' },
+    { prop: 'holderamount', label: '持仓金额' },
+    { prop: 'closepl', label: '参考损益' },
+    { prop: 'expiredate', label: '到期日' }
+])
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onCloseSubmit = (row: Model.TradeHolderDetailRsp) => {
+    ElMessageBox.confirm(
+        '是否立即平仓?'
+    ).then(() => {
+        const { marketid, goodsid, buyorsell, tradeid } = row
+        formData.Header = { MarketID: marketid, GoodsID: goodsid }
+        formData.GoodsID = goodsid
+        formData.BuyOrSell = buyorsell
+        formData.MarketID = marketid
+        formData.TradeID = handleRequestBigNumber(tradeid)
+
+        holderCloseSubmit().then(() => {
+            ElMessage.success('提交成功')
+            onCancel(true)
+        }).catch((err) => {
+            ElMessage.error('提交失败:' + err)
+        })
+    })
+}
+
+</script>

+ 63 - 33
src/packages/pc/views/footer/swap/position/index.vue

@@ -1,47 +1,77 @@
-<!-- 掉期市场-持仓汇总 -->
+<!-- 掉期市场-持仓汇总 -->
 <template>
-    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading">
-        <!-- 挂牌类型 -->
-        <template #wrtradetype="{ value }">
-            {{ getWrTradeTypeName(value) }}
+    <app-table :data="positionList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey"
+        :expand-row-keys="expandKeys" @row-click="rowClick">
+        <!-- 商品代码/名称 -->
+        <template #wrtradetype="{ row }">
+            {{ row.goodscode }}/{{ row.goodsname }}
         </template>
-        <!-- 成交金额 -->
-        <template #tradeamount="{ row }">
-            {{ formatDecimal(row.tradeqty*row.tradeprice) }}
+        <!-- 方向 -->
+        <template #buyorsell="{ value }">
+            {{ getBuyOrSellName(value) }}
         </template>
-        <!-- 成交时间 -->
-        <template #tradetime="{ value }">
-            {{ formatDate(value) }}
+        <!-- 最新价 -->
+        <template #lastprice="{ row }">
+            <span :class="row.lastColor">
+                {{ handleNumberValue(formatDecimal(row.lastprice, row.decimalplace)) }}
+            </span>
+        </template>
+        <!-- 持仓均价 -->
+        <template #averageprice="{ value }">
+            {{ handleNumberValue(formatDecimal(value))  }}
+        </template>
+        <!-- 浮动盈亏-->
+        <template #closepl="{ row }">
+            <span :class="row.closeplColor">{{ formatDecimal(row.closepl) }}</span>
+        </template>
+        <!-- 展开行 -->
+        <template #expand="{ row }">
+            <div class="buttonbar">
+                <el-button type="danger" size="small" @click="showComponent('close', row)">平仓</el-button>
+            </div>
+        </template>
+        <template #footer>
+            <component ref="componentRef" v-bind="{ position: selectedRow }" :is="componentMap.get(componentId)"
+                @closed="closeComponent" v-if="componentId" />
         </template>
     </app-table>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
-import { formatDate, formatDecimal } from '@/filters'
-import { useRequest } from '@/hooks/request'
-import { queryWrTradeDetail } from '@/services/api/order'
-import { getWrTradeTypeName } from '@/constants/order'
+import { shallowRef, defineAsyncComponent } from 'vue'
+import { formatDecimal, handleNumberValue } from '@/filters'
+import { usePosition } from '@/business/position'
+import { useComponent } from '@/hooks/component'
+import { getBuyOrSellName } from '@/constants/order'
+import { useComposeTable } from '@pc/components/base/table'
 import AppTable from '@pc/components/base/table/index.vue'
 
-const { loading, dataList } = useRequest(queryWrTradeDetail, {
-    params: {
-        marketid: 17201,
-        haswr: 1
-    },
-})
+const componentMap = new Map<string, unknown>([
+    ['close', defineAsyncComponent(() => import('./close/index.vue'))],
+])
+
+const { positionList, loading } = usePosition(46)
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
+
+const { rowKey, expandKeys, rowClick } = useComposeTable<Model.TradePositionRsp>({ rowKey: 'pkid' })
+const selectedRow = shallowRef<Model.TradePositionRsp>()
 
 const tableColumns = shallowRef<Model.TableColumn[]>([
-    { prop: 'wrtradedetailid', label: '成交单号' },
-    { prop: 'wrtradetype', label: '类型' },
-    { prop: 'deliverygoodsname', label: '品种' },
-    { prop: 'wrstandardname', label: '商品' },
-    { prop: 'warehousename', label: '仓库' },
-    { prop: 'tradeprice', label: '成交价格' },
-    { prop: 'tradeqty', label: '成交数量' },
-    { prop: 'tradeamount', label: '成交金额 ' },
-    { prop: 'chargevalue', label: '手续费' },
-    { prop: 'matchusername', label: '对手方' },
-    { prop: 'tradetime', label: '成交时间' }
+    { prop: 'goodsname', label: '商品代码/名称' },
+    { prop: 'buyorsell', label: '方向' },
+    { prop: 'curpositionqty', label: '持有数量' },
+    { prop: 'lastprice', label: '现价' },
+    { prop: 'averageprice', label: '持仓均价' },
+    { prop: 'frozenqty', label: '冻结数量' },
+    { prop: 'curholderamount', label: '持仓金额' },
+    { prop: 'enableqty', label: '可用数量' },
+    { prop: 'closepl', label: '参考损益' }
 ])
+
+const showComponent = (componentName: string, row: Model.TradePositionRsp) => {
+    selectedRow.value = row
+    openComponent(componentName)
+}
+
 </script>

+ 1 - 1
src/packages/pc/views/footer/swap/trade/index.vue

@@ -48,6 +48,6 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'tradeqty', label: '成交数量' },
     { prop: 'tradeamount', label: '成交金额 ' },
     { prop: 'closepl', label: '平仓盈亏' },
-    { prop: 'tradetime', label: '时间' },
+    { prop: 'tradetime', label: '成交时间' },
 ])
 </script>

+ 101 - 0
src/packages/pc/views/market/trade/market/index.vue

@@ -0,0 +1,101 @@
+<!-- 交易市场-参考行情" -->
+<template>
+    <app-table :data="marketGoodsList" v-model:columns="tableColumns" @row-click="onRowClick" showIndex>
+        <!-- 买价 -->
+        <template #bid="{ row }">
+            <span :class="row.bidColor">{{ handleNumberValue(formatDecimal(row.bid, row.decimalplace)) }}</span>
+        </template>
+        <!-- 卖价 -->
+        <template #ask="{ row }">
+            <span :class="row.askColor">{{ handleNumberValue(formatDecimal(row.ask, row.decimalplace)) }}</span>
+        </template>
+        <!-- 当前价 -->
+        <template #last="{ row }">
+            <span :class="row.lastColor">{{ handleNumberValue(formatDecimal(row.last, row.decimalplace)) }}</span>
+        </template>
+        <!-- 涨跌 -->
+        <template #rise="{ row }">
+            <span :class="row.lastColor">{{ formatDecimal(row.rise, row.decimalplace) }}</span>
+        </template>
+        <!-- 涨跌幅 -->
+        <template #change="{ row }">
+            <span :class="row.lastColor">{{ parsePercent(row.change) }}</span>
+        </template>
+        <!-- 开盘 -->
+        <template #opened="{ row }">
+            <span :class="row.openedColor">{{ handleNumberValue(formatDecimal(row.opened, row.decimalplace)) }}</span>
+        </template>
+        <!-- 最低 -->
+        <template #lowest="{ row }">
+            <span :class="row.lowestColor">{{ handleNumberValue(formatDecimal(row.lowest, row.decimalplace)) }}</span>
+        </template>
+        <!-- 最高 -->
+        <template #highest="{ row }">
+            <span :class="row.highestColor">{{ handleNumberValue(formatDecimal(row.highest, row.decimalplace)) }}</span>
+        </template>
+        <!-- 振幅 -->
+        <template #amplitude="{ value }">
+            <span>{{ parsePercent(value) }}</span>
+        </template>
+        <template #footer>
+            <component ref="componentRef" v-bind="{ goodsId: selectedGoodsId }" :is="componentMap.get(componentId)"
+                @closed="closeComponent" v-if="componentId" />
+        </template>
+    </app-table>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, onMounted, onUnmounted, defineAsyncComponent } from 'vue'
+import { useComponent } from '@/hooks/component'
+import { parsePercent, handleNumberValue, formatDecimal } from '@/filters'
+import { useFuturesStore, useGlobalStore } from '@/stores'
+import quoteSocket from '@/services/websocket/quote'
+import AppTable from '@pc/components/base/table/index.vue'
+
+const subscribe = quoteSocket.createSubscribe()
+const globalStore = useGlobalStore()
+const { setMarketId, onResponse, $toRefs } = useFuturesStore()
+const { marketGoodsList, selectedGoodsId } = $toRefs()
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => true, false)
+
+const componentMap = new Map<string, unknown>([
+    ['detail', defineAsyncComponent(() => import('@pc/components/modules/goods-detail/index.vue'))], // 详情
+])
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'goodscode', label: '商品代码' },
+    { prop: 'goodsname', label: '商品' },
+    { prop: 'ask', label: '卖价' },
+    { prop: 'bid', label: '买价' },
+    { prop: 'last', label: '最新价' },
+    { prop: 'rise', label: '涨跌' },
+    { prop: 'change', label: '幅度' },
+    { prop: 'opened', label: '今开' },
+    { prop: 'presettle', label: '昨结' },
+    { prop: 'lowest', label: '最低' },
+    { prop: 'highest', label: '最高' },
+    { prop: 'amplitude', label: '振幅' },
+])
+
+const onRowClick = (row: Model.GoodsQuote) => {
+    selectedGoodsId.value = row.goodsid
+    openComponent('detail')
+}
+
+onResponse(() => {
+    const goodsCodes = marketGoodsList.value.map((e) => e.goodscode)
+    subscribe.start(...goodsCodes)
+})
+
+onMounted(() => {
+    setMarketId(99201)
+    globalStore.showGoodsListing = false
+})
+
+onUnmounted(() => {
+    globalStore.showGoodsListing = false
+    subscribe.stop()
+})
+
+</script>

+ 94 - 0
src/packages/pc/views/market/trade/swap/detail/index.vue

@@ -0,0 +1,94 @@
+<!-- 掉期市场-商品详情 -->
+<template>
+    <teleport to="#appPageTeleport">
+        <app-view class="g-view-detail">
+            <template #header>
+                <div class="g-view-detail__header">
+                    <div class="iconbar">
+                        <el-button type="primary" icon="ArrowLeftBold" @click="emit('closed')" link />
+                    </div>
+                    <ul class="breadcrumb" v-if="selectedRow">
+                        <li>
+                            <span>{{ selectedRow?.goodscode }}</span>
+                            <span>{{ selectedRow?.goodsname }}</span>
+                        </li>
+                    </ul>
+                    <ul class="datainfo" v-if="selectedRow">
+                        <li>
+                            <span>卖量:</span>
+                            <span>{{ selectedRow?.sellqty }}</span>
+                        </li>
+                        <li>
+                            <span>买量:</span>
+                            <span>{{ selectedRow?.buyqty }}</span>
+                        </li>
+                    </ul>
+                    <div class="buttonbar">
+                        <template v-if="market">
+                            <span style="color: #3a87f7;padding:0 10px;">
+                                市场状态:{{ [2, 6].includes(market.runstatus) ? getRunStatusName(market.runstatus) : '未开市' }}
+                            </span>
+                        </template>
+                        <slot name="headerRight"></slot> 
+                        <el-button type="primary" @click="active = false" v-if="active">买卖大厅</el-button>
+                        <el-button type="primary" @click="active = true" v-else>图表</el-button>
+                        <el-button type="primary" @click="openComponent('listing')">挂牌求购</el-button>
+                    </div>
+                </div>
+            </template>
+            <Chart class="g-view-detail__container" v-bind="{ goodsCode }" v-if="active" />
+            <Order class="g-view-detail__container" v-bind="{ goodsId }" v-else />
+            <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)"
+                    @closed="closeComponent" v-if="componentId" />
+        </app-view>
+    </teleport>
+</template>
+
+<script lang="ts" setup>
+import { onUnmounted, shallowRef, computed, defineAsyncComponent, PropType } from 'vue'
+import { timerTask } from '@/utils/timer'
+import { useComponent } from '@/hooks/component'
+import { getRunStatusName } from '@/constants/market'
+import { useRequest } from '@/hooks/request'
+import { queryMarketRun } from '@/services/api/market'
+import { useFuturesStore } from '@/stores'
+
+const Chart = defineAsyncComponent(() => import('@pc/components/modules/goods-detail/chart/index.vue'))
+const Order = defineAsyncComponent(() => import('./order/index.vue'))
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
+const componentMap = new Map<string, unknown>([
+    ['listing', defineAsyncComponent(() => import('./listing/index.vue'))], // 挂牌
+])
+
+const props = defineProps({
+    goodsId: {
+        type: Number,
+        required: true
+    },
+    selectedRow: {
+        type: Object as PropType<Model.QuoteGoodsListRsp>,
+        required: false
+    }
+})
+
+const emit = defineEmits(['closed'])
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getGoodsQuote(props.goodsId)
+const active = shallowRef(true)
+const goodsCode = computed(() => quote.value?.goodscode ?? '')
+const goodsId = computed(() => props.selectedRow?.goodsid ?? 0)
+
+const { data: market, run } = useRequest(queryMarketRun, {
+    params: {
+        marketID: quote.value?.marketid
+    },
+    onSuccess: (res) => {
+        market.value = res.data[0]
+        // 每1分钟轮询刷新
+        timerTask.setTimeout(() => run(), 60 * 1000, 'getMarketRun')
+    }
+})
+
+onUnmounted(() => timerTask.clearTimeout('getMarketRun'))
+</script>

+ 164 - 0
src/packages/pc/views/market/trade/swap/detail/listing/index.vue

@@ -0,0 +1,164 @@
+<!-- 掉期市场-买卖大厅-挂牌 -->
+<template>
+    <app-drawer title="挂牌" v-model:show="show" :width="800" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="100px" :model="formData" :rules="formRules">
+            <el-form-item label="方向" prop="BuyOrSell">
+                <el-radio-group v-model="formData.BuyOrSell">
+                    <el-radio :label="0">买挂牌</el-radio>
+                    <el-radio :label="1">卖挂牌</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item label="价格类型" prop="PriceMove">
+                <el-radio-group v-model="priceMove">
+                    <el-radio :label="2">固定价</el-radio>
+                    <el-radio v-if="userStore.userType != 5" :label="3">浮动价</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item v-if="priceMove === 2" prop="OrderPrice" label="挂牌价格">
+                <el-input-number placeholder="请输入" :min="0" :precision="2" v-model="formData.OrderPrice" />
+            </el-form-item>
+            <el-form-item v-if="priceMove === 3" prop="MarketMaxSub" label="点差">
+                <el-input-number placeholder="请输入" :min="0" :precision="2" v-model="formData.MarketMaxSub" />
+            </el-form-item>
+            <el-form-item prop="OrderQty" label="挂牌数量">
+                <div class="g-qty-group">
+                    <el-input-number placeholder="请输入" :min="0" :precision="0" :step="qtyStep"
+                        v-model="formData.OrderQty" />
+                    <el-radio-group size="small" v-model="qtyStep" @change="onRadioChange">
+                        <el-radio v-for="(value, index) in qtyStepList" :key="index" :label="value" border />
+                    </el-radio-group>
+                </div>
+            </el-form-item> 
+            <el-form-item label="交易账号">
+                <span>{{ accountStore.currentAccountId }}</span>
+            </el-form-item>
+            <el-form-item label="参考价格" v-if="priceMove === 2">
+                <span :class="quote?.lastColor">{{ quote?.last ?? 0.0 }}</span>
+            </el-form-item>
+            <el-form-item label="估算价格" v-if="priceMove === 3">
+                <span>0.0</span>
+            </el-form-item>
+            <el-form-item label="挂牌金额" v-if="priceMove === 2">
+                <span>{{ amount.toFixed(2) }}</span>
+            </el-form-item>
+            <el-form-item label="估算金额" v-if="priceMove === 3">
+                <span>{{ amount.toFixed(2) }}</span>
+            </el-form-item>
+            <el-form-item label="履约保证金">
+                <span>{{ permargin.toFixed(2) }}</span>
+            </el-form-item>
+            <el-form-item label="可用资金">
+                <span>{{ accountStore.currentAccount.avaiableMoney?.toFixed(2) }}</span>
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)">取消</el-button>
+            <el-button type="danger" @click="onSubmit()">确定</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, PropType } from 'vue'
+import { ElMessage, FormInstance, FormRules, ElMessageBox } from 'element-plus'
+import { EBuildType, EDelistingType, EListingSelectType, EOrderOperateType, EPriceMode, EValidType } from '@/constants/client'
+import { useOrder } from '@/business/trade'
+import { useAccountStore, useFuturesStore, useUserStore } from '@/stores'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.QuoteGoodsListRsp>,
+        required: true
+    }
+})
+
+const accountStore = useAccountStore()
+const userStore = useUserStore()
+const futuresStore = useFuturesStore()
+
+/// 价格类型
+const priceMove = shallowRef(EPriceMode.PRICEMODE_LIMIT)
+/// 挂牌金额
+const amount = shallowRef(0.0)
+/// 履约保证金
+const permargin = shallowRef(0.0)
+
+const { formData, formSubmit, loading } = useOrder()
+const show = shallowRef(true)
+const refresh = shallowRef(false)
+const formRef = shallowRef<FormInstance>()
+const qtyStepList = [1, 5, 10, 20, 30, 50] // 数量步长列表
+const qtyStep = shallowRef(qtyStepList[0]) // 数量步长
+
+// 商品盘面
+const quote = futuresStore.getGoodsQuote(props.selectedRow.refgoodsid)
+
+const formRules: FormRules = {
+    OrderPrice: [{
+        required: true,
+        validator: (rule, value, callback) => {
+            if (value) {
+                callback()
+            } else {
+                callback(new Error('请输入挂牌价格'))
+            }
+        }
+    }],
+    OrderQty: [{
+        required: true,
+        validator: (rule, value, callback) => {
+            if (value) {
+                callback()
+            } else {
+                callback(new Error('请输入挂牌数量'))
+            }
+        }
+    }],
+}
+
+const onRadioChange = (value: number) => {
+    formData.OrderQty = value
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            ElMessageBox.confirm(
+                '*若存在价格匹配的反方向委托订单,系统将会自动撤销。',
+                '是否立即挂牌?'
+            ).then(() => {
+                /// 买卖方向
+                const { goodsid } = props.selectedRow?? {}
+                /// 获取对应的市场ID
+                formData.MarketID = futuresStore.getGoodsMarket(goodsid)
+                formData.OrderPrice = priceMove.value === EPriceMode.PRICEMODE_LIMIT ? formData.OrderPrice : 0.0
+                formData.PriceMode = priceMove.value
+                formData.MarketMaxSub = priceMove.value === EPriceMode.PRICEMODE_LIMIT ? 0.0 : formData.MarketMaxSub
+                formData.GoodsID = goodsid
+                formData.PriceMode = priceMove.value
+                formData.ListingSelectType = EListingSelectType.LISTINGSELECTTYPE_LISTING
+                formData.DelistingType = EDelistingType.DELISTINGTYPE_SELECTED
+                formData.BuildType = EBuildType.BUILDTYPE_OPEN
+                formData.TimevalidType = EValidType.VALIDTYPE_DR
+                formData.OperateType = EOrderOperateType.ORDEROPERATETYPE_NORMAL
+                formData.TriggerType = 0
+                formData.ServiceTime = ""
+                formData.ValidTime = ""
+
+                formSubmit().then(() => {
+                    ElMessage.success('提交成功')
+                    onCancel(true)
+                }).catch((err) => {
+                    ElMessage.error('提交失败:' + err)
+                })
+            })
+        }
+    })
+}
+</script>

+ 162 - 0
src/packages/pc/views/market/trade/swap/detail/order/delisting/index.vue

@@ -0,0 +1,162 @@
+<!-- 掉期市场 - 买卖大厅 - 摘牌 -->
+<template>
+    <app-drawer title="摘牌" v-model:show="show" :width="800" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="100px" :model="formData" :rules="formRules">
+            <el-form-item label="交易账户">
+                <span>{{ accountStore.currentAccountId }}</span>
+            </el-form-item>
+            <el-form-item label="挂牌方">
+                <span>{{ selectedRow.username }}</span>
+            </el-form-item>
+            <el-form-item label="价格类型" prop="PriceMove">
+                <el-radio-group v-model="priceMove">
+                    <el-radio :label="2">固定价</el-radio>
+                    <el-radio v-if="userStore.userType != 5" :label="3">浮动价</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item label="当前价格" v-if="priceMove === 2">
+                <span>{{ formatDecimal(selectedRow.orderprice) }}</span>
+            </el-form-item>
+            <el-form-item label="浮动价格" v-if="priceMove === 3">
+                <span>{{ '******' }}</span>
+            </el-form-item>
+            <el-form-item v-if="priceMove === 2" prop="OrderPrice" label="摘牌价格">
+                <el-input-number placeholder="请输入" :min="0" :precision="2" v-model="formData.OrderPrice" />
+            </el-form-item>
+            <el-form-item v-if="priceMove === 3" prop="MarketMaxSub" label="点差">
+                <el-input-number placeholder="请输入" :min="0" :precision="2" v-model="formData.MarketMaxSub" />
+            </el-form-item>
+            <el-form-item prop="OrderQty" label="摘牌数量">
+                <div class="g-qty-group">
+                    <el-input-number placeholder="请输入" :min="0" :max="selectedRow.orderqty" :precision="0" :step="qtyStep"
+                        v-model="formData.OrderQty" />
+                    <el-radio-group size="small" v-model="qtyStep" @change="onRadioChange">
+                        <el-radio v-for="(value, index) in qtyStepList" :key="index" :label="value" border />
+                    </el-radio-group>
+                </div>
+            </el-form-item> 
+            <el-form-item label="可摘数量">
+                <span>{{ selectedRow.orderqty }}</span>
+            </el-form-item>
+            <el-form-item label="参考价格" v-if="priceMove === 2">
+                <span :class="quote?.lastColor">{{ quote?.last ?? 0.0 }}</span>
+            </el-form-item>
+            <el-form-item label="估算价格" v-if="priceMove === 3">
+                <span>0.0</span>
+            </el-form-item>
+            <el-form-item label="挂牌金额" v-if="priceMove === 2">
+                <span>{{ amount.toFixed(2) }}</span>
+            </el-form-item>
+            <el-form-item label="估算金额" v-if="priceMove === 3">
+                <span>{{ amount.toFixed(2) }}</span>
+            </el-form-item>
+            <el-form-item label="履约保证金">
+                <span>{{ permargin.toFixed(2) }}</span>
+            </el-form-item>
+            <el-form-item label="可用资金">
+                <span>{{ accountStore.currentAccount.avaiableMoney?.toFixed(2) }}</span>
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)">取消</el-button>
+            <el-button type="danger" :disabled="!formData.OrderQty || formData.OrderQty > selectedRow.orderqty"
+                        @click="onSubmit(EBuildType.BUILDTYPE_OPEN)">{{ selectedRow.buyorsell === 0 ? '卖出' : '买入' }}</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, PropType, computed } from 'vue'
+import { ElMessage, FormInstance, FormRules, ElMessageBox } from 'element-plus'
+import { formatDecimal, handleRequestBigNumber } from '@/filters'
+import { EBuildType, EDelistingType, EListingSelectType, EOrderOperateType, EPriceMode, EValidType } from '@/constants/client'
+import { BuyOrSell } from '@/constants/order'
+import { useOrder } from '@/business/trade'
+import { usePosition } from '@/business/position'
+import { useFuturesStore, useAccountStore, useUserStore } from '@/stores'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.TjmdTradeOrderDetailRsp>,
+        required: true
+    }
+})
+
+const accountStore = useAccountStore()
+const userStore = useUserStore()
+
+/// 价格类型
+const priceMove = shallowRef(EPriceMode.PRICEMODE_LIMIT)
+/// 挂牌金额
+const amount = shallowRef(0.0)
+/// 履约保证金
+const permargin = shallowRef(0.0)
+
+const { formData, formSubmit, loading } = useOrder()
+const futuresStore = useFuturesStore()
+const show = shallowRef(true)
+const refresh = shallowRef(false)
+const formRef = shallowRef<FormInstance>()
+const qtyStepList = [1, 5, 10, 20, 30, 50] // 数量步长列表
+const qtyStep = shallowRef(qtyStepList[0]) // 数量步长
+
+// 商品盘面
+const quote = futuresStore.getGoodsQuote(props.selectedRow.goodsid)
+
+const formRules: FormRules = {
+    OrderQty: [{
+        required: true,
+        validator: (rule, value, callback) => {
+            if (value) {
+                callback()
+            } else {
+                callback(new Error('请输入摘牌数量'))
+            }
+        }
+    }],
+}
+
+const onRadioChange = (value: number) => {
+    formData.OrderQty = value
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = (buildType: number) => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            ElMessageBox.confirm(
+                '*若存在价格匹配的反方向委托订单,系统将会自动撤销。',
+                '是否立即摘牌?'
+            ).then(() => {
+                const { goodsid, orderid, orderprice, buyorsell } = props.selectedRow
+                const { marketid = 0 } = quote.value ?? {}
+
+                /// 获取对应的市场ID
+                formData.MarketID = marketid
+                formData.PriceMode = EPriceMode.PRICEMODE_LIMIT
+                formData.RelatedID = handleRequestBigNumber(orderid)
+                formData.OrderPrice = orderprice
+                formData.BuyOrSell = buyorsell === BuyOrSell.Buy ? BuyOrSell.Sell : BuyOrSell.Buy
+                formData.GoodsID = goodsid
+                formData.ListingSelectType = EListingSelectType.LISTINGSELECTTYPE_DELISTING
+                formData.DelistingType = EDelistingType.DELISTINGTYPE_SELECTED
+                formData.BuildType = buildType
+                formData.TimevalidType = EValidType.VALIDTYPE_DR
+                formData.OperateType = EOrderOperateType.ORDEROPERATETYPE_NORMAL
+
+                formSubmit().then(() => {
+                    ElMessage.success('提交成功')
+                    onCancel(true)
+                }).catch((err) => {
+                    ElMessage.error('提交失败:' + err)
+                })
+            })
+        }
+    })
+}
+</script>

+ 10 - 0
src/packages/pc/views/market/trade/swap/detail/order/index.less

@@ -0,0 +1,10 @@
+.app-goods-detail-order {
+    display: flex;
+    height: 100%;
+    overflow: hidden;
+    background-color: #181e22;
+
+    .app-table {
+        flex: 1;
+    }
+}

+ 99 - 0
src/packages/pc/views/market/trade/swap/detail/order/index.vue

@@ -0,0 +1,99 @@
+<!-- 掉期市场 - 商品详情 - 买卖大厅 -->
+<template>
+    <div class="app-goods-detail-order">
+        <app-table class="sell" :data="sellList" v-model:columns="sellColumns" :row-key="rowKey"
+            :expand-row-keys="expandKeys" @row-click="rowClick" showIndex>
+            <!-- 展开行 -->
+            <template #expand="{ row, index }">
+                <div class="buttonbar" v-if="index === 0">
+                    <el-button type="primary" size="small" :disabled="row.userid === loginStore.userId"
+                        @click="showComponent('delisting', row)">摘牌</el-button>
+                </div>
+            </template>
+        </app-table>
+        <app-table class="buy" :data="buyList" v-model:columns="buyColumns" :row-key="rowKey" :expand-row-keys="expandKeys"
+            @row-click="rowClick" showIndex>
+            <!-- 展开行 -->
+            <template #expand="{ row, index }">
+                <div class="buttonbar" v-if="index === 0">
+                    <el-button type="primary" size="small" :disabled="row.userid === loginStore.userId"
+                        @click="showComponent('delisting', row)">摘牌</el-button>
+                </div>
+            </template>
+        </app-table>
+        <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)" @closed="closeComponent"
+            v-if="componentId" />
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, defineAsyncComponent, watch } from 'vue'
+import { useRequest } from '@/hooks/request'
+import { BuyOrSell } from '@/constants/order'
+import { useComponent } from '@/hooks/component'
+import { useComposeTable } from '@pc/components/base/table'
+import { queryTjmdTradeOrderDetail } from '@/services/api/swap'
+import { useLoginStore, useFuturesStore } from '@/stores'
+import AppTable from '@pc/components/base/table/index.vue'
+
+const props = defineProps({
+    goodsId: {
+        type: Number,
+        required: true
+    },
+})
+
+const componentMap = new Map<string, unknown>([
+    ['delisting', defineAsyncComponent(() => import('./delisting/index.vue'))], // 摘牌
+])
+
+const loginStore = useLoginStore()
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getGoodsQuote(props.goodsId)
+
+const { rowKey, expandKeys, selectedRow, rowClick } = useComposeTable<Model.TjmdTradeOrderDetailRsp>({ rowKey: 'orderid' })
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
+
+const { dataList: buyList, run: getBuyList } = useRequest(queryTjmdTradeOrderDetail, {
+    params: {
+        pagesize: 20,
+        goodsid: props.goodsId,
+        buyorsell: BuyOrSell.Buy
+    },
+})
+
+const { dataList: sellList, run: getSellList } = useRequest(queryTjmdTradeOrderDetail, {
+    params: {
+        pagesize: 20,
+        goodsid: props.goodsId,
+        buyorsell: BuyOrSell.Sell
+    },
+})
+
+const buyColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'orderprice', label: '买价' },
+    { prop: 'orderqty', label: '买量' },
+    { prop: 'username', label: '购买方' },
+])
+
+const sellColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'orderprice', label: '卖价' },
+    { prop: 'orderqty', label: '卖量' },
+    { prop: 'username', label: '销售方' },
+])
+
+const showComponent = (componentName: string, row: Model.TjmdTradeOrderDetailRsp) => {
+    selectedRow.value = row
+    openComponent(componentName)
+}
+
+// 监听行情变动
+watch(() => quote.value, () => {
+    getBuyList()
+    getSellList()
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 123 - 0
src/packages/pc/views/market/trade/swap/index.vue

@@ -0,0 +1,123 @@
+<!-- 交易市场 - 掉期市场 -->
+<template>
+    <app-table :data="tableList" v-model:columns="tableColumns" @row-click="onRowClick" showIndex>
+        <!-- 当前价 -->
+        <template #last="{ row }">
+            <span :class="row.lastColor">{{ handleNumberValue(formatDecimal(row.last, row.decimalplace)) }}</span>
+        </template>
+        <!-- 涨跌 -->
+        <template #rise="{ row }">
+            <span :class="row.lastColor">{{ formatDecimal(row.rise, row.decimalplace) }}</span>
+        </template>
+        <!-- 涨跌幅 -->
+        <template #change="{ row }">
+            <span :class="row.lastColor">{{ row.change }}</span>
+        </template>
+        <!-- 开盘 -->
+        <template #opened="{ row }">
+            <span :class="row.openedColor">{{ handleNumberValue(formatDecimal(row.opened, row.decimalplace)) }}</span>
+        </template>
+        <!-- 最低 -->
+        <template #lowest="{ row }">
+            <span :class="row.lowestColor">{{ handleNumberValue(formatDecimal(row.lowest, row.decimalplace)) }}</span>
+        </template>
+        <!-- 最高 -->
+        <template #highest="{ row }">
+            <span :class="row.highestColor">{{ handleNumberValue(formatDecimal(row.highest, row.decimalplace)) }}</span>
+        </template>
+        <template #footer>
+            <component ref="componentRef" v-bind="{ goodsId: selectedGoodsId, selectedRow }" :is="componentMap.get(componentId)"
+                @closed="closeComponent" v-if="componentId" />
+        </template>
+    </app-table>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, onUnmounted, defineAsyncComponent, computed } from 'vue'
+import { useComponent } from '@/hooks/component'
+import { parsePercent, handleNumberValue, formatDecimal } from '@/filters'
+import { useFuturesStore, useGlobalStore, useUserStore } from '@/stores'
+import { useRequest } from '@/hooks/request'
+import { queryQuoteGoodsList } from '@/services/api/swap'
+import quoteSocket from '@/services/websocket/quote'
+import AppTable from '@pc/components/base/table/index.vue'
+
+const subscribe = quoteSocket.createSubscribe()
+const globalStore = useGlobalStore()
+const { onResponse } = useFuturesStore()
+const futuresStore = useFuturesStore()
+const userStore = useUserStore()
+const selectedRow = shallowRef<Model.QuoteGoodsListRsp>()
+const { $toRefs } = useFuturesStore()
+const { selectedGoodsId } = $toRefs()
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => true, false)
+
+const componentMap = new Map<string, unknown>([
+    ['detail', defineAsyncComponent(() => import('./detail/index.vue'))], // 详情
+])
+
+const { dataList } = useRequest(queryQuoteGoodsList, {
+    params: {
+        usertype: userStore.userType ?? 0,
+        marketids: userStore.getMarketId('TRADEMODE_TJMD').toString()
+    },
+    onSuccess: (res) => {
+        const goodsCodes = res.data.map((e) => e.refgoodscode)
+        subscribe.start(...goodsCodes)
+    }
+})
+
+const tableList = computed(() => {
+    return dataList.value.map((item) => {
+        const quote = futuresStore.getGoodsQuote(item.refgoodscode)
+        const { goodsname, lastColor, openedColor, lowestColor, highestColor, last, presettle, rise, change, amplitude, highest, lowest, opened } = quote.value ?? {}
+        return {
+            ...item,
+            goodsname,
+            lastColor,
+            openedColor,
+            lowestColor,
+            highestColor,
+            last: handleNumberValue(last),
+            rise: handleNumberValue(rise?.toFixed(item.decimalplace)),
+            change: parsePercent(change),
+            opened: handleNumberValue(opened),
+            presettle: handleNumberValue(presettle),
+            lowest: handleNumberValue(lowest),
+            highest: handleNumberValue(highest),
+            amplitude: parsePercent(amplitude),
+        }
+    })
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'goodscode', label: '商品代码' },
+    { prop: 'goodsname', label: '商品' },
+    { prop: 'last', label: '最新价' },
+    { prop: 'rise', label: '涨跌' },
+    { prop: 'change', label: '涨跌幅' },
+    { prop: 'opened', label: '今开' },
+    { prop: 'presettle', label: '昨结' },
+    { prop: 'lowest', label: '最低' },
+    { prop: 'highest', label: '最高' },
+    { prop: 'amplitude', label: '振幅' },
+])
+
+const onRowClick = (row: Model.QuoteGoodsListRsp) => {
+    selectedGoodsId.value = row.refgoodsid
+    selectedRow.value = row
+    openComponent('detail')
+}
+
+onResponse(() => {
+    const goodsCodes = dataList.value.map((e) => e.refgoodscode)
+    subscribe.start(...goodsCodes)
+})
+
+onUnmounted(() => {
+    globalStore.showGoodsListing = false
+    subscribe.stop()
+})
+
+</script>

+ 1 - 1
src/packages/pc/views/query/trade/swap/history/index.vue

@@ -66,7 +66,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'tradeqty', label: '成交数量' },
     { prop: 'tradeamount', label: '成交金额 ' },
     { prop: 'closepl', label: '平仓盈亏' },
-    { prop: 'tradetime', label: '时间' },
+    { prop: 'tradetime', label: '成交时间' },
 ])
 
 filterOptons.buttonList = [

+ 2 - 2
src/packages/pc/views/query/trade/swap/list/index.vue

@@ -17,7 +17,7 @@
         <template #tradeamount="{ row }">
             {{ formatDecimal(row.tradeqty*row.tradeprice) }}
         </template>
-        <!-- 时间 -->
+        <!-- 成交时间 -->
         <template #tradetime="{ value }">
             {{ formatDate(value) }}
         </template>
@@ -48,6 +48,6 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'tradeqty', label: '成交数量' },
     { prop: 'tradeamount', label: '成交金额 ' },
     { prop: 'closepl', label: '平仓盈亏' },
-    { prop: 'tradetime', label: '时间' },
+    { prop: 'tradetime', label: '成交时间' },
 ])
 </script>