Prechádzať zdrojové kódy

按单平仓支持市价限价

Handy_Cao 1 rok pred
rodič
commit
362a4454d3

+ 1 - 1
public/locales/en-US.json

@@ -1352,7 +1352,7 @@
             "tips2": "Please enter SMS verification code",
             "tips3": "Please enter new password",
             "tips4": "Please enter confirmation password",
-            "tips5": "The password must be any combination of two characters with a minimum length of 6 characters",
+            "tips5": "Combination of two characters with a minimum length of 6 bits",
             "tips6": "The new password is inconsistent with the confirmation password",
             "tips7": "Failure to send",
             "tips8": "Password reset successfully, please log in again."

+ 2 - 0
src/constants/client.ts

@@ -1071,6 +1071,8 @@ export enum EOrderOperateType {
     ORDEROPERATETYPE_ONTIME_MEMBER_CLOSE = 22,
     /// 融资买入
     ORDEROPERATETYPE_BUYBYFINANCE = 23,
+    /// 按单平仓
+    ORDEROPERATETYPE_HOLDER_CLOSE = 24,
 }
 
 /// 业务类型

+ 4 - 16
src/packages/mobile/views/pricing/list/Index.vue

@@ -45,9 +45,7 @@
 import { computed, onUnmounted, PropType } from 'vue'
 import { parsePercent, handleNumberValue, formatDecimal } from '@/filters'
 import { useNavigation } from '@mobile/router/navigation'
-import { useRequest } from '@/hooks/request'
-import { queryQuoteGoodsList } from '@/services/api/swap'
-import { useFuturesStore, useUserStore, i18n } from '@/stores'
+import { useFuturesStore, i18n } from '@/stores'
 import quoteSocket from '@/services/websocket/quote'
 import AppList from '@mobile/components/base/list/index.vue'
 import { BuyOrSell, BuildType } from '@/constants/order'
@@ -65,25 +63,15 @@ const props = defineProps({
 const { global: { t } } = i18n
 const { router, getQueryString } = useNavigation()
 const futuresStore = useFuturesStore()
-const userStore = useUserStore()
 const subscribe = quoteSocket.createSubscribe()
 
+const { quoteGoodsList: dataList } = futuresStore
+
 const title = getQueryString('title')
 const titleName = computed(() => title ? decodeURIComponent(title) : props.marketSection?.displayname ?? '订单点价')
 
-const { dataList } = useRequest(queryQuoteGoodsList, {
-    params: {
-        usertype: userStore.userType ?? 0,
-        marketids: '10101'
-    },
-    onSuccess: (res) => {
-        const goodsCodes = res.data.map((e) => e.goodscode)
-        subscribe.start(...goodsCodes)
-    }
-})
-
 const tableList = computed(() => {
-    return dataList.value.map((item) => {
+    return dataList.map((item) => {
         const quote = futuresStore.getGoodsQuote(item.goodscode)
         const { lastColor, openedColor, lowestColor, highestColor, last = 0, presettle = 0, rise = 0, change, amplitude, highest = 0, lowest = 0, opened = 0, ask = 0, bid = 0, bidColor, askColor, decimalplace } = quote.value ?? {}
         return {

+ 1 - 1
src/packages/pc/views/footer/presell/transferposition/index.vue

@@ -29,7 +29,7 @@
                     @click="showComponent('append', row)">{{ t('operation.deposit2') }}</el-button>
                 <el-button type="danger" v-if="row.paystatus === 2" size="small"
                     @click="showComponent('listing', row)">{{ t('operation.transfer') }}</el-button>
-                <el-button type="primary" v-if="row.paystatus === 2" size="small"
+                <el-button type="primary" v-if="row.paystatus === 2 && row.accountid != row.provideraccountid" size="small"
                     @click="showComponent('delivery', row)">{{ t('operation.delivery') }}</el-button>
             </div>
         </template>

+ 85 - 19
src/packages/pc/views/footer/pricing/detail/components/transfer/index.vue

@@ -1,6 +1,6 @@
-<!-- 挂牌点价-持仓明细-转让 -->
+<!-- 挂牌点价-持仓明细-平仓 -->
 <template>
-    <app-drawer :title="t('operation.transfer')" :width="800" v-model:show="show" :loading="loading" :refresh="refresh">
+    <app-drawer :title="t('operation.close')" :width="800" v-model:show="show" :loading="loading" :refresh="refresh">
         <el-form ref="formRef" class="el-form--horizontal" label-width="120px" :model="formData" :rules="formRules">
             <el-form-item :label="t('position.transfer.goodsname')">
                 <span>{{ selectedRow.goodscode }}/{{ selectedRow.goodsname }}</span>
@@ -17,16 +17,26 @@
             <el-form-item :label="t('position.transfer.holderprice')">
                 <span>{{ formatDecimal(selectedRow.holderprice, selectedRow.decimalplace) }}</span>
             </el-form-item>
-            <el-form-item :label="t('position.transfer.freezeqty')">
+            <el-form-item :label="t('position.goods.freezeqty')">
                 <span>{{ selectedRow.freezeqty }}</span>
             </el-form-item>
+            <el-form-item :label="t('position.transfer.enableqty')">
+                <span>{{ maxQty }}</span>
+            </el-form-item>
             <el-form-item :label="t('position.transfer.closepl')">
                 <span :class="handlePriceColor(closepl)">{{ formatDecimal(closepl, selectedRow.decimalplace) }}</span>
             </el-form-item>
-            <el-form-item :label="t('position.transfer.enableqty')">
-                <span>{{ maxQty }}</span>
+            <el-form-item prop="PriceMode" :label="t('quote.pricing.pricemode')">
+                <el-radio-group v-model="formData.PriceMode">
+                    <el-radio v-for="(item, index) in getPricemode2List()" :key="index" :label="item.value">
+                        {{ item.label }}
+                    </el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item prop="MarketMaxSub" :label="t('quote.pricing.marketmaxsub1')" v-if="formData.PriceMode === PriceMode.Market">
+                <el-input-number ref="priceRef" :placeholder="t('common.pleaseenter')" :min="0" :max="999" v-model="formData.MarketMaxSub" />
             </el-form-item>
-            <el-form-item prop="OrderPrice" :label="t('position.transfer.transferprice')">
+            <el-form-item prop="OrderPrice" :label="t('position.transfer.transferprice')" v-if="formData.PriceMode === PriceMode.Limit">
                 <el-input-number :placeholder="t('position.transfer.tips3')" v-model="formData.OrderPrice" :step="quote?.decimalvalue"
                     :precision="quote?.decimalplace" />
             </el-form-item>
@@ -34,6 +44,12 @@
                 <div class="g-qty-group">
                     <el-input-number :placeholder="t('position.transfer.tips4')" v-model="formData.OrderQty" :precision="0" :max="maxQty"
                         :min="0" />
+                    <el-radio-group size="small" v-model="qtyStep" @change="onRadioChange">
+                        <el-radio v-for="(value, index) in qtyStepList" :key="index" :label="value" border
+                            style="width: 25%;">
+                            {{ parsePercent(value, 0) }}
+                        </el-radio>
+                    </el-radio-group>
                 </div>
             </el-form-item>
         </el-form>
@@ -48,10 +64,10 @@
 import { ref, PropType, computed, onMounted } from 'vue'
 import { ElMessage, FormInstance, FormRules } from 'element-plus'
 import { useOrder } from '@/business/trade'
-import { formatDecimal, handlePriceColor, handleRequestBigNumber } from '@/filters'
-import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
-import { useFuturesStore, usePositionStore, i18n } from '@/stores'
-import { EBuildType, EDelistingType, EListingSelectType, EPriceMode, EValidType } from '@/constants/client'
+import { formatDecimal, handlePriceColor, handleRequestBigNumber, parsePercent } from '@/filters'
+import { getBuyOrSellName, BuyOrSell, getPricemode2List, PriceMode } from '@/constants/order'
+import { useFuturesStore, usePositionStore, i18n, useSettingStore } from '@/stores'
+import { EBuildType, EDelistingType, EListingSelectType, EValidType, EOrderOperateType } from '@/constants/client'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 
 const props = defineProps({
@@ -63,8 +79,17 @@ const props = defineProps({
 
 const futuresStore = useFuturesStore()
 const positionStore = usePositionStore()
+const settingStore = useSettingStore()
+// 价格类型
+const orderPriceType = computed(() => settingStore.getSettingValue('orderPriceType'))
 const quote = futuresStore.getGoodsQuote(props.selectedRow.goodscode)
 const { t } = i18n.global
+const qtyStepList = [0.25, 0.5, 0.75, 1] // 数量步长列表
+const qtyStep = ref(1) // 数量步长
+const { formSubmit, formData, loading } = useOrder()
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
 
 // 可用数量
 const maxQty = computed(() => {
@@ -84,11 +109,6 @@ const closepl = computed(() => {
     return price ? (marketValue - holderamount) * (buyorsell === BuyOrSell.Buy ? 1 : -1) : 0
 })
 
-const { formSubmit, formData, loading } = useOrder()
-const show = ref(true)
-const refresh = ref(false)
-const formRef = ref<FormInstance>()
-
 const formRules: FormRules = {
     OrderPrice: [{
         message: t('position.transfer.tips3'),
@@ -96,6 +116,16 @@ const formRules: FormRules = {
             return !!formData.OrderPrice
         }
     }],
+    MarketMaxSub: [{
+        type: 'number',
+        validator: (rule, value, callback) => {
+            if (value) {
+                callback()
+            } else {
+                callback(new Error(t('quote.pricing.tips3')))
+            }
+        }
+    }],
     OrderQty: [{
         message: t('position.transfer.tips4'),
         validator: () => {
@@ -109,6 +139,31 @@ const onCancel = (isRefresh = false) => {
     refresh.value = isRefresh
 }
 
+const getOrderPrice = () => {
+    const { last, bid, ask, presettle = 0 } = quote.value ?? {}
+    const price = last || presettle
+    // 1=现价,2=对手价,3=实时价,4=实时对手价
+    switch (orderPriceType.value) {
+        case 1:
+        case 3:
+            return price
+        case 2:
+        case 4:
+            if (formData.BuyOrSell === BuyOrSell.Buy) {
+                return ask || price
+            } else {
+                return bid || price
+            }
+        default:
+            return 0
+    }
+}
+
+const marketPrice = computed(() => {
+    const { ask = 0, bid = 0 } = quote.value ?? {}
+    return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
+})
+
 const onCloseSumit = () => {
     formRef.value?.validate((valid) => {
         if (valid) {
@@ -116,26 +171,34 @@ const onCloseSumit = () => {
             /// 市场ID
             formData.Header = { MarketID: marketid, GoodsID: goodsid }
             formData.MarketID = marketid
-            formData.PriceMode = EPriceMode.PRICEMODE_LIMIT
+            if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
             formData.BuyOrSell = buyorsell === BuyOrSell.Buy ? BuyOrSell.Sell : BuyOrSell.Buy
             formData.GoodsID = goodsid
             formData.ListingSelectType = EListingSelectType.LISTINGSELECTTYPE_DELISTINGTHENLISTING
             formData.DelistingType = EDelistingType.DELISTINGTYPE_PRICE
             formData.BuildType = EBuildType.BUILDTYPE_CLOSE
             formData.TimevalidType = EValidType.VALIDTYPE_DR
-            formData.OperateType = 24
+            formData.OperateType = EOrderOperateType.ORDEROPERATETYPE_HOLDER_CLOSE
             formData.RelatedID = handleRequestBigNumber(tradeid)
 
             formSubmit().then(() => {
-                ElMessage.success(t('position.transfer.tips2'))
+                if (settingStore.getSettingValue('showOrderSuccessMessage')) {
+                    ElMessage.success(t('position.transfer.tips2'))
+                }
                 onCancel(true)
             }).catch((err) => {
-                ElMessage.error(t('common.submitfailure') + err)
+                if (settingStore.getSettingValue('showOrderFailMessage')) {
+                    ElMessage.error(t('common.submitfailure') + err)
+                }
             })
         }
     })
 }
 
+const onRadioChange = (value: number) => {
+    formData.OrderQty = Math.trunc(maxQty.value * value) || 1
+}
+
 onMounted(() => {
     const { bid, ask, presettle = 0 } = quote.value ?? {}
     switch (props.selectedRow.buyorsell) {
@@ -148,6 +211,9 @@ onMounted(() => {
         default:
             formData.OrderPrice = presettle
     }
+    formData.OrderPrice = getOrderPrice()
     formData.OrderQty = maxQty.value
+    formData.PriceMode = PriceMode.Market
+    formData.MarketMaxSub = 100.0
 })
 </script>

+ 1 - 1
src/packages/pc/views/footer/pricing/detail/index.vue

@@ -7,7 +7,7 @@
         </template>
         <!-- 操作 -->
         <template #operate="{ row }">
-            <el-button type="danger" size="small" @click="showComponent('transfer', row)">{{ t('operation.transfer') }}</el-button>
+            <el-button type="danger" size="small" @click="showComponent('transfer', row)">{{ t('operation.close') }}</el-button>
         </template>
         <template #append v-if="showLoadMore">
             <el-button size="small" plain @click="loadMore">{{ t('common.loadMore' )}}</el-button>

+ 3 - 3
src/packages/pc/views/footer/pricing/position/components/transfer/index.vue

@@ -1,6 +1,6 @@
-<!-- 挂牌点价-合约汇总-转让 -->
+<!-- 挂牌点价-合约汇总-平仓 -->
 <template>
-    <app-drawer :title="t('operation.transfer')" :width="800" v-model:show="show" :loading="loading" :refresh="refresh">
+    <app-drawer :title="t('operation.close')" :width="800" v-model:show="show" :loading="loading" :refresh="refresh">
         <el-form ref="formRef" class="el-form--horizontal" label-width="120px" :model="formData" :rules="formRules">
             <el-form-item :label="t('position.transfer.goodsname')">
                 <span>{{ position.goodscode }}/{{ position.goodsname }}</span>
@@ -159,7 +159,7 @@ const onCloseSumit = () => {
         if (valid) {
             const { marketid, goodsid, buyorsell } = props.position
             /// 市场ID
-            formData.Header = { GoodsID: goodsid }
+            formData.Header = { GoodsID: goodsid, MarketID: marketid }
             formData.MarketID = marketid
             if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
             formData.BuyOrSell = buyorsell === BuyOrSell.Buy ? BuyOrSell.Sell : BuyOrSell.Buy

+ 1 - 1
src/packages/pc/views/footer/pricing/position/index.vue

@@ -26,7 +26,7 @@
         </template>
         <!-- 操作 -->
         <template #operate="{ row }">
-            <el-button type="danger" size="small" @click="showComponent('transfer', row)">{{ t('operation.transfer') }}</el-button>
+            <el-button type="danger" size="small" @click="showComponent('transfer', row)">{{ t('operation.close') }}</el-button>
         </template>
         <template #footer>
             <component ref="componentRef" v-bind="{ position: selectedRow }" :is="componentMap.get(componentId)"

+ 3 - 18
src/packages/pc/views/market/trade/pricing/index.vue

@@ -44,15 +44,12 @@
 import { shallowRef, onMounted, onUnmounted, defineAsyncComponent, computed } from 'vue'
 import { useComponent } from '@/hooks/component'
 import { parsePercent, handleNumberValue, formatDecimal } from '@/filters'
-import { useFuturesStore, useUserStore, useGlobalStore } from '@/stores'
-import { useRequest } from '@/hooks/request'
-import { queryQuoteGoodsList } from '@/services/api/swap'
+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 futuresStore = useFuturesStore()
-const userStore = useUserStore()
 const globalStore = useGlobalStore()
 
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => true, false)
@@ -61,20 +58,10 @@ const componentMap = new Map<string, unknown>([
     ['detail', defineAsyncComponent(() => import('@pc/components/modules/goods-detail/index.vue'))], // 详情
 ])
 
-const { dataList, run } = useRequest(queryQuoteGoodsList, {
-    manual: true,
-    params: {
-        usertype: userStore.userType ?? 0,
-        marketids: '10101'
-    },
-    onSuccess: (res) => {
-        const goodsCodes = res.data.map((e) => e.goodscode)
-        subscribe.start(...goodsCodes)
-    }
-})
+const { quoteGoodsList: dataList } = futuresStore
 
 const tableList = computed(() => {
-    return dataList.value.map((item ) => {
+    return dataList.map((item ) => {
         const quote = futuresStore.getGoodsQuote(item.goodscode)
         const { lastColor, openedColor, lowestColor, highestColor, last = 0, presettle = 0, rise = 0, change, amplitude, highest = 0, lowest = 0, opened = 0, ask = 0, bid = 0, bidColor, askColor, decimalplace } = quote.value ?? {}
         return {
@@ -118,8 +105,6 @@ const onRowClick = (row: Model.GoodsQuote) => {
     openComponent('detail')
 }
 
-futuresStore.onDataCompleted(() => run())
-
 onMounted(() => {
     globalStore.showPricingListing = true
 })

+ 30 - 1
src/stores/modules/futures.ts

@@ -5,10 +5,12 @@ import { handlePriceColor } from '@/filters'
 import { queryMemberGoodsLimitConfig } from '@/services/api/common'
 import { getTodayAccountConfigInfo } from '@/services/api/account'
 import { queryErmcpGoods, queryQuoteDay } from '@/services/api/goods'
+import { queryQuoteGoodsList } from '@/services/api/swap'
 import { wordArrayToUint8Array } from '@/services/websocket/package/crypto'
 import { decodeProto } from '@/services/websocket/package/package50/proto'
 import { defineStore } from '../store'
 import { useAccountStore } from './account'
+import { useUserStore } from './user'
 import CryptoJS from 'crypto-js'
 import eventBus from '@/services/bus'
 import moment from 'moment'
@@ -33,6 +35,7 @@ export const useFuturesStore = defineStore(() => {
         goodsList: <(Model.GoodsRsp & Partial<Model.MemberGoodsLimitConfigRsp>)[]>[], // 商品列表
         quotationList: <Model.GoodsQuote[]>[], // 行情列表
         selectedGoodsId: <number | undefined>undefined, // 当前选中的商品ID
+        quoteGoodsList: <(Model.QuoteGoodsListRsp & Partial<Model.MemberGoodsLimitConfigRsp>)[]>[], // 挂牌点价商品列表
     })
 
     // 指定市场ID的商品列表
@@ -72,10 +75,12 @@ export const useFuturesStore = defineStore(() => {
     const getGoodsList = async () => {
         state.loading = true
         state.goodsList = []
+        state.quoteGoodsList = []
         state.quotationList = []
         timerTask.clearTimeout('quoteDay')
 
         const accountStore = useAccountStore()
+        const userStore = useUserStore()
         // 任务 #5511
         const { data: accountConfig } = await getTodayAccountConfigInfo()
         // 任务 #5197
@@ -85,10 +90,17 @@ export const useFuturesStore = defineStore(() => {
             }
         })
         const { data: goodsList } = await queryErmcpGoods()
+        const { data: qGoodsList } = await queryQuoteGoodsList({
+            data: {
+                usertype: userStore.userType ?? 0,
+                marketids: '10101'
+            }
+        })
 
         for (let i = 0; i < goodsList.length; i++) {
             const item = goodsList[i]
             const limit = limitConfig.find((e) => e.goodsid === item.goodsid)
+
             // 跳过不显示的商品
             if (limit?.isnodisplay) {
                 continue
@@ -123,6 +135,24 @@ export const useFuturesStore = defineStore(() => {
             })
         }
 
+        /// 挂牌点价商品
+        for (let i = 0; i < qGoodsList.length; i++) {
+            const item = qGoodsList[i]
+            const limit = limitConfig.find((e) => e.goodsid === item.goodsid)
+
+            // 跳过不显示的商品
+            if (limit?.isnodisplay) {
+                continue
+            }
+
+            // 组合商品属性
+            state.quoteGoodsList.push({
+                ...item,
+                iscannotbuy: limit?.iscannotbuy ?? 0,
+                iscannotsell: limit?.iscannotsell ?? 0
+            })
+        }
+
         // 待优化
         getQuoteDay().then(() => {
             dataCompletedCallback.forEach((callback) => callback())
@@ -136,7 +166,6 @@ export const useFuturesStore = defineStore(() => {
             timerTask.setTimeout(() => getQuoteDay(), 5 * 60 * 1000, 'quoteDay')
         })
     }
-
     // 获取商品盘面信息(待优化)
     const getQuoteDay = async () => {
         state.loading = true

+ 2 - 0
src/types/model/transfer.d.ts

@@ -39,6 +39,8 @@ declare namespace Model {
         lasttradedate: string; // 最后交易日期
         paystatus: number; // 支付状态 -1:待支付 2:已支付
         presaleprice: number; // 发售价(49)
+        provideraccountid: number; // 发售方资金账户ID(49)\供货商资金账户ID(50)
+        provideruserid: number;    // 发售方用户ID(49)\供货商(50)
         quoteminunit: number; // 行情小数位
         relatedgoodsid: number; // 关联交易合约ID
         sellname: string; // 客户名称(企业名称)